#!/usr/bin/env python3 # Copyright (c) 2019 Nordic Semiconductor ASA # SPDX-License-Identifier: BSD-3-Clause import io import os import sys import edtlib # Test suite for edtlib.py. Run it directly as an executable, in this # directory: # # $ ./testedtlib.py # # test.dts is the main test file. test-bindings/ and test-bindings-2/ has # bindings. The tests mostly use string comparisons via the various __repr__() # methods. def run(): """ Runs all edtlib tests. Immediately exits with status 1 and a message on stderr on test suite failures. """ warnings = io.StringIO() edt = edtlib.EDT("test.dts", ["test-bindings"], warnings) # Verify warnings verify_eq(warnings.getvalue(), """\ warning: unit address and first address in 'reg' (0x1) don't match for /reg-zero-size-cells/node warning: unit address and first address in 'reg' (0x5) don't match for /reg-ranges/parent/node warning: unit address and first address in 'reg' (0x30000000200000001) don't match for /reg-nested-ranges/grandparent/parent/node """) # # Test interrupts # verify_streq(edt.get_node("/interrupt-parent-test/node").interrupts, "[, data: OrderedDict([('one', 1), ('two', 2), ('three', 3)])>, , data: OrderedDict([('one', 4), ('two', 5), ('three', 6)])>]") verify_streq(edt.get_node("/interrupts-extended-test/node").interrupts, "[, data: OrderedDict([('one', 1)])>, , data: OrderedDict([('one', 2), ('two', 3)])>, , data: OrderedDict([('one', 4), ('two', 5), ('three', 6)])>]") verify_streq(edt.get_node("/interrupt-map-test/node@0").interrupts, "[, data: OrderedDict([('one', 0)])>, , data: OrderedDict([('one', 0), ('two', 1)])>, , data: OrderedDict([('one', 0), ('two', 0), ('three', 2)])>]") verify_streq(edt.get_node("/interrupt-map-test/node@1").interrupts, "[, data: OrderedDict([('one', 3)])>, , data: OrderedDict([('one', 0), ('two', 4)])>, , data: OrderedDict([('one', 0), ('two', 0), ('three', 5)])>]") verify_streq(edt.get_node("/interrupt-map-bitops-test/node@70000000E").interrupts, "[, data: OrderedDict([('one', 3), ('two', 2)])>]") # # Test 'reg' # verify_streq(edt.get_node("/reg-zero-address-cells/node").regs, "[, ]") verify_streq(edt.get_node("/reg-zero-size-cells/node").regs, "[, ]") verify_streq(edt.get_node("/reg-ranges/parent/node").regs, "[, , , , , ]") verify_streq(edt.get_node("/reg-nested-ranges/grandparent/parent/node").regs, "[]") # # Test 'pinctrl-' # verify_streq(edt.get_node("/pinctrl/dev").pinctrls, "[, ]>, , ]>]") # # Test Node.parent and Node.children # verify_eq(edt.get_node("/").parent, None) verify_streq(edt.get_node("/parent/child-1").parent, "") verify_streq(edt.get_node("/parent/child-2/grandchild").parent, "") verify_streq(edt.get_node("/parent").children, "OrderedDict([('child-1', ), ('child-2', )])") verify_eq(edt.get_node("/parent/child-1").children, {}) # # Test 'include:' and the legacy 'inherits: !include ...' # verify_streq(edt.get_node("/binding-include").description, "Parent binding") verify_streq(edt.get_node("/binding-include").props, "OrderedDict([('foo', ), ('bar', ), ('baz', ), ('qaz', )])") # # Test 'bus:' and 'on-bus:' # verify_eq(edt.get_node("/buses/foo-bus").bus, "foo") # foo-bus does not itself appear on a bus verify_eq(edt.get_node("/buses/foo-bus").on_bus, None) verify_eq(edt.get_node("/buses/foo-bus").bus_node, None) # foo-bus/node is not a bus node... verify_eq(edt.get_node("/buses/foo-bus/node").bus, None) # ...but is on a bus verify_eq(edt.get_node("/buses/foo-bus/node").on_bus, "foo") verify_eq(edt.get_node("/buses/foo-bus/node").bus_node.path, "/buses/foo-bus") # Same compatible string, but different bindings from being on different # buses verify_streq(edt.get_node("/buses/foo-bus/node").binding_path, "test-bindings/device-on-foo-bus.yaml") verify_streq(edt.get_node("/buses/bar-bus/node").binding_path, "test-bindings/device-on-bar-bus.yaml") # foo-bus/node/nested also appears on the foo-bus bus verify_eq(edt.get_node("/buses/foo-bus/node/nested").on_bus, "foo") verify_streq(edt.get_node("/buses/foo-bus/node/nested").binding_path, "test-bindings/device-on-foo-bus.yaml") # # Test 'child-binding:' # child1 = edt.get_node("/child-binding/child-1") child2 = edt.get_node("/child-binding/child-2") grandchild = edt.get_node("/child-binding/child-1/grandchild") verify_streq(child1.binding_path, "test-bindings/child-binding.yaml") verify_streq(child1.description, "child node") verify_streq(child1.props, "OrderedDict([('child-prop', )])") verify_streq(child2.binding_path, "test-bindings/child-binding.yaml") verify_streq(child2.description, "child node") verify_streq(child2.props, "OrderedDict([('child-prop', )])") verify_streq(grandchild.binding_path, "test-bindings/child-binding.yaml") verify_streq(grandchild.description, "grandchild node") verify_streq(grandchild.props, "OrderedDict([('grandchild-prop', )])") # # Test EDT.compat2enabled # verify_streq(edt.compat2enabled["compat2enabled"], "[, ]") if "compat2enabled-disabled" in edt.compat2enabled: fail("'compat2enabled-disabled' should not appear in edt.compat2enabled") # # Test Node.props (derived from DT and 'properties:' in the binding) # verify_streq(edt.get_node("/props").props["int"], "") verify_streq(edt.get_node("/props").props["existent-boolean"], "") verify_streq(edt.get_node("/props").props["nonexistent-boolean"], "") verify_streq(edt.get_node("/props").props["array"], "") verify_streq(edt.get_node("/props").props["uint8-array"], r"") verify_streq(edt.get_node("/props").props["string"], "") verify_streq(edt.get_node("/props").props["string-array"], "") verify_streq(edt.get_node("/props").props["phandle-ref"], ">") verify_streq(edt.get_node("/props").props["phandle-refs"], ", ]>") verify_streq(edt.get_node("/props").props["phandle-array-foos"], ", data: OrderedDict([('one', 1)])>, , data: OrderedDict([('one', 2), ('two', 3)])>]>") verify_streq(edt.get_node("/props").props["foo-gpios"], ", data: OrderedDict([('gpio-one', 1)])>]>") verify_streq(edt.get_node("/props").props["path"], ">") # # Test -map, via gpio-map (the most common case) # verify_streq(edt.get_node("/gpio-map/source").props["foo-gpios"], ", data: OrderedDict([('val', 6)])>, , data: OrderedDict([('val', 5)])>]>") # # Test property default values given in bindings # verify_streq(edt.get_node("/defaults").props, r"OrderedDict([('int', ), ('array', ), ('uint8-array', ), ('string', ), ('string-array', ), ('default-not-used', )])") # # Test having multiple directories with bindings, with a different .dts file # edt = edtlib.EDT("test-multidir.dts", ["test-bindings", "test-bindings-2"]) verify_streq(edt.get_node("/in-dir-1").binding_path, "test-bindings/multidir.yaml") verify_streq(edt.get_node("/in-dir-2").binding_path, "test-bindings-2/multidir.yaml") # # Test dependency relations # verify_eq(edt.get_node("/").dep_ordinal, 0) verify_eq(edt.get_node("/in-dir-1").dep_ordinal, 1) if edt.get_node("/") not in edt.get_node("/in-dir-1").depends_on: fail("/ should be a direct dependency of /in-dir-1") if edt.get_node("/in-dir-1") not in edt.get_node("/").required_by: fail("/in-dir-1 should directly depend on /") # # Test error messages from _slice() # verify_error(""" /dts-v1/; / { #address-cells = <1>; #size-cells = <2>; sub { reg = <3>; }; }; """, "'reg' property in 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').") verify_error(""" /dts-v1/; / { sub { interrupts = <1>; interrupt-parent = < &{/controller} >; }; controller { interrupt-controller; #interrupt-cells = <2>; }; }; """, "'interrupts' property in 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').") verify_error(""" /dts-v1/; / { #address-cells = <1>; sub-1 { #address-cells = <2>; #size-cells = <3>; ranges = <4 5>; sub-2 { reg = <1 2 3 4 5>; }; }; }; """, "'ranges' property in 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').") print("all tests passed") def verify_error(dts, error): # Verifies that parsing a file with the contents 'dts' (a string) raises an # EDTError with the message 'error' # Could use the 'tempfile' module instead of 'error.dts', but having a # consistent filename makes error messages consistent and easier to test. # error.dts is kept if the test fails, which is helpful. with open("error.dts", "w", encoding="utf-8") as f: f.write(dts) f.flush() # Can't have unbuffered text IO, so flush() instead try: edtlib.EDT("error.dts", []) except edtlib.EDTError as e: if str(e) != error: fail(f"expected the EDTError '{error}', got the EDTError '{e}'") except Exception as e: fail(f"expected the EDTError '{error}', got the {type(e).__name__} '{e}'") else: fail(f"expected the error '{error}', got no error") os.remove("error.dts") def fail(msg): sys.exit("test failed: " + msg) def verify_eq(actual, expected): if actual != expected: # Put values on separate lines to make it easy to spot differences fail("not equal (expected value last):\n'{}'\n'{}'" .format(actual, expected)) def verify_streq(actual, expected): verify_eq(str(actual), expected) if __name__ == "__main__": run()