Back to index

python-biopython  1.60
test_PhyloXML.py
Go to the documentation of this file.
00001 # Copyright (C) 2009 by Eric Talevich (eric.talevich@gmail.com)
00002 # This code is part of the Biopython distribution and governed by its
00003 # license. Please see the LICENSE file that should have been included
00004 # as part of this package.
00005 
00006 """Unit tests for the PhyloXML and PhyloXMLIO modules.
00007 """
00008 
00009 import os
00010 import tempfile
00011 import unittest
00012 from itertools import chain
00013 
00014 from Bio import Alphabet
00015 from Bio.Seq import Seq
00016 from Bio.SeqRecord import SeqRecord
00017 from Bio.Align import MultipleSeqAlignment
00018 from Bio.Phylo import PhyloXML as PX, PhyloXMLIO
00019 
00020 # Example PhyloXML files
00021 EX_APAF = 'PhyloXML/apaf.xml'
00022 EX_BCL2 = 'PhyloXML/bcl_2.xml'
00023 EX_MADE = 'PhyloXML/made_up.xml'
00024 EX_PHYLO = 'PhyloXML/phyloxml_examples.xml'
00025 EX_DOLLO = 'PhyloXML/o_tol_332_d_dollo.xml'
00026 
00027 # Temporary file name for Writer tests below
00028 DUMMY = tempfile.mktemp()
00029 
00030 
00031 # ---------------------------------------------------------
00032 # Parser tests
00033 
00034 def _test_read_factory(source, count):
00035     """Generate a test method for read()ing the given source.
00036 
00037     The generated function reads an example file to produce a phyloXML object,
00038     then tests for existence of the root node, and counts the number of
00039     phylogenies under the root.
00040     """
00041     fname = os.path.basename(source)
00042     def test_read(self):
00043         phx = PhyloXMLIO.read(source)
00044         self.assertTrue(phx)
00045         self.assertEqual(len(phx), count[0])
00046         self.assertEqual(len(phx.other), count[1])
00047     test_read.__doc__ = "Read %s to produce a phyloXML object." % fname
00048     return test_read
00049 
00050 
00051 def _test_parse_factory(source, count):
00052     """Generate a test method for parse()ing the given source.
00053 
00054     The generated function extracts each phylogenetic tree using the parse()
00055     function and counts the total number of trees extracted.
00056     """
00057     fname = os.path.basename(source)
00058     def test_parse(self):
00059         trees = PhyloXMLIO.parse(source)
00060         self.assertEqual(len(list(trees)), count)
00061     test_parse.__doc__ = "Parse the phylogenies in %s." % fname
00062     return test_parse
00063 
00064 
00065 def _test_shape_factory(source, shapes):
00066     """Generate a test method for checking tree shapes.
00067 
00068     Counts the branches at each level of branching in a phylogenetic tree, 3
00069     clades deep.
00070     """
00071     fname = os.path.basename(source)
00072     def test_shape(self):
00073         trees = PhyloXMLIO.parse(source)
00074         for tree, shape_expect in zip(trees, shapes):
00075             self.assertEqual(len(tree.clade), len(shape_expect))
00076             for clade, sub_expect in zip(tree.clade, shape_expect):
00077                 self.assertEqual(len(clade), sub_expect[0])
00078                 for subclade, len_expect in zip(clade, sub_expect[1]):
00079                     self.assertEqual(len(subclade), len_expect)
00080     test_shape.__doc__ = "Check the branching structure of %s." % fname
00081     return test_shape
00082 
00083 
00084 class ParseTests(unittest.TestCase):
00085     """Tests for proper parsing of example phyloXML files."""
00086 
00087     test_read_apaf = _test_read_factory(EX_APAF, (1, 0))
00088     test_read_bcl2 = _test_read_factory(EX_BCL2, (1, 0))
00089     test_read_made = _test_read_factory(EX_MADE, (6, 0))
00090     test_read_phylo = _test_read_factory(EX_PHYLO, (13, 1))
00091     test_read_dollo = _test_read_factory(EX_DOLLO, (1, 0))
00092 
00093     test_parse_apaf = _test_parse_factory(EX_APAF, 1)
00094     test_parse_bcl2 = _test_parse_factory(EX_BCL2, 1)
00095     test_parse_made = _test_parse_factory(EX_MADE, 6)
00096     test_parse_phylo = _test_parse_factory(EX_PHYLO, 13)
00097     test_parse_dollo = _test_parse_factory(EX_DOLLO, 1)
00098 
00099     test_shape_apaf = _test_shape_factory(EX_APAF,
00100             # lvl-2 clades, sub-clade counts, lvl-3 clades
00101                 ( ( (2, (2, 2)),
00102                     (2, (2, 2)),
00103                   ),
00104                 ),
00105             )
00106     test_shape_bcl2 = _test_shape_factory(EX_BCL2,
00107                 ( ( (2, (2, 2)),
00108                     (2, (2, 2)),
00109                   ),
00110                 ),
00111             )
00112     test_shape_phylo = _test_shape_factory(EX_PHYLO,
00113                 ( ( (2, (0, 0)),
00114                     (0, ()),
00115                   ),
00116                   ( (2, (0, 0)),
00117                     (0, ()),
00118                   ),
00119                   ( (2, (0, 0)),
00120                     (0, ()),
00121                   ),
00122                   ( (2, (0, 0)),
00123                     (0, ()),
00124                   ),
00125                   ( (2, (0, 0)),
00126                     (0, ()),
00127                   ),
00128                   ( (2, (0, 0)),
00129                     (0, ()),
00130                   ),
00131                   ( (2, (0, 0)),
00132                     (0, ()),
00133                   ),
00134                   ( (2, (0, 0)),
00135                     (0, ()),
00136                   ),
00137                   ( (2, (0, 0)),
00138                     (0, ()),
00139                   ),
00140                   ( (0, ()),
00141                     (2, (0, 0)),
00142                   ),
00143                   ( (3, (0, 0, 0)),
00144                     (0, ()),
00145                   ),
00146                   ( (2, (0, 0)),
00147                     (0, ()),
00148                   ),
00149                   ( (2, (0, 0)),
00150                     (0, ()),
00151                   ),
00152                 ),
00153             )
00154     test_shape_dollo = _test_shape_factory(EX_DOLLO,
00155                 ( ( (2, (2, 2)),
00156                     (2, (2, 2)),
00157                   ),
00158                 ),
00159             )
00160 
00161 
00162 class TreeTests(unittest.TestCase):
00163     """Tests for instantiation and attributes of each complex type."""
00164     # ENH: also test check_str() regexps wherever they're used
00165 
00166     def test_Phyloxml(self):
00167         """Instantiation of Phyloxml objects."""
00168         phx = PhyloXMLIO.read(EX_PHYLO)
00169         self.assertTrue(isinstance(phx, PX.Phyloxml))
00170         for tree in phx:
00171             self.assertTrue(isinstance(tree, PX.Phylogeny))
00172         for otr in phx.other:
00173             self.assertTrue(isinstance(otr, PX.Other))
00174 
00175     def test_Other(self):
00176         """Instantiation of Other objects."""
00177         phx = PhyloXMLIO.read(EX_PHYLO)
00178         otr = phx.other[0]
00179         self.assertTrue(isinstance(otr, PX.Other))
00180         self.assertEqual(otr.tag, 'alignment')
00181         self.assertEqual(otr.namespace, 'http://example.org/align')
00182         self.assertEqual(len(otr.children), 3)
00183         for child, name, value in zip(otr, ('A', 'B', 'C'), (
00184             'acgtcgcggcccgtggaagtcctctcct', 'aggtcgcggcctgtggaagtcctctcct',
00185             'taaatcgc--cccgtgg-agtccc-cct')):
00186             self.assertEqual(child.tag, 'seq')
00187             self.assertEqual(child.attributes['name'], name)
00188             self.assertEqual(child.value, value)
00189 
00190     def test_Phylogeny(self):
00191         """Instantiation of Phylogeny objects."""
00192         trees = list(PhyloXMLIO.parse(EX_PHYLO))
00193         # Monitor lizards
00194         self.assertEqual(trees[9].name, 'monitor lizards')
00195         self.assertEqual(trees[9].description,
00196                 'a pylogeny of some monitor lizards')
00197         self.assertEqual(trees[9].rooted, True)
00198         # Network (unrooted)
00199         tree6 = trees[6]
00200         self.assertEqual(trees[6].name,
00201                 'network, node B is connected to TWO nodes: AB and C')
00202         self.assertEqual(trees[6].rooted, False)
00203 
00204     def test_Clade(self):
00205         """Instantiation of Clade objects."""
00206         tree = list(PhyloXMLIO.parse(EX_PHYLO))[6]
00207         clade_ab, clade_c = tree.clade.clades
00208         clade_a, clade_b = clade_ab.clades
00209         for clade, id_source, name, blen in zip(
00210                 (clade_ab, clade_a, clade_b, clade_c),
00211                 ('ab', 'a', 'b', 'c'),
00212                 ('AB', 'A', 'B', 'C'),
00213                 (0.06, 0.102, 0.23, 0.4)):
00214             self.assertTrue(isinstance(clade, PX.Clade))
00215             self.assertEqual(clade.id_source, id_source)
00216             self.assertEqual(clade.name, name)
00217             self.assertAlmostEqual(clade.branch_length, blen)
00218 
00219     def test_Annotation(self):
00220         """Instantiation of Annotation objects."""
00221         tree = list(PhyloXMLIO.parse(EX_PHYLO))[3]
00222         ann = tree.clade[1].sequences[0].annotations[0]
00223         self.assertTrue(isinstance(ann, PX.Annotation))
00224         self.assertEqual(ann.desc, 'alcohol dehydrogenase')
00225         self.assertAlmostEqual(ann.confidence.value, 0.67)
00226         self.assertEqual(ann.confidence.type, 'probability')
00227 
00228     def test_BinaryCharacters(self):
00229         """Instantiation of BinaryCharacters objects."""
00230         #Because we short circult interation, must close handle explicitly
00231         #to avoid a ResourceWarning
00232         handle = open(EX_DOLLO)
00233         tree = PhyloXMLIO.parse(handle).next()
00234         handle.close()
00235         bchars = tree.clade[0,0].binary_characters
00236         self.assertTrue(isinstance(bchars, PX.BinaryCharacters))
00237         self.assertEqual(bchars.type, 'parsimony inferred')
00238         for name, count, value in (
00239                 ('gained',  2, ['Cofilin_ADF', 'Gelsolin']),
00240                 ('lost',    0, []),
00241                 ('present', 2, ['Cofilin_ADF', 'Gelsolin']),
00242                 ('absent',  None, []),
00243                 ):
00244             self.assertEqual(getattr(bchars, name+'_count'), count)
00245             self.assertEqual(getattr(bchars, name), value)
00246 
00247     # TODO: BranchColor -- see made_up.xml
00248 
00249     def test_CladeRelation(self):
00250         """Instantiation of CladeRelation objects."""
00251         tree = list(PhyloXMLIO.parse(EX_PHYLO))[6]
00252         crel = tree.clade_relations[0]
00253         self.assertTrue(isinstance(crel, PX.CladeRelation))
00254         self.assertEqual(crel.id_ref_0, 'b')
00255         self.assertEqual(crel.id_ref_1, 'c')
00256         self.assertEqual(crel.type, 'network_connection')
00257 
00258     def test_Confidence(self):
00259         """Instantiation of Confidence objects."""
00260         #Because we short circult interation, must close handle explicitly
00261         handle = open(EX_MADE)
00262         tree = PhyloXMLIO.parse(handle).next()
00263         handle.close()
00264         self.assertEqual(tree.name, 'testing confidence')
00265         for conf, type, val in zip(tree.confidences,
00266                 ('bootstrap', 'probability'),
00267                 (89.0, 0.71)):
00268             self.assertTrue(isinstance(conf, PX.Confidence))
00269             self.assertEqual(conf.type, type)
00270             self.assertAlmostEqual(conf.value, val)
00271         self.assertEqual(tree.clade.name, 'b')
00272         self.assertAlmostEqual(tree.clade.width, 0.2)
00273         for conf, val in zip(tree.clade[0].confidences,
00274                 (0.9, 0.71)):
00275             self.assertTrue(isinstance(conf, PX.Confidence))
00276             self.assertEqual(conf.type, 'probability')
00277             self.assertAlmostEqual(conf.value, val)
00278 
00279     def test_Date(self):
00280         """Instantiation of Date objects."""
00281         tree = list(PhyloXMLIO.parse(EX_PHYLO))[11]
00282         silurian = tree.clade[0,0].date
00283         devonian = tree.clade[0,1].date
00284         ediacaran = tree.clade[1].date
00285         for date, desc, val in zip(
00286                 (silurian, devonian, ediacaran),
00287                 # (10, 20, 30), # range is deprecated
00288                 ('Silurian', 'Devonian', 'Ediacaran'),
00289                 (425, 320, 600)):
00290             self.assertTrue(isinstance(date, PX.Date))
00291             self.assertEqual(date.unit, 'mya')
00292             # self.assertAlmostEqual(date.range, rang)
00293             self.assertEqual(date.desc, desc)
00294             self.assertAlmostEqual(date.value, val)
00295 
00296     def test_Distribution(self):
00297         """Instantiation of Distribution objects.
00298 
00299         Also checks Point type and safe Unicode handling (?).
00300         """
00301         tree = list(PhyloXMLIO.parse(EX_PHYLO))[10]
00302         hirschweg = tree.clade[0,0].distributions[0]
00303         nagoya = tree.clade[0,1].distributions[0]
00304         eth_zurich = tree.clade[0,2].distributions[0]
00305         san_diego = tree.clade[1].distributions[0]
00306         for dist, desc, lati, longi, alti in zip(
00307                 (hirschweg, nagoya, eth_zurich, san_diego),
00308                 ('Hirschweg, Winterthur, Switzerland',
00309                     'Nagoya, Aichi, Japan',
00310                     u'ETH Z\xfcrich',
00311                     'San Diego'),
00312                 (47.481277, 35.155904, 47.376334, 32.880933),
00313                 (8.769303, 136.915863, 8.548108, -117.217543),
00314                 (472, 10, 452, 104)):
00315             self.assertTrue(isinstance(dist, PX.Distribution))
00316             self.assertEqual(dist.desc, desc)
00317             point = dist.points[0]
00318             self.assertTrue(isinstance(point, PX.Point))
00319             self.assertEqual(point.geodetic_datum, 'WGS84')
00320             self.assertEqual(point.lat, lati)
00321             self.assertEqual(point.long, longi)
00322             self.assertEqual(point.alt, alti)
00323 
00324     def test_DomainArchitecture(self):
00325         """Instantiation of DomainArchitecture objects.
00326 
00327         Also checks ProteinDomain type.
00328         """
00329         #Because we short circult interation, must close handle explicitly
00330         handle = open(EX_APAF)
00331         tree = PhyloXMLIO.parse(handle).next()
00332         handle.close()
00333         clade = tree.clade[0,0,0,0,0,0,0,0,0,0]
00334         darch = clade.sequences[0].domain_architecture
00335         self.assertTrue(isinstance(darch, PX.DomainArchitecture))
00336         self.assertEqual(darch.length, 1249)
00337         for domain, start, end, conf, value in zip(darch.domains,
00338                 (6, 109, 605, 647, 689, 733, 872, 993, 1075, 1117, 1168),
00339                 (90, 414, 643, 685, 729, 771, 910, 1031, 1113, 1155, 1204),
00340                 (7.0e-26, 7.2e-117, 2.4e-6, 1.1e-12, 2.4e-7, 4.7e-14, 2.5e-8,
00341                     4.6e-6, 6.3e-7, 1.4e-7, 0.3),
00342                 ('CARD', 'NB-ARC', 'WD40', 'WD40', 'WD40', 'WD40', 'WD40',
00343                     'WD40', 'WD40', 'WD40', 'WD40')):
00344             self.assertTrue(isinstance(domain, PX.ProteinDomain))
00345             self.assertEqual(domain.start + 1, start)
00346             self.assertEqual(domain.end, end)
00347             self.assertAlmostEqual(domain.confidence, conf)
00348             self.assertEqual(domain.value, value)
00349 
00350     def test_Events(self):
00351         """Instantiation of Events objects."""
00352         tree = list(PhyloXMLIO.parse(EX_PHYLO))[4]
00353         event_s = tree.clade.events
00354         self.assertTrue(isinstance(event_s, PX.Events))
00355         self.assertEqual(event_s.speciations, 1)
00356         event_d = tree.clade[0].events
00357         self.assertTrue(isinstance(event_d, PX.Events))
00358         self.assertEqual(event_d.duplications, 1)
00359 
00360     def test_Polygon(self):
00361         """Instantiation of Polygon objects."""
00362         tree = PhyloXMLIO.read(EX_MADE).phylogenies[1]
00363         self.assertEqual(tree.name, 'testing polygon')
00364         dist = tree.clade[0].distributions[0]
00365         for poly in dist.polygons:
00366             self.assertTrue(isinstance(poly, PX.Polygon))
00367             self.assertEqual(len(poly.points), 3)
00368         self.assertEqual(dist.polygons[0].points[0].alt_unit, 'm')
00369         for point, lati, longi, alti in zip(
00370                 chain(dist.polygons[0].points, dist.polygons[1].points),
00371                 (47.481277, 35.155904, 47.376334, 40.481277, 25.155904,
00372                     47.376334),
00373                 (8.769303, 136.915863, 8.548108, 8.769303, 136.915863,
00374                     7.548108),
00375                 (472, 10, 452, 42, 10, 452),
00376                 ):
00377             self.assertTrue(isinstance(point, PX.Point))
00378             self.assertEqual(point.geodetic_datum, 'WGS84')
00379             self.assertEqual(point.lat, lati)
00380             self.assertEqual(point.long, longi)
00381             self.assertEqual(point.alt, alti)
00382 
00383     def test_Property(self):
00384         """Instantiation of Property objects."""
00385         tree = list(PhyloXMLIO.parse(EX_PHYLO))[8]
00386         for prop, id_ref, value in zip(
00387                 tree.properties,
00388                 ('id_a', 'id_b', 'id_c'),
00389                 ('1200', '2300', '200')):
00390             self.assertTrue(isinstance(prop, PX.Property))
00391             self.assertEqual(prop.id_ref, id_ref)
00392             self.assertEqual(prop.datatype, "xsd:integer")
00393             self.assertEqual(prop.ref, "NOAA:depth")
00394             self.assertEqual(prop.applies_to, "node")
00395             self.assertEqual(prop.unit, "METRIC:m" )
00396             self.assertEqual(prop.value, value)
00397 
00398     def test_Reference(self):
00399         """Instantiation of Reference objects."""
00400         #Because we short circult interation, must close handle explicitly
00401         #to avoid a ResourceWarning
00402         handle = open(EX_DOLLO)
00403         tree = PhyloXMLIO.parse(handle).next()
00404         handle.close()
00405         reference = tree.clade[0,0,0,0,0,0].references[0]
00406         self.assertTrue(isinstance(reference, PX.Reference))
00407         self.assertEqual(reference.doi, '10.1038/nature06614')
00408         self.assertEqual(reference.desc, None)
00409 
00410     def test_Sequence(self):
00411         """Instantiation of Sequence objects.
00412 
00413         Also checks Accession and Annotation types.
00414         """
00415         trees = list(PhyloXMLIO.parse(EX_PHYLO))
00416         # Simple element with id_source
00417         seq0 = trees[4].clade[1].sequences[0]
00418         self.assertTrue(isinstance(seq0, PX.Sequence))
00419         self.assertEqual(seq0.id_source, 'z')
00420         self.assertEqual(seq0.symbol, 'ADHX')
00421         self.assertEqual(seq0.accession.source, 'ncbi')
00422         self.assertEqual(seq0.accession.value, 'Q17335')
00423         self.assertEqual(seq0.name, 'alcohol dehydrogenase')
00424         self.assertEqual(seq0.annotations[0].ref, 'InterPro:IPR002085')
00425         # More complete elements
00426         seq1 = trees[5].clade[0,0].sequences[0]
00427         seq2 = trees[5].clade[0,1].sequences[0]
00428         seq3 = trees[5].clade[1].sequences[0]
00429         for seq, sym, acc, name, mol_seq, ann_refs in zip(
00430                 (seq1, seq2, seq3),
00431                 ('ADHX', 'RT4I1', 'ADHB'),
00432                 ('P81431', 'Q54II4', 'Q04945'),
00433                 ('Alcohol dehydrogenase class-3',
00434                  'Reticulon-4-interacting protein 1 homolog, ' \
00435                          'mitochondrial precursor',
00436                  'NADH-dependent butanol dehydrogenase B'),
00437                 ('TDATGKPIKCMAAIAWEAKKPLSIEEVEVAPPKSGEVRIKILHSGVCHTD',
00438                  'MKGILLNGYGESLDLLEYKTDLPVPKPIKSQVLIKIHSTSINPLDNVMRK',
00439                  'MVDFEYSIPTRIFFGKDKINVLGRELKKYGSKVLIVYGGGSIKRNGIYDK'),
00440                 (("EC:1.1.1.1", "GO:0004022"),
00441                  ("GO:0008270", "GO:0016491"),
00442                  ("GO:0046872", "KEGG:Tetrachloroethene degradation")),
00443                 ):
00444             self.assertTrue(isinstance(seq, PX.Sequence))
00445             self.assertEqual(seq.symbol, sym)
00446             self.assertEqual(seq.accession.source, 'UniProtKB')
00447             self.assertEqual(seq.accession.value, acc)
00448             self.assertEqual(seq.name, name)
00449             self.assertEqual(seq.mol_seq.value, mol_seq)
00450             self.assertEqual(seq.annotations[0].ref, ann_refs[0])
00451             self.assertEqual(seq.annotations[1].ref, ann_refs[1])
00452 
00453     def test_SequenceRelation(self):
00454         """Instantiation of SequenceRelation objects."""
00455         tree = list(PhyloXMLIO.parse(EX_PHYLO))[4]
00456         for seqrel, id_ref_0, id_ref_1, type in zip(
00457                 tree.sequence_relations,
00458                 ('x', 'x', 'y'), ('y', 'z', 'z'),
00459                 ('paralogy', 'orthology', 'orthology')):
00460             self.assertTrue(isinstance(seqrel, PX.SequenceRelation))
00461             self.assertEqual(seqrel.id_ref_0, id_ref_0)
00462             self.assertEqual(seqrel.id_ref_1, id_ref_1)
00463             self.assertEqual(seqrel.type, type)
00464 
00465     def test_Taxonomy(self):
00466         """Instantiation of Taxonomy objects.
00467 
00468         Also checks Id type.
00469         """
00470         trees = list(PhyloXMLIO.parse(EX_PHYLO))
00471         # Octopus
00472         tax5 = trees[5].clade[0,0].taxonomies[0]
00473         self.assertTrue(isinstance(tax5, PX.Taxonomy))
00474         self.assertEqual(tax5.id.value, '6645')
00475         self.assertEqual(tax5.id.provider, 'NCBI')
00476         self.assertEqual(tax5.code, 'OCTVU')
00477         self.assertEqual(tax5.scientific_name, 'Octopus vulgaris')
00478         # Nile monitor
00479         tax9 = trees[9].clade[0].taxonomies[0]
00480         self.assertTrue(isinstance(tax9, PX.Taxonomy))
00481         self.assertEqual(tax9.id.value, '62046')
00482         self.assertEqual(tax9.id.provider, 'NCBI')
00483         self.assertEqual(tax9.scientific_name, 'Varanus niloticus')
00484         self.assertEqual(tax9.common_names[0], 'Nile monitor')
00485         self.assertEqual(tax9.rank, 'species')
00486 
00487     def test_Uri(self):
00488         """Instantiation of Uri objects."""
00489         tree = list(PhyloXMLIO.parse(EX_PHYLO))[9]
00490         uri = tree.clade.taxonomies[0].uri
00491         self.assertTrue(isinstance(uri, PX.Uri))
00492         self.assertEqual(uri.desc, 'EMBL REPTILE DATABASE')
00493         self.assertEqual(uri.value,
00494                 'http://www.embl-heidelberg.de/~uetz/families/Varanidae.html')
00495 
00496 
00497 # ---------------------------------------------------------
00498 # Serialization tests
00499 
00500 class WriterTests(unittest.TestCase):
00501     """Tests for serialization of objects to phyloXML format.
00502     
00503     Modifies the globally defined filenames in order to run the other parser
00504     tests on files (re)generated by PhyloXMLIO's own writer.
00505     """
00506 
00507     def _rewrite_and_call(self, orig_fname, test_cases):
00508         """Parse, rewrite and retest a phyloXML example file."""
00509         infile = open(orig_fname, 'rb')
00510         phx = PhyloXMLIO.read(infile)
00511         infile.close()
00512         outfile = open(DUMMY, 'w+b')
00513         PhyloXMLIO.write(phx, outfile)
00514         outfile.close()
00515         for cls, tests in test_cases:
00516             inst = cls('setUp')
00517             for test in tests:
00518                 getattr(inst, test)()
00519 
00520     def test_apaf(self):
00521         """Round-trip parsing and serialization of apaf.xml."""
00522         global EX_APAF
00523         orig_fname = EX_APAF
00524         try:
00525             EX_APAF = DUMMY
00526             self._rewrite_and_call(orig_fname, (
00527                 (ParseTests, [
00528                     'test_read_apaf', 'test_parse_apaf', 'test_shape_apaf']),
00529                 (TreeTests, ['test_DomainArchitecture']),
00530                 ))
00531         finally:
00532             EX_APAF = orig_fname
00533 
00534     def test_bcl2(self):
00535         """Round-trip parsing and serialization of bcl_2.xml."""
00536         global EX_BCL2
00537         orig_fname = EX_BCL2
00538         try:
00539             EX_BCL2 = DUMMY
00540             self._rewrite_and_call(orig_fname, (
00541                 (ParseTests, [
00542                     'test_read_bcl2', 'test_parse_bcl2', 'test_shape_bcl2']),
00543                 (TreeTests, ['test_Confidence']),
00544                 ))
00545         finally:
00546             EX_BCL2 = orig_fname
00547 
00548     def test_made(self):
00549         """Round-trip parsing and serialization of made_up.xml."""
00550         global EX_MADE
00551         orig_fname = EX_MADE
00552         try:
00553             EX_MADE = DUMMY
00554             self._rewrite_and_call(orig_fname, (
00555                 (ParseTests, ['test_read_made', 'test_parse_made']),
00556                 (TreeTests, ['test_Confidence', 'test_Polygon']),
00557                 ))
00558         finally:
00559             EX_MADE = orig_fname
00560 
00561     def test_phylo(self):
00562         """Round-trip parsing and serialization of phyloxml_examples.xml."""
00563         global EX_PHYLO
00564         orig_fname = EX_PHYLO
00565         try:
00566             EX_PHYLO = DUMMY
00567             self._rewrite_and_call(orig_fname, (
00568                 (ParseTests, [
00569                     'test_read_phylo', 'test_parse_phylo', 'test_shape_phylo']),
00570                 (TreeTests, [
00571                     'test_Phyloxml',   'test_Other',
00572                     'test_Phylogeny',  'test_Clade',
00573                     'test_Annotation', 'test_CladeRelation',
00574                     'test_Date',       'test_Distribution',
00575                     'test_Events',     'test_Property',
00576                     'test_Sequence',   'test_SequenceRelation',
00577                     'test_Taxonomy',   'test_Uri',
00578                     ]),
00579                 ))
00580         finally:
00581             EX_PHYLO = orig_fname
00582 
00583     def test_dollo(self):
00584         """Round-trip parsing and serialization of o_tol_332_d_dollo.xml."""
00585         global EX_DOLLO
00586         orig_fname = EX_DOLLO
00587         try:
00588             EX_DOLLO = DUMMY
00589             self._rewrite_and_call(orig_fname, (
00590                 (ParseTests, ['test_read_dollo', 'test_parse_dollo']),
00591                 (TreeTests, ['test_BinaryCharacters']),
00592                 ))
00593         finally:
00594             EX_DOLLO = orig_fname
00595 
00596 
00597 # ---------------------------------------------------------
00598 # Method tests
00599 
00600 class MethodTests(unittest.TestCase):
00601     """Tests for methods on specific classes/objects."""
00602     def setUp(self):
00603         self.phyloxml = PhyloXMLIO.read(EX_PHYLO)
00604 
00605     # Type conversions
00606 
00607     def test_clade_to_phylogeny(self):
00608         """Convert a Clade object to a new Phylogeny."""
00609         clade = self.phyloxml.phylogenies[0].clade[0]
00610         tree = clade.to_phylogeny(rooted=True)
00611         self.assertTrue(isinstance(tree, PX.Phylogeny))
00612 
00613     def test_phylogeny_to_phyloxml(self):
00614         """Convert a Phylogeny object to a new Phyloxml."""
00615         tree = self.phyloxml.phylogenies[0]
00616         doc = tree.to_phyloxml_container()
00617         self.assertTrue(isinstance(doc, PX.Phyloxml))
00618 
00619     def test_sequence_conversion(self):
00620         pseq = PX.Sequence(
00621             type='protein',
00622             # id_ref=None,
00623             # id_source=None,
00624             symbol='ADHX',
00625             accession=PX.Accession('P81431', source='UniProtKB'),
00626             name='Alcohol dehydrogenase class-3',
00627             # location=None,
00628             mol_seq=PX.MolSeq(
00629                 'TDATGKPIKCMAAIAWEAKKPLSIEEVEVAPPKSGEVRIKILHSGVCHTD'),
00630             uri=None,
00631             annotations=[PX.Annotation(ref='EC:1.1.1.1'),
00632                          PX.Annotation(ref='GO:0004022')],
00633             domain_architecture=PX.DomainArchitecture(
00634                 length=50,
00635                 domains=[PX.ProteinDomain(*args) for args in (
00636                     # value,   start,   end,    confidence
00637                     ('FOO',     0,      5,      7.0e-26),
00638                     ('BAR',     8,      13,     7.2e-117),
00639                     ('A-OK',    21,     34,     2.4e-06),
00640                     ('WD40',    40,     50,     0.3))],
00641                 ))
00642         srec = pseq.to_seqrecord()
00643         # TODO: check seqrec-specific traits (see args)
00644         #   Seq(letters, alphabet), id, name, description, features
00645         pseq2 = PX.Sequence.from_seqrecord(srec)
00646         # TODO: check the round-tripped attributes again
00647 
00648     def test_to_alignment(self):
00649         tree = self.phyloxml.phylogenies[0]
00650         aln = tree.to_alignment()
00651         self.assertTrue(isinstance(aln, MultipleSeqAlignment))
00652         self.assertEqual(len(aln), 0)
00653         # Add sequences to the terminals
00654         alphabet = Alphabet.Gapped(Alphabet.generic_dna)
00655         for tip, seqstr in zip(tree.get_terminals(),
00656                 ('AA--TTA', 'AA--TTG', 'AACCTTC')):
00657             tip.sequences.append(PX.Sequence.from_seqrecord(
00658                 SeqRecord(Seq(seqstr, alphabet), id=str(tip))))
00659         # Check the alignment
00660         aln = tree.to_alignment()
00661         self.assertTrue(isinstance(aln, MultipleSeqAlignment))
00662         self.assertEqual(len(aln), 3)
00663         self.assertEqual(aln.get_alignment_length(), 7)
00664 
00665     # Syntax sugar
00666 
00667     def test_clade_getitem(self):
00668         """Clade.__getitem__: get sub-clades by extended indexing."""
00669         tree = self.phyloxml.phylogenies[3]
00670         self.assertEqual(tree.clade[0,0], tree.clade.clades[0].clades[0])
00671         self.assertEqual(tree.clade[0,1], tree.clade.clades[0].clades[1])
00672         self.assertEqual(tree.clade[1], tree.clade.clades[1])
00673         self.assertEqual(len(tree.clade[:]), len(tree.clade.clades))
00674         self.assertEqual(len(tree.clade[0,:]),
00675                          len(tree.clade.clades[0].clades))
00676 
00677     def test_phyloxml_getitem(self):
00678         """Phyloxml.__getitem__: get phylogenies by name or index."""
00679         self.assertTrue(self.phyloxml.phylogenies[9] is self.phyloxml[9])
00680         self.assertTrue(self.phyloxml['monitor lizards'] is self.phyloxml[9])
00681         self.assertEqual(len(self.phyloxml[:]), len(self.phyloxml))
00682 
00683     def test_events(self):
00684         """Events: Mapping-type behavior."""
00685         evts = self.phyloxml.phylogenies[4].clade.events
00686         # Container behavior: __len__, __contains__
00687         self.assertEqual(len(evts), 1)
00688         self.assertEqual('speciations' in evts, True)
00689         self.assertEqual('duplications' in evts, False)
00690         # Attribute access: __get/set/delitem__
00691         self.assertEqual(evts['speciations'], 1)
00692         self.assertRaises(KeyError, lambda k: evts[k], 'duplications')
00693         evts['duplications'] = 3
00694         self.assertEqual(evts.duplications, 3)
00695         self.assertEqual(len(evts), 2)
00696         del evts['speciations']
00697         self.assertEqual(evts.speciations, None)
00698         self.assertEqual(len(evts), 1)
00699         # Iteration: __iter__, keys, values, items
00700         self.assertEqual(list(iter(evts)), ['duplications'])
00701         self.assertEqual(list(evts.keys()), ['duplications'])
00702         self.assertEqual(list(evts.values()), [3])
00703         self.assertEqual(list(evts.items()), [('duplications', 3)])
00704 
00705     def test_singlular(self):
00706         """Clade, Phylogeny: Singular properties for plural attributes."""
00707         conf = PX.Confidence(0.9, 'bootstrap')
00708         taxo = PX.Taxonomy(rank='genus')
00709         # Clade.taxonomy, Clade.confidence
00710         clade = PX.Clade(confidences=[conf], taxonomies=[taxo])
00711         self.assertEqual(clade.confidence.type, 'bootstrap')
00712         self.assertEqual(clade.taxonomy.rank, 'genus')
00713         # raise if len > 1
00714         clade.confidences.append(conf)
00715         self.assertRaises(AttributeError, getattr, clade, 'confidence')
00716         clade.taxonomies.append(taxo)
00717         self.assertRaises(AttributeError, getattr, clade, 'taxonomy')
00718         # None if []
00719         clade.confidences = []
00720         self.assertEqual(clade.confidence, None)
00721         clade.taxonomies = []
00722         self.assertEqual(clade.taxonomy, None)
00723         # Phylogeny.confidence
00724         tree = PX.Phylogeny(True, confidences=[conf])
00725         self.assertEqual(tree.confidence.type, 'bootstrap')
00726         tree.confidences.append(conf)
00727         self.assertRaises(AttributeError, getattr, tree, 'confidence')
00728         tree.confidences = []
00729         self.assertEqual(tree.confidence, None)
00730 
00731     # Other methods
00732 
00733     def test_color_hex(self):
00734         """BranchColor: to_hex() method."""
00735         black = PX.BranchColor(0, 0, 0)
00736         self.assertEqual(black.to_hex(), '#000000')
00737         white = PX.BranchColor(255, 255, 255)
00738         self.assertEqual(white.to_hex(), '#ffffff')
00739         green = PX.BranchColor(14, 192, 113)
00740         self.assertEqual(green.to_hex(), '#0ec071')
00741 
00742 # ---------------------------------------------------------
00743 
00744 if __name__ == '__main__':
00745     runner = unittest.TextTestRunner(verbosity=2)
00746     unittest.main(testRunner=runner)
00747     # Clean up the temporary file
00748     if os.path.exists(DUMMY):
00749         os.remove(DUMMY)