3030import xml .etree
3131
3232import msprime
33+ from xmlunittest import XmlTestCase
3334
3435import tests .tsutil as tsutil
3536import tskit
@@ -44,18 +45,20 @@ def get_binary_tree(self):
4445 ts = msprime .simulate (10 , random_seed = 1 , mutation_rate = 1 )
4546 return next (ts .trees ())
4647
47- def get_nonbinary_tree (self ):
48+ def get_nonbinary_ts (self , sample_size = 10 ):
4849 demographic_events = [
4950 msprime .SimpleBottleneck (time = 0.1 , population = 0 , proportion = 0.5 )
5051 ]
51- ts = msprime .simulate (
52- 10 ,
52+ return msprime .simulate (
53+ sample_size ,
5354 recombination_rate = 5 ,
5455 mutation_rate = 10 ,
5556 demographic_events = demographic_events ,
5657 random_seed = 1 ,
5758 )
58- for t in ts .trees ():
59+
60+ def get_nonbinary_tree (self , sample_size = 10 ):
61+ for t in self .get_nonbinary_ts (sample_size ).trees ():
5962 for u in t .nodes ():
6063 if len (t .children (u )) > 2 :
6164 return t
@@ -1134,16 +1137,44 @@ def test_max_tree_height(self):
11341137 t .draw_text (max_tree_height = bad_max_tree_height )
11351138
11361139
1137- class TestDrawSvg (TestTreeDraw ):
1140+ class TestDrawSvg (TestTreeDraw , XmlTestCase ):
11381141 """
11391142 Tests the SVG tree drawing.
11401143 """
11411144
1142- def verify_basic_svg (self , svg , width = 200 , height = 200 ):
1145+ def verify_basic_svg (self , svg , width = 200 , height = 200 , new_routine = True ):
1146+ prefix = "{http://www.w3.org/2000/svg}"
11431147 root = xml .etree .ElementTree .fromstring (svg )
1144- self .assertEqual (root .tag , "{http://www.w3.org/2000/svg} svg" )
1148+ self .assertEqual (root .tag , prefix + " svg" )
11451149 self .assertEqual (width , int (root .attrib ["width" ]))
11461150 self .assertEqual (height , int (root .attrib ["height" ]))
1151+ if new_routine :
1152+ # Verify the class structure of the svg
1153+ root_group = root .find (prefix + "g" )
1154+ self .assertIn ("class" , root_group .attrib )
1155+ self .assertRegexpMatches (
1156+ root_group .attrib ["class" ], r"\b(tree|tree-sequence)\b"
1157+ )
1158+ if "tree-sequence" in root_group .attrib ["class" ]:
1159+ trees = root_group .find (prefix + "g" )
1160+ self .assertIn ("class" , trees .attrib )
1161+ self .assertRegexpMatches (trees .attrib ["class" ], r"\btrees\b" )
1162+ first_tree = trees .find (prefix + "g" )
1163+ self .assertIn ("class" , first_tree .attrib )
1164+ self .assertRegexpMatches (first_tree .attrib ["class" ], r"\btree\b" )
1165+ else :
1166+ first_tree = root_group
1167+ # Check that we have edges, symbols, and labels groups
1168+ for group in first_tree .findall (prefix + "g" ):
1169+ self .assertIn ("class" , group .attrib )
1170+ cls = group .attrib ["class" ]
1171+ self .assertRegexpMatches (cls , r"\b(edges|symbols|labels)\b" )
1172+ if "symbols" in cls or "labels" in cls :
1173+ # Check that we have nodes & mutations subgroups
1174+ for subgroup in group .findall (prefix + "g" ):
1175+ self .assertIn ("class" , subgroup .attrib )
1176+ subcls = subgroup .attrib ["class" ]
1177+ self .assertRegexpMatches (subcls , r"\b(nodes|mutations)\b" )
11471178
11481179 def test_draw_file (self ):
11491180 t = self .get_binary_tree ()
@@ -1177,7 +1208,7 @@ def test_draw_file(self):
11771208 def test_draw_defaults (self ):
11781209 t = self .get_binary_tree ()
11791210 svg = t .draw ()
1180- self .verify_basic_svg (svg )
1211+ self .verify_basic_svg (svg , new_routine = False )
11811212 svg = t .draw_svg ()
11821213 self .verify_basic_svg (svg )
11831214
@@ -1483,3 +1514,156 @@ def test_tree_height_scale(self):
14831514 for bad_scale in [0 , "" , "NOT A SCALE" ]:
14841515 with self .assertRaises (ValueError ):
14851516 ts .draw_svg (tree_height_scale = bad_scale )
1517+
1518+ def test_known_svg_tree (self ):
1519+ tree = self .get_nonbinary_tree (sample_size = 3 )
1520+ svg = tree .draw_svg (
1521+ root_svg_attributes = {"id" : "AB" }, style = ".edges {stroke: red}"
1522+ )
1523+ expected_svg = b"""<?xml version="1.0" encoding="utf-8" ?>
1524+ <svg baseProfile="full" height="200" id="AB" version="1.1" width="200"
1525+ xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events"
1526+ xmlns:xlink="http://www.w3.org/1999/xlink">
1527+ <defs><style type="text/css"><![CDATA[.edges {stroke: red}]]></style></defs>
1528+ <g class="tree t0">
1529+ <g class="edges" fill="none" stroke="black">
1530+ <path class="p4 c0" d="M 55.0 170.0 V 30.0 H 100.0"/>
1531+ <path class="p4 c1" d="M 100.0 170.0 V 30.0 H 100.0"/>
1532+ <path class="p4 c2" d="M 145.0 170.0 V 30.0 H 100.0"/>
1533+ </g>
1534+ <g class="symbols">
1535+ <g class="nodes">
1536+ <circle class="n4" cx="100.0" cy="30.0" r="3"/>
1537+ <circle class="n0 sample" cx="55.0" cy="170.0" r="3"/>
1538+ <circle class="n1 sample" cx="100.0" cy="170.0" r="3"/>
1539+ <circle class="n2 sample" cx="145.0" cy="170.0" r="3"/>
1540+ </g>
1541+ <g class="mutations" fill="red"/>
1542+ </g>
1543+ <g class="labels" dominant-baseline="middle" font-size="14">
1544+ <g class="nodes">
1545+ <g text-anchor="start"/>
1546+ <g text-anchor="middle">
1547+ <g transform="translate(100.0, 25.0)"><text class="n4">4</text></g>
1548+ <g transform="translate(55.0, 190.0)"><text class="n0 sample">0</text></g>
1549+ <g transform="translate(100.0, 190.0)"><text class="n1 sample">1</text></g>
1550+ <g transform="translate(145.0, 190.0)"><text class="n2 sample">2</text></g>
1551+ </g>
1552+ <g text-anchor="end"/>
1553+ </g>
1554+ <g class="mutations" font-style="italic">
1555+ <g text-anchor="start"/>
1556+ <g text-anchor="end"/>
1557+ </g>
1558+ </g>
1559+ </g>
1560+ </svg>"""
1561+ self .assertXmlEquivalentOutputs (svg , expected_svg )
1562+
1563+ def test_known_svg_ts (self ):
1564+ ts = self .get_nonbinary_ts (sample_size = 3 )
1565+ svg = ts .draw_svg (
1566+ root_svg_attributes = {"id" : "AB" }, style = ".edges {stroke: red}"
1567+ )
1568+ self .verify_basic_svg (svg , width = 200 * ts .num_trees )
1569+ expected_svg = b"""<?xml version="1.0" encoding="utf-8" ?>
1570+ <svg baseProfile="full" id="AB" height="200" version="1.1" width="400"
1571+ xmlns="http://www.w3.org/2000/svg" xmlns:ev="http://www.w3.org/2001/xml-events"
1572+ xmlns:xlink="http://www.w3.org/1999/xlink">
1573+ <defs><style type="text/css"><![CDATA[.edges {stroke: red}]]></style></defs>
1574+ <g class="tree-sequence">
1575+ <g class="trees">
1576+ <g class="tree t0" transform="translate(20 15)">
1577+ <g class="edges" fill="none" stroke="black">
1578+ <path class="p4 c0" d="M 50.0 140.0 V 30.0 H 90.0"/>
1579+ <path class="p4 c1" d="M 90.0 140.0 V 30.0 H 90.0"/>
1580+ <path class="p4 c2" d="M 130.0 140.0 V 30.0 H 90.0"/>
1581+ </g>
1582+ <g class="symbols">
1583+ <g class="nodes">
1584+ <circle class="n4" cx="90.0" cy="30.0" r="3"/>
1585+ <circle class="n0 sample" cx="50.0" cy="140.0" r="3"/>
1586+ <circle class="n1 sample" cx="90.0" cy="140.0" r="3"/>
1587+ <circle class="n2 sample" cx="130.0" cy="140.0" r="3"/>
1588+ </g>
1589+ <g class="mutations" fill="red"/>
1590+ </g>
1591+ <g class="labels" dominant-baseline="middle" font-size="14">
1592+ <g class="nodes">
1593+ <g text-anchor="start"/>
1594+ <g text-anchor="middle">
1595+ <g transform="translate(90.0, 25.0)"><text class="n4">4</text></g>
1596+ <g transform="translate(50.0, 160.0)"><text class="n0 sample">0</text></g>
1597+ <g transform="translate(90.0, 160.0)"><text class="n1 sample">1</text></g>
1598+ <g transform="translate(130.0, 160.0)"><text class="n2 sample">2</text></g>
1599+ </g>
1600+ <g text-anchor="end"/>
1601+ </g>
1602+ <g class="mutations" font-style="italic">
1603+ <g text-anchor="start"/>
1604+ <g text-anchor="end"/>
1605+ </g>
1606+ </g>
1607+ </g>
1608+ <g class="tree t1" transform="translate(200.0 15)">
1609+ <g class="edges" fill="none" stroke="black">
1610+ <path class="p4 c1" d="M 50.0 140.0 V 30.0 H 80.0"/>
1611+ <path class="p4 c3" d="M 110.0 100.38696392754377 V 30.0 H 80.0"/>
1612+ <path class="p3 c0" d="M 90.0 140.0 V 100.38696392754377 H 110.0"/>
1613+ <path class="p3 c2" d="M 130.0 140.0 V 100.38696392754377 H 110.0"/>
1614+ </g>
1615+ <g class="symbols">
1616+ <g class="nodes">
1617+ <circle class="n4" cx="80.0" cy="30.0" r="3"/>
1618+ <circle class="n1 sample" cx="50.0" cy="140.0" r="3"/>
1619+ <circle class="n3" cx="110.0" cy="100.38696392754377" r="3"/>
1620+ <circle class="n0 sample" cx="90.0" cy="140.0" r="3"/>
1621+ <circle class="n2 sample" cx="130.0" cy="140.0" r="3"/>
1622+ </g>
1623+ <g class="mutations" fill="red">
1624+ <rect class="m0 s0 n1" height="6" transform="translate(-3 -3)" width="6"
1625+ x="50.0" y="85.0"/>
1626+ </g>
1627+ </g>
1628+ <g class="labels" dominant-baseline="middle" font-size="14">
1629+ <g class="nodes">
1630+ <g text-anchor="start">
1631+ <g transform="translate(115.0, 95.38696392754377)">
1632+ <text class="n3">3</text>
1633+ </g>
1634+ </g>
1635+ <g text-anchor="middle">
1636+ <g transform="translate(80.0, 25.0)"><text class="n4">4</text></g>
1637+ <g transform="translate(50.0, 160.0)"><text class="n1 sample">1</text></g>
1638+ <g transform="translate(90.0, 160.0)"><text class="n0 sample">0</text></g>
1639+ <g transform="translate(130.0, 160.0)"><text class="n2 sample">2</text></g>
1640+ </g>
1641+ <g text-anchor="end"/>
1642+ </g>
1643+ <g class="mutations" font-style="italic">
1644+ <g text-anchor="start"/>
1645+ <g text-anchor="end">
1646+ <g transform="translate(45.0, 89.0)"><text class="m0 s0 n1">0</text></g>
1647+ </g>
1648+ </g>
1649+ </g>
1650+ </g>
1651+ </g>
1652+ <g class="axis">
1653+ <line stroke="black" x1="20" x2="380" y1="180" y2="180"/>
1654+ <line stroke="black" x1="20" x2="20" y1="175" y2="185"/>
1655+ <g transform="translate(20, 200)">
1656+ <text font-size="14" font-weight="bold" text-anchor="middle">0.00</text>
1657+ </g>
1658+ <line stroke="black" x1="200.0" x2="200.0" y1="175" y2="185"/>
1659+ <g transform="translate(200.0, 200)">
1660+ <text font-size="14" font-weight="bold" text-anchor="middle">0.16</text>
1661+ </g>
1662+ <line stroke="black" x1="380.0" x2="380.0" y1="175" y2="185"/>
1663+ <g transform="translate(380.0, 200)">
1664+ <text font-size="14" font-weight="bold" text-anchor="middle">1.00</text>
1665+ </g>
1666+ </g>
1667+ </g>
1668+ </svg>"""
1669+ self .assertXmlEquivalentOutputs (svg , expected_svg )
0 commit comments