36 from __future__
import print_function
39 from contextlib
import contextmanager
51 from xml.dom.minidom
import parseString
54 from cStringIO
import StringIO
56 from io
import StringIO
59 from unittest
import subTest
67 whitespace = re.compile(
r'\s+')
72 if whitespace.sub(
' ', a).strip() == whitespace.sub(
' ', b).strip():
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):
84 def match_splits(a_, b_):
85 if len(a_) != len(b_):
87 for a, b
in zip(a_, b_):
91 if abs(float(a) - float(b)) > 1.0e-9:
97 return match_splits(a.split(), b.split())
101 if len(a.attributes) != len(b.attributes):
102 print(
'Different number of attributes')
104 a_atts = a.attributes.items()
105 b_atts = b.attributes.items()
109 for a, b
in zip(a_atts, b_atts):
111 print(
'Different attribute names: %s and %s' % (a[0], b[0]))
114 print(
'Different attribute values: %s and %s' % (a[1], b[1]))
122 print(
"Different text values: '%s' and '%s'" % (a, b))
132 if a.nodeType != b.nodeType:
133 print(
'Different node types: %s and %s' % (a, b))
137 if a.nodeType
in [xml.dom.Node.TEXT_NODE,
138 xml.dom.Node.CDATA_SECTION_NODE,
139 xml.dom.Node.COMMENT_NODE]:
143 if a.nodeType != xml.dom.Node.ELEMENT_NODE:
147 if a.nodeName != b.nodeName:
148 print(
'Different element names: %s and %s' % (a.nodeName, b.nodeName))
160 ((a.nodeType
in ignore_nodes)
or 161 (a.nodeType == xml.dom.Node.TEXT_NODE
and whitespace.sub(
'', a.data) ==
""))):
164 ((b.nodeType
in ignore_nodes)
or 165 (b.nodeType == xml.dom.Node.TEXT_NODE
and whitespace.sub(
'', b.data) ==
""))):
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)
190 print(
'Match failed:')
191 a.writexml(sys.stdout)
194 b.writexml(sys.stdout)
203 old, sys.stderr = sys.stderr, StringIO()
204 result = function(*args, **kwargs)
206 yield (result, sys.stderr.read())
212 with
capture_stderr(xacro.error,
'Hello World', alt_text=
'')
as (result, output):
213 self.assertEqual(output,
'Hello World\n')
221 self.assertTrue(
text_matches(
" foo bar ",
"foo \t\n\r bar"))
224 self.assertTrue(
text_matches(
"0.123456789",
"0.123456788"))
227 self.assertFalse(
text_matches(
"0.123456789",
"0.1234567879"))
230 self.assertTrue(
text_matches(
"{'a': 1, 'b': 2, 'c': 3}",
"{'c': 3, 'b': 2, 'a': 1}"))
233 self.assertFalse(
text_matches(
"{'a': 1, 'b': 2, 'c': 3}",
"{'c': 3, 'b': 2, 'a': 0}"))
236 self.assertTrue(
xml_matches(
'''<foo/>''',
'''<foo> \t\n\r </foo>'''))
239 self.assertTrue(
xml_matches(
'''<foo> \t\n\r </foo>''',
'''<foo/>'''))
242 self.assertTrue(
xml_matches(
'''<a><b/></a>''',
'''<a>\n<b> </b> </a>'''))
245 self.assertTrue(
xml_matches(
'''<a><b/><!-- foo --> <!-- bar --></a>''',
246 '''<a><b/></a>''', [xml.dom.Node.COMMENT_NODE]))
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()})
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'))
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))
284 self.assertTrue(v
is None)
285 self.assertEqual(r, rest, msg=
"'{0}' != '{1}' parsing {2}".format(r, rest, s))
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']
293 for default
in defaults:
294 seps = [
'=',
':=']
if forward
or default
else [
'']
296 for rest
in [
'',
' ',
' bar',
' bar=42']:
297 s =
'foo{0}{1}{2}{3}'.format(sep, forward, default, rest)
299 default
if default
else None,
303 for ws
in [
' ',
' \t ',
' \n ']:
304 self.
check_macro_arg(ws +
'foo' + ws +
'bar=42' + ws,
'foo',
None,
None,
'bar=42' + ws)
307 tokens = [
'ab',
'cd',
'ef']
308 for sep
in [
' ',
',',
';',
', ']:
312 tokens =
' '.join([
'ab',
' ',
'cd',
'ef'])
314 self.assertEqual(len(results), 5)
315 self.assertEqual(
' '.join(results), tokens)
321 super(TestXacroBase, self).
__init__(*args, **kwargs)
332 args.update(vars(opts))
333 args.update(dict(in_order=self.
in_order))
336 doc = xacro.parse(xml)
337 xacro.filestack =
None 338 xacro.process_doc(doc, **args)
344 args.append(
'--legacy')
345 test_dir = os.path.abspath(os.path.dirname(__file__))
346 subprocess.call([
'xacro', input_path] + args)
352 super(TestXacroCommentsIgnored, self).
__init__(*args, **kwargs)
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')
361 xml.dom.minidom.parse(pr2_golden_parse_path),
368 super(TestXacroCommentsIgnored, self).
__init__(*args, **kwargs)
372 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 373 <xacro:property name="invalid.name"/></a>''' 377 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 378 <xacro:property name="__hidden"/></a>''' 381 self.assertEqual(str(cm.exception),
'Property names must not start with double underscore:__hidden')
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>''' 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> 400 res =
'''<a><a name="bar"/></a>''' 405 self.assertTrue(
"deprecated use of 'call' as macro name" in output)
410 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 411 <xacro:call macro="foo"/></a>''')
416 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 417 <xacro:undefined><foo/><bar/></xacro:undefined></a>''')
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"/> 425 res =
'''<a><A/><B/></a>''' 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> 433 <xacro:foo name="A" value="foo"/> 434 <xacro:foo name="B" value="bar"/> 436 res =
'''<a><tag A="foo"/><tag B="bar"/></a>''' 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}"/> 466 <xacro:macro name="inner" params="*the_block"> 467 <in_the_inner><xacro:insert_block name="the_block" /></in_the_inner> 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> 472 <xacro:outer><woot /></xacro:outer></a>'''),
473 '''<a><in_the_outer><in_the_inner><woot /></in_the_inner></in_the_outer></a>''')
477 <xacro:macro name="foo" params="lst">${lst[-1]}</xacro:macro> 478 <xacro:foo lst="${[1,2,3]}"/></a>'''),
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>''')
489 <xacro:property name="foo" value="42" /> 490 <the_foo result="${foo}" /> 493 <the_foo result="42" /> 498 <xacro:macro name="foo" params="factor"> 499 <xacro:property name="foo" value="${21*factor}" scope="parent"/> 501 <xacro:foo factor="2"/><a foo="${foo}"/></a>'''),
502 '''<a><a foo="42"/></a>''')
506 <xacro:macro name="foo" params="factor"> 507 <xacro:macro name="bar"> 508 <xacro:property name="foo" value="${21*factor}" scope="global"/> 512 <xacro:foo factor="2"/><a foo="${foo}"/></a>'''),
513 '''<a><a foo="42"/></a>''')
517 '''<a><f v="0.25" /></a>''')
521 '''<a><f v="''' + os.path.abspath((__file__).replace(
".pyc",
".py") +
'''" /></a>'''))
525 '''<a><f v="my_arg" /></a>''')
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)" />''')
533 '''<a b="$" c="text $" d="text $ text"/>''')
537 <xacro:macro name="foo" params="*block"> 538 <xacro:insert_block name="block" /> 539 <xacro:insert_block name="block" /> 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" /> 565 for d
in [dict(A=
'1', B=
'2'), dict(A=
'2', B=
'1')]:
570 <xacro:macro name="m" params="num"> 571 <test number="${num}" /> 573 <xacro:m num="100" /> 576 <test number="100" /> 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> 587 <xacro:insert_block name="some_block" /> 591 <foo><some_block attr="2">bar</some_block></foo> 595 src =
'''<a xmlns:xacro="http://www.ros.org/xacro"><xacro:include filename="include1.xml"/></a>''' 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]']:
606 self.
quick_xacro,
'''<a xmlns:xacro="http://www.ros.org/xacro"> 607 <xacro:include filename="include-nada.xml" /></a>''')
611 src =
'''<a><include filename="nada"><tag/></include></a>''' 614 self.assertEqual(output,
'')
617 doc =
'''<a xmlns:xacro="http://www.ros.org/xacro"> 618 <xacro:property name="file" value="include1.xml"/> 619 <xacro:include filename="${file}" /></a>''' 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"/> 632 <a><inc1/><inc1/><subdir_inc1/><subdir_inc1/><inc1/></a>''')
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}"/> 645 <inc1/><inc2/><main var="main" A="2" B="3"/> 655 <robot xmlns:xacro="http://www.ros.org/wiki/xacro"> 656 <xacro:if value="false"> 659 <xacro:if value="true"> 670 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 671 <xacro:if value="nonsense"><foo/></xacro:if></a>''')
675 <robot xmlns:xacro="http://www.ros.org/wiki/xacro"> 676 <xacro:if value="${0*42}"> 682 <xacro:if value="${0}"> 685 <xacro:if value="${1*2+3}"> 695 <robot xmlns:xacro="http://www.ros.org/wiki/xacro"> 696 <xacro:if value="${3*0.0}"> 699 <xacro:if value="${3*0.1}"> 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> 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/>''')
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> 740 <a xmlns:xacro="http://www.ros.org/wiki/xacro"> 741 <xacro:property name="xyz" value="5 -2"/> 750 <a xmlns:xacro="http://www.ros.org/wiki/xacro"> 751 <foo function="${1. + sin(pi)}"/> 754 <foo function="1.0"/> 760 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${__import__('math')}</a>''')
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')} 774 '''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${"".__class__.__base__.__subclasses__()}</a>''')
778 <a xmlns:xacro="http://www.ros.org/wiki/xacro"> 779 <xacro:if value="1"><!-- comment --> text <b>bar</b></xacro:if> 781 <a><!-- comment --> text <b>bar</b></a>''')
785 self.
quick_xacro(
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 786 <xacro:macro name="foo" params="*block"> 789 <xacro:insert_block name="block" /> 792 <!-- ignored comment --> 805 <a xmlns:xacro="http://www.ros.org/wiki/xacro"> 808 <!-- ignore multiline comments before any xacro tag --> 810 <xacro:property name="foo" value="1"/> 812 <xacro:if value="1"><!-- B --></xacro:if> 814 <xacro:macro name="foo"><!-- C --></xacro:macro> 818 <a><!-- A --><!-- B --><!-- C --></a>''')
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 }"/> 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"/> 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}"/> 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"'))
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"/> 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}"/> 871 <answer product="6"/> 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}"/> 884 <answer b="42 42 42"/> 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}"/> 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}"/> 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}"/> 924 <link name="my_link"> 925 <origin xyz="0 0 21.0"/> 931 <robot xmlns:xacro="http://www.ros.org/wiki/xacro"> 932 <xacro:property name="x" value="0"/> 933 <tag badness="${1/x}"/> 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}" /> 947 <xacro:fixed_link child_link="foo"> 948 <origin xyz="0 0 0" rpy="0 0 0" /> 953 <joint name="foo_joint" type="fixed"> 954 <origin rpy="0 0 0" xyz="0 0 0"/> 955 <parent link="base_link"/> 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}" /> 971 <xacro:fixed_link child_link="foo" parent_link="bar"> 972 <origin xyz="0 0 0" rpy="0 0 0" /> 977 <joint name="foo_joint" type="fixed"> 978 <origin rpy="0 0 0" xyz="0 0 0"/> 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}" /> 995 <xacro:fixed_link child_link="foo"> 996 <origin xyz="0 0 0" rpy="0 0 0" /> 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)"/> 1010 <link name="my_link"> 1011 <origin xyz="0 0 2"/> 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)"/> 1023 ''', [
'foo:=4']),
''' 1025 <link name="my_link"> 1026 <origin xyz="0 0 4"/> 1031 self.assertRaises(Exception, self.
quick_xacro,
''' 1032 <a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1033 <a arg="$(arg foo)"/> 1039 <a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1040 <xacro:arg name="foo" default=""/>$(arg foo)</a>'''),
'''<a/>''')
1044 <a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1045 <xacro:arg name="foo" default="bar"/>${xacro.arg('foo')}</a>'''),
'<a>bar</a>')
1050 tmp_dir_name = tempfile.mkdtemp()
1051 output_path = os.path.join(tmp_dir_name,
"should_not_exist")
1052 self.
run_xacro(
'broken.xacro',
'-o', output_path)
1054 output_file_created = os.path.isfile(output_path)
1055 shutil.rmtree(tmp_dir_name)
1057 self.assertFalse(output_file_created)
1062 tmp_dir_name = tempfile.mkdtemp()
1063 shutil.rmtree(tmp_dir_name)
1064 output_path = os.path.join(tmp_dir_name,
"out")
1065 self.
run_xacro(
'include1.xml',
'-o', output_path)
1067 output_file_created = os.path.isfile(output_path)
1068 shutil.rmtree(tmp_dir_name)
1070 self.assertTrue(output_file_created)
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}"/> 1081 <a list="[0, 1+1, 2]" tuple="(0,1+1,2)" dict="{'a':0, 'b':1+1, 'c':2}"/> 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}"/> 1093 <a list="[0, 2, 2]" tuple="(0, 2, 2)" dict="{'a': 0, 'c': 2, 'b': 2}"/> 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),
''' 1103 <arg name="foo" value="bar"/> 1104 <include filename="foo"/> 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}"/> 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}"/> 1136 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1137 <xacro:macro name="xacro:my_macro"><foo/></xacro:macro> 1140 res =
'''<a><foo/></a>''' 1143 self.assertTrue(
"macro names must not contain prefix 'xacro:'" in output)
1146 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1147 <xacro:property name="pi" value="3.14"/></a>''' 1150 self.assertTrue(output)
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> 1160 res =
'''<a><d d="${a}"> a=2 b=1 c=${a} </d></a>''' 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> 1169 res =
'''<a>%s</a>''' 1175 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${2*'$(arg var)'}</a>''' 1176 res =
'''<a>%s</a>''' 1180 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro">$(arg ${'v'+'ar'})</a>''' 1181 res =
'''<a>%s</a>''' 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"/>''' 1190 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"><b xacro:targetNamespace="http://www.ros.org"/></a>''' 1191 res =
'''<a><b/></a>''' 1195 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro">${{xacro.{f}('colored', 'text', 2, 3.14)}}</a>''' 1197 for f
in [
'message',
'warning',
'error']:
1200 self.assertTrue(
'colored text 2 3.14' in output)
1204 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1205 <xacro:include filename="raise.xacro"/> 1211 expected =
'''when instantiating macro: inner ({file}) 1212 instantiated from: outer ({file}) 1214 '''.format(file=
"./raise.xacro" if self.
in_order else "???")
1215 self.assertEqual(output, expected)
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> 1224 res =
'''<a xmlns:a="http://www.ros.org/a" xmlns:b="http://www.ros.org/b" />''' 1228 original =
'<!-- ${name} -->' 1229 processed =
'<!-- foo -->' 1230 enabler =
'<!-- xacro:eval-comments{suffix} -->' 1231 disabler = enabler.format(suffix=
":off")
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)
1245 except AssertionError
as e:
1246 print(
"When evaluating\n{}".format(src.format(**src_params)))
1253 super(TestXacroInorder, self).
__init__(*args, **kwargs)
1257 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1258 <xacro:macro name="scope"><xacro:include filename="location.xacro"/></xacro:macro> 1264 self.assertTrue(output ==
'''when instantiating macro: scope (???) 1265 in file: ./location.xacro 1266 included from: string 1270 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1271 <xacro:property name="str" value="sin"/> 1273 res =
'''<a>sin</a>''' 1276 self.assertTrue(
"redefining global symbol: str" in output)
1279 doc = (
'''<a xmlns:xacro="http://www.ros.org/xacro"> 1280 <xacro:if value="false"><xacro:include filename="non-existent"/></xacro:if></a>''')
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" /> 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}"/> 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"/> 1311 <a xmlns:xacro="http://www.ros.org/xacro"> 1312 <xacro:macro name="foo" params="file:=include1.xml"><xacro:include filename="${file}"/></xacro:macro> 1314 <xacro:foo file="${xacro.abs_filename('include1.xml')}"/> 1315 <xacro:include filename="subdir/foo.xacro"/> 1316 <xacro:foo file="$(cwd)/subdir/include1.xml"/> 1318 res =
'''<a><inc1/><inc1/><subdir_inc1/><subdir_inc1/></a>''' 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>''' 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} 1333 res =
'''<a>3</a>''' 1337 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1338 <xacro:property name="prop" value="root"/> 1339 <root prop="${prop}"/> 1341 <xacro:include filename="A.xacro" ns="A"/> 1342 <root prop="${prop}" A.prop="${A.prop}" A.B.prop="${A.B.prop}" /> 1345 <root prop="${prop}"/> 1350 <root A.B.prop="b" A.prop="B" prop="root"/> 1351 <root prop="macro"/> 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']}"/> 1363 res =
'''<a><{tag}/></a>''' 1364 for i
in [
'inc1',
'inc2']:
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}"/> 1376 res =
'''<a><{tag}/></a>''' 1377 for i
in [
'inc1',
'inc2']:
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}"/> 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}"/> 1397 res =
'''<a>4243</a>''' 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} 1406 res =
'''<a>True False</a>''' 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]} 1415 res =
'''<a>A2 B1 1 2 3 4</a>''' 1420 <a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1421 ${[2*item.val.x for item in xacro.load_yaml('list2.yaml')]} 1423 res =
'''<a>[2, 4]</a>''' 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}"/> 1432 res =
'''<a><values a="{}" b="{}" c="42"/></a>'''.format(math.pi, 0.5*math.pi)
1436 for file
in [
'constructors_bad1.yaml',
'constructors_bad2.yaml']:
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}}" /> 1446 self.assertTrue(hasattr(yaml,
'arms'))
1447 self.assertFalse(hasattr(yaml,
'this_key_does_not_exist'))
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="-"/> 1455 <xacro:property name="foo" value="${3*7}"/> 1457 <xacro:property name="foo" value="*"/> 1461 <f val="42"/><f val="**"/></a>''' 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}"> 1472 <xacro:property name="bar" value="dummy"/> 1473 <xacro:property name="foo" value="21"/></a>''' 1475 self.assertTrue(
"Document is incompatible to in-order processing." in output)
1476 self.assertTrue(
"foo" in output)
1477 self.assertTrue(
"bar" not in output)
1481 <a xmlns:xacro="http://www.ros.org/xacro"> 1482 <xacro:property name="prop" default="false"/> 1483 <xacro:unless value="${prop}"> 1485 <xacro:property name="prop" value="true"/> 1488 <!-- second foo should be ignored --> 1489 <xacro:unless value="${prop}"> 1491 <xacro:property name="prop" value="true"/> 1494 res =
'''<a><foo/></a>''' 1498 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro">🍔 </a>''' 1502 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1503 <xacro:property name="burger" value="🍔"/> 1505 res =
'''<a>🍔</a>''' 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>''' 1516 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1517 <xacro:property name="burger"> 1520 <xacro:insert_block name="burger"/></a>''' 1521 res =
'''<a>🍔</a>''' 1525 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1526 <xacro:property name="burger" value="🍔"/> 1527 <xacro:if value="${burger == u'🍔'}"> 1531 res =
'''<a>🍟</a>''' 1535 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1536 <xacro:macro name="burger" params="how_many"> 1539 <xacro:burger how_many="4"/> 1541 res =
'''<a>🍔🍔🍔🍔</a>''' 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()
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)
1557 src =
'''<a xmlns:xacro="http://www.ros.org/wiki/xacro"> 1558 <xacro:macro name="foo"><bar/></xacro:macro> 1572 template =
'<a xmlns:xacro="http://www.ros.org/wiki/xacro"><xacro:property name="p" {} /> ${{p}} </a>' 1574 def check(attributes, expected, **kwargs):
1575 with
subTest(msg=
'Checking ' + attributes):
1577 self.
quick_xacro(template.format(attributes), **kwargs)
1578 self.assertEqual(str(cm.exception), expected)
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)
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)
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" /> 1599 if __name__ ==
'__main__':
def test_issue_63_fixed_with_inorder_processing(self)
def test_unicode_conditional(self)
def test_error_reporting(self)
def test_integer_if_statement(self)
def __init__(self, args, kwargs)
def test_include_glob(self)
def test_property_resolution_with_namespaced_include(self)
def test_yaml_support_list_of_x(self)
def test_parse_macro_whitespace(self)
def test_include_deprecated(self)
def test_multiple_blocks(self)
def test_restricted_builtins_nested(self)
def xml_matches(a, b, ignore_nodes=[])
def test_normalize_whitespace_text(self)
def test_print_location(self)
def test_iterable_literals_plain(self)
def test_equality_expression_in_if_statement(self)
def test_multiple_recursive_evaluation(self)
def test_macro_params_escaped_string(self)
def test_iterable_literals_eval(self)
def test_include_from_macro(self)
def test_target_namespace(self)
def test_consider_non_elements_if(self)
def test_yaml_support(self)
def test_unicode_macro(self)
def test_expression_in_extension(self)
def test_is_valid_name(self)
def test_escaping_dollar_braces(self)
def test_include_lazy(self)
def test_consecutive_if(self)
def test_recursive_definition(self)
def test_normalize_whitespace_trim(self)
def capture_stderr(function, args, kwargs)
def test_xml_namespace_lifting(self)
def test_insert_block_property(self)
def test_include_recursive(self)
def test_consider_non_elements_block(self)
def test_capture_stderr(self, args, kwargs)
def test_invalid_property_name(self)
def test_inorder_processing(self)
def test_unicode_literal_parsing(self)
def test_issue_68_numeric_arg(self)
def test_match_similar_numbers(self)
def test_ignore_comments(self)
def test_dynamic_macro_name_clash(self)
def test_no_evaluation(self)
def test_property_replacement(self)
def test_default_param(self)
def test_unicode_file(self)
def test_transitive_evaluation(self)
def test_property_if_statement(self)
def test_mismatch_different_dicts(self)
def test_yaml_custom_constructors(self)
def check_macro_arg(self, s, param, forward, default, rest)
def test_macro_undefined(self)
def test_invalid_property_definitions(self)
def test_tokenize_keep_empty(self)
def test_default_arg_override(self)
def test_yaml_hasattr_support(self)
def test_just_a_dollar_sign(self)
def test_normalize_whitespace_nested(self)
def test_property_scope_global(self)
def test_empty_node_vs_whitespace(self)
def test_recursive_evaluation_wrong_order(self)
def test_target_namespace_only_from_root(self)
def test_xacro_attribute(self)
def test_macro_name_with_colon(self)
def test_yaml_support_dotted(self)
def test_message_functions(self)
def __init__(self, args, kwargs)
def test_overwrite_globals(self)
def test_mismatch_different_numbers(self)
def tokenize(s, sep=', skip_empty=True)
def test_unicode_property(self)
def test_dynamic_macro_names(self)
def test_default_arg(self)
def test_multiple_insert_blocks(self)
def test_property_scope_parent(self)
def test_unicode_property_attribute(self)
def test_yaml_support_dotted_arith(self)
def test_namespace_propagation(self)
def test_resolve_macro(self)
def test_remove_property(self)
def test_xacro_element(self)
def test_no_double_evaluation(self)
def test_float_if_statement(self)
def test_property_scope_parent_namespaced(self)
def test_match_unordered_dicts(self)
def test_broken_input_doesnt_create_empty_output_file(self)
def test_include_with_namespace(self)
def __init__(self, args, kwargs)
def test_substitution_args_arg(self)
def test_property_forwarding(self)
def quick_xacro(self, xml, cli=None, kwargs)
def test_default_arg_missing(self)
def test_enforce_xacro_ns(self)
def test_ignore_xacro_comments(self)
def all_attributes_match(a, b)
def test_param_missing(self)
def test_dynamic_macro_undefined(self)
def test_substitution_args_find(self)
def test_yaml_support_dotted_key_error(self)
def test_greedy_property_evaluation(self)
def test_recursive_bad_math(self)
def test_recursive_evaluation(self)
def test_default_property(self)
def assert_matches(self, a, b)
def test_transitive_arg_evaluation(self)
def test_redefine_global_symbol(self)
def test_should_replace_before_macroexpand(self)
def test_unicode_property_block(self)
def test_restricted_builtins(self)
def test_integer_stays_integer(self)
def process_args(argv, require_input=True)
def test_parse_macro_arg(self)
def test_arg_function(self)
def test_evaluate_macro_params_before_body(self)
def test_yaml_support_dotted_list_iterator(self)
def test_invalid_if_statement(self)
def test_invalid_syntax(self)
def test_default_arg_empty(self)
def test_yaml_support_key_in_dict(self)
def test_create_subdirs(self)
def test_multiple_definition_and_evaluation(self)
def test_math_ignores_spaces(self)
def test_extension_in_expression(self)
def nodes_match(a, b, ignore_nodes)
def test_default_param_override(self)
def test_check_order_warning(self)
def test_double_underscore_property_name_raises(self)
def test_math_expressions(self)
def test_whitespace_vs_empty_node(self)
def test_macro_name_clash(self)
def test_from_issue(self)
def text_values_match(a, b)
def test_boolean_if_statement(self)
def test_include_from_variable(self)
def test_macro_default_param_evaluation_order(self)
def test_include_nonexistent(self)
def test_multi_tree_evaluation(self)
def run_xacro(self, input_path, args)
def test_yaml_custom_constructors_illegal_expr(self)