test_xacro.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 # -*- coding: utf-8 -*-
3 
4 # Copyright (c) 2015, Open Source Robotics Foundation, Inc.
5 # Copyright (c) 2013, Willow Garage, Inc.
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are met:
10 #
11 # * Redistributions of source code must retain the above copyright
12 # notice, this list of conditions and the following disclaimer.
13 # * Redistributions in binary form must reproduce the above copyright
14 # notice, this list of conditions and the following disclaimer in the
15 # documentation and/or other materials provided with the distribution.
16 # * Neither the name of the Open Source Robotics Foundation, Inc.
17 # nor the names of its contributors may be used to endorse or promote
18 # products derived from this software without specific prior
19 # written permission.
20 #
21 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22 # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24 # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
25 # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26 # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27 # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28 # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29 # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30 # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31 # POSSIBILITY OF SUCH DAMAGE.
32 
33 # Authors: Stuart Glaser, William Woodall, Robert Haschke
34 # Maintainer: Robert Haschke <rhaschke@techfak.uni-bielefeld.de>
35 
36 from __future__ import print_function
37 
38 import ast
39 from contextlib import contextmanager
40 import itertools
41 import math
42 import os.path
43 import re
44 import shutil
45 import subprocess
46 import sys
47 import tempfile
48 import unittest
49 import xacro
50 import xml.dom
51 from xml.dom.minidom import parseString
52 
53 try:
54  from cStringIO import StringIO # Python 2.x
55 except ImportError:
56  from io import StringIO # Python 3.x
57 
58 try:
59  from unittest import subTest
60 except ImportError:
61  # subTest was introduced in 3.4 only. Provide a dummy fallback.
62  @contextmanager
63  def subTest(msg):
64  yield None
65 
66 # regex to match whitespace
67 whitespace = re.compile(r'\s+')
68 
69 
71  # generic comparison
72  if whitespace.sub(' ', a).strip() == whitespace.sub(' ', b).strip():
73  return True
74 
75  try: # special handling of dicts: ignore order
76  a_dict = ast.literal_eval(a)
77  b_dict = ast.literal_eval(b)
78  if (isinstance(a_dict, dict) and isinstance(b_dict, dict) and a_dict == b_dict):
79  return True
80  except Exception: # Attribute values aren't dicts
81  pass
82 
83  # on failure, try to split a and b at whitespace and compare snippets
84  def match_splits(a_, b_):
85  if len(a_) != len(b_):
86  return False
87  for a, b in zip(a_, b_):
88  if a == b:
89  continue
90  try: # compare numeric values only up to some accuracy
91  if abs(float(a) - float(b)) > 1.0e-9:
92  return False
93  except ValueError: # values aren't numeric and not identical
94  return False
95  return True
96 
97  return match_splits(a.split(), b.split())
98 
99 
101  if len(a.attributes) != len(b.attributes):
102  print('Different number of attributes')
103  return False
104  a_atts = a.attributes.items()
105  b_atts = b.attributes.items()
106  a_atts.sort()
107  b_atts.sort()
108 
109  for a, b in zip(a_atts, b_atts):
110  if a[0] != b[0]:
111  print('Different attribute names: %s and %s' % (a[0], b[0]))
112  return False
113  if not text_values_match(a[1], b[1]):
114  print('Different attribute values: %s and %s' % (a[1], b[1]))
115  return False
116  return True
117 
118 
119 def text_matches(a, b):
120  if text_values_match(a, b):
121  return True
122  print("Different text values: '%s' and '%s'" % (a, b))
123  return False
124 
125 
126 def nodes_match(a, b, ignore_nodes):
127  if not a and not b:
128  return True
129  if not a or not b:
130  return False
131 
132  if a.nodeType != b.nodeType:
133  print('Different node types: %s and %s' % (a, b))
134  return False
135 
136  # compare text-valued nodes
137  if a.nodeType in [xml.dom.Node.TEXT_NODE,
138  xml.dom.Node.CDATA_SECTION_NODE,
139  xml.dom.Node.COMMENT_NODE]:
140  return text_matches(a.data, b.data)
141 
142  # ignore all other nodes except ELEMENTs
143  if a.nodeType != xml.dom.Node.ELEMENT_NODE:
144  return True
145 
146  # compare ELEMENT nodes
147  if a.nodeName != b.nodeName:
148  print('Different element names: %s and %s' % (a.nodeName, b.nodeName))
149  return False
150 
151  if not all_attributes_match(a, b):
152  return False
153 
154  a = a.firstChild
155  b = b.firstChild
156  while a or b:
157  # ignore whitespace-only text nodes
158  # we could have several text nodes in a row, due to replacements
159  while (a and
160  ((a.nodeType in ignore_nodes) or
161  (a.nodeType == xml.dom.Node.TEXT_NODE and whitespace.sub('', a.data) == ""))):
162  a = a.nextSibling
163  while (b and
164  ((b.nodeType in ignore_nodes) or
165  (b.nodeType == xml.dom.Node.TEXT_NODE and whitespace.sub('', b.data) == ""))):
166  b = b.nextSibling
167 
168  if not nodes_match(a, b, ignore_nodes):
169  return False
170 
171  if a:
172  a = a.nextSibling
173  if b:
174  b = b.nextSibling
175 
176  return True
177 
178 
179 def xml_matches(a, b, ignore_nodes=[]):
180  if isinstance(a, str):
181  return xml_matches(parseString(a).documentElement, b, ignore_nodes)
182  if isinstance(b, str):
183  return xml_matches(a, parseString(b).documentElement, ignore_nodes)
184  if a.nodeType == xml.dom.Node.DOCUMENT_NODE:
185  return xml_matches(a.documentElement, b, ignore_nodes)
186  if b.nodeType == xml.dom.Node.DOCUMENT_NODE:
187  return xml_matches(a, b.documentElement, ignore_nodes)
188 
189  if not nodes_match(a, b, ignore_nodes):
190  print('Match failed:')
191  a.writexml(sys.stdout)
192  print()
193  print('=' * 78)
194  b.writexml(sys.stdout)
195  print()
196  return False
197  return True
198 
199 
200 # capture output going to file=sys.stdout | sys.stderr
201 @contextmanager
202 def capture_stderr(function, *args, **kwargs):
203  old, sys.stderr = sys.stderr, StringIO() # temporarily replace sys.stderr with StringIO()
204  result = function(*args, **kwargs)
205  sys.stderr.seek(0)
206  yield (result, sys.stderr.read())
207  sys.stderr = old # restore sys.stderr
208 
209 
210 class TestUtils(unittest.TestCase):
211  def test_capture_stderr(self, *args, **kwargs):
212  with capture_stderr(xacro.error, 'Hello World', alt_text='') as (result, output):
213  self.assertEqual(output, 'Hello World\n')
214 
215 
216 class TestMatchXML(unittest.TestCase):
218  self.assertTrue(text_matches("", " \t\n\r"))
219 
221  self.assertTrue(text_matches(" foo bar ", "foo \t\n\r bar"))
222 
224  self.assertTrue(text_matches("0.123456789", "0.123456788"))
225 
227  self.assertFalse(text_matches("0.123456789", "0.1234567879"))
228 
230  self.assertTrue(text_matches("{'a': 1, 'b': 2, 'c': 3}", "{'c': 3, 'b': 2, 'a': 1}"))
231 
233  self.assertFalse(text_matches("{'a': 1, 'b': 2, 'c': 3}", "{'c': 3, 'b': 2, 'a': 0}"))
234 
236  self.assertTrue(xml_matches('''<foo/>''', '''<foo> \t\n\r </foo>'''))
237 
239  self.assertTrue(xml_matches('''<foo> \t\n\r </foo>''', '''<foo/>'''))
240 
242  self.assertTrue(xml_matches('''<a><b/></a>''', '''<a>\n<b> </b> </a>'''))
243 
245  self.assertTrue(xml_matches('''<a><b/><!-- foo --> <!-- bar --></a>''',
246  '''<a><b/></a>''', [xml.dom.Node.COMMENT_NODE]))
247 
248 
249 class TestXacroFunctions(unittest.TestCase):
251  self.assertTrue(xacro.is_valid_name("_valid_name_123"))
252  self.assertFalse(xacro.is_valid_name('pass')) # syntactically correct keyword
253  self.assertFalse(xacro.is_valid_name('foo ')) # trailing whitespace
254  self.assertFalse(xacro.is_valid_name(' foo')) # leading whitespace
255  self.assertFalse(xacro.is_valid_name('1234')) # number
256  self.assertFalse(xacro.is_valid_name('1234abc')) # number and letters
257  self.assertFalse(xacro.is_valid_name('')) # empty string
258  self.assertFalse(xacro.is_valid_name(' ')) # whitespace only
259  self.assertFalse(xacro.is_valid_name('foo bar')) # several tokens
260  self.assertFalse(xacro.is_valid_name('no-dashed-names-for-you'))
261  self.assertFalse(xacro.is_valid_name('invalid.too')) # dot separates fields
262 
264  # define three nested dicts with the same names (keys)
265  content = {'simple': 'simple'}
266  ns2 = dict({k: v + '2' for k, v in content.items()})
267  ns1 = dict({k: v + '1' for k, v in content.items()})
268  ns1.update(ns2=ns2)
269  ns = dict(content)
270  ns.update(ns1=ns1)
271 
272  self.assertEqual(xacro.resolve_macro('simple', ns, ns), (ns, ns, 'simple'))
273  self.assertEqual(xacro.resolve_macro('ns1.simple', ns, ns), (ns1, ns1, 'simple1'))
274  self.assertEqual(xacro.resolve_macro('ns1.ns2.simple', ns, ns), (ns2, ns2, 'simple2'))
275 
276  def check_macro_arg(self, s, param, forward, default, rest):
277  p, v, r = xacro.parse_macro_arg(s)
278  self.assertEqual(p, param, msg="'{0}' != '{1}' parsing {2}".format(p, param, s))
279  if forward or default:
280  self.assertTrue(v is not None)
281  self.assertEqual(v[0], forward, msg="'{0}' != '{1}' parsing {2}".format(v[0], forward, s))
282  self.assertEqual(v[1], default, msg="'{0}' != '{1}' parsing {2}".format(v[1], default, s))
283  else:
284  self.assertTrue(v is None)
285  self.assertEqual(r, rest, msg="'{0}' != '{1}' parsing {2}".format(r, rest, s))
286 
288  for forward in ['', '^', '^|']:
289  defaults = ['', "'single quoted'", '"double quoted"', '${2 * (prop_with_spaces + 1)}', '$(substitution arg)',
290  "anything~w/o~space-'space allowed in quotes'(\"as here too\")", 'unquoted']
291  if forward == '^':
292  defaults = [''] # default allowed allowed afer ^|
293  for default in defaults:
294  seps = ['=', ':='] if forward or default else ['']
295  for sep in seps:
296  for rest in ['', ' ', ' bar', ' bar=42']:
297  s = 'foo{0}{1}{2}{3}'.format(sep, forward, default, rest)
298  self.check_macro_arg(s, 'foo', 'foo' if forward else None,
299  default if default else None,
300  rest.lstrip())
301 
303  for ws in [' ', ' \t ', ' \n ']:
304  self.check_macro_arg(ws + 'foo' + ws + 'bar=42' + ws, 'foo', None, None, 'bar=42' + ws)
305 
306  def test_tokenize(self):
307  tokens = ['ab', 'cd', 'ef']
308  for sep in [' ', ',', ';', ', ']:
309  self.assertEqual(xacro.tokenize(sep.join(tokens)), tokens)
310 
312  tokens = ' '.join(['ab', ' ', 'cd', 'ef'])
313  results = xacro.tokenize(tokens, sep=' ', skip_empty=False)
314  self.assertEqual(len(results), 5) # intermediate space becomes ' ' split into 2 empty fields
315  self.assertEqual(' '.join(results), tokens)
316 
317 
318 # base class providing some convenience functions
319 class TestXacroBase(unittest.TestCase):
320  def __init__(self, *args, **kwargs):
321  super(TestXacroBase, self).__init__(*args, **kwargs)
322  self.in_order = False
323  self.ignore_nodes = []
324 
325  def assert_matches(self, a, b):
326  self.assertTrue(xml_matches(a, b, self.ignore_nodes))
327 
328  def quick_xacro(self, xml, cli=None, **kwargs):
329  args = {}
330  if cli:
331  opts, _ = xacro.cli.process_args(cli, require_input=False)
332  args.update(vars(opts)) # initialize with cli args
333  args.update(dict(in_order=self.in_order)) # set in_order option from test class
334  args.update(kwargs) # explicit function args have highest priority
335 
336  doc = xacro.parse(xml)
337  xacro.filestack = None # Reset filestack
338  xacro.process_doc(doc, **args)
339  return doc
340 
341  def run_xacro(self, input_path, *args):
342  args = list(args)
343  if not self.in_order:
344  args.append('--legacy')
345  test_dir = os.path.abspath(os.path.dirname(__file__))
346  subprocess.call(['xacro', input_path] + args)
347 
348 
349 # class to match XML docs while ignoring any comments
351  def __init__(self, *args, **kwargs):
352  super(TestXacroCommentsIgnored, self).__init__(*args, **kwargs)
353  self.ignore_nodes = [xml.dom.Node.COMMENT_NODE]
354 
355  def test_pr2(self):
356  # run xacro on the pr2 tree snapshot
357  test_dir = os.path.abspath(os.path.dirname(__file__))
358  pr2_xacro_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2.urdf.xacro')
359  pr2_golden_parse_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2_1.11.4.xml')
360  self.assert_matches(
361  xml.dom.minidom.parse(pr2_golden_parse_path),
362  self.quick_xacro(open(pr2_xacro_path)))
363 
364 
365 # standard test class (including the test from TestXacroCommentsIgnored)
367  def __init__(self, *args, **kwargs):
368  super(TestXacroCommentsIgnored, self).__init__(*args, **kwargs)
369  self.ignore_nodes = []
370 
372  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
373  <xacro:property name="invalid.name"/></a>'''
374  self.assertRaises(xacro.XacroException, self.quick_xacro, src)
375 
377  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
378  <xacro:property name="__hidden"/></a>'''
379  with self.assertRaises(xacro.XacroException) as cm:
380  self.quick_xacro(src)
381  self.assertEqual(str(cm.exception), 'Property names must not start with double underscore:__hidden')
382 
384  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
385  <xacro:macro name="foo"><a>foo</a></xacro:macro>
386  <xacro:macro name="bar"><b>bar</b></xacro:macro>
387  <xacro:property name="var" value="%s"/>
388  <xacro:call macro="${var}"/></a>'''
389  res = '''<a>%s</a>'''
390  self.assert_matches(self.quick_xacro(src % "foo"), res % "<a>foo</a>")
391  self.assert_matches(self.quick_xacro(src % "bar"), res % "<b>bar</b>")
392 
394  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
395  <xacro:macro name="foo"><a name="foo"/></xacro:macro>
396  <xacro:macro name="call"><a name="bar"/></xacro:macro>
397  <xacro:call/></a>'''
398  # for now we only issue a deprecated warning and expect the old behaviour
399  # resolving macro "call"
400  res = '''<a><a name="bar"/></a>'''
401  # new behaviour would be to resolve to foo of course
402  # res = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><a name="foo"/></a>'''
403  with capture_stderr(self.quick_xacro, src) as (result, output):
404  self.assert_matches(result, res)
405  self.assertTrue("deprecated use of 'call' as macro name" in output)
406 
408  self.assertRaises(xacro.XacroException,
409  self.quick_xacro,
410  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
411  <xacro:call macro="foo"/></a>''')
412 
414  self.assertRaises(xacro.XacroException,
415  self.quick_xacro,
416  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
417  <xacro:undefined><foo/><bar/></xacro:undefined></a>''')
418 
420  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
421  <xacro:macro name="foo" params="name"><xacro:element xacro:name="${name}"/></xacro:macro>
422  <xacro:foo name="A"/>
423  <xacro:foo name="B"/>
424 </a>'''
425  res = '''<a><A/><B/></a>'''
426  self.assert_matches(self.quick_xacro(src), res)
427 
429  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
430  <xacro:macro name="foo" params="name value">
431  <tag><xacro:attribute name="${name}" value="${value}"/></tag>
432  </xacro:macro>
433  <xacro:foo name="A" value="foo"/>
434  <xacro:foo name="B" value="bar"/>
435 </a>'''
436  res = '''<a><tag A="foo"/><tag B="bar"/></a>'''
437  self.assert_matches(self.quick_xacro(src), res)
438 
440  src = '''
441 <xml xmlns:xacro="http://www.ros.org/wiki/xacro">
442  <xacro:property name="foo" value="1.0"/>
443  <xacro:macro name="m" params="foo"><a foo="${foo}"/></xacro:macro>
444  <xacro:m foo="1 ${foo}"/>
445  <!-- now redefining the property and macro -->
446  <xacro:property name="foo" value="2.0"/>
447  <xacro:macro name="m" params="foo"><b bar="${foo}"/></xacro:macro>
448  <xacro:m foo="2 ${foo}"/>
449 </xml>'''
450  oldOrder = '''
451 <xml>
452  <b bar="1 2.0"/>
453  <b bar="2 2.0"/>
454 </xml>
455 '''
456  inOrder = '''
457 <xml>
458  <a foo="1 1.0"/>
459  <b bar="2 2.0"/>
460 </xml>
461 '''
462  self.assert_matches(self.quick_xacro(src), inOrder if self.in_order else oldOrder)
463 
465  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
466 <xacro:macro name="inner" params="*the_block">
467  <in_the_inner><xacro:insert_block name="the_block" /></in_the_inner>
468 </xacro:macro>
469 <xacro:macro name="outer" params="*the_block">
470  <in_the_outer><xacro:inner><xacro:insert_block name="the_block" /></xacro:inner></in_the_outer>
471 </xacro:macro>
472 <xacro:outer><woot /></xacro:outer></a>'''),
473  '''<a><in_the_outer><in_the_inner><woot /></in_the_inner></in_the_outer></a>''')
474 
476  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
477  <xacro:macro name="foo" params="lst">${lst[-1]}</xacro:macro>
478  <xacro:foo lst="${[1,2,3]}"/></a>'''),
479  '''<a>3</a>''')
480 
482  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
483  <xacro:macro name="foo" params="a='1 -2' c=3"><bar a="${a}" c="${c}"/></xacro:macro>
484  <xacro:foo/></a>'''),
485  '''<a><bar a="1 -2" c="3"/></a>''')
486 
488  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
489  <xacro:property name="foo" value="42" />
490  <the_foo result="${foo}" />
491 </a>'''),
492  '''<a>
493  <the_foo result="42" />
494 </a>''')
495 
497  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
498  <xacro:macro name="foo" params="factor">
499  <xacro:property name="foo" value="${21*factor}" scope="parent"/>
500  </xacro:macro>
501  <xacro:foo factor="2"/><a foo="${foo}"/></a>'''),
502  '''<a><a foo="42"/></a>''')
503 
505  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
506  <xacro:macro name="foo" params="factor">
507  <xacro:macro name="bar">
508  <xacro:property name="foo" value="${21*factor}" scope="global"/>
509  </xacro:macro>
510  <xacro:bar/>
511  </xacro:macro>
512  <xacro:foo factor="2"/><a foo="${foo}"/></a>'''),
513  '''<a><a foo="42"/></a>''')
514 
516  self.assert_matches(self.quick_xacro('''<a><f v="${0.9 / 2 - 0.2}" /></a>'''),
517  '''<a><f v="0.25" /></a>''')
518 
520  self.assert_matches(self.quick_xacro('''<a><f v="$(find xacro)/test/test_xacro.py" /></a>'''),
521  '''<a><f v="''' + os.path.abspath((__file__).replace(".pyc", ".py") + '''" /></a>'''))
522 
524  self.assert_matches(self.quick_xacro('''<a><f v="$(arg sub_arg)" /></a>''', cli=['sub_arg:=my_arg']),
525  '''<a><f v="my_arg" /></a>''')
526 
528  self.assert_matches(self.quick_xacro('''<a b="$${foo}" c="$$${foo}" d="text $${foo}" e="text $$${foo}" f="$$(pwd)" />'''),
529  '''<a b="${foo}" c="$${foo}" d="text ${foo}" e="text $${foo}" f="$(pwd)" />''')
530 
532  self.assert_matches(self.quick_xacro('''<a b="$" c="text $" d="text $ text"/>'''),
533  '''<a b="$" c="text $" d="text $ text"/>''')
534 
536  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
537 <xacro:macro name="foo" params="*block">
538  <xacro:insert_block name="block" />
539  <xacro:insert_block name="block" />
540 </xacro:macro>
541 <xacro:foo>
542  <a_block />
543 </xacro:foo>
544 </a>'''), '''<a>
545  <a_block />
546  <a_block />
547 </a>''')
548 
550  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
551 <xacro:macro name="foo" params="*block{A} *block{B}">
552  <xacro:insert_block name="block1" />
553  <xacro:insert_block name="block2" />
554 </xacro:macro>
555 <xacro:foo>
556  <block1/>
557  <block2/>
558 </xacro:foo>
559 </a>'''
560  res = '''<a>
561 <block{A}/>
562 <block{B}/>
563 </a>'''
564  # test both, reversal and non-reversal of block order
565  for d in [dict(A='1', B='2'), dict(A='2', B='1')]:
566  self.assert_matches(self.quick_xacro(src.format(**d)), res.format(**d))
567 
569  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
570 <xacro:macro name="m" params="num">
571  <test number="${num}" />
572 </xacro:macro>
573 <xacro:m num="100" />
574 </a>'''), '''
575 <a>
576  <test number="100" />
577 </a>''')
578 
580  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
581 <xacro:macro name="bar">bar</xacro:macro>
582 <xacro:property name="val" value="2" />
583 <xacro:property name="some_block">
584  <some_block attr="${val}"><xacro:bar/></some_block>
585 </xacro:property>
586 <foo>
587  <xacro:insert_block name="some_block" />
588 </foo>
589 </a>'''), '''
590 <a>
591 <foo><some_block attr="2">bar</some_block></foo>
592 </a>''')
593 
594  def test_include(self):
595  src = '''<a xmlns:xacro="http://www.ros.org/xacro"><xacro:include filename="include1.xml"/></a>'''
596  self.assert_matches(self.quick_xacro(src), '''<a><inc1/></a>''')
597 
598  def test_include_glob(self):
599  src = '''<a xmlns:xacro="http://www.ros.org/xacro"><xacro:include filename="include{glob}.xml"/></a>'''
600  res = '<a><inc1/><inc2/></a>'
601  for pattern in ['*', '?', '[1-2]']:
602  self.assert_matches(self.quick_xacro(src.format(glob=pattern)), res)
603 
605  self.assertRaises(xacro.XacroException,
606  self.quick_xacro, '''<a xmlns:xacro="http://www.ros.org/xacro">
607  <xacro:include filename="include-nada.xml" /></a>''')
608 
610  # <include> tags with some non-trivial content should not issue the deprecation warning
611  src = '''<a><include filename="nada"><tag/></include></a>'''
612  with capture_stderr(self.quick_xacro, src) as (result, output):
613  self.assert_matches(result, src)
614  self.assertEqual(output, '')
615 
617  doc = '''<a xmlns:xacro="http://www.ros.org/xacro">
618  <xacro:property name="file" value="include1.xml"/>
619  <xacro:include filename="${file}" /></a>'''
620  if self.in_order:
621  self.assert_matches(self.quick_xacro(doc), '''<a><inc1/></a>''')
622  else:
623  self.assertRaises(xacro.XacroException, self.quick_xacro, doc)
624 
626  self.assert_matches(self.quick_xacro('''
627 <a xmlns:xacro="http://www.ros.org/xacro">
628  <xacro:include filename="include1.xml"/>
629  <xacro:include filename="./include1.xml"/>
630  <xacro:include filename="subdir/include-recursive.xacro"/>
631 </a>'''), '''
632 <a><inc1/><inc1/><subdir_inc1/><subdir_inc1/><inc1/></a>''')
633 
635  src = '''
636 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
637  <xacro:property name="var" value="main"/>
638  <xacro:include filename="include1.xacro" ns="A"/>
639  <xacro:include filename="include2.xacro" ns="B"/>
640  <xacro:A.foo/><xacro:B.foo/>
641  <main var="${var}" A="${2*A.var}" B="${B.var+1}"/>
642 </a>'''
643  res = '''
644 <a>
645  <inc1/><inc2/><main var="main" A="2" B="3"/>
646 </a>'''
647 
648  if self.in_order:
649  self.assert_matches(self.quick_xacro(src), res)
650  else:
651  self.assertRaises(xacro.XacroException, self.quick_xacro, src)
652 
654  self.assert_matches(self.quick_xacro('''
655 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
656  <xacro:if value="false">
657  <a />
658  </xacro:if>
659  <xacro:if value="true">
660  <b />
661  </xacro:if>
662 </robot>'''), '''
663 <robot>
664  <b />
665 </robot>''')
666 
668  self.assertRaises(xacro.XacroException,
669  self.quick_xacro,
670  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
671  <xacro:if value="nonsense"><foo/></xacro:if></a>''')
672 
674  self.assert_matches(self.quick_xacro('''
675 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
676  <xacro:if value="${0*42}">
677  <a />
678  </xacro:if>
679  <xacro:if value="0">
680  <b />
681  </xacro:if>
682  <xacro:if value="${0}">
683  <c />
684  </xacro:if>
685  <xacro:if value="${1*2+3}">
686  <d />
687  </xacro:if>
688 </robot>'''), '''
689 <robot>
690  <d />
691 </robot>''')
692 
694  self.assert_matches(self.quick_xacro('''
695 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
696  <xacro:if value="${3*0.0}">
697  <a />
698  </xacro:if>
699  <xacro:if value="${3*0.1}">
700  <b />
701  </xacro:if>
702 </robot>'''), '''
703 <robot>
704  <b />
705 </robot>''')
706 
708  self.assert_matches(self.quick_xacro('''
709 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
710  <xacro:property name="condT" value="${True}"/>
711  <xacro:property name="condF" value="${False}"/>
712  <xacro:if value="${condF}"><a /></xacro:if>
713  <xacro:if value="${condT}"><b /></xacro:if>
714  <xacro:if value="${True}"><c /></xacro:if>
715 </robot>'''), '''
716 <robot>
717  <b /><c />
718 </robot>''')
719 
721  self.assert_matches(self.quick_xacro('''
722 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
723  <xacro:if value="1"><xacro:if value="0"><a>bar</a></xacro:if></xacro:if>
724 </a>'''), '''<a/>''')
725 
727  self.assert_matches(self.quick_xacro('''
728 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
729  <xacro:property name="var" value="useit"/>
730  <xacro:if value="${var == 'useit'}"><foo>bar</foo></xacro:if>
731  <xacro:if value="${'use' in var}"><bar>foo</bar></xacro:if>
732 </a>'''), '''
733 <a>
734 <foo>bar</foo>
735 <bar>foo</bar>
736 </a>''')
737 
739  self.assert_matches(self.quick_xacro('''
740 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
741  <xacro:property name="xyz" value="5 -2"/>
742  <foo>${xyz}</foo>
743 </a>'''), '''
744 <a>
745  <foo>5 -2</foo>
746 </a>''')
747 
749  self.assert_matches(self.quick_xacro('''
750 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
751  <foo function="${1. + sin(pi)}"/>
752 </a>'''), '''
753 <a>
754  <foo function="1.0"/>
755 </a>''')
756 
757  # https://realpython.com/python-eval-function/#minimizing-the-security-issues-of-eval
759  self.assertRaises(xacro.XacroException, self.quick_xacro,
760  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${__import__('math')}</a>''')
761 
763  self.assertRaises(xacro.XacroException, self.quick_xacro,
764  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
765 <xacro:macro name="foo" params="arg">
766  <xacro:property name="prop" value="${arg}"/>
767  ${__import__('math')}
768 </xacro:macro>
769 <xacro:foo/>
770 </a>''')
771 
772  def test_safe_eval(self):
773  self.assertRaises(xacro.XacroException, self.quick_xacro,
774  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${"".__class__.__base__.__subclasses__()}</a>''')
775 
777  self.assert_matches(self.quick_xacro('''
778 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
779  <xacro:if value="1"><!-- comment --> text <b>bar</b></xacro:if>
780 </a>'''), '''
781 <a><!-- comment --> text <b>bar</b></a>''')
782 
784  self.assert_matches(
785  self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
786 <xacro:macro name="foo" params="*block">
787  <!-- comment -->
788  foo
789  <xacro:insert_block name="block" />
790 </xacro:macro>
791 <xacro:foo>
792  <!-- ignored comment -->
793  ignored text
794  <a_block />
795 </xacro:foo>
796 </a>'''), '''
797 <a>
798  <!-- comment -->
799  foo
800  <a_block />
801 </a>''')
802 
804  self.assert_matches(self.quick_xacro('''
805 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
806  <!-- A -->
807 
808  <!-- ignore multiline comments before any xacro tag -->
809  <!-- ignored -->
810  <xacro:property name="foo" value="1"/>
811  <!-- ignored -->
812  <xacro:if value="1"><!-- B --></xacro:if>
813  <!-- ignored -->
814  <xacro:macro name="foo"><!-- C --></xacro:macro>
815  <!-- ignored -->
816  <xacro:foo/>
817 </a>'''), '''
818 <a><!-- A --><!-- B --><!-- C --></a>''')
819 
821  self.assert_matches(self.quick_xacro('''
822 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
823  <xacro:property name="a" value=" 42 "/>
824  <xacro:property name="a2" value="${ 2 * a }"/>
825  <a doubled="${a2}"/>
826 </robot>'''), '''
827 <robot>
828  <a doubled="84"/>
829 </robot>''')
830 
832  self.assert_matches(self.quick_xacro('''
833 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
834  <xacro:property name="a2" value="${2*a}"/>
835  <xacro:property name="a" value="42"/>
836  <a doubled="${a2}"/>
837 </robot>'''), '''
838 <robot>
839  <a doubled="84"/>
840 </robot>''')
841 
843  with self.assertRaises(xacro.XacroException) as cm:
844  self.quick_xacro('''
845 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
846  <xacro:property name="a" value="${a2}"/>
847  <xacro:property name="a2" value="${2*a}"/>
848  <a doubled="${a2}"/>
849 </robot>''')
850  msg = str(cm.exception)
851  self.assertTrue(msg.startswith('circular variable definition: a2 -> a -> a2\n'
852  'Consider disabling lazy evaluation via lazy_eval="false"'))
853 
855  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
856  <xacro:property name="s" value="AbCd"/>
857  <xacro:property name="s" value="${s.lower()}" lazy_eval="false"/>
858  ${s}</a>'''
859  self.assert_matches(self.quick_xacro(src), '<a>abcd</a>')
860 
862  self.assert_matches(self.quick_xacro('''
863 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
864  <xacro:property name="a" value="1"/>
865  <xacro:property name="b" value="2"/>
866  <xacro:property name="c" value="3"/>
867  <xacro:property name="product" value="${a*b*c}"/>
868  <answer product="${product}"/>
869 </robot>'''), '''
870 <robot>
871  <answer product="6"/>
872 </robot>''')
873 
875  self.assert_matches(self.quick_xacro('''
876 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
877  <xacro:property name="a" value="42"/>
878  <xacro:property name="b" value="${a}"/>
879  <xacro:property name="b" value="${-a}"/>
880  <xacro:property name="b" value="${a}"/>
881  <answer b="${b} ${b} ${b}"/>
882 </robot>'''), '''
883 <robot>
884  <answer b="42 42 42"/>
885 </robot>''')
886 
888  self.assert_matches(self.quick_xacro('''
889 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
890  <xacro:property name="a" value="42"/>
891  <xacro:property name="b" value="${a}"/>
892  <xacro:property name="c" value="${b}"/>
893  <xacro:property name="d" value="${c}"/>
894  <answer d="${d}"/>
895 </robot>'''), '''
896 <robot>
897  <answer d="42"/>
898 </robot>''')
899 
901  self.assert_matches(self.quick_xacro('''
902 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
903  <xacro:property name="a" value="42"/>
904  <xacro:property name="b" value="2.1"/>
905  <xacro:property name="c" value="${a}"/>
906  <xacro:property name="d" value="${b}"/>
907  <xacro:property name="f" value="${c*d}"/>
908  <answer f="${f}"/>
909 </robot>'''), '''
910 <robot>
911  <answer f="88.2"/>
912 </robot>''')
913 
914  def test_from_issue(self):
915  self.assert_matches(self.quick_xacro('''
916 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
917  <xacro:property name="x" value="42"/>
918  <xacro:property name="wheel_width" value="${x}"/>
919  <link name="my_link">
920  <origin xyz="0 0 ${wheel_width/2}"/>
921  </link>
922 </robot>'''), '''
923 <robot>
924  <link name="my_link">
925  <origin xyz="0 0 21.0"/>
926  </link>
927 </robot>''')
928 
930  self.assertRaises(xacro.XacroException, self.quick_xacro, '''
931 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
932  <xacro:property name="x" value="0"/>
933  <tag badness="${1/x}"/>
934 </robot>''')
935 
937  self.assert_matches(self.quick_xacro('''
938 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
939  <xacro:macro name="fixed_link" params="parent_link:=base_link child_link *joint_pose">
940  <link name="${child_link}"/>
941  <joint name="${child_link}_joint" type="fixed">
942  <xacro:insert_block name="joint_pose" />
943  <parent link="${parent_link}"/>
944  <child link="${child_link}" />
945  </joint>
946  </xacro:macro>
947  <xacro:fixed_link child_link="foo">
948  <origin xyz="0 0 0" rpy="0 0 0" />
949  </xacro:fixed_link >
950 </robot>'''), '''
951 <robot>
952  <link name="foo"/>
953  <joint name="foo_joint" type="fixed">
954  <origin rpy="0 0 0" xyz="0 0 0"/>
955  <parent link="base_link"/>
956  <child link="foo"/>
957  </joint>
958 </robot>''')
959 
961  self.assert_matches(self.quick_xacro('''
962 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
963  <xacro:macro name="fixed_link" params="parent_link:=base_link child_link *joint_pose">
964  <link name="${child_link}"/>
965  <joint name="${child_link}_joint" type="fixed">
966  <xacro:insert_block name="joint_pose" />
967  <parent link="${parent_link}"/>
968  <child link="${child_link}" />
969  </joint>
970  </xacro:macro>
971  <xacro:fixed_link child_link="foo" parent_link="bar">
972  <origin xyz="0 0 0" rpy="0 0 0" />
973  </xacro:fixed_link >
974 </robot>'''), '''
975 <robot>
976  <link name="foo"/>
977  <joint name="foo_joint" type="fixed">
978  <origin rpy="0 0 0" xyz="0 0 0"/>
979  <parent link="bar"/>
980  <child link="foo"/>
981  </joint>
982 </robot>''')
983 
985  self.assertRaises(xacro.XacroException, self.quick_xacro, '''
986 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
987  <xacro:macro name="fixed_link" params="parent_link child_link *joint_pose">
988  <link name="${child_link}"/>
989  <joint name="${child_link}_joint" type="fixed">
990  <xacro:insert_block name="joint_pose" />
991  <parent link="${parent_link}"/>
992  <child link="${child_link}" />
993  </joint>
994  </xacro:macro>
995  <xacro:fixed_link child_link="foo">
996  <origin xyz="0 0 0" rpy="0 0 0" />
997  </xacro:fixed_link >
998 </robot>''')
999 
1000  def test_default_arg(self):
1001  self.assert_matches(self.quick_xacro('''
1002 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
1003  <xacro:arg name="foo" default="2"/>
1004  <link name="my_link">
1005  <origin xyz="0 0 $(arg foo)"/>
1006  </link>
1007 </robot>
1008 '''), '''
1009 <robot>
1010  <link name="my_link">
1011  <origin xyz="0 0 2"/>
1012  </link>
1013 </robot>''')
1014 
1016  self.assert_matches(self.quick_xacro('''
1017 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
1018  <xacro:arg name="foo" default="2"/>
1019  <link name="my_link">
1020  <origin xyz="0 0 $(arg foo)"/>
1021  </link>
1022 </robot>
1023 ''', ['foo:=4']), '''
1024 <robot>
1025  <link name="my_link">
1026  <origin xyz="0 0 4"/>
1027  </link>
1028 </robot>''')
1029 
1031  self.assertRaises(Exception, self.quick_xacro, '''
1032 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1033  <a arg="$(arg foo)"/>
1034 </a>
1035 ''')
1036 
1038  self.assert_matches(self.quick_xacro('''
1039 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1040 <xacro:arg name="foo" default=""/>$(arg foo)</a>'''), '''<a/>''')
1041 
1043  self.assert_matches(self.quick_xacro('''
1044 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1045 <xacro:arg name="foo" default="bar"/>${xacro.arg('foo')}</a>'''), '<a>bar</a>')
1046 
1048  # run xacro on broken input file to make sure we don't create an
1049  # empty output file
1050  tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
1051  output_path = os.path.join(tmp_dir_name, "should_not_exist")
1052  self.run_xacro('broken.xacro', '-o', output_path)
1053 
1054  output_file_created = os.path.isfile(output_path)
1055  shutil.rmtree(tmp_dir_name) # clean up after ourselves
1056 
1057  self.assertFalse(output_file_created)
1058 
1060  # run xacro to create output file in non-existent directory
1061  # to make sure this directory will be created by xacro
1062  tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
1063  shutil.rmtree(tmp_dir_name) # ensure directory is removed
1064  output_path = os.path.join(tmp_dir_name, "out")
1065  self.run_xacro('include1.xml', '-o', output_path)
1066 
1067  output_file_created = os.path.isfile(output_path)
1068  shutil.rmtree(tmp_dir_name) # clean up after ourselves
1069 
1070  self.assertTrue(output_file_created)
1071 
1073  self.assert_matches(self.quick_xacro('''
1074 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1075  <xacro:property name="l" value="[0, 1+1, 2]"/>
1076  <xacro:property name="t" value="(0,1+1,2)"/>
1077  <xacro:property name="d" value="{'a':0, 'b':1+1, 'c':2}"/>
1078  <a list="${l}" tuple="${t}" dict="${d}"/>
1079 </a>'''), '''
1080 <a>
1081  <a list="[0, 1+1, 2]" tuple="(0,1+1,2)" dict="{'a':0, 'b':1+1, 'c':2}"/>
1082 </a>''')
1083 
1085  self.assert_matches(self.quick_xacro('''
1086 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1087  <xacro:property name="l" value="${[0, 1+1, 2]}"/>
1088  <xacro:property name="t" value="${(0,1+1,2)}"/>
1089  <xacro:property name="d" value="${dict(a=0, b=1+1, c=2)}"/>
1090  <a list="${l}" tuple="${t}" dict="${d}"/>
1091 </a>'''), '''
1092 <a>
1093  <a list="[0, 2, 2]" tuple="(0, 2, 2)" dict="{'a': 0, 'c': 2, 'b': 2}"/>
1094 </a>''')
1095 
1097  self.assert_matches(self.quick_xacro('''
1098 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1099  <arg name="foo" value="bar"/>
1100  <include filename="foo"/>
1101 </a>''', xacro_ns=False), '''
1102 <a>
1103  <arg name="foo" value="bar"/>
1104  <include filename="foo"/>
1105 </a>''')
1106 
1108  # If a property is assigned from a substitution arg, then this properties' value was
1109  # no longer converted to a python type, so that e.g. 0.5 remained u'0.5'.
1110  # If this property is then used in a numerical expression an exception is thrown.
1111  self.assert_matches(self.quick_xacro('''
1112 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1113  <xacro:arg name="foo" default="0.5"/>
1114  <xacro:property name="prop" value="$(arg foo)" />
1115  <a prop="${prop-0.3}"/>
1116 </a>
1117 '''), '''
1118 <a>
1119  <a prop="0.2"/>
1120 </a>''')
1121 
1123  self.assert_matches(self.quick_xacro('''
1124 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1125  <xacro:arg name="foo" default="0.5"/>
1126  <xacro:arg name="bar" default="$(arg foo)"/>
1127  <xacro:property name="prop" value="$(arg bar)" />
1128  <a prop="${prop-0.3}"/>
1129 </a>
1130 '''), '''
1131 <a>
1132  <a prop="0.2"/>
1133 </a>''')
1134 
1136  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1137  <xacro:macro name="xacro:my_macro"><foo/></xacro:macro>
1138  <xacro:my_macro/>
1139  </a>'''
1140  res = '''<a><foo/></a>'''
1141  with capture_stderr(self.quick_xacro, src) as (result, output):
1142  self.assert_matches(result, res)
1143  self.assertTrue("macro names must not contain prefix 'xacro:'" in output)
1144 
1146  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1147  <xacro:property name="pi" value="3.14"/></a>'''
1148  with capture_stderr(self.quick_xacro, src) as (result, output):
1149  self.assert_matches(result, '<a/>')
1150  self.assertTrue(output)
1151 
1153  src = '''
1154 <a xmlns:xacro="http://www.ros.org/xacro">
1155  <xacro:macro name="foo" params="a b:=${a} c:=$${a}"> a=${a} b=${b} c=${c} </xacro:macro>
1156  <xacro:property name="a" value="1"/>
1157  <xacro:property name="d" value="$${a}"/>
1158  <d d="${d}"><xacro:foo a="2"/></d>
1159 </a>'''
1160  res = '''<a><d d="${a}"> a=2 b=1 c=${a} </d></a>'''
1161  self.assert_matches(self.quick_xacro(src), res)
1162 
1164  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1165  <xacro:property name="arg" value="42"/>
1166  <xacro:macro name="foo" params="arg:=^%s">${arg}</xacro:macro>
1167  <xacro:foo/>
1168  </a>'''
1169  res = '''<a>%s</a>'''
1170  self.assert_matches(self.quick_xacro(src % ''), res % '42')
1171  self.assert_matches(self.quick_xacro(src % '|'), res % '42')
1172  self.assert_matches(self.quick_xacro(src % '|6'), res % '42')
1173 
1175  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${2*'$(arg var)'}</a>'''
1176  res = '''<a>%s</a>'''
1177  self.assert_matches(self.quick_xacro(src, ['var:=xacro']), res % (2 * 'xacro'))
1178 
1180  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">$(arg ${'v'+'ar'})</a>'''
1181  res = '''<a>%s</a>'''
1182  self.assert_matches(self.quick_xacro(src, ['var:=xacro']), res % 'xacro')
1183 
1185  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro" xacro:targetNamespace="http://www.ros.org"/>'''
1186  res = '''<a xmlns="http://www.ros.org"/>'''
1187  self.assert_matches(self.quick_xacro(src), res)
1188 
1190  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><b xacro:targetNamespace="http://www.ros.org"/></a>'''
1191  res = '''<a><b/></a>'''
1192  self.assert_matches(self.quick_xacro(src), res)
1193 
1195  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${{xacro.{f}('colored', 'text', 2, 3.14)}}</a>'''
1196  res = '''<a/>'''
1197  for f in ['message', 'warning', 'error']:
1198  with capture_stderr(self.quick_xacro, src.format(f=f)) as (result, output):
1199  self.assert_matches(result, res)
1200  self.assertTrue('colored text 2 3.14' in output)
1201  self.assertRaises(xacro.XacroException, self.quick_xacro, src.format(f='fatal'))
1202 
1204  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1205  <xacro:include filename="raise.xacro"/>
1206  <xacro:outer/>
1207  </a>'''
1208  with self.assertRaises(xacro.XacroException):
1209  self.quick_xacro(src)
1210  with capture_stderr(xacro.print_location) as (_, output):
1211  expected = '''when instantiating macro: inner ({file})
1212 instantiated from: outer ({file})
1213 in file: string
1214 '''.format(file="./raise.xacro" if self.in_order else "???")
1215  self.assertEqual(output, expected)
1216 
1218  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro" xmlns:a="http://www.ros.org/a">
1219  <xacro:macro name="test">
1220  <xacro:include filename="namespace.xml"></xacro:include>
1221  </xacro:macro>
1222  <xacro:test />
1223 </a>'''
1224  res = '''<a xmlns:a="http://www.ros.org/a" xmlns:b="http://www.ros.org/b" />'''
1225  self.assert_matches(self.quick_xacro(src), res)
1226 
1227  def test_comments(self):
1228  original = '<!-- ${name} -->'
1229  processed = '<!-- foo -->'
1230  enabler = '<!-- xacro:eval-comments{suffix} -->'
1231  disabler = enabler.format(suffix=":off")
1232 
1233  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1234  <xacro:property name="name" value="foo"/>
1235  {enabler}{comment}{text}{comment}</a>'''
1236  result = '<a>{c1}{text}{c2}</a>'
1237  for enable, suffix, text in itertools.product([False, True], ["", ":on", ":off"], ["", " ", " text ", "<tag/>", disabler]):
1238  src_params = dict(comment=original, text=text,
1239  enabler=enabler.format(suffix=suffix) if enable else "")
1240  enabled = enable and suffix != ":off"
1241  res_params = dict(c1=processed if enabled else original, text="" if text == disabler else text,
1242  c2=processed if enabled and not text.strip() and text != disabler else original)
1243  try:
1244  self.assert_matches(self.quick_xacro(src.format(**src_params)), result.format(**res_params))
1245  except AssertionError as e:
1246  print("When evaluating\n{}".format(src.format(**src_params)))
1247  raise
1248 
1249 
1250 # test class for in-order processing
1252  def __init__(self, *args, **kwargs):
1253  super(TestXacroInorder, self).__init__(*args, **kwargs)
1254  self.in_order = True
1255 
1257  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1258  <xacro:macro name="scope"><xacro:include filename="location.xacro"/></xacro:macro>
1259  <xacro:scope/>
1260  </a>'''
1261  res = '''<a/>'''
1262  with capture_stderr(self.quick_xacro, src) as (result, output):
1263  self.assert_matches(result, res)
1264  self.assertTrue(output == '''when instantiating macro: scope (???)
1265 in file: ./location.xacro
1266 included from: string
1267 ''')
1268 
1270  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1271  <xacro:property name="str" value="sin"/>
1272  ${str}</a>'''
1273  res = '''<a>sin</a>'''
1274  with capture_stderr(self.quick_xacro, src) as (result, output):
1275  self.assert_matches(result, res)
1276  self.assertTrue("redefining global symbol: str" in output)
1277 
1279  doc = ('''<a xmlns:xacro="http://www.ros.org/xacro">
1280  <xacro:if value="false"><xacro:include filename="non-existent"/></xacro:if></a>''')
1281  self.assert_matches(self.quick_xacro(doc), '''<a/>''')
1282 
1284  self.assert_matches(self.quick_xacro('''
1285 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1286  <xacro:arg name="has_stuff" default="false"/>
1287  <xacro:if value="$(arg has_stuff)">
1288  <xacro:include file="$(find nonexistent_package)/stuff.urdf" />
1289  </xacro:if>
1290 </a>'''), '<a/>')
1291 
1292  # https://github.com/ros/xacro/issues/307
1294  src = '''<a version="1.0" xmlns:xacro="http://www.ros.org/wiki/xacro">
1295  <xacro:include filename="./include2.xacro" ns="B"/>
1296  <xacro:property name="ext" value="main"/>
1297  <xacro:property name="var" value="main"/>
1298  <xacro:B.bar arg="${ext}"/>
1299  <xacro:B.bar arg="${var}"/>
1300  <xacro:B.bar arg="${inner}"/>
1301 </a>'''
1302  res = '''<a version="1.0">
1303  <a arg="main" ext="main" var="2"/>
1304  <a arg="2" ext="main" var="2"/>
1305  <a arg="int" ext="main" var="2"/>
1306 </a>'''
1307  self.assert_matches(self.quick_xacro(src), res)
1308 
1310  src = '''
1311  <a xmlns:xacro="http://www.ros.org/xacro">
1312  <xacro:macro name="foo" params="file:=include1.xml"><xacro:include filename="${file}"/></xacro:macro>
1313  <xacro:foo/>
1314  <xacro:foo file="${xacro.abs_filename('include1.xml')}"/>
1315  <xacro:include filename="subdir/foo.xacro"/>
1316  <xacro:foo file="$(cwd)/subdir/include1.xml"/>
1317  </a>'''
1318  res = '''<a><inc1/><inc1/><subdir_inc1/><subdir_inc1/></a>'''
1319  self.assert_matches(self.quick_xacro(src), res)
1320 
1322  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1323  <xacro:include filename="include3.xacro" ns="C"/><xacro:C.foo/></a>'''
1324  res = '''<a><inc3 included="inner"/><inner/></a>'''
1325  self.assert_matches(self.quick_xacro(src), res)
1326 
1327  def test_dotify(self):
1328  src = '''
1329  <a xmlns:xacro="http://www.ros.org/xacro">
1330  <xacro:property name="settings" value="${xacro.dotify(dict(a=1, b=2))}"/>
1331  ${settings.a + settings.b}
1332  </a>'''
1333  res = '''<a>3</a>'''
1334  self.assert_matches(self.quick_xacro(src), res)
1335 
1337  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1338  <xacro:property name="prop" value="root"/>
1339  <root prop="${prop}"/>
1340 
1341  <xacro:include filename="A.xacro" ns="A"/>
1342  <root prop="${prop}" A.prop="${A.prop}" A.B.prop="${A.B.prop}" />
1343 
1344  <xacro:A.B.set/>
1345  <root prop="${prop}"/>
1346 </a>'''
1347  res = '''<a>
1348  <root prop="root"/>
1349  <A prop="B"/>
1350  <root A.B.prop="b" A.prop="B" prop="root"/>
1351  <root prop="macro"/>
1352 </a>'''
1353  self.assert_matches(self.quick_xacro(src), res)
1354 
1356  src = '''
1357 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1358  <xacro:property name="settings" value="${xacro.load_yaml('settings.yaml')}"/>
1359  <xacro:property name="type" value="$(arg type)"/>
1360  <xacro:include filename="${settings['arms'][type]['file']}"/>
1361  <xacro:call macro="${settings['arms'][type]['macro']}"/>
1362 </a>'''
1363  res = '''<a><{tag}/></a>'''
1364  for i in ['inc1', 'inc2']:
1365  self.assert_matches(self.quick_xacro(src, cli=['type:=%s' % i]),
1366  res.format(tag=i))
1367 
1369  src = '''
1370 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1371  <xacro:property name="settings" value="${xacro.load_yaml('settings.yaml')}"/>
1372  <xacro:property name="type" value="$(arg type)"/>
1373  <xacro:include filename="${settings.arms[type].file}"/>
1374  <xacro:call macro="${settings.arms[type].macro}"/>
1375 </a>'''
1376  res = '''<a><{tag}/></a>'''
1377  for i in ['inc1', 'inc2']:
1378  self.assert_matches(self.quick_xacro(src, cli=['type:=%s' % i]),
1379  res.format(tag=i))
1380 
1382  src = '''
1383 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1384  <xacro:property name="settings" value="${xacro.load_yaml('settings.yaml')}"/>
1385  <xacro:property name="bar" value="${settings.baz}"/>
1386  ${bar}
1387 </a>'''
1388  self.assertRaises(xacro.XacroException, self.quick_xacro, src)
1389 
1391  src = '''
1392 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1393  <xacro:property name="settings" value="${xacro.load_yaml('settings.yaml')}"/>
1394  <xacro:property name="bar" value="${settings.arms.inc2.props.port + 1}"/>
1395  ${bar}
1396 </a>'''
1397  res = '''<a>4243</a>'''
1398  self.assert_matches(self.quick_xacro(src), res)
1399 
1401  src = '''
1402 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1403  <xacro:property name="settings" value="${xacro.load_yaml('settings.yaml')}"/>
1404  ${'arms' in settings} ${'baz' in settings}
1405 </a>'''
1406  res = '''<a>True False</a>'''
1407  self.assert_matches(self.quick_xacro(src), res)
1408 
1410  src = '''
1411  <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1412  <xacro:property name="l" value="${xacro.load_yaml('list.yaml')}"/>
1413  ${l[0][1]} ${l[1][0]} ${l[2].a.A} ${l[2].a.B[0]} ${l[2].a.B[1]} ${l[2].b[0]}
1414  </a>'''
1415  res = '''<a>A2 B1 1 2 3 4</a>'''
1416  self.assert_matches(self.quick_xacro(src), res)
1417 
1419  src = '''
1420  <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1421  ${[2*item.val.x for item in xacro.load_yaml('list2.yaml')]}
1422  </a>'''
1423  res = '''<a>[2, 4]</a>'''
1424  self.assert_matches(self.quick_xacro(src), res)
1425 
1427  src = '''
1428 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1429  <xacro:property name="values" value="${xacro.load_yaml('constructors.yaml')}"/>
1430  <values a="${values.a}" b="${values.b}" c="${values.c}"/>
1431 </a>'''
1432  res = '''<a><values a="{}" b="{}" c="42"/></a>'''.format(math.pi, 0.5*math.pi)
1433  self.assert_matches(self.quick_xacro(src), res)
1434 
1436  for file in ['constructors_bad1.yaml', 'constructors_bad2.yaml']:
1437  src = '''
1438 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1439  <xacro:property name="values" value="${{xacro.load_yaml({file})}}"/>
1440  <values a="${{values.a}}" />
1441 </a>'''
1442  self.assertRaises(xacro.XacroException, self.quick_xacro, src.format(file=file))
1443 
1445  yaml = xacro.load_yaml('settings.yaml')
1446  self.assertTrue(hasattr(yaml, 'arms'))
1447  self.assertFalse(hasattr(yaml, 'this_key_does_not_exist'))
1448 
1450  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1451 <xacro:macro name="foo" params="arg:=${2*foo}">
1452  <xacro:property name="foo" value="-"/>
1453  <f val="${arg}"/>
1454 </xacro:macro>
1455 <xacro:property name="foo" value="${3*7}"/>
1456 <xacro:foo/>
1457 <xacro:property name="foo" value="*"/>
1458 <xacro:foo/>
1459 </a>'''
1460  res = '''<a>
1461 <f val="42"/><f val="**"/></a>'''
1462  self.assert_matches(self.quick_xacro(src), res)
1463 
1465  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1466 <xacro:property name="bar" value="unused"/>
1467 <xacro:property name="foo" value="unused"/>
1468 <xacro:macro name="foo" params="arg:=${foo}">
1469  <a val="${arg}"/>
1470 </xacro:macro>
1471 <xacro:foo/>
1472 <xacro:property name="bar" value="dummy"/>
1473 <xacro:property name="foo" value="21"/></a>'''
1474  with capture_stderr(self.quick_xacro, src, do_check_order=True) as (result, output):
1475  self.assertTrue("Document is incompatible to in-order processing." in output)
1476  self.assertTrue("foo" in output) # foo should be reported
1477  self.assertTrue("bar" not in output) # bar shouldn't be reported
1478 
1480  src = '''
1481  <a xmlns:xacro="http://www.ros.org/xacro">
1482  <xacro:property name="prop" default="false"/>
1483  <xacro:unless value="${prop}">
1484  <foo/>
1485  <xacro:property name="prop" value="true"/>
1486  </xacro:unless>
1487 
1488  <!-- second foo should be ignored -->
1489  <xacro:unless value="${prop}">
1490  <foo/>
1491  <xacro:property name="prop" value="true"/>
1492  </xacro:unless>
1493  </a>'''
1494  res = '''<a><foo/></a>'''
1495  self.assert_matches(self.quick_xacro(src), res)
1496 
1498  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">🍔 </a>'''
1499  self.assert_matches(self.quick_xacro(src), '''<a>🍔 </a>''')
1500 
1502  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1503 <xacro:property name="burger" value="🍔"/>
1504 ${burger}</a>'''
1505  res = '''<a>🍔</a>'''
1506  self.assert_matches(self.quick_xacro(src), res)
1507 
1509  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1510 <xacro:property name="burger" value="🍔"/>
1511 <b c="${burger}"/></a>'''
1512  res = '''<a><b c="🍔"/></a>'''
1513  self.assert_matches(self.quick_xacro(src), res)
1514 
1516  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1517 <xacro:property name="burger">
1518 🍔
1519 </xacro:property>
1520 <xacro:insert_block name="burger"/></a>'''
1521  res = '''<a>🍔</a>'''
1522  self.assert_matches(self.quick_xacro(src), res)
1523 
1525  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1526 <xacro:property name="burger" value="🍔"/>
1527 <xacro:if value="${burger == u'🍔'}">
1528 🍟
1529 </xacro:if>
1530 </a>'''
1531  res = '''<a>🍟</a>'''
1532  self.assert_matches(self.quick_xacro(src), res)
1533 
1535  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1536 <xacro:macro name="burger" params="how_many">
1537 ${u'🍔' * how_many}
1538 </xacro:macro>
1539 <xacro:burger how_many="4"/>
1540 </a>'''
1541  res = '''<a>🍔🍔🍔🍔</a>'''
1542  self.assert_matches(self.quick_xacro(src), res)
1543 
1545  # run the full xacro processing pipeline on a file with
1546  # unicode characters in it and make sure the output is correct
1547  test_dir = os.path.abspath(os.path.dirname(__file__))
1548  input_path = os.path.join(test_dir, 'emoji.xacro')
1549  tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
1550  output_path = os.path.join(tmp_dir_name, "out.xml")
1551  self.run_xacro(input_path, '-o', output_path)
1552  self.assertTrue(os.path.isfile(output_path))
1553  self.assert_matches(xml.dom.minidom.parse(output_path), '''<robot>🍔</robot>''')
1554  shutil.rmtree(tmp_dir_name) # clean up after ourselves
1555 
1557  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1558 <xacro:macro name="foo"><bar/></xacro:macro>
1559 <foo/></a>
1560 '''
1561  self.assert_matches(self.quick_xacro(src, ['--xacro-ns']), '<a><foo/></a>')
1562  self.assert_matches(self.quick_xacro(src), '<a><bar/></a>')
1563 
1565  self.assertRaises(xacro.XacroException, self.quick_xacro, '<a>a${</a>')
1566  self.assertRaises(xacro.XacroException, self.quick_xacro, '<a>${b</a>')
1567  self.assertRaises(xacro.XacroException, self.quick_xacro, '<a>${{}}</a>')
1568  self.assertRaises(xacro.XacroException, self.quick_xacro, '<a>a$(</a>')
1569  self.assertRaises(xacro.XacroException, self.quick_xacro, '<a>$(b</a>')
1570 
1572  template = '<a xmlns:xacro="http://www.ros.org/wiki/xacro"><xacro:property name="p" {} /> ${{p}} </a>'
1573 
1574  def check(attributes, expected, **kwargs):
1575  with subTest(msg='Checking ' + attributes):
1576  with self.assertRaises(xacro.XacroException) as cm:
1577  self.quick_xacro(template.format(attributes), **kwargs)
1578  self.assertEqual(str(cm.exception), expected)
1579 
1580  expected = 'Property attributes default, value, and remove are mutually exclusive: p'
1581  check('value="" default=""', expected)
1582  check('value="" remove="true"', expected)
1583  check('default="" remove="true"', expected)
1584  self.assert_matches(self.quick_xacro(template.format('default="42" remove="false"')), '<a>42</a>')
1585 
1586  expected = 'Property attribute {} supported with in-order option only'
1587  for name in ['default', 'remove']:
1588  check('{}="1"'.format(name), expected.format(name), in_order=False)
1589 
1591  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1592  <xacro:property name="p" default="1st" />
1593  <xacro:property name="p" remove="true" />
1594  <xacro:property name="p" default="2nd" />
1595  ${p}</a>'''
1596  self.assert_matches(self.quick_xacro(src), '<a>2nd</a>')
1597 
1598 
1599 if __name__ == '__main__':
1600  unittest.main()
def test_issue_63_fixed_with_inorder_processing(self)
Definition: test_xacro.py:1283
def test_error_reporting(self)
Definition: test_xacro.py:1203
def test_integer_if_statement(self)
Definition: test_xacro.py:673
def __init__(self, args, kwargs)
Definition: test_xacro.py:320
def test_include_glob(self)
Definition: test_xacro.py:598
def test_property_resolution_with_namespaced_include(self)
Definition: test_xacro.py:1293
def test_yaml_support_list_of_x(self)
Definition: test_xacro.py:1409
def test_include_deprecated(self)
Definition: test_xacro.py:609
def test_multiple_blocks(self)
Definition: test_xacro.py:549
def test_restricted_builtins_nested(self)
Definition: test_xacro.py:762
def xml_matches(a, b, ignore_nodes=[])
Definition: test_xacro.py:179
def test_normalize_whitespace_text(self)
Definition: test_xacro.py:217
def test_iterable_literals_plain(self)
Definition: test_xacro.py:1072
def test_equality_expression_in_if_statement(self)
Definition: test_xacro.py:726
def test_multiple_recursive_evaluation(self)
Definition: test_xacro.py:861
def test_macro_params_escaped_string(self)
Definition: test_xacro.py:481
def test_iterable_literals_eval(self)
Definition: test_xacro.py:1084
def test_target_namespace(self)
Definition: test_xacro.py:1184
def load_yaml(filename)
Definition: __init__.py:132
def test_consider_non_elements_if(self)
Definition: test_xacro.py:776
def test_expression_in_extension(self)
Definition: test_xacro.py:1179
def test_escaping_dollar_braces(self)
Definition: test_xacro.py:527
def test_consecutive_if(self)
Definition: test_xacro.py:720
def test_recursive_definition(self)
Definition: test_xacro.py:842
def test_normalize_whitespace_trim(self)
Definition: test_xacro.py:220
def capture_stderr(function, args, kwargs)
Definition: test_xacro.py:202
def test_xml_namespace_lifting(self)
Definition: test_xacro.py:1217
def test_insert_block_property(self)
Definition: test_xacro.py:579
def test_include_recursive(self)
Definition: test_xacro.py:625
def test_consider_non_elements_block(self)
Definition: test_xacro.py:783
def test_capture_stderr(self, args, kwargs)
Definition: test_xacro.py:211
def test_invalid_property_name(self)
Definition: test_xacro.py:371
def test_inorder_processing(self)
Definition: test_xacro.py:439
def test_unicode_literal_parsing(self)
Definition: test_xacro.py:1497
def test_issue_68_numeric_arg(self)
Definition: test_xacro.py:1107
def test_match_similar_numbers(self)
Definition: test_xacro.py:223
def test_ignore_comments(self)
Definition: test_xacro.py:244
def test_dynamic_macro_name_clash(self)
Definition: test_xacro.py:393
def test_no_evaluation(self)
Definition: test_xacro.py:738
def test_property_replacement(self)
Definition: test_xacro.py:487
def test_default_param(self)
Definition: test_xacro.py:936
def test_transitive_evaluation(self)
Definition: test_xacro.py:887
def test_property_if_statement(self)
Definition: test_xacro.py:707
def test_mismatch_different_dicts(self)
Definition: test_xacro.py:232
def test_yaml_custom_constructors(self)
Definition: test_xacro.py:1426
def test_comments(self)
Definition: test_xacro.py:1227
def check_macro_arg(self, s, param, forward, default, rest)
Definition: test_xacro.py:276
def test_macro_undefined(self)
Definition: test_xacro.py:413
def test_invalid_property_definitions(self)
Definition: test_xacro.py:1571
def test_default_arg_override(self)
Definition: test_xacro.py:1015
def test_just_a_dollar_sign(self)
Definition: test_xacro.py:531
def test_normalize_whitespace_nested(self)
Definition: test_xacro.py:241
def test_property_scope_global(self)
Definition: test_xacro.py:504
def test_empty_node_vs_whitespace(self)
Definition: test_xacro.py:235
def test_recursive_evaluation_wrong_order(self)
Definition: test_xacro.py:831
def test_target_namespace_only_from_root(self)
Definition: test_xacro.py:1189
def test_xacro_attribute(self)
Definition: test_xacro.py:428
def test_macro_name_with_colon(self)
Definition: test_xacro.py:1135
def test_safe_eval(self)
Definition: test_xacro.py:772
def test_message_functions(self)
Definition: test_xacro.py:1194
def test_include(self)
Definition: test_xacro.py:594
def __init__(self, args, kwargs)
Definition: test_xacro.py:367
def test_overwrite_globals(self)
Definition: test_xacro.py:1145
def test_mismatch_different_numbers(self)
Definition: test_xacro.py:226
def is_valid_name(name)
Definition: __init__.py:620
def tokenize(s, sep=', skip_empty=True)
Definition: __init__.py:152
def test_dynamic_macro_names(self)
Definition: test_xacro.py:383
def test_default_arg(self)
Definition: test_xacro.py:1000
def test_multiple_insert_blocks(self)
Definition: test_xacro.py:535
def test_property_scope_parent(self)
Definition: test_xacro.py:496
def test_unicode_property_attribute(self)
Definition: test_xacro.py:1508
def test_yaml_support_dotted_arith(self)
Definition: test_xacro.py:1390
def test_namespace_propagation(self)
Definition: test_xacro.py:1321
def test_xacro_element(self)
Definition: test_xacro.py:419
def test_no_double_evaluation(self)
Definition: test_xacro.py:1152
def test_float_if_statement(self)
Definition: test_xacro.py:693
def test_property_scope_parent_namespaced(self)
Definition: test_xacro.py:1336
def test_match_unordered_dicts(self)
Definition: test_xacro.py:229
def test_broken_input_doesnt_create_empty_output_file(self)
Definition: test_xacro.py:1047
def test_include_with_namespace(self)
Definition: test_xacro.py:634
def __init__(self, args, kwargs)
Definition: test_xacro.py:1252
def test_substitution_args_arg(self)
Definition: test_xacro.py:523
def test_property_forwarding(self)
Definition: test_xacro.py:1163
def quick_xacro(self, xml, cli=None, kwargs)
Definition: test_xacro.py:328
def test_default_arg_missing(self)
Definition: test_xacro.py:1030
def test_enforce_xacro_ns(self)
Definition: test_xacro.py:1096
def test_ignore_xacro_comments(self)
Definition: test_xacro.py:803
def all_attributes_match(a, b)
Definition: test_xacro.py:100
def test_param_missing(self)
Definition: test_xacro.py:984
def test_dynamic_macro_undefined(self)
Definition: test_xacro.py:407
def test_substitution_args_find(self)
Definition: test_xacro.py:519
def test_yaml_support_dotted_key_error(self)
Definition: test_xacro.py:1381
def test_greedy_property_evaluation(self)
Definition: test_xacro.py:854
def test_recursive_bad_math(self)
Definition: test_xacro.py:929
def test_recursive_evaluation(self)
Definition: test_xacro.py:820
def assert_matches(self, a, b)
Definition: test_xacro.py:325
def test_transitive_arg_evaluation(self)
Definition: test_xacro.py:1122
def test_redefine_global_symbol(self)
Definition: test_xacro.py:1269
def test_should_replace_before_macroexpand(self)
Definition: test_xacro.py:464
def test_unicode_property_block(self)
Definition: test_xacro.py:1515
def test_restricted_builtins(self)
Definition: test_xacro.py:758
def __init__(self, args, kwargs)
Definition: test_xacro.py:351
def test_integer_stays_integer(self)
Definition: test_xacro.py:568
def process_args(argv, require_input=True)
Definition: cli.py:65
def test_arg_function(self)
Definition: test_xacro.py:1042
def test_evaluate_macro_params_before_body(self)
Definition: test_xacro.py:475
def test_yaml_support_dotted_list_iterator(self)
Definition: test_xacro.py:1418
def test_invalid_if_statement(self)
Definition: test_xacro.py:667
def test_default_arg_empty(self)
Definition: test_xacro.py:1037
def test_yaml_support_key_in_dict(self)
Definition: test_xacro.py:1400
def test_create_subdirs(self)
Definition: test_xacro.py:1059
def parse_macro_arg(s)
Definition: __init__.py:644
def test_multiple_definition_and_evaluation(self)
Definition: test_xacro.py:874
def test_math_ignores_spaces(self)
Definition: test_xacro.py:515
def text_matches(a, b)
Definition: test_xacro.py:119
def test_extension_in_expression(self)
Definition: test_xacro.py:1174
def subTest(msg)
Definition: test_xacro.py:63
def nodes_match(a, b, ignore_nodes)
Definition: test_xacro.py:126
def test_default_param_override(self)
Definition: test_xacro.py:960
def test_double_underscore_property_name_raises(self)
Definition: test_xacro.py:376
def test_math_expressions(self)
Definition: test_xacro.py:748
def test_whitespace_vs_empty_node(self)
Definition: test_xacro.py:238
def test_from_issue(self)
Definition: test_xacro.py:914
def text_values_match(a, b)
Definition: test_xacro.py:70
def test_boolean_if_statement(self)
Definition: test_xacro.py:653
def test_include_from_variable(self)
Definition: test_xacro.py:616
def test_macro_default_param_evaluation_order(self)
Definition: test_xacro.py:1449
def test_include_nonexistent(self)
Definition: test_xacro.py:604
def test_multi_tree_evaluation(self)
Definition: test_xacro.py:900
def run_xacro(self, input_path, args)
Definition: test_xacro.py:341
def test_yaml_custom_constructors_illegal_expr(self)
Definition: test_xacro.py:1435


xacro
Author(s): Stuart Glaser, William Woodall, Robert Haschke
autogenerated on Thu May 25 2023 02:45:08