test_xacro.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 from __future__ import print_function
5 
6 import sys
7 import unittest
8 import xacro
9 from xml.dom.minidom import parseString
10 import xml.dom
11 import os.path
12 import tempfile
13 import shutil
14 import subprocess
15 import re
16 import ast
17 try:
18  from cStringIO import StringIO # Python 2.x
19 except ImportError:
20  from io import StringIO # Python 3.x
21 from contextlib import contextmanager
22 
23 
24 # regex to match whitespace
25 whitespace = re.compile(r'\s+')
26 
28  # generic comparison
29  if whitespace.sub(' ', a).strip() == whitespace.sub(' ', b).strip():
30  return True
31 
32  try: # special handling of dicts: ignore order
33  a_dict = ast.literal_eval(a)
34  b_dict = ast.literal_eval(b)
35  if (isinstance(a_dict, dict) and isinstance(b_dict, dict) and a_dict == b_dict):
36  return True
37  except: # Attribute values aren't dicts
38  pass
39 
40  # on failure, try to split a and b at whitespace and compare snippets
41  def match_splits(a_, b_):
42  if len(a_) != len(b_): return False
43  for a, b in zip(a_, b_):
44  if a == b: continue
45  try: # compare numeric values only up to some accuracy
46  if abs(float(a) - float(b)) > 1.0e-9:
47  return False
48  except ValueError: # values aren't numeric and not identical
49  return False
50  return True
51 
52  return match_splits(a.split(), b.split())
53 
54 
56  if len(a.attributes) != len(b.attributes):
57  print("Different number of attributes")
58  return False
59  a_atts = a.attributes.items()
60  b_atts = b.attributes.items()
61  a_atts.sort()
62  b_atts.sort()
63 
64  for a, b in zip(a_atts, b_atts):
65  if a[0] != b[0]:
66  print("Different attribute names: %s and %s" % (a[0], b[0]))
67  return False
68  if not text_values_match(a[1], b[1]):
69  print("Different attribute values: %s and %s" % (a[1], b[1]))
70  return False
71  return True
72 
73 
74 def text_matches(a, b):
75  if text_values_match(a, b): return True
76  print("Different text values: '%s' and '%s'" % (a, b))
77  return False
78 
79 
80 def nodes_match(a, b, ignore_nodes):
81  if not a and not b:
82  return True
83  if not a or not b:
84  return False
85 
86  if a.nodeType != b.nodeType:
87  print("Different node types: %s and %s" % (a, b))
88  return False
89 
90  # compare text-valued nodes
91  if a.nodeType in [xml.dom.Node.TEXT_NODE,
92  xml.dom.Node.CDATA_SECTION_NODE,
93  xml.dom.Node.COMMENT_NODE]:
94  return text_matches(a.data, b.data)
95 
96  # ignore all other nodes except ELEMENTs
97  if a.nodeType != xml.dom.Node.ELEMENT_NODE:
98  return True
99 
100  # compare ELEMENT nodes
101  if a.nodeName != b.nodeName:
102  print("Different element names: %s and %s" % (a.nodeName, b.nodeName))
103  return False
104 
105  if not all_attributes_match(a, b):
106  return False
107 
108  a = a.firstChild
109  b = b.firstChild
110  while a or b:
111  # ignore whitespace-only text nodes
112  # we could have several text nodes in a row, due to replacements
113  while (a and
114  ((a.nodeType in ignore_nodes) or
115  (a.nodeType == xml.dom.Node.TEXT_NODE and whitespace.sub('', a.data) == ""))):
116  a = a.nextSibling
117  while (b and
118  ((b.nodeType in ignore_nodes) or
119  (b.nodeType == xml.dom.Node.TEXT_NODE and whitespace.sub('', b.data) == ""))):
120  b = b.nextSibling
121 
122  if not nodes_match(a, b, ignore_nodes):
123  return False
124 
125  if a: a = a.nextSibling
126  if b: b = b.nextSibling
127 
128  return True
129 
130 
131 def xml_matches(a, b, ignore_nodes=[]):
132  if isinstance(a, str):
133  return xml_matches(parseString(a).documentElement, b, ignore_nodes)
134  if isinstance(b, str):
135  return xml_matches(a, parseString(b).documentElement, ignore_nodes)
136  if a.nodeType == xml.dom.Node.DOCUMENT_NODE:
137  return xml_matches(a.documentElement, b, ignore_nodes)
138  if b.nodeType == xml.dom.Node.DOCUMENT_NODE:
139  return xml_matches(a, b.documentElement, ignore_nodes)
140 
141  if not nodes_match(a, b, ignore_nodes):
142  print("Match failed:")
143  a.writexml(sys.stdout)
144  print()
145  print('=' * 78)
146  b.writexml(sys.stdout)
147  print()
148  return False
149  return True
150 
151 
152 # capture output going to file=sys.stdout | sys.stderr
153 @contextmanager
154 def capture_stderr(function, *args, **kwargs):
155  old, sys.stderr = sys.stderr, StringIO() # temporarily replace sys.stderr with StringIO()
156  result = function(*args, **kwargs)
157  sys.stderr.seek(0)
158  yield (result, sys.stderr.read())
159  sys.stderr = old # restore sys.stderr
160 
161 
162 class TestMatchXML(unittest.TestCase):
164  self.assertTrue(text_matches("", " \t\n\r"))
166  self.assertTrue(text_matches(" foo bar ", "foo \t\n\r bar"))
167 
169  self.assertTrue(text_matches("0.123456789", "0.123456788"))
171  self.assertFalse(text_matches("0.123456789", "0.1234567879"))
172 
174  self.assertTrue(text_matches("{'a': 1, 'b': 2, 'c': 3}", "{'c': 3, 'b': 2, 'a': 1}"))
176  self.assertFalse(text_matches("{'a': 1, 'b': 2, 'c': 3}", "{'c': 3, 'b': 2, 'a': 0}"))
177 
179  self.assertTrue(xml_matches('''<foo/>''', '''<foo> \t\n\r </foo>'''))
181  self.assertTrue(xml_matches('''<foo> \t\n\r </foo>''', '''<foo/>'''))
183  self.assertTrue(xml_matches('''<a><b/></a>''', '''<a>\n<b> </b> </a>'''))
184 
186  self.assertTrue(xml_matches('''<a><b/><!-- foo --> <!-- bar --></a>''',
187  '''<a><b/></a>''', [xml.dom.Node.COMMENT_NODE]))
188 
189 
190 class TestXacroFunctions(unittest.TestCase):
192  self.assertTrue(xacro.is_valid_name("_valid_name_123"))
193  self.assertFalse(xacro.is_valid_name('pass')) # syntactically correct keyword
194  self.assertFalse(xacro.is_valid_name('foo ')) # trailing whitespace
195  self.assertFalse(xacro.is_valid_name(' foo')) # leading whitespace
196  self.assertFalse(xacro.is_valid_name('1234')) # number
197  self.assertFalse(xacro.is_valid_name('1234abc')) # number and letters
198  self.assertFalse(xacro.is_valid_name('')) # empty string
199  self.assertFalse(xacro.is_valid_name(' ')) # whitespace only
200  self.assertFalse(xacro.is_valid_name('foo bar')) # several tokens
201  self.assertFalse(xacro.is_valid_name('no-dashed-names-for-you'))
202  self.assertFalse(xacro.is_valid_name('invalid.too')) # dot separates fields
203 
205  # define three nested macro dicts with the same macro names (keys)
206  content = {'xacro:simple': 'simple'}
207  ns2 = dict({k: v+'2' for k,v in content.items()})
208  ns1 = dict({k: v+'1' for k,v in content.items()})
209  ns1.update(ns2=ns2)
210  macros = dict(content)
211  macros.update(ns1=ns1)
212 
213  self.assertEqual(xacro.resolve_macro('simple', macros), 'simple')
214  self.assertEqual(xacro.resolve_macro('ns1.simple', macros), 'simple1')
215  self.assertEqual(xacro.resolve_macro('ns1.ns2.simple', macros), 'simple2')
216 
217  self.assertEqual(xacro.resolve_macro('xacro:simple', macros), 'simple')
218  self.assertEqual(xacro.resolve_macro('xacro:ns1.simple', macros), 'simple1')
219  self.assertEqual(xacro.resolve_macro('xacro:ns1.ns2.simple', macros), 'simple2')
220 
221  def check_macro_arg(self, s, param, forward, default, rest):
222  p, v, r = xacro.parse_macro_arg(s)
223  self.assertEqual(p, param, msg="'{0}' != '{1}' parsing {2}".format(p, param, s))
224  if forward or default:
225  self.assertTrue(v is not None)
226  self.assertEqual(v[0], forward, msg="'{0}' != '{1}' parsing {2}".format(v[0], forward, s))
227  self.assertEqual(v[1], default, msg="'{0}' != '{1}' parsing {2}".format(v[1], default, s))
228  else:
229  self.assertTrue(v is None)
230  self.assertEqual(r, rest, msg="'{0}' != '{1}' parsing {2}".format(r, rest, s))
231 
233  for forward in ['', '^', '^|']:
234  defaults = ['', "f('some string','some other')", "f('a b')"]
235  if forward == '^': defaults = ['']
236  for default in defaults:
237  seps = ['=', ':='] if forward or default else ['']
238  for sep in seps:
239  for rest in ['', ' ', ' bar', ' bar=42']:
240  s = 'foo{0}{1}{2}{3}'.format(sep, forward, default, rest)
241  self.check_macro_arg(s, 'foo', 'foo' if forward else None,
242  default if default else None,
243  rest.lstrip())
245  for ws in [' ', ' \t ', ' \n ']:
246  self.check_macro_arg(ws + 'foo' + ws + 'bar=42' + ws, 'foo', None, None, 'bar=42' + ws)
247 
248 # base class providing some convenience functions
249 class TestXacroBase(unittest.TestCase):
250  def __init__(self, *args, **kwargs):
251  super(TestXacroBase, self).__init__(*args, **kwargs)
252  self.in_order = False
253  self.ignore_nodes = []
254 
255  def assert_matches(self, a, b):
256  self.assertTrue(xml_matches(a, b, self.ignore_nodes))
257 
258  def quick_xacro(self, xml, cli=None, **kwargs):
259  args = {}
260  if cli:
261  opts, _ = xacro.cli.process_args(cli, require_input=False)
262  args.update(vars(opts)) # initialize with cli args
263  args.update(dict(in_order = self.in_order)) # set in_order option from test class
264  args.update(kwargs) # explicit function args have highest priority
265 
266  doc = xacro.parse(xml)
267  xacro.process_doc(doc, **args)
268  return doc
269 
270  def run_xacro(self, input_path, *args):
271  args = list(args)
272  if self.in_order:
273  args.append('--inorder')
274  test_dir = os.path.abspath(os.path.dirname(__file__))
275  xacro_path = os.path.join(test_dir, '..', 'scripts', 'xacro')
276  subprocess.call([xacro_path, input_path] + args)
277 
278 
279 # class to match XML docs while ignoring any comments
281  def __init__(self, *args, **kwargs):
282  super(TestXacroCommentsIgnored, self).__init__(*args, **kwargs)
283  self.ignore_nodes = [xml.dom.Node.COMMENT_NODE]
284 
285  def test_pr2(self):
286  # run xacro on the pr2 tree snapshot
287  test_dir= os.path.abspath(os.path.dirname(__file__))
288  pr2_xacro_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2.urdf.xacro')
289  pr2_golden_parse_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2_1.11.4.xml')
290  self.assert_matches(
291  xml.dom.minidom.parse(pr2_golden_parse_path),
292  self.quick_xacro(open(pr2_xacro_path)))
293 
294 
295 # standard test class (including the test from TestXacroCommentsIgnored)
297  def __init__(self, *args, **kwargs):
298  super(TestXacroCommentsIgnored, self).__init__(*args, **kwargs)
299  self.ignore_nodes = []
300 
302  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
303  <xacro:property name="invalid.name"/></a>'''
304  self.assertRaises(xacro.XacroException, self.quick_xacro, src)
305 
307  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
308  <xacro:macro name="foo"><a>foo</a></xacro:macro>
309  <xacro:macro name="bar"><b>bar</b></xacro:macro>
310  <xacro:property name="var" value="%s"/>
311  <xacro:call macro="${var}"/></a>'''
312  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">%s</a>'''
313  self.assert_matches(self.quick_xacro(src % "foo"), res % "<a>foo</a>")
314  self.assert_matches(self.quick_xacro(src % "bar"), res % "<b>bar</b>")
315 
317  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
318  <xacro:macro name="foo"><a name="foo"/></xacro:macro>
319  <xacro:macro name="call"><a name="bar"/></xacro:macro>
320  <xacro:call/></a>'''
321  # for now we only issue a deprecated warning and expect the old behaviour
322  # resolving macro "call"
323  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><a name="bar"/></a>'''
324  # new behaviour would be to resolve to foo of course
325  # res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><a name="foo"/></a>'''
326  with capture_stderr(self.quick_xacro, src) as (result, output):
327  self.assert_matches(result, res)
328  self.assertTrue("deprecated use of macro name 'call'" in output)
329 
331  self.assertRaises(xacro.XacroException,
332  self.quick_xacro,
333  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
334  <xacro:call macro="foo"/></a>''')
335 
337  self.assertRaises(xacro.XacroException,
338  self.quick_xacro,
339  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
340  <xacro:undefined><foo/><bar/></xacro:undefined></a>''')
341 
343  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
344  <xacro:macro name="foo" params="name"><xacro:element xacro:name="${name}"/></xacro:macro>
345  <xacro:foo name="A"/>
346  <xacro:foo name="B"/>
347 </a>'''
348  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><A/><B/></a>'''
349  self.assert_matches(self.quick_xacro(src), res)
350 
352  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
353  <xacro:macro name="foo" params="name value">
354  <tag><xacro:attribute name="${name}" value="${value}"/></tag>
355  </xacro:macro>
356  <xacro:foo name="A" value="foo"/>
357  <xacro:foo name="B" value="bar"/>
358 </a>'''
359  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><tag A="foo"/><tag B="bar"/></a>'''
360  self.assert_matches(self.quick_xacro(src), res)
361 
363  src = '''<xml xmlns:xacro="http://www.ros.org/wiki/xacro">
364  <xacro:property name="foo" value="1.0"/>
365  <xacro:macro name="m" params="foo"><a foo="${foo}"/></xacro:macro>
366  <xacro:m foo="1 ${foo}"/>
367  <!-- now redefining the property and macro -->
368  <xacro:property name="foo" value="2.0"/>
369  <xacro:macro name="m" params="foo"><b bar="${foo}"/></xacro:macro>
370  <xacro:m foo="2 ${foo}"/>
371 </xml>'''
372  oldOrder = '''
373 <xml xmlns:xacro="http://www.ros.org/wiki/xacro">
374  <b bar="1 2.0"/>
375  <b bar="2 2.0"/>
376 </xml>
377 '''
378  inOrder = '''
379 <xml xmlns:xacro="http://www.ros.org/wiki/xacro">
380  <a foo="1 1.0"/>
381  <b bar="2 2.0"/>
382 </xml>
383 '''
384  self.assert_matches(self.quick_xacro(src), inOrder if self.in_order else oldOrder)
385 
387  self.assert_matches(
388  self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
389 <xacro:macro name="inner" params="*the_block">
390  <in_the_inner><xacro:insert_block name="the_block" /></in_the_inner>
391 </xacro:macro>
392 <xacro:macro name="outer" params="*the_block">
393  <in_the_outer><xacro:inner><xacro:insert_block name="the_block" /></xacro:inner></in_the_outer>
394 </xacro:macro>
395 <xacro:outer><woot /></xacro:outer></a>'''),
396  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
397 <in_the_outer><in_the_inner><woot /></in_the_inner></in_the_outer></a>''')
398 
400  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
401  <xacro:macro name="foo" params="lst">${lst[-1]}</xacro:macro>
402  <foo lst="${[1,2,3]}"/></a>'''),
403  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">3</a>''')
404 
406  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
407  <xacro:macro name="foo" params="a='1 -2' c=3"><bar a="${a}" c="${c}"/></xacro:macro>
408  <xacro:foo/></a>'''),
409  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><bar a="1 -2" c="3"/></a>''')
410 
412  self.assert_matches(
413  self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
414  <xacro:property name="foo" value="42" />
415  <the_foo result="${foo}" />
416 </a>'''),
417  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
418  <the_foo result="42" />
419 </a>''')
420 
422  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
423  <xacro:macro name="foo" params="factor">
424  <xacro:property name="foo" value="${21*factor}" scope="parent"/>
425  </xacro:macro>
426  <xacro:foo factor="2"/><a foo="${foo}"/></a>'''),
427  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><a foo="42"/></a>''')
428 
430  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
431  <xacro:macro name="foo" params="factor">
432  <xacro:macro name="bar">
433  <xacro:property name="foo" value="${21*factor}" scope="global"/>
434  </xacro:macro>
435  <xacro:bar/>
436  </xacro:macro>
437  <xacro:foo factor="2"/><a foo="${foo}"/></a>'''),
438  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><a foo="42"/></a>''')
439 
441  self.assert_matches(
442  self.quick_xacro('''<a><f v="${0.9 / 2 - 0.2}" /></a>'''),
443  '''<a><f v="0.25" /></a>''')
444 
446  self.assert_matches(
447  self.quick_xacro('''<a><f v="$(find xacro)/test/test_xacro.py" /></a>'''),
448  '''<a><f v="''' + os.path.abspath((__file__).replace(".pyc",".py") + '''" /></a>'''))
449 
451  self.assert_matches(
452  self.quick_xacro('''<a><f v="$(arg sub_arg)" /></a>''', cli=['sub_arg:=my_arg']),
453  '''<a><f v="my_arg" /></a>''')
454 
456  self.assert_matches(
457  self.quick_xacro('''<a b="$${foo}" c="$$${foo}" d="text $${foo}" e="text $$${foo}" f="$$(pwd)" />'''),
458  '''<a b="${foo}" c="$${foo}" d="text ${foo}" e="text $${foo}" f="$(pwd)" />''')
459 
461  self.assert_matches(
462  self.quick_xacro('''<a b="$" c="text $" d="text $ text"/>'''),
463  '''<a b="$" c="text $" d="text $ text"/>''')
464 
466  self.assert_matches(
467  self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
468 <xacro:macro name="foo" params="*block">
469  <xacro:insert_block name="block" />
470  <xacro:insert_block name="block" />
471 </xacro:macro>
472 <xacro:foo>
473  <a_block />
474 </xacro:foo>
475 </a>'''),
476  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
477  <a_block />
478  <a_block />
479 </a>''')
480 
482  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
483 <xacro:macro name="foo" params="*block{A} *block{B}">
484  <xacro:insert_block name="block1" />
485  <xacro:insert_block name="block2" />
486 </xacro:macro>
487 <xacro:foo>
488  <block1/>
489  <block2/>
490 </xacro:foo>
491 </a>'''
492  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
493 <block{A}/>
494 <block{B}/>
495 </a>'''
496  # test both, reversal and non-reversal of block order
497  for d in [dict(A='1', B='2'), dict(A='2', B='1')]:
498  self.assert_matches(self.quick_xacro(src.format(**d)), res.format(**d))
499 
501  self.assert_matches(
502  self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
503 <xacro:macro name="m" params="num">
504  <test number="${num}" />
505 </xacro:macro>
506 <xacro:m num="100" />
507 </a>'''),
508  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
509  <test number="100" />
510 </a>''')
511 
513  self.assert_matches(
514  self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
515 <xacro:macro name="bar">bar</xacro:macro>
516 <xacro:property name="val" value="2" />
517 <xacro:property name="some_block">
518  <some_block attr="${val}"><xacro:bar/></some_block>
519 </xacro:property>
520 <foo>
521  <xacro:insert_block name="some_block" />
522 </foo>
523 </a>'''),
524  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
525 <foo><some_block attr="2">bar</some_block></foo>
526 </a>''')
527 
528  def test_include(self):
529  self.assert_matches(self.quick_xacro('''\
530 <a xmlns:xacro="http://www.ros.org/xacro">
531  <xacro:include filename="include1.xml" /></a>'''),
532  '''<a xmlns:xacro="http://www.ros.org/xacro"><inc1/></a>''')
533 
534  def test_include_glob(self):
535  input = '''<a xmlns:xacro="http://www.ros.org/xacro">
536  <xacro:include filename="include{glob}.xml"/></a>'''
537  result = '<a xmlns:xacro="http://www.ros.org/xacro"><inc1/><inc2/></a>'
538  for pattern in ['*', '?', '[1-2]']:
539  self.assert_matches(self.quick_xacro(input.format(glob=pattern)), result)
540 
542  self.assertRaises(xacro.XacroException,
543  self.quick_xacro, '''<a xmlns:xacro="http://www.ros.org/xacro">
544  <xacro:include filename="include-nada.xml" /></a>''')
545 
547  # <include> tags with some non-trivial content should not issue the deprecation warning
548  src = '''<a><include filename="nada"><tag/></include></a>'''
549  with capture_stderr(self.quick_xacro, src) as (result, output):
550  self.assert_matches(result, src)
551  self.assertEqual(output, '')
552 
554  doc = '''<a xmlns:xacro="http://www.ros.org/xacro">
555  <xacro:property name="file" value="include1.xml"/>
556  <xacro:include filename="${file}" /></a>'''
557  if self.in_order:
558  self.assert_matches(self.quick_xacro(doc),
559  '''<a xmlns:xacro="http://www.ros.org/xacro"><inc1/></a>''')
560  else:
561  self.assertRaises(xacro.XacroException, self.quick_xacro, doc)
562 
564  self.assert_matches(self.quick_xacro('''\
565 <a xmlns:xacro="http://www.ros.org/xacro">
566  <xacro:include filename="include1.xml"/>
567  <xacro:include filename="./include1.xml"/>
568  <xacro:include filename="subdir/include-recursive.xacro"/>
569 </a>'''),
570 '''<a xmlns:xacro="http://www.ros.org/xacro">
571 <inc1/><inc1/>
572 <subdir_inc1/><subdir_inc1/><inc1/></a>''')
573 
575  doc = '''
576 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
577  <xacro:property name="var" value="main"/>
578  <xacro:include filename="include1.xacro" ns="A"/>
579  <xacro:include filename="include2.xacro" ns="B"/>
580  <A.foo/><B.foo/>
581  <main var="${var}" A="${2*A.var}" B="${B.var+1}"/>
582 </a>'''
583  result = '''
584 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
585  <inc1/><inc2/><main var="main" A="2" B="3"/>
586 </a>'''
587 
588  if self.in_order:
589  self.assert_matches(self.quick_xacro(doc), result)
590  else:
591  self.assertRaises(xacro.XacroException, self.quick_xacro, doc)
592 
594  self.assert_matches(
595  self.quick_xacro('''\
596 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
597  <xacro:if value="false">
598  <a />
599  </xacro:if>
600  <xacro:if value="true">
601  <b />
602  </xacro:if>
603 </robot>'''),
604  '''\
605 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
606  <b />
607 </robot>''')
608 
610  self.assertRaises(xacro.XacroException,
611  self.quick_xacro,
612  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
613  <xacro:if value="nonsense"><foo/></xacro:if></a>''')
614 
616  self.assert_matches(
617  self.quick_xacro('''\
618 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
619  <xacro:if value="${0*42}">
620  <a />
621  </xacro:if>
622  <xacro:if value="0">
623  <b />
624  </xacro:if>
625  <xacro:if value="${0}">
626  <c />
627  </xacro:if>
628  <xacro:if value="${1*2+3}">
629  <d />
630  </xacro:if>
631 </robot>'''),
632  '''\
633 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
634  <d />
635 </robot>''')
636 
638  self.assert_matches(
639  self.quick_xacro('''\
640 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
641  <xacro:if value="${3*0.0}">
642  <a />
643  </xacro:if>
644  <xacro:if value="${3*0.1}">
645  <b />
646  </xacro:if>
647 </robot>'''),
648  '''\
649 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
650  <b />
651 </robot>''')
652 
654  self.assert_matches(
655  self.quick_xacro('''\
656 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
657  <xacro:property name="condT" value="${True}"/>
658  <xacro:property name="condF" value="${False}"/>
659  <xacro:if value="${condF}"><a /></xacro:if>
660  <xacro:if value="${condT}"><b /></xacro:if>
661  <xacro:if value="${True}"><c /></xacro:if>
662 </robot>'''),
663  '''\
664 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
665  <b /><c />
666 </robot>''')
667 
669  self.assert_matches(self.quick_xacro('''
670 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
671  <xacro:if value="1"><xacro:if value="0"><a>bar</a></xacro:if></xacro:if>
672 </a>'''),
673 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"/>''')
674 
676  self.assert_matches(self.quick_xacro('''
677 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
678  <xacro:property name="var" value="useit"/>
679  <xacro:if value="${var == 'useit'}"><foo>bar</foo></xacro:if>
680  <xacro:if value="${'use' in var}"><bar>foo</bar></xacro:if>
681 </a>'''),
682 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
683 <foo>bar</foo>
684 <bar>foo</bar>
685 </a>''')
686 
688  self.assert_matches(self.quick_xacro('''
689 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
690  <xacro:property name="xyz" value="5 -2"/>
691  <foo>${xyz}</foo>
692 </a>'''),
693 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
694  <foo>5 -2</foo>
695 </a>''')
696 
698  self.assert_matches(self.quick_xacro('''
699 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
700  <foo function="${1. + sin(pi)}"/>
701 </a>'''),
702 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
703  <foo function="1.0"/>
704 </a>''')
705 
707  self.assert_matches(self.quick_xacro('''
708 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
709  <xacro:if value="1"><!-- comment --> text <b>bar</b></xacro:if>
710 </a>'''),
711 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
712 <!-- comment --> text <b>bar</b></a>''')
713 
715  self.assert_matches(
716  self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
717 <xacro:macro name="foo" params="*block">
718  <!-- comment -->
719  foo
720  <xacro:insert_block name="block" />
721 </xacro:macro>
722 <xacro:foo>
723  <!-- ignored comment -->
724  ignored text
725  <a_block />
726 </xacro:foo>
727 </a>'''),
728  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
729  <!-- comment -->
730  foo
731  <a_block />
732 </a>''')
733 
735  self.assert_matches(self.quick_xacro('''
736 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
737  <!-- A -->
738 
739  <!-- ignore multiline comments before any xacro tag -->
740  <!-- ignored -->
741  <xacro:property name="foo" value="1"/>
742  <!-- ignored -->
743  <xacro:if value="1"><!-- B --></xacro:if>
744  <!-- ignored -->
745  <xacro:macro name="foo"><!-- C --></xacro:macro>
746  <!-- ignored -->
747  <xacro:foo/>
748 </a>'''),
749 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
750 <!-- A --><!-- B --><!-- C --></a>''')
751 
753  self.assert_matches(
754  self.quick_xacro('''\
755 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
756  <xacro:property name="a" value=" 42 "/>
757  <xacro:property name="a2" value="${ 2 * a }"/>
758  <a doubled="${a2}"/>
759 </robot>'''),
760  '''\
761 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
762  <a doubled="84"/>
763 </robot>''')
764 
766  self.assert_matches(
767  self.quick_xacro('''\
768 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
769  <xacro:property name="a2" value="${2*a}"/>
770  <xacro:property name="a" value="42"/>
771  <a doubled="${a2}"/>
772 </robot>'''),
773  '''\
774 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
775  <a doubled="84"/>
776 </robot>''')
777 
779  self.assertRaises(xacro.XacroException,
780  self.quick_xacro, '''\
781 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
782  <xacro:property name="a" value="${a2}"/>
783  <xacro:property name="a2" value="${2*a}"/>
784  <a doubled="${a2}"/>
785 </robot>''')
786 
788  self.assert_matches(
789  self.quick_xacro('''\
790 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
791  <xacro:property name="a" value="1"/>
792  <xacro:property name="b" value="2"/>
793  <xacro:property name="c" value="3"/>
794  <xacro:property name="product" value="${a*b*c}"/>
795  <answer product="${product}"/>
796 </robot>'''),
797  '''\
798 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
799  <answer product="6"/>
800 </robot>''')
801 
803  self.assert_matches(
804  self.quick_xacro('''\
805 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
806  <xacro:property name="a" value="42"/>
807  <xacro:property name="b" value="${a}"/>
808  <xacro:property name="b" value="${-a}"/>
809  <xacro:property name="b" value="${a}"/>
810  <answer b="${b} ${b} ${b}"/>
811 </robot>'''),
812  '''\
813 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
814  <answer b="42 42 42"/>
815 </robot>''')
816 
818  self.assert_matches(
819  self.quick_xacro('''\
820 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
821  <xacro:property name="a" value="42"/>
822  <xacro:property name="b" value="${a}"/>
823  <xacro:property name="c" value="${b}"/>
824  <xacro:property name="d" value="${c}"/>
825  <answer d="${d}"/>
826 </robot>'''),
827  '''\
828 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
829  <answer d="42"/>
830 </robot>''')
831 
833  self.assert_matches(
834  self.quick_xacro('''\
835 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
836  <xacro:property name="a" value="42"/>
837  <xacro:property name="b" value="2.1"/>
838  <xacro:property name="c" value="${a}"/>
839  <xacro:property name="d" value="${b}"/>
840  <xacro:property name="f" value="${c*d}"/>
841  <answer f="${f}"/>
842 </robot>'''),
843  '''\
844 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
845  <answer f="88.2"/>
846 </robot>''')
847 
848  def test_from_issue(self):
849  self.assert_matches(
850  self.quick_xacro('''\
851 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
852  <xacro:property name="x" value="42"/>
853  <xacro:property name="wheel_width" value="${x}"/>
854  <link name="my_link">
855  <origin xyz="0 0 ${wheel_width/2}"/>
856  </link>
857 </robot>'''),
858  '''\
859 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
860  <link name="my_link">
861  <origin xyz="0 0 21.0"/>
862  </link>
863 </robot>''')
864 
866  self.assertRaises(xacro.XacroException,
867  self.quick_xacro, '''\
868 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
869  <xacro:property name="x" value="0"/>
870  <tag badness="${1/x}"/>
871 </robot>''')
872 
874  self.assert_matches(
875  self.quick_xacro('''\
876 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
877  <xacro:macro name="fixed_link" params="parent_link:=base_link child_link *joint_pose">
878  <link name="${child_link}"/>
879  <joint name="${child_link}_joint" type="fixed">
880  <xacro:insert_block name="joint_pose" />
881  <parent link="${parent_link}"/>
882  <child link="${child_link}" />
883  </joint>
884  </xacro:macro>
885  <xacro:fixed_link child_link="foo">
886  <origin xyz="0 0 0" rpy="0 0 0" />
887  </xacro:fixed_link >
888 </robot>'''),
889  '''\
890 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
891  <link name="foo"/>
892  <joint name="foo_joint" type="fixed">
893  <origin rpy="0 0 0" xyz="0 0 0"/>
894  <parent link="base_link"/>
895  <child link="foo"/>
896  </joint>
897 </robot>''')
898 
900  self.assert_matches(
901  self.quick_xacro('''\
902 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
903  <xacro:macro name="fixed_link" params="parent_link:=base_link child_link *joint_pose">
904  <link name="${child_link}"/>
905  <joint name="${child_link}_joint" type="fixed">
906  <xacro:insert_block name="joint_pose" />
907  <parent link="${parent_link}"/>
908  <child link="${child_link}" />
909  </joint>
910  </xacro:macro>
911  <xacro:fixed_link child_link="foo" parent_link="bar">
912  <origin xyz="0 0 0" rpy="0 0 0" />
913  </xacro:fixed_link >
914 </robot>'''),
915  '''\
916 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
917  <link name="foo"/>
918  <joint name="foo_joint" type="fixed">
919  <origin rpy="0 0 0" xyz="0 0 0"/>
920  <parent link="bar"/>
921  <child link="foo"/>
922  </joint>
923 </robot>''')
924 
926  self.assertRaises(xacro.XacroException,
927  self.quick_xacro, '''\
928 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
929  <xacro:macro name="fixed_link" params="parent_link child_link *joint_pose">
930  <link name="${child_link}"/>
931  <joint name="${child_link}_joint" type="fixed">
932  <xacro:insert_block name="joint_pose" />
933  <parent link="${parent_link}"/>
934  <child link="${child_link}" />
935  </joint>
936  </xacro:macro>
937  <xacro:fixed_link child_link="foo">
938  <origin xyz="0 0 0" rpy="0 0 0" />
939  </xacro:fixed_link >
940 </robot>''')
941 
942  def test_default_arg(self):
943  self.assert_matches(
944  self.quick_xacro('''\
945 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
946  <xacro:arg name="foo" default="2"/>
947  <link name="my_link">
948  <origin xyz="0 0 $(arg foo)"/>
949  </link>
950 </robot>
951 '''),'''\
952 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
953  <link name="my_link">
954  <origin xyz="0 0 2"/>
955  </link>
956 </robot>''')
957 
959  self.assert_matches(
960  self.quick_xacro('''\
961 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
962  <xacro:arg name="foo" default="2"/>
963  <link name="my_link">
964  <origin xyz="0 0 $(arg foo)"/>
965  </link>
966 </robot>
967 ''', ['foo:=4']),'''\
968 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
969  <link name="my_link">
970  <origin xyz="0 0 4"/>
971  </link>
972 </robot>''')
973 
975  self.assertRaises(Exception,
976  self.quick_xacro, '''\
977 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
978  <a arg="$(arg foo)"/>
979 </a>
980 ''')
981 
983  self.assert_matches(self.quick_xacro('''
984 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
985 <xacro:arg name="foo" default=""/>$(arg foo)</a>'''),
986  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"/>''')
987 
989  # run xacro on broken input file to make sure we don't create an
990  # empty output file
991  tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
992  output_path = os.path.join(tmp_dir_name, "should_not_exist")
993  self.run_xacro('broken.xacro', '-o', output_path)
994 
995  output_file_created = os.path.isfile(output_path)
996  shutil.rmtree(tmp_dir_name) # clean up after ourselves
997 
998  self.assertFalse(output_file_created)
999 
1001  # run xacro to create output file in non-existent directory
1002  # to make sure this directory will be created by xacro
1003  tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
1004  shutil.rmtree(tmp_dir_name) # ensure directory is removed
1005  output_path = os.path.join(tmp_dir_name, "out")
1006  self.run_xacro('include1.xml', '-o', output_path)
1007 
1008  output_file_created = os.path.isfile(output_path)
1009  shutil.rmtree(tmp_dir_name) # clean up after ourselves
1010 
1011  self.assertTrue(output_file_created)
1012 
1014  self.assert_matches(
1015  self.quick_xacro('''\
1016 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1017  <xacro:property name="list" value="[0, 1+1, 2]"/>
1018  <xacro:property name="tuple" value="(0,1+1,2)"/>
1019  <xacro:property name="dict" value="{'a':0, 'b':1+1, 'c':2}"/>
1020  <a list="${list}" tuple="${tuple}" dict="${dict}"/>
1021 </a>'''),
1022 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1023  <a list="[0, 1+1, 2]" tuple="(0,1+1,2)" dict="{'a':0, 'b':1+1, 'c':2}"/>
1024 </a>''')
1025 
1027  self.assert_matches(
1028  self.quick_xacro('''\
1029 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1030  <xacro:property name="list" value="${[0, 1+1, 2]}"/>
1031  <xacro:property name="tuple" value="${(0,1+1,2)}"/>
1032  <xacro:property name="dic" value="${dict(a=0, b=1+1, c=2)}"/>
1033  <a list="${list}" tuple="${tuple}" dict="${dic}"/>
1034 </a>'''),
1035 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1036  <a list="[0, 2, 2]" tuple="(0, 2, 2)" dict="{'a': 0, 'c': 2, 'b': 2}"/>
1037 </a>''')
1038 
1040  self.assert_matches(
1041  self.quick_xacro('''\
1042 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1043  <arg name="foo" value="bar"/>
1044  <include filename="foo"/>
1045 </a>''', xacro_ns=False),
1046 '''\
1047 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1048  <arg name="foo" value="bar"/>
1049  <include filename="foo"/>
1050 </a>''')
1051 
1053  # If a property is assigned from a substitution arg, then this properties' value was
1054  # no longer converted to a python type, so that e.g. 0.5 remained u'0.5'.
1055  # If this property is then used in a numerical expression an exception is thrown.
1056  self.assert_matches(
1057  self.quick_xacro('''\
1058 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1059  <xacro:arg name="foo" default="0.5"/>
1060  <xacro:property name="prop" value="$(arg foo)" />
1061  <a prop="${prop-0.3}"/>
1062 </a>
1063 '''),'''\
1064 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1065  <a prop="0.2"/>
1066 </a>''')
1067 
1069  self.assert_matches(
1070  self.quick_xacro('''\
1071 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1072  <xacro:arg name="foo" default="0.5"/>
1073  <xacro:arg name="bar" default="$(arg foo)"/>
1074  <xacro:property name="prop" value="$(arg bar)" />
1075  <a prop="${prop-0.3}"/>
1076 </a>
1077 '''),'''\
1078 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1079  <a prop="0.2"/>
1080 </a>''')
1081 
1083  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1084  <xacro:macro name="xacro:my_macro"><foo/></xacro:macro>
1085  <xacro:my_macro/>
1086  </a>'''
1087  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><foo/></a>'''
1088  self.assert_matches(self.quick_xacro(src), res)
1089 
1091  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1092  <xacro:property name="pi" value="3.14"/></a>'''
1093  with capture_stderr(self.quick_xacro, src) as (result, output):
1094  self.assert_matches(result, '<a xmlns:xacro="http://www.ros.org/wiki/xacro"/>')
1095  self.assertTrue(output)
1096 
1098  src = '''
1099 <a xmlns:xacro="http://www.ros.org/xacro">
1100  <xacro:macro name="foo" params="a b:=${a} c:=$${a}"> a=${a} b=${b} c=${c} </xacro:macro>
1101  <xacro:property name="a" value="1"/>
1102  <xacro:property name="d" value="$${a}"/>
1103  <d d="${d}"><foo a="2"/></d>
1104 </a>'''
1105  res = '''<a xmlns:xacro="http://www.ros.org/xacro"><d d="${a}"> a=2 b=1 c=${a} </d></a>'''
1106  self.assert_matches(self.quick_xacro(src), res)
1107 
1109  src='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1110  <xacro:property name="arg" value="42"/>
1111  <xacro:macro name="foo" params="arg:=^%s">${arg}</xacro:macro>
1112  <xacro:foo/>
1113  </a>'''
1114  res='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">%s</a>'''
1115  self.assert_matches(self.quick_xacro(src % ''), res % '42')
1116  self.assert_matches(self.quick_xacro(src % '|'), res % '42')
1117  self.assert_matches(self.quick_xacro(src % '|6'), res % '42')
1118 
1120  src='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${2*'$(arg var)'}</a>'''
1121  res='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">%s</a>'''
1122  self.assert_matches(self.quick_xacro(src, ['var:=xacro']), res % (2*'xacro'))
1123 
1125  src='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">$(arg ${'v'+'ar'})</a>'''
1126  res='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">%s</a>'''
1127  self.assert_matches(self.quick_xacro(src, ['var:=xacro']), res % 'xacro')
1128 
1129 # test class for in-order processing
1131  def __init__(self, *args, **kwargs):
1132  super(TestXacroInorder, self).__init__(*args, **kwargs)
1133  self.in_order = True
1134 
1136  doc = ('''<a xmlns:xacro="http://www.ros.org/xacro">
1137  <xacro:if value="false"><xacro:include filename="non-existent"/></xacro:if></a>''')
1138  self.assert_matches(self.quick_xacro(doc),
1139  '''<a xmlns:xacro="http://www.ros.org/xacro"/>''')
1140 
1142  self.assert_matches(
1143  self.quick_xacro('''\
1144 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1145  <xacro:arg name="has_stuff" default="false"/>
1146  <xacro:if value="$(arg has_stuff)">
1147  <xacro:include file="$(find nonexistent_package)/stuff.urdf" />
1148  </xacro:if>
1149 </a>'''),
1150 '<a xmlns:xacro="http://www.ros.org/wiki/xacro"/>')
1151 
1153  src = '''
1154 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1155  <xacro:property name="settings" value="${load_yaml('settings.yaml')}"/>
1156  <xacro:property name="type" value="$(arg type)"/>
1157  <xacro:include filename="${settings['arms'][type]['file']}"/>
1158  <xacro:call macro="${settings['arms'][type]['macro']}"/>
1159 </a>'''
1160  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><{tag}/></a>'''
1161  for i in ['inc1', 'inc2']:
1162  self.assert_matches(self.quick_xacro(src, cli=['type:=%s' % i]),
1163  res.format(tag=i))
1164 
1166  src='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1167 <xacro:macro name="foo" params="arg:=${2*foo}">
1168  <xacro:property name="foo" value="-"/>
1169  <f val="${arg}"/>
1170 </xacro:macro>
1171 <xacro:property name="foo" value="${3*7}"/>
1172 <xacro:foo/>
1173 <xacro:property name="foo" value="*"/>
1174 <xacro:foo/>
1175 </a>'''
1176  res='''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1177 <f val="42"/><f val="**"/></a>'''
1178  self.assert_matches(self.quick_xacro(src), res)
1179 
1181  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1182 <xacro:property name="bar" value="unused"/>
1183 <xacro:property name="foo" value="unused"/>
1184 <xacro:macro name="foo" params="arg:=${foo}">
1185  <a val="${arg}"/>
1186 </xacro:macro>
1187 <xacro:foo/>
1188 <xacro:property name="bar" value="dummy"/>
1189 <xacro:property name="foo" value="21"/></a>'''
1190  with capture_stderr(self.quick_xacro, src, do_check_order=True) as (result, output):
1191  self.assertTrue("Document is incompatible to --inorder processing." in output)
1192  self.assertTrue("foo" in output) # foo should be reported
1193  self.assertTrue("bar" not in output) # bar shouldn't be reported
1194 
1196  src = '''
1197  <a xmlns:xacro="http://www.ros.org/xacro">
1198  <xacro:property name="prop" default="false"/>
1199  <xacro:unless value="${prop}">
1200  <foo/>
1201  <xacro:property name="prop" value="true"/>
1202  </xacro:unless>
1203 
1204  <!-- second foo should be ignored -->
1205  <xacro:unless value="${prop}">
1206  <foo/>
1207  <xacro:property name="prop" value="true"/>
1208  </xacro:unless>
1209  </a>'''
1210  res = '''<a xmlns:xacro="http://www.ros.org/xacro"><foo/></a>'''
1211  self.assert_matches(self.quick_xacro(src), res)
1212 
1214  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">🍔 </a>'''
1215  self.assert_matches(self.quick_xacro(src), src)
1216 
1218  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1219 <xacro:property name="burger" value="🍔"/>
1220 ${burger}</a>'''
1221  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">🍔</a>'''
1222  self.assert_matches(self.quick_xacro(src), res)
1223 
1225  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1226 <xacro:property name="burger" value="🍔"/>
1227 <b c="${burger}"/></a>'''
1228  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><b c="🍔"/></a>'''
1229  self.assert_matches(self.quick_xacro(src), res)
1230 
1232  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1233 <xacro:property name="burger">
1234 🍔
1235 </xacro:property>
1236 <xacro:insert_block name="burger"/></a>'''
1237  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">🍔</a>'''
1238  self.assert_matches(self.quick_xacro(src), res)
1239 
1241  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1242 <xacro:property name="burger" value="🍔"/>
1243 <xacro:if value="${burger == u'🍔'}">
1244 🍟
1245 </xacro:if>
1246 </a>'''
1247  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">🍟</a>'''
1248  self.assert_matches(self.quick_xacro(src), res)
1249 
1251  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1252 <xacro:macro name="burger" params="how_many">
1253 ${u'🍔' * how_many}
1254 </xacro:macro>
1255 <xacro:burger how_many="4"/>
1256 </a>'''
1257  res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1258 🍔🍔🍔🍔</a>'''
1259  self.assert_matches(self.quick_xacro(src), res)
1260 
1262  # run the full xacro processing pipeline on a file with
1263  # unicode characters in it and make sure the output is correct
1264  test_dir= os.path.abspath(os.path.dirname(__file__))
1265  input_path = os.path.join(test_dir, 'emoji.xacro')
1266  tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
1267  output_path = os.path.join(tmp_dir_name, "out.xml")
1268  self.run_xacro(input_path, '-o', output_path)
1269  output_file_created = os.path.isfile(output_path)
1270  self.assert_matches(xml.dom.minidom.parse(output_path),
1271  '''<robot xmlns:xacro="http://ros.org/wiki/xacro">🍔</robot>''')
1272  shutil.rmtree(tmp_dir_name) # clean up after ourselves
1273 
1274 if __name__ == '__main__':
1275  unittest.main()
def text_matches(a, b)
Definition: test_xacro.py:74
def test_multiple_blocks(self)
Definition: test_xacro.py:481
def test_extension_in_expression(self)
Definition: test_xacro.py:1119
def test_normalize_whitespace_nested(self)
Definition: test_xacro.py:182
def test_default_arg_empty(self)
Definition: test_xacro.py:982
def test_xacro_attribute(self)
Definition: test_xacro.py:351
def test_include_from_variable(self)
Definition: test_xacro.py:553
def test_recursive_bad_math(self)
Definition: test_xacro.py:865
def test_whitespace_vs_empty_node(self)
Definition: test_xacro.py:180
def __init__(self, args, kwargs)
Definition: test_xacro.py:297
def assert_matches(self, a, b)
Definition: test_xacro.py:255
def test_property_scope_global(self)
Definition: test_xacro.py:429
def test_include_deprecated(self)
Definition: test_xacro.py:546
def text_values_match(a, b)
Definition: test_xacro.py:27
def test_match_similar_numbers(self)
Definition: test_xacro.py:168
def __init__(self, args, kwargs)
Definition: test_xacro.py:281
def test_equality_expression_in_if_statement(self)
Definition: test_xacro.py:675
def test_default_param_override(self)
Definition: test_xacro.py:899
def test_multiple_definition_and_evaluation(self)
Definition: test_xacro.py:802
def test_evaluate_macro_params_before_body(self)
Definition: test_xacro.py:399
def test_broken_input_doesnt_create_empty_output_file(self)
Definition: test_xacro.py:988
def test_math_ignores_spaces(self)
Definition: test_xacro.py:440
def test_include_recursive(self)
Definition: test_xacro.py:563
def test_multiple_insert_blocks(self)
Definition: test_xacro.py:465
def test_math_expressions(self)
Definition: test_xacro.py:697
def test_boolean_if_statement(self)
Definition: test_xacro.py:593
def test_consider_non_elements_block(self)
Definition: test_xacro.py:714
def test_recursive_evaluation_wrong_order(self)
Definition: test_xacro.py:765
def test_integer_if_statement(self)
Definition: test_xacro.py:615
def __init__(self, args, kwargs)
Definition: test_xacro.py:250
def test_overwrite_globals(self)
Definition: test_xacro.py:1090
def capture_stderr(function, args, kwargs)
Definition: test_xacro.py:154
def test_mismatch_different_numbers(self)
Definition: test_xacro.py:170
def test_issue_63_fixed_with_inorder_processing(self)
Definition: test_xacro.py:1141
def test_recursive_definition(self)
Definition: test_xacro.py:778
def test_macro_name_with_colon(self)
Definition: test_xacro.py:1082
def test_property_forwarding(self)
Definition: test_xacro.py:1108
def test_invalid_property_name(self)
Definition: test_xacro.py:301
def test_transitive_evaluation(self)
Definition: test_xacro.py:817
def is_valid_name(name)
def run_xacro(self, input_path, args)
Definition: test_xacro.py:270
def test_multi_tree_evaluation(self)
Definition: test_xacro.py:832
def test_include_nonexistent(self)
Definition: test_xacro.py:541
def test_float_if_statement(self)
Definition: test_xacro.py:637
def xml_matches(a, b, ignore_nodes=[])
Definition: test_xacro.py:131
def test_substitution_args_arg(self)
Definition: test_xacro.py:450
def test_default_arg_override(self)
Definition: test_xacro.py:958
def test_iterable_literals_eval(self)
Definition: test_xacro.py:1026
def test_macro_default_param_evaluation_order(self)
Definition: test_xacro.py:1165
def test_consider_non_elements_if(self)
Definition: test_xacro.py:706
def test_integer_stays_integer(self)
Definition: test_xacro.py:500
def test_macro_undefined(self)
Definition: test_xacro.py:336
def test_multiple_recursive_evaluation(self)
Definition: test_xacro.py:787
def all_attributes_match(a, b)
Definition: test_xacro.py:55
def test_dynamic_macro_name_clash(self)
Definition: test_xacro.py:316
def test_substitution_args_find(self)
Definition: test_xacro.py:445
def test_insert_block_property(self)
Definition: test_xacro.py:512
def test_macro_params_escaped_string(self)
Definition: test_xacro.py:405
def test_include_with_namespace(self)
Definition: test_xacro.py:574
def test_match_unordered_dicts(self)
Definition: test_xacro.py:173
def test_recursive_evaluation(self)
Definition: test_xacro.py:752
def test_property_scope_parent(self)
Definition: test_xacro.py:421
def test_no_double_evaluation(self)
Definition: test_xacro.py:1097
def test_normalize_whitespace_text(self)
Definition: test_xacro.py:163
def test_expression_in_extension(self)
Definition: test_xacro.py:1124
def process_args(argv, require_input=True)
Definition: cli.py:61
def test_transitive_arg_evaluation(self)
Definition: test_xacro.py:1068
def test_mismatch_different_dicts(self)
Definition: test_xacro.py:175
def test_property_replacement(self)
Definition: test_xacro.py:411
def parse_macro_arg(s)
def test_ignore_xacro_comments(self)
Definition: test_xacro.py:734
def test_dynamic_macro_names(self)
Definition: test_xacro.py:306
def test_inorder_processing(self)
Definition: test_xacro.py:362
def test_issue_68_numeric_arg(self)
Definition: test_xacro.py:1052
def test_invalid_if_statement(self)
Definition: test_xacro.py:609
def test_just_a_dollar_sign(self)
Definition: test_xacro.py:460
def test_should_replace_before_macroexpand(self)
Definition: test_xacro.py:386
def test_normalize_whitespace_trim(self)
Definition: test_xacro.py:165
def test_empty_node_vs_whitespace(self)
Definition: test_xacro.py:178
def test_escaping_dollar_braces(self)
Definition: test_xacro.py:455
def quick_xacro(self, xml, cli=None, kwargs)
Definition: test_xacro.py:258
def check_macro_arg(self, s, param, forward, default, rest)
Definition: test_xacro.py:221
def test_dynamic_macro_undefined(self)
Definition: test_xacro.py:330
def __init__(self, args, kwargs)
Definition: test_xacro.py:1131
def test_default_arg_missing(self)
Definition: test_xacro.py:974
def test_iterable_literals_plain(self)
Definition: test_xacro.py:1013
def nodes_match(a, b, ignore_nodes)
Definition: test_xacro.py:80


xacro
Author(s): Stuart Glaser, William Woodall, Robert Haschke
autogenerated on Sat Jun 8 2019 18:04:14