scripts: dts: convert test suites to pytest
Use the pytest test framework in the dtlib.py and edtlib.py test suites (testdtlib.py and testedtlib.py respectively). The goal here is not to change what is being tested. The existing test suite is excellent and very thorough. However, it is made up of executable scripts where all of the tests are run using a hand-rolled framework in a single function per file. This is a bit all-or-nothing and prevents various nice features available in the de-facto standard pytest test framework from being used. In particular, pytest can: - drop into a debugger (pdb) when there is a problem - accept a pattern which specifies a subset of tests to run - print very detailed error messages about the actual and expected results in various traceback formats from brief to very verbose - gather coverage data for the python scripts being tested (via plugin) - run tests in parallel (via plugin) - It's easy in pytest to run tests with temporary directories using the tmp_path and other fixtures. This us avoid temporarily dirtying the working tree as is done now. Moving to pytest lets us leverage all of these things without any loss in ease of use (in fact, some things are nicer in pytest): - Any function that starts with "test_" is automatically picked up and run. No need for rolling up lists of functions into a test suite. - Tests are written using ordinary Python 'assert' statements. - Pytest magic unpacks the AST of failed asserts to print details on what went wrong in really nice ways. For example, it will show you exactly what parts of two strings that are expected to be equal differ. For the most part, this is a pretty mechanical conversion: - extract helpers and test cases into separate functions - insert temporary paths and adjust tests accordingly to not match file names exactly - use 'assert CONDITION' instead of 'if not CONDITION: fail()' There are a few cases where making this happen required slightly larger changes than that, but they are limited. Move the checks from check_compliance.py to a new GitHub workflow, removing hacks that are no longer needed. Signed-off-by: Martí Bolívar <marti.bolivar@nordicsemi.no>
This commit is contained in:
parent
0e7a76e462
commit
a8612f75c5
53
.github/workflows/devicetree_checks.yml
vendored
Normal file
53
.github/workflows/devicetree_checks.yml
vendored
Normal file
|
@ -0,0 +1,53 @@
|
|||
# Copyright (c) 2020 Linaro Limited.
|
||||
# Copyright (c) 2020 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
name: Devicetree script tests
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'scripts/dts/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'scripts/dts/**'
|
||||
|
||||
jobs:
|
||||
build:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
matrix:
|
||||
python-version: [3.6, 3.7, 3.8]
|
||||
os: [ubuntu-latest, macos-latest]
|
||||
steps:
|
||||
- name: checkout
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v1
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: cache-pip-linux
|
||||
if: startsWith(runner.os, 'Linux')
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/.cache/pip
|
||||
key: ${{ runner.os }}-pip-${{ matrix.python-version }}
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-${{ matrix.python-version }}
|
||||
- name: cache-pip-mac
|
||||
if: startsWith(runner.os, 'macOS')
|
||||
uses: actions/cache@v1
|
||||
with:
|
||||
path: ~/Library/Caches/pip
|
||||
# Trailing '-' was just to get a different cache name
|
||||
key: ${{ runner.os }}-pip-${{ matrix.python-version }}-
|
||||
restore-keys: |
|
||||
${{ runner.os }}-pip-${{ matrix.python-version }}-
|
||||
- name: install python dependencies
|
||||
run: |
|
||||
pip3 install wheel
|
||||
pip3 install pytest pyyaml
|
||||
- name: run pytest
|
||||
working-directory: scripts/dts
|
||||
run: |
|
||||
python -m pytest testdtlib.py testedtlib.py
|
|
@ -550,51 +550,6 @@ UNDEF_KCONFIG_WHITELIST = {
|
|||
}
|
||||
|
||||
|
||||
class DeviceTreeCheck(ComplianceTest):
|
||||
"""
|
||||
Runs the dtlib and edtlib test suites in scripts/dts/.
|
||||
"""
|
||||
name = "Devicetree"
|
||||
doc = "See https://docs.zephyrproject.org/latest/guides/dts/index.html for more details"
|
||||
path_hint = ZEPHYR_BASE
|
||||
|
||||
def run(self):
|
||||
if not ZEPHYR_BASE:
|
||||
self.skip("Not a Zephyr tree (ZEPHYR_BASE unset)")
|
||||
|
||||
scripts_path = os.path.join(ZEPHYR_BASE, "scripts", "dts")
|
||||
|
||||
sys.path.insert(0, scripts_path)
|
||||
import testdtlib
|
||||
import testedtlib
|
||||
|
||||
# Hack: The test suites expect to be run from the scripts/dts
|
||||
# directory, because they compare repr() output that contains relative
|
||||
# paths against an expected string. Temporarily change the working
|
||||
# directory to scripts/dts/.
|
||||
#
|
||||
# Warning: This is not thread-safe, though the test suites run in a
|
||||
# fraction of a second.
|
||||
old_dir = os.getcwd()
|
||||
os.chdir(scripts_path)
|
||||
try:
|
||||
logger.info("cd %s && ./testdtlib.py", scripts_path)
|
||||
testdtlib.run()
|
||||
logger.info("cd %s && ./testedtlib.py", scripts_path)
|
||||
testedtlib.run()
|
||||
except SystemExit as e:
|
||||
# The dtlib and edtlib test suites call sys.exit() on failure,
|
||||
# which raises SystemExit. Let any errors in the test scripts
|
||||
# themselves trickle through and turn into an internal CI error.
|
||||
self.add_failure(str(e))
|
||||
except Exception as e:
|
||||
# Report other exceptions as an internal test failure
|
||||
self.error(str(e))
|
||||
finally:
|
||||
# Restore working directory
|
||||
os.chdir(old_dir)
|
||||
|
||||
|
||||
class Codeowners(ComplianceTest):
|
||||
"""
|
||||
Check if added files have an owner.
|
||||
|
|
|
@ -23,11 +23,7 @@ import re
|
|||
import sys
|
||||
import textwrap
|
||||
|
||||
# NOTE: testdtlib.py is the test suite for this library. It can be run directly
|
||||
# as a script:
|
||||
#
|
||||
# ./testdtlib.py
|
||||
|
||||
# NOTE: testdtlib.py is the test suite for this library.
|
||||
|
||||
class DT:
|
||||
"""
|
||||
|
|
|
@ -22,10 +22,7 @@ The top-level entry point of the library is the EDT class. EDT.__init__() takes
|
|||
a .dts file to parse and a list of paths to directories containing bindings.
|
||||
"""
|
||||
|
||||
# NOTE: testedtlib.py is the test suite for this library. It can be run
|
||||
# directly as a script:
|
||||
#
|
||||
# ./testedtlib.py
|
||||
# NOTE: testedtlib.py is the test suite for this library.
|
||||
|
||||
# Implementation notes
|
||||
# --------------------
|
||||
|
|
1116
scripts/dts/testdtlib.py
Executable file → Normal file
1116
scripts/dts/testdtlib.py
Executable file → Normal file
File diff suppressed because it is too large
Load diff
382
scripts/dts/testedtlib.py
Executable file → Normal file
382
scripts/dts/testedtlib.py
Executable file → Normal file
|
@ -1,258 +1,251 @@
|
|||
#!/usr/bin/env python3
|
||||
|
||||
# Copyright (c) 2019 Nordic Semiconductor ASA
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
|
||||
import io
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pytest
|
||||
|
||||
import edtlib
|
||||
|
||||
# Test suite for edtlib.py. Run it directly as an executable, in this
|
||||
# directory:
|
||||
# Test suite for edtlib.py.
|
||||
#
|
||||
# $ ./testedtlib.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.
|
||||
|
||||
|
||||
def run():
|
||||
"""
|
||||
Runs all edtlib tests. Immediately exits with status 1 and a message on
|
||||
stderr on test suite failures.
|
||||
"""
|
||||
|
||||
def test_warnings():
|
||||
'''Tests for situations that should cause warnings.'''
|
||||
warnings = io.StringIO()
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"], warnings)
|
||||
edtlib.EDT("test.dts", ["test-bindings"], warnings)
|
||||
|
||||
# Verify warnings
|
||||
verify_eq(warnings.getvalue(), """\
|
||||
assert 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
|
||||
#
|
||||
def test_interrupts():
|
||||
'''Tests for the interrupts property.'''
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"])
|
||||
|
||||
verify_streq(edt.get_node("/interrupt-parent-test/node").interrupts,
|
||||
"[<ControllerAndData, name: foo, controller: <Node /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: OrderedDict([('one', 1), ('two', 2), ('three', 3)])>, <ControllerAndData, name: bar, controller: <Node /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: OrderedDict([('one', 4), ('two', 5), ('three', 6)])>]")
|
||||
assert str(edt.get_node("/interrupt-parent-test/node").interrupts) == \
|
||||
"[<ControllerAndData, name: foo, controller: <Node /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: OrderedDict([('one', 1), ('two', 2), ('three', 3)])>, <ControllerAndData, name: bar, controller: <Node /interrupt-parent-test/controller in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: OrderedDict([('one', 4), ('two', 5), ('three', 6)])>]"
|
||||
|
||||
verify_streq(edt.get_node("/interrupts-extended-test/node").interrupts,
|
||||
"[<ControllerAndData, controller: <Node /interrupts-extended-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: OrderedDict([('one', 1)])>, <ControllerAndData, controller: <Node /interrupts-extended-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: OrderedDict([('one', 2), ('two', 3)])>, <ControllerAndData, controller: <Node /interrupts-extended-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: OrderedDict([('one', 4), ('two', 5), ('three', 6)])>]")
|
||||
assert str(edt.get_node("/interrupts-extended-test/node").interrupts) == \
|
||||
"[<ControllerAndData, controller: <Node /interrupts-extended-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: OrderedDict([('one', 1)])>, <ControllerAndData, controller: <Node /interrupts-extended-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: OrderedDict([('one', 2), ('two', 3)])>, <ControllerAndData, controller: <Node /interrupts-extended-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: OrderedDict([('one', 4), ('two', 5), ('three', 6)])>]"
|
||||
|
||||
verify_streq(edt.get_node("/interrupt-map-test/node@0").interrupts,
|
||||
"[<ControllerAndData, controller: <Node /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: OrderedDict([('one', 0)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: OrderedDict([('one', 0), ('two', 1)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: OrderedDict([('one', 0), ('two', 0), ('three', 2)])>]")
|
||||
assert str(edt.get_node("/interrupt-map-test/node@0").interrupts) == \
|
||||
"[<ControllerAndData, controller: <Node /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: OrderedDict([('one', 0)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: OrderedDict([('one', 0), ('two', 1)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: OrderedDict([('one', 0), ('two', 0), ('three', 2)])>]"
|
||||
|
||||
verify_streq(edt.get_node("/interrupt-map-test/node@1").interrupts,
|
||||
"[<ControllerAndData, controller: <Node /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: OrderedDict([('one', 3)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: OrderedDict([('one', 0), ('two', 4)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: OrderedDict([('one', 0), ('two', 0), ('three', 5)])>]")
|
||||
assert str(edt.get_node("/interrupt-map-test/node@1").interrupts) == \
|
||||
"[<ControllerAndData, controller: <Node /interrupt-map-test/controller-0 in 'test.dts', binding test-bindings/interrupt-1-cell.yaml>, data: OrderedDict([('one', 3)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-1 in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: OrderedDict([('one', 0), ('two', 4)])>, <ControllerAndData, controller: <Node /interrupt-map-test/controller-2 in 'test.dts', binding test-bindings/interrupt-3-cell.yaml>, data: OrderedDict([('one', 0), ('two', 0), ('three', 5)])>]"
|
||||
|
||||
verify_streq(edt.get_node("/interrupt-map-bitops-test/node@70000000E").interrupts,
|
||||
"[<ControllerAndData, controller: <Node /interrupt-map-bitops-test/controller in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: OrderedDict([('one', 3), ('two', 2)])>]")
|
||||
assert str(edt.get_node("/interrupt-map-bitops-test/node@70000000E").interrupts) == \
|
||||
"[<ControllerAndData, controller: <Node /interrupt-map-bitops-test/controller in 'test.dts', binding test-bindings/interrupt-2-cell.yaml>, data: OrderedDict([('one', 3), ('two', 2)])>]"
|
||||
|
||||
#
|
||||
# Test 'reg'
|
||||
#
|
||||
def test_reg():
|
||||
'''Tests for the regs property'''
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"])
|
||||
|
||||
verify_streq(edt.get_node("/reg-zero-address-cells/node").regs,
|
||||
"[<Register, size: 0x1>, <Register, size: 0x2>]")
|
||||
assert str(edt.get_node("/reg-zero-address-cells/node").regs) == \
|
||||
"[<Register, size: 0x1>, <Register, size: 0x2>]"
|
||||
|
||||
verify_streq(edt.get_node("/reg-zero-size-cells/node").regs,
|
||||
"[<Register, addr: 0x1>, <Register, addr: 0x2>]")
|
||||
assert str(edt.get_node("/reg-zero-size-cells/node").regs) == \
|
||||
"[<Register, addr: 0x1>, <Register, addr: 0x2>]"
|
||||
|
||||
verify_streq(edt.get_node("/reg-ranges/parent/node").regs,
|
||||
"[<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>]")
|
||||
assert str(edt.get_node("/reg-ranges/parent/node").regs) == \
|
||||
"[<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>]"
|
||||
|
||||
verify_streq(edt.get_node("/reg-nested-ranges/grandparent/parent/node").regs,
|
||||
"[<Register, addr: 0x30000000200000001, size: 0x1>]")
|
||||
assert str(edt.get_node("/reg-nested-ranges/grandparent/parent/node").regs) == \
|
||||
"[<Register, addr: 0x30000000200000001, size: 0x1>]"
|
||||
|
||||
#
|
||||
# Test 'pinctrl-<index>'
|
||||
#
|
||||
def test_pinctrl():
|
||||
'''Test 'pinctrl-<index>'.'''
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"])
|
||||
|
||||
verify_streq(edt.get_node("/pinctrl/dev").pinctrls,
|
||||
"[<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>]>]")
|
||||
assert str(edt.get_node("/pinctrl/dev").pinctrls) == \
|
||||
"[<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>]>]"
|
||||
|
||||
#
|
||||
# Test Node.parent and Node.children
|
||||
#
|
||||
def test_hierarchy():
|
||||
'''Test Node.parent and Node.children'''
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"])
|
||||
|
||||
verify_eq(edt.get_node("/").parent, None)
|
||||
assert edt.get_node("/").parent is None
|
||||
|
||||
verify_streq(edt.get_node("/parent/child-1").parent,
|
||||
"<Node /parent in 'test.dts', no binding>")
|
||||
assert str(edt.get_node("/parent/child-1").parent) == \
|
||||
"<Node /parent in 'test.dts', no binding>"
|
||||
|
||||
verify_streq(edt.get_node("/parent/child-2/grandchild").parent,
|
||||
"<Node /parent/child-2 in 'test.dts', no binding>")
|
||||
assert str(edt.get_node("/parent/child-2/grandchild").parent) == \
|
||||
"<Node /parent/child-2 in 'test.dts', no binding>"
|
||||
|
||||
verify_streq(edt.get_node("/parent").children,
|
||||
"OrderedDict([('child-1', <Node /parent/child-1 in 'test.dts', no binding>), ('child-2', <Node /parent/child-2 in 'test.dts', no binding>)])")
|
||||
assert str(edt.get_node("/parent").children) == \
|
||||
"OrderedDict([('child-1', <Node /parent/child-1 in 'test.dts', no binding>), ('child-2', <Node /parent/child-2 in 'test.dts', no binding>)])"
|
||||
|
||||
verify_eq(edt.get_node("/parent/child-1").children, {})
|
||||
assert edt.get_node("/parent/child-1").children == {}
|
||||
|
||||
#
|
||||
# Test 'include:' and the legacy 'inherits: !include ...'
|
||||
#
|
||||
def test_include():
|
||||
'''Test 'include:' and the legacy 'inherits: !include ...' in bindings'''
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"])
|
||||
|
||||
verify_streq(edt.get_node("/binding-include").description,
|
||||
"Parent binding")
|
||||
assert str(edt.get_node("/binding-include").description) == \
|
||||
"Parent binding"
|
||||
|
||||
verify_streq(edt.get_node("/binding-include").props,
|
||||
"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>)])")
|
||||
assert str(edt.get_node("/binding-include").props) == \
|
||||
"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>)])"
|
||||
|
||||
#
|
||||
# Test 'bus:' and 'on-bus:'
|
||||
#
|
||||
def test_bus():
|
||||
'''Test 'bus:' and 'on-bus:' in bindings'''
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"])
|
||||
|
||||
assert edt.get_node("/buses/foo-bus").bus == "foo"
|
||||
|
||||
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)
|
||||
assert edt.get_node("/buses/foo-bus").on_bus is None
|
||||
assert edt.get_node("/buses/foo-bus").bus_node is None
|
||||
|
||||
# foo-bus/node is not a bus node...
|
||||
verify_eq(edt.get_node("/buses/foo-bus/node").bus, None)
|
||||
assert edt.get_node("/buses/foo-bus/node").bus is 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")
|
||||
assert edt.get_node("/buses/foo-bus/node").on_bus == "foo"
|
||||
assert 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")
|
||||
assert str(edt.get_node("/buses/foo-bus/node").binding_path) == \
|
||||
"test-bindings/device-on-foo-bus.yaml"
|
||||
assert str(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:'
|
||||
#
|
||||
assert edt.get_node("/buses/foo-bus/node/nested").on_bus == "foo"
|
||||
assert str(edt.get_node("/buses/foo-bus/node/nested").binding_path) == \
|
||||
"test-bindings/device-on-foo-bus.yaml"
|
||||
|
||||
def test_child_binding():
|
||||
'''Test 'child-binding:' in bindings'''
|
||||
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")
|
||||
|
||||
verify_streq(child1.binding_path, "test-bindings/child-binding.yaml")
|
||||
verify_streq(child1.description, "child node")
|
||||
verify_streq(child1.props, "OrderedDict([('child-prop', <Property, name: child-prop, type: int, value: 1>)])")
|
||||
assert str(child1.binding_path) == "test-bindings/child-binding.yaml"
|
||||
assert str(child1.description) == "child node"
|
||||
assert str(child1.props) == "OrderedDict([('child-prop', <Property, name: child-prop, type: int, value: 1>)])"
|
||||
|
||||
verify_streq(child2.binding_path, "test-bindings/child-binding.yaml")
|
||||
verify_streq(child2.description, "child node")
|
||||
verify_streq(child2.props, "OrderedDict([('child-prop', <Property, name: child-prop, type: int, value: 3>)])")
|
||||
assert str(child2.binding_path) == "test-bindings/child-binding.yaml"
|
||||
assert str(child2.description) == "child node"
|
||||
assert str(child2.props) == "OrderedDict([('child-prop', <Property, name: child-prop, type: int, value: 3>)])"
|
||||
|
||||
verify_streq(grandchild.binding_path, "test-bindings/child-binding.yaml")
|
||||
verify_streq(grandchild.description, "grandchild node")
|
||||
verify_streq(grandchild.props, "OrderedDict([('grandchild-prop', <Property, name: grandchild-prop, type: int, value: 2>)])")
|
||||
assert str(grandchild.binding_path) == "test-bindings/child-binding.yaml"
|
||||
assert str(grandchild.description) == "grandchild node"
|
||||
assert str(grandchild.props) == "OrderedDict([('grandchild-prop', <Property, name: grandchild-prop, type: int, value: 2>)])"
|
||||
|
||||
#
|
||||
# Test EDT.compat2enabled
|
||||
#
|
||||
def test_compat2enabled():
|
||||
'''Test EDT.compat2enabled'''
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"])
|
||||
|
||||
verify_streq(edt.compat2enabled["compat2enabled"], "[<Node /compat2enabled/foo-1 in 'test.dts', no binding>, <Node /compat2enabled/foo-2 in 'test.dts', no binding>]")
|
||||
assert str(edt.compat2enabled["compat2enabled"]) == \
|
||||
"[<Node /compat2enabled/foo-1 in 'test.dts', no binding>, <Node /compat2enabled/foo-2 in 'test.dts', no binding>]"
|
||||
|
||||
if "compat2enabled-disabled" in edt.compat2enabled:
|
||||
fail("'compat2enabled-disabled' should not appear in edt.compat2enabled")
|
||||
assert "compat2enabled-disabled" not in edt.compat2enabled
|
||||
|
||||
#
|
||||
# Test Node.props (derived from DT and 'properties:' in the binding)
|
||||
#
|
||||
def test_props():
|
||||
'''Test Node.props (derived from DT and 'properties:' in the binding)'''
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"])
|
||||
|
||||
verify_streq(edt.get_node("/props").props["int"],
|
||||
"<Property, name: int, type: int, value: 1>")
|
||||
assert str(edt.get_node("/props").props["int"]) == \
|
||||
"<Property, name: int, type: int, value: 1>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["existent-boolean"],
|
||||
"<Property, name: existent-boolean, type: boolean, value: True>")
|
||||
assert str(edt.get_node("/props").props["existent-boolean"]) == \
|
||||
"<Property, name: existent-boolean, type: boolean, value: True>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["nonexistent-boolean"],
|
||||
"<Property, name: nonexistent-boolean, type: boolean, value: False>")
|
||||
assert str(edt.get_node("/props").props["nonexistent-boolean"]) == \
|
||||
"<Property, name: nonexistent-boolean, type: boolean, value: False>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["array"],
|
||||
"<Property, name: array, type: array, value: [1, 2, 3]>")
|
||||
assert str(edt.get_node("/props").props["array"]) == \
|
||||
"<Property, name: array, type: array, value: [1, 2, 3]>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["uint8-array"],
|
||||
r"<Property, name: uint8-array, type: uint8-array, value: b'\x124'>")
|
||||
assert str(edt.get_node("/props").props["uint8-array"]) == \
|
||||
r"<Property, name: uint8-array, type: uint8-array, value: b'\x124'>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["string"],
|
||||
"<Property, name: string, type: string, value: 'foo'>")
|
||||
assert str(edt.get_node("/props").props["string"]) == \
|
||||
"<Property, name: string, type: string, value: 'foo'>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["string-array"],
|
||||
"<Property, name: string-array, type: string-array, value: ['foo', 'bar', 'baz']>")
|
||||
assert str(edt.get_node("/props").props["string-array"]) == \
|
||||
"<Property, name: string-array, type: string-array, value: ['foo', 'bar', 'baz']>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["phandle-ref"],
|
||||
"<Property, name: phandle-ref, type: phandle, value: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>>")
|
||||
assert str(edt.get_node("/props").props["phandle-ref"]) == \
|
||||
"<Property, name: phandle-ref, type: phandle, value: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["phandle-refs"],
|
||||
"<Property, name: phandle-refs, type: phandles, value: [<Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>]>")
|
||||
assert str(edt.get_node("/props").props["phandle-refs"]) == \
|
||||
"<Property, name: phandle-refs, type: phandles, value: [<Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>]>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["phandle-array-foos"],
|
||||
"<Property, name: phandle-array-foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, data: OrderedDict([('one', 1)])>, <ControllerAndData, controller: <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>, data: OrderedDict([('one', 2), ('two', 3)])>]>")
|
||||
assert str(edt.get_node("/props").props["phandle-array-foos"]) == \
|
||||
"<Property, name: phandle-array-foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, data: OrderedDict([('one', 1)])>, <ControllerAndData, controller: <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>, data: OrderedDict([('one', 2), ('two', 3)])>]>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["foo-gpios"],
|
||||
"<Property, name: foo-gpios, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, data: OrderedDict([('gpio-one', 1)])>]>")
|
||||
assert str(edt.get_node("/props").props["foo-gpios"]) == \
|
||||
"<Property, name: foo-gpios, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, data: OrderedDict([('gpio-one', 1)])>]>"
|
||||
|
||||
verify_streq(edt.get_node("/props").props["path"],
|
||||
"<Property, name: path, type: path, value: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>>")
|
||||
assert str(edt.get_node("/props").props["path"]) == \
|
||||
"<Property, name: path, type: path, value: <Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>>"
|
||||
|
||||
#
|
||||
# Test <prefix>-map, via gpio-map (the most common case)
|
||||
#
|
||||
def test_nexus():
|
||||
'''Test <prefix>-map via gpio-map (the most common case).'''
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"])
|
||||
|
||||
verify_streq(edt.get_node("/gpio-map/source").props["foo-gpios"],
|
||||
"<Property, name: foo-gpios, type: phandle-array, value: [<ControllerAndData, controller: <Node /gpio-map/destination in 'test.dts', binding test-bindings/gpio-dst.yaml>, data: OrderedDict([('val', 6)])>, <ControllerAndData, controller: <Node /gpio-map/destination in 'test.dts', binding test-bindings/gpio-dst.yaml>, data: OrderedDict([('val', 5)])>]>")
|
||||
assert str(edt.get_node("/gpio-map/source").props["foo-gpios"]) == \
|
||||
"<Property, name: foo-gpios, type: phandle-array, value: [<ControllerAndData, controller: <Node /gpio-map/destination in 'test.dts', binding test-bindings/gpio-dst.yaml>, data: OrderedDict([('val', 6)])>, <ControllerAndData, controller: <Node /gpio-map/destination in 'test.dts', binding test-bindings/gpio-dst.yaml>, data: OrderedDict([('val', 5)])>]>"
|
||||
|
||||
#
|
||||
# Test property default values given in bindings
|
||||
#
|
||||
def test_prop_defaults():
|
||||
'''Test property default values given in bindings'''
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"])
|
||||
|
||||
verify_streq(edt.get_node("/defaults").props,
|
||||
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>)])")
|
||||
assert str(edt.get_node("/defaults").props) == \
|
||||
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>)])"
|
||||
|
||||
#
|
||||
# Test binding inference
|
||||
#
|
||||
def test_binding_inference():
|
||||
'''Test inferred bindings for special zephyr-specific nodes.'''
|
||||
warnings = io.StringIO()
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"], warnings)
|
||||
|
||||
verify_streq(edt.get_node("/zephyr,user").props, r"OrderedDict()")
|
||||
assert str(edt.get_node("/zephyr,user").props) == r"OrderedDict()"
|
||||
|
||||
edt = edtlib.EDT("test.dts", ["test-bindings"], warnings,
|
||||
infer_binding_for_paths=["/zephyr,user"])
|
||||
|
||||
verify_streq(edt.get_node("/zephyr,user").props,
|
||||
r"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 /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>>), ('phandles', <Property, name: phandles, type: phandles, value: [<Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>]>), ('phandle-array-foos', <Property, name: phandle-array-foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>, data: OrderedDict([('one', 1), ('two', 2)])>]>)])")
|
||||
|
||||
#
|
||||
# Test having multiple directories with bindings, with a different .dts file
|
||||
#
|
||||
assert str(edt.get_node("/zephyr,user").props) == \
|
||||
r"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 /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>>), ('phandles', <Property, name: phandles, type: phandles, value: [<Node /props/ctrl-1 in 'test.dts', binding test-bindings/phandle-array-controller-1.yaml>, <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>]>), ('phandle-array-foos', <Property, name: phandle-array-foos, type: phandle-array, value: [<ControllerAndData, controller: <Node /props/ctrl-2 in 'test.dts', binding test-bindings/phandle-array-controller-2.yaml>, data: OrderedDict([('one', 1), ('two', 2)])>]>)])"
|
||||
|
||||
def test_multi_bindings():
|
||||
'''Test having multiple directories with bindings'''
|
||||
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")
|
||||
assert str(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")
|
||||
assert str(edt.get_node("/in-dir-2").binding_path) == \
|
||||
"test-bindings-2/multidir.yaml"
|
||||
|
||||
#
|
||||
# Test dependency relations
|
||||
#
|
||||
def test_dependencies():
|
||||
''''Test dependency relations'''
|
||||
edt = edtlib.EDT("test-multidir.dts", ["test-bindings", "test-bindings-2"])
|
||||
|
||||
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 /")
|
||||
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
|
||||
|
||||
#
|
||||
# Test error messages from _slice()
|
||||
#
|
||||
def test_slice_errs(tmp_path):
|
||||
'''Test error messages from the internal _slice() helper'''
|
||||
|
||||
dts_file = tmp_path / "error.dts"
|
||||
|
||||
verify_error("""
|
||||
/dts-v1/;
|
||||
|
@ -265,7 +258,9 @@ warning: unit address and first address in 'reg' (0x30000000200000001) don't mat
|
|||
reg = <3>;
|
||||
};
|
||||
};
|
||||
""", "'reg' property in <Node /sub in 'error.dts'> 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').")
|
||||
""",
|
||||
dts_file,
|
||||
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').")
|
||||
|
||||
verify_error("""
|
||||
/dts-v1/;
|
||||
|
@ -280,7 +275,9 @@ warning: unit address and first address in 'reg' (0x30000000200000001) don't mat
|
|||
#interrupt-cells = <2>;
|
||||
};
|
||||
};
|
||||
""", "'interrupts' property in <Node /sub in 'error.dts'> 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').")
|
||||
""",
|
||||
dts_file,
|
||||
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').")
|
||||
|
||||
verify_error("""
|
||||
/dts-v1/;
|
||||
|
@ -298,49 +295,22 @@ warning: unit address and first address in 'reg' (0x30000000200000001) don't mat
|
|||
};
|
||||
};
|
||||
};
|
||||
""", "'ranges' property in <Node /sub-1 in 'error.dts'> 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').")
|
||||
""",
|
||||
dts_file,
|
||||
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').")
|
||||
|
||||
print("all tests passed")
|
||||
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.
|
||||
|
||||
|
||||
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:
|
||||
with open(dts_file, "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")
|
||||
with pytest.raises(edtlib.EDTError) as e:
|
||||
edtlib.EDT(dts_file, [])
|
||||
|
||||
|
||||
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()
|
||||
assert str(e.value) == expected_err
|
||||
|
|
Loading…
Reference in a new issue