The python test framework is built on top of the ChipDeviceCtrl.py python controller API and the Mobly test framework. Python tests are interaction tests, and can be used for certification testing, and / or integration testing in the CI.
Python tests located in src/python_testing
class TC_MYTEST_1_1(MatterBaseTest): @async_test_body async def test_TC_MYTEST_1_1(self): vendor_name = await self.read_single_attribute_check_success( dev_ctrl=self.default_controller, <span style="color:#38761D"># defaults to self.default_controlller</span> node_id = self.dut_node_id, <span style="color:#38761D"># defaults to self.dut_node_id</span> cluster=Clusters.BasicInformation, attribute=Clusters.BasicInformation.Attributes.VendorName, endpoint = 0, <span style="color:#38761D">#defaults to 0</span> ) asserts.assert_equal(vendor_name, “Test vendor name”, “Unexpected vendor name”) if __name__ == "__main__": default_matter_test_main()
In this test, asserts.assert_equal is used to fail the test on condition failure (throws an exception).
Because the test requires the use of the async function read_single_attribute_check_success, the test is decorated with the @async_test_body
decorator
The default_matter_test_main() function is used to run the test on the command line. These two lines should appear verbatim at the bottom of every python test file.
Common import used in test files: import chip.clusters as Clusters
Each cluster is defined in the Clusters.<ClusterName>
namespace and contains always:
Each Clusters.<ClusterName>
will include the appropriate sub-classes (if defined for the cluster):
Attributes derive from ClusterAttributeDescriptor
Each Clusters.<ClusterName>.Attributes.<AttributeName>
class has:
Example:
Commands derive from ClusterCommand
Each Clusters.<ClusterName>.Commands.<CommandName>
class has:
Example:
Events derive from ClusterEvent
Each Clusters.<ClusterName>.Events.<EventName>
class has:
Example:
Enums derive from MatterIntEnum
Each Clusters.<ClusterName>.Enum.<EnumName>
has
Example:
Bitmaps derive from IntFlag
Each Clusters.<ClusterName>.Bitmaps.<BitmapName>
has: - k
Special class:
Example:
Structs derive from ClusterObject
Each Clusters.<ClusterName>.Structs.<StructName>
has:
Example
ClusterObjects.py has a set of objects that map ID to the code generated object.
chip.clusters.ClusterObjects.ALL_CLUSTERS
chip.clusters.ClusterObjects.ALL_ATTRIBUTES
chip.clusters.ClusterObjects.ALL_ACCEPTED_COMMANDS/ALL_GENERATED_COMMANDS
The ChipDeviceCtrl API is implemented in ChipDeviceCtrl.py.
The ChipDeviceCtrl implements a python-based controller that can be used to commission and control devices. The API is documented here in the ChipDeviceCtrl API documentation
The API doc gives full descriptions of the APIs being used. The most commonly used functions are linked below
Examples: Wildcard read (all clusters, all endpoints):
await dev_ctrl.ReadAttribute(node_id, [()])
Wildcard read (single endpoint 0)
await dev_ctrl.ReadAttribute(node_id, [(0)])
Wildcard read (single cluster from single endpoint 0)
await dev_ctrl.ReadAttribute(node_id, [(1, Clusters.OnOff)])
Single attribute
await dev_ctrl.ReadAttribute(node_id, [(1, Clusters.OnOff.Attributes.OnTime)])
Multi-path
await dev_ctrl.ReadAttribute(node_id, [(1, Clusters.OnOff.Attributes.OnTime),(1, Clusters.OnOff.Attributes.OnOff)])
Example:
urgent = 1 await dev_ctrl ReadEvent(node_id, [(1, Clusters.TimeSynchronization.Events.MissingTrustedTimeSource, urgent)])
Subscriptions are handled in the Read / ReadAttribute / ReadEvent APIs. To initiate a subscription, set the reportInterval
tuple to set the floor and ceiling. The keepSubscriptions
and autoResubscribe
parameters also apply to subscriptions.
Subscription return ClusterAttribute.SubscriptionTransaction
. This can be used to set callbacks. The object is returned after the priming data read is complete, and the values there are used to populate the cache. The attribute callbacks are called on update.
Example for setting callbacks:
q = queue.Queue() cb = SimpleEventCallback("cb", cluster_id, event_id, q) urgent = 1 subscription = await dev_ctrl.ReadEvent(nodeid=1, events=[(1, event, urgent)], reportInterval=[1, 3]) subscription.SetEventUpdateCallback(callback=cb) try: q.get(block=True, timeout=timeout) except queue.Empty: asserts.assert_fail(“Timeout on event”)
Handles concrete paths only (per spec), can handle lists. Returns list of PyChipError
ClusterAttributeDescriptor
class with the value you want to send, tuple is (endpoint, attribute)Example:
res = await devCtrl.WriteAttribute(nodeid=0, attributes=[(0,Clusters.BasicInformation.Attributes.NodeLabel("Test"))]) asserts.assert_equal(ret[0].status, Status.Success, “write failed”)
Example:
pai = await dev_ctrl.SendCommand(nodeid, 0, Clusters.OperationalCredentials.Commands.CertificateChainRequest(2))
The test system is based on Mobly, and the matter_testing_support.py class provides some helpers for Mobly integration
use as:
if __name__ == "__main__": default_matter_test_main()
The python testing system also includes several functions for integrations with the test harness. To integrate with the test harness, you can define the following functions on your class to allow the test harness UI to properly work through your tests.
All of these functions are demonstrated in the hello_example.py reference.
steps_YourFunctionName
to allow the test harness to display the steps<stepnum>
) function to walk through the stepsdesc_YourFunctionName
to send back a string with the test descriptionpics_YourFunctionName
to send back a list of pics. If this function is omitted, the test will be run for every endpoint on every device.default_timeout
to adjust the timeout. The default is 90 secondsDeferred failures: For some tests, it makes sense to perform the entire test before failing and collect all the errors so the developers can address all the failures without needing to re-run the test multiple times. For example, tests that look at every attribute on the cluster and perform independent operations on them etc.
For such tests, use the ProblemNotice format and the convenience functions:
These functions keep track of the problems, and will print them at the end of the test. The test will not be failed until the assert is called.
A good example of this type of test can be found in the device basic composition tests, where all the test steps are independent and performed on a single read. See Device Basic Composition tests
To create a controller on a new fabric:
new_CA = self.certificate_authority_manager.NewCertificateAuthority() new_fabric_admin = new_certificate_authority.NewFabricAdmin(vendorId=0xFFF1, fabricId=self.matter_test_config.fabric_id + 1) TH2 = new_fabric_admin.NewController(nodeId=112233)
Open a commissioning window (ECW):
params = self.OpenCommissioningWindow(dev_ctrl=self.default_controller, node_id=self.dut_node_id)
To create a new controller on the SAME fabric, allocate a new controller from the fabric admin
Fabric admin for default controller:
fa=self.certificate_authority_manager.activeCaList[0].adminList[0] second_ctrl = fa.new_fabric_admin.NewController(nodeId=node_id)
You can run the python script as-is for local testing against an already-running DUT
./scripts/tests/run_python_test.py
is a convenient script to fire up an example DUT on the host, with factory reset support
./scripts/tests/run_python_test.py --factoryreset --app <your_app> --app-args "whatever" --script <your_script> --script-args "whatever"
Note that devices must be commissioned by the python test harness to run tests. chip-tool and the python test harness DO NOT share a fabric.