test_edtlib.py 25 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533
  1. # Copyright (c) 2019 Nordic Semiconductor ASA
  2. # SPDX-License-Identifier: BSD-3-Clause
  3. import contextlib
  4. import io
  5. from logging import WARNING
  6. import os
  7. from pathlib import Path
  8. import pytest
  9. from devicetree import edtlib
  10. # Test suite for edtlib.py.
  11. #
  12. # Run it using pytest (https://docs.pytest.org/en/stable/usage.html):
  13. #
  14. # $ pytest testedtlib.py
  15. #
  16. # See the comment near the top of testdtlib.py for additional pytest advice.
  17. #
  18. # test.dts is the main test file. test-bindings/ and test-bindings-2/ has
  19. # bindings. The tests mostly use string comparisons via the various __repr__()
  20. # methods.
  21. HERE = os.path.dirname(__file__)
  22. @contextlib.contextmanager
  23. def from_here():
  24. # Convenience hack to minimize diff from zephyr.
  25. cwd = os.getcwd()
  26. try:
  27. os.chdir(HERE)
  28. yield
  29. finally:
  30. os.chdir(cwd)
  31. def hpath(filename):
  32. '''Convert 'filename' to the host path syntax.'''
  33. return os.fspath(Path(filename))
  34. def test_warnings(caplog):
  35. '''Tests for situations that should cause warnings.'''
  36. with from_here(): edtlib.EDT("test.dts", ["test-bindings"])
  37. enums_hpath = hpath('test-bindings/enums.yaml')
  38. expected_warnings = [
  39. f"'oldprop' is marked as deprecated in 'properties:' in {hpath('test-bindings/deprecated.yaml')} for node /test-deprecated.",
  40. "unit address and first address in 'reg' (0x1) don't match for /reg-zero-size-cells/node",
  41. "unit address and first address in 'reg' (0x5) don't match for /reg-ranges/parent/node",
  42. "unit address and first address in 'reg' (0x30000000200000001) don't match for /reg-nested-ranges/grandparent/parent/node",
  43. f"compatible 'enums' in binding '{enums_hpath}' has non-tokenizable enum for property 'string-enum': 'foo bar', 'foo_bar'",
  44. f"compatible 'enums' in binding '{enums_hpath}' has enum for property 'tokenizable-lower-enum' that is only tokenizable in lowercase: 'bar', 'BAR'",
  45. ]
  46. assert caplog.record_tuples == [('devicetree.edtlib', WARNING, warning_message)
  47. for warning_message in expected_warnings]
  48. def test_interrupts():
  49. '''Tests for the interrupts property.'''
  50. with from_here():
  51. edt = edtlib.EDT("test.dts", ["test-bindings"])
  52. filenames = {i: hpath(f'test-bindings/interrupt-{i}-cell.yaml')
  53. for i in range(1, 4)}
  54. assert str(edt.get_node("/interrupt-parent-test/node").interrupts) == \
  55. f"[<ControllerAndData, name: foo, controller: <Node /interrupt-parent-test/controller in 'test.dts', binding {filenames[3]}>, data: OrderedDict([('one', 1), ('two', 2), ('three', 3)])>, <ControllerAndData, name: bar, controller: <Node /interrupt-parent-test/controller in 'test.dts', binding {filenames[3]}>, data: OrderedDict([('one', 4), ('two', 5), ('three', 6)])>]"
  56. assert str(edt.get_node("/interrupts-extended-test/node").interrupts) == \
  57. f"[<ControllerAndData, controller: <Node /interrupts-extended-test/controller-0 in 'test.dts', binding {filenames[1]}>, data: OrderedDict([('one', 1)])>, <ControllerAndData, controller: <Node /interrupts-extended-test/controller-1 in 'test.dts', binding {filenames[2]}>, data: OrderedDict([('one', 2), ('two', 3)])>, <ControllerAndData, controller: <Node /interrupts-extended-test/controller-2 in 'test.dts', binding {filenames[3]}>, data: OrderedDict([('one', 4), ('two', 5), ('three', 6)])>]"
  58. assert str(edt.get_node("/interrupt-map-test/node@0").interrupts) == \
  59. f"[<ControllerAndData, controller: <Node /interrupt-map-test/controller-0 in 'test.dts', binding {filenames[1]}>, data: OrderedDict([('one', 0)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-1 in 'test.dts', binding {filenames[2]}>, data: OrderedDict([('one', 0), ('two', 1)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-2 in 'test.dts', binding {filenames[3]}>, data: OrderedDict([('one', 0), ('two', 0), ('three', 2)])>]"
  60. assert str(edt.get_node("/interrupt-map-test/node@1").interrupts) == \
  61. f"[<ControllerAndData, controller: <Node /interrupt-map-test/controller-0 in 'test.dts', binding {filenames[1]}>, data: OrderedDict([('one', 3)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-1 in 'test.dts', binding {filenames[2]}>, data: OrderedDict([('one', 0), ('two', 4)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-2 in 'test.dts', binding {filenames[3]}>, data: OrderedDict([('one', 0), ('two', 0), ('three', 5)])>]"
  62. assert str(edt.get_node("/interrupt-map-bitops-test/node@70000000E").interrupts) == \
  63. f"[<ControllerAndData, controller: <Node /interrupt-map-bitops-test/controller in 'test.dts', binding {filenames[2]}>, data: OrderedDict([('one', 3), ('two', 2)])>]"
  64. def test_reg():
  65. '''Tests for the regs property'''
  66. with from_here():
  67. edt = edtlib.EDT("test.dts", ["test-bindings"])
  68. assert str(edt.get_node("/reg-zero-address-cells/node").regs) == \
  69. "[<Register, size: 0x1>, <Register, size: 0x2>]"
  70. assert str(edt.get_node("/reg-zero-size-cells/node").regs) == \
  71. "[<Register, addr: 0x1>, <Register, addr: 0x2>]"
  72. assert str(edt.get_node("/reg-ranges/parent/node").regs) == \
  73. "[<Register, addr: 0x5, size: 0x1>, <Register, addr: 0xe0000000f, size: 0x1>, <Register, addr: 0xc0000000e, size: 0x1>, <Register, addr: 0xc0000000d, size: 0x1>, <Register, addr: 0xa0000000b, size: 0x1>, <Register, addr: 0x0, size: 0x1>]"
  74. assert str(edt.get_node("/reg-nested-ranges/grandparent/parent/node").regs) == \
  75. "[<Register, addr: 0x30000000200000001, size: 0x1>]"
  76. def test_pinctrl():
  77. '''Test 'pinctrl-<index>'.'''
  78. with from_here():
  79. edt = edtlib.EDT("test.dts", ["test-bindings"])
  80. assert str(edt.get_node("/pinctrl/dev").pinctrls) == \
  81. "[<PinCtrl, name: zero, configuration nodes: []>, <PinCtrl, name: one, configuration nodes: [<Node /pinctrl/pincontroller/state-1 in 'test.dts', no binding>]>, <PinCtrl, name: two, configuration nodes: [<Node /pinctrl/pincontroller/state-1 in 'test.dts', no binding>, <Node /pinctrl/pincontroller/state-2 in 'test.dts', no binding>]>]"
  82. def test_hierarchy():
  83. '''Test Node.parent and Node.children'''
  84. with from_here():
  85. edt = edtlib.EDT("test.dts", ["test-bindings"])
  86. assert edt.get_node("/").parent is None
  87. assert str(edt.get_node("/parent/child-1").parent) == \
  88. "<Node /parent in 'test.dts', no binding>"
  89. assert str(edt.get_node("/parent/child-2/grandchild").parent) == \
  90. "<Node /parent/child-2 in 'test.dts', no binding>"
  91. assert str(edt.get_node("/parent").children) == \
  92. "OrderedDict([('child-1', <Node /parent/child-1 in 'test.dts', no binding>), ('child-2', <Node /parent/child-2 in 'test.dts', no binding>)])"
  93. assert edt.get_node("/parent/child-1").children == {}
  94. def test_include():
  95. '''Test 'include:' and the legacy 'inherits: !include ...' in bindings'''
  96. with from_here():
  97. edt = edtlib.EDT("test.dts", ["test-bindings"])
  98. assert str(edt.get_node("/binding-include").description) == \
  99. "Parent binding"
  100. assert str(edt.get_node("/binding-include").props) == \
  101. "OrderedDict([('foo', <Property, name: foo, type: int, value: 0>), ('bar', <Property, name: bar, type: int, value: 1>), ('baz', <Property, name: baz, type: int, value: 2>), ('qaz', <Property, name: qaz, type: int, value: 3>)])"
  102. def test_include_filters():
  103. '''Test property-allowlist and property-blocklist in an include.'''
  104. fname2path = {'include.yaml': 'test-bindings-include/include.yaml',
  105. 'include-2.yaml': 'test-bindings-include/include-2.yaml'}
  106. with pytest.raises(edtlib.EDTError) as e:
  107. with from_here():
  108. edtlib.Binding("test-bindings-include/allow-and-blocklist.yaml", fname2path)
  109. assert ("should not specify both 'property-allowlist:' and 'property-blocklist:'"
  110. in str(e.value))
  111. with pytest.raises(edtlib.EDTError) as e:
  112. with from_here():
  113. edtlib.Binding("test-bindings-include/allow-and-blocklist-child.yaml", fname2path)
  114. assert ("should not specify both 'property-allowlist:' and 'property-blocklist:'"
  115. in str(e.value))
  116. with pytest.raises(edtlib.EDTError) as e:
  117. with from_here():
  118. edtlib.Binding("test-bindings-include/allow-not-list.yaml", fname2path)
  119. value_str = str(e.value)
  120. assert value_str.startswith("'property-allowlist' value")
  121. assert value_str.endswith("should be a list")
  122. with pytest.raises(edtlib.EDTError) as e:
  123. with from_here():
  124. edtlib.Binding("test-bindings-include/block-not-list.yaml", fname2path)
  125. value_str = str(e.value)
  126. assert value_str.startswith("'property-blocklist' value")
  127. assert value_str.endswith("should be a list")
  128. with pytest.raises(edtlib.EDTError) as e:
  129. with from_here():
  130. binding = edtlib.Binding("test-bindings-include/include-invalid-keys.yaml", fname2path)
  131. value_str = str(e.value)
  132. assert value_str.startswith(
  133. "'include:' in test-bindings-include/include-invalid-keys.yaml should not have these "
  134. "unexpected contents: ")
  135. assert 'bad-key-1' in value_str
  136. assert 'bad-key-2' in value_str
  137. with pytest.raises(edtlib.EDTError) as e:
  138. with from_here():
  139. binding = edtlib.Binding("test-bindings-include/include-invalid-type.yaml", fname2path)
  140. value_str = str(e.value)
  141. assert value_str.startswith(
  142. "'include:' in test-bindings-include/include-invalid-type.yaml "
  143. "should be a string or list, but has type ")
  144. with pytest.raises(edtlib.EDTError) as e:
  145. with from_here():
  146. binding = edtlib.Binding("test-bindings-include/include-no-name.yaml", fname2path)
  147. value_str = str(e.value)
  148. assert value_str.startswith("'include:' element")
  149. assert value_str.endswith(
  150. "in test-bindings-include/include-no-name.yaml should have a 'name' key")
  151. with from_here():
  152. binding = edtlib.Binding("test-bindings-include/allowlist.yaml", fname2path)
  153. assert set(binding.prop2specs.keys()) == {'x'} # 'x' is allowed
  154. binding = edtlib.Binding("test-bindings-include/empty-allowlist.yaml", fname2path)
  155. assert set(binding.prop2specs.keys()) == set() # nothing is allowed
  156. binding = edtlib.Binding("test-bindings-include/blocklist.yaml", fname2path)
  157. assert set(binding.prop2specs.keys()) == {'y', 'z'} # 'x' is blocked
  158. binding = edtlib.Binding("test-bindings-include/empty-blocklist.yaml", fname2path)
  159. assert set(binding.prop2specs.keys()) == {'x', 'y', 'z'} # nothing is blocked
  160. binding = edtlib.Binding("test-bindings-include/intermixed.yaml", fname2path)
  161. assert set(binding.prop2specs.keys()) == {'x', 'a'}
  162. binding = edtlib.Binding("test-bindings-include/include-no-list.yaml", fname2path)
  163. assert set(binding.prop2specs.keys()) == {'x', 'y', 'z'}
  164. binding = edtlib.Binding("test-bindings-include/filter-child-bindings.yaml", fname2path)
  165. child = binding.child_binding
  166. grandchild = child.child_binding
  167. assert set(binding.prop2specs.keys()) == {'x'}
  168. assert set(child.prop2specs.keys()) == {'child-prop-2'}
  169. assert set(grandchild.prop2specs.keys()) == {'grandchild-prop-1'}
  170. def test_bus():
  171. '''Test 'bus:' and 'on-bus:' in bindings'''
  172. with from_here():
  173. edt = edtlib.EDT("test.dts", ["test-bindings"])
  174. assert edt.get_node("/buses/foo-bus").bus == "foo"
  175. # foo-bus does not itself appear on a bus
  176. assert edt.get_node("/buses/foo-bus").on_bus is None
  177. assert edt.get_node("/buses/foo-bus").bus_node is None
  178. # foo-bus/node1 is not a bus node...
  179. assert edt.get_node("/buses/foo-bus/node1").bus is None
  180. # ...but is on a bus
  181. assert edt.get_node("/buses/foo-bus/node1").on_bus == "foo"
  182. assert edt.get_node("/buses/foo-bus/node1").bus_node.path == \
  183. "/buses/foo-bus"
  184. # foo-bus/node2 is not a bus node...
  185. assert edt.get_node("/buses/foo-bus/node2").bus is None
  186. # ...but is on a bus
  187. assert edt.get_node("/buses/foo-bus/node2").on_bus == "foo"
  188. # no-bus-node is not a bus node...
  189. assert edt.get_node("/buses/no-bus-node").bus is None
  190. # ... and is not on a bus
  191. assert edt.get_node("/buses/no-bus-node").on_bus is None
  192. # Same compatible string, but different bindings from being on different
  193. # buses
  194. assert str(edt.get_node("/buses/foo-bus/node1").binding_path) == \
  195. hpath("test-bindings/device-on-foo-bus.yaml")
  196. assert str(edt.get_node("/buses/foo-bus/node2").binding_path) == \
  197. hpath("test-bindings/device-on-any-bus.yaml")
  198. assert str(edt.get_node("/buses/bar-bus/node").binding_path) == \
  199. hpath("test-bindings/device-on-bar-bus.yaml")
  200. assert str(edt.get_node("/buses/no-bus-node").binding_path) == \
  201. hpath("test-bindings/device-on-any-bus.yaml")
  202. # foo-bus/node/nested also appears on the foo-bus bus
  203. assert edt.get_node("/buses/foo-bus/node1/nested").on_bus == "foo"
  204. assert str(edt.get_node("/buses/foo-bus/node1/nested").binding_path) == \
  205. hpath("test-bindings/device-on-foo-bus.yaml")
  206. def test_child_binding():
  207. '''Test 'child-binding:' in bindings'''
  208. with from_here():
  209. edt = edtlib.EDT("test.dts", ["test-bindings"])
  210. child1 = edt.get_node("/child-binding/child-1")
  211. child2 = edt.get_node("/child-binding/child-2")
  212. grandchild = edt.get_node("/child-binding/child-1/grandchild")
  213. assert str(child1.binding_path) == hpath("test-bindings/child-binding.yaml")
  214. assert str(child1.description) == "child node"
  215. assert str(child1.props) == "OrderedDict([('child-prop', <Property, name: child-prop, type: int, value: 1>)])"
  216. assert str(child2.binding_path) == hpath("test-bindings/child-binding.yaml")
  217. assert str(child2.description) == "child node"
  218. assert str(child2.props) == "OrderedDict([('child-prop', <Property, name: child-prop, type: int, value: 3>)])"
  219. assert str(grandchild.binding_path) == hpath("test-bindings/child-binding.yaml")
  220. assert str(grandchild.description) == "grandchild node"
  221. assert str(grandchild.props) == "OrderedDict([('grandchild-prop', <Property, name: grandchild-prop, type: int, value: 2>)])"
  222. with from_here():
  223. binding_file = Path("test-bindings/child-binding.yaml").resolve()
  224. top = edtlib.Binding(binding_file, {})
  225. child = top.child_binding
  226. assert Path(top.path) == binding_file
  227. assert Path(child.path) == binding_file
  228. assert top.compatible == 'top-binding'
  229. assert child.compatible is None
  230. with from_here():
  231. binding_file = Path("test-bindings/child-binding-with-compat.yaml").resolve()
  232. top = edtlib.Binding(binding_file, {})
  233. child = top.child_binding
  234. assert Path(top.path) == binding_file
  235. assert Path(child.path) == binding_file
  236. assert top.compatible == 'top-binding-with-compat'
  237. assert child.compatible == 'child-compat'
  238. def test_props():
  239. '''Test Node.props (derived from DT and 'properties:' in the binding)'''
  240. with from_here():
  241. edt = edtlib.EDT("test.dts", ["test-bindings"])
  242. filenames = {i: hpath(f'test-bindings/phandle-array-controller-{i}.yaml')
  243. for i in range(0, 4)}
  244. assert str(edt.get_node("/props").props["int"]) == \
  245. "<Property, name: int, type: int, value: 1>"
  246. assert str(edt.get_node("/props").props["existent-boolean"]) == \
  247. "<Property, name: existent-boolean, type: boolean, value: True>"
  248. assert str(edt.get_node("/props").props["nonexistent-boolean"]) == \
  249. "<Property, name: nonexistent-boolean, type: boolean, value: False>"
  250. assert str(edt.get_node("/props").props["array"]) == \
  251. "<Property, name: array, type: array, value: [1, 2, 3]>"
  252. assert str(edt.get_node("/props").props["uint8-array"]) == \
  253. r"<Property, name: uint8-array, type: uint8-array, value: b'\x124'>"
  254. assert str(edt.get_node("/props").props["string"]) == \
  255. "<Property, name: string, type: string, value: 'foo'>"
  256. assert str(edt.get_node("/props").props["string-array"]) == \
  257. "<Property, name: string-array, type: string-array, value: ['foo', 'bar', 'baz']>"
  258. assert str(edt.get_node("/props").props["phandle-ref"]) == \
  259. f"<Property, name: phandle-ref, type: phandle, value: <Node /ctrl-1 in 'test.dts', binding {filenames[1]}>>"
  260. assert str(edt.get_node("/props").props["phandle-refs"]) == \
  261. f"<Property, name: phandle-refs, type: phandles, value: [<Node /ctrl-1 in 'test.dts', binding {filenames[1]}>, <Node /ctrl-2 in 'test.dts', binding {filenames[2]}>]>"
  262. assert str(edt.get_node("/props").props["phandle-array-foos"]) == \
  263. f"<Property, name: phandle-array-foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /ctrl-1 in 'test.dts', binding {filenames[1]}>, data: OrderedDict([('one', 1)])>, <ControllerAndData, controller: <Node /ctrl-2 in 'test.dts', binding {filenames[2]}>, data: OrderedDict([('one', 2), ('two', 3)])>]>"
  264. assert str(edt.get_node("/props-2").props["phandle-array-foos"]) == \
  265. ("<Property, name: phandle-array-foos, type: phandle-array, value: ["
  266. f"<ControllerAndData, name: a, controller: <Node /ctrl-0-1 in 'test.dts', binding {filenames[0]}>, data: OrderedDict()>, "
  267. "None, "
  268. f"<ControllerAndData, name: b, controller: <Node /ctrl-0-2 in 'test.dts', binding {filenames[0]}>, data: OrderedDict()>]>")
  269. assert str(edt.get_node("/props").props["foo-gpios"]) == \
  270. f"<Property, name: foo-gpios, type: phandle-array, value: [<ControllerAndData, controller: <Node /ctrl-1 in 'test.dts', binding {filenames[1]}>, data: OrderedDict([('gpio-one', 1)])>]>"
  271. assert str(edt.get_node("/props").props["path"]) == \
  272. f"<Property, name: path, type: path, value: <Node /ctrl-1 in 'test.dts', binding {filenames[1]}>>"
  273. def test_nexus():
  274. '''Test <prefix>-map via gpio-map (the most common case).'''
  275. with from_here():
  276. edt = edtlib.EDT("test.dts", ["test-bindings"])
  277. filename = hpath('test-bindings/gpio-dst.yaml')
  278. assert str(edt.get_node("/gpio-map/source").props["foo-gpios"]) == \
  279. f"<Property, name: foo-gpios, type: phandle-array, value: [<ControllerAndData, controller: <Node /gpio-map/destination in 'test.dts', binding {filename}>, data: OrderedDict([('val', 6)])>, <ControllerAndData, controller: <Node /gpio-map/destination in 'test.dts', binding {filename}>, data: OrderedDict([('val', 5)])>]>"
  280. def test_prop_defaults():
  281. '''Test property default values given in bindings'''
  282. with from_here():
  283. edt = edtlib.EDT("test.dts", ["test-bindings"])
  284. assert str(edt.get_node("/defaults").props) == \
  285. r"OrderedDict([('int', <Property, name: int, type: int, value: 123>), ('array', <Property, name: array, type: array, value: [1, 2, 3]>), ('uint8-array', <Property, name: uint8-array, type: uint8-array, value: b'\x89\xab\xcd'>), ('string', <Property, name: string, type: string, value: 'hello'>), ('string-array', <Property, name: string-array, type: string-array, value: ['hello', 'there']>), ('default-not-used', <Property, name: default-not-used, type: int, value: 234>)])"
  286. def test_prop_enums():
  287. '''test properties with enum: in the binding'''
  288. with from_here():
  289. edt = edtlib.EDT("test.dts", ["test-bindings"])
  290. props = edt.get_node('/enums').props
  291. int_enum = props['int-enum']
  292. string_enum = props['string-enum']
  293. tokenizable_enum = props['tokenizable-enum']
  294. tokenizable_lower_enum = props['tokenizable-lower-enum']
  295. no_enum = props['no-enum']
  296. assert int_enum.val == 1
  297. assert int_enum.enum_index == 0
  298. assert not int_enum.spec.enum_tokenizable
  299. assert not int_enum.spec.enum_upper_tokenizable
  300. assert string_enum.val == 'foo_bar'
  301. assert string_enum.enum_index == 1
  302. assert not string_enum.spec.enum_tokenizable
  303. assert not string_enum.spec.enum_upper_tokenizable
  304. assert tokenizable_enum.val == '123 is ok'
  305. assert tokenizable_enum.val_as_token == '123_is_ok'
  306. assert tokenizable_enum.enum_index == 2
  307. assert tokenizable_enum.spec.enum_tokenizable
  308. assert tokenizable_enum.spec.enum_upper_tokenizable
  309. assert tokenizable_lower_enum.val == 'bar'
  310. assert tokenizable_lower_enum.val_as_token == 'bar'
  311. assert tokenizable_lower_enum.enum_index == 0
  312. assert tokenizable_lower_enum.spec.enum_tokenizable
  313. assert not tokenizable_lower_enum.spec.enum_upper_tokenizable
  314. assert no_enum.enum_index is None
  315. assert not no_enum.spec.enum_tokenizable
  316. assert not no_enum.spec.enum_upper_tokenizable
  317. def test_binding_inference():
  318. '''Test inferred bindings for special zephyr-specific nodes.'''
  319. warnings = io.StringIO()
  320. with from_here():
  321. edt = edtlib.EDT("test.dts", ["test-bindings"], warnings)
  322. assert str(edt.get_node("/zephyr,user").props) == r"OrderedDict()"
  323. with from_here():
  324. edt = edtlib.EDT("test.dts", ["test-bindings"], warnings,
  325. infer_binding_for_paths=["/zephyr,user"])
  326. filenames = {i: hpath(f'test-bindings/phandle-array-controller-{i}.yaml')
  327. for i in range(1, 3)}
  328. assert str(edt.get_node("/zephyr,user").props) == \
  329. rf"OrderedDict([('boolean', <Property, name: boolean, type: boolean, value: True>), ('bytes', <Property, name: bytes, type: uint8-array, value: b'\x81\x82\x83'>), ('number', <Property, name: number, type: int, value: 23>), ('numbers', <Property, name: numbers, type: array, value: [1, 2, 3]>), ('string', <Property, name: string, type: string, value: 'text'>), ('strings', <Property, name: strings, type: string-array, value: ['a', 'b', 'c']>), ('handle', <Property, name: handle, type: phandle, value: <Node /ctrl-1 in 'test.dts', binding {filenames[1]}>>), ('phandles', <Property, name: phandles, type: phandles, value: [<Node /ctrl-1 in 'test.dts', binding {filenames[1]}>, <Node /ctrl-2 in 'test.dts', binding {filenames[2]}>]>), ('phandle-array-foos', <Property, name: phandle-array-foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /ctrl-2 in 'test.dts', binding {filenames[2]}>, data: OrderedDict([('one', 1), ('two', 2)])>]>)])"
  330. def test_multi_bindings():
  331. '''Test having multiple directories with bindings'''
  332. with from_here():
  333. edt = edtlib.EDT("test-multidir.dts", ["test-bindings", "test-bindings-2"])
  334. assert str(edt.get_node("/in-dir-1").binding_path) == \
  335. hpath("test-bindings/multidir.yaml")
  336. assert str(edt.get_node("/in-dir-2").binding_path) == \
  337. hpath("test-bindings-2/multidir.yaml")
  338. def test_dependencies():
  339. ''''Test dependency relations'''
  340. with from_here():
  341. edt = edtlib.EDT("test-multidir.dts", ["test-bindings", "test-bindings-2"])
  342. assert edt.get_node("/").dep_ordinal == 0
  343. assert edt.get_node("/in-dir-1").dep_ordinal == 1
  344. assert edt.get_node("/") in edt.get_node("/in-dir-1").depends_on
  345. assert edt.get_node("/in-dir-1") in edt.get_node("/").required_by
  346. def test_slice_errs(tmp_path):
  347. '''Test error messages from the internal _slice() helper'''
  348. dts_file = tmp_path / "error.dts"
  349. verify_error("""
  350. /dts-v1/;
  351. / {
  352. #address-cells = <1>;
  353. #size-cells = <2>;
  354. sub {
  355. reg = <3>;
  356. };
  357. };
  358. """,
  359. dts_file,
  360. f"'reg' property in <Node /sub in '{dts_file}'> has length 4, which is not evenly divisible by 12 (= 4*(<#address-cells> (= 1) + <#size-cells> (= 2))). Note that #*-cells properties come either from the parent node or from the controller (in the case of 'interrupts').")
  361. verify_error("""
  362. /dts-v1/;
  363. / {
  364. sub {
  365. interrupts = <1>;
  366. interrupt-parent = < &{/controller} >;
  367. };
  368. controller {
  369. interrupt-controller;
  370. #interrupt-cells = <2>;
  371. };
  372. };
  373. """,
  374. dts_file,
  375. f"'interrupts' property in <Node /sub in '{dts_file}'> has length 4, which is not evenly divisible by 8 (= 4*<#interrupt-cells>). Note that #*-cells properties come either from the parent node or from the controller (in the case of 'interrupts').")
  376. verify_error("""
  377. /dts-v1/;
  378. / {
  379. #address-cells = <1>;
  380. sub-1 {
  381. #address-cells = <2>;
  382. #size-cells = <3>;
  383. ranges = <4 5>;
  384. sub-2 {
  385. reg = <1 2 3 4 5>;
  386. };
  387. };
  388. };
  389. """,
  390. dts_file,
  391. f"'ranges' property in <Node /sub-1 in '{dts_file}'> has length 8, which is not evenly divisible by 24 (= 4*(<#address-cells> (= 2) + <#address-cells for parent> (= 1) + <#size-cells> (= 3))). Note that #*-cells properties come either from the parent node or from the controller (in the case of 'interrupts').")
  392. def test_bad_compatible(tmp_path):
  393. # An invalid compatible should cause an error, even on a node with
  394. # no binding.
  395. dts_file = tmp_path / "error.dts"
  396. verify_error("""
  397. /dts-v1/;
  398. / {
  399. foo {
  400. compatible = "no, whitespace";
  401. };
  402. };
  403. """,
  404. dts_file,
  405. r"node '/foo' compatible 'no, whitespace' must match this regular expression: '^[a-zA-Z][a-zA-Z0-9,+\-._]+$'")
  406. def verify_error(dts, dts_file, expected_err):
  407. # Verifies that parsing a file 'dts_file' with the contents 'dts'
  408. # (a string) raises an EDTError with the message 'expected_err'.
  409. #
  410. # The path 'dts_file' is written with the string 'dts' before the
  411. # test is run.
  412. with open(dts_file, "w", encoding="utf-8") as f:
  413. f.write(dts)
  414. f.flush() # Can't have unbuffered text IO, so flush() instead
  415. with pytest.raises(edtlib.EDTError) as e:
  416. edtlib.EDT(dts_file, [])
  417. assert str(e.value) == expected_err