test_dtlib.py 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272
  1. # Copyright (c) 2019, Nordic Semiconductor
  2. # SPDX-License-Identifier: BSD-3-Clause
  3. import contextlib
  4. import os
  5. import re
  6. import tempfile
  7. import pytest
  8. from devicetree import dtlib
  9. # Test suite for dtlib.py.
  10. #
  11. # Run it using pytest (https://docs.pytest.org/en/stable/usage.html):
  12. #
  13. # $ pytest tests/test_dtlib.py
  14. #
  15. # Extra options you can pass to pytest for debugging:
  16. #
  17. # - to stop on the first failure with shorter traceback output,
  18. # use '-x --tb=native'
  19. # - to drop into a debugger on failure, use '--pdb'
  20. # - to run a particular test function or functions, use
  21. # '-k test_function_pattern_goes_here'
  22. def parse(dts, include_path=(), **kwargs):
  23. '''Parse a DTS string 'dts', using the given include path.
  24. Any kwargs are passed on to DT().'''
  25. fd, path = tempfile.mkstemp(prefix='pytest-', suffix='.dts')
  26. try:
  27. os.write(fd, dts.encode('utf-8'))
  28. return dtlib.DT(path, include_path, **kwargs)
  29. finally:
  30. os.close(fd)
  31. os.unlink(path)
  32. def verify_parse(dts, expected, include_path=()):
  33. '''Like parse(), but also verifies that the parsed DT object's string
  34. representation is expected[1:-1].
  35. The [1:] is so that the first line can be put on a separate line
  36. after triple quotes, as is done below.'''
  37. dt = parse(dts[1:], include_path)
  38. actual = str(dt)
  39. expected = expected[1:-1]
  40. assert actual == expected, f'unexpected round-trip on {dts}'
  41. return dt
  42. def verify_error(dts, expected_msg):
  43. '''Verify that parsing 'dts' results in a DTError with the
  44. given error message 'msg'. The message must match exactly.'''
  45. with pytest.raises(dtlib.DTError) as e:
  46. parse(dts[1:])
  47. actual_msg = str(e.value)
  48. assert actual_msg == expected_msg, f'wrong error from {dts}'
  49. def verify_error_endswith(dts, expected_msg):
  50. '''
  51. Like verify_error(), but checks the message ends with
  52. 'expected_msg' instead of checking for strict equality.
  53. '''
  54. with pytest.raises(dtlib.DTError) as e:
  55. parse(dts[1:])
  56. actual_msg = str(e.value)
  57. assert actual_msg.endswith(expected_msg), f'wrong error from {dts}'
  58. def verify_error_matches(dts, expected_re):
  59. '''
  60. Like verify_error(), but checks the message fully matches regular
  61. expression 'expected_re' instead of checking for strict equality.
  62. '''
  63. with pytest.raises(dtlib.DTError) as e:
  64. parse(dts[1:])
  65. actual_msg = str(e.value)
  66. assert re.fullmatch(expected_re, actual_msg), \
  67. f'wrong error from {dts}' \
  68. f'actual message:\n{actual_msg!r}\n' \
  69. f'does not match:\n{expected_re!r}'
  70. @contextlib.contextmanager
  71. def temporary_chdir(dirname):
  72. '''A context manager that changes directory to 'dirname'.
  73. The current working directory is unconditionally returned to its
  74. present location after the context manager exits.
  75. '''
  76. here = os.getcwd()
  77. try:
  78. os.chdir(dirname)
  79. yield
  80. finally:
  81. os.chdir(here)
  82. def test_invalid_nodenames():
  83. # Regression test that verifies node names are not matched against
  84. # the more permissive set of rules used for property names.
  85. verify_error_endswith("""
  86. /dts-v1/;
  87. / { node? {}; };
  88. """,
  89. "/node?: bad character '?' in node name")
  90. def test_cell_parsing():
  91. '''Miscellaneous properties containing zero or more cells'''
  92. verify_parse("""
  93. /dts-v1/;
  94. / {
  95. a;
  96. b = < >;
  97. c = [ ];
  98. d = < 10 20 >;
  99. e = < 0U 1L 2UL 3LL 4ULL >;
  100. f = < 0x10 0x20 >;
  101. g = < 010 020 >;
  102. h = /bits/ 8 < 0x10 0x20 (-1) >;
  103. i = /bits/ 16 < 0x10 0x20 (-1) >;
  104. j = /bits/ 32 < 0x10 0x20 (-1) >;
  105. k = /bits/ 64 < 0x10 0x20 (-1) >;
  106. l = < 'a' 'b' 'c' >;
  107. };
  108. """,
  109. """
  110. /dts-v1/;
  111. / {
  112. a;
  113. b;
  114. c;
  115. d = < 0xa 0x14 >;
  116. e = < 0x0 0x1 0x2 0x3 0x4 >;
  117. f = < 0x10 0x20 >;
  118. g = < 0x8 0x10 >;
  119. h = [ 10 20 FF ];
  120. i = /bits/ 16 < 0x10 0x20 0xffff >;
  121. j = < 0x10 0x20 0xffffffff >;
  122. k = /bits/ 64 < 0x10 0x20 0xffffffffffffffff >;
  123. l = < 0x61 0x62 0x63 >;
  124. };
  125. """)
  126. verify_error_endswith("""
  127. /dts-v1/;
  128. / {
  129. a = /bits/ 16 < 0x10000 >;
  130. };
  131. """,
  132. ":4 (column 18): parse error: 65536 does not fit in 16 bits")
  133. verify_error_endswith("""
  134. /dts-v1/;
  135. / {
  136. a = < 0x100000000 >;
  137. };
  138. """,
  139. ":4 (column 8): parse error: 4294967296 does not fit in 32 bits")
  140. verify_error_endswith("""
  141. /dts-v1/;
  142. / {
  143. a = /bits/ 128 < 0 >;
  144. };
  145. """,
  146. ":4 (column 13): parse error: expected 8, 16, 32, or 64")
  147. def test_bytes_parsing():
  148. '''Properties with byte array values'''
  149. verify_parse("""
  150. /dts-v1/;
  151. / {
  152. a = [ ];
  153. b = [ 12 34 ];
  154. c = [ 1234 ];
  155. };
  156. """,
  157. """
  158. /dts-v1/;
  159. / {
  160. a;
  161. b = [ 12 34 ];
  162. c = [ 12 34 ];
  163. };
  164. """)
  165. verify_error_endswith("""
  166. /dts-v1/;
  167. / {
  168. a = [ 123 ];
  169. };
  170. """,
  171. ":4 (column 10): parse error: expected two-digit byte or ']'")
  172. def test_string_parsing():
  173. '''Properties with string values'''
  174. verify_parse(r"""
  175. /dts-v1/;
  176. / {
  177. a = "";
  178. b = "ABC";
  179. c = "\\\"\xab\377\a\b\t\n\v\f\r";
  180. };
  181. """,
  182. r"""
  183. /dts-v1/;
  184. / {
  185. a = "";
  186. b = "ABC";
  187. c = "\\\"\xab\xff\a\b\t\n\v\f\r";
  188. };
  189. """)
  190. verify_error_endswith(r"""
  191. /dts-v1/;
  192. / {
  193. a = "\400";
  194. };
  195. """,
  196. ":4 (column 6): parse error: octal escape out of range (> 255)")
  197. def test_char_literal_parsing():
  198. '''Properties with character literal values'''
  199. verify_parse(r"""
  200. /dts-v1/;
  201. / {
  202. a = < '\'' >;
  203. b = < '\x12' >;
  204. };
  205. """,
  206. """
  207. /dts-v1/;
  208. / {
  209. a = < 0x27 >;
  210. b = < 0x12 >;
  211. };
  212. """)
  213. verify_error_endswith("""
  214. /dts-v1/;
  215. / {
  216. // Character literals are not allowed at the top level
  217. a = 'x';
  218. };
  219. """,
  220. ":5 (column 6): parse error: malformed value")
  221. verify_error_endswith("""
  222. /dts-v1/;
  223. / {
  224. a = < '' >;
  225. };
  226. """,
  227. ":4 (column 7): parse error: character literals must be length 1")
  228. verify_error_endswith("""
  229. /dts-v1/;
  230. / {
  231. a = < '12' >;
  232. };
  233. """,
  234. ":4 (column 7): parse error: character literals must be length 1")
  235. def test_incbin(tmp_path):
  236. '''Test /incbin/, an undocumented feature that allows for
  237. binary file inclusion.
  238. https://github.com/dgibson/dtc/commit/e37ec7d5889fa04047daaa7a4ff55150ed7954d4'''
  239. open(tmp_path / "tmp_bin", "wb").write(b"\00\01\02\03")
  240. verify_parse(f"""
  241. /dts-v1/;
  242. / {{
  243. a = /incbin/ ("{tmp_path}/tmp_bin");
  244. b = /incbin/ ("{tmp_path}/tmp_bin", 1, 1);
  245. c = /incbin/ ("{tmp_path}/tmp_bin", 1, 2);
  246. }};
  247. """,
  248. """
  249. /dts-v1/;
  250. / {
  251. a = [ 00 01 02 03 ];
  252. b = [ 01 ];
  253. c = [ 01 02 ];
  254. };
  255. """)
  256. verify_parse("""
  257. /dts-v1/;
  258. / {
  259. a = /incbin/ ("tmp_bin");
  260. };
  261. """,
  262. """
  263. /dts-v1/;
  264. / {
  265. a = [ 00 01 02 03 ];
  266. };
  267. """,
  268. include_path=(tmp_path,))
  269. verify_error_endswith(r"""
  270. /dts-v1/;
  271. / {
  272. a = /incbin/ ("missing");
  273. };
  274. """,
  275. ":4 (column 25): parse error: 'missing' could not be found")
  276. def test_node_merging():
  277. '''
  278. Labels and properties specified for the same node in different
  279. statements should be merged.
  280. '''
  281. verify_parse("""
  282. /dts-v1/;
  283. / {
  284. l1: l2: l1: foo {
  285. foo1 = [ 01 ];
  286. l4: l5: bar {
  287. bar1 = [ 01 ];
  288. };
  289. };
  290. };
  291. l3: &l1 {
  292. foo2 = [ 02 ];
  293. l6: l7: bar {
  294. bar2 = [ 02 ];
  295. };
  296. };
  297. &l3 {
  298. foo3 = [ 03 ];
  299. };
  300. &{/foo} {
  301. foo4 = [ 04 ];
  302. };
  303. &{/foo/bar} {
  304. bar3 = [ 03 ];
  305. l8: baz {};
  306. };
  307. / {
  308. };
  309. / {
  310. top = [ 01 ];
  311. };
  312. """,
  313. """
  314. /dts-v1/;
  315. / {
  316. top = [ 01 ];
  317. l1: l2: l3: foo {
  318. foo1 = [ 01 ];
  319. foo2 = [ 02 ];
  320. foo3 = [ 03 ];
  321. foo4 = [ 04 ];
  322. l4: l5: l6: l7: bar {
  323. bar1 = [ 01 ];
  324. bar2 = [ 02 ];
  325. bar3 = [ 03 ];
  326. l8: baz {
  327. };
  328. };
  329. };
  330. };
  331. """)
  332. verify_error_endswith("""
  333. /dts-v1/;
  334. / {
  335. };
  336. &missing {
  337. };
  338. """,
  339. ":6 (column 1): parse error: undefined node label 'missing'")
  340. verify_error_endswith("""
  341. /dts-v1/;
  342. / {
  343. };
  344. &{foo} {
  345. };
  346. """,
  347. ":6 (column 1): parse error: node path 'foo' does not start with '/'")
  348. verify_error_endswith("""
  349. /dts-v1/;
  350. / {
  351. };
  352. &{/foo} {
  353. };
  354. """,
  355. ":6 (column 1): parse error: component 'foo' in path '/foo' does not exist")
  356. def test_property_labels():
  357. '''Like nodes, properties can have labels too.'''
  358. def verify_label2prop(label, expected):
  359. actual = dt.label2prop[label].name
  360. assert actual == expected, f"label '{label}' mapped to wrong property"
  361. dt = verify_parse("""
  362. /dts-v1/;
  363. / {
  364. a;
  365. b;
  366. l2: c;
  367. l4: l5: l5: l4: d = < 0 >;
  368. };
  369. / {
  370. l1: b;
  371. l3: c;
  372. l6: d;
  373. };
  374. """,
  375. """
  376. /dts-v1/;
  377. / {
  378. a;
  379. l1: b;
  380. l2: l3: c;
  381. l4: l5: l6: d = < 0x0 >;
  382. };
  383. """)
  384. verify_label2prop("l1", "b")
  385. verify_label2prop("l2", "c")
  386. verify_label2prop("l3", "c")
  387. verify_label2prop("l4", "d")
  388. verify_label2prop("l5", "d")
  389. verify_label2prop("l6", "d")
  390. def test_property_offset_labels():
  391. '''
  392. It's possible to give labels to data at nonnegative byte offsets
  393. within a property value.
  394. '''
  395. def verify_label2offset(label, expected_prop, expected_offset):
  396. actual_prop, actual_offset = dt.label2prop_offset[label]
  397. actual_prop = actual_prop.name
  398. assert (actual_prop, actual_offset) == \
  399. (expected_prop, expected_offset), \
  400. f"label '{label}' maps to wrong offset or property"
  401. dt = verify_parse("""
  402. /dts-v1/;
  403. / {
  404. a = l01: l02: < l03: &node l04: l05: 2 l06: >,
  405. l07: l08: [ l09: 03 l10: l11: 04 l12: l13: ] l14:, "A";
  406. b = < 0 > l23: l24:;
  407. node: node {
  408. };
  409. };
  410. """,
  411. """
  412. /dts-v1/;
  413. / {
  414. a = l01: l02: < l03: &node l04: l05: 0x2 l06: l07: l08: >, [ l09: 03 l10: l11: 04 l12: l13: l14: ], "A";
  415. b = < 0x0 l23: l24: >;
  416. node: node {
  417. phandle = < 0x1 >;
  418. };
  419. };
  420. """)
  421. verify_label2offset("l01", "a", 0)
  422. verify_label2offset("l02", "a", 0)
  423. verify_label2offset("l04", "a", 4)
  424. verify_label2offset("l05", "a", 4)
  425. verify_label2offset("l06", "a", 8)
  426. verify_label2offset("l09", "a", 8)
  427. verify_label2offset("l10", "a", 9)
  428. verify_label2offset("l23", "b", 4)
  429. verify_label2offset("l24", "b", 4)
  430. def test_unit_addr():
  431. '''Node unit addresses must be correctly extracted from their names.'''
  432. def verify_unit_addr(path, expected):
  433. node = dt.get_node(path)
  434. assert node.unit_addr == expected, \
  435. f"{node!r} has unexpected unit address"
  436. dt = verify_parse("""
  437. /dts-v1/;
  438. / {
  439. no-unit-addr {
  440. };
  441. unit-addr@ABC {
  442. };
  443. unit-addr-non-numeric@foo-bar {
  444. };
  445. };
  446. """,
  447. """
  448. /dts-v1/;
  449. / {
  450. no-unit-addr {
  451. };
  452. unit-addr@ABC {
  453. };
  454. unit-addr-non-numeric@foo-bar {
  455. };
  456. };
  457. """)
  458. verify_unit_addr("/no-unit-addr", "")
  459. verify_unit_addr("/unit-addr@ABC", "ABC")
  460. verify_unit_addr("/unit-addr-non-numeric@foo-bar", "foo-bar")
  461. def test_node_path_references():
  462. '''Node phandles may be specified using a reference to the node's path.'''
  463. verify_parse("""
  464. /dts-v1/;
  465. / {
  466. a = &label;
  467. b = [ 01 ], &label;
  468. c = [ 01 ], &label, <2>;
  469. d = &{/abc};
  470. label: abc {
  471. e = &label;
  472. f = &{/abc};
  473. };
  474. };
  475. """,
  476. """
  477. /dts-v1/;
  478. / {
  479. a = &label;
  480. b = [ 01 ], &label;
  481. c = [ 01 ], &label, < 0x2 >;
  482. d = &{/abc};
  483. label: abc {
  484. e = &label;
  485. f = &{/abc};
  486. };
  487. };
  488. """)
  489. verify_error("""
  490. /dts-v1/;
  491. / {
  492. sub {
  493. x = &missing;
  494. };
  495. };
  496. """,
  497. "/sub: undefined node label 'missing'")
  498. verify_error("""
  499. /dts-v1/;
  500. / {
  501. sub {
  502. x = &{/sub/missing};
  503. };
  504. };
  505. """,
  506. "/sub: component 'missing' in path '/sub/missing' does not exist")
  507. def test_phandles():
  508. '''Various tests related to phandles.'''
  509. verify_parse("""
  510. /dts-v1/;
  511. / {
  512. x = < &a &{/b} &c >;
  513. dummy1 {
  514. phandle = < 1 >;
  515. };
  516. dummy2 {
  517. phandle = < 3 >;
  518. };
  519. a: a {
  520. };
  521. b {
  522. };
  523. c: c {
  524. phandle = < 0xFF >;
  525. };
  526. };
  527. """,
  528. """
  529. /dts-v1/;
  530. / {
  531. x = < &a &{/b} &c >;
  532. dummy1 {
  533. phandle = < 0x1 >;
  534. };
  535. dummy2 {
  536. phandle = < 0x3 >;
  537. };
  538. a: a {
  539. phandle = < 0x2 >;
  540. };
  541. b {
  542. phandle = < 0x4 >;
  543. };
  544. c: c {
  545. phandle = < 0xff >;
  546. };
  547. };
  548. """)
  549. # Check that a node can be assigned a phandle to itself. This just forces a
  550. # phandle to be allocated on it. The C tools support this too.
  551. verify_parse("""
  552. /dts-v1/;
  553. / {
  554. dummy {
  555. phandle = < 1 >;
  556. };
  557. a {
  558. foo: phandle = < &{/a} >;
  559. };
  560. label: b {
  561. bar: phandle = < &label >;
  562. };
  563. };
  564. """,
  565. """
  566. /dts-v1/;
  567. / {
  568. dummy {
  569. phandle = < 0x1 >;
  570. };
  571. a {
  572. foo: phandle = < &{/a} >;
  573. };
  574. label: b {
  575. bar: phandle = < &label >;
  576. };
  577. };
  578. """)
  579. verify_error("""
  580. /dts-v1/;
  581. / {
  582. sub {
  583. x = < &missing >;
  584. };
  585. };
  586. """,
  587. "/sub: undefined node label 'missing'")
  588. verify_error_endswith("""
  589. /dts-v1/;
  590. / {
  591. a: sub {
  592. x = /bits/ 16 < &a >;
  593. };
  594. };
  595. """,
  596. ":5 (column 19): parse error: phandle references are only allowed in arrays with 32-bit elements")
  597. verify_error("""
  598. /dts-v1/;
  599. / {
  600. foo {
  601. phandle = [ 00 ];
  602. };
  603. };
  604. """,
  605. "/foo: bad phandle length (1), expected 4 bytes")
  606. verify_error("""
  607. /dts-v1/;
  608. / {
  609. foo {
  610. phandle = < 0 >;
  611. };
  612. };
  613. """,
  614. "/foo: bad value 0x00000000 for phandle")
  615. verify_error("""
  616. /dts-v1/;
  617. / {
  618. foo {
  619. phandle = < (-1) >;
  620. };
  621. };
  622. """,
  623. "/foo: bad value 0xffffffff for phandle")
  624. verify_error("""
  625. /dts-v1/;
  626. / {
  627. foo {
  628. phandle = < 17 >;
  629. };
  630. bar {
  631. phandle = < 17 >;
  632. };
  633. };
  634. """,
  635. "/bar: duplicated phandle 0x11 (seen before at /foo)")
  636. verify_error("""
  637. /dts-v1/;
  638. / {
  639. foo {
  640. phandle = < &{/bar} >;
  641. };
  642. bar {
  643. };
  644. };
  645. """,
  646. "/foo: phandle refers to another node")
  647. def test_phandle2node():
  648. '''Test the phandle2node dict in a dt instance.'''
  649. def verify_phandle2node(prop, offset, expected_name):
  650. phandle = dtlib.to_num(dt.root.props[prop].value[offset:offset + 4])
  651. actual_name = dt.phandle2node[phandle].name
  652. assert actual_name == expected_name, \
  653. f"'{prop}' is a phandle for the wrong thing"
  654. dt = parse("""
  655. /dts-v1/;
  656. / {
  657. phandle_ = < &{/node1} 0 1 >;
  658. phandles = < 0 &{/node2} 1 &{/node3} >;
  659. node1 {
  660. phandle = < 123 >;
  661. };
  662. node2 {
  663. };
  664. node3 {
  665. };
  666. };
  667. """)
  668. verify_phandle2node("phandle_", 0, "node1")
  669. verify_phandle2node("phandles", 4, "node2")
  670. verify_phandle2node("phandles", 12, "node3")
  671. def test_mixed_assign():
  672. '''Test mixed value type assignments'''
  673. verify_parse("""
  674. /dts-v1/;
  675. / {
  676. x = /bits/ 8 < 0xFF 0xFF >,
  677. &abc,
  678. < 0xFF &abc 0xFF &abc >,
  679. &abc,
  680. [ FF FF ],
  681. "abc";
  682. abc: abc {
  683. };
  684. };
  685. """,
  686. """
  687. /dts-v1/;
  688. / {
  689. x = [ FF FF ], &abc, < 0xff &abc 0xff &abc >, &abc, [ FF FF ], "abc";
  690. abc: abc {
  691. phandle = < 0x1 >;
  692. };
  693. };
  694. """)
  695. def test_deletion():
  696. '''Properties and nodes may be deleted from the tree.'''
  697. # Test property deletion
  698. verify_parse("""
  699. /dts-v1/;
  700. / {
  701. keep = < 1 >;
  702. delete = < &sub >, &sub;
  703. /delete-property/ missing;
  704. /delete-property/ delete;
  705. sub: sub {
  706. y = < &sub >, &sub;
  707. };
  708. };
  709. &sub {
  710. /delete-property/ y;
  711. };
  712. """,
  713. """
  714. /dts-v1/;
  715. / {
  716. keep = < 0x1 >;
  717. sub: sub {
  718. };
  719. };
  720. """)
  721. # Test node deletion
  722. verify_parse("""
  723. /dts-v1/;
  724. / {
  725. sub1 {
  726. x = < 1 >;
  727. sub2 {
  728. x = < &sub >, &sub;
  729. };
  730. /delete-node/ sub2;
  731. };
  732. sub3: sub3 {
  733. x = < &sub >, &sub;
  734. };
  735. sub4 {
  736. x = < &sub >, &sub;
  737. };
  738. };
  739. /delete-node/ &sub3;
  740. /delete-node/ &{/sub4};
  741. """,
  742. """
  743. /dts-v1/;
  744. / {
  745. sub1 {
  746. x = < 0x1 >;
  747. };
  748. };
  749. """)
  750. verify_error_endswith("""
  751. /dts-v1/;
  752. / {
  753. };
  754. /delete-node/ &missing;
  755. """,
  756. ":6 (column 15): parse error: undefined node label 'missing'")
  757. verify_error_endswith("""
  758. /dts-v1/;
  759. /delete-node/ {
  760. """,
  761. ":3 (column 15): parse error: expected label (&foo) or path (&{/foo/bar}) reference")
  762. def test_include_curdir(tmp_path):
  763. '''Verify that /include/ (which is handled in the lexer) searches the
  764. current directory'''
  765. with temporary_chdir(tmp_path):
  766. with open("same-dir-1", "w") as f:
  767. f.write("""
  768. x = [ 00 ];
  769. /include/ "same-dir-2"
  770. """)
  771. with open("same-dir-2", "w") as f:
  772. f.write("""
  773. y = [ 01 ];
  774. /include/ "same-dir-3"
  775. """)
  776. with open("same-dir-3", "w") as f:
  777. f.write("""
  778. z = [ 02 ];
  779. """)
  780. with open("test.dts", "w") as f:
  781. f.write("""
  782. /dts-v1/;
  783. / {
  784. /include/ "same-dir-1"
  785. };
  786. """)
  787. dt = dtlib.DT("test.dts")
  788. assert str(dt) == """
  789. /dts-v1/;
  790. / {
  791. x = [ 00 ];
  792. y = [ 01 ];
  793. z = [ 02 ];
  794. };
  795. """[1:-1]
  796. def test_include_is_lexical(tmp_path):
  797. '''/include/ is done in the lexer, which means that property
  798. definitions can span multiple included files in different
  799. directories.'''
  800. with open(tmp_path / "tmp2.dts", "w") as f:
  801. f.write("""
  802. /dts-v1/;
  803. / {
  804. """)
  805. with open(tmp_path / "tmp3.dts", "w") as f:
  806. f.write("""
  807. x = <1>;
  808. """)
  809. subdir_1 = tmp_path / "subdir-1"
  810. subdir_1.mkdir()
  811. with open(subdir_1 / "via-include-path-1", "w") as f:
  812. f.write("""
  813. = /include/ "via-include-path-2"
  814. """)
  815. subdir_2 = tmp_path / "subdir-2"
  816. subdir_2.mkdir()
  817. with open(subdir_2 / "via-include-path-2", "w") as f:
  818. f.write("""
  819. <2>;
  820. };
  821. """)
  822. with open(tmp_path / "test.dts", "w") as test_dts:
  823. test_dts.write("""
  824. /include/ "tmp2.dts"
  825. /include/ "tmp3.dts"
  826. y /include/ "via-include-path-1"
  827. """)
  828. with temporary_chdir(tmp_path):
  829. dt = dtlib.DT("test.dts", include_path=(subdir_1, subdir_2))
  830. expected_dt = """
  831. /dts-v1/;
  832. / {
  833. x = < 0x1 >;
  834. y = < 0x2 >;
  835. };
  836. """[1:-1]
  837. assert str(dt) == expected_dt
  838. def test_include_misc(tmp_path):
  839. '''Miscellaneous /include/ tests.'''
  840. # Missing includes should error out.
  841. verify_error_endswith("""
  842. /include/ "missing"
  843. """,
  844. ":1 (column 1): parse error: 'missing' could not be found")
  845. # Verify that an error in an included file points to the right location
  846. with temporary_chdir(tmp_path):
  847. with open("tmp2.dts", "w") as f:
  848. f.write("""\
  849. x
  850. """)
  851. with open("tmp.dts", "w") as f:
  852. f.write("""
  853. /include/ "tmp2.dts"
  854. """)
  855. with pytest.raises(dtlib.DTError) as e:
  856. dtlib.DT("tmp.dts")
  857. assert str(e.value) == \
  858. "tmp2.dts:3 (column 3): parse error: expected '/dts-v1/;' at start of file"
  859. def test_include_recursion(tmp_path):
  860. '''Test recursive /include/ detection'''
  861. with temporary_chdir(tmp_path):
  862. with open("tmp2.dts", "w") as f:
  863. f.write('/include/ "tmp3.dts"\n')
  864. with open("tmp3.dts", "w") as f:
  865. f.write('/include/ "tmp.dts"\n')
  866. with open("tmp.dts", "w") as f:
  867. f.write('/include/ "tmp2.dts"\n')
  868. with pytest.raises(dtlib.DTError) as e:
  869. dtlib.DT("tmp.dts")
  870. expected_err = """\
  871. tmp3.dts:1 (column 1): parse error: recursive /include/:
  872. tmp.dts:1 ->
  873. tmp2.dts:1 ->
  874. tmp3.dts:1 ->
  875. tmp.dts"""
  876. assert str(e.value) == expected_err
  877. with open("tmp.dts", "w") as f:
  878. f.write('/include/ "tmp.dts"\n')
  879. with pytest.raises(dtlib.DTError) as e:
  880. dtlib.DT("tmp.dts")
  881. expected_err = """\
  882. tmp.dts:1 (column 1): parse error: recursive /include/:
  883. tmp.dts:1 ->
  884. tmp.dts"""
  885. assert str(e.value) == expected_err
  886. def test_omit_if_no_ref():
  887. '''The /omit-if-no-ref/ marker is a bit of undocumented
  888. dtc magic that removes a node from the tree if it isn't
  889. referred to elsewhere.
  890. https://elinux.org/Device_Tree_Source_Undocumented
  891. '''
  892. verify_parse("""
  893. /dts-v1/;
  894. / {
  895. x = < &{/referenced} >, &referenced2;
  896. /omit-if-no-ref/ referenced {
  897. };
  898. referenced2: referenced2 {
  899. };
  900. /omit-if-no-ref/ unreferenced {
  901. };
  902. l1: /omit-if-no-ref/ unreferenced2 {
  903. };
  904. /omit-if-no-ref/ l2: unreferenced3 {
  905. };
  906. unreferenced4: unreferenced4 {
  907. };
  908. unreferenced5 {
  909. };
  910. };
  911. /omit-if-no-ref/ &referenced2;
  912. /omit-if-no-ref/ &unreferenced4;
  913. /omit-if-no-ref/ &{/unreferenced5};
  914. """,
  915. """
  916. /dts-v1/;
  917. / {
  918. x = < &{/referenced} >, &referenced2;
  919. referenced {
  920. phandle = < 0x1 >;
  921. };
  922. referenced2: referenced2 {
  923. };
  924. };
  925. """)
  926. verify_error_endswith("""
  927. /dts-v1/;
  928. / {
  929. /omit-if-no-ref/ x = "";
  930. };
  931. """,
  932. ":4 (column 21): parse error: /omit-if-no-ref/ can only be used on nodes")
  933. verify_error_endswith("""
  934. /dts-v1/;
  935. / {
  936. /omit-if-no-ref/ x;
  937. };
  938. """,
  939. ":4 (column 20): parse error: /omit-if-no-ref/ can only be used on nodes")
  940. verify_error_endswith("""
  941. /dts-v1/;
  942. / {
  943. /omit-if-no-ref/ {
  944. };
  945. };
  946. """,
  947. ":4 (column 19): parse error: expected node or property name")
  948. verify_error_endswith("""
  949. /dts-v1/;
  950. / {
  951. /omit-if-no-ref/ = < 0 >;
  952. };
  953. """,
  954. ":4 (column 19): parse error: expected node or property name")
  955. verify_error_endswith("""
  956. /dts-v1/;
  957. / {
  958. };
  959. /omit-if-no-ref/ &missing;
  960. """,
  961. ":6 (column 18): parse error: undefined node label 'missing'")
  962. verify_error_endswith("""
  963. /dts-v1/;
  964. /omit-if-no-ref/ {
  965. """,
  966. ":3 (column 18): parse error: expected label (&foo) or path (&{/foo/bar}) reference")
  967. def test_expr():
  968. '''Property values may contain expressions.'''
  969. verify_parse("""
  970. /dts-v1/;
  971. / {
  972. ter1 = < (0 ? 1 : 0 ? 2 : 3) >;
  973. ter2 = < (0 ? 1 : 1 ? 2 : 3) >;
  974. ter3 = < (1 ? 1 : 0 ? 2 : 3) >;
  975. ter4 = < (1 ? 1 : 1 ? 2 : 3) >;
  976. or1 = < (0 || 0) >;
  977. or2 = < (0 || 1) >;
  978. or3 = < (1 || 0) >;
  979. or4 = < (1 || 1) >;
  980. and1 = < (0 && 0) >;
  981. and2 = < (0 && 1) >;
  982. and3 = < (1 && 0) >;
  983. and4 = < (1 && 1) >;
  984. bitor = < (1 | 2) >;
  985. bitxor = < (7 ^ 2) >;
  986. bitand = < (3 & 6) >;
  987. eq1 = < (1 == 0) >;
  988. eq2 = < (1 == 1) >;
  989. neq1 = < (1 != 0) >;
  990. neq2 = < (1 != 1) >;
  991. lt1 = < (1 < 2) >;
  992. lt2 = < (2 < 2) >;
  993. lt3 = < (3 < 2) >;
  994. lteq1 = < (1 <= 2) >;
  995. lteq2 = < (2 <= 2) >;
  996. lteq3 = < (3 <= 2) >;
  997. gt1 = < (1 > 2) >;
  998. gt2 = < (2 > 2) >;
  999. gt3 = < (3 > 2) >;
  1000. gteq1 = < (1 >= 2) >;
  1001. gteq2 = < (2 >= 2) >;
  1002. gteq3 = < (3 >= 2) >;
  1003. lshift = < (2 << 3) >;
  1004. rshift = < (16 >> 3) >;
  1005. add = < (3 + 4) >;
  1006. sub = < (7 - 4) >;
  1007. mul = < (3 * 4) >;
  1008. div = < (11 / 3) >;
  1009. mod = < (11 % 3) >;
  1010. unary_minus = < (-3) >;
  1011. bitnot = < (~1) >;
  1012. not0 = < (!-1) >;
  1013. not1 = < (!0) >;
  1014. not2 = < (!1) >;
  1015. not3 = < (!2) >;
  1016. nest = < (((--3) + (-2)) * (--(-2))) >;
  1017. char_lits = < ('a' + 'b') >;
  1018. };
  1019. """,
  1020. """
  1021. /dts-v1/;
  1022. / {
  1023. ter1 = < 0x3 >;
  1024. ter2 = < 0x2 >;
  1025. ter3 = < 0x1 >;
  1026. ter4 = < 0x1 >;
  1027. or1 = < 0x0 >;
  1028. or2 = < 0x1 >;
  1029. or3 = < 0x1 >;
  1030. or4 = < 0x1 >;
  1031. and1 = < 0x0 >;
  1032. and2 = < 0x0 >;
  1033. and3 = < 0x0 >;
  1034. and4 = < 0x1 >;
  1035. bitor = < 0x3 >;
  1036. bitxor = < 0x5 >;
  1037. bitand = < 0x2 >;
  1038. eq1 = < 0x0 >;
  1039. eq2 = < 0x1 >;
  1040. neq1 = < 0x1 >;
  1041. neq2 = < 0x0 >;
  1042. lt1 = < 0x1 >;
  1043. lt2 = < 0x0 >;
  1044. lt3 = < 0x0 >;
  1045. lteq1 = < 0x1 >;
  1046. lteq2 = < 0x1 >;
  1047. lteq3 = < 0x0 >;
  1048. gt1 = < 0x0 >;
  1049. gt2 = < 0x0 >;
  1050. gt3 = < 0x1 >;
  1051. gteq1 = < 0x0 >;
  1052. gteq2 = < 0x1 >;
  1053. gteq3 = < 0x1 >;
  1054. lshift = < 0x10 >;
  1055. rshift = < 0x2 >;
  1056. add = < 0x7 >;
  1057. sub = < 0x3 >;
  1058. mul = < 0xc >;
  1059. div = < 0x3 >;
  1060. mod = < 0x2 >;
  1061. unary_minus = < 0xfffffffd >;
  1062. bitnot = < 0xfffffffe >;
  1063. not0 = < 0x0 >;
  1064. not1 = < 0x1 >;
  1065. not2 = < 0x0 >;
  1066. not3 = < 0x0 >;
  1067. nest = < 0xfffffffe >;
  1068. char_lits = < 0xc3 >;
  1069. };
  1070. """)
  1071. verify_error_endswith("""
  1072. /dts-v1/;
  1073. / {
  1074. a = < (1/(-1 + 1)) >;
  1075. };
  1076. """,
  1077. ":4 (column 18): parse error: division by zero")
  1078. verify_error_endswith("""
  1079. /dts-v1/;
  1080. / {
  1081. a = < (1%0) >;
  1082. };
  1083. """,
  1084. ":4 (column 11): parse error: division by zero")
  1085. def test_comment_removal():
  1086. '''Comments should be removed when round-tripped to a str.'''
  1087. verify_parse("""
  1088. /**//dts-v1//**/;//
  1089. //
  1090. // foo
  1091. / /**/{// foo
  1092. x/**/=/*
  1093. foo
  1094. */</**/1/***/>/****/;/**/}/*/**/;
  1095. """,
  1096. """
  1097. /dts-v1/;
  1098. / {
  1099. x = < 0x1 >;
  1100. };
  1101. """)
  1102. def verify_path_is(path, node_name, dt):
  1103. '''Verify 'node.name' matches 'node_name' in 'dt'.'''
  1104. try:
  1105. node = dt.get_node(path)
  1106. assert node.name == node_name, f'unexpected path {path}'
  1107. except dtlib.DTError:
  1108. assert False, f'no node found for path {path}'
  1109. def verify_path_error(path, msg, dt):
  1110. '''Verify that an attempt to get node 'path' from 'dt' raises
  1111. a DTError whose str is 'msg'.'''
  1112. with pytest.raises(dtlib.DTError) as e:
  1113. dt.get_node(path)
  1114. assert str(e.value) == msg, f"'{path}' gives the wrong error"
  1115. def test_get_node():
  1116. '''Test DT.get_node().'''
  1117. dt = parse("""
  1118. /dts-v1/;
  1119. / {
  1120. foo {
  1121. bar {
  1122. };
  1123. };
  1124. baz {
  1125. };
  1126. };
  1127. """)
  1128. verify_path_is("/", "/", dt)
  1129. verify_path_is("//", "/", dt)
  1130. verify_path_is("///", "/", dt)
  1131. verify_path_is("/foo", "foo", dt)
  1132. verify_path_is("//foo", "foo", dt)
  1133. verify_path_is("///foo", "foo", dt)
  1134. verify_path_is("/foo/bar", "bar", dt)
  1135. verify_path_is("//foo//bar", "bar", dt)
  1136. verify_path_is("///foo///bar", "bar", dt)
  1137. verify_path_is("/baz", "baz", dt)
  1138. verify_path_error(
  1139. "",
  1140. "no alias '' found -- did you forget the leading '/' in the node path?",
  1141. dt)
  1142. verify_path_error(
  1143. "missing",
  1144. "no alias 'missing' found -- did you forget the leading '/' in the node path?",
  1145. dt)
  1146. verify_path_error(
  1147. "/missing",
  1148. "component 'missing' in path '/missing' does not exist",
  1149. dt)
  1150. verify_path_error(
  1151. "/foo/missing",
  1152. "component 'missing' in path '/foo/missing' does not exist",
  1153. dt)
  1154. def verify_path_exists(path):
  1155. assert dt.has_node(path), f"path '{path}' does not exist"
  1156. def verify_path_missing(path):
  1157. assert not dt.has_node(path), f"path '{path}' exists"
  1158. verify_path_exists("/")
  1159. verify_path_exists("/foo")
  1160. verify_path_exists("/foo/bar")
  1161. verify_path_missing("/missing")
  1162. verify_path_missing("/foo/missing")
  1163. def test_aliases():
  1164. '''Test /aliases'''
  1165. dt = parse("""
  1166. /dts-v1/;
  1167. / {
  1168. aliases {
  1169. alias1 = &l1;
  1170. alias2 = &l2;
  1171. alias3 = &{/sub/node3};
  1172. alias4 = &{/node4};
  1173. };
  1174. l1: node1 {
  1175. };
  1176. l2: node2 {
  1177. };
  1178. sub {
  1179. node3 {
  1180. };
  1181. };
  1182. node4 {
  1183. node5 {
  1184. };
  1185. };
  1186. };
  1187. """)
  1188. def verify_alias_target(alias, node_name):
  1189. verify_path_is(alias, node_name, dt)
  1190. assert alias in dt.alias2node
  1191. assert dt.alias2node[alias].name == node_name, f"bad result for {alias}"
  1192. verify_alias_target("alias1", "node1")
  1193. verify_alias_target("alias2", "node2")
  1194. verify_alias_target("alias3", "node3")
  1195. verify_path_is("alias4/node5", "node5", dt)
  1196. verify_path_error(
  1197. "alias4/node5/node6",
  1198. "component 'node6' in path 'alias4/node5/node6' does not exist",
  1199. dt)
  1200. verify_error_matches("""
  1201. /dts-v1/;
  1202. / {
  1203. aliases {
  1204. a = [ 00 ];
  1205. };
  1206. };
  1207. """,
  1208. "expected property 'a' on /aliases in .*" +
  1209. re.escape("to be assigned with either 'a = &foo' or 'a = \"/path/to/node\"', not 'a = [ 00 ];'"))
  1210. verify_error_matches(r"""
  1211. /dts-v1/;
  1212. / {
  1213. aliases {
  1214. a = "\xFF";
  1215. };
  1216. };
  1217. """,
  1218. re.escape(r"value of property 'a' (b'\xff\x00') on /aliases in ") +
  1219. ".* is not valid UTF-8")
  1220. verify_error("""
  1221. /dts-v1/;
  1222. / {
  1223. aliases {
  1224. A = "/aliases";
  1225. };
  1226. };
  1227. """,
  1228. "/aliases: alias property name 'A' should include only characters from [0-9a-z-]")
  1229. verify_error_matches(r"""
  1230. /dts-v1/;
  1231. / {
  1232. aliases {
  1233. a = "/missing";
  1234. };
  1235. };
  1236. """,
  1237. "property 'a' on /aliases in .* points to the non-existent node \"/missing\"")
  1238. def test_prop_type():
  1239. '''Test Property.type'''
  1240. def verify_type(prop, expected):
  1241. actual = dt.root.props[prop].type
  1242. assert actual == expected, f'{prop} has wrong type'
  1243. dt = parse("""
  1244. /dts-v1/;
  1245. / {
  1246. empty;
  1247. bytes1 = [ ];
  1248. bytes2 = [ 01 ];
  1249. bytes3 = [ 01 02 ];
  1250. bytes4 = foo: [ 01 bar: 02 ];
  1251. bytes5 = /bits/ 8 < 1 2 3 >;
  1252. num = < 1 >;
  1253. nums1 = < >;
  1254. nums2 = < >, < >;
  1255. nums3 = < 1 2 >;
  1256. nums4 = < 1 2 >, < 3 >, < 4 >;
  1257. string = "foo";
  1258. strings = "foo", "bar";
  1259. path1 = &node;
  1260. path2 = &{/node};
  1261. phandle1 = < &node >;
  1262. phandle2 = < &{/node} >;
  1263. phandles1 = < &node &node >;
  1264. phandles2 = < &node >, < &node >;
  1265. phandle-and-nums-1 = < &node 1 >;
  1266. phandle-and-nums-2 = < &node 1 2 &node 3 4 >;
  1267. phandle-and-nums-3 = < &node 1 2 >, < &node 3 4 >;
  1268. compound1 = < 1 >, [ 02 ];
  1269. compound2 = "foo", < >;
  1270. node: node {
  1271. };
  1272. };
  1273. """)
  1274. verify_type("empty", dtlib.Type.EMPTY)
  1275. verify_type("bytes1", dtlib.Type.BYTES)
  1276. verify_type("bytes2", dtlib.Type.BYTES)
  1277. verify_type("bytes3", dtlib.Type.BYTES)
  1278. verify_type("bytes4", dtlib.Type.BYTES)
  1279. verify_type("bytes5", dtlib.Type.BYTES)
  1280. verify_type("num", dtlib.Type.NUM)
  1281. verify_type("nums1", dtlib.Type.NUMS)
  1282. verify_type("nums2", dtlib.Type.NUMS)
  1283. verify_type("nums3", dtlib.Type.NUMS)
  1284. verify_type("nums4", dtlib.Type.NUMS)
  1285. verify_type("string", dtlib.Type.STRING)
  1286. verify_type("strings", dtlib.Type.STRINGS)
  1287. verify_type("phandle1", dtlib.Type.PHANDLE)
  1288. verify_type("phandle2", dtlib.Type.PHANDLE)
  1289. verify_type("phandles1", dtlib.Type.PHANDLES)
  1290. verify_type("phandles2", dtlib.Type.PHANDLES)
  1291. verify_type("phandle-and-nums-1", dtlib.Type.PHANDLES_AND_NUMS)
  1292. verify_type("phandle-and-nums-2", dtlib.Type.PHANDLES_AND_NUMS)
  1293. verify_type("phandle-and-nums-3", dtlib.Type.PHANDLES_AND_NUMS)
  1294. verify_type("path1", dtlib.Type.PATH)
  1295. verify_type("path2", dtlib.Type.PATH)
  1296. verify_type("compound1", dtlib.Type.COMPOUND)
  1297. verify_type("compound2", dtlib.Type.COMPOUND)
  1298. def test_prop_type_casting():
  1299. '''Test Property.to_{num,nums,string,strings,node}()'''
  1300. dt = parse(r"""
  1301. /dts-v1/;
  1302. / {
  1303. u = < 1 >;
  1304. s = < 0xFFFFFFFF >;
  1305. u8 = /bits/ 8 < 1 >;
  1306. u16 = /bits/ 16 < 1 2 >;
  1307. u64 = /bits/ 64 < 1 >;
  1308. bytes = [ 01 02 03 ];
  1309. empty;
  1310. zero = < >;
  1311. two_u = < 1 2 >;
  1312. two_s = < 0xFFFFFFFF 0xFFFFFFFE >;
  1313. three_u = < 1 2 3 >;
  1314. three_u_split = < 1 >, < 2 >, < 3 >;
  1315. empty_string = "";
  1316. string = "foo\tbar baz";
  1317. invalid_string = "\xff";
  1318. strings = "foo", "bar", "baz";
  1319. invalid_strings = "foo", "\xff", "bar";
  1320. ref = <&{/target}>;
  1321. refs = <&{/target} &{/target2}>;
  1322. refs2 = <&{/target}>, <&{/target2}>;
  1323. path = &{/target};
  1324. manualpath = "/target";
  1325. missingpath = "/missing";
  1326. target {
  1327. phandle = < 100 >;
  1328. };
  1329. target2 {
  1330. };
  1331. };
  1332. """)
  1333. # Test Property.to_num()
  1334. def verify_to_num(prop, signed, expected):
  1335. signed_str = "a signed" if signed else "an unsigned"
  1336. actual = dt.root.props[prop].to_num(signed)
  1337. assert actual == expected, \
  1338. f"{prop} has bad {signed_str} numeric value"
  1339. def verify_to_num_error_matches(prop, expected_re):
  1340. with pytest.raises(dtlib.DTError) as e:
  1341. dt.root.props[prop].to_num()
  1342. actual_msg = str(e.value)
  1343. assert re.fullmatch(expected_re, actual_msg), \
  1344. f"'{prop}' to_num gives the wrong error: " \
  1345. f"actual message:\n{actual_msg!r}\n" \
  1346. f"does not match:\n{expected_re!r}"
  1347. verify_to_num("u", False, 1)
  1348. verify_to_num("u", True, 1)
  1349. verify_to_num("s", False, 0xFFFFFFFF)
  1350. verify_to_num("s", True, -1)
  1351. verify_to_num_error_matches(
  1352. "two_u",
  1353. "expected property 'two_u' on / in .* to be assigned with " +
  1354. re.escape("'two_u = < (number) >;', not 'two_u = < 0x1 0x2 >;'"))
  1355. verify_to_num_error_matches(
  1356. "u8",
  1357. "expected property 'u8' on / in .* to be assigned with " +
  1358. re.escape("'u8 = < (number) >;', not 'u8 = [ 01 ];'"))
  1359. verify_to_num_error_matches(
  1360. "u16",
  1361. "expected property 'u16' on / in .* to be assigned with " +
  1362. re.escape("'u16 = < (number) >;', not 'u16 = /bits/ 16 < 0x1 0x2 >;'"))
  1363. verify_to_num_error_matches(
  1364. "u64",
  1365. "expected property 'u64' on / in .* to be assigned with " +
  1366. re.escape("'u64 = < (number) >;', not 'u64 = /bits/ 64 < 0x1 >;'"))
  1367. verify_to_num_error_matches(
  1368. "string",
  1369. "expected property 'string' on / in .* to be assigned with " +
  1370. re.escape("'string = < (number) >;', not 'string = \"foo\\tbar baz\";'"))
  1371. # Test Property.to_nums()
  1372. def verify_to_nums(prop, signed, expected):
  1373. signed_str = "signed" if signed else "unsigned"
  1374. actual = dt.root.props[prop].to_nums(signed)
  1375. assert actual == expected, \
  1376. f"'{prop}' gives the wrong {signed_str} numbers"
  1377. def verify_to_nums_error_matches(prop, expected_re):
  1378. with pytest.raises(dtlib.DTError) as e:
  1379. dt.root.props[prop].to_nums()
  1380. assert re.fullmatch(expected_re, str(e.value)), \
  1381. f"'{prop}' to_nums gives the wrong error"
  1382. verify_to_nums("zero", False, [])
  1383. verify_to_nums("u", False, [1])
  1384. verify_to_nums("two_u", False, [1, 2])
  1385. verify_to_nums("two_u", True, [1, 2])
  1386. verify_to_nums("two_s", False, [0xFFFFFFFF, 0xFFFFFFFE])
  1387. verify_to_nums("two_s", True, [-1, -2])
  1388. verify_to_nums("three_u", False, [1, 2, 3])
  1389. verify_to_nums("three_u_split", False, [1, 2, 3])
  1390. verify_to_nums_error_matches(
  1391. "empty",
  1392. "expected property 'empty' on / in .* to be assigned with " +
  1393. re.escape("'empty = < (number) (number) ... >;', not 'empty;'"))
  1394. verify_to_nums_error_matches(
  1395. "string",
  1396. "expected property 'string' on / in .* to be assigned with " +
  1397. re.escape("'string = < (number) (number) ... >;', ") +
  1398. re.escape("not 'string = \"foo\\tbar baz\";'"))
  1399. # Test Property.to_bytes()
  1400. def verify_to_bytes(prop, expected):
  1401. actual = dt.root.props[prop].to_bytes()
  1402. assert actual == expected, f"'{prop}' gives the wrong bytes"
  1403. def verify_to_bytes_error_matches(prop, expected_re):
  1404. with pytest.raises(dtlib.DTError) as e:
  1405. dt.root.props[prop].to_bytes()
  1406. assert re.fullmatch(expected_re, str(e.value)), \
  1407. f"'{prop}' gives the wrong error"
  1408. verify_to_bytes("u8", b"\x01")
  1409. verify_to_bytes("bytes", b"\x01\x02\x03")
  1410. verify_to_bytes_error_matches(
  1411. "u16",
  1412. "expected property 'u16' on / in .* to be assigned with " +
  1413. re.escape("'u16 = [ (byte) (byte) ... ];', ") +
  1414. re.escape("not 'u16 = /bits/ 16 < 0x1 0x2 >;'"))
  1415. verify_to_bytes_error_matches(
  1416. "empty",
  1417. "expected property 'empty' on / in .* to be assigned with " +
  1418. re.escape("'empty = [ (byte) (byte) ... ];', not 'empty;'"))
  1419. # Test Property.to_string()
  1420. def verify_to_string(prop, expected):
  1421. actual = dt.root.props[prop].to_string()
  1422. assert actual == expected, f"'{prop}' to_string gives the wrong string"
  1423. def verify_to_string_error_matches(prop, expected_re):
  1424. with pytest.raises(dtlib.DTError) as e:
  1425. dt.root.props[prop].to_string()
  1426. assert re.fullmatch(expected_re, str(e.value)), \
  1427. f"'{prop}' gives the wrong error"
  1428. verify_to_string("empty_string", "")
  1429. verify_to_string("string", "foo\tbar baz")
  1430. verify_to_string_error_matches(
  1431. "u",
  1432. "expected property 'u' on / in .* to be assigned with " +
  1433. re.escape("'u = \"string\";', not 'u = < 0x1 >;'"))
  1434. verify_to_string_error_matches(
  1435. "strings",
  1436. "expected property 'strings' on / in .* to be assigned with " +
  1437. re.escape("'strings = \"string\";', ")+
  1438. re.escape("not 'strings = \"foo\", \"bar\", \"baz\";'"))
  1439. verify_to_string_error_matches(
  1440. "invalid_string",
  1441. re.escape(r"value of property 'invalid_string' (b'\xff\x00') on / ") +
  1442. "in .* is not valid UTF-8")
  1443. # Test Property.to_strings()
  1444. def verify_to_strings(prop, expected):
  1445. actual = dt.root.props[prop].to_strings()
  1446. assert actual == expected, f"'{prop}' to_strings gives the wrong value"
  1447. def verify_to_strings_error_matches(prop, expected_re):
  1448. with pytest.raises(dtlib.DTError) as e:
  1449. dt.root.props[prop].to_strings()
  1450. assert re.fullmatch(expected_re, str(e.value)), \
  1451. f"'{prop}' gives the wrong error"
  1452. verify_to_strings("empty_string", [""])
  1453. verify_to_strings("string", ["foo\tbar baz"])
  1454. verify_to_strings("strings", ["foo", "bar", "baz"])
  1455. verify_to_strings_error_matches(
  1456. "u",
  1457. "expected property 'u' on / in .* to be assigned with " +
  1458. re.escape("'u = \"string\", \"string\", ... ;', not 'u = < 0x1 >;'"))
  1459. verify_to_strings_error_matches(
  1460. "invalid_strings",
  1461. "value of property 'invalid_strings' " +
  1462. re.escape(r"(b'foo\x00\xff\x00bar\x00') on / in ") +
  1463. ".* is not valid UTF-8")
  1464. # Test Property.to_node()
  1465. def verify_to_node(prop, path):
  1466. actual = dt.root.props[prop].to_node().path
  1467. assert actual == path, f"'{prop}' points at wrong path"
  1468. def verify_to_node_error_matches(prop, expected_re):
  1469. with pytest.raises(dtlib.DTError) as e:
  1470. dt.root.props[prop].to_node()
  1471. assert re.fullmatch(expected_re, str(e.value)), \
  1472. f"'{prop} gives the wrong error"
  1473. verify_to_node("ref", "/target")
  1474. verify_to_node_error_matches(
  1475. "u",
  1476. "expected property 'u' on / in .* to be assigned with " +
  1477. re.escape("'u = < &foo >;', not 'u = < 0x1 >;'"))
  1478. verify_to_node_error_matches(
  1479. "string",
  1480. "expected property 'string' on / in .* to be assigned with " +
  1481. re.escape("'string = < &foo >;', not 'string = \"foo\\tbar baz\";'"))
  1482. # Test Property.to_nodes()
  1483. def verify_to_nodes(prop, paths):
  1484. actual = [node.path for node in dt.root.props[prop].to_nodes()]
  1485. assert actual == paths, f"'{prop} gives wrong node paths"
  1486. def verify_to_nodes_error_matches(prop, expected_re):
  1487. with pytest.raises(dtlib.DTError) as e:
  1488. dt.root.props[prop].to_nodes()
  1489. assert re.fullmatch(expected_re, str(e.value)), \
  1490. f"'{prop} gives wrong error"
  1491. verify_to_nodes("zero", [])
  1492. verify_to_nodes("ref", ["/target"])
  1493. verify_to_nodes("refs", ["/target", "/target2"])
  1494. verify_to_nodes("refs2", ["/target", "/target2"])
  1495. verify_to_nodes_error_matches(
  1496. "u",
  1497. "expected property 'u' on / in .* to be assigned with " +
  1498. re.escape("'u = < &foo &bar ... >;', not 'u = < 0x1 >;'"))
  1499. verify_to_nodes_error_matches(
  1500. "string",
  1501. "expected property 'string' on / in .* to be assigned with " +
  1502. re.escape("'string = < &foo &bar ... >;', ") +
  1503. re.escape("not 'string = \"foo\\tbar baz\";'"))
  1504. # Test Property.to_path()
  1505. def verify_to_path(prop, path):
  1506. actual = dt.root.props[prop].to_path().path
  1507. assert actual == path, f"'{prop} gives the wrong path"
  1508. def verify_to_path_error_matches(prop, expected_re):
  1509. with pytest.raises(dtlib.DTError) as e:
  1510. dt.root.props[prop].to_path()
  1511. assert re.fullmatch(expected_re, str(e.value)), \
  1512. f"'{prop} gives the wrong error"
  1513. verify_to_path("path", "/target")
  1514. verify_to_path("manualpath", "/target")
  1515. verify_to_path_error_matches(
  1516. "u",
  1517. "expected property 'u' on / in .* to be assigned with either " +
  1518. re.escape("'u = &foo' or 'u = \"/path/to/node\"', not 'u = < 0x1 >;'"))
  1519. verify_to_path_error_matches(
  1520. "missingpath",
  1521. "property 'missingpath' on / in .* points to the non-existent node "
  1522. '"/missing"')
  1523. # Test top-level to_num() and to_nums()
  1524. def verify_raw_to_num(fn, prop, length, signed, expected):
  1525. actual = fn(dt.root.props[prop].value, length, signed)
  1526. assert actual == expected, \
  1527. f"{fn.__name__}(<{prop}>, {length}, {signed}) gives wrong value"
  1528. def verify_raw_to_num_error(fn, data, length, msg):
  1529. with pytest.raises(dtlib.DTError) as e:
  1530. fn(data, length)
  1531. assert str(e.value) == msg, \
  1532. (f"{fn.__name__}() called with data='{data}', length='{length}' "
  1533. "gives the wrong error")
  1534. verify_raw_to_num(dtlib.to_num, "u", None, False, 1)
  1535. verify_raw_to_num(dtlib.to_num, "u", 4, False, 1)
  1536. verify_raw_to_num(dtlib.to_num, "s", None, False, 0xFFFFFFFF)
  1537. verify_raw_to_num(dtlib.to_num, "s", None, True, -1)
  1538. verify_raw_to_num(dtlib.to_nums, "empty", 4, False, [])
  1539. verify_raw_to_num(dtlib.to_nums, "u16", 2, False, [1, 2])
  1540. verify_raw_to_num(dtlib.to_nums, "two_s", 4, False, [0xFFFFFFFF, 0xFFFFFFFE])
  1541. verify_raw_to_num(dtlib.to_nums, "two_s", 4, True, [-1, -2])
  1542. verify_raw_to_num_error(dtlib.to_num, 0, 0, "'0' has type 'int', expected 'bytes'")
  1543. verify_raw_to_num_error(dtlib.to_num, b"", 0, "'length' must be greater than zero, was 0")
  1544. verify_raw_to_num_error(dtlib.to_num, b"foo", 2, "b'foo' is 3 bytes long, expected 2")
  1545. verify_raw_to_num_error(dtlib.to_nums, 0, 0, "'0' has type 'int', expected 'bytes'")
  1546. verify_raw_to_num_error(dtlib.to_nums, b"", 0, "'length' must be greater than zero, was 0")
  1547. verify_raw_to_num_error(dtlib.to_nums, b"foooo", 2, "b'foooo' is 5 bytes long, expected a length that's a a multiple of 2")
  1548. def test_duplicate_labels():
  1549. '''
  1550. It is an error to duplicate labels in most conditions, but there
  1551. are some exceptions where it's OK.
  1552. '''
  1553. verify_error("""
  1554. /dts-v1/;
  1555. / {
  1556. sub1 {
  1557. label: foo {
  1558. };
  1559. };
  1560. sub2 {
  1561. label: bar {
  1562. };
  1563. };
  1564. };
  1565. """,
  1566. "Label 'label' appears on /sub1/foo and on /sub2/bar")
  1567. verify_error("""
  1568. /dts-v1/;
  1569. / {
  1570. sub {
  1571. label: foo {
  1572. };
  1573. };
  1574. };
  1575. / {
  1576. sub {
  1577. label: bar {
  1578. };
  1579. };
  1580. };
  1581. """,
  1582. "Label 'label' appears on /sub/bar and on /sub/foo")
  1583. verify_error("""
  1584. /dts-v1/;
  1585. / {
  1586. foo: a = < 0 >;
  1587. foo: node {
  1588. };
  1589. };
  1590. """,
  1591. "Label 'foo' appears on /node and on property 'a' of node /")
  1592. verify_error("""
  1593. /dts-v1/;
  1594. / {
  1595. foo: a = < 0 >;
  1596. node {
  1597. foo: b = < 0 >;
  1598. };
  1599. };
  1600. """,
  1601. "Label 'foo' appears on property 'a' of node / and on property 'b' of node /node")
  1602. verify_error("""
  1603. /dts-v1/;
  1604. / {
  1605. foo: a = foo: < 0 >;
  1606. };
  1607. """,
  1608. "Label 'foo' appears in the value of property 'a' of node / and on property 'a' of node /")
  1609. # Giving the same label twice for the same node is fine
  1610. verify_parse("""
  1611. /dts-v1/;
  1612. / {
  1613. sub {
  1614. label: foo {
  1615. };
  1616. };
  1617. };
  1618. / {
  1619. sub {
  1620. label: foo {
  1621. };
  1622. };
  1623. };
  1624. """,
  1625. """
  1626. /dts-v1/;
  1627. / {
  1628. sub {
  1629. label: foo {
  1630. };
  1631. };
  1632. };
  1633. """)
  1634. # Duplicate labels are fine if one of the nodes is deleted
  1635. verify_parse("""
  1636. /dts-v1/;
  1637. / {
  1638. label: foo {
  1639. };
  1640. label: bar {
  1641. };
  1642. };
  1643. /delete-node/ &{/bar};
  1644. """,
  1645. """
  1646. /dts-v1/;
  1647. / {
  1648. label: foo {
  1649. };
  1650. };
  1651. """)
  1652. #
  1653. # Test overriding/deleting a property with references
  1654. #
  1655. verify_parse("""
  1656. /dts-v1/;
  1657. / {
  1658. x = &foo, < &foo >;
  1659. y = &foo, < &foo >;
  1660. foo: foo {
  1661. };
  1662. };
  1663. / {
  1664. x = < 1 >;
  1665. /delete-property/ y;
  1666. };
  1667. """,
  1668. """
  1669. /dts-v1/;
  1670. / {
  1671. x = < 0x1 >;
  1672. foo: foo {
  1673. };
  1674. };
  1675. """)
  1676. #
  1677. # Test self-referential node
  1678. #
  1679. verify_parse("""
  1680. /dts-v1/;
  1681. / {
  1682. label: foo {
  1683. x = &{/foo}, &label, < &label >;
  1684. };
  1685. };
  1686. """,
  1687. """
  1688. /dts-v1/;
  1689. / {
  1690. label: foo {
  1691. x = &{/foo}, &label, < &label >;
  1692. phandle = < 0x1 >;
  1693. };
  1694. };
  1695. """)
  1696. #
  1697. # Test /memreserve/
  1698. #
  1699. dt = verify_parse("""
  1700. /dts-v1/;
  1701. l1: l2: /memreserve/ (1 + 1) (2 * 2);
  1702. /memreserve/ 0x100 0x200;
  1703. / {
  1704. };
  1705. """,
  1706. """
  1707. /dts-v1/;
  1708. l1: l2: /memreserve/ 0x0000000000000002 0x0000000000000004;
  1709. /memreserve/ 0x0000000000000100 0x0000000000000200;
  1710. / {
  1711. };
  1712. """)
  1713. expected = [(["l1", "l2"], 2, 4), ([], 0x100, 0x200)]
  1714. assert dt.memreserves == expected
  1715. verify_error_endswith("""
  1716. /dts-v1/;
  1717. foo: / {
  1718. };
  1719. """,
  1720. ":3 (column 6): parse error: expected /memreserve/ after labels at beginning of file")
  1721. def test_reprs():
  1722. '''Test the __repr__() functions.'''
  1723. dts = """
  1724. /dts-v1/;
  1725. / {
  1726. x = < 0 >;
  1727. sub {
  1728. y = < 1 >;
  1729. };
  1730. };
  1731. """
  1732. dt = parse(dts, include_path=("foo", "bar"))
  1733. assert re.fullmatch(r"DT\(filename='.*', include_path=.'foo', 'bar'.\)",
  1734. repr(dt))
  1735. assert re.fullmatch("<Property 'x' at '/' in '.*'>",
  1736. repr(dt.root.props["x"]))
  1737. assert re.fullmatch("<Node /sub in '.*'>",
  1738. repr(dt.root.nodes["sub"]))
  1739. dt = parse(dts, include_path=iter(("foo", "bar")))
  1740. assert re.fullmatch(r"DT\(filename='.*', include_path=.'foo', 'bar'.\)",
  1741. repr(dt))
  1742. def test_names():
  1743. '''Tests for node/property names.'''
  1744. verify_parse(r"""
  1745. /dts-v1/;
  1746. / {
  1747. // A leading \ is accepted but ignored in node/propert names
  1748. \aA0,._+*#?- = &_, &{/aA0,._+@-};
  1749. // Names that overlap with operators and integer literals
  1750. + = [ 00 ];
  1751. * = [ 02 ];
  1752. - = [ 01 ];
  1753. ? = [ 03 ];
  1754. 0 = [ 04 ];
  1755. 0x123 = [ 05 ];
  1756. // Node names are more restrictive than property names.
  1757. _: \aA0,._+@- {
  1758. };
  1759. 0 {
  1760. };
  1761. };
  1762. """,
  1763. """
  1764. /dts-v1/;
  1765. / {
  1766. aA0,._+*#?- = &_, &{/aA0,._+@-};
  1767. + = [ 00 ];
  1768. * = [ 02 ];
  1769. - = [ 01 ];
  1770. ? = [ 03 ];
  1771. 0 = [ 04 ];
  1772. 0x123 = [ 05 ];
  1773. _: aA0,._+@- {
  1774. };
  1775. 0 {
  1776. };
  1777. };
  1778. """)
  1779. verify_error_endswith(r"""
  1780. /dts-v1/;
  1781. / {
  1782. foo@3;
  1783. };
  1784. """,
  1785. ":4 (column 7): parse error: '@' is only allowed in node names")
  1786. verify_error_endswith(r"""
  1787. /dts-v1/;
  1788. / {
  1789. foo@3 = < 0 >;
  1790. };
  1791. """,
  1792. ":4 (column 8): parse error: '@' is only allowed in node names")
  1793. verify_error_endswith(r"""
  1794. /dts-v1/;
  1795. / {
  1796. foo@2@3 {
  1797. };
  1798. };
  1799. """,
  1800. ":4 (column 10): parse error: multiple '@' in node name")
  1801. def test_dense_input():
  1802. '''
  1803. Test that a densely written DTS input round-trips to something
  1804. readable.
  1805. '''
  1806. verify_parse("""
  1807. /dts-v1/;/{l1:l2:foo{l3:l4:bar{l5:x=l6:/bits/8<l7:1 l8:2>l9:,[03],"a";};};};
  1808. """,
  1809. """
  1810. /dts-v1/;
  1811. / {
  1812. l1: l2: foo {
  1813. l3: l4: bar {
  1814. l5: x = l6: [ l7: 01 l8: 02 l9: ], [ 03 ], "a";
  1815. };
  1816. };
  1817. };
  1818. """)
  1819. def test_misc():
  1820. '''Test miscellaneous errors and non-errors.'''
  1821. verify_error_endswith("", ":1 (column 1): parse error: expected '/dts-v1/;' at start of file")
  1822. verify_error_endswith("""
  1823. /dts-v1/;
  1824. """,
  1825. ":2 (column 1): parse error: no root node defined")
  1826. verify_error_endswith("""
  1827. /dts-v1/; /plugin/;
  1828. """,
  1829. ":1 (column 11): parse error: /plugin/ is not supported")
  1830. verify_error_endswith("""
  1831. /dts-v1/;
  1832. / {
  1833. foo: foo {
  1834. };
  1835. };
  1836. // Only one label supported before label references at the top level
  1837. l1: l2: &foo {
  1838. };
  1839. """,
  1840. ":9 (column 5): parse error: expected label reference (&foo)")
  1841. verify_error_endswith("""
  1842. /dts-v1/;
  1843. / {
  1844. foo: {};
  1845. };
  1846. """,
  1847. ":4 (column 14): parse error: expected node or property name")
  1848. # Multiple /dts-v1/ at the start of a file is fine
  1849. verify_parse("""
  1850. /dts-v1/;
  1851. /dts-v1/;
  1852. / {
  1853. };
  1854. """,
  1855. """
  1856. /dts-v1/;
  1857. / {
  1858. };
  1859. """)
  1860. def test_dangling_alias():
  1861. dt = parse('''
  1862. /dts-v1/;
  1863. / {
  1864. aliases { foo = "/missing"; };
  1865. };
  1866. ''', force=True)
  1867. assert dt.get_node('/aliases').props['foo'].to_string() == '/missing'