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 import tempfile
00012 import shutil
00013 import subprocess
00014 import re
00015 from cStringIO import StringIO
00016 from contextlib import contextmanager
00017
00018
00019
00020 whitespace = re.compile(r'\s+')
00021
00022 def all_attributes_match(a, b):
00023 if len(a.attributes) != len(b.attributes):
00024 print("Different number of attributes")
00025 return False
00026 a_atts = [(a.attributes.item(i).name, a.attributes.item(i).value) for i in range(len(a.attributes))]
00027 b_atts = [(b.attributes.item(i).name, b.attributes.item(i).value) for i in range(len(b.attributes))]
00028 a_atts.sort()
00029 b_atts.sort()
00030
00031 for i in range(len(a_atts)):
00032 if a_atts[i][0] != b_atts[i][0]:
00033 print("Different attribute names: %s and %s" % (a_atts[i][0], b_atts[i][0]))
00034 return False
00035 try:
00036 if abs(float(a_atts[i][1]) - float(b_atts[i][1])) > 1.0e-9:
00037 print("Different attribute values: %s and %s" % (a_atts[i][1], b_atts[i][1]))
00038 return False
00039 except ValueError:
00040 if a_atts[i][1] != b_atts[i][1]:
00041 print("Different attribute values: %s and %s" % (a_atts[i][1], b_atts[i][1]))
00042 return False
00043
00044 return True
00045
00046 def text_matches(a, b):
00047 a_norm = whitespace.sub(' ', a)
00048 b_norm = whitespace.sub(' ', b)
00049 if a_norm.strip() == b_norm.strip(): return True
00050 print("Different text values: '%s' and '%s'" % (a, b))
00051 return False
00052
00053 def nodes_match(a, b, ignore_nodes):
00054 if not a and not b:
00055 return True
00056 if not a or not b:
00057 return False
00058
00059 if a.nodeType != b.nodeType:
00060 print("Different node types: %s and %s" % (a, b))
00061 return False
00062
00063
00064 if a.nodeType in [xml.dom.Node.TEXT_NODE,
00065 xml.dom.Node.CDATA_SECTION_NODE,
00066 xml.dom.Node.COMMENT_NODE]:
00067 return text_matches(a.data, b.data)
00068
00069
00070 if a.nodeType != xml.dom.Node.ELEMENT_NODE:
00071 return True
00072
00073
00074 if a.nodeName != b.nodeName:
00075 print("Different element names: %s and %s" % (a.nodeName, b.nodeName))
00076 return False
00077
00078 if not all_attributes_match(a, b):
00079 return False
00080
00081 a = a.firstChild
00082 b = b.firstChild
00083 while a or b:
00084
00085
00086 while (a and
00087 ((a.nodeType in ignore_nodes) or
00088 (a.nodeType == xml.dom.Node.TEXT_NODE and whitespace.sub('', a.data) == ""))):
00089 a = a.nextSibling
00090 while (b and
00091 ((b.nodeType in ignore_nodes) or
00092 (b.nodeType == xml.dom.Node.TEXT_NODE and whitespace.sub('', b.data) == ""))):
00093 b = b.nextSibling
00094
00095 if not nodes_match(a, b, ignore_nodes):
00096 return False
00097
00098 if a: a = a.nextSibling
00099 if b: b = b.nextSibling
00100
00101 return True
00102
00103
00104 def xml_matches(a, b, ignore_nodes=[]):
00105 if isinstance(a, str):
00106 return xml_matches(parseString(a).documentElement, b, ignore_nodes)
00107 if isinstance(b, str):
00108 return xml_matches(a, parseString(b).documentElement, ignore_nodes)
00109 if a.nodeType == xml.dom.Node.DOCUMENT_NODE:
00110 return xml_matches(a.documentElement, b, ignore_nodes)
00111 if b.nodeType == xml.dom.Node.DOCUMENT_NODE:
00112 return xml_matches(a, b.documentElement, ignore_nodes)
00113
00114 if not nodes_match(a, b, ignore_nodes):
00115 print("Match failed:")
00116 a.writexml(sys.stdout)
00117 print()
00118 print('=' * 78)
00119 b.writexml(sys.stdout)
00120 print()
00121 return False
00122 return True
00123
00124
00125
00126 @contextmanager
00127 def capture_stderr(function, *args, **kwargs):
00128 old, sys.stderr = sys.stderr, StringIO()
00129 result = function(*args, **kwargs)
00130 sys.stderr.seek(0)
00131 yield (result, sys.stderr.read())
00132 sys.stderr = old
00133
00134
00135 class TestMatchXML(unittest.TestCase):
00136 def test_normalize_whitespace_text(self):
00137 self.assertTrue(text_matches("", " \t\n\r"))
00138 def test_normalize_whitespace_trim(self):
00139 self.assertTrue(text_matches(" foo bar ", "foo \t\n\r bar"))
00140
00141 def test_empty_node_vs_whitespace(self):
00142 self.assertTrue(xml_matches('''<foo/>''', '''<foo> \t\n\r </foo>'''))
00143 def test_whitespace_vs_empty_node(self):
00144 self.assertTrue(xml_matches('''<foo> \t\n\r </foo>''', '''<foo/>'''))
00145 def test_normalize_whitespace_nested(self):
00146 self.assertTrue(xml_matches('''<a><b/></a>''', '''<a>\n<b> </b> </a>'''))
00147
00148 def test_ignore_comments(self):
00149 self.assertTrue(xml_matches('''<a><b/><!-- foo --> <!-- bar --></a>''',
00150 '''<a><b/></a>''', [xml.dom.Node.COMMENT_NODE]))
00151
00152
00153 class TestXacroFunctions(unittest.TestCase):
00154 def test_is_valid_name(self):
00155 self.assertTrue(xacro.is_valid_name("_valid_name_123"))
00156 self.assertFalse(xacro.is_valid_name('pass'))
00157 self.assertFalse(xacro.is_valid_name('foo '))
00158 self.assertFalse(xacro.is_valid_name(' foo'))
00159 self.assertFalse(xacro.is_valid_name('1234'))
00160 self.assertFalse(xacro.is_valid_name('1234abc'))
00161 self.assertFalse(xacro.is_valid_name(''))
00162 self.assertFalse(xacro.is_valid_name(' '))
00163 self.assertFalse(xacro.is_valid_name('foo bar'))
00164 self.assertFalse(xacro.is_valid_name('no-dashed-names-for-you'))
00165 self.assertFalse(xacro.is_valid_name('invalid.too'))
00166
00167 def test_resolve_macro(self):
00168
00169 content = {'xacro:simple': 'simple'}
00170 ns2 = dict({k: v+'2' for k,v in content.iteritems()})
00171 ns1 = dict({k: v+'1' for k,v in content.iteritems()})
00172 ns1.update(ns2=ns2)
00173 macros = dict(content)
00174 macros.update(ns1=ns1)
00175
00176 self.assertEqual(xacro.resolve_macro('simple', macros), 'simple')
00177 self.assertEqual(xacro.resolve_macro('ns1.simple', macros), 'simple1')
00178 self.assertEqual(xacro.resolve_macro('ns1.ns2.simple', macros), 'simple2')
00179
00180 self.assertEqual(xacro.resolve_macro('xacro:simple', macros), 'simple')
00181 self.assertEqual(xacro.resolve_macro('xacro:ns1.simple', macros), 'simple1')
00182 self.assertEqual(xacro.resolve_macro('xacro:ns1.ns2.simple', macros), 'simple2')
00183
00184 def check_macro_arg(self, s, param, forward, default, rest):
00185 p, v, r = xacro.parse_macro_arg(s)
00186 self.assertEqual(p, param, msg="'{0}' != '{1}' parsing {2}".format(p, param, s))
00187 if forward or default:
00188 self.assertTrue(v is not None)
00189 self.assertEqual(v[0], forward, msg="'{0}' != '{1}' parsing {2}".format(v[0], forward, s))
00190 self.assertEqual(v[1], default, msg="'{0}' != '{1}' parsing {2}".format(v[1], default, s))
00191 else:
00192 self.assertTrue(v is None)
00193 self.assertEqual(r, rest, msg="'{0}' != '{1}' parsing {2}".format(r, rest, s))
00194
00195 def test_parse_macro_arg(self):
00196 for forward in ['', '^', '^|']:
00197 defaults = ['', "f('some string','some other')", "f('a b')"]
00198 if forward == '^': defaults = ['']
00199 for default in defaults:
00200 seps = ['=', ':='] if forward or default else ['']
00201 for sep in seps:
00202 for rest in ['', ' ', ' bar', ' bar=42']:
00203 s = 'foo{0}{1}{2}{3}'.format(sep, forward, default, rest)
00204 self.check_macro_arg(s, 'foo', 'foo' if forward else None,
00205 default if default else None,
00206 rest.lstrip())
00207 def test_parse_macro_whitespace(self):
00208 for ws in [' ', ' \t ', ' \n ']:
00209 self.check_macro_arg(ws + 'foo' + ws + 'bar=42' + ws, 'foo', None, None, 'bar=42' + ws)
00210
00211
00212 class TestXacroBase(unittest.TestCase):
00213 def __init__(self, *args, **kwargs):
00214 super(TestXacroBase, self).__init__(*args, **kwargs)
00215 self.in_order = False
00216 self.ignore_nodes = []
00217
00218 def assert_matches(self, a, b):
00219 self.assertTrue(xml_matches(a, b, self.ignore_nodes))
00220
00221 def quick_xacro(self, xml, cli=None, **kwargs):
00222 args = {}
00223 if cli:
00224 opts, _ = xacro.cli.process_args(cli, require_input=False)
00225 args.update(vars(opts))
00226 args.update(dict(in_order = self.in_order))
00227 args.update(kwargs)
00228
00229 doc = xacro.parse(xml)
00230 xacro.process_doc(doc, **args)
00231 return doc
00232
00233 def run_xacro(self, input_path, *args):
00234 args = list(args)
00235 if self.in_order:
00236 args.append('--inorder')
00237 test_dir = os.path.abspath(os.path.dirname(__file__))
00238 xacro_path = os.path.join(test_dir, '..', 'scripts', 'xacro')
00239 subprocess.call([xacro_path, input_path] + args)
00240
00241
00242
00243 class TestXacroCommentsIgnored(TestXacroBase):
00244 def __init__(self, *args, **kwargs):
00245 super(TestXacroCommentsIgnored, self).__init__(*args, **kwargs)
00246 self.ignore_nodes = [xml.dom.Node.COMMENT_NODE]
00247
00248 def test_pr2(self):
00249
00250 test_dir= os.path.abspath(os.path.dirname(__file__))
00251 pr2_xacro_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2.urdf.xacro')
00252 pr2_golden_parse_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2_1.11.4.xml')
00253 self.assert_matches(
00254 xml.dom.minidom.parse(pr2_golden_parse_path),
00255 self.quick_xacro(open(pr2_xacro_path)))
00256
00257
00258
00259 class TestXacro(TestXacroCommentsIgnored):
00260 def __init__(self, *args, **kwargs):
00261 super(TestXacroCommentsIgnored, self).__init__(*args, **kwargs)
00262 self.ignore_nodes = []
00263
00264 def test_invalid_property_name(self):
00265 src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00266 <xacro:property name="invalid.name"/></a>'''
00267 self.assertRaises(xacro.XacroException, self.quick_xacro, src)
00268
00269 def test_dynamic_macro_names(self):
00270 src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00271 <xacro:macro name="foo"><a>foo</a></xacro:macro>
00272 <xacro:macro name="bar"><b>bar</b></xacro:macro>
00273 <xacro:property name="var" value="%s"/>
00274 <xacro:call macro="${var}"/></a>'''
00275 res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">%s</a>'''
00276 self.assert_matches(self.quick_xacro(src % "foo"), res % "<a>foo</a>")
00277 self.assert_matches(self.quick_xacro(src % "bar"), res % "<b>bar</b>")
00278
00279 def test_dynamic_macro_name_clash(self):
00280 src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00281 <xacro:macro name="foo"><a name="foo"/></xacro:macro>
00282 <xacro:macro name="call"><a name="bar"/></xacro:macro>
00283 <xacro:call/></a>'''
00284
00285
00286 res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><a name="bar"/></a>'''
00287
00288
00289 with capture_stderr(self.quick_xacro, src) as (result, output):
00290 self.assert_matches(result, res)
00291 self.assertTrue("deprecated use of macro name 'call'" in output)
00292
00293 def test_dynamic_macro_undefined(self):
00294 self.assertRaises(xacro.XacroException,
00295 self.quick_xacro,
00296 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00297 <xacro:call macro="foo"/></a>''')
00298
00299 def test_macro_undefined(self):
00300 self.assertRaises(xacro.XacroException,
00301 self.quick_xacro,
00302 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00303 <xacro:undefined><foo/><bar/></xacro:undefined></a>''')
00304
00305 def test_xacro_element(self):
00306 src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00307 <xacro:macro name="foo" params="name"><xacro:element xacro:name="${name}"/></xacro:macro>
00308 <xacro:foo name="A"/>
00309 <xacro:foo name="B"/>
00310 </a>'''
00311 res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><A/><B/></a>'''
00312 self.assert_matches(self.quick_xacro(src), res)
00313
00314 def test_xacro_attribute(self):
00315 src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00316 <xacro:macro name="foo" params="name value">
00317 <tag><xacro:attribute name="${name}" value="${value}"/></tag>
00318 </xacro:macro>
00319 <xacro:foo name="A" value="foo"/>
00320 <xacro:foo name="B" value="bar"/>
00321 </a>'''
00322 res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><tag A="foo"/><tag B="bar"/></a>'''
00323 self.assert_matches(self.quick_xacro(src), res)
00324
00325 def test_inorder_processing(self):
00326 src = '''<xml xmlns:xacro="http://www.ros.org/wiki/xacro">
00327 <xacro:property name="foo" value="1.0"/>
00328 <xacro:macro name="m" params="foo"><a foo="${foo}"/></xacro:macro>
00329 <xacro:m foo="1 ${foo}"/>
00330 <!-- now redefining the property and macro -->
00331 <xacro:property name="foo" value="2.0"/>
00332 <xacro:macro name="m" params="foo"><b bar="${foo}"/></xacro:macro>
00333 <xacro:m foo="2 ${foo}"/>
00334 </xml>'''
00335 oldOrder = '''
00336 <xml xmlns:xacro="http://www.ros.org/wiki/xacro">
00337 <b bar="1 2.0"/>
00338 <b bar="2 2.0"/>
00339 </xml>
00340 '''
00341 inOrder = '''
00342 <xml xmlns:xacro="http://www.ros.org/wiki/xacro">
00343 <a foo="1 1.0"/>
00344 <b bar="2 2.0"/>
00345 </xml>
00346 '''
00347 self.assert_matches(self.quick_xacro(src), inOrder if self.in_order else oldOrder)
00348
00349 def test_should_replace_before_macroexpand(self):
00350 self.assert_matches(
00351 self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00352 <xacro:macro name="inner" params="*the_block">
00353 <in_the_inner><xacro:insert_block name="the_block" /></in_the_inner>
00354 </xacro:macro>
00355 <xacro:macro name="outer" params="*the_block">
00356 <in_the_outer><xacro:inner><xacro:insert_block name="the_block" /></xacro:inner></in_the_outer>
00357 </xacro:macro>
00358 <xacro:outer><woot /></xacro:outer></a>'''),
00359 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00360 <in_the_outer><in_the_inner><woot /></in_the_inner></in_the_outer></a>''')
00361
00362 def test_evaluate_macro_params_before_body(self):
00363 self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00364 <xacro:macro name="foo" params="lst">${lst[-1]}</xacro:macro>
00365 <foo lst="${[1,2,3]}"/></a>'''),
00366 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">3</a>''')
00367
00368 def test_property_replacement(self):
00369 self.assert_matches(
00370 self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00371 <xacro:property name="foo" value="42" />
00372 <the_foo result="${foo}" />
00373 </a>'''),
00374 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00375 <the_foo result="42" />
00376 </a>''')
00377
00378 def test_property_scope_parent(self):
00379 self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00380 <xacro:macro name="foo" params="factor">
00381 <xacro:property name="foo" value="${21*factor}" scope="parent"/>
00382 </xacro:macro>
00383 <xacro:foo factor="2"/><a foo="${foo}"/></a>'''),
00384 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><a foo="42"/></a>''')
00385
00386 def test_property_scope_global(self):
00387 self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00388 <xacro:macro name="foo" params="factor">
00389 <xacro:macro name="bar">
00390 <xacro:property name="foo" value="${21*factor}" scope="global"/>
00391 </xacro:macro>
00392 <xacro:bar/>
00393 </xacro:macro>
00394 <xacro:foo factor="2"/><a foo="${foo}"/></a>'''),
00395 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><a foo="42"/></a>''')
00396
00397 def test_math_ignores_spaces(self):
00398 self.assert_matches(
00399 self.quick_xacro('''<a><f v="${0.9 / 2 - 0.2}" /></a>'''),
00400 '''<a><f v="0.25" /></a>''')
00401
00402 def test_substitution_args_find(self):
00403 self.assert_matches(
00404 self.quick_xacro('''<a><f v="$(find xacro)/test/test_xacro.py" /></a>'''),
00405 '''<a><f v="''' + os.path.abspath((__file__).replace(".pyc",".py") + '''" /></a>'''))
00406
00407 def test_substitution_args_arg(self):
00408 self.assert_matches(
00409 self.quick_xacro('''<a><f v="$(arg sub_arg)" /></a>''', cli=['sub_arg:=my_arg']),
00410 '''<a><f v="my_arg" /></a>''')
00411
00412 def test_escaping_dollar_braces(self):
00413 self.assert_matches(
00414 self.quick_xacro('''<a b="$${foo}" c="$$${foo}" />'''),
00415 '''<a b="${foo}" c="$${foo}" />''')
00416
00417 def test_just_a_dollar_sign(self):
00418 self.assert_matches(
00419 self.quick_xacro('''<a b="$" />'''),
00420 '''<a b="$" />''')
00421
00422 def test_multiple_insert_blocks(self):
00423 self.assert_matches(
00424 self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00425 <xacro:macro name="foo" params="*block">
00426 <xacro:insert_block name="block" />
00427 <xacro:insert_block name="block" />
00428 </xacro:macro>
00429 <xacro:foo>
00430 <a_block />
00431 </xacro:foo>
00432 </a>'''),
00433 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00434 <a_block />
00435 <a_block />
00436 </a>''')
00437
00438 def test_multiple_blocks(self):
00439 src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00440 <xacro:macro name="foo" params="*block{A} *block{B}">
00441 <xacro:insert_block name="block1" />
00442 <xacro:insert_block name="block2" />
00443 </xacro:macro>
00444 <xacro:foo>
00445 <block1/>
00446 <block2/>
00447 </xacro:foo>
00448 </a>'''
00449 res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00450 <block{A}/>
00451 <block{B}/>
00452 </a>'''
00453
00454 for d in [dict(A='1', B='2'), dict(A='2', B='1')]:
00455 self.assert_matches(self.quick_xacro(src.format(**d)), res.format(**d))
00456
00457 def test_integer_stays_integer(self):
00458 self.assert_matches(
00459 self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00460 <xacro:macro name="m" params="num">
00461 <test number="${num}" />
00462 </xacro:macro>
00463 <xacro:m num="100" />
00464 </a>'''),
00465 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00466 <test number="100" />
00467 </a>''')
00468
00469 def test_insert_block_property(self):
00470 self.assert_matches(
00471 self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00472 <xacro:macro name="bar">bar</xacro:macro>
00473 <xacro:property name="val" value="2" />
00474 <xacro:property name="some_block">
00475 <some_block attr="${val}"><xacro:bar/></some_block>
00476 </xacro:property>
00477 <foo>
00478 <xacro:insert_block name="some_block" />
00479 </foo>
00480 </a>'''),
00481 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00482 <foo><some_block attr="2">bar</some_block></foo>
00483 </a>''')
00484
00485 def test_include(self):
00486 self.assert_matches(self.quick_xacro('''\
00487 <a xmlns:xacro="http://www.ros.org/xacro">
00488 <xacro:include filename="include1.xml" /></a>'''),
00489 '''<a xmlns:xacro="http://www.ros.org/xacro"><inc1/></a>''')
00490
00491 def test_include_glob(self):
00492 input = '''<a xmlns:xacro="http://www.ros.org/xacro">
00493 <xacro:include filename="include{glob}.xml"/></a>'''
00494 result = '<a xmlns:xacro="http://www.ros.org/xacro"><inc1/><inc2/></a>'
00495 for pattern in ['*', '?', '[1-2]']:
00496 self.assert_matches(self.quick_xacro(input.format(glob=pattern)), result)
00497
00498 def test_include_nonexistent(self):
00499 self.assertRaises(xacro.XacroException,
00500 self.quick_xacro, '''<a xmlns:xacro="http://www.ros.org/xacro">
00501 <xacro:include filename="include-nada.xml" /></a>''')
00502
00503 def test_include_deprecated(self):
00504
00505 src = '''<a><include filename="nada"><tag/></include></a>'''
00506 with capture_stderr(self.quick_xacro, src) as (result, output):
00507 self.assert_matches(result, src)
00508 self.assertEqual(output, '')
00509
00510 def test_include_from_variable(self):
00511 doc = '''<a xmlns:xacro="http://www.ros.org/xacro">
00512 <xacro:property name="file" value="include1.xml"/>
00513 <xacro:include filename="${file}" /></a>'''
00514 if self.in_order:
00515 self.assert_matches(self.quick_xacro(doc),
00516 '''<a xmlns:xacro="http://www.ros.org/xacro"><inc1/></a>''')
00517 else:
00518 self.assertRaises(xacro.XacroException, self.quick_xacro, doc)
00519
00520 def test_include_recursive(self):
00521 self.assert_matches(self.quick_xacro('''\
00522 <a xmlns:xacro="http://www.ros.org/xacro">
00523 <xacro:include filename="include1.xml"/>
00524 <xacro:include filename="./include1.xml"/>
00525 <xacro:include filename="subdir/include-recursive.xacro"/>
00526 </a>'''),
00527 '''<a xmlns:xacro="http://www.ros.org/xacro">
00528 <inc1/><inc1/>
00529 <subdir_inc1/><subdir_inc1/><inc1/></a>''')
00530
00531 def test_include_with_namespace(self):
00532 doc = '''
00533 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00534 <xacro:property name="var" value="main"/>
00535 <xacro:include filename="include1.xacro" ns="A"/>
00536 <xacro:include filename="include2.xacro" ns="B"/>
00537 <A.foo/><B.foo/>
00538 <main var="${var}" A="${2*A.var}" B="${B.var+1}"/>
00539 </a>'''
00540 result = '''
00541 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00542 <inc1/><inc2/><main var="main" A="2" B="3"/>
00543 </a>'''
00544
00545 if self.in_order:
00546 self.assert_matches(self.quick_xacro(doc), result)
00547 else:
00548 self.assertRaises(xacro.XacroException, self.quick_xacro, doc)
00549
00550 def test_boolean_if_statement(self):
00551 self.assert_matches(
00552 self.quick_xacro('''\
00553 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00554 <xacro:if value="false">
00555 <a />
00556 </xacro:if>
00557 <xacro:if value="true">
00558 <b />
00559 </xacro:if>
00560 </robot>'''),
00561 '''\
00562 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00563 <b />
00564 </robot>''')
00565
00566 def test_invalid_if_statement(self):
00567 self.assertRaises(xacro.XacroException,
00568 self.quick_xacro,
00569 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00570 <xacro:if value="nonsense"><foo/></xacro:if></a>''')
00571
00572 def test_integer_if_statement(self):
00573 self.assert_matches(
00574 self.quick_xacro('''\
00575 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00576 <xacro:if value="${0*42}">
00577 <a />
00578 </xacro:if>
00579 <xacro:if value="0">
00580 <b />
00581 </xacro:if>
00582 <xacro:if value="${0}">
00583 <c />
00584 </xacro:if>
00585 <xacro:if value="${1*2+3}">
00586 <d />
00587 </xacro:if>
00588 </robot>'''),
00589 '''\
00590 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00591 <d />
00592 </robot>''')
00593
00594 def test_float_if_statement(self):
00595 self.assert_matches(
00596 self.quick_xacro('''\
00597 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00598 <xacro:if value="${3*0.0}">
00599 <a />
00600 </xacro:if>
00601 <xacro:if value="${3*0.1}">
00602 <b />
00603 </xacro:if>
00604 </robot>'''),
00605 '''\
00606 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00607 <b />
00608 </robot>''')
00609
00610 def test_consecutive_if(self):
00611 self.assert_matches(self.quick_xacro('''
00612 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00613 <xacro:if value="1"><xacro:if value="0"><a>bar</a></xacro:if></xacro:if>
00614 </a>'''),
00615 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"/>''')
00616
00617 def test_equality_expression_in_if_statement(self):
00618 self.assert_matches(self.quick_xacro('''
00619 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00620 <xacro:property name="var" value="useit"/>
00621 <xacro:if value="${var == 'useit'}"><foo>bar</foo></xacro:if>
00622 <xacro:if value="${'use' in var}"><bar>foo</bar></xacro:if>
00623 </a>'''),
00624 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00625 <foo>bar</foo>
00626 <bar>foo</bar>
00627 </a>''')
00628
00629 def test_no_evaluation(self):
00630 self.assert_matches(self.quick_xacro('''
00631 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00632 <xacro:property name="xyz" value="5 -2"/>
00633 <foo>${xyz}</foo>
00634 </a>'''),
00635 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00636 <foo>5 -2</foo>
00637 </a>''')
00638
00639 def test_math_expressions(self):
00640 self.assert_matches(self.quick_xacro('''
00641 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00642 <foo function="${1. + sin(pi)}"/>
00643 </a>'''),
00644 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00645 <foo function="1.0"/>
00646 </a>''')
00647
00648 def test_consider_non_elements_if(self):
00649 self.assert_matches(self.quick_xacro('''
00650 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00651 <xacro:if value="1"><!-- comment --> text <b>bar</b></xacro:if>
00652 </a>'''),
00653 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00654 <!-- comment --> text <b>bar</b></a>''')
00655
00656 def test_consider_non_elements_block(self):
00657 self.assert_matches(
00658 self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00659 <xacro:macro name="foo" params="*block">
00660 <!-- comment -->
00661 foo
00662 <xacro:insert_block name="block" />
00663 </xacro:macro>
00664 <xacro:foo>
00665 <!-- ignored comment -->
00666 ignored text
00667 <a_block />
00668 </xacro:foo>
00669 </a>'''),
00670 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00671 <!-- comment -->
00672 foo
00673 <a_block />
00674 </a>''')
00675
00676 def test_ignore_xacro_comments(self):
00677 self.assert_matches(self.quick_xacro('''
00678 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00679 <!-- A -->
00680
00681 <!-- ignore multiline comments before any xacro tag -->
00682 <!-- ignored -->
00683 <xacro:property name="foo" value="1"/>
00684 <!-- ignored -->
00685 <xacro:if value="1"><!-- B --></xacro:if>
00686 <!-- ignored -->
00687 <xacro:macro name="foo"><!-- C --></xacro:macro>
00688 <!-- ignored -->
00689 <xacro:foo/>
00690 </a>'''),
00691 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00692 <!-- A --><!-- B --><!-- C --></a>''')
00693
00694 def test_recursive_evaluation(self):
00695 self.assert_matches(
00696 self.quick_xacro('''\
00697 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00698 <xacro:property name="a" value=" 42 "/>
00699 <xacro:property name="a2" value="${ 2 * a }"/>
00700 <a doubled="${a2}"/>
00701 </robot>'''),
00702 '''\
00703 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00704 <a doubled="84"/>
00705 </robot>''')
00706
00707 def test_recursive_evaluation_wrong_order(self):
00708 self.assert_matches(
00709 self.quick_xacro('''\
00710 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00711 <xacro:property name="a2" value="${2*a}"/>
00712 <xacro:property name="a" value="42"/>
00713 <a doubled="${a2}"/>
00714 </robot>'''),
00715 '''\
00716 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00717 <a doubled="84"/>
00718 </robot>''')
00719
00720 def test_recursive_definition(self):
00721 self.assertRaises(xacro.XacroException,
00722 self.quick_xacro, '''\
00723 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00724 <xacro:property name="a" value="${a2}"/>
00725 <xacro:property name="a2" value="${2*a}"/>
00726 <a doubled="${a2}"/>
00727 </robot>''')
00728
00729 def test_multiple_recursive_evaluation(self):
00730 self.assert_matches(
00731 self.quick_xacro('''\
00732 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00733 <xacro:property name="a" value="1"/>
00734 <xacro:property name="b" value="2"/>
00735 <xacro:property name="c" value="3"/>
00736 <xacro:property name="product" value="${a*b*c}"/>
00737 <answer product="${product}"/>
00738 </robot>'''),
00739 '''\
00740 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00741 <answer product="6"/>
00742 </robot>''')
00743
00744 def test_multiple_definition_and_evaluation(self):
00745 self.assert_matches(
00746 self.quick_xacro('''\
00747 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00748 <xacro:property name="a" value="42"/>
00749 <xacro:property name="b" value="${a}"/>
00750 <xacro:property name="b" value="${-a}"/>
00751 <xacro:property name="b" value="${a}"/>
00752 <answer b="${b} ${b} ${b}"/>
00753 </robot>'''),
00754 '''\
00755 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00756 <answer b="42 42 42"/>
00757 </robot>''')
00758
00759 def test_transitive_evaluation(self):
00760 self.assert_matches(
00761 self.quick_xacro('''\
00762 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00763 <xacro:property name="a" value="42"/>
00764 <xacro:property name="b" value="${a}"/>
00765 <xacro:property name="c" value="${b}"/>
00766 <xacro:property name="d" value="${c}"/>
00767 <answer d="${d}"/>
00768 </robot>'''),
00769 '''\
00770 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00771 <answer d="42"/>
00772 </robot>''')
00773
00774 def test_multi_tree_evaluation(self):
00775 self.assert_matches(
00776 self.quick_xacro('''\
00777 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00778 <xacro:property name="a" value="42"/>
00779 <xacro:property name="b" value="2.1"/>
00780 <xacro:property name="c" value="${a}"/>
00781 <xacro:property name="d" value="${b}"/>
00782 <xacro:property name="f" value="${c*d}"/>
00783 <answer f="${f}"/>
00784 </robot>'''),
00785 '''\
00786 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00787 <answer f="88.2"/>
00788 </robot>''')
00789
00790 def test_from_issue(self):
00791 self.assert_matches(
00792 self.quick_xacro('''\
00793 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00794 <xacro:property name="x" value="42"/>
00795 <xacro:property name="wheel_width" value="${x}"/>
00796 <link name="my_link">
00797 <origin xyz="0 0 ${wheel_width/2}"/>
00798 </link>
00799 </robot>'''),
00800 '''\
00801 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00802 <link name="my_link">
00803 <origin xyz="0 0 21.0"/>
00804 </link>
00805 </robot>''')
00806
00807 def test_recursive_bad_math(self):
00808 self.assertRaises(xacro.XacroException,
00809 self.quick_xacro, '''\
00810 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00811 <xacro:property name="x" value="0"/>
00812 <tag badness="${1/x}"/>
00813 </robot>''')
00814
00815 def test_default_param(self):
00816 self.assert_matches(
00817 self.quick_xacro('''\
00818 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00819 <xacro:macro name="fixed_link" params="parent_link:=base_link child_link *joint_pose">
00820 <link name="${child_link}"/>
00821 <joint name="${child_link}_joint" type="fixed">
00822 <xacro:insert_block name="joint_pose" />
00823 <parent link="${parent_link}"/>
00824 <child link="${child_link}" />
00825 </joint>
00826 </xacro:macro>
00827 <xacro:fixed_link child_link="foo">
00828 <origin xyz="0 0 0" rpy="0 0 0" />
00829 </xacro:fixed_link >
00830 </robot>'''),
00831 '''\
00832 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00833 <link name="foo"/>
00834 <joint name="foo_joint" type="fixed">
00835 <origin rpy="0 0 0" xyz="0 0 0"/>
00836 <parent link="base_link"/>
00837 <child link="foo"/>
00838 </joint>
00839 </robot>''')
00840
00841 def test_default_param_override(self):
00842 self.assert_matches(
00843 self.quick_xacro('''\
00844 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00845 <xacro:macro name="fixed_link" params="parent_link:=base_link child_link *joint_pose">
00846 <link name="${child_link}"/>
00847 <joint name="${child_link}_joint" type="fixed">
00848 <xacro:insert_block name="joint_pose" />
00849 <parent link="${parent_link}"/>
00850 <child link="${child_link}" />
00851 </joint>
00852 </xacro:macro>
00853 <xacro:fixed_link child_link="foo" parent_link="bar">
00854 <origin xyz="0 0 0" rpy="0 0 0" />
00855 </xacro:fixed_link >
00856 </robot>'''),
00857 '''\
00858 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00859 <link name="foo"/>
00860 <joint name="foo_joint" type="fixed">
00861 <origin rpy="0 0 0" xyz="0 0 0"/>
00862 <parent link="bar"/>
00863 <child link="foo"/>
00864 </joint>
00865 </robot>''')
00866
00867 def test_param_missing(self):
00868 self.assertRaises(xacro.XacroException,
00869 self.quick_xacro, '''\
00870 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00871 <xacro:macro name="fixed_link" params="parent_link child_link *joint_pose">
00872 <link name="${child_link}"/>
00873 <joint name="${child_link}_joint" type="fixed">
00874 <xacro:insert_block name="joint_pose" />
00875 <parent link="${parent_link}"/>
00876 <child link="${child_link}" />
00877 </joint>
00878 </xacro:macro>
00879 <xacro:fixed_link child_link="foo">
00880 <origin xyz="0 0 0" rpy="0 0 0" />
00881 </xacro:fixed_link >
00882 </robot>''')
00883
00884 def test_default_arg(self):
00885 self.assert_matches(
00886 self.quick_xacro('''\
00887 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00888 <xacro:arg name="foo" default="2"/>
00889 <link name="my_link">
00890 <origin xyz="0 0 $(arg foo)"/>
00891 </link>
00892 </robot>
00893 '''),'''\
00894 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00895 <link name="my_link">
00896 <origin xyz="0 0 2"/>
00897 </link>
00898 </robot>''')
00899
00900 def test_default_arg_override(self):
00901 self.assert_matches(
00902 self.quick_xacro('''\
00903 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00904 <xacro:arg name="foo" default="2"/>
00905 <link name="my_link">
00906 <origin xyz="0 0 $(arg foo)"/>
00907 </link>
00908 </robot>
00909 ''', ['foo:=4']),'''\
00910 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
00911 <link name="my_link">
00912 <origin xyz="0 0 4"/>
00913 </link>
00914 </robot>''')
00915
00916 def test_default_arg_missing(self):
00917 self.assertRaises(Exception,
00918 self.quick_xacro, '''\
00919 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00920 <a arg="$(arg foo)"/>
00921 </a>
00922 ''')
00923
00924 def test_default_arg_empty(self):
00925 self.assert_matches(self.quick_xacro('''
00926 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00927 <xacro:arg name="foo" default=""/>$(arg foo)</a>'''),
00928 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"/>''')
00929
00930 def test_broken_input_doesnt_create_empty_output_file(self):
00931
00932
00933 tmp_dir_name = tempfile.mkdtemp()
00934 output_path = os.path.join(tmp_dir_name, "should_not_exist")
00935 self.run_xacro('broken.xacro', '-o', output_path)
00936
00937 output_file_created = os.path.isfile(output_path)
00938 shutil.rmtree(tmp_dir_name)
00939
00940 self.assertFalse(output_file_created)
00941
00942 def test_create_subdirs(self):
00943
00944
00945 tmp_dir_name = tempfile.mkdtemp()
00946 shutil.rmtree(tmp_dir_name)
00947 output_path = os.path.join(tmp_dir_name, "out")
00948 self.run_xacro('include1.xml', '-o', output_path)
00949
00950 output_file_created = os.path.isfile(output_path)
00951 shutil.rmtree(tmp_dir_name)
00952
00953 self.assertTrue(output_file_created)
00954
00955 def test_iterable_literals_plain(self):
00956 self.assert_matches(
00957 self.quick_xacro('''\
00958 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00959 <xacro:property name="list" value="[0, 1+1, 2]"/>
00960 <xacro:property name="tuple" value="(0,1+1,2)"/>
00961 <xacro:property name="dict" value="{'a':0, 'b':1+1, 'c':2}"/>
00962 <a list="${list}" tuple="${tuple}" dict="${dict}"/>
00963 </a>'''),
00964 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00965 <a list="[0, 1+1, 2]" tuple="(0,1+1,2)" dict="{'a':0, 'b':1+1, 'c':2}"/>
00966 </a>''')
00967
00968 def test_iterable_literals_eval(self):
00969 self.assert_matches(
00970 self.quick_xacro('''\
00971 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00972 <xacro:property name="list" value="${[0, 1+1, 2]}"/>
00973 <xacro:property name="tuple" value="${(0,1+1,2)}"/>
00974 <xacro:property name="dic" value="${dict(a=0, b=1+1, c=2)}"/>
00975 <a list="${list}" tuple="${tuple}" dict="${dic}"/>
00976 </a>'''),
00977 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
00978 <a list="[0, 2, 2]" tuple="(0, 2, 2)" dict="{'a': 0, 'c': 2, 'b': 2}"/>
00979 </a>''')
00980
00981 def test_enforce_xacro_ns(self):
00982 self.assert_matches(
00983 self.quick_xacro('''\
00984 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00985 <arg name="foo" value="bar"/>
00986 <include filename="foo"/>
00987 </a>''', xacro_ns=False),
00988 '''\
00989 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
00990 <arg name="foo" value="bar"/>
00991 <include filename="foo"/>
00992 </a>''')
00993
00994 def test_issue_68_numeric_arg(self):
00995
00996
00997
00998 self.assert_matches(
00999 self.quick_xacro('''\
01000 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
01001 <xacro:arg name="foo" default="0.5"/>
01002 <xacro:property name="prop" value="$(arg foo)" />
01003 <a prop="${prop-0.3}"/>
01004 </a>
01005 '''),'''\
01006 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
01007 <a prop="0.2"/>
01008 </a>''')
01009
01010 def test_transitive_arg_evaluation(self):
01011 self.assert_matches(
01012 self.quick_xacro('''\
01013 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
01014 <xacro:arg name="foo" default="0.5"/>
01015 <xacro:arg name="bar" default="$(arg foo)"/>
01016 <xacro:property name="prop" value="$(arg bar)" />
01017 <a prop="${prop-0.3}"/>
01018 </a>
01019 '''),'''\
01020 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
01021 <a prop="0.2"/>
01022 </a>''')
01023
01024 def test_macro_name_with_colon(self):
01025 src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
01026 <xacro:macro name="xacro:my_macro"><foo/></xacro:macro>
01027 <xacro:my_macro/>
01028 </a>'''
01029 res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><foo/></a>'''
01030 self.assert_matches(self.quick_xacro(src), res)
01031
01032 def test_overwrite_globals(self):
01033 src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
01034 <xacro:property name="pi" value="3.14"/></a>'''
01035 with capture_stderr(self.quick_xacro, src) as (result, output):
01036 self.assert_matches(result, '<a xmlns:xacro="http://www.ros.org/wiki/xacro"/>')
01037 self.assertTrue(output)
01038
01039 def test_no_double_evaluation(self):
01040 src = '''
01041 <a xmlns:xacro="http://www.ros.org/xacro">
01042 <xacro:macro name="foo" params="a b:=${a} c:=$${a}"> a=${a} b=${b} c=${c} </xacro:macro>
01043 <xacro:property name="a" value="1"/>
01044 <xacro:property name="d" value="$${a}"/>
01045 <d d="${d}"><foo a="2"/></d>
01046 </a>'''
01047 res = '''<a xmlns:xacro="http://www.ros.org/xacro"><d d="${a}"> a=2 b=1 c=${a} </d></a>'''
01048 self.assert_matches(self.quick_xacro(src), res)
01049
01050 def test_property_forwarding(self):
01051 src='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
01052 <xacro:property name="arg" value="42"/>
01053 <xacro:macro name="foo" params="arg:=^%s">${arg}</xacro:macro>
01054 <xacro:foo/>
01055 </a>'''
01056 res='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">%s</a>'''
01057 self.assert_matches(self.quick_xacro(src % ''), res % '42')
01058 self.assert_matches(self.quick_xacro(src % '|'), res % '42')
01059 self.assert_matches(self.quick_xacro(src % '|6'), res % '42')
01060
01061 def test_extension_in_expression(self):
01062 src='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${2*'$(arg var)'}</a>'''
01063 res='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">%s</a>'''
01064 self.assert_matches(self.quick_xacro(src, ['var:=xacro']), res % (2*'xacro'))
01065
01066 def test_expression_in_extension(self):
01067 src='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">$(arg ${'v'+'ar'})</a>'''
01068 res='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">%s</a>'''
01069 self.assert_matches(self.quick_xacro(src, ['var:=xacro']), res % 'xacro')
01070
01071
01072 class TestXacroInorder(TestXacro):
01073 def __init__(self, *args, **kwargs):
01074 super(TestXacroInorder, self).__init__(*args, **kwargs)
01075 self.in_order = True
01076
01077 def test_include_lazy(self):
01078 doc = ('''<a xmlns:xacro="http://www.ros.org/xacro">
01079 <xacro:if value="false"><xacro:include filename="non-existent"/></xacro:if></a>''')
01080 self.assert_matches(self.quick_xacro(doc),
01081 '''<a xmlns:xacro="http://www.ros.org/xacro"/>''')
01082
01083 def test_issue_63_fixed_with_inorder_processing(self):
01084 self.assert_matches(
01085 self.quick_xacro('''\
01086 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
01087 <xacro:arg name="has_stuff" default="false"/>
01088 <xacro:if value="$(arg has_stuff)">
01089 <xacro:include file="$(find nonexistent_package)/stuff.urdf" />
01090 </xacro:if>
01091 </a>'''),
01092 '<a xmlns:xacro="http://www.ros.org/wiki/xacro"/>')
01093
01094 def test_yaml_support(self):
01095 src = '''
01096 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
01097 <xacro:property name="settings" value="${load_yaml('settings.yaml')}"/>
01098 <xacro:property name="type" value="$(arg type)"/>
01099 <xacro:include filename="${settings['arms'][type]['file']}"/>
01100 <xacro:call macro="${settings['arms'][type]['macro']}"/>
01101 </a>'''
01102 res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><{tag}/></a>'''
01103 for i in ['inc1', 'inc2']:
01104 self.assert_matches(self.quick_xacro(src, cli=['type:=%s' % i]),
01105 res.format(tag=i))
01106
01107 def test_macro_default_param_evaluation_order(self):
01108 src='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
01109 <xacro:macro name="foo" params="arg:=${2*foo}">
01110 <xacro:property name="foo" value="-"/>
01111 <f val="${arg}"/>
01112 </xacro:macro>
01113 <xacro:property name="foo" value="${3*7}"/>
01114 <xacro:foo/>
01115 <xacro:property name="foo" value="*"/>
01116 <xacro:foo/>
01117 </a>'''
01118 res='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
01119 <f val="42"/><f val="**"/></a>'''
01120 self.assert_matches(self.quick_xacro(src), res)
01121
01122 def test_check_order_warning(self):
01123 src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
01124 <xacro:property name="bar" value="unused"/>
01125 <xacro:property name="foo" value="unused"/>
01126 <xacro:macro name="foo" params="arg:=${foo}">
01127 <a val="${arg}"/>
01128 </xacro:macro>
01129 <xacro:foo/>
01130 <xacro:property name="bar" value="dummy"/>
01131 <xacro:property name="foo" value="21"/></a>'''
01132 with capture_stderr(self.quick_xacro, src, do_check_order=True) as (result, output):
01133 self.assertTrue("Document is incompatible to --inorder processing." in output)
01134 self.assertTrue("foo" in output)
01135 self.assertTrue("bar" not in output)
01136
01137
01138 if __name__ == '__main__':
01139 unittest.main()