1# Copyright (c) 2019, Nordic Semiconductor 2# SPDX-License-Identifier: BSD-3-Clause 3 4import contextlib 5import os 6import re 7import tempfile 8from copy import deepcopy 9from typing import Optional 10 11import pytest 12 13from devicetree import dtlib 14 15# Test suite for dtlib.py. 16# 17# Run it using pytest (https://docs.pytest.org/en/stable/usage.html): 18# 19# $ pytest tests/test_dtlib.py 20# 21# Extra options you can pass to pytest for debugging: 22# 23# - to stop on the first failure with shorter traceback output, 24# use '-x --tb=native' 25# - to drop into a debugger on failure, use '--pdb' 26# - to run a particular test function or functions, use 27# '-k test_function_pattern_goes_here' 28 29def uncomment(dts): 30 '''Trim added comments from a DT string.''' 31 32 # remove node comments, including leading empty line 33 # but keep the one before the root node 34 dts = re.sub(r'\n\n[ \t]*/\*.*?\*/\n', '\n', dts, flags=re.DOTALL) 35 dts = re.sub(r'\n/ {\n', r'\n\n/ {\n', dts) 36 37 # remove property comments 38 dts = re.sub(r'[ \t]*/\*.*?\*/\n', '\n', dts) 39 40 return dts 41 42def parse(dts, include_path=(), **kwargs): 43 '''Parse a DTS string 'dts', using the given include path. 44 45 Any kwargs are passed on to DT().''' 46 47 fd, path = tempfile.mkstemp(prefix='pytest-', suffix='.dts') 48 try: 49 os.write(fd, dts.encode('utf-8')) 50 return dtlib.DT(path, include_path, **kwargs) 51 finally: 52 os.close(fd) 53 os.unlink(path) 54 55def verify_parse(dts, expected, include_path=()): 56 '''Like parse(), but also verifies that the parsed DT object's string 57 representation is expected[1:-1]. 58 59 The [1:] is so that the first line can be put on a separate line 60 after triple quotes, as is done below.''' 61 62 dt = parse(dts[1:], include_path) 63 64 actual = uncomment(str(dt)) 65 expected = expected[1:-1] 66 assert actual == expected, f'unexpected round-trip on {dts}' 67 68 return dt 69 70def verify_error(dts, expected_msg): 71 '''Verify that parsing 'dts' results in a DTError with the 72 given error message 'msg'. The message must match exactly.''' 73 74 with dtlib_raises(expected_msg): 75 parse(dts[1:]) 76 77def verify_error_endswith(dts, expected_msg): 78 ''' 79 Like verify_error(), but checks the message ends with 80 'expected_msg' instead of checking for strict equality. 81 ''' 82 83 with dtlib_raises(err_endswith=expected_msg): 84 parse(dts[1:]) 85 86def verify_error_matches(dts, expected_re): 87 ''' 88 Like verify_error(), but checks the message fully matches regular 89 expression 'expected_re' instead of checking for strict equality. 90 ''' 91 92 with dtlib_raises(err_matches=expected_re): 93 parse(dts[1:]) 94 95@contextlib.contextmanager 96def temporary_chdir(dirname): 97 '''A context manager that changes directory to 'dirname'. 98 99 The current working directory is unconditionally returned to its 100 present location after the context manager exits. 101 ''' 102 here = os.getcwd() 103 try: 104 os.chdir(dirname) 105 yield 106 finally: 107 os.chdir(here) 108 109@contextlib.contextmanager 110def dtlib_raises(err: Optional[str] = None, 111 err_endswith: Optional[str] = None, 112 err_matches: Optional[str] = None): 113 '''A context manager for running a block of code that should raise 114 DTError. Exactly one of the arguments 'err', 'err_endswith', 115 and 'err_matches' must be given. The semantics are: 116 117 - err: error message must be exactly this 118 - err_endswith: error message must end with this 119 - err_matches: error message must match this regular expression 120 ''' 121 122 assert sum([bool(err), bool(err_endswith), bool(err_matches)]) == 1 123 124 with pytest.raises(dtlib.DTError) as e: 125 yield 126 127 actual_err = str(e.value) 128 if err: 129 assert actual_err == err 130 elif err_endswith: 131 assert actual_err.endswith(err_endswith) 132 else: 133 assert re.fullmatch(err_matches, actual_err), \ 134 f'actual message:\n{actual_err!r}\n' \ 135 f'does not match:\n{err_matches!r}' 136 137def test_invalid_nodenames(): 138 # Regression test that verifies node names are not matched against 139 # the more permissive set of rules used for property names. 140 141 verify_error_endswith(""" 142/dts-v1/; 143/ { node? {}; }; 144""", 145 "/node?: bad character '?' in node name") 146 147def test_cell_parsing(): 148 '''Miscellaneous properties containing zero or more cells''' 149 150 verify_parse(""" 151/dts-v1/; 152 153/ { 154 a; 155 b = < >; 156 c = [ ]; 157 d = < 10 20 >; 158 e = < 0U 1L 2UL 3LL 4ULL >; 159 f = < 0x10 0x20 >; 160 g = < 010 020 >; 161 h = /bits/ 8 < 0x10 0x20 (-1) >; 162 i = /bits/ 16 < 0x10 0x20 (-1) >; 163 j = /bits/ 32 < 0x10 0x20 (-1) >; 164 k = /bits/ 64 < 0x10 0x20 (-1) >; 165 l = < 'a' 'b' 'c' >; 166 m = [ 00 01 02 03 04 05 06 07 08 09 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 1a 1b ]; 167 n = < 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 >; 168}; 169""", 170""" 171/dts-v1/; 172 173/ { 174 a; 175 b; 176 c; 177 d = < 0xa 0x14 >; 178 e = < 0x0 0x1 0x2 0x3 0x4 >; 179 f = < 0x10 0x20 >; 180 g = < 0x8 0x10 >; 181 h = [ 10 20 FF ]; 182 i = /bits/ 16 < 0x10 0x20 0xffff >; 183 j = < 0x10 0x20 0xffffffff >; 184 k = /bits/ 64 < 0x10 0x20 0xffffffffffffffff >; 185 l = < 0x61 0x62 0x63 >; 186 m = [ 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F 10 11 12 13 14 15 16 17 18 19 1A 187 1B ]; 188 n = < 0x0 0x1 0x2 0x3 0x4 0x5 0x6 0x7 0x8 0x9 0xa 0xb 0xc 0xd 0xe 0xf 0x10 0x11 0x12 0x13 189 0x14 >; 190}; 191""") 192 193 verify_error_endswith(""" 194/dts-v1/; 195 196/ { 197 a = /bits/ 16 < 0x10000 >; 198}; 199""", 200":4 (column 18): parse error: 65536 does not fit in 16 bits") 201 202 verify_error_endswith(""" 203/dts-v1/; 204 205/ { 206 a = < 0x100000000 >; 207}; 208""", 209":4 (column 8): parse error: 4294967296 does not fit in 32 bits") 210 211 verify_error_endswith(""" 212/dts-v1/; 213 214/ { 215 a = /bits/ 128 < 0 >; 216}; 217""", 218":4 (column 13): parse error: expected 8, 16, 32, or 64") 219 220def test_bytes_parsing(): 221 '''Properties with byte array values''' 222 223 verify_parse(""" 224/dts-v1/; 225 226/ { 227 a = [ ]; 228 b = [ 12 34 ]; 229 c = [ 1234 ]; 230}; 231""", 232""" 233/dts-v1/; 234 235/ { 236 a; 237 b = [ 12 34 ]; 238 c = [ 12 34 ]; 239}; 240""") 241 242 verify_error_endswith(""" 243/dts-v1/; 244 245/ { 246 a = [ 123 ]; 247}; 248""", 249":4 (column 10): parse error: expected two-digit byte or ']'") 250 251def test_string_parsing(): 252 '''Properties with string values''' 253 254 verify_parse(r""" 255/dts-v1/; 256 257/ { 258 a = ""; 259 b = "ABC"; 260 c = "\\\"\xab\377\a\b\t\n\v\f\r"; 261}; 262""", 263r""" 264/dts-v1/; 265 266/ { 267 a = ""; 268 b = "ABC"; 269 c = "\\\"\xab\xff\a\b\t\n\v\f\r"; 270}; 271""") 272 273 verify_error_endswith(r""" 274/dts-v1/; 275 276/ { 277 a = "\400"; 278}; 279""", 280":4 (column 6): parse error: octal escape out of range (> 255)") 281 282def test_char_literal_parsing(): 283 '''Properties with character literal values''' 284 285 verify_parse(r""" 286/dts-v1/; 287 288/ { 289 a = < '\'' >; 290 b = < '\x12' >; 291}; 292""", 293""" 294/dts-v1/; 295 296/ { 297 a = < 0x27 >; 298 b = < 0x12 >; 299}; 300""") 301 302 verify_error_endswith(""" 303/dts-v1/; 304 305/ { 306 // Character literals are not allowed at the top level 307 a = 'x'; 308}; 309""", 310":5 (column 6): parse error: malformed value") 311 312 verify_error_endswith(""" 313/dts-v1/; 314 315/ { 316 a = < '' >; 317}; 318""", 319":4 (column 7): parse error: character literals must be length 1") 320 321 verify_error_endswith(""" 322/dts-v1/; 323 324/ { 325 a = < '12' >; 326}; 327""", 328":4 (column 7): parse error: character literals must be length 1") 329 330def test_incbin(tmp_path): 331 '''Test /incbin/, an undocumented feature that allows for 332 binary file inclusion. 333 334 https://github.com/dgibson/dtc/commit/e37ec7d5889fa04047daaa7a4ff55150ed7954d4''' 335 336 open(tmp_path / "tmp_bin", "wb").write(b"\00\01\02\03") 337 338 verify_parse(f""" 339/dts-v1/; 340 341/ {{ 342 a = /incbin/ ("{tmp_path}/tmp_bin"); 343 b = /incbin/ ("{tmp_path}/tmp_bin", 1, 1); 344 c = /incbin/ ("{tmp_path}/tmp_bin", 1, 2); 345}}; 346""", 347""" 348/dts-v1/; 349 350/ { 351 a = [ 00 01 02 03 ]; 352 b = [ 01 ]; 353 c = [ 01 02 ]; 354}; 355""") 356 357 verify_parse(""" 358/dts-v1/; 359 360/ { 361 a = /incbin/ ("tmp_bin"); 362}; 363""", 364""" 365/dts-v1/; 366 367/ { 368 a = [ 00 01 02 03 ]; 369}; 370""", 371 include_path=(tmp_path,)) 372 373 verify_error_endswith(r""" 374/dts-v1/; 375 376/ { 377 a = /incbin/ ("missing"); 378}; 379""", 380":4 (column 25): parse error: 'missing' could not be found") 381 382def test_node_merging(): 383 ''' 384 Labels and properties specified for the same node in different 385 statements should be merged. 386 ''' 387 388 verify_parse(""" 389/dts-v1/; 390 391/ { 392 l1: l2: l1: foo { 393 foo1 = [ 01 ]; 394 l4: l5: bar { 395 bar1 = [ 01 ]; 396 }; 397 }; 398}; 399 400l3: &l1 { 401 foo2 = [ 02 ]; 402 l6: l7: bar { 403 bar2 = [ 02 ]; 404 }; 405}; 406 407&l3 { 408 foo3 = [ 03 ]; 409}; 410 411&{/foo} { 412 foo4 = [ 04 ]; 413}; 414 415&{/foo/bar} { 416 bar3 = [ 03 ]; 417 l8: baz {}; 418}; 419 420/ { 421}; 422 423/ { 424 top = [ 01 ]; 425}; 426""", 427""" 428/dts-v1/; 429 430/ { 431 top = [ 01 ]; 432 l1: l2: l3: foo { 433 foo1 = [ 01 ]; 434 foo2 = [ 02 ]; 435 foo3 = [ 03 ]; 436 foo4 = [ 04 ]; 437 l4: l5: l6: l7: bar { 438 bar1 = [ 01 ]; 439 bar2 = [ 02 ]; 440 bar3 = [ 03 ]; 441 l8: baz { 442 }; 443 }; 444 }; 445}; 446""") 447 448 verify_error_endswith(""" 449/dts-v1/; 450 451/ { 452}; 453 454&missing { 455}; 456""", 457":6 (column 1): parse error: undefined node label 'missing'") 458 459 verify_error_endswith(""" 460/dts-v1/; 461 462/ { 463}; 464 465&{foo} { 466}; 467""", 468":6 (column 1): parse error: node path 'foo' does not start with '/'") 469 470 verify_error_endswith(""" 471/dts-v1/; 472 473/ { 474}; 475 476&{/foo} { 477}; 478""", 479":6 (column 1): parse error: component 'foo' in path '/foo' does not exist") 480 481def test_property_labels(): 482 '''Like nodes, properties can have labels too.''' 483 484 def verify_label2prop(label, expected): 485 actual = dt.label2prop[label].name 486 assert actual == expected, f"label '{label}' mapped to wrong property" 487 488 dt = verify_parse(""" 489/dts-v1/; 490 491/ { 492 a; 493 b; 494 l2: c; 495 l4: l5: l5: l4: d = < 0 >; 496}; 497 498/ { 499 l1: b; 500 l3: c; 501 l6: d; 502}; 503""", 504""" 505/dts-v1/; 506 507/ { 508 a; 509 l1: b; 510 l2: l3: c; 511 l4: l5: l6: d = < 0x0 >; 512}; 513""") 514 515 verify_label2prop("l1", "b") 516 verify_label2prop("l2", "c") 517 verify_label2prop("l3", "c") 518 verify_label2prop("l4", "d") 519 verify_label2prop("l5", "d") 520 verify_label2prop("l6", "d") 521 522def test_property_offset_labels(): 523 ''' 524 It's possible to give labels to data at nonnegative byte offsets 525 within a property value. 526 ''' 527 528 def verify_label2offset(label, expected_prop, expected_offset): 529 actual_prop, actual_offset = dt.label2prop_offset[label] 530 actual_prop = actual_prop.name 531 assert (actual_prop, actual_offset) == \ 532 (expected_prop, expected_offset), \ 533 f"label '{label}' maps to wrong offset or property" 534 535 dt = verify_parse(""" 536/dts-v1/; 537 538/ { 539 a = l01: l02: < l03: &node l04: l05: 2 l06: >, 540 l07: l08: [ l09: 03 l10: l11: 04 l12: l13: ] l14:, "A"; 541 542 b = < 0 > l23: l24:; 543 544 node: node { 545 }; 546}; 547""", 548""" 549/dts-v1/; 550 551/ { 552 a = l01: l02: < l03: &node l04: l05: 0x2 l06: l07: l08: >, 553 [ l09: 03 l10: l11: 04 l12: l13: l14: ], 554 "A"; 555 b = < 0x0 l23: l24: >; 556 node: node { 557 phandle = < 0x1 >; 558 }; 559}; 560""") 561 562 verify_label2offset("l01", "a", 0) 563 verify_label2offset("l02", "a", 0) 564 verify_label2offset("l04", "a", 4) 565 verify_label2offset("l05", "a", 4) 566 verify_label2offset("l06", "a", 8) 567 verify_label2offset("l09", "a", 8) 568 verify_label2offset("l10", "a", 9) 569 570 verify_label2offset("l23", "b", 4) 571 verify_label2offset("l24", "b", 4) 572 573def test_unit_addr(): 574 '''Node unit addresses must be correctly extracted from their names.''' 575 576 def verify_unit_addr(path, expected): 577 node = dt.get_node(path) 578 assert node.unit_addr == expected, \ 579 f"{node!r} has unexpected unit address" 580 581 dt = verify_parse(""" 582/dts-v1/; 583 584/ { 585 no-unit-addr { 586 }; 587 588 unit-addr@ABC { 589 }; 590 591 unit-addr-non-numeric@foo-bar { 592 }; 593}; 594""", 595""" 596/dts-v1/; 597 598/ { 599 no-unit-addr { 600 }; 601 unit-addr@ABC { 602 }; 603 unit-addr-non-numeric@foo-bar { 604 }; 605}; 606""") 607 608 verify_unit_addr("/no-unit-addr", "") 609 verify_unit_addr("/unit-addr@ABC", "ABC") 610 verify_unit_addr("/unit-addr-non-numeric@foo-bar", "foo-bar") 611 612def test_node_path_references(): 613 '''Node phandles may be specified using a reference to the node's path.''' 614 615 verify_parse(""" 616/dts-v1/; 617 618/ { 619 a = &label; 620 b = [ 01 ], &label; 621 c = [ 01 ], &label, <2>; 622 d = &{/abc}; 623 label: abc { 624 e = &label; 625 f = &{/abc}; 626 }; 627}; 628""", 629""" 630/dts-v1/; 631 632/ { 633 a = &label; 634 b = [ 01 ], 635 &label; 636 c = [ 01 ], 637 &label, 638 < 0x2 >; 639 d = &{/abc}; 640 label: abc { 641 e = &label; 642 f = &{/abc}; 643 }; 644}; 645""") 646 647 verify_error(""" 648/dts-v1/; 649 650/ { 651 sub { 652 x = &missing; 653 }; 654}; 655""", 656"/sub: undefined node label 'missing'") 657 658 verify_error(""" 659/dts-v1/; 660 661/ { 662 sub { 663 x = &{/sub/missing}; 664 }; 665}; 666""", 667"/sub: component 'missing' in path '/sub/missing' does not exist") 668 669def test_phandles(): 670 '''Various tests related to phandles.''' 671 672 verify_parse(""" 673/dts-v1/; 674 675/ { 676 x = < &a &{/b} &c >; 677 678 dummy1 { 679 phandle = < 1 >; 680 }; 681 682 dummy2 { 683 phandle = < 3 >; 684 }; 685 686 a: a { 687 }; 688 689 b { 690 }; 691 692 c: c { 693 phandle = < 0xFF >; 694 }; 695}; 696""", 697""" 698/dts-v1/; 699 700/ { 701 x = < &a &{/b} &c >; 702 dummy1 { 703 phandle = < 0x1 >; 704 }; 705 dummy2 { 706 phandle = < 0x3 >; 707 }; 708 a: a { 709 phandle = < 0x2 >; 710 }; 711 b { 712 phandle = < 0x4 >; 713 }; 714 c: c { 715 phandle = < 0xff >; 716 }; 717}; 718""") 719 720 # Check that a node can be assigned a phandle to itself. This just forces a 721 # phandle to be allocated on it. The C tools support this too. 722 verify_parse(""" 723/dts-v1/; 724 725/ { 726 dummy { 727 phandle = < 1 >; 728 }; 729 730 a { 731 foo: phandle = < &{/a} >; 732 }; 733 734 label: b { 735 bar: phandle = < &label >; 736 }; 737}; 738""", 739""" 740/dts-v1/; 741 742/ { 743 dummy { 744 phandle = < 0x1 >; 745 }; 746 a { 747 foo: phandle = < &{/a} >; 748 }; 749 label: b { 750 bar: phandle = < &label >; 751 }; 752}; 753""") 754 755 verify_error(""" 756/dts-v1/; 757 758/ { 759 sub { 760 x = < &missing >; 761 }; 762}; 763""", 764"/sub: undefined node label 'missing'") 765 766 verify_error_endswith(""" 767/dts-v1/; 768 769/ { 770 a: sub { 771 x = /bits/ 16 < &a >; 772 }; 773}; 774""", 775":5 (column 19): parse error: phandle references are only allowed in arrays with 32-bit elements") 776 777 verify_error(""" 778/dts-v1/; 779 780/ { 781 foo { 782 phandle = [ 00 ]; 783 }; 784}; 785""", 786"/foo: bad phandle length (1), expected 4 bytes") 787 788 verify_error(""" 789/dts-v1/; 790 791/ { 792 foo { 793 phandle = < 0 >; 794 }; 795}; 796""", 797"/foo: bad value 0x00000000 for phandle") 798 799 verify_error(""" 800/dts-v1/; 801 802/ { 803 foo { 804 phandle = < (-1) >; 805 }; 806}; 807""", 808"/foo: bad value 0xffffffff for phandle") 809 810 verify_error(""" 811/dts-v1/; 812 813/ { 814 foo { 815 phandle = < 17 >; 816 }; 817 818 bar { 819 phandle = < 17 >; 820 }; 821}; 822""", 823"/bar: duplicated phandle 0x11 (seen before at /foo)") 824 825 verify_error(""" 826/dts-v1/; 827 828/ { 829 foo { 830 phandle = < &{/bar} >; 831 }; 832 833 bar { 834 }; 835}; 836""", 837"/foo: phandle refers to another node") 838 839def test_phandle2node(): 840 '''Test the phandle2node dict in a dt instance.''' 841 842 def verify_phandle2node(prop, offset, expected_name): 843 phandle = dtlib.to_num(dt.root.props[prop].value[offset:offset + 4]) 844 actual_name = dt.phandle2node[phandle].name 845 846 assert actual_name == expected_name, \ 847 f"'{prop}' is a phandle for the wrong thing" 848 849 dt = parse(""" 850/dts-v1/; 851 852/ { 853 phandle_ = < &{/node1} 0 1 >; 854 phandles = < 0 &{/node2} 1 &{/node3} >; 855 856 node1 { 857 phandle = < 123 >; 858 }; 859 860 node2 { 861 }; 862 863 node3 { 864 }; 865}; 866""") 867 868 verify_phandle2node("phandle_", 0, "node1") 869 verify_phandle2node("phandles", 4, "node2") 870 verify_phandle2node("phandles", 12, "node3") 871 872def test_mixed_assign(): 873 '''Test mixed value type assignments''' 874 875 verify_parse(""" 876/dts-v1/; 877 878/ { 879 x = /bits/ 8 < 0xFF 0xFF >, 880 &abc, 881 < 0xFF &abc 0xFF &abc >, 882 &abc, 883 [ FF FF ], 884 "abc"; 885 886 abc: abc { 887 }; 888}; 889""", 890""" 891/dts-v1/; 892 893/ { 894 x = [ FF FF ], 895 &abc, 896 < 0xff &abc 0xff &abc >, 897 &abc, 898 [ FF FF ], 899 "abc"; 900 abc: abc { 901 phandle = < 0x1 >; 902 }; 903}; 904""") 905 906def test_deletion(): 907 '''Properties and nodes may be deleted from the tree.''' 908 909 # Test property deletion 910 911 verify_parse(""" 912/dts-v1/; 913 914/ { 915 keep = < 1 >; 916 delete = < &sub >, ⊂ 917 /delete-property/ missing; 918 /delete-property/ delete; 919 sub: sub { 920 y = < &sub >, ⊂ 921 }; 922}; 923 924&sub { 925 /delete-property/ y; 926}; 927""", 928""" 929/dts-v1/; 930 931/ { 932 keep = < 0x1 >; 933 sub: sub { 934 }; 935}; 936""") 937 938 # Test node deletion 939 940 verify_parse(""" 941/dts-v1/; 942 943/ { 944 x = "foo"; 945 sub0 { 946 x = "bar"; 947 }; 948}; 949 950/delete-node/ &{/}; 951 952/ { 953 sub1 { 954 x = < 1 >; 955 sub2 { 956 x = < &sub >, ⊂ 957 }; 958 /delete-node/ sub2; 959 }; 960 961 sub3: sub3 { 962 x = < &sub >, ⊂ 963 }; 964 965 sub4 { 966 x = < &sub >, ⊂ 967 }; 968}; 969 970/delete-node/ &sub3; 971/delete-node/ &{/sub4}; 972""", 973""" 974/dts-v1/; 975 976/ { 977 sub1 { 978 x = < 0x1 >; 979 }; 980}; 981""") 982 983 verify_parse(""" 984/dts-v1/; 985 986/ { 987 x: x = < &sub >, ⊂ 988 989 sub1 { 990 x = < &sub >, ⊂ 991 }; 992 sub2: sub2 { 993 x = < &sub >, ⊂ 994 }; 995}; 996 997/delete-node/ &{/}; 998""", 999""" 1000/dts-v1/; 1001 1002/ { 1003}; 1004""") 1005 1006 verify_error_endswith(""" 1007/dts-v1/; 1008 1009/ { 1010}; 1011 1012/delete-node/ &missing; 1013""", 1014":6 (column 15): parse error: undefined node label 'missing'") 1015 1016 verify_error_endswith(""" 1017/dts-v1/; 1018 1019/delete-node/ { 1020""", 1021":3 (column 15): parse error: expected label (&foo) or path (&{/foo/bar}) reference") 1022 1023def test_include_curdir(tmp_path): 1024 '''Verify that /include/ (which is handled in the lexer) searches the 1025 current directory''' 1026 1027 with temporary_chdir(tmp_path): 1028 with open("same-dir-1", "w") as f: 1029 f.write(""" 1030 x = [ 00 ]; 1031 /include/ "same-dir-2" 1032""") 1033 with open("same-dir-2", "w") as f: 1034 f.write(""" 1035 y = [ 01 ]; 1036 /include/ "same-dir-3" 1037""") 1038 with open("same-dir-3", "w") as f: 1039 f.write(""" 1040 z = [ 02 ]; 1041""") 1042 with open("test.dts", "w") as f: 1043 f.write(""" 1044/dts-v1/; 1045 1046/ { 1047 /include/ "same-dir-1" 1048}; 1049""") 1050 dt = dtlib.DT("test.dts") 1051 assert uncomment(str(dt)) == """ 1052/dts-v1/; 1053 1054/ { 1055 x = [ 00 ]; 1056 y = [ 01 ]; 1057 z = [ 02 ]; 1058}; 1059"""[1:-1] 1060 1061def test_include_is_lexical(tmp_path): 1062 '''/include/ is done in the lexer, which means that property 1063 definitions can span multiple included files in different 1064 directories.''' 1065 1066 with open(tmp_path / "tmp2.dts", "w") as f: 1067 f.write(""" 1068/dts-v1/; 1069/ { 1070""") 1071 with open(tmp_path / "tmp3.dts", "w") as f: 1072 f.write(""" 1073 x = <1>; 1074""") 1075 1076 subdir_1 = tmp_path / "subdir-1" 1077 subdir_1.mkdir() 1078 with open(subdir_1 / "via-include-path-1", "w") as f: 1079 f.write(""" 1080 = /include/ "via-include-path-2" 1081""") 1082 1083 subdir_2 = tmp_path / "subdir-2" 1084 subdir_2.mkdir() 1085 with open(subdir_2 / "via-include-path-2", "w") as f: 1086 f.write(""" 1087 <2>; 1088}; 1089""") 1090 1091 with open(tmp_path / "test.dts", "w") as test_dts: 1092 test_dts.write(""" 1093/include/ "tmp2.dts" 1094/include/ "tmp3.dts" 1095y /include/ "via-include-path-1" 1096""") 1097 1098 with temporary_chdir(tmp_path): 1099 dt = dtlib.DT("test.dts", include_path=(subdir_1, subdir_2)) 1100 expected_dt = """ 1101/dts-v1/; 1102 1103/ { 1104 x = < 0x1 >; 1105 y = < 0x2 >; 1106}; 1107"""[1:-1] 1108 assert uncomment(str(dt)) == expected_dt 1109 1110def test_include_misc(tmp_path): 1111 '''Miscellaneous /include/ tests.''' 1112 1113 # Missing includes should error out. 1114 1115 verify_error_endswith(""" 1116/include/ "missing" 1117""", 1118":1 (column 1): parse error: 'missing' could not be found") 1119 1120 # Verify that an error in an included file points to the right location 1121 1122 with temporary_chdir(tmp_path): 1123 with open("tmp2.dts", "w") as f: 1124 f.write("""\ 1125 1126 1127 x 1128""") 1129 with open("tmp.dts", "w") as f: 1130 f.write(""" 1131 1132 1133/include/ "tmp2.dts" 1134""") 1135 with dtlib_raises("tmp2.dts:3 (column 3): parse error: " 1136 "expected '/dts-v1/;' at start of file"): 1137 dtlib.DT("tmp.dts") 1138 1139def test_include_recursion(tmp_path): 1140 '''Test recursive /include/ detection''' 1141 1142 with temporary_chdir(tmp_path): 1143 with open("tmp2.dts", "w") as f: 1144 f.write('/include/ "tmp3.dts"\n') 1145 with open("tmp3.dts", "w") as f: 1146 f.write('/include/ "tmp.dts"\n') 1147 1148 with open("tmp.dts", "w") as f: 1149 f.write('/include/ "tmp2.dts"\n') 1150 expected_err = """\ 1151tmp3.dts:1 (column 1): parse error: recursive /include/: 1152tmp.dts:1 -> 1153tmp2.dts:1 -> 1154tmp3.dts:1 -> 1155tmp.dts""" 1156 with dtlib_raises(expected_err): 1157 dtlib.DT("tmp.dts") 1158 1159 with open("tmp.dts", "w") as f: 1160 f.write('/include/ "tmp.dts"\n') 1161 expected_err = """\ 1162tmp.dts:1 (column 1): parse error: recursive /include/: 1163tmp.dts:1 -> 1164tmp.dts""" 1165 with dtlib_raises(expected_err): 1166 dtlib.DT("tmp.dts") 1167 1168def test_omit_if_no_ref(): 1169 '''The /omit-if-no-ref/ marker is a bit of undocumented 1170 dtc magic that removes a node from the tree if it isn't 1171 referred to elsewhere. 1172 1173 https://elinux.org/Device_Tree_Source_Undocumented 1174 ''' 1175 1176 verify_parse(""" 1177/dts-v1/; 1178 1179/ { 1180 x = < &{/referenced} >, &referenced2; 1181 1182 /omit-if-no-ref/ referenced { 1183 }; 1184 1185 referenced2: referenced2 { 1186 }; 1187 1188 /omit-if-no-ref/ unreferenced { 1189 }; 1190 1191 l1: /omit-if-no-ref/ unreferenced2 { 1192 }; 1193 1194 /omit-if-no-ref/ l2: unreferenced3 { 1195 }; 1196 1197 unreferenced4: unreferenced4 { 1198 }; 1199 1200 unreferenced5 { 1201 }; 1202}; 1203 1204/omit-if-no-ref/ &referenced2; 1205/omit-if-no-ref/ &unreferenced4; 1206/omit-if-no-ref/ &{/unreferenced5}; 1207""", 1208""" 1209/dts-v1/; 1210 1211/ { 1212 x = < &{/referenced} >, 1213 &referenced2; 1214 referenced { 1215 phandle = < 0x1 >; 1216 }; 1217 referenced2: referenced2 { 1218 }; 1219}; 1220""") 1221 1222 verify_error_endswith(""" 1223/dts-v1/; 1224 1225/ { 1226 /omit-if-no-ref/ x = ""; 1227}; 1228""", 1229":4 (column 21): parse error: /omit-if-no-ref/ can only be used on nodes") 1230 1231 verify_error_endswith(""" 1232/dts-v1/; 1233 1234/ { 1235 /omit-if-no-ref/ x; 1236}; 1237""", 1238":4 (column 20): parse error: /omit-if-no-ref/ can only be used on nodes") 1239 1240 verify_error_endswith(""" 1241/dts-v1/; 1242 1243/ { 1244 /omit-if-no-ref/ { 1245 }; 1246}; 1247""", 1248":4 (column 19): parse error: expected node or property name") 1249 1250 verify_error_endswith(""" 1251/dts-v1/; 1252 1253/ { 1254 /omit-if-no-ref/ = < 0 >; 1255}; 1256""", 1257":4 (column 19): parse error: expected node or property name") 1258 1259 verify_error_endswith(""" 1260/dts-v1/; 1261 1262/ { 1263}; 1264 1265/omit-if-no-ref/ &missing; 1266""", 1267":6 (column 18): parse error: undefined node label 'missing'") 1268 1269 verify_error_endswith(""" 1270/dts-v1/; 1271 1272/omit-if-no-ref/ { 1273""", 1274":3 (column 18): parse error: expected label (&foo) or path (&{/foo/bar}) reference") 1275 1276def test_expr(): 1277 '''Property values may contain expressions.''' 1278 1279 verify_parse(""" 1280/dts-v1/; 1281 1282/ { 1283 ter1 = < (0 ? 1 : 0 ? 2 : 3) >; 1284 ter2 = < (0 ? 1 : 1 ? 2 : 3) >; 1285 ter3 = < (1 ? 1 : 0 ? 2 : 3) >; 1286 ter4 = < (1 ? 1 : 1 ? 2 : 3) >; 1287 or1 = < (0 || 0) >; 1288 or2 = < (0 || 1) >; 1289 or3 = < (1 || 0) >; 1290 or4 = < (1 || 1) >; 1291 and1 = < (0 && 0) >; 1292 and2 = < (0 && 1) >; 1293 and3 = < (1 && 0) >; 1294 and4 = < (1 && 1) >; 1295 bitor = < (1 | 2) >; 1296 bitxor = < (7 ^ 2) >; 1297 bitand = < (3 & 6) >; 1298 eq1 = < (1 == 0) >; 1299 eq2 = < (1 == 1) >; 1300 neq1 = < (1 != 0) >; 1301 neq2 = < (1 != 1) >; 1302 lt1 = < (1 < 2) >; 1303 lt2 = < (2 < 2) >; 1304 lt3 = < (3 < 2) >; 1305 lteq1 = < (1 <= 2) >; 1306 lteq2 = < (2 <= 2) >; 1307 lteq3 = < (3 <= 2) >; 1308 gt1 = < (1 > 2) >; 1309 gt2 = < (2 > 2) >; 1310 gt3 = < (3 > 2) >; 1311 gteq1 = < (1 >= 2) >; 1312 gteq2 = < (2 >= 2) >; 1313 gteq3 = < (3 >= 2) >; 1314 lshift = < (2 << 3) >; 1315 rshift = < (16 >> 3) >; 1316 add = < (3 + 4) >; 1317 sub = < (7 - 4) >; 1318 mul = < (3 * 4) >; 1319 div = < (11 / 3) >; 1320 mod = < (11 % 3) >; 1321 unary_minus = < (-3) >; 1322 bitnot = < (~1) >; 1323 not0 = < (!-1) >; 1324 not1 = < (!0) >; 1325 not2 = < (!1) >; 1326 not3 = < (!2) >; 1327 nest = < (((--3) + (-2)) * (--(-2))) >; 1328 char_lits = < ('a' + 'b') >; 1329}; 1330""", 1331""" 1332/dts-v1/; 1333 1334/ { 1335 ter1 = < 0x3 >; 1336 ter2 = < 0x2 >; 1337 ter3 = < 0x1 >; 1338 ter4 = < 0x1 >; 1339 or1 = < 0x0 >; 1340 or2 = < 0x1 >; 1341 or3 = < 0x1 >; 1342 or4 = < 0x1 >; 1343 and1 = < 0x0 >; 1344 and2 = < 0x0 >; 1345 and3 = < 0x0 >; 1346 and4 = < 0x1 >; 1347 bitor = < 0x3 >; 1348 bitxor = < 0x5 >; 1349 bitand = < 0x2 >; 1350 eq1 = < 0x0 >; 1351 eq2 = < 0x1 >; 1352 neq1 = < 0x1 >; 1353 neq2 = < 0x0 >; 1354 lt1 = < 0x1 >; 1355 lt2 = < 0x0 >; 1356 lt3 = < 0x0 >; 1357 lteq1 = < 0x1 >; 1358 lteq2 = < 0x1 >; 1359 lteq3 = < 0x0 >; 1360 gt1 = < 0x0 >; 1361 gt2 = < 0x0 >; 1362 gt3 = < 0x1 >; 1363 gteq1 = < 0x0 >; 1364 gteq2 = < 0x1 >; 1365 gteq3 = < 0x1 >; 1366 lshift = < 0x10 >; 1367 rshift = < 0x2 >; 1368 add = < 0x7 >; 1369 sub = < 0x3 >; 1370 mul = < 0xc >; 1371 div = < 0x3 >; 1372 mod = < 0x2 >; 1373 unary_minus = < 0xfffffffd >; 1374 bitnot = < 0xfffffffe >; 1375 not0 = < 0x0 >; 1376 not1 = < 0x1 >; 1377 not2 = < 0x0 >; 1378 not3 = < 0x0 >; 1379 nest = < 0xfffffffe >; 1380 char_lits = < 0xc3 >; 1381}; 1382""") 1383 1384 verify_error_endswith(""" 1385/dts-v1/; 1386 1387/ { 1388 a = < (1/(-1 + 1)) >; 1389}; 1390""", 1391":4 (column 18): parse error: division by zero") 1392 1393 verify_error_endswith(""" 1394/dts-v1/; 1395 1396/ { 1397 a = < (1%0) >; 1398}; 1399""", 1400":4 (column 11): parse error: division by zero") 1401 1402def test_comment_removal(): 1403 '''Comments should be removed when round-tripped to a str.''' 1404 1405 verify_parse(""" 1406/**//dts-v1//**/;// 1407// 1408// foo 1409/ /**/{// foo 1410x/**/=/* 1411foo 1412*/</**/1/***/>/****/;/**/}/*/**/; 1413""", 1414""" 1415/dts-v1/; 1416 1417/ { 1418 x = < 0x1 >; 1419}; 1420""") 1421 1422def verify_path_is(path, node_name, dt): 1423 '''Verify 'node.name' matches 'node_name' in 'dt'.''' 1424 1425 try: 1426 node = dt.get_node(path) 1427 assert node.name == node_name, f'unexpected path {path}' 1428 except dtlib.DTError: 1429 assert False, f'no node found for path {path}' 1430 1431def verify_path_error(path, msg, dt): 1432 '''Verify that an attempt to get node 'path' from 'dt' raises 1433 a DTError whose str is 'msg'.''' 1434 1435 with dtlib_raises(msg): 1436 dt.get_node(path) 1437 1438def test_get_node(): 1439 '''Test DT.get_node().''' 1440 1441 dt = parse(""" 1442/dts-v1/; 1443 1444/ { 1445 foo { 1446 bar { 1447 }; 1448 }; 1449 1450 baz { 1451 }; 1452}; 1453""") 1454 1455 verify_path_is("/", "/", dt) 1456 verify_path_is("//", "/", dt) 1457 verify_path_is("///", "/", dt) 1458 verify_path_is("/foo", "foo", dt) 1459 verify_path_is("//foo", "foo", dt) 1460 verify_path_is("///foo", "foo", dt) 1461 verify_path_is("/foo/bar", "bar", dt) 1462 verify_path_is("//foo//bar", "bar", dt) 1463 verify_path_is("///foo///bar", "bar", dt) 1464 verify_path_is("/baz", "baz", dt) 1465 1466 verify_path_error( 1467 "", 1468 "no alias '' found -- did you forget the leading '/' in the node path?", 1469 dt) 1470 verify_path_error( 1471 "missing", 1472 "no alias 'missing' found -- did you forget the leading '/' in the node path?", 1473 dt) 1474 verify_path_error( 1475 "/missing", 1476 "component 'missing' in path '/missing' does not exist", 1477 dt) 1478 verify_path_error( 1479 "/foo/missing", 1480 "component 'missing' in path '/foo/missing' does not exist", 1481 dt) 1482 1483 def verify_path_exists(path): 1484 assert dt.has_node(path), f"path '{path}' does not exist" 1485 1486 def verify_path_missing(path): 1487 assert not dt.has_node(path), f"path '{path}' exists" 1488 1489 verify_path_exists("/") 1490 verify_path_exists("/foo") 1491 verify_path_exists("/foo/bar") 1492 1493 verify_path_missing("/missing") 1494 verify_path_missing("/foo/missing") 1495 1496def test_aliases(): 1497 '''Test /aliases''' 1498 1499 dt = parse(""" 1500/dts-v1/; 1501 1502/ { 1503 aliases { 1504 alias1 = &l1; 1505 alias2 = &l2; 1506 alias3 = &{/sub/node3}; 1507 alias4 = &{/node4}; 1508 }; 1509 1510 l1: node1 { 1511 }; 1512 1513 l2: node2 { 1514 }; 1515 1516 sub { 1517 node3 { 1518 }; 1519 }; 1520 1521 node4 { 1522 node5 { 1523 }; 1524 }; 1525}; 1526""") 1527 1528 def verify_alias_target(alias, node_name): 1529 verify_path_is(alias, node_name, dt) 1530 assert alias in dt.alias2node 1531 assert dt.alias2node[alias].name == node_name, f"bad result for {alias}" 1532 1533 verify_alias_target("alias1", "node1") 1534 verify_alias_target("alias2", "node2") 1535 verify_alias_target("alias3", "node3") 1536 verify_path_is("alias4/node5", "node5", dt) 1537 1538 verify_path_error( 1539 "alias4/node5/node6", 1540 "component 'node6' in path 'alias4/node5/node6' does not exist", 1541 dt) 1542 1543 verify_error_matches(""" 1544/dts-v1/; 1545 1546/ { 1547 aliases { 1548 a = [ 00 ]; 1549 }; 1550}; 1551""", 1552"expected property 'a' on /aliases in .*" + 1553re.escape("to be assigned with either 'a = &foo' or 'a = \"/path/to/node\"', not 'a = [ 00 ];'")) 1554 1555 verify_error_matches(r""" 1556/dts-v1/; 1557 1558/ { 1559 aliases { 1560 a = "\xFF"; 1561 }; 1562}; 1563""", 1564re.escape(r"value of property 'a' (b'\xff\x00') on /aliases in ") + 1565".* is not valid UTF-8") 1566 1567 verify_error(""" 1568/dts-v1/; 1569 1570/ { 1571 aliases { 1572 A = "/aliases"; 1573 }; 1574}; 1575""", 1576"/aliases: alias property name 'A' should include only characters from [0-9a-z-]") 1577 1578 verify_error_matches(r""" 1579/dts-v1/; 1580 1581/ { 1582 aliases { 1583 a = "/missing"; 1584 }; 1585}; 1586""", 1587"property 'a' on /aliases in .* points to the non-existent node \"/missing\"") 1588 1589def test_prop_type(): 1590 '''Test Property.type''' 1591 1592 def verify_type(prop, expected): 1593 actual = dt.root.props[prop].type 1594 assert actual == expected, f'{prop} has wrong type' 1595 1596 dt = parse(""" 1597/dts-v1/; 1598 1599/ { 1600 empty; 1601 bytes1 = [ ]; 1602 bytes2 = [ 01 ]; 1603 bytes3 = [ 01 02 ]; 1604 bytes4 = foo: [ 01 bar: 02 ]; 1605 bytes5 = /bits/ 8 < 1 2 3 >; 1606 num = < 1 >; 1607 nums1 = < >; 1608 nums2 = < >, < >; 1609 nums3 = < 1 2 >; 1610 nums4 = < 1 2 >, < 3 >, < 4 >; 1611 string = "foo"; 1612 strings = "foo", "bar"; 1613 path1 = &node; 1614 path2 = &{/node}; 1615 phandle1 = < &node >; 1616 phandle2 = < &{/node} >; 1617 phandles1 = < &node &node >; 1618 phandles2 = < &node >, < &node >; 1619 phandle-and-nums-1 = < &node 1 >; 1620 phandle-and-nums-2 = < &node 1 2 &node 3 4 >; 1621 phandle-and-nums-3 = < &node 1 2 >, < &node 3 4 >; 1622 compound1 = < 1 >, [ 02 ]; 1623 compound2 = "foo", < >; 1624 1625 node: node { 1626 }; 1627}; 1628""") 1629 1630 verify_type("empty", dtlib.Type.EMPTY) 1631 verify_type("bytes1", dtlib.Type.BYTES) 1632 verify_type("bytes2", dtlib.Type.BYTES) 1633 verify_type("bytes3", dtlib.Type.BYTES) 1634 verify_type("bytes4", dtlib.Type.BYTES) 1635 verify_type("bytes5", dtlib.Type.BYTES) 1636 verify_type("num", dtlib.Type.NUM) 1637 verify_type("nums1", dtlib.Type.NUMS) 1638 verify_type("nums2", dtlib.Type.NUMS) 1639 verify_type("nums3", dtlib.Type.NUMS) 1640 verify_type("nums4", dtlib.Type.NUMS) 1641 verify_type("string", dtlib.Type.STRING) 1642 verify_type("strings", dtlib.Type.STRINGS) 1643 verify_type("phandle1", dtlib.Type.PHANDLE) 1644 verify_type("phandle2", dtlib.Type.PHANDLE) 1645 verify_type("phandles1", dtlib.Type.PHANDLES) 1646 verify_type("phandles2", dtlib.Type.PHANDLES) 1647 verify_type("phandle-and-nums-1", dtlib.Type.PHANDLES_AND_NUMS) 1648 verify_type("phandle-and-nums-2", dtlib.Type.PHANDLES_AND_NUMS) 1649 verify_type("phandle-and-nums-3", dtlib.Type.PHANDLES_AND_NUMS) 1650 verify_type("path1", dtlib.Type.PATH) 1651 verify_type("path2", dtlib.Type.PATH) 1652 verify_type("compound1", dtlib.Type.COMPOUND) 1653 verify_type("compound2", dtlib.Type.COMPOUND) 1654 1655def test_prop_type_casting(): 1656 '''Test Property.to_{num,nums,string,strings,node}()''' 1657 1658 dt = parse(r""" 1659/dts-v1/; 1660 1661/ { 1662 u = < 1 >; 1663 s = < 0xFFFFFFFF >; 1664 u8 = /bits/ 8 < 1 >; 1665 u16 = /bits/ 16 < 1 2 >; 1666 u64 = /bits/ 64 < 1 >; 1667 bytes = [ 01 02 03 ]; 1668 empty; 1669 zero = < >; 1670 two_u = < 1 2 >; 1671 two_s = < 0xFFFFFFFF 0xFFFFFFFE >; 1672 three_u = < 1 2 3 >; 1673 three_u_split = < 1 >, < 2 >, < 3 >; 1674 empty_string = ""; 1675 string = "foo\tbar baz"; 1676 invalid_string = "\xff"; 1677 strings = "foo", "bar", "baz"; 1678 invalid_strings = "foo", "\xff", "bar"; 1679 ref = <&{/target}>; 1680 refs = <&{/target} &{/target2}>; 1681 refs2 = <&{/target}>, <&{/target2}>; 1682 path = &{/target}; 1683 manualpath = "/target"; 1684 missingpath = "/missing"; 1685 1686 target { 1687 phandle = < 100 >; 1688 }; 1689 1690 target2 { 1691 }; 1692}; 1693""") 1694 1695 # Test Property.to_num() 1696 1697 def verify_to_num(prop, signed, expected): 1698 signed_str = "a signed" if signed else "an unsigned" 1699 actual = dt.root.props[prop].to_num(signed) 1700 assert actual == expected, \ 1701 f"{prop} has bad {signed_str} numeric value" 1702 1703 def verify_to_num_error_matches(prop, expected_re): 1704 with dtlib_raises(err_matches=expected_re): 1705 dt.root.props[prop].to_num() 1706 1707 verify_to_num("u", False, 1) 1708 verify_to_num("u", True, 1) 1709 verify_to_num("s", False, 0xFFFFFFFF) 1710 verify_to_num("s", True, -1) 1711 1712 verify_to_num_error_matches( 1713 "two_u", 1714 "expected property 'two_u' on / in .* to be assigned with " + 1715 re.escape("'two_u = < (number) >;', not 'two_u = < 0x1 0x2 >;'")) 1716 verify_to_num_error_matches( 1717 "u8", 1718 "expected property 'u8' on / in .* to be assigned with " + 1719 re.escape("'u8 = < (number) >;', not 'u8 = [ 01 ];'")) 1720 verify_to_num_error_matches( 1721 "u16", 1722 "expected property 'u16' on / in .* to be assigned with " + 1723 re.escape("'u16 = < (number) >;', not 'u16 = /bits/ 16 < 0x1 0x2 >;'")) 1724 verify_to_num_error_matches( 1725 "u64", 1726 "expected property 'u64' on / in .* to be assigned with " + 1727 re.escape("'u64 = < (number) >;', not 'u64 = /bits/ 64 < 0x1 >;'")) 1728 verify_to_num_error_matches( 1729 "string", 1730 "expected property 'string' on / in .* to be assigned with " + 1731 re.escape("'string = < (number) >;', not 'string = \"foo\\tbar baz\";'")) 1732 1733 # Test Property.to_nums() 1734 1735 def verify_to_nums(prop, signed, expected): 1736 signed_str = "signed" if signed else "unsigned" 1737 actual = dt.root.props[prop].to_nums(signed) 1738 assert actual == expected, \ 1739 f"'{prop}' gives the wrong {signed_str} numbers" 1740 1741 def verify_to_nums_error_matches(prop, expected_re): 1742 with dtlib_raises(err_matches=expected_re): 1743 dt.root.props[prop].to_nums() 1744 1745 verify_to_nums("zero", False, []) 1746 verify_to_nums("u", False, [1]) 1747 verify_to_nums("two_u", False, [1, 2]) 1748 verify_to_nums("two_u", True, [1, 2]) 1749 verify_to_nums("two_s", False, [0xFFFFFFFF, 0xFFFFFFFE]) 1750 verify_to_nums("two_s", True, [-1, -2]) 1751 verify_to_nums("three_u", False, [1, 2, 3]) 1752 verify_to_nums("three_u_split", False, [1, 2, 3]) 1753 1754 verify_to_nums_error_matches( 1755 "empty", 1756 "expected property 'empty' on / in .* to be assigned with " + 1757 re.escape("'empty = < (number) (number) ... >;', not 'empty;'")) 1758 verify_to_nums_error_matches( 1759 "string", 1760 "expected property 'string' on / in .* to be assigned with " + 1761 re.escape("'string = < (number) (number) ... >;', ") + 1762 re.escape("not 'string = \"foo\\tbar baz\";'")) 1763 1764 # Test Property.to_bytes() 1765 1766 def verify_to_bytes(prop, expected): 1767 actual = dt.root.props[prop].to_bytes() 1768 assert actual == expected, f"'{prop}' gives the wrong bytes" 1769 1770 def verify_to_bytes_error_matches(prop, expected_re): 1771 with dtlib_raises(err_matches=expected_re): 1772 dt.root.props[prop].to_bytes() 1773 1774 verify_to_bytes("u8", b"\x01") 1775 verify_to_bytes("bytes", b"\x01\x02\x03") 1776 1777 verify_to_bytes_error_matches( 1778 "u16", 1779 "expected property 'u16' on / in .* to be assigned with " + 1780 re.escape("'u16 = [ (byte) (byte) ... ];', ") + 1781 re.escape("not 'u16 = /bits/ 16 < 0x1 0x2 >;'")) 1782 verify_to_bytes_error_matches( 1783 "empty", 1784 "expected property 'empty' on / in .* to be assigned with " + 1785 re.escape("'empty = [ (byte) (byte) ... ];', not 'empty;'")) 1786 1787 # Test Property.to_string() 1788 1789 def verify_to_string(prop, expected): 1790 actual = dt.root.props[prop].to_string() 1791 assert actual == expected, f"'{prop}' to_string gives the wrong string" 1792 1793 def verify_to_string_error_matches(prop, expected_re): 1794 with dtlib_raises(err_matches=expected_re): 1795 dt.root.props[prop].to_string() 1796 1797 verify_to_string("empty_string", "") 1798 verify_to_string("string", "foo\tbar baz") 1799 1800 verify_to_string_error_matches( 1801 "u", 1802 "expected property 'u' on / in .* to be assigned with " + 1803 re.escape("'u = \"string\";', not 'u = < 0x1 >;'")) 1804 verify_to_string_error_matches( 1805 "strings", 1806 "expected property 'strings' on / in .* to be assigned with " + 1807 re.escape("'strings = \"string\";', ")+ 1808 "not 'strings = \"foo\",\\s*\"bar\",\\s*\"baz\";'") 1809 verify_to_string_error_matches( 1810 "invalid_string", 1811 re.escape(r"value of property 'invalid_string' (b'\xff\x00') on / ") + 1812 "in .* is not valid UTF-8") 1813 1814 # Test Property.to_strings() 1815 1816 def verify_to_strings(prop, expected): 1817 actual = dt.root.props[prop].to_strings() 1818 assert actual == expected, f"'{prop}' to_strings gives the wrong value" 1819 1820 def verify_to_strings_error_matches(prop, expected_re): 1821 with dtlib_raises(err_matches=expected_re): 1822 dt.root.props[prop].to_strings() 1823 1824 verify_to_strings("empty_string", [""]) 1825 verify_to_strings("string", ["foo\tbar baz"]) 1826 verify_to_strings("strings", ["foo", "bar", "baz"]) 1827 1828 verify_to_strings_error_matches( 1829 "u", 1830 "expected property 'u' on / in .* to be assigned with " + 1831 re.escape("'u = \"string\", \"string\", ... ;', not 'u = < 0x1 >;'")) 1832 verify_to_strings_error_matches( 1833 "invalid_strings", 1834 "value of property 'invalid_strings' " + 1835 re.escape(r"(b'foo\x00\xff\x00bar\x00') on / in ") + 1836 ".* is not valid UTF-8") 1837 1838 # Test Property.to_node() 1839 1840 def verify_to_node(prop, path): 1841 actual = dt.root.props[prop].to_node().path 1842 assert actual == path, f"'{prop}' points at wrong path" 1843 1844 def verify_to_node_error_matches(prop, expected_re): 1845 with dtlib_raises(err_matches=expected_re): 1846 dt.root.props[prop].to_node() 1847 1848 verify_to_node("ref", "/target") 1849 1850 verify_to_node_error_matches( 1851 "u", 1852 "expected property 'u' on / in .* to be assigned with " + 1853 re.escape("'u = < &foo >;', not 'u = < 0x1 >;'")) 1854 verify_to_node_error_matches( 1855 "string", 1856 "expected property 'string' on / in .* to be assigned with " + 1857 re.escape("'string = < &foo >;', not 'string = \"foo\\tbar baz\";'")) 1858 1859 # Test Property.to_nodes() 1860 1861 def verify_to_nodes(prop, paths): 1862 actual = [node.path for node in dt.root.props[prop].to_nodes()] 1863 assert actual == paths, f"'{prop} gives wrong node paths" 1864 1865 def verify_to_nodes_error_matches(prop, expected_re): 1866 with dtlib_raises(err_matches=expected_re): 1867 dt.root.props[prop].to_nodes() 1868 1869 verify_to_nodes("zero", []) 1870 verify_to_nodes("ref", ["/target"]) 1871 verify_to_nodes("refs", ["/target", "/target2"]) 1872 verify_to_nodes("refs2", ["/target", "/target2"]) 1873 1874 verify_to_nodes_error_matches( 1875 "u", 1876 "expected property 'u' on / in .* to be assigned with " + 1877 re.escape("'u = < &foo &bar ... >;', not 'u = < 0x1 >;'")) 1878 verify_to_nodes_error_matches( 1879 "string", 1880 "expected property 'string' on / in .* to be assigned with " + 1881 re.escape("'string = < &foo &bar ... >;', ") + 1882 re.escape("not 'string = \"foo\\tbar baz\";'")) 1883 1884 # Test Property.to_path() 1885 1886 def verify_to_path(prop, path): 1887 actual = dt.root.props[prop].to_path().path 1888 assert actual == path, f"'{prop} gives the wrong path" 1889 1890 def verify_to_path_error_matches(prop, expected_re): 1891 with dtlib_raises(err_matches=expected_re): 1892 dt.root.props[prop].to_path() 1893 1894 verify_to_path("path", "/target") 1895 verify_to_path("manualpath", "/target") 1896 1897 verify_to_path_error_matches( 1898 "u", 1899 "expected property 'u' on / in .* to be assigned with either " + 1900 re.escape("'u = &foo' or 'u = \"/path/to/node\"', not 'u = < 0x1 >;'")) 1901 verify_to_path_error_matches( 1902 "missingpath", 1903 "property 'missingpath' on / in .* points to the non-existent node " 1904 '"/missing"') 1905 1906 # Test top-level to_num() and to_nums() 1907 1908 def verify_raw_to_num(fn, prop, length, signed, expected): 1909 actual = fn(dt.root.props[prop].value, length, signed) 1910 assert actual == expected, \ 1911 f"{fn.__name__}(<{prop}>, {length}, {signed}) gives wrong value" 1912 1913 def verify_raw_to_num_error(fn, data, length, msg): 1914 # We're using this instead of dtlib_raises() for the extra 1915 # context we get from the assertion below. 1916 with pytest.raises(dtlib.DTError) as e: 1917 fn(data, length) 1918 assert str(e.value) == msg, \ 1919 (f"{fn.__name__}() called with data='{data}', length='{length}' " 1920 "gives the wrong error") 1921 1922 verify_raw_to_num(dtlib.to_num, "u", None, False, 1) 1923 verify_raw_to_num(dtlib.to_num, "u", 4, False, 1) 1924 verify_raw_to_num(dtlib.to_num, "s", None, False, 0xFFFFFFFF) 1925 verify_raw_to_num(dtlib.to_num, "s", None, True, -1) 1926 verify_raw_to_num(dtlib.to_nums, "empty", 4, False, []) 1927 verify_raw_to_num(dtlib.to_nums, "u16", 2, False, [1, 2]) 1928 verify_raw_to_num(dtlib.to_nums, "two_s", 4, False, [0xFFFFFFFF, 0xFFFFFFFE]) 1929 verify_raw_to_num(dtlib.to_nums, "two_s", 4, True, [-1, -2]) 1930 1931 verify_raw_to_num_error(dtlib.to_num, 0, 0, "'0' has type 'int', expected 'bytes'") 1932 verify_raw_to_num_error(dtlib.to_num, b"", 0, "'length' must be greater than zero, was 0") 1933 verify_raw_to_num_error(dtlib.to_num, b"foo", 2, "b'foo' is 3 bytes long, expected 2") 1934 verify_raw_to_num_error(dtlib.to_nums, 0, 0, "'0' has type 'int', expected 'bytes'") 1935 verify_raw_to_num_error(dtlib.to_nums, b"", 0, "'length' must be greater than zero, was 0") 1936 verify_raw_to_num_error(dtlib.to_nums, b"foooo", 2, "b'foooo' is 5 bytes long, expected a length that's a multiple of 2") 1937 1938def test_duplicate_labels(): 1939 ''' 1940 It is an error to duplicate labels in most conditions, but there 1941 are some exceptions where it's OK. 1942 ''' 1943 1944 verify_error(""" 1945/dts-v1/; 1946 1947/ { 1948 sub1 { 1949 label: foo { 1950 }; 1951 }; 1952 1953 sub2 { 1954 label: bar { 1955 }; 1956 }; 1957}; 1958""", 1959"Label 'label' appears on /sub1/foo and on /sub2/bar") 1960 1961 verify_error(""" 1962/dts-v1/; 1963 1964/ { 1965 sub { 1966 label: foo { 1967 }; 1968 }; 1969}; 1970/ { 1971 sub { 1972 label: bar { 1973 }; 1974 }; 1975}; 1976""", 1977"Label 'label' appears on /sub/bar and on /sub/foo") 1978 1979 verify_error(""" 1980/dts-v1/; 1981 1982/ { 1983 foo: a = < 0 >; 1984 foo: node { 1985 }; 1986}; 1987""", 1988"Label 'foo' appears on /node and on property 'a' of node /") 1989 1990 verify_error(""" 1991/dts-v1/; 1992 1993/ { 1994 foo: a = < 0 >; 1995 node { 1996 foo: b = < 0 >; 1997 }; 1998}; 1999""", 2000"Label 'foo' appears on property 'a' of node / and on property 'b' of node /node") 2001 2002 verify_error(""" 2003/dts-v1/; 2004 2005/ { 2006 foo: a = foo: < 0 >; 2007}; 2008""", 2009"Label 'foo' appears in the value of property 'a' of node / and on property 'a' of node /") 2010 2011 # Giving the same label twice for the same node is fine 2012 verify_parse(""" 2013/dts-v1/; 2014 2015/ { 2016 sub { 2017 label: foo { 2018 }; 2019 }; 2020}; 2021/ { 2022 2023 sub { 2024 label: foo { 2025 }; 2026 }; 2027}; 2028""", 2029""" 2030/dts-v1/; 2031 2032/ { 2033 sub { 2034 label: foo { 2035 }; 2036 }; 2037}; 2038""") 2039 2040 # Duplicate labels are fine if one of the nodes is deleted 2041 verify_parse(""" 2042/dts-v1/; 2043 2044/ { 2045 label: foo { 2046 }; 2047 label: bar { 2048 }; 2049}; 2050 2051/delete-node/ &{/bar}; 2052""", 2053""" 2054/dts-v1/; 2055 2056/ { 2057 label: foo { 2058 }; 2059}; 2060""") 2061 2062 # 2063 # Test overriding/deleting a property with references 2064 # 2065 2066 verify_parse(""" 2067/dts-v1/; 2068 2069/ { 2070 x = &foo, < &foo >; 2071 y = &foo, < &foo >; 2072 foo: foo { 2073 }; 2074}; 2075 2076/ { 2077 x = < 1 >; 2078 /delete-property/ y; 2079}; 2080""", 2081""" 2082/dts-v1/; 2083 2084/ { 2085 x = < 0x1 >; 2086 foo: foo { 2087 }; 2088}; 2089""") 2090 2091 # 2092 # Test self-referential node 2093 # 2094 2095 verify_parse(""" 2096/dts-v1/; 2097 2098/ { 2099 label: foo { 2100 x = &{/foo}, &label, < &label >; 2101 }; 2102}; 2103""", 2104""" 2105/dts-v1/; 2106 2107/ { 2108 label: foo { 2109 x = &{/foo}, 2110 &label, 2111 < &label >; 2112 phandle = < 0x1 >; 2113 }; 2114}; 2115""") 2116 2117 # 2118 # Test /memreserve/ 2119 # 2120 2121 dt = verify_parse(""" 2122/dts-v1/; 2123 2124l1: l2: /memreserve/ (1 + 1) (2 * 2); 2125/memreserve/ 0x100 0x200; 2126 2127/ { 2128}; 2129""", 2130""" 2131/dts-v1/; 2132 2133l1: l2: /memreserve/ 0x0000000000000002 0x0000000000000004; 2134/memreserve/ 0x0000000000000100 0x0000000000000200; 2135 2136/ { 2137}; 2138""") 2139 2140 expected = [(["l1", "l2"], 2, 4), ([], 0x100, 0x200)] 2141 assert dt.memreserves == expected 2142 2143 verify_error_endswith(""" 2144/dts-v1/; 2145 2146foo: / { 2147}; 2148""", 2149":3 (column 6): parse error: expected /memreserve/ after labels at beginning of file") 2150 2151def test_reprs(): 2152 '''Test the __repr__() functions.''' 2153 2154 dts = """ 2155/dts-v1/; 2156 2157/ { 2158 x = < 0 >; 2159 sub { 2160 y = < 1 >; 2161 }; 2162}; 2163""" 2164 2165 dt = parse(dts, include_path=("foo", "bar")) 2166 2167 assert re.fullmatch(r"DT\(filename='.*', include_path=.'foo', 'bar'.\)", 2168 repr(dt)) 2169 assert re.fullmatch("<Property 'x' on / in .*:5>", 2170 repr(dt.root.props["x"])) 2171 assert re.fullmatch("<Node /sub in .*:6>", 2172 repr(dt.root.nodes["sub"])) 2173 2174 dt = parse(dts, include_path=iter(("foo", "bar"))) 2175 2176 assert re.fullmatch(r"DT\(filename='.*', include_path=.'foo', 'bar'.\)", 2177 repr(dt)) 2178 2179def test_names(): 2180 '''Tests for node/property names.''' 2181 2182 verify_parse(r""" 2183/dts-v1/; 2184 2185/ { 2186 // A leading \ is accepted but ignored in node/propert names 2187 \aA0,._+*#?- = &_, &{/aA0,._+@-}; 2188 2189 // Names that overlap with operators and integer literals 2190 2191 + = [ 00 ]; 2192 * = [ 02 ]; 2193 - = [ 01 ]; 2194 ? = [ 03 ]; 2195 0 = [ 04 ]; 2196 0x123 = [ 05 ]; 2197 2198 // Node names are more restrictive than property names. 2199 _: \aA0,._+@- { 2200 }; 2201 2202 0 { 2203 }; 2204}; 2205""", 2206""" 2207/dts-v1/; 2208 2209/ { 2210 aA0,._+*#?- = &_, 2211 &{/aA0,._+@-}; 2212 + = [ 00 ]; 2213 * = [ 02 ]; 2214 - = [ 01 ]; 2215 ? = [ 03 ]; 2216 0 = [ 04 ]; 2217 0x123 = [ 05 ]; 2218 _: aA0,._+@- { 2219 }; 2220 0 { 2221 }; 2222}; 2223""") 2224 2225 verify_error_endswith(r""" 2226/dts-v1/; 2227 2228/ { 2229 foo@3; 2230}; 2231""", 2232":4 (column 7): parse error: '@' is only allowed in node names") 2233 2234 verify_error_endswith(r""" 2235/dts-v1/; 2236 2237/ { 2238 foo@3 = < 0 >; 2239}; 2240""", 2241":4 (column 8): parse error: '@' is only allowed in node names") 2242 2243 verify_error_endswith(r""" 2244/dts-v1/; 2245 2246/ { 2247 foo@2@3 { 2248 }; 2249}; 2250""", 2251":4 (column 10): parse error: multiple '@' in node name") 2252 2253def test_dense_input(): 2254 ''' 2255 Test that a densely written DTS input round-trips to something 2256 readable. 2257 ''' 2258 2259 verify_parse(""" 2260/dts-v1/;/{l1:l2:foo{l3:l4:bar{l5:x=l6:/bits/8<l7:1 l8:2>l9:,[03],"a";};};}; 2261""", 2262""" 2263/dts-v1/; 2264 2265/ { 2266 l1: l2: foo { 2267 l3: l4: bar { 2268 l5: x = l6: [ l7: 01 l8: 02 l9: ], 2269 [ 03 ], 2270 "a"; 2271 }; 2272 }; 2273}; 2274""") 2275 2276def test_misc(): 2277 '''Test miscellaneous errors and non-errors.''' 2278 2279 verify_error_endswith("", ":1 (column 1): parse error: expected '/dts-v1/;' at start of file") 2280 2281 verify_error_endswith(""" 2282/dts-v1/; 2283""", 2284":2 (column 1): parse error: no root node defined") 2285 2286 verify_error_endswith(""" 2287/dts-v1/; /plugin/; 2288""", 2289":1 (column 11): parse error: /plugin/ is not supported") 2290 2291 verify_error_endswith(""" 2292/dts-v1/; 2293 2294/ { 2295 foo: foo { 2296 }; 2297}; 2298 2299// Only one label supported before label references at the top level 2300l1: l2: &foo { 2301}; 2302""", 2303":9 (column 5): parse error: expected label reference (&foo)") 2304 2305 verify_error_endswith(""" 2306/dts-v1/; 2307 2308/ { 2309 foo: {}; 2310}; 2311""", 2312":4 (column 14): parse error: expected node or property name") 2313 2314 # Multiple /dts-v1/ at the start of a file is fine 2315 verify_parse(""" 2316/dts-v1/; 2317/dts-v1/; 2318 2319/ { 2320}; 2321""", 2322""" 2323/dts-v1/; 2324 2325/ { 2326}; 2327""") 2328 2329def test_dangling_alias(): 2330 dt = parse(''' 2331/dts-v1/; 2332 2333/ { 2334 aliases { foo = "/missing"; }; 2335}; 2336''', force=True) 2337 assert dt.get_node('/aliases').props['foo'].to_string() == '/missing' 2338 2339def test_duplicate_nodes(): 2340 # Duplicate node names in the same {} block are an error in dtc, 2341 # so we want to reproduce the same behavior. But we also need to 2342 # make sure that doesn't break overlays modifying the same node. 2343 2344 verify_error_endswith(""" 2345/dts-v1/; 2346 2347/ { 2348 foo {}; 2349 foo {}; 2350}; 2351""", "/foo: duplicate node name") 2352 2353 verify_parse(""" 2354/dts-v1/; 2355 2356/ { 2357 foo { prop = <3>; }; 2358}; 2359/ { 2360 foo { prop = <4>; }; 2361}; 2362""", 2363""" 2364/dts-v1/; 2365 2366/ { 2367 foo { 2368 prop = < 0x4 >; 2369 }; 2370}; 2371""") 2372 2373def test_deepcopy(): 2374 dt = parse(''' 2375/dts-v1/; 2376 2377memreservelabel: /memreserve/ 0xdeadbeef 0x4000; 2378 2379/ { 2380 aliases { 2381 foo = &nodelabel; 2382 }; 2383 rootprop_label: rootprop = prop_offset0: <0x12345678 prop_offset4: 0x0>; 2384 nodelabel: node@1234 { 2385 nodeprop = <3>; 2386 subnode { 2387 ref-to-node = <&nodelabel>; 2388 }; 2389 }; 2390}; 2391''') 2392 dt_copy = deepcopy(dt) 2393 assert dt_copy.filename == dt.filename 2394 2395 # dt_copy.root checks: 2396 root_copy = dt_copy.root 2397 assert root_copy is not dt.root 2398 assert root_copy.parent is None 2399 assert root_copy.dt is dt_copy 2400 assert root_copy.labels == [] 2401 assert root_copy.labels is not dt.root.labels 2402 2403 # dt_copy.memreserves checks: 2404 assert dt_copy.memreserves == [ 2405 (set(['memreservelabel']), 0xdeadbeef, 0x4000) 2406 ] 2407 assert dt_copy.memreserves is not dt.memreserves 2408 2409 # Miscellaneous dt_copy node and property checks: 2410 assert 'rootprop' in root_copy.props 2411 rootprop_copy = root_copy.props['rootprop'] 2412 assert rootprop_copy is not dt.root.props['rootprop'] 2413 assert rootprop_copy.name == 'rootprop' 2414 assert rootprop_copy.value == b'\x12\x34\x56\x78\0\0\0\0' 2415 assert rootprop_copy.type == dtlib.Type.NUMS 2416 assert rootprop_copy.labels == ['rootprop_label'] 2417 assert rootprop_copy.labels is not dt.root.props['rootprop'].labels 2418 assert rootprop_copy.offset_labels == { 2419 'prop_offset0': 0, 2420 'prop_offset4': 4, 2421 } 2422 assert rootprop_copy.offset_labels is not \ 2423 dt.root.props['rootprop'].offset_labels 2424 assert rootprop_copy.node is root_copy 2425 2426 assert dt_copy.has_node('/node@1234') 2427 node_copy = dt_copy.get_node('/node@1234') 2428 assert node_copy is not dt.get_node('/node@1234') 2429 assert node_copy.labels == ['nodelabel'] 2430 assert node_copy.labels is not dt.get_node('/node@1234').labels 2431 assert node_copy.name == 'node@1234' 2432 assert node_copy.unit_addr == '1234' 2433 assert node_copy.path == '/node@1234' 2434 assert set(node_copy.props.keys()) == set(['nodeprop', 'phandle']) 2435 assert node_copy.props is not dt.get_node('/node@1234').props 2436 assert node_copy.props['nodeprop'].name == 'nodeprop' 2437 assert node_copy.props['nodeprop'].labels == [] 2438 assert node_copy.props['nodeprop'].offset_labels == {} 2439 assert node_copy.props['nodeprop'].node is node_copy 2440 assert node_copy.dt is dt_copy 2441 2442 assert 'subnode' in node_copy.nodes 2443 subnode_copy = node_copy.nodes['subnode'] 2444 assert subnode_copy is not dt.get_node('/node@1234/subnode') 2445 assert subnode_copy.parent is node_copy 2446 2447 # dt_copy.label2prop and .label2prop_offset checks: 2448 assert 'rootprop_label' in dt_copy.label2prop 2449 assert dt_copy.label2prop['rootprop_label'] is rootprop_copy 2450 assert list(dt_copy.label2prop_offset.keys()) == ['prop_offset0', 2451 'prop_offset4'] 2452 assert dt_copy.label2prop_offset['prop_offset4'][0] is rootprop_copy 2453 assert dt_copy.label2prop_offset['prop_offset4'][1] == 4 2454 2455 # dt_copy.foo2node checks: 2456 def check_node_lookup_table(attr_name): 2457 original = getattr(dt, attr_name) 2458 copy = getattr(dt_copy, attr_name) 2459 assert original is not copy 2460 assert list(original.keys()) == list(copy.keys()) 2461 assert all([original_node.path == copy_node.path and 2462 original_node is not copy_node 2463 for original_node, copy_node in 2464 zip(original.values(), copy.values())]) 2465 2466 check_node_lookup_table('alias2node') 2467 check_node_lookup_table('label2node') 2468 check_node_lookup_table('phandle2node') 2469 2470 assert list(dt_copy.alias2node.keys()) == ['foo'] 2471 assert dt_copy.alias2node['foo'] is node_copy 2472 2473 assert list(dt_copy.label2node.keys()) == ['nodelabel'] 2474 assert dt_copy.label2node['nodelabel'] is node_copy 2475 2476 assert dt_copy.phandle2node 2477 # This is a little awkward because of the way dtlib allocates 2478 # phandles. 2479 phandle2node_copy_values = set(dt_copy.phandle2node.values()) 2480 assert node_copy in phandle2node_copy_values 2481 for node in dt.node_iter(): 2482 assert node not in phandle2node_copy_values 2483 2484def test_move_node(): 2485 # Test cases for DT.move_node(). 2486 2487 dt = parse(''' 2488/dts-v1/; 2489 2490/ { 2491 aliases { 2492 parent-alias = &parent_label; 2493 }; 2494 parent_label: parent { 2495 child {}; 2496 }; 2497 bar { 2498 shouldbechosen { 2499 foo = "bar"; 2500 }; 2501 }; 2502}; 2503''') 2504 parent = dt.get_node('/parent') 2505 child = dt.get_node('/parent/child') 2506 2507 dt.move_node(parent, '/newpath') 2508 2509 assert parent.path == '/newpath' 2510 assert child.path == '/newpath/child' 2511 assert child.parent is parent 2512 assert child.parent is dt.get_node('/newpath') 2513 assert dt.get_node('parent-alias') is parent 2514 assert dt.label2node['parent_label'] is parent 2515 2516 assert not dt.has_node('/chosen') 2517 dt.move_node(dt.get_node('/bar/shouldbechosen'), '/chosen') 2518 assert dt.has_node('/chosen') 2519 assert 'foo' in dt.get_node('/chosen').props 2520 2521 with dtlib_raises("the root node can't be moved"): 2522 dt.move_node(dt.root, '/somewhere/else') 2523 2524 with dtlib_raises("can't move '/newpath' to '/aliases': " 2525 "destination node exists"): 2526 dt.move_node(parent, '/aliases') 2527 2528 with dtlib_raises("path 'xyz' doesn't start with '/'"): 2529 dt.move_node(parent, 'xyz') 2530 2531 with dtlib_raises("new path '/ invalid': bad character ' '"): 2532 dt.move_node(parent, '/ invalid') 2533 2534 with dtlib_raises("can't move '/newpath' to '/foo/bar': " 2535 "parent node '/foo' doesn't exist"): 2536 dt.move_node(parent, '/foo/bar') 2537 2538def test_filename_and_lineno(): 2539 """Test that filename and lineno are correctly tracked for nodes and properties.""" 2540 2541 with tempfile.TemporaryDirectory() as tmpdir: 2542 included_file = os.path.join(tmpdir, 'included.dtsi') 2543 with open(included_file, 'w') as f: 2544 f.write('''/* a new node */ 2545/ { 2546 node1: test-node1 { 2547 prop1A = "short value 1A"; 2548 }; 2549}; 2550''') 2551 2552 main_file = os.path.join(tmpdir, 'test_with_include.dts') 2553 with open(main_file, 'w') as f: 2554 f.write('''/dts-v1/; 2555 2556/include/ "included.dtsi" 2557 2558/ { 2559 node2: test-node2 { 2560 prop2A = "value 2A"; 2561 prop2B = "multi", "line", "value", "2B"; 2562 }; 2563}; 2564 2565&node1 { 2566 prop1B = "longer value 1B"; 2567}; 2568''') 2569 2570 dt = dtlib.DT(main_file, include_path=[tmpdir], base_dir=tmpdir) 2571 2572 test_node2 = dt.get_node('/test-node2') 2573 prop2A = test_node2.props['prop2A'] 2574 prop2B = test_node2.props['prop2B'] 2575 2576 assert os.path.samefile(test_node2.filename, main_file) 2577 assert test_node2.lineno == 6 2578 assert os.path.samefile(prop2A.filename, main_file) 2579 assert prop2A.lineno == 7 2580 assert os.path.samefile(prop2B.filename, main_file) 2581 assert prop2B.lineno == 8 2582 2583 test_node1 = dt.get_node('/test-node1') 2584 prop1A = test_node1.props['prop1A'] 2585 prop1B = test_node1.props['prop1B'] 2586 2587 assert os.path.samefile(test_node1.filename, included_file) 2588 assert test_node1.lineno == 3 2589 assert os.path.samefile(prop1A.filename, included_file) 2590 assert prop1A.lineno == 4 2591 assert os.path.samefile(prop1B.filename, main_file) 2592 assert prop1B.lineno == 13 2593 2594 # Test contents and alignment of the generated file comments 2595 assert str(dt) == ''' 2596/dts-v1/; 2597 2598/* node '/' defined in included.dtsi:2 */ 2599/ { 2600 2601 /* node '/test-node1' defined in included.dtsi:3 */ 2602 node1: test-node1 { 2603 prop1A = "short value 1A"; /* in included.dtsi:4 */ 2604 prop1B = "longer value 1B"; /* in test_with_include.dts:13 */ 2605 }; 2606 2607 /* node '/test-node2' defined in test_with_include.dts:6 */ 2608 node2: test-node2 { 2609 prop2A = "value 2A"; /* in test_with_include.dts:7 */ 2610 prop2B = "multi", 2611 "line", 2612 "value", 2613 "2B"; /* in test_with_include.dts:8 */ 2614 }; 2615}; 2616'''[1:-1] 2617