# Copyright (c) 2019 Nordic Semiconductor ASA # SPDX-License-Identifier: BSD-3-Clause import contextlib from copy import deepcopy import io from logging import WARNING import os from pathlib import Path import pytest from devicetree import edtlib # Test suite for edtlib.py. # # Run it using pytest (https://docs.pytest.org/en/stable/usage.html): # # $ pytest testedtlib.py # # See the comment near the top of testdtlib.py for additional pytest advice. # # 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. HERE = os.path.dirname(__file__) @contextlib.contextmanager def from_here(): # Convenience hack to minimize diff from zephyr. cwd = os.getcwd() try: os.chdir(HERE) yield finally: os.chdir(cwd) def hpath(filename): '''Convert 'filename' to the host path syntax.''' return os.fspath(Path(filename)) def test_warnings(caplog): '''Tests for situations that should cause warnings.''' with from_here(): edtlib.EDT("test.dts", ["test-bindings"]) enums_hpath = hpath('test-bindings/enums.yaml') expected_warnings = [ f"'oldprop' is marked as deprecated in 'properties:' in {hpath('test-bindings/deprecated.yaml')} for node /test-deprecated.", "unit address and first address in 'reg' (0x1) don't match for /reg-zero-size-cells/node", "unit address and first address in 'reg' (0x5) don't match for /reg-ranges/parent/node", "unit address and first address in 'reg' (0x30000000200000001) don't match for /reg-nested-ranges/grandparent/parent/node", f"compatible 'enums' in binding '{enums_hpath}' has non-tokenizable enum for property 'string-enum': 'foo bar', 'foo_bar'", f"compatible 'enums' in binding '{enums_hpath}' has enum for property 'tokenizable-lower-enum' that is only tokenizable in lowercase: 'bar', 'BAR'", ] assert caplog.record_tuples == [('devicetree.edtlib', WARNING, warning_message) for warning_message in expected_warnings] def test_interrupts(): '''Tests for the interrupts property.''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) filenames = {i: hpath(f'test-bindings/interrupt-{i}-cell.yaml') for i in range(1, 4)} assert str(edt.get_node("/interrupt-parent-test/node").interrupts) == \ f"[, data: {{'one': 1, 'two': 2, 'three': 3}}>, , data: {{'one': 4, 'two': 5, 'three': 6}}>]" assert str(edt.get_node("/interrupts-extended-test/node").interrupts) == \ f"[, data: {{'one': 1}}>, , data: {{'one': 2, 'two': 3}}>, , data: {{'one': 4, 'two': 5, 'three': 6}}>]" assert str(edt.get_node("/interrupt-map-test/node@0").interrupts) == \ f"[, data: {{'one': 0}}>, , data: {{'one': 0, 'two': 1}}>, , data: {{'one': 0, 'two': 0, 'three': 2}}>]" assert str(edt.get_node("/interrupt-map-test/node@1").interrupts) == \ f"[, data: {{'one': 3}}>, , data: {{'one': 0, 'two': 4}}>, , data: {{'one': 0, 'two': 0, 'three': 5}}>]" assert str(edt.get_node("/interrupt-map-bitops-test/node@70000000E").interrupts) == \ f"[, data: {{'one': 3, 'two': 2}}>]" def test_ranges(): '''Tests for the ranges property''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) assert str(edt.get_node("/reg-ranges/parent").ranges) == \ "[, , ]" assert str(edt.get_node("/reg-nested-ranges/grandparent").ranges) == \ "[]" assert str(edt.get_node("/reg-nested-ranges/grandparent/parent").ranges) == \ "[]" assert str(edt.get_node("/ranges-zero-cells/node").ranges) == "[]" assert str(edt.get_node("/ranges-zero-parent-cells/node").ranges) == \ "[, , ]" assert str(edt.get_node("/ranges-one-address-cells/node").ranges) == \ "[, , ]" assert str(edt.get_node("/ranges-one-address-two-size-cells/node").ranges) == \ "[, , ]" assert str(edt.get_node("/ranges-two-address-cells/node@1").ranges) == \ "[, , ]" assert str(edt.get_node("/ranges-two-address-two-size-cells/node@1").ranges) == \ "[, , ]" assert str(edt.get_node("/ranges-three-address-cells/node@1").ranges) == \ "[, , ]" assert str(edt.get_node("/ranges-three-address-two-size-cells/node@1").ranges) == \ "[, , ]" def test_reg(): '''Tests for the regs property''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) assert str(edt.get_node("/reg-zero-address-cells/node").regs) == \ "[, ]" assert str(edt.get_node("/reg-zero-size-cells/node").regs) == \ "[, ]" assert str(edt.get_node("/reg-ranges/parent/node").regs) == \ "[, , , , , ]" assert str(edt.get_node("/reg-nested-ranges/grandparent/parent/node").regs) == \ "[]" def test_pinctrl(): '''Test 'pinctrl-'.''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) assert str(edt.get_node("/pinctrl/dev").pinctrls) == \ "[, ]>, , ]>]" def test_hierarchy(): '''Test Node.parent and Node.children''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) assert edt.get_node("/").parent is None assert str(edt.get_node("/parent/child-1").parent) == \ "" assert str(edt.get_node("/parent/child-2/grandchild").parent) == \ "" assert str(edt.get_node("/parent").children) == \ "{'child-1': , 'child-2': }" assert edt.get_node("/parent/child-1").children == {} def test_child_index(): '''Test Node.child_index.''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) parent, child_1, child_2 = [edt.get_node(path) for path in ("/parent", "/parent/child-1", "/parent/child-2")] assert parent.child_index(child_1) == 0 assert parent.child_index(child_2) == 1 with pytest.raises(KeyError): parent.child_index(parent) def test_include(): '''Test 'include:' and the legacy 'inherits: !include ...' in bindings''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) assert str(edt.get_node("/binding-include").description) == \ "Parent binding" assert str(edt.get_node("/binding-include").props) == ( "{" "'foo': , " "'bar': , " "'baz': , " "'qaz': " "}" ) assert str(edt.get_node("/binding-include/child").props) == ( "{" "'foo': , " "'bar': , " "'baz': , " "'qaz': " "}" ) def test_include_filters(): '''Test property-allowlist and property-blocklist in an include.''' fname2path = {'include.yaml': 'test-bindings-include/include.yaml', 'include-2.yaml': 'test-bindings-include/include-2.yaml'} with pytest.raises(edtlib.EDTError) as e: with from_here(): edtlib.Binding("test-bindings-include/allow-and-blocklist.yaml", fname2path) assert ("should not specify both 'property-allowlist:' and 'property-blocklist:'" in str(e.value)) with pytest.raises(edtlib.EDTError) as e: with from_here(): edtlib.Binding("test-bindings-include/allow-and-blocklist-child.yaml", fname2path) assert ("should not specify both 'property-allowlist:' and 'property-blocklist:'" in str(e.value)) with pytest.raises(edtlib.EDTError) as e: with from_here(): edtlib.Binding("test-bindings-include/allow-not-list.yaml", fname2path) value_str = str(e.value) assert value_str.startswith("'property-allowlist' value") assert value_str.endswith("should be a list") with pytest.raises(edtlib.EDTError) as e: with from_here(): edtlib.Binding("test-bindings-include/block-not-list.yaml", fname2path) value_str = str(e.value) assert value_str.startswith("'property-blocklist' value") assert value_str.endswith("should be a list") with pytest.raises(edtlib.EDTError) as e: with from_here(): binding = edtlib.Binding("test-bindings-include/include-invalid-keys.yaml", fname2path) value_str = str(e.value) assert value_str.startswith( "'include:' in test-bindings-include/include-invalid-keys.yaml should not have these " "unexpected contents: ") assert 'bad-key-1' in value_str assert 'bad-key-2' in value_str with pytest.raises(edtlib.EDTError) as e: with from_here(): binding = edtlib.Binding("test-bindings-include/include-invalid-type.yaml", fname2path) value_str = str(e.value) assert value_str.startswith( "'include:' in test-bindings-include/include-invalid-type.yaml " "should be a string or list, but has type ") with pytest.raises(edtlib.EDTError) as e: with from_here(): binding = edtlib.Binding("test-bindings-include/include-no-name.yaml", fname2path) value_str = str(e.value) assert value_str.startswith("'include:' element") assert value_str.endswith( "in test-bindings-include/include-no-name.yaml should have a 'name' key") with from_here(): binding = edtlib.Binding("test-bindings-include/allowlist.yaml", fname2path) assert set(binding.prop2specs.keys()) == {'x'} # 'x' is allowed binding = edtlib.Binding("test-bindings-include/empty-allowlist.yaml", fname2path) assert set(binding.prop2specs.keys()) == set() # nothing is allowed binding = edtlib.Binding("test-bindings-include/blocklist.yaml", fname2path) assert set(binding.prop2specs.keys()) == {'y', 'z'} # 'x' is blocked binding = edtlib.Binding("test-bindings-include/empty-blocklist.yaml", fname2path) assert set(binding.prop2specs.keys()) == {'x', 'y', 'z'} # nothing is blocked binding = edtlib.Binding("test-bindings-include/intermixed.yaml", fname2path) assert set(binding.prop2specs.keys()) == {'x', 'a'} binding = edtlib.Binding("test-bindings-include/include-no-list.yaml", fname2path) assert set(binding.prop2specs.keys()) == {'x', 'y', 'z'} binding = edtlib.Binding("test-bindings-include/filter-child-bindings.yaml", fname2path) child = binding.child_binding grandchild = child.child_binding assert set(binding.prop2specs.keys()) == {'x'} assert set(child.prop2specs.keys()) == {'child-prop-2'} assert set(grandchild.prop2specs.keys()) == {'grandchild-prop-1'} binding = edtlib.Binding("test-bindings-include/allow-and-blocklist-multilevel.yaml", fname2path) assert set(binding.prop2specs.keys()) == {'x'} # 'x' is allowed child = binding.child_binding assert set(child.prop2specs.keys()) == {'child-prop-1', 'child-prop-2', 'x', 'z'} # root level 'y' is blocked def test_bus(): '''Test 'bus:' and 'on-bus:' in bindings''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) assert isinstance(edt.get_node("/buses/foo-bus").buses, list) assert "foo" in edt.get_node("/buses/foo-bus").buses # foo-bus does not itself appear on a bus assert isinstance(edt.get_node("/buses/foo-bus").on_buses, list) assert not edt.get_node("/buses/foo-bus").on_buses assert edt.get_node("/buses/foo-bus").bus_node is None # foo-bus/node1 is not a bus node... assert isinstance(edt.get_node("/buses/foo-bus/node1").buses, list) assert not edt.get_node("/buses/foo-bus/node1").buses # ...but is on a bus assert isinstance(edt.get_node("/buses/foo-bus/node1").on_buses, list) assert "foo" in edt.get_node("/buses/foo-bus/node1").on_buses assert edt.get_node("/buses/foo-bus/node1").bus_node.path == \ "/buses/foo-bus" # foo-bus/node2 is not a bus node... assert isinstance(edt.get_node("/buses/foo-bus/node2").buses, list) assert not edt.get_node("/buses/foo-bus/node2").buses # ...but is on a bus assert isinstance(edt.get_node("/buses/foo-bus/node2").on_buses, list) assert "foo" in edt.get_node("/buses/foo-bus/node2").on_buses # no-bus-node is not a bus node... assert isinstance(edt.get_node("/buses/no-bus-node").buses, list) assert not edt.get_node("/buses/no-bus-node").buses # ... and is not on a bus assert isinstance(edt.get_node("/buses/no-bus-node").on_buses, list) assert not edt.get_node("/buses/no-bus-node").on_buses # Same compatible string, but different bindings from being on different # buses assert str(edt.get_node("/buses/foo-bus/node1").binding_path) == \ hpath("test-bindings/device-on-foo-bus.yaml") assert str(edt.get_node("/buses/foo-bus/node2").binding_path) == \ hpath("test-bindings/device-on-any-bus.yaml") assert str(edt.get_node("/buses/bar-bus/node").binding_path) == \ hpath("test-bindings/device-on-bar-bus.yaml") assert str(edt.get_node("/buses/no-bus-node").binding_path) == \ hpath("test-bindings/device-on-any-bus.yaml") # foo-bus/node/nested also appears on the foo-bus bus assert isinstance(edt.get_node("/buses/foo-bus/node1/nested").on_buses, list) assert "foo" in edt.get_node("/buses/foo-bus/node1/nested").on_buses assert str(edt.get_node("/buses/foo-bus/node1/nested").binding_path) == \ hpath("test-bindings/device-on-foo-bus.yaml") def test_child_binding(): '''Test 'child-binding:' in bindings''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) 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") assert str(child1.binding_path) == hpath("test-bindings/child-binding.yaml") assert str(child1.description) == "child node" assert str(child1.props) == "{'child-prop': }" assert str(child2.binding_path) == hpath("test-bindings/child-binding.yaml") assert str(child2.description) == "child node" assert str(child2.props) == "{'child-prop': }" assert str(grandchild.binding_path) == hpath("test-bindings/child-binding.yaml") assert str(grandchild.description) == "grandchild node" assert str(grandchild.props) == "{'grandchild-prop': }" with from_here(): binding_file = Path("test-bindings/child-binding.yaml").resolve() top = edtlib.Binding(binding_file, {}) child = top.child_binding assert Path(top.path) == binding_file assert Path(child.path) == binding_file assert top.compatible == 'top-binding' assert child.compatible is None with from_here(): binding_file = Path("test-bindings/child-binding-with-compat.yaml").resolve() top = edtlib.Binding(binding_file, {}) child = top.child_binding assert Path(top.path) == binding_file assert Path(child.path) == binding_file assert top.compatible == 'top-binding-with-compat' assert child.compatible == 'child-compat' def test_props(): '''Test Node.props (derived from DT and 'properties:' in the binding)''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) filenames = {i: hpath(f'test-bindings/phandle-array-controller-{i}.yaml') for i in range(0, 4)} assert str(edt.get_node("/props").props["int"]) == \ "" assert str(edt.get_node("/props").props["existent-boolean"]) == \ "" assert str(edt.get_node("/props").props["nonexistent-boolean"]) == \ "" assert str(edt.get_node("/props").props["array"]) == \ "" assert str(edt.get_node("/props").props["uint8-array"]) == \ r"" assert str(edt.get_node("/props").props["string"]) == \ "" assert str(edt.get_node("/props").props["string-array"]) == \ "" assert str(edt.get_node("/props").props["phandle-ref"]) == \ f">" assert str(edt.get_node("/props").props["phandle-refs"]) == \ f", ]>" assert str(edt.get_node("/props").props["phandle-array-foos"]) == \ f", data: {{'one': 1}}>, , data: {{'one': 2, 'two': 3}}>]>" assert str(edt.get_node("/props-2").props["phandle-array-foos"]) == \ (", data: {{}}>, " "None, " f", data: {{}}>]>") assert str(edt.get_node("/props").props["foo-gpios"]) == \ f", data: {{'gpio-one': 1}}>]>" assert str(edt.get_node("/props").props["path"]) == \ f">" def test_nexus(): '''Test -map via gpio-map (the most common case).''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) filename = hpath('test-bindings/gpio-dst.yaml') assert str(edt.get_node("/gpio-map/source").props["foo-gpios"]) == \ f", data: {{'val': 6}}>, , data: {{'val': 5}}>]>" assert str(edt.get_node("/gpio-map/source").props["foo-gpios"].val[0].basename) == f"gpio" def test_prop_defaults(): '''Test property default values given in bindings''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) assert str(edt.get_node("/defaults").props) == \ r"{'int': , 'array': , 'uint8-array': , 'string': , 'string-array': , 'default-not-used': }" def test_prop_enums(): '''test properties with enum: in the binding''' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"]) props = edt.get_node('/enums').props int_enum = props['int-enum'] string_enum = props['string-enum'] tokenizable_enum = props['tokenizable-enum'] tokenizable_lower_enum = props['tokenizable-lower-enum'] no_enum = props['no-enum'] assert int_enum.val == 1 assert int_enum.enum_index == 0 assert not int_enum.spec.enum_tokenizable assert not int_enum.spec.enum_upper_tokenizable assert string_enum.val == 'foo_bar' assert string_enum.enum_index == 1 assert not string_enum.spec.enum_tokenizable assert not string_enum.spec.enum_upper_tokenizable assert tokenizable_enum.val == '123 is ok' assert tokenizable_enum.val_as_token == '123_is_ok' assert tokenizable_enum.enum_index == 2 assert tokenizable_enum.spec.enum_tokenizable assert tokenizable_enum.spec.enum_upper_tokenizable assert tokenizable_lower_enum.val == 'bar' assert tokenizable_lower_enum.val_as_token == 'bar' assert tokenizable_lower_enum.enum_index == 0 assert tokenizable_lower_enum.spec.enum_tokenizable assert not tokenizable_lower_enum.spec.enum_upper_tokenizable assert no_enum.enum_index is None assert not no_enum.spec.enum_tokenizable assert not no_enum.spec.enum_upper_tokenizable def test_binding_inference(): '''Test inferred bindings for special zephyr-specific nodes.''' warnings = io.StringIO() with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"], warnings) assert str(edt.get_node("/zephyr,user").props) == '{}' with from_here(): edt = edtlib.EDT("test.dts", ["test-bindings"], warnings, infer_binding_for_paths=["/zephyr,user"]) filenames = {i: hpath(f'test-bindings/phandle-array-controller-{i}.yaml') for i in range(1, 3)} assert str(edt.get_node("/zephyr,user").props) == ( "{" "'boolean': , " r"'bytes': , " "'number': , " "'numbers': , " "'string': , " "'strings': , " f"'handle': >, " f"'phandles': , ]>, " f"'phandle-array-foos': , data: {{'one': 1, 'two': 2}}>]>" "}" ) def test_multi_bindings(): '''Test having multiple directories with bindings''' with from_here(): edt = edtlib.EDT("test-multidir.dts", ["test-bindings", "test-bindings-2"]) assert str(edt.get_node("/in-dir-1").binding_path) == \ hpath("test-bindings/multidir.yaml") assert str(edt.get_node("/in-dir-2").binding_path) == \ hpath("test-bindings-2/multidir.yaml") def test_dependencies(): ''''Test dependency relations''' with from_here(): edt = edtlib.EDT("test-multidir.dts", ["test-bindings", "test-bindings-2"]) assert edt.get_node("/").dep_ordinal == 0 assert edt.get_node("/in-dir-1").dep_ordinal == 1 assert edt.get_node("/") in edt.get_node("/in-dir-1").depends_on assert edt.get_node("/in-dir-1") in edt.get_node("/").required_by def test_slice_errs(tmp_path): '''Test error messages from the internal _slice() helper''' dts_file = tmp_path / "error.dts" verify_error(""" /dts-v1/; / { #address-cells = <1>; #size-cells = <2>; sub { reg = <3>; }; }; """, dts_file, f"'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>; }; }; """, dts_file, f"'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>; }; }; }; """, dts_file, f"'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').") def test_bad_compatible(tmp_path): # An invalid compatible should cause an error, even on a node with # no binding. dts_file = tmp_path / "error.dts" verify_error(""" /dts-v1/; / { foo { compatible = "no, whitespace"; }; }; """, dts_file, r"node '/foo' compatible 'no, whitespace' must match this regular expression: '^[a-zA-Z][a-zA-Z0-9,+\-._]+$'") def test_wrong_props(): '''Test Node.wrong_props (derived from DT and 'properties:' in the binding)''' with from_here(): with pytest.raises(edtlib.EDTError) as e: edtlib.Binding("test-wrong-bindings/wrong-specifier-space-type.yaml", None) assert ("'specifier-space' in 'properties: wrong-type-for-specifier-space' has type 'phandle', expected 'phandle-array'" in str(e.value)) with pytest.raises(edtlib.EDTError) as e: edtlib.Binding("test-wrong-bindings/wrong-phandle-array-name.yaml", None) value_str = str(e.value) assert value_str.startswith("'wrong-phandle-array-name' in 'properties:'") assert value_str.endswith("but no 'specifier-space' was provided.") def test_deepcopy(): with from_here(): # We intentionally use different kwarg values than the # defaults to make sure they're getting copied. This implies # we have to set werror=True, so we can't use test.dts, since # that generates warnings on purpose. edt = edtlib.EDT("test-multidir.dts", ["test-bindings", "test-bindings-2"], warn_reg_unit_address_mismatch=False, default_prop_types=False, support_fixed_partitions_on_any_bus=False, infer_binding_for_paths=['/test-node'], vendor_prefixes={'test-vnd': 'A test vendor'}, werror=True) edt_copy = deepcopy(edt) def equal_paths(list1, list2): assert len(list1) == len(list2) return all(elt1.path == elt2.path for elt1, elt2 in zip(list1, list2)) def equal_key2path(key2node1, key2node2): assert len(key2node1) == len(key2node2) return (all(key1 == key2 for (key1, key2) in zip(key2node1, key2node2)) and all(node1.path == node2.path for (node1, node2) in zip(key2node1.values(), key2node2.values()))) def equal_key2paths(key2nodes1, key2nodes2): assert len(key2nodes1) == len(key2nodes2) return (all(key1 == key2 for (key1, key2) in zip(key2nodes1, key2nodes2)) and all(equal_paths(nodes1, nodes2) for (nodes1, nodes2) in zip(key2nodes1.values(), key2nodes2.values()))) def test_equal_but_not_same(attribute, equal=None): if equal is None: equal = lambda a, b: a == b copy = getattr(edt_copy, attribute) original = getattr(edt, attribute) assert equal(copy, original) assert copy is not original test_equal_but_not_same("nodes", equal_paths) test_equal_but_not_same("compat2nodes", equal_key2paths) test_equal_but_not_same("compat2okay", equal_key2paths) test_equal_but_not_same("compat2vendor") test_equal_but_not_same("compat2model") test_equal_but_not_same("label2node", equal_key2path) test_equal_but_not_same("dep_ord2node", equal_key2path) assert edt_copy.dts_path == "test-multidir.dts" assert edt_copy.bindings_dirs == ["test-bindings", "test-bindings-2"] assert edt_copy.bindings_dirs is not edt.bindings_dirs assert not edt_copy._warn_reg_unit_address_mismatch assert not edt_copy._default_prop_types assert not edt_copy._fixed_partitions_no_bus assert edt_copy._infer_binding_for_paths == set(["/test-node"]) assert edt_copy._infer_binding_for_paths is not edt._infer_binding_for_paths assert edt_copy._vendor_prefixes == {"test-vnd": "A test vendor"} assert edt_copy._vendor_prefixes is not edt._vendor_prefixes assert edt_copy._werror test_equal_but_not_same("_compat2binding", equal_key2path) test_equal_but_not_same("_binding_paths") test_equal_but_not_same("_binding_fname2path") assert len(edt_copy._node2enode) == len(edt._node2enode) for node1, node2 in zip(edt_copy._node2enode, edt._node2enode): enode1 = edt_copy._node2enode[node1] enode2 = edt._node2enode[node2] assert node1.path == node2.path assert enode1.path == enode2.path assert node1 is not node2 assert enode1 is not enode2 assert edt_copy._dt is not edt._dt def verify_error(dts, dts_file, expected_err): # Verifies that parsing a file 'dts_file' with the contents 'dts' # (a string) raises an EDTError with the message 'expected_err'. # # The path 'dts_file' is written with the string 'dts' before the # test is run. with open(dts_file, "w", encoding="utf-8") as f: f.write(dts) f.flush() # Can't have unbuffered text IO, so flush() instead with pytest.raises(edtlib.EDTError) as e: edtlib.EDT(dts_file, []) assert str(e.value) == expected_err