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 TestTable(unittest.TestCase):
211  def test_top(self):
212  top = xacro.Table()
213  self.assertTrue(top.top() is top)
214 
215  sub = xacro.Table(top)
216  self.assertTrue(sub.top() is top)
217 
218  def test_contains(self):
219  top = xacro.Table(dict(a=1))
220  self.assertTrue('a' in top)
221 
222  self.assertFalse('b' in top)
223  top['b'] = 2
224  self.assertTrue('b' in top)
225 
226  sub = xacro.Table(top)
227  sub['c'] = 3
228  for key in ['a', 'b', 'c']:
229  self.assertTrue(key in sub)
230 
231  def test_get(self):
232  top = xacro.Table(dict(a=1))
233  self.assertEqual(top['a'], 1)
234  top['b'] = 2
235  self.assertEqual(top['b'], 2)
236 
237  sub = xacro.Table(top)
238  sub['c'] = 3
239  for i, key in enumerate(['a', 'b', 'c']):
240  self.assertEqual(sub[key], i+1)
241 
242  def test_del(self):
243  top = xacro.Table(dict(a=1))
244  top['b'] = 2
245  sub = xacro.Table(top)
246  sub['b'] = 3
247 
248  self.assertEqual(sub['b'], 3)
249  self.assertEqual(top['b'], 2)
250 
251  del sub['b']
252  self.assertFalse('b' in sub)
253  self.assertFalse('b' in top)
254 
255  # redefining global symbol
256  with capture_stderr(xacro.Table.__setitem__, sub, 'a', 42) as (_, output):
257  self.assertTrue('redefining global symbol: a' in output)
258  self.assertEqual(sub['a'], 42)
259  self.assertEqual(sub.parent['a'], 1)
260 
261  # removing the redefine stops before globals!
262  with capture_stderr(xacro.Table.__delitem__, sub, 'a') as (_, output):
263  self.assertTrue('Cannot remove global symbol: a' in output)
264  self.assertEqual(sub['a'], 1)
265 
266 
267 class TestUtils(unittest.TestCase):
268  def test_capture_stderr(self, *args, **kwargs):
269  with capture_stderr(xacro.error, 'Hello World', alt_text='') as (result, output):
270  self.assertEqual(output, 'Hello World\n')
271 
272 
273 class TestMatchXML(unittest.TestCase):
275  self.assertTrue(text_matches("", " \t\n\r"))
276 
278  self.assertTrue(text_matches(" foo bar ", "foo \t\n\r bar"))
279 
281  self.assertTrue(text_matches("0.123456789", "0.123456788"))
282 
284  self.assertFalse(text_matches("0.123456789", "0.1234567879"))
285 
287  self.assertTrue(text_matches("{'a': 1, 'b': 2, 'c': 3}", "{'c': 3, 'b': 2, 'a': 1}"))
288 
290  self.assertFalse(text_matches("{'a': 1, 'b': 2, 'c': 3}", "{'c': 3, 'b': 2, 'a': 0}"))
291 
293  self.assertTrue(xml_matches('''<foo/>''', '''<foo> \t\n\r </foo>'''))
294 
296  self.assertTrue(xml_matches('''<foo> \t\n\r </foo>''', '''<foo/>'''))
297 
299  self.assertTrue(xml_matches('''<a><b/></a>''', '''<a>\n<b> </b> </a>'''))
300 
302  self.assertTrue(xml_matches('''<a><b/><!-- foo --> <!-- bar --></a>''',
303  '''<a><b/></a>''', [xml.dom.Node.COMMENT_NODE]))
304 
305 
306 class TestXacroFunctions(unittest.TestCase):
308  self.assertTrue(xacro.is_valid_name("_valid_name_123"))
309  self.assertFalse(xacro.is_valid_name('pass')) # syntactically correct keyword
310  self.assertFalse(xacro.is_valid_name('foo ')) # trailing whitespace
311  self.assertFalse(xacro.is_valid_name(' foo')) # leading whitespace
312  self.assertFalse(xacro.is_valid_name('1234')) # number
313  self.assertFalse(xacro.is_valid_name('1234abc')) # number and letters
314  self.assertFalse(xacro.is_valid_name('')) # empty string
315  self.assertFalse(xacro.is_valid_name(' ')) # whitespace only
316  self.assertFalse(xacro.is_valid_name('foo bar')) # several tokens
317  self.assertFalse(xacro.is_valid_name('no-dashed-names-for-you'))
318  self.assertFalse(xacro.is_valid_name('invalid.too')) # dot separates fields
319 
321  # define three nested dicts with the same names (keys)
322  content = {'simple': 'simple'}
323  ns2 = dict({k: v + '2' for k, v in content.items()})
324  ns1 = dict({k: v + '1' for k, v in content.items()})
325  ns1.update(ns2=ns2)
326  ns = dict(content)
327  ns.update(ns1=ns1)
328 
329  self.assertEqual(xacro.resolve_macro('simple', ns, ns), (ns, ns, 'simple'))
330  self.assertEqual(xacro.resolve_macro('ns1.simple', ns, ns), (ns1, ns1, 'simple1'))
331  self.assertEqual(xacro.resolve_macro('ns1.ns2.simple', ns, ns), (ns2, ns2, 'simple2'))
332 
333  def check_macro_arg(self, s, param, forward, default, rest):
334  p, v, r = xacro.parse_macro_arg(s)
335  self.assertEqual(p, param, msg="'{0}' != '{1}' parsing {2}".format(p, param, s))
336  if forward or default:
337  self.assertTrue(v is not None)
338  self.assertEqual(v[0], forward, msg="'{0}' != '{1}' parsing {2}".format(v[0], forward, s))
339  self.assertEqual(v[1], default, msg="'{0}' != '{1}' parsing {2}".format(v[1], default, s))
340  else:
341  self.assertTrue(v is None)
342  self.assertEqual(r, rest, msg="'{0}' != '{1}' parsing {2}".format(r, rest, s))
343 
345  for forward in ['', '^', '^|']:
346  defaults = ['', "'single quoted'", '"double quoted"', '${2 * (prop_with_spaces + 1)}', '$(substitution arg)',
347  "anything~w/o~space-'space allowed in quotes'(\"as here too\")", 'unquoted']
348  if forward == '^':
349  defaults = [''] # default allowed allowed afer ^|
350  for default in defaults:
351  seps = ['=', ':='] if forward or default else ['']
352  for sep in seps:
353  for rest in ['', ' ', ' bar', ' bar=42']:
354  s = 'foo{0}{1}{2}{3}'.format(sep, forward, default, rest)
355  self.check_macro_arg(s, 'foo', 'foo' if forward else None,
356  default if default else None,
357  rest.lstrip())
358 
360  for ws in [' ', ' \t ', ' \n ']:
361  self.check_macro_arg(ws + 'foo' + ws + 'bar=42' + ws, 'foo', None, None, 'bar=42' + ws)
362 
363  def test_tokenize(self):
364  tokens = ['ab', 'cd', 'ef']
365  for sep in [' ', ',', ';', ', ']:
366  self.assertEqual(xacro.tokenize(sep.join(tokens)), tokens)
367 
369  tokens = ' '.join(['ab', ' ', 'cd', 'ef'])
370  results = xacro.tokenize(tokens, sep=' ', skip_empty=False)
371  self.assertEqual(len(results), 5) # intermediate space becomes ' ' split into 2 empty fields
372  self.assertEqual(' '.join(results), tokens)
373 
374 
375 # base class providing some convenience functions
376 class TestXacroBase(unittest.TestCase):
377  def __init__(self, *args, **kwargs):
378  super(TestXacroBase, self).__init__(*args, **kwargs)
379  self.ignore_nodes = []
380 
381  def assert_matches(self, a, b):
382  self.assertTrue(xml_matches(a, b, self.ignore_nodes))
383 
384  def quick_xacro(self, xml, cli=None, **kwargs):
385  args = {}
386  if cli:
387  opts, _ = xacro.cli.process_args(cli, require_input=False)
388  args.update(vars(opts)) # initialize with cli args
389  args.update(kwargs) # explicit function args have highest priority
390 
391  doc = xacro.parse(xml)
392  xacro.filestack = None # Reset filestack
393  xacro.process_doc(doc, **args)
394  return doc
395 
396  def run_xacro(self, input_path, *args):
397  args = list(args)
398  subprocess.call(['xacro', input_path] + args)
399 
400 
401 # class to match XML docs while ignoring any comments
403  def __init__(self, *args, **kwargs):
404  super(TestXacroCommentsIgnored, self).__init__(*args, **kwargs)
405  self.ignore_nodes = [xml.dom.Node.COMMENT_NODE]
406 
407  def test_pr2(self):
408  # run xacro on the pr2 tree snapshot
409  test_dir = os.path.abspath(os.path.dirname(__file__))
410  pr2_xacro_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2.urdf.xacro')
411  pr2_golden_parse_path = os.path.join(test_dir, 'robots', 'pr2', 'pr2_1.11.4.xml')
412  self.assert_matches(
413  xml.dom.minidom.parse(pr2_golden_parse_path),
414  self.quick_xacro(open(pr2_xacro_path)))
415 
416 
417 # standard test class (including the test from TestXacroCommentsIgnored)
419  def __init__(self, *args, **kwargs):
420  super(TestXacroCommentsIgnored, self).__init__(*args, **kwargs)
421  self.ignore_nodes = []
422 
424  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
425  <xacro:property name="invalid.name"/></a>'''
426  self.assertRaises(xacro.XacroException, self.quick_xacro, src)
427 
429  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
430  <xacro:property name="__hidden"/></a>'''
431  with self.assertRaises(xacro.XacroException) as cm:
432  self.quick_xacro(src)
433  self.assertEqual(str(cm.exception), 'Property names must not start with double underscore:__hidden')
434 
436  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
437  <xacro:macro name="foo"><a>foo</a></xacro:macro>
438  <xacro:macro name="bar"><b>bar</b></xacro:macro>
439  <xacro:property name="var" value="%s"/>
440  <xacro:call macro="${var}"/></a>'''
441  res = '''<a>%s</a>'''
442  self.assert_matches(self.quick_xacro(src % "foo"), res % "<a>foo</a>")
443  self.assert_matches(self.quick_xacro(src % "bar"), res % "<b>bar</b>")
444 
446  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
447  <xacro:macro name="foo"><a name="foo"/></xacro:macro>
448  <xacro:macro name="call"><a name="bar"/></xacro:macro>
449  <xacro:call macro="foo"/></a>'''
450  self.assertRaises(xacro.XacroException, self.quick_xacro, src)
451 
453  self.assertRaises(xacro.XacroException,
454  self.quick_xacro,
455  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
456  <xacro:call macro="foo"/></a>''')
457 
459  self.assertRaises(xacro.XacroException,
460  self.quick_xacro,
461  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
462  <xacro:undefined><foo/><bar/></xacro:undefined></a>''')
463 
465  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
466  <xacro:macro name="foo" params="name"><xacro:element xacro:name="${name}"/></xacro:macro>
467  <xacro:foo name="A"/>
468  <xacro:foo name="B"/>
469 </a>'''
470  res = '''<a><A/><B/></a>'''
471  self.assert_matches(self.quick_xacro(src), res)
472 
474  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
475  <xacro:macro name="foo" params="name value">
476  <tag><xacro:attribute name="${name}" value="${value}"/></tag>
477  </xacro:macro>
478  <xacro:foo name="A" value="foo"/>
479  <xacro:foo name="B" value="bar"/>
480 </a>'''
481  res = '''<a><tag A="foo"/><tag B="bar"/></a>'''
482  self.assert_matches(self.quick_xacro(src), res)
483 
485  src = '''
486 <xml xmlns:xacro="http://www.ros.org/wiki/xacro">
487  <xacro:property name="foo" value="1.0"/>
488  <xacro:macro name="m" params="foo"><a foo="${foo}"/></xacro:macro>
489  <xacro:m foo="1 ${foo}"/>
490  <!-- now redefining the property and macro -->
491  <xacro:property name="foo" value="2.0"/>
492  <xacro:macro name="m" params="foo"><b bar="${foo}"/></xacro:macro>
493  <xacro:m foo="2 ${foo}"/>
494 </xml>'''
495  expected = '''
496 <xml>
497  <a foo="1 1.0"/>
498  <b bar="2 2.0"/>
499 </xml>
500 '''
501  self.assert_matches(self.quick_xacro(src), expected)
502 
504  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
505 <xacro:macro name="inner" params="*the_block">
506  <in_the_inner><xacro:insert_block name="the_block" /></in_the_inner>
507 </xacro:macro>
508 <xacro:macro name="outer" params="*the_block">
509  <in_the_outer><xacro:inner><xacro:insert_block name="the_block" /></xacro:inner></in_the_outer>
510 </xacro:macro>
511 <xacro:outer><woot /></xacro:outer></a>'''
512  res = '''<a><in_the_outer><in_the_inner><woot /></in_the_inner></in_the_outer></a>'''
513  self.assert_matches(self.quick_xacro(src), res)
514 
516  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
517  <xacro:macro name="foo" params="lst">${lst[-1]}</xacro:macro>
518  <xacro:foo lst="${[1,2,3]}"/></a>'''
519  self.assert_matches(self.quick_xacro(src), '''<a>3</a>''')
520 
522  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
523  <xacro:macro name="foo" params="a='1 -2' c=3"><bar a="${a}" c="${c}"/></xacro:macro>
524  <xacro:foo/></a>'''
525  self.assert_matches(self.quick_xacro(src), '''<a><bar a="1 -2" c="3"/></a>''')
526 
528  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
529  <xacro:property name="foo" value="42" />
530  <the_foo result="${foo}" />
531 </a>'''
532  res = '''<a><the_foo result="42"/></a>'''
533  self.assert_matches(self.quick_xacro(src), res)
534 
536  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
537  <xacro:macro name="foo" params="factor">
538  <xacro:property name="foo" value="${21*factor}" scope="parent"/>
539  </xacro:macro>
540  <xacro:foo factor="2"/><a foo="${foo}"/></a>'''
541  self.assert_matches(self.quick_xacro(src), '''<a><a foo="42"/></a>''')
542 
544  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
545  <xacro:macro name="foo" params="factor">
546  <xacro:macro name="bar">
547  <xacro:property name="foo" value="${21*factor}" scope="global"/>
548  </xacro:macro>
549  <xacro:bar/>
550  </xacro:macro>
551  <xacro:foo factor="2"/><a foo="${foo}"/></a>'''
552  self.assert_matches(self.quick_xacro(src), '''<a><a foo="42"/></a>''')
553 
555  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
556  <xacro:property name="abc" value="${[1,2,3]}"/>
557  <xacro:property name="xyz" value="${[abc[i]*abc[i] for i in [0,1,2]]}"/>
558  ${xyz}
559  </a>'''
560  self.assert_matches(self.quick_xacro(src), '''<a>[1, 4, 9]</a>''')
561 
563  src = '''<a><f v="${0.9 / 2 - 0.2}" /></a>'''
564  self.assert_matches(self.quick_xacro(src), '''<a><f v="0.25" /></a>''')
565 
567  self.assert_matches(self.quick_xacro('''<a><f v="$(find xacro)/test/test_xacro.py" /></a>'''),
568  '''<a><f v="''' + os.path.abspath((__file__).replace(".pyc",".py") + '''" /></a>'''))
569 
571  res = '''<a><f v="my_arg" /></a>'''
572  self.assert_matches(self.quick_xacro('''<a><f v="$(arg sub_arg)" /></a>''', cli=['sub_arg:=my_arg']), res)
573 
575  src = '''<a b="$${foo}" c="$$${foo}" d="text $${foo}" e="text $$${foo}" f="$$(pwd)" />'''
576  res = '''<a b="${foo}" c="$${foo}" d="text ${foo}" e="text $${foo}" f="$(pwd)" />'''
577  self.assert_matches(self.quick_xacro(src), res)
578 
580  src = '''<a b="$" c="text $" d="text $ text"/>'''
581  self.assert_matches(self.quick_xacro(src), src)
582 
584  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
585 <xacro:macro name="foo" params="*block">
586  <xacro:insert_block name="block" />
587  <xacro:insert_block name="block" />
588 </xacro:macro>
589 <xacro:foo>
590  <a_block />
591 </xacro:foo>
592 </a>'''), '''<a>
593  <a_block />
594  <a_block />
595 </a>''')
596 
598  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
599 <xacro:macro name="foo" params="*block{A} *block{B}">
600  <xacro:insert_block name="block1" />
601  <xacro:insert_block name="block2" />
602 </xacro:macro>
603 <xacro:foo>
604  <block1/>
605  <block2/>
606 </xacro:foo>
607 </a>'''
608  res = '''<a>
609 <block{A}/>
610 <block{B}/>
611 </a>'''
612  # test both, reversal and non-reversal of block order
613  for d in [dict(A='1', B='2'), dict(A='2', B='1')]:
614  self.assert_matches(self.quick_xacro(src.format(**d)), res.format(**d))
615 
617  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
618 <xacro:macro name="m" params="num">
619  <test number="${num}" />
620 </xacro:macro>
621 <xacro:m num="100" />
622 </a>'''), '''
623 <a>
624  <test number="100" />
625 </a>''')
626 
628  self.assert_matches(self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
629 <xacro:macro name="bar">bar</xacro:macro>
630 <xacro:property name="val" value="2" />
631 <xacro:property name="some_block">
632  <some_block attr="${val}"><xacro:bar/></some_block>
633 </xacro:property>
634 <foo>
635  <xacro:insert_block name="some_block" />
636 </foo>
637 </a>'''), '''
638 <a>
639 <foo><some_block attr="2">bar</some_block></foo>
640 </a>''')
641 
642  def test_include(self):
643  src = '''<a xmlns:xacro="http://www.ros.org/xacro"><xacro:include filename="include1.xml"/></a>'''
644  self.assert_matches(self.quick_xacro(src), '''<a><inc1/></a>''')
645 
646  def test_include_glob(self):
647  src = '''<a xmlns:xacro="http://www.ros.org/xacro"><xacro:include filename="include{glob}.xml"/></a>'''
648  res = '<a><inc1/><inc2/></a>'
649  for pattern in ['*', '?', '[1-2]']:
650  self.assert_matches(self.quick_xacro(src.format(glob=pattern)), res)
651 
653  self.assertRaises(xacro.XacroException,
654  self.quick_xacro, '''<a xmlns:xacro="http://www.ros.org/xacro">
655  <xacro:include filename="include-nada.xml" /></a>''')
656 
658  # <include> tags with some non-trivial content should not issue the deprecation warning
659  src = '''<a><include filename="nada"><tag/></include></a>'''
660  with capture_stderr(self.quick_xacro, src) as (result, output):
661  self.assert_matches(result, src)
662  self.assertEqual(output, '')
663 
665  doc = '''<a xmlns:xacro="http://www.ros.org/xacro">
666  <xacro:property name="file" value="include1.xml"/>
667  <xacro:include filename="${file}" /></a>'''
668  self.assert_matches(self.quick_xacro(doc), '''<a><inc1/></a>''')
669 
671  self.assert_matches(self.quick_xacro('''
672 <a xmlns:xacro="http://www.ros.org/xacro">
673  <xacro:include filename="include1.xml"/>
674  <xacro:include filename="./include1.xml"/>
675  <xacro:include filename="subdir/include-recursive.xacro"/>
676 </a>'''), '''
677 <a><inc1/><inc1/><subdir_inc1/><subdir_inc1/><inc1/></a>''')
678 
680  src = '''
681 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
682  <xacro:property name="var" value="main"/>
683  <xacro:include filename="include1.xacro" ns="A"/>
684  <xacro:include filename="include2.xacro" ns="B"/>
685  <xacro:A.foo/><xacro:B.foo/>
686  <main var="${var}" A="${2*A.var}" B="${B.var+1}"/>
687 </a>'''
688  res = '''
689 <a>
690  <inc1/><inc2/><main var="main" A="2" B="3"/>
691 </a>'''
692  self.assert_matches(self.quick_xacro(src), res)
693 
695  src = '''<root xmlns:xacro="http://www.ros.org/wiki/xacro">
696  <xacro:property name="prop" value="outer"/>
697  <xacro:macro name="foo">
698  <outer prop="${prop}"/>
699  <xacro:property name="prop" value="inner"/>
700  <xacro:macro name="foo">
701  <inner prop="${prop}"/>
702  </xacro:macro>
703  <xacro:foo/>
704  </xacro:macro>
705  <xacro:foo/>
706  <xacro:foo/>
707 </root>'''
708  res = '''<root>
709  <outer prop="outer"/>
710  <inner prop="inner"/>
711  <outer prop="outer"/>
712  <inner prop="inner"/>
713 </root>'''
714 
716  self.assert_matches(self.quick_xacro('''
717 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
718  <xacro:if value="false">
719  <a />
720  </xacro:if>
721  <xacro:if value="true">
722  <b />
723  </xacro:if>
724 </robot>'''), '''
725 <robot>
726  <b />
727 </robot>''')
728 
730  self.assertRaises(xacro.XacroException,
731  self.quick_xacro,
732  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
733  <xacro:if value="nonsense"><foo/></xacro:if></a>''')
734 
736  self.assert_matches(self.quick_xacro('''
737 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
738  <xacro:if value="${0*42}">
739  <a />
740  </xacro:if>
741  <xacro:if value="0">
742  <b />
743  </xacro:if>
744  <xacro:if value="${0}">
745  <c />
746  </xacro:if>
747  <xacro:if value="${1*2+3}">
748  <d />
749  </xacro:if>
750 </robot>'''), '''
751 <robot>
752  <d />
753 </robot>''')
754 
756  self.assert_matches(self.quick_xacro('''
757 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
758  <xacro:if value="${3*0.0}">
759  <a />
760  </xacro:if>
761  <xacro:if value="${3*0.1}">
762  <b />
763  </xacro:if>
764 </robot>'''), '''
765 <robot>
766  <b />
767 </robot>''')
768 
770  self.assert_matches(self.quick_xacro('''
771 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
772  <xacro:property name="condT" value="${True}"/>
773  <xacro:property name="condF" value="${False}"/>
774  <xacro:if value="${condF}"><a /></xacro:if>
775  <xacro:if value="${condT}"><b /></xacro:if>
776  <xacro:if value="${True}"><c /></xacro:if>
777 </robot>'''), '''
778 <robot>
779  <b /><c />
780 </robot>''')
781 
783  self.assert_matches(self.quick_xacro('''
784 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
785  <xacro:if value="1"><xacro:if value="0"><a>bar</a></xacro:if></xacro:if>
786 </a>'''), '''<a/>''')
787 
789  self.assert_matches(self.quick_xacro('''
790 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
791  <xacro:property name="var" value="useit"/>
792  <xacro:if value="${var == 'useit'}"><foo>bar</foo></xacro:if>
793  <xacro:if value="${'use' in var}"><bar>foo</bar></xacro:if>
794 </a>'''), '''
795 <a>
796  <foo>bar</foo>
797  <bar>foo</bar>
798 </a>''')
799 
801  self.assert_matches(self.quick_xacro('''
802 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
803  <xacro:property name="xyz" value="5 -2"/>
804  <foo>${xyz}</foo>
805 </a>'''), '''
806 <a>
807  <foo>5 -2</foo>
808 </a>''')
809 
811  self.assert_matches(self.quick_xacro('''
812 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
813  <foo function="${1. + sin(pi)}"/>
814 </a>'''), '''
815 <a>
816  <foo function="1.0"/>
817 </a>''')
818 
819  # https://realpython.com/python-eval-function/#minimizing-the-security-issues-of-eval
821  self.assertRaises(xacro.XacroException, self.quick_xacro,
822  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${__import__('math')}</a>''')
823 
825  self.assertRaises(xacro.XacroException, self.quick_xacro,
826  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
827 <xacro:macro name="foo" params="arg">
828  <xacro:property name="prop" value="${arg}"/>
829  ${__import__('math')}
830 </xacro:macro>
831 <xacro:foo/>
832 </a>''')
833 
834  def test_safe_eval(self):
835  self.assertRaises(xacro.XacroException, self.quick_xacro,
836  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${"".__class__.__base__.__subclasses__()}</a>''')
837 
839  self.assert_matches(self.quick_xacro('''
840 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
841  <xacro:if value="1"><!-- comment --> text <b>bar</b></xacro:if>
842 </a>'''), '''
843 <a><!-- comment --> text <b>bar</b></a>''')
844 
846  self.assert_matches(
847  self.quick_xacro('''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
848 <xacro:macro name="foo" params="*block">
849  <!-- comment -->
850  foo
851  <xacro:insert_block name="block" />
852 </xacro:macro>
853 <xacro:foo>
854  <!-- ignored comment -->
855  ignored text
856  <a_block />
857 </xacro:foo>
858 </a>'''), '''
859 <a>
860  <!-- comment -->
861  foo
862  <a_block />
863 </a>''')
864 
866  self.assert_matches(self.quick_xacro('''
867 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
868  <!-- A -->
869 
870  <!-- ignore multiline comments before any xacro tag -->
871  <!-- ignored -->
872  <xacro:property name="foo" value="1"/>
873  <!-- ignored -->
874  <xacro:if value="1"><!-- B --></xacro:if>
875  <!-- ignored -->
876  <xacro:macro name="foo"><!-- C --></xacro:macro>
877  <!-- ignored -->
878  <xacro:foo/>
879 </a>'''), '''
880 <a><!-- A --><!-- B --><!-- C --></a>''')
881 
883  self.assert_matches(self.quick_xacro('''
884 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
885  <xacro:property name="a" value=" 42 "/>
886  <xacro:property name="a2" value="${ 2 * a }"/>
887  <a doubled="${a2}"/>
888 </robot>'''), '''
889 <robot>
890  <a doubled="84"/>
891 </robot>''')
892 
894  self.assert_matches(self.quick_xacro('''
895 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
896  <xacro:property name="a2" value="${2*a}"/>
897  <xacro:property name="a" value="42"/>
898  <a doubled="${a2}"/>
899 </robot>'''), '''
900 <robot>
901  <a doubled="84"/>
902 </robot>''')
903 
905  with self.assertRaises(xacro.XacroException) as cm:
906  self.quick_xacro('''
907 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
908  <xacro:property name="a" value="${a2}"/>
909  <xacro:property name="a2" value="${2*a}"/>
910  <a doubled="${a2}"/>
911 </robot>''')
912  msg = str(cm.exception)
913  self.assertTrue(msg.startswith('circular variable definition: a2 -> a -> a2\n'
914  'Consider disabling lazy evaluation via lazy_eval="false"'))
915 
917  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
918  <xacro:property name="s" value="AbCd"/>
919  <xacro:property name="s" value="${s.lower()}" lazy_eval="false"/>
920  ${s}</a>'''
921  self.assert_matches(self.quick_xacro(src), '<a>abcd</a>')
922 
924  self.assert_matches(self.quick_xacro('''
925 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
926  <xacro:property name="a" value="1"/>
927  <xacro:property name="b" value="2"/>
928  <xacro:property name="c" value="3"/>
929  <xacro:property name="product" value="${a*b*c}"/>
930  <answer product="${product}"/>
931 </robot>'''), '''
932 <robot>
933  <answer product="6"/>
934 </robot>''')
935 
937  self.assert_matches(self.quick_xacro('''
938 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
939  <xacro:property name="a" value="42"/>
940  <xacro:property name="b" value="${a}"/>
941  <xacro:property name="b" value="${-a}"/>
942  <xacro:property name="b" value="${a}"/>
943  <answer b="${b} ${b} ${b}"/>
944 </robot>'''), '''
945 <robot>
946  <answer b="42 42 42"/>
947 </robot>''')
948 
950  self.assert_matches(self.quick_xacro('''
951 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
952  <xacro:property name="a" value="42"/>
953  <xacro:property name="b" value="${a}"/>
954  <xacro:property name="c" value="${b}"/>
955  <xacro:property name="d" value="${c}"/>
956  <answer d="${d}"/>
957 </robot>'''), '''
958 <robot>
959  <answer d="42"/>
960 </robot>''')
961 
963  self.assert_matches(self.quick_xacro('''
964 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
965  <xacro:property name="a" value="42"/>
966  <xacro:property name="b" value="2.1"/>
967  <xacro:property name="c" value="${a}"/>
968  <xacro:property name="d" value="${b}"/>
969  <xacro:property name="f" value="${c*d}"/>
970  <answer f="${f}"/>
971 </robot>'''), '''
972 <robot>
973  <answer f="88.2"/>
974 </robot>''')
975 
976  def test_from_issue(self):
977  self.assert_matches(self.quick_xacro('''
978 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
979  <xacro:property name="x" value="42"/>
980  <xacro:property name="wheel_width" value="${x}"/>
981  <link name="my_link">
982  <origin xyz="0 0 ${wheel_width/2}"/>
983  </link>
984 </robot>'''), '''
985 <robot>
986  <link name="my_link">
987  <origin xyz="0 0 21.0"/>
988  </link>
989 </robot>''')
990 
992  self.assertRaises(xacro.XacroException, self.quick_xacro, '''
993 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
994  <xacro:property name="x" value="0"/>
995  <tag badness="${1/x}"/>
996 </robot>''')
997 
999  self.assert_matches(self.quick_xacro('''
1000 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
1001  <xacro:macro name="fixed_link" params="parent_link:=base_link child_link *joint_pose">
1002  <link name="${child_link}"/>
1003  <joint name="${child_link}_joint" type="fixed">
1004  <xacro:insert_block name="joint_pose" />
1005  <parent link="${parent_link}"/>
1006  <child link="${child_link}" />
1007  </joint>
1008  </xacro:macro>
1009  <xacro:fixed_link child_link="foo">
1010  <origin xyz="0 0 0" rpy="0 0 0" />
1011  </xacro:fixed_link >
1012 </robot>'''), '''
1013 <robot>
1014  <link name="foo"/>
1015  <joint name="foo_joint" type="fixed">
1016  <origin rpy="0 0 0" xyz="0 0 0"/>
1017  <parent link="base_link"/>
1018  <child link="foo"/>
1019  </joint>
1020 </robot>''')
1021 
1023  self.assert_matches(self.quick_xacro('''
1024 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
1025  <xacro:macro name="fixed_link" params="parent_link:=base_link child_link *joint_pose">
1026  <link name="${child_link}"/>
1027  <joint name="${child_link}_joint" type="fixed">
1028  <xacro:insert_block name="joint_pose" />
1029  <parent link="${parent_link}"/>
1030  <child link="${child_link}" />
1031  </joint>
1032  </xacro:macro>
1033  <xacro:fixed_link child_link="foo" parent_link="bar">
1034  <origin xyz="0 0 0" rpy="0 0 0" />
1035  </xacro:fixed_link >
1036 </robot>'''), '''
1037 <robot>
1038  <link name="foo"/>
1039  <joint name="foo_joint" type="fixed">
1040  <origin rpy="0 0 0" xyz="0 0 0"/>
1041  <parent link="bar"/>
1042  <child link="foo"/>
1043  </joint>
1044 </robot>''')
1045 
1047  self.assertRaises(xacro.XacroException, self.quick_xacro, '''
1048 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
1049  <xacro:macro name="fixed_link" params="parent_link child_link *joint_pose">
1050  <link name="${child_link}"/>
1051  <joint name="${child_link}_joint" type="fixed">
1052  <xacro:insert_block name="joint_pose" />
1053  <parent link="${parent_link}"/>
1054  <child link="${child_link}" />
1055  </joint>
1056  </xacro:macro>
1057  <xacro:fixed_link child_link="foo">
1058  <origin xyz="0 0 0" rpy="0 0 0" />
1059  </xacro:fixed_link >
1060 </robot>''')
1061 
1062  def test_default_arg(self):
1063  self.assert_matches(self.quick_xacro('''
1064 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
1065  <xacro:arg name="foo" default="2"/>
1066  <link name="my_link">
1067  <origin xyz="0 0 $(arg foo)"/>
1068  </link>
1069 </robot>
1070 '''), '''
1071 <robot>
1072  <link name="my_link">
1073  <origin xyz="0 0 2"/>
1074  </link>
1075 </robot>''')
1076 
1078  self.assert_matches(self.quick_xacro('''
1079 <robot xmlns:xacro="http://www.ros.org/wiki/xacro">
1080  <xacro:arg name="foo" default="2"/>
1081  <link name="my_link">
1082  <origin xyz="0 0 $(arg foo)"/>
1083  </link>
1084 </robot>
1085 ''', ['foo:=4']), '''
1086 <robot>
1087  <link name="my_link">
1088  <origin xyz="0 0 4"/>
1089  </link>
1090 </robot>''')
1091 
1093  self.assertRaises(Exception, self.quick_xacro, '''
1094 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1095  <a arg="$(arg foo)"/>
1096 </a>
1097 ''')
1098 
1100  self.assert_matches(self.quick_xacro('''
1101 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1102 <xacro:arg name="foo" default=""/>$(arg foo)</a>'''), '''<a/>''')
1103 
1105  self.assert_matches(self.quick_xacro('''
1106 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1107 <xacro:arg name="foo" default="bar"/>${xacro.arg('foo')}</a>'''), '<a>bar</a>')
1108 
1110  self.assertRaises(xml.parsers.expat.ExpatError, self.quick_xacro,
1111  '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1112  <xacro:include filename="broken.xacro"/></a>''')
1113  self.assertEqual(xacro.filestack, [None, './broken.xacro'])
1114 
1116  # run xacro on broken input file to make sure we don't create an
1117  # empty output file
1118  tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
1119  output_path = os.path.join(tmp_dir_name, "should_not_exist")
1120  self.run_xacro('broken.xacro', '-o', output_path)
1121 
1122  output_file_created = os.path.isfile(output_path)
1123  shutil.rmtree(tmp_dir_name) # clean up after ourselves
1124 
1125  self.assertFalse(output_file_created)
1126 
1128  # run xacro to create output file in non-existent directory
1129  # to make sure this directory will be created by xacro
1130  tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
1131  shutil.rmtree(tmp_dir_name) # ensure directory is removed
1132  output_path = os.path.join(tmp_dir_name, "out")
1133  self.run_xacro('include1.xml', '-o', output_path)
1134 
1135  output_file_created = os.path.isfile(output_path)
1136  shutil.rmtree(tmp_dir_name) # clean up after ourselves
1137 
1138  self.assertTrue(output_file_created)
1139 
1141  self.assert_matches(self.quick_xacro('''
1142 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1143  <xacro:property name="l" value="[0, 1+1, 2]"/>
1144  <xacro:property name="t" value="(0,1+1,2)"/>
1145  <xacro:property name="d" value="{'a':0, 'b':1+1, 'c':2}"/>
1146  <a list="${l}" tuple="${t}" dict="${d}"/>
1147 </a>'''), '''
1148 <a>
1149  <a list="[0, 1+1, 2]" tuple="(0,1+1,2)" dict="{'a':0, 'b':1+1, 'c':2}"/>
1150 </a>''')
1151 
1153  self.assert_matches(self.quick_xacro('''
1154 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1155  <xacro:property name="l" value="${[0, 1+1, 2]}"/>
1156  <xacro:property name="t" value="${(0,1+1,2)}"/>
1157  <xacro:property name="d" value="${dict(a=0, b=1+1, c=2)}"/>
1158  <a list="${l}" tuple="${t}" dict="${d}"/>
1159 </a>'''), '''
1160 <a>
1161  <a list="[0, 2, 2]" tuple="(0, 2, 2)" dict="{'a': 0, 'c': 2, 'b': 2}"/>
1162 </a>''')
1163 
1165  self.assert_matches(self.quick_xacro('''
1166 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1167  <xacro:property name="f" value="1.23"/>
1168  <xacro:property name="i" value="123"/>
1169  <xacro:property name="s" value="1_2_3"/>
1170  float=${f+1} int=${i+1} string=${s}
1171 </a>'''), '''
1172 <a>
1173  float=2.23 int=124 string=1_2_3
1174 </a>''')
1175 
1177  self.assert_matches(self.quick_xacro('''
1178 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1179  <arg name="foo" value="bar"/>
1180  <include filename="foo"/>
1181 </a>''', xacro_ns=False), '''
1182 <a>
1183  <arg name="foo" value="bar"/>
1184  <include filename="foo"/>
1185 </a>''')
1186 
1188  # If a property is assigned from a substitution arg, then this properties' value was
1189  # no longer converted to a python type, so that e.g. 0.5 remained u'0.5'.
1190  # If this property is then used in a numerical expression an exception is thrown.
1191  self.assert_matches(self.quick_xacro('''
1192 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1193  <xacro:arg name="foo" default="0.5"/>
1194  <xacro:property name="prop" value="$(arg foo)" />
1195  <a prop="${prop-0.3}"/>
1196 </a>
1197 '''), '''
1198 <a>
1199  <a prop="0.2"/>
1200 </a>''')
1201 
1203  self.assert_matches(self.quick_xacro('''
1204 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1205  <xacro:arg name="foo" default="0.5"/>
1206  <xacro:arg name="bar" default="$(arg foo)"/>
1207  <xacro:property name="prop" value="$(arg bar)" />
1208  <a prop="${prop-0.3}"/>
1209 </a>
1210 '''), '''
1211 <a>
1212  <a prop="0.2"/>
1213 </a>''')
1214 
1216  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1217  <xacro:macro name="xacro:my_macro"><foo/></xacro:macro>
1218  <xacro:my_macro/>
1219  </a>'''
1220  res = '''<a><foo/></a>'''
1221  with capture_stderr(self.quick_xacro, src) as (result, output):
1222  self.assert_matches(result, res)
1223  self.assertTrue("macro names must not contain prefix 'xacro:'" in output)
1224 
1226  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1227  <xacro:property name="pi" value="3.14"/></a>'''
1228  with capture_stderr(self.quick_xacro, src) as (result, output):
1229  self.assert_matches(result, '<a/>')
1230  self.assertTrue(output)
1231 
1233  src = '''
1234 <a xmlns:xacro="http://www.ros.org/xacro">
1235  <xacro:macro name="foo" params="a b:=${a} c:=$${a}"> a=${a} b=${b} c=${c} </xacro:macro>
1236  <xacro:property name="a" value="1"/>
1237  <xacro:property name="d" value="$${a}"/>
1238  <d d="${d}"><xacro:foo a="2"/></d>
1239 </a>'''
1240  res = '''<a><d d="${a}"> a=2 b=1 c=${a} </d></a>'''
1241  self.assert_matches(self.quick_xacro(src), res)
1242 
1244  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1245  <xacro:property name="arg" value="42"/>
1246  <xacro:macro name="foo" params="arg:=^%s">${arg}</xacro:macro>
1247  <xacro:foo/>
1248  </a>'''
1249  res = '''<a>%s</a>'''
1250  self.assert_matches(self.quick_xacro(src % ''), res % '42')
1251  self.assert_matches(self.quick_xacro(src % '|'), res % '42')
1252  self.assert_matches(self.quick_xacro(src % '|6'), res % '42')
1253 
1255  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${2*'$(arg var)'}</a>'''
1256  res = '''<a>%s</a>'''
1257  self.assert_matches(self.quick_xacro(src, ['var:=xacro']), res % (2 * 'xacro'))
1258 
1260  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">$(arg ${'v'+'ar'})</a>'''
1261  res = '''<a>%s</a>'''
1262  self.assert_matches(self.quick_xacro(src, ['var:=xacro']), res % 'xacro')
1263 
1265  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro" xacro:targetNamespace="http://www.ros.org"/>'''
1266  res = '''<a xmlns="http://www.ros.org"/>'''
1267  self.assert_matches(self.quick_xacro(src), res)
1268 
1270  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><b xacro:targetNamespace="http://www.ros.org"/></a>'''
1271  res = '''<a><b/></a>'''
1272  self.assert_matches(self.quick_xacro(src), res)
1273 
1275  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${{xacro.{f}('colored', 'text', 2, 3.14)}}</a>'''
1276  res = '''<a/>'''
1277  for f in ['message', 'warning', 'error']:
1278  with capture_stderr(self.quick_xacro, src.format(f=f)) as (result, output):
1279  self.assert_matches(result, res)
1280  self.assertTrue('colored text 2 3.14' in output)
1281  self.assertRaises(xacro.XacroException, self.quick_xacro, src.format(f='fatal'))
1282 
1284  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1285  <xacro:include filename="raise.xacro"/>
1286  <xacro:outer/>
1287  </a>'''
1288  with self.assertRaises(xacro.XacroException):
1289  self.quick_xacro(src)
1290  with capture_stderr(xacro.print_location) as (_, output):
1291  expected = '''when instantiating macro: inner ({file})
1292 instantiated from: outer ({file})
1293 in file: string
1294 '''.format(file="./raise.xacro")
1295  self.assertEqual(output, expected)
1296 
1298  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro" xmlns:a="http://www.ros.org/a">
1299  <xacro:macro name="test">
1300  <xacro:include filename="namespace.xml"></xacro:include>
1301  </xacro:macro>
1302  <xacro:test />
1303 </a>'''
1304  res = '''<a xmlns:a="http://www.ros.org/a" xmlns:b="http://www.ros.org/b" />'''
1305  self.assert_matches(self.quick_xacro(src), res)
1306 
1307  def test_comments(self):
1308  original = '<!-- ${name} -->'
1309  processed = '<!-- foo -->'
1310  enabler = '<!-- xacro:eval-comments{suffix} -->'
1311  disabler = enabler.format(suffix=":off")
1312 
1313  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1314  <xacro:property name="name" value="foo"/>
1315  {enabler}{comment}{text}{comment}</a>'''
1316  result = '<a>{c1}{text}{c2}</a>'
1317  for enable, suffix, text in itertools.product([False, True], ["", ":on", ":off"], ["", " ", " text ", "<tag/>", disabler]):
1318  src_params = dict(comment=original, text=text,
1319  enabler=enabler.format(suffix=suffix) if enable else "")
1320  enabled = enable and suffix != ":off"
1321  res_params = dict(c1=processed if enabled else original, text="" if text == disabler else text,
1322  c2=processed if enabled and not text.strip() and text != disabler else original)
1323  try:
1324  self.assert_matches(self.quick_xacro(src.format(**src_params)), result.format(**res_params))
1325  except AssertionError as e:
1326  print("When evaluating\n{}".format(src.format(**src_params)))
1327  raise
1328 
1330  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1331  <xacro:macro name="scope"><xacro:include filename="location.xacro"/></xacro:macro>
1332  <xacro:scope/>
1333  </a>'''
1334  res = '''<a/>'''
1335  with capture_stderr(self.quick_xacro, src) as (result, output):
1336  self.assert_matches(result, res)
1337  self.assertTrue(output == '''when instantiating macro: scope (???)
1338 in file: ./location.xacro
1339 included from: string
1340 ''')
1341 
1343  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1344  <xacro:property name="str" value="sin"/>
1345  ${str}</a>'''
1346  res = '''<a>sin</a>'''
1347  with capture_stderr(self.quick_xacro, src) as (result, output):
1348  self.assert_matches(result, res)
1349  self.assertTrue("redefining global symbol: str" in output)
1350 
1352  doc = ('''<a xmlns:xacro="http://www.ros.org/xacro">
1353  <xacro:if value="false"><xacro:include filename="non-existent"/></xacro:if></a>''')
1354  self.assert_matches(self.quick_xacro(doc), '''<a/>''')
1355 
1357  self.assert_matches(self.quick_xacro('''
1358 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1359  <xacro:arg name="has_stuff" default="false"/>
1360  <xacro:if value="$(arg has_stuff)">
1361  <xacro:include file="$(find nonexistent_package)/stuff.urdf" />
1362  </xacro:if>
1363 </a>'''), '<a/>')
1364 
1365  # https://github.com/ros/xacro/issues/307
1367  src = '''<a version="1.0" xmlns:xacro="http://www.ros.org/wiki/xacro">
1368  <xacro:include filename="./include2.xacro" ns="B"/>
1369  <xacro:property name="ext" value="main"/>
1370  <xacro:property name="var" value="main"/>
1371  <xacro:B.bar arg="${ext}"/>
1372  <xacro:B.bar arg="${var}"/>
1373  <xacro:B.bar arg="${inner}"/>
1374 </a>'''
1375  res = '''<a version="1.0">
1376  <a arg="main" ext="main" var="2"/>
1377  <a arg="2" ext="main" var="2"/>
1378  <a arg="int" ext="main" var="2"/>
1379 </a>'''
1380  self.assert_matches(self.quick_xacro(src), res)
1381 
1383  src = '''
1384  <a xmlns:xacro="http://www.ros.org/xacro">
1385  <xacro:macro name="foo" params="file:=include1.xml"><xacro:include filename="${file}"/></xacro:macro>
1386  <xacro:foo/>
1387  <xacro:foo file="${xacro.abs_filename('include1.xml')}"/>
1388  <xacro:include filename="subdir/foo.xacro"/>
1389  <xacro:foo file="$(cwd)/subdir/include1.xml"/>
1390  </a>'''
1391  res = '''<a><inc1/><inc1/><subdir_inc1/><subdir_inc1/></a>'''
1392  self.assert_matches(self.quick_xacro(src), res)
1393 
1395  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1396  <xacro:include filename="include3.xacro" ns="C"/><xacro:C.foo/></a>'''
1397  res = '''<a><inc3 included="inner"/><inner/></a>'''
1398  self.assert_matches(self.quick_xacro(src), res)
1399 
1400  def test_dotify(self):
1401  src = '''
1402  <a xmlns:xacro="http://www.ros.org/xacro">
1403  <xacro:property name="settings" value="${xacro.dotify(dict(a=1, b=2))}"/>
1404  ${settings.a + settings.b}
1405  </a>'''
1406  res = '''<a>3</a>'''
1407  self.assert_matches(self.quick_xacro(src), res)
1408 
1410  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1411  <xacro:property name="prop" value="root"/>
1412  <root prop="${prop}"/>
1413 
1414  <xacro:include filename="A.xacro" ns="A"/>
1415  <root prop="${prop}" A.prop="${A.prop}" A.B.prop="${A.B.prop}" />
1416 
1417  <xacro:A.B.set/>
1418  <root prop="${prop}"/>
1419 </a>'''
1420  res = '''<a>
1421  <root prop="root"/>
1422  <A prop="B"/>
1423  <root A.B.prop="b" A.prop="B" prop="root"/>
1424  <root prop="macro"/>
1425 </a>'''
1426  self.assert_matches(self.quick_xacro(src), res)
1427 
1429  src = '''
1430 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1431  <xacro:property name="settings" value="${xacro.load_yaml('settings.yaml')}"/>
1432  <xacro:property name="type" value="$(arg type)"/>
1433  <xacro:include filename="${settings['arms'][type]['file']}"/>
1434  <xacro:call macro="${settings['arms'][type]['macro']}"/>
1435 </a>'''
1436  res = '''<a><{tag}/></a>'''
1437  for i in ['inc1', 'inc2']:
1438  self.assert_matches(self.quick_xacro(src, cli=['type:=%s' % i]),
1439  res.format(tag=i))
1440 
1442  src = '''
1443 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1444  <xacro:property name="settings" value="${xacro.load_yaml('settings.yaml')}"/>
1445  <xacro:property name="type" value="$(arg type)"/>
1446  <xacro:include filename="${settings.arms[type].file}"/>
1447  <xacro:call macro="${settings.arms[type].macro}"/>
1448 </a>'''
1449  res = '''<a><{tag}/></a>'''
1450  for i in ['inc1', 'inc2']:
1451  self.assert_matches(self.quick_xacro(src, cli=['type:=%s' % i]),
1452  res.format(tag=i))
1453 
1455  src = '''
1456 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1457  <xacro:property name="settings" value="${xacro.load_yaml('settings.yaml')}"/>
1458  <xacro:property name="bar" value="${settings.baz}"/>
1459  ${bar}
1460 </a>'''
1461  self.assertRaises(xacro.XacroException, self.quick_xacro, src)
1462 
1464  src = '''
1465 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1466  <xacro:property name="settings" value="${xacro.load_yaml('settings.yaml')}"/>
1467  <xacro:property name="bar" value="${settings.arms.inc2.props.port + 1}"/>
1468  ${bar}
1469 </a>'''
1470  res = '''<a>4243</a>'''
1471  self.assert_matches(self.quick_xacro(src), res)
1472 
1474  src = '''
1475 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1476  <xacro:property name="settings" value="${xacro.load_yaml('settings.yaml')}"/>
1477  ${'arms' in settings} ${'baz' in settings}
1478 </a>'''
1479  res = '''<a>True False</a>'''
1480  self.assert_matches(self.quick_xacro(src), res)
1481 
1483  src = '''
1484  <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1485  <xacro:property name="l" value="${xacro.load_yaml('list.yaml')}"/>
1486  ${l[0][1]} ${l[1][0]} ${l[2].a.A} ${l[2].a.B[0]} ${l[2].a.B[1]} ${l[2].b[0]}
1487  </a>'''
1488  res = '''<a>A2 B1 1 2 3 4</a>'''
1489  self.assert_matches(self.quick_xacro(src), res)
1490 
1492  src = '''
1493  <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1494  ${[2*item.val.x for item in xacro.load_yaml('list2.yaml')]}
1495  </a>'''
1496  res = '''<a>[2, 4]</a>'''
1497  self.assert_matches(self.quick_xacro(src), res)
1498 
1500  src = '''
1501 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1502  <xacro:property name="values" value="${xacro.load_yaml('constructors.yaml')}"/>
1503  <values no_tag="${values.no_tag}" angles="${values.angles}" lengths="${values.lengths}"/>
1504 </a>'''
1505  res = '''<a><values no_tag="42" angles="{}" lengths="{}"/></a>'''.format([math.pi]*2, [25.0]*4)
1506  self.assert_matches(self.quick_xacro(src), res)
1507 
1509  for file in ['constructors_bad1.yaml', 'constructors_bad2.yaml']:
1510  src = '''
1511 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1512  <xacro:property name="values" value="${{xacro.load_yaml({file})}}"/>
1513  <values a="${{values.a}}" />
1514 </a>'''
1515  self.assertRaises(xacro.XacroException, self.quick_xacro, src.format(file=file))
1516 
1518  yaml = xacro.load_yaml('settings.yaml')
1519  self.assertTrue(hasattr(yaml, 'arms'))
1520  self.assertFalse(hasattr(yaml, 'this_key_does_not_exist'))
1521 
1523  src = '''
1524 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1525  <xacro:include filename="non-existent.xacro"/>
1526 </a>'''
1527  self.assertRaises(xacro.XacroException, self.quick_xacro, src)
1528 
1530  src = '''
1531 <a xmlns:xacro="http://www.ros.org/wiki/xacro">
1532  <xacro:include filename="non-existent.xacro" optional="True"/>
1533 </a>'''
1534  res = '''<a></a>'''
1535  self.assert_matches(self.quick_xacro(src), res)
1536 
1538  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1539 <xacro:macro name="foo" params="arg:=${2*foo}">
1540  <xacro:property name="foo" value="-"/>
1541  <f val="${arg}"/>
1542 </xacro:macro>
1543 <xacro:property name="foo" value="${3*7}"/>
1544 <xacro:foo/>
1545 <xacro:property name="foo" value="*"/>
1546 <xacro:foo/>
1547 </a>'''
1548  res = '''<a>
1549 <f val="42"/><f val="**"/></a>'''
1550  self.assert_matches(self.quick_xacro(src), res)
1551 
1553  src = '''
1554  <a xmlns:xacro="http://www.ros.org/xacro">
1555  <xacro:property name="prop" default="false"/>
1556  <xacro:unless value="${prop}">
1557  <foo/>
1558  <xacro:property name="prop" value="true"/>
1559  </xacro:unless>
1560 
1561  <!-- second foo should be ignored -->
1562  <xacro:unless value="${prop}">
1563  <foo/>
1564  <xacro:property name="prop" value="true"/>
1565  </xacro:unless>
1566  </a>'''
1567  res = '''<a><foo/></a>'''
1568  self.assert_matches(self.quick_xacro(src), res)
1569 
1571  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">🍔 </a>'''
1572  self.assert_matches(self.quick_xacro(src), '''<a>🍔 </a>''')
1573 
1575  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1576 <xacro:property name="burger" value="🍔"/>
1577 ${burger}</a>'''
1578  res = '''<a>🍔</a>'''
1579  self.assert_matches(self.quick_xacro(src), res)
1580 
1582  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1583 <xacro:property name="burger" value="🍔"/>
1584 <b c="${burger}"/></a>'''
1585  res = '''<a><b c="🍔"/></a>'''
1586  self.assert_matches(self.quick_xacro(src), res)
1587 
1589  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1590 <xacro:property name="burger">
1591 🍔
1592 </xacro:property>
1593 <xacro:insert_block name="burger"/></a>'''
1594  res = '''<a>🍔</a>'''
1595  self.assert_matches(self.quick_xacro(src), res)
1596 
1598  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1599 <xacro:property name="burger" value="🍔"/>
1600 <xacro:if value="${burger == u'🍔'}">
1601 🍟
1602 </xacro:if>
1603 </a>'''
1604  res = '''<a>🍟</a>'''
1605  self.assert_matches(self.quick_xacro(src), res)
1606 
1608  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1609 <xacro:macro name="burger" params="how_many">
1610 ${u'🍔' * how_many}
1611 </xacro:macro>
1612 <xacro:burger how_many="4"/>
1613 </a>'''
1614  res = '''<a>🍔🍔🍔🍔</a>'''
1615  self.assert_matches(self.quick_xacro(src), res)
1616 
1618  # run the full xacro processing pipeline on a file with
1619  # unicode characters in it and make sure the output is correct
1620  test_dir = os.path.abspath(os.path.dirname(__file__))
1621  input_path = os.path.join(test_dir, 'emoji.xacro')
1622  tmp_dir_name = tempfile.mkdtemp() # create directory we can trash
1623  output_path = os.path.join(tmp_dir_name, "out.xml")
1624  self.run_xacro(input_path, '-o', output_path)
1625  self.assertTrue(os.path.isfile(output_path))
1626  self.assert_matches(xml.dom.minidom.parse(output_path), '''<robot>🍔</robot>''')
1627  shutil.rmtree(tmp_dir_name) # clean up after ourselves
1628 
1630  self.assertRaises(xacro.XacroException, self.quick_xacro, '<a>a${</a>')
1631  self.assertRaises(xacro.XacroException, self.quick_xacro, '<a>${b</a>')
1632  self.assertRaises(xacro.XacroException, self.quick_xacro, '<a>${{}}</a>')
1633  self.assertRaises(xacro.XacroException, self.quick_xacro, '<a>a$(</a>')
1634  self.assertRaises(xacro.XacroException, self.quick_xacro, '<a>$(b</a>')
1635 
1637  template = '<a xmlns:xacro="http://www.ros.org/wiki/xacro"><xacro:property name="p" {} /> ${{p}} </a>'
1638 
1639  def check(attributes, expected, **kwargs):
1640  with subTest(msg='Checking ' + attributes):
1641  with self.assertRaises(xacro.XacroException) as cm:
1642  self.quick_xacro(template.format(attributes), **kwargs)
1643  self.assertEqual(str(cm.exception), expected)
1644 
1645  expected = 'Property attributes default, value, and remove are mutually exclusive: p'
1646  check('value="" default=""', expected)
1647  check('value="" remove="true"', expected)
1648  check('default="" remove="true"', expected)
1649  self.assert_matches(self.quick_xacro(template.format('default="42" remove="false"')), '<a>42</a>')
1650 
1652  src = '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">
1653  <xacro:property name="p" default="1st" />
1654  <xacro:property name="p" remove="true" />
1655  <xacro:property name="p" default="2nd" />
1656  ${p}</a>'''
1657  self.assert_matches(self.quick_xacro(src), '<a>2nd</a>')
1658 
1659 
1660 if __name__ == '__main__':
1661  unittest.main()
test_xacro.TestXacro
Definition: test_xacro.py:418
test_xacro.TestXacroFunctions.test_parse_macro_whitespace
def test_parse_macro_whitespace(self)
Definition: test_xacro.py:359
test_xacro.TestXacro.test_multiple_insert_blocks
def test_multiple_insert_blocks(self)
Definition: test_xacro.py:583
test_xacro.TestXacro.test_unicode_property_attribute
def test_unicode_property_attribute(self)
Definition: test_xacro.py:1581
test_xacro.TestXacro.test_include_lazy
def test_include_lazy(self)
Definition: test_xacro.py:1351
test_xacro.TestXacro.test_include_from_variable
def test_include_from_variable(self)
Definition: test_xacro.py:664
test_xacro.TestXacro.test_default_property
def test_default_property(self)
Definition: test_xacro.py:1552
test_xacro.TestXacro.test_ignore_xacro_comments
def test_ignore_xacro_comments(self)
Definition: test_xacro.py:865
test_xacro.TestXacro.test_default_arg
def test_default_arg(self)
Definition: test_xacro.py:1062
test_xacro.TestXacro.test_yaml_support_dotted_list_iterator
def test_yaml_support_dotted_list_iterator(self)
Definition: test_xacro.py:1491
test_xacro.TestMatchXML.test_normalize_whitespace_text
def test_normalize_whitespace_text(self)
Definition: test_xacro.py:274
test_xacro.TestXacro.test_unicode_property
def test_unicode_property(self)
Definition: test_xacro.py:1574
test_xacro.TestXacro.test_math_expressions
def test_math_expressions(self)
Definition: test_xacro.py:810
test_xacro.TestXacroBase.assert_matches
def assert_matches(self, a, b)
Definition: test_xacro.py:381
test_xacro.subTest
def subTest(msg)
Definition: test_xacro.py:63
test_xacro.TestXacro.test_dynamic_macro_names
def test_dynamic_macro_names(self)
Definition: test_xacro.py:435
test_xacro.TestXacro.test_unicode_file
def test_unicode_file(self)
Definition: test_xacro.py:1617
test_xacro.TestXacroBase.ignore_nodes
ignore_nodes
Definition: test_xacro.py:379
xacro.parse_macro_arg
def parse_macro_arg(s)
Definition: __init__.py:583
test_xacro.TestXacro.test_broken_input_doesnt_create_empty_output_file
def test_broken_input_doesnt_create_empty_output_file(self)
Definition: test_xacro.py:1115
test_xacro.TestXacro.test_integer_if_statement
def test_integer_if_statement(self)
Definition: test_xacro.py:735
test_xacro.TestXacro.test_dotify
def test_dotify(self)
Definition: test_xacro.py:1400
test_xacro.TestXacro.test_param_missing
def test_param_missing(self)
Definition: test_xacro.py:1046
test_xacro.TestXacro.test_create_subdirs
def test_create_subdirs(self)
Definition: test_xacro.py:1127
test_xacro.TestXacro.test_property_scope_parent_namespaced
def test_property_scope_parent_namespaced(self)
Definition: test_xacro.py:1409
test_xacro.TestXacro.test_print_location
def test_print_location(self)
Definition: test_xacro.py:1329
test_xacro.TestXacro.test_default_arg_override
def test_default_arg_override(self)
Definition: test_xacro.py:1077
test_xacro.TestXacroBase.run_xacro
def run_xacro(self, input_path, *args)
Definition: test_xacro.py:396
xacro.is_valid_name
def is_valid_name(name)
Definition: __init__.py:559
test_xacro.TestTable.test_get
def test_get(self)
Definition: test_xacro.py:231
test_xacro.TestXacro.test_default_param
def test_default_param(self)
Definition: test_xacro.py:998
test_xacro.TestXacro.test_property_scope_global
def test_property_scope_global(self)
Definition: test_xacro.py:543
test_xacro.TestXacro.test_transitive_arg_evaluation
def test_transitive_arg_evaluation(self)
Definition: test_xacro.py:1202
test_xacro.TestXacro.test_substitution_args_find
def test_substitution_args_find(self)
Definition: test_xacro.py:566
test_xacro.TestXacro.test_overwrite_globals
def test_overwrite_globals(self)
Definition: test_xacro.py:1225
test_xacro.TestXacroFunctions.check_macro_arg
def check_macro_arg(self, s, param, forward, default, rest)
Definition: test_xacro.py:333
test_xacro.TestXacro.test_restricted_builtins
def test_restricted_builtins(self)
Definition: test_xacro.py:820
test_xacro.TestXacroFunctions.test_is_valid_name
def test_is_valid_name(self)
Definition: test_xacro.py:307
test_xacro.TestXacro.test_no_evaluation
def test_no_evaluation(self)
Definition: test_xacro.py:800
test_xacro.TestXacro.test_multi_tree_evaluation
def test_multi_tree_evaluation(self)
Definition: test_xacro.py:962
test_xacro.TestXacro.test_equality_expression_in_if_statement
def test_equality_expression_in_if_statement(self)
Definition: test_xacro.py:788
test_xacro.TestXacro.test_greedy_property_evaluation
def test_greedy_property_evaluation(self)
Definition: test_xacro.py:916
xacro.tokenize
def tokenize(s, sep=',;', skip_empty=True)
Definition: __init__.py:161
test_xacro.TestXacro.test_invalid_syntax
def test_invalid_syntax(self)
Definition: test_xacro.py:1629
test_xacro.TestXacro.test_default_arg_empty
def test_default_arg_empty(self)
Definition: test_xacro.py:1099
test_xacro.TestXacro.test_macro_has_new_scope
def test_macro_has_new_scope(self)
Definition: test_xacro.py:694
test_xacro.TestXacro.test_unicode_literal_parsing
def test_unicode_literal_parsing(self)
Definition: test_xacro.py:1570
test_xacro.TestXacro.test_from_issue
def test_from_issue(self)
Definition: test_xacro.py:976
test_xacro.TestXacro.test_yaml_hasattr_support
def test_yaml_hasattr_support(self)
Definition: test_xacro.py:1517
test_xacro.TestXacro.test_recursive_evaluation_wrong_order
def test_recursive_evaluation_wrong_order(self)
Definition: test_xacro.py:893
test_xacro.TestMatchXML.test_whitespace_vs_empty_node
def test_whitespace_vs_empty_node(self)
Definition: test_xacro.py:295
test_xacro.TestXacro.test_default_param_override
def test_default_param_override(self)
Definition: test_xacro.py:1022
test_xacro.TestMatchXML.test_mismatch_different_dicts
def test_mismatch_different_dicts(self)
Definition: test_xacro.py:289
test_xacro.all_attributes_match
def all_attributes_match(a, b)
Definition: test_xacro.py:100
test_xacro.TestXacro.test_issue_63_fixed_with_inorder_processing
def test_issue_63_fixed_with_inorder_processing(self)
Definition: test_xacro.py:1356
test_xacro.TestXacro.test_remove_property
def test_remove_property(self)
Definition: test_xacro.py:1651
test_xacro.TestXacro.test_xacro_exist_required
def test_xacro_exist_required(self)
Definition: test_xacro.py:1522
test_xacro.TestXacro.test_yaml_support_dotted
def test_yaml_support_dotted(self)
Definition: test_xacro.py:1441
test_xacro.TestXacro.test_include
def test_include(self)
Definition: test_xacro.py:642
test_xacro.TestXacro.test_include_with_namespace
def test_include_with_namespace(self)
Definition: test_xacro.py:679
test_xacro.TestXacro.test_iterable_literals_eval
def test_iterable_literals_eval(self)
Definition: test_xacro.py:1152
test_xacro.TestXacro.test_yaml_custom_constructors
def test_yaml_custom_constructors(self)
Definition: test_xacro.py:1499
test_xacro.TestXacroFunctions.test_tokenize_keep_empty
def test_tokenize_keep_empty(self)
Definition: test_xacro.py:368
test_xacro.TestTable
Definition: test_xacro.py:210
test_xacro.TestXacro.test_property_scope_parent
def test_property_scope_parent(self)
Definition: test_xacro.py:535
test_xacro.TestMatchXML
Definition: test_xacro.py:273
test_xacro.TestUtils.test_capture_stderr
def test_capture_stderr(self, *args, **kwargs)
Definition: test_xacro.py:268
test_xacro.TestXacro.test_include_recursive
def test_include_recursive(self)
Definition: test_xacro.py:670
test_xacro.TestXacroBase.quick_xacro
def quick_xacro(self, xml, cli=None, **kwargs)
Definition: test_xacro.py:384
test_xacro.TestXacro.test_invalid_property_name
def test_invalid_property_name(self)
Definition: test_xacro.py:423
test_xacro.TestXacro.test_consider_non_elements_if
def test_consider_non_elements_if(self)
Definition: test_xacro.py:838
test_xacro.TestXacro.test_property_if_statement
def test_property_if_statement(self)
Definition: test_xacro.py:769
test_xacro.TestXacro.test_include_from_macro
def test_include_from_macro(self)
Definition: test_xacro.py:1382
xacro.XacroException
Definition: __init__.py:255
test_xacro.TestXacro.test_dynamic_macro_name_clash
def test_dynamic_macro_name_clash(self)
Definition: test_xacro.py:445
test_xacro.TestXacro.test_recursive_evaluation
def test_recursive_evaluation(self)
Definition: test_xacro.py:882
test_xacro.TestXacro.test_macro_default_param_evaluation_order
def test_macro_default_param_evaluation_order(self)
Definition: test_xacro.py:1537
test_xacro.TestXacro.test_error_reporting
def test_error_reporting(self)
Definition: test_xacro.py:1283
test_xacro.TestXacro.test_insert_block_property
def test_insert_block_property(self)
Definition: test_xacro.py:627
test_xacro.TestXacro.test_macro_undefined
def test_macro_undefined(self)
Definition: test_xacro.py:458
test_xacro.TestXacro.__init__
def __init__(self, *args, **kwargs)
Definition: test_xacro.py:419
test_xacro.TestXacro.test_include_nonexistent
def test_include_nonexistent(self)
Definition: test_xacro.py:652
test_xacro.TestXacro.test_evaluate_macro_params_before_body
def test_evaluate_macro_params_before_body(self)
Definition: test_xacro.py:515
test_xacro.TestXacro.test_message_functions
def test_message_functions(self)
Definition: test_xacro.py:1274
test_xacro.TestTable.test_contains
def test_contains(self)
Definition: test_xacro.py:218
test_xacro.TestXacro.test_iterable_literals_plain
def test_iterable_literals_plain(self)
Definition: test_xacro.py:1140
xacro.Table
Definition: __init__.py:318
test_xacro.TestXacro.test_xacro_exist_optional
def test_xacro_exist_optional(self)
Definition: test_xacro.py:1529
test_xacro.TestXacro.test_literals_eval
def test_literals_eval(self)
Definition: test_xacro.py:1164
test_xacro.TestXacro.test_invalid_property_definitions
def test_invalid_property_definitions(self)
Definition: test_xacro.py:1636
test_xacro.TestMatchXML.test_mismatch_different_numbers
def test_mismatch_different_numbers(self)
Definition: test_xacro.py:283
test_xacro.TestXacro.test_namespace_propagation
def test_namespace_propagation(self)
Definition: test_xacro.py:1394
test_xacro.capture_stderr
def capture_stderr(function, *args, **kwargs)
Definition: test_xacro.py:202
test_xacro.TestXacro.test_recursive_definition
def test_recursive_definition(self)
Definition: test_xacro.py:904
test_xacro.TestXacroCommentsIgnored.__init__
def __init__(self, *args, **kwargs)
Definition: test_xacro.py:403
test_xacro.TestXacro.test_issue_68_numeric_arg
def test_issue_68_numeric_arg(self)
Definition: test_xacro.py:1187
test_xacro.TestXacro.test_multiple_definition_and_evaluation
def test_multiple_definition_and_evaluation(self)
Definition: test_xacro.py:936
test_xacro.TestXacro.test_xacro_element
def test_xacro_element(self)
Definition: test_xacro.py:464
test_xacro.TestXacroFunctions.test_tokenize
def test_tokenize(self)
Definition: test_xacro.py:363
test_xacro.TestXacro.test_property_replacement
def test_property_replacement(self)
Definition: test_xacro.py:527
test_xacro.TestXacro.test_expression_in_extension
def test_expression_in_extension(self)
Definition: test_xacro.py:1259
test_xacro.TestXacro.test_substitution_args_arg
def test_substitution_args_arg(self)
Definition: test_xacro.py:570
test_xacro.TestMatchXML.test_ignore_comments
def test_ignore_comments(self)
Definition: test_xacro.py:301
test_xacro.TestXacro.test_default_arg_missing
def test_default_arg_missing(self)
Definition: test_xacro.py:1092
test_xacro.TestXacro.test_no_double_evaluation
def test_no_double_evaluation(self)
Definition: test_xacro.py:1232
test_xacro.TestXacro.test_extension_in_expression
def test_extension_in_expression(self)
Definition: test_xacro.py:1254
test_xacro.TestXacroCommentsIgnored.test_pr2
def test_pr2(self)
Definition: test_xacro.py:407
test_xacro.TestXacro.test_yaml_support_dotted_arith
def test_yaml_support_dotted_arith(self)
Definition: test_xacro.py:1463
test_xacro.TestXacro.test_unicode_conditional
def test_unicode_conditional(self)
Definition: test_xacro.py:1597
test_xacro.TestXacroBase
Definition: test_xacro.py:376
test_xacro.TestXacro.test_multiple_recursive_evaluation
def test_multiple_recursive_evaluation(self)
Definition: test_xacro.py:923
test_xacro.TestXacro.test_macro_params_escaped_string
def test_macro_params_escaped_string(self)
Definition: test_xacro.py:521
test_xacro.TestXacro.test_yaml_support_list_of_x
def test_yaml_support_list_of_x(self)
Definition: test_xacro.py:1482
test_xacro.TestXacro.test_escaping_dollar_braces
def test_escaping_dollar_braces(self)
Definition: test_xacro.py:574
test_xacro.TestXacro.test_target_namespace
def test_target_namespace(self)
Definition: test_xacro.py:1264
test_xacro.TestXacroCommentsIgnored
Definition: test_xacro.py:402
test_xacro.TestXacro.test_recursive_bad_math
def test_recursive_bad_math(self)
Definition: test_xacro.py:991
test_xacro.TestXacro.test_inorder_processing
def test_inorder_processing(self)
Definition: test_xacro.py:484
test_xacro.TestTable.test_del
def test_del(self)
Definition: test_xacro.py:242
test_xacro.TestXacroBase.__init__
def __init__(self, *args, **kwargs)
Definition: test_xacro.py:377
test_xacro.TestXacro.test_boolean_if_statement
def test_boolean_if_statement(self)
Definition: test_xacro.py:715
test_xacro.TestXacro.test_restricted_builtins_nested
def test_restricted_builtins_nested(self)
Definition: test_xacro.py:824
test_xacro.TestMatchXML.test_normalize_whitespace_trim
def test_normalize_whitespace_trim(self)
Definition: test_xacro.py:277
test_xacro.TestXacro.test_include_glob
def test_include_glob(self)
Definition: test_xacro.py:646
test_xacro.TestXacro.test_target_namespace_only_from_root
def test_target_namespace_only_from_root(self)
Definition: test_xacro.py:1269
test_xacro.TestXacro.test_should_replace_before_macroexpand
def test_should_replace_before_macroexpand(self)
Definition: test_xacro.py:503
test_xacro.TestXacro.test_property_forwarding
def test_property_forwarding(self)
Definition: test_xacro.py:1243
test_xacro.TestXacro.test_unicode_property_block
def test_unicode_property_block(self)
Definition: test_xacro.py:1588
test_xacro.xml_matches
def xml_matches(a, b, ignore_nodes=[])
Definition: test_xacro.py:179
test_xacro.TestXacro.test_enforce_xacro_ns
def test_enforce_xacro_ns(self)
Definition: test_xacro.py:1176
test_xacro.TestXacro.test_yaml_support
def test_yaml_support(self)
Definition: test_xacro.py:1428
test_xacro.nodes_match
def nodes_match(a, b, ignore_nodes)
Definition: test_xacro.py:126
test_xacro.text_matches
def text_matches(a, b)
Definition: test_xacro.py:119
test_xacro.TestXacro.test_xml_namespace_lifting
def test_xml_namespace_lifting(self)
Definition: test_xacro.py:1297
test_xacro.TestXacro.test_multiple_blocks
def test_multiple_blocks(self)
Definition: test_xacro.py:597
xacro.load_yaml
def load_yaml(filename)
Definition: __init__.py:141
test_xacro.TestXacro.test_dynamic_macro_undefined
def test_dynamic_macro_undefined(self)
Definition: test_xacro.py:452
test_xacro.TestXacro.test_consider_non_elements_block
def test_consider_non_elements_block(self)
Definition: test_xacro.py:845
test_xacro.text_values_match
def text_values_match(a, b)
Definition: test_xacro.py:70
test_xacro.TestXacroFunctions.test_parse_macro_arg
def test_parse_macro_arg(self)
Definition: test_xacro.py:344
test_xacro.TestXacro.test_just_a_dollar_sign
def test_just_a_dollar_sign(self)
Definition: test_xacro.py:579
test_xacro.TestTable.test_top
def test_top(self)
Definition: test_xacro.py:211
test_xacro.TestXacro.test_include_deprecated
def test_include_deprecated(self)
Definition: test_xacro.py:657
test_xacro.TestXacro.test_safe_eval
def test_safe_eval(self)
Definition: test_xacro.py:834
test_xacro.TestXacro.test_invalid_if_statement
def test_invalid_if_statement(self)
Definition: test_xacro.py:729
test_xacro.TestXacro.test_float_if_statement
def test_float_if_statement(self)
Definition: test_xacro.py:755
test_xacro.TestXacro.test_redefine_global_symbol
def test_redefine_global_symbol(self)
Definition: test_xacro.py:1342
test_xacro.TestXacro.test_xacro_attribute
def test_xacro_attribute(self)
Definition: test_xacro.py:473
test_xacro.TestXacroFunctions.test_resolve_macro
def test_resolve_macro(self)
Definition: test_xacro.py:320
test_xacro.TestXacro.test_unicode_macro
def test_unicode_macro(self)
Definition: test_xacro.py:1607
test_xacro.TestMatchXML.test_match_similar_numbers
def test_match_similar_numbers(self)
Definition: test_xacro.py:280
test_xacro.TestUtils
Definition: test_xacro.py:267
test_xacro.TestXacro.test_macro_name_with_colon
def test_macro_name_with_colon(self)
Definition: test_xacro.py:1215
test_xacro.TestXacroFunctions
Definition: test_xacro.py:306
test_xacro.TestMatchXML.test_match_unordered_dicts
def test_match_unordered_dicts(self)
Definition: test_xacro.py:286
test_xacro.TestXacro.test_property_resolution_with_namespaced_include
def test_property_resolution_with_namespaced_include(self)
Definition: test_xacro.py:1366
test_xacro.TestXacro.test_transitive_evaluation
def test_transitive_evaluation(self)
Definition: test_xacro.py:949
test_xacro.TestXacro.test_yaml_support_key_in_dict
def test_yaml_support_key_in_dict(self)
Definition: test_xacro.py:1473
test_xacro.TestXacro.test_integer_stays_integer
def test_integer_stays_integer(self)
Definition: test_xacro.py:616
test_xacro.TestMatchXML.test_normalize_whitespace_nested
def test_normalize_whitespace_nested(self)
Definition: test_xacro.py:298
test_xacro.TestXacro.test_comments
def test_comments(self)
Definition: test_xacro.py:1307
test_xacro.TestXacro.test_property_in_comprehension
def test_property_in_comprehension(self)
Definition: test_xacro.py:554
test_xacro.TestXacro.test_double_underscore_property_name_raises
def test_double_underscore_property_name_raises(self)
Definition: test_xacro.py:428
test_xacro.TestXacro.test_yaml_support_dotted_key_error
def test_yaml_support_dotted_key_error(self)
Definition: test_xacro.py:1454
xacro.cli.process_args
def process_args(argv, require_input=True)
Definition: cli.py:65
test_xacro.TestXacro.test_broken_include_error_reporting
def test_broken_include_error_reporting(self)
Definition: test_xacro.py:1109
test_xacro.TestXacro.test_math_ignores_spaces
def test_math_ignores_spaces(self)
Definition: test_xacro.py:562
test_xacro.TestMatchXML.test_empty_node_vs_whitespace
def test_empty_node_vs_whitespace(self)
Definition: test_xacro.py:292
test_xacro.TestXacro.test_arg_function
def test_arg_function(self)
Definition: test_xacro.py:1104
test_xacro.TestXacro.test_consecutive_if
def test_consecutive_if(self)
Definition: test_xacro.py:782
test_xacro.TestXacro.test_yaml_custom_constructors_illegal_expr
def test_yaml_custom_constructors_illegal_expr(self)
Definition: test_xacro.py:1508


xacro
Author(s): Stuart Glaser, William Woodall, Robert Haschke
autogenerated on Sat Jan 11 2025 03:50:37