test_xacro.py
Go to the documentation of this file.
00001 #! /usr/bin/env python
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 # regex to match whitespace
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:  # Attribute values aren't numeric
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     # compare text-valued nodes
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     # ignore all other nodes except ELEMENTs
00070     if a.nodeType != xml.dom.Node.ELEMENT_NODE:
00071         return True
00072 
00073     # compare ELEMENT nodes
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         # ignore whitespace-only text nodes
00085         # we could have several text nodes in a row, due to replacements
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 # capture output going to file=sys.stdout | sys.stderr
00126 @contextmanager
00127 def capture_stderr(function, *args, **kwargs):
00128   old, sys.stderr = sys.stderr, StringIO()  # temporarily replace sys.stderr with StringIO()
00129   result = function(*args, **kwargs)
00130   sys.stderr.seek(0)
00131   yield (result, sys.stderr.read())
00132   sys.stderr = old  # restore sys.stderr
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'))     # syntactically correct keyword
00157         self.assertFalse(xacro.is_valid_name('foo '))     # trailing whitespace
00158         self.assertFalse(xacro.is_valid_name(' foo'))     # leading whitespace
00159         self.assertFalse(xacro.is_valid_name('1234'))     # number
00160         self.assertFalse(xacro.is_valid_name('1234abc'))  # number and letters
00161         self.assertFalse(xacro.is_valid_name(''))         # empty string
00162         self.assertFalse(xacro.is_valid_name('   '))      # whitespace only
00163         self.assertFalse(xacro.is_valid_name('foo bar'))  # several tokens
00164         self.assertFalse(xacro.is_valid_name('no-dashed-names-for-you'))
00165         self.assertFalse(xacro.is_valid_name('invalid.too'))  # dot separates fields
00166 
00167     def test_resolve_macro(self):
00168         # define three nested macro dicts with the same macro names (keys)
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 # base class providing some convenience functions
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))  # initialize with cli args
00226         args.update(dict(in_order = self.in_order))  # set in_order option from test class
00227         args.update(kwargs)  # explicit function args have highest priority
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 # class to match XML docs while ignoring any comments
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         # run xacro on the pr2 tree snapshot
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 # standard test class (including the test from TestXacroCommentsIgnored)
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         # for now we only issue a deprecated warning and expect the old behaviour
00285         # resolving macro "call"
00286         res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><a name="bar"/></a>'''
00287         # new behaviour would be to resolve to foo of course
00288         # res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><a name="foo"/></a>'''
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         # test both, reversal and non-reversal of block order
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         # <include> tags with some non-trivial content should not issue the deprecation warning
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         # run xacro on broken input file to make sure we don't create an
00932         # empty output file
00933         tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
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) # clean up after ourselves
00939 
00940         self.assertFalse(output_file_created)
00941 
00942     def test_create_subdirs(self):
00943         # run xacro to create output file in non-existent directory
00944         # to make sure this directory will be created by xacro
00945         tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
00946         shutil.rmtree(tmp_dir_name) # ensure directory is removed
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) # clean up after ourselves
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         # If a property is assigned from a substitution arg, then this properties' value was
00996         # no longer converted to a python type, so that e.g. 0.5 remained u'0.5'.
00997         # If this property is then used in a numerical expression an exception is thrown.
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 # test class for in-order processing
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)  # foo should be reported
01135             self.assertTrue("bar" not in output)  # bar shouldn't be reported
01136 
01137 
01138 if __name__ == '__main__':
01139     unittest.main()


xacro
Author(s): Stuart Glaser, William Woodall, Robert Haschke
autogenerated on Fri Jun 24 2016 04:05:50