Go to the documentation of this file.00001
00002
00003 from __future__ import print_function
00004
00005 import sys
00006 import unittest
00007 import xacro
00008 from xml.dom.minidom import parseString
00009 import xml.dom
00010 import os.path
00011 from rosgraph.names import load_mappings
00012 from xacro import set_substitution_args_context
00013
00014
00015 def all_attributes_match(a, b):
00016 if len(a.attributes) != len(b.attributes):
00017 print("Different number of attributes")
00018 return False
00019 a_atts = [(a.attributes.item(i).name, a.attributes.item(i).value) for i in range(len(a.attributes))]
00020 b_atts = [(b.attributes.item(i).name, b.attributes.item(i).value) for i in range(len(b.attributes))]
00021 a_atts.sort()
00022 b_atts.sort()
00023
00024 for i in range(len(a_atts)):
00025 if a_atts[i][0] != b_atts[i][0]:
00026 print("Different attribute names: %s and %s" % (a_atts[i][0], b_atts[i][0]))
00027 return False
00028 try:
00029 if abs(float(a_atts[i][1]) - float(b_atts[i][1])) > 1.0e-9:
00030 print("Different attribute values: %s and %s" % (a_atts[i][1], b_atts[i][1]))
00031 return False
00032 except ValueError:
00033 if a_atts[i][1] != b_atts[i][1]:
00034 print("Different attribute values: %s and %s" % (a_atts[i][1], b_atts[i][1]))
00035 return False
00036
00037 return True
00038
00039
00040 def elements_match(a, b):
00041 if not a and not b:
00042 return True
00043 if not a or not b:
00044 return False
00045
00046 if a.nodeType != b.nodeType:
00047 print("Different node types: %d and %d" % (a.nodeType, b.nodeType))
00048 return False
00049 if a.nodeName != b.nodeName:
00050 print("Different element names: %s and %s" % (a.nodeName, b.nodeName))
00051 return False
00052
00053 if not all_attributes_match(a, b):
00054 return False
00055
00056 if not elements_match(xacro.first_child_element(a), xacro.first_child_element(b)):
00057 return False
00058 if not elements_match(xacro.next_sibling_element(a), xacro.next_sibling_element(b)):
00059 return False
00060 return True
00061
00062
00063 def xml_matches(a, b):
00064 if isinstance(a, str):
00065 return xml_matches(parseString(a).documentElement, b)
00066 if isinstance(b, str):
00067 return xml_matches(a, parseString(b).documentElement)
00068 if a.nodeType == xml.dom.Node.DOCUMENT_NODE:
00069 return xml_matches(a.documentElement, b)
00070 if b.nodeType == xml.dom.Node.DOCUMENT_NODE:
00071 return xml_matches(a, b.documentElement)
00072
00073 if not elements_match(a, b):
00074 print("Match failed:")
00075 a.writexml(sys.stdout)
00076 print
00077 print('=' * 78)
00078 b.writexml(sys.stdout)
00079 return False
00080 return True
00081
00082
00083 def quick_xacro(xml):
00084 if isinstance(xml, str):
00085 doc = parseString(xml)
00086 return quick_xacro(doc)
00087 xacro.eval_self_contained(xml)
00088 return xml
00089
00090
00091 class TestXacro(unittest.TestCase):
00092
00093 def test_DEPRECATED_should_replace_before_macroexpand(self):
00094 self.assertTrue(
00095 xml_matches(
00096 quick_xacro('''<a>
00097 <macro name="inner" params="*the_block">
00098 <in_the_inner><insert_block name="the_block" /></in_the_inner>
00099 </macro>
00100 <macro name="outer" params="*the_block">
00101 <in_the_outer><inner><insert_block name="the_block" /></inner></in_the_outer>
00102 </macro>
00103 <outer><woot /></outer></a>'''),
00104 '''<a>
00105 <in_the_outer><in_the_inner><woot /></in_the_inner></in_the_outer></a>'''))
00106
00107 def test_should_replace_before_macroexpand(self):
00108 self.assertTrue(
00109 xml_matches(
00110 quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00111 <xacro:macro name="inner" params="*the_block">
00112 <in_the_inner><xacro:insert_block name="the_block" /></in_the_inner>
00113 </xacro:macro>
00114 <xacro:macro name="outer" params="*the_block">
00115 <in_the_outer><xacro:inner><insert_block name="the_block" /></xacro:inner></in_the_outer>
00116 </xacro:macro>
00117 <xacro:outer><woot /></xacro:outer></a>'''),
00118 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00119 <in_the_outer><in_the_inner><woot /></in_the_inner></in_the_outer></a>'''))
00120
00121 def test_property_replacement(self):
00122 self.assertTrue(
00123 xml_matches(
00124 quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00125 <xacro:property name="foo" value="42" />
00126 <the_foo result="${foo}" />
00127 </a>'''),
00128 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00129 <the_foo result="42" />
00130 </a>'''))
00131
00132 def test_math_ignores_spaces(self):
00133 self.assertTrue(
00134 xml_matches(
00135 quick_xacro('''<a><f v="${0.9 / 2 - 0.2}" /></a>'''),
00136 '''<a><f v="0.25" /></a>'''))
00137
00138 def test_substitution_args_find(self):
00139 self.assertTrue(
00140 xml_matches(
00141 quick_xacro('''<a><f v="$(find xacro)/test/test_xacro.py" /></a>'''),
00142 '''<a><f v="''' + os.path.abspath(__file__) + '''" /></a>'''))
00143
00144 def test_substitution_args_arg(self):
00145 set_substitution_args_context(load_mappings(['sub_arg:=my_arg']))
00146 self.assertTrue(
00147 xml_matches(
00148 quick_xacro('''<a><f v="$(arg sub_arg)" /></a>'''),
00149 '''<a><f v="my_arg" /></a>'''))
00150 set_substitution_args_context({})
00151
00152 def test_escaping_dollar_braces(self):
00153 self.assertTrue(
00154 xml_matches(
00155 quick_xacro('''<a b="$${foo}" c="$$${foo}" />'''),
00156 '''<a b="${foo}" c="$${foo}" />'''))
00157
00158 def test_just_a_dollar_sign(self):
00159 self.assertTrue(
00160 xml_matches(
00161 quick_xacro('''<a b="$" />'''),
00162 '''<a b="$" />'''))
00163
00164 def test_multiple_insert_blocks(self):
00165 self.assertTrue(
00166 xml_matches(
00167 quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00168 <xacro:macro name="foo" params="*block">
00169 <xacro:insert_block name="block" />
00170 <xacro:insert_block name="block" />
00171 </xacro:macro>
00172 <xacro:foo>
00173 <a_block />
00174 </xacro:foo>
00175 </a>'''),
00176 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00177 <a_block />
00178 <a_block />
00179 </a>'''))
00180
00181 def test_multiple_blocks(self):
00182 self.assertTrue(
00183 xml_matches(
00184 quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00185 <xacro:macro name="foo" params="*block1 *block2">
00186 <xacro:insert_block name="block2" />
00187 <first>
00188 <xacro:insert_block name="block1" />
00189 </first>
00190 </xacro:macro>
00191 <xacro:foo>
00192 <first_block />
00193 <second_block />
00194 </xacro:foo>
00195 </a>'''),
00196 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00197 <second_block />
00198 <first>
00199 <first_block />
00200 </first>
00201 </a>'''))
00202
00203 def test_integer_stays_integer(self):
00204 self.assertTrue(
00205 xml_matches(
00206 quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00207 <xacro:macro name="m" params="num">
00208 <test number="${num}" />
00209 </xacro:macro>
00210 <xacro:m num="100" />
00211 </a>'''),
00212 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00213 <test number="100" />
00214 </a>'''))
00215
00216 def test_insert_block_property(self):
00217 self.assertTrue(
00218 xml_matches(
00219 quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00220 <xacro:property name="some_block">
00221 <some_block />
00222 </xacro:property>
00223 <foo>
00224 <xacro:insert_block name="some_block" />
00225 </foo>
00226 </a>'''),
00227 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00228 <foo><some_block /></foo>
00229 </a>'''))
00230
00231 def test_include(self):
00232 doc = parseString('''<a xmlns:xacro="http://www.ros.org/xacro">
00233 <xacro:include filename="include1.xml" /></a>''')
00234 xacro.process_includes(doc, os.path.dirname(os.path.realpath(__file__)))
00235 self.assertTrue(
00236 xml_matches(doc, '''<a xmlns:xacro="http://www.ros.org/xacro"><foo /><bar /></a>'''))
00237
00238 def test_include_glob(self):
00239 doc = parseString('''<a xmlns:xacro="http://www.ros.org/xacro">
00240 <xacro:include filename="include*.xml" /></a>''')
00241 xacro.process_includes(doc, os.path.dirname(os.path.realpath(__file__)))
00242 self.assertTrue(
00243 xml_matches(doc, '<a xmlns:xacro="http://www.ros.org/xacro"><foo /><bar /><baz /></a>'))
00244
00245 def test_include_nonexistent(self):
00246 doc = parseString('''<a xmlns:xacro="http://www.ros.org/xacro">
00247 <xacro:include filename="include-nada.xml" /></a>''')
00248 self.assertRaises(xacro.XacroException,
00249 xacro.process_includes, doc, os.path.dirname(os.path.realpath(__file__)))
00250
00251 def test_numeric_if_statement(self):
00252 doc = parseString('''\
00253 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00254 <xacro:if value="${3*0}"/>
00255 <xacro:if value="0"/>
00256 <xacro:if value="1"/>
00257 </robot>''')
00258 quick_xacro(doc)
00259 self.assertTrue(
00260 xml_matches(
00261 quick_xacro('''\
00262 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00263 <xacro:if value="${3*0}">
00264 <a />
00265 </xacro:if>
00266 <xacro:if value="0">
00267 <b />
00268 </xacro:if>
00269 <xacro:if value="1">
00270 <c />
00271 </xacro:if>
00272 </robot>'''),
00273 '''\
00274 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00275 <c />
00276 </robot>'''))