Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c6908e9
Add automatic device attribute dump on composition test failures:
j-ororke Sep 17, 2025
e2154ab
Restyled by autopep8
restyled-commits Sep 17, 2025
9c36c19
Remove duplicate on_fail() and unused import from TC_DeviceConformance
j-ororke Sep 17, 2025
5edf777
Removing unused logging import
j-ororke Sep 17, 2025
1438550
Change inheritance order to enable automatic device attribute dump on…
j-ororke Sep 17, 2025
9abde9b
Restyled by autopep8
restyled-commits Sep 17, 2025
dc5df9e
resolving linting errors and making sure that on test failures we emi…
j-ororke Sep 17, 2025
6756ca7
Restyled by autopep8
restyled-commits Sep 17, 2025
10f1ca6
Update basic_composition.py
j-ororke Sep 24, 2025
a026a85
Update basic_composition.py
j-ororke Sep 24, 2025
b1b1a4e
Merge branch 'master' into create_global_attr_log_function
j-ororke Sep 24, 2025
b149ff6
Restyled by autopep8
restyled-commits Sep 24, 2025
6992404
Move device attribute dumping from BasicCompositionTests to MatterBas…
j-ororke Oct 1, 2025
fee2649
Restyled by autopep8
restyled-commits Oct 1, 2025
855d881
Merge branch 'master' into create_global_attr_log_function
j-ororke Oct 1, 2025
13fb594
Merge branch 'master' into create_global_attr_log_function
j-ororke Oct 16, 2025
530ac3a
Fix Python 3.11 compatibility and improve test failure User Experience:
j-ororke Oct 16, 2025
f2fc773
removed duplicate log_structured_data function from basic_composition…
j-ororke Nov 6, 2025
7c93c8c
removed duplicate log_structured_data function from basic_composition…
j-ororke Nov 6, 2025
914426f
Restyled by autopep8
restyled-commits Nov 6, 2025
127cb15
Resolving linting error
j-ororke Nov 6, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 2 additions & 9 deletions src/python_testing/TC_DeviceBasicComposition.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@
from matter.clusters.Types import Nullable
from matter.exceptions import ChipStackError
from matter.interaction_model import InteractionModelError, Status
from matter.testing.basic_composition import BasicCompositionTests
from matter.testing.basic_composition import BasicCompositionTests, log_structured_data
from matter.testing.global_attribute_ids import (AttributeIdType, ClusterIdType, CommandIdType, GlobalAttributeIds,
attribute_id_type, cluster_id_type, command_id_type)
from matter.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main
Expand Down Expand Up @@ -272,7 +272,7 @@ def check_no_duplicates(obj: Any) -> None:
raise ValueError(f"Value {str(obj)} contains duplicate values")


class TC_DeviceBasicComposition(MatterBaseTest, BasicCompositionTests):
class TC_DeviceBasicComposition(BasicCompositionTests, MatterBaseTest):
@async_test_body
async def setup_class(self):
super().setup_class()
Expand Down Expand Up @@ -966,13 +966,6 @@ def test_TC_IDM_12_1(self):
json_str, txt_str = self.dump_wildcard(dump_device_composition_path)

# Structured dump so we can pull these back out of the logs
def log_structured_data(start_tag: str, dump_string):
lines = dump_string.splitlines()
logging.info(f'{start_tag}BEGIN ({len(lines)} lines)====')
for line in lines:
logging.info(f'{start_tag}{line}')
logging.info(f'{start_tag}END ====')

log_structured_data('==== json: ', json_str)
log_structured_data('==== txt: ', txt_str)

Expand Down
2 changes: 1 addition & 1 deletion src/python_testing/TC_DeviceConformance.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
from matter.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main


class TC_DeviceConformance(MatterBaseTest, DeviceConformanceTests):
class TC_DeviceConformance(DeviceConformanceTests, MatterBaseTest):
@async_test_body
async def setup_class(self):
super().setup_class()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,23 @@
LOGGER = logging.getLogger(__name__)


def log_structured_data(start_tag: str, dump_string: str):
"""Log structured data with a clear start and end marker.

This function is used to output device attribute dumps and other structured
data to logs in a format that can be easily extracted for debugging.

Args:
start_tag: A prefix tag to identify the type of data being logged
dump_string: The data to be logged
"""
lines = dump_string.splitlines()
LOGGER.info(f'{start_tag}BEGIN ({len(lines)} lines)====')
for line in lines:
LOGGER.info(f'{start_tag}{line}')
LOGGER.info(f'{start_tag}END ====')


@dataclass
class ArlData:
have_arl: bool
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,7 +206,7 @@ def setup_class(self):
self.stored_global_wildcard = None

def teardown_class(self):
"""Final teardown after all tests: log all problems.
"""Final teardown after all tests: log all problems and dump device attributes if available.
Test authors may overwrite this method in the derived class to perform teardown that is common for all tests
This function is called only once per class. To perform teardown after each test, use teardown_test.
Test authors that implement steps in this function need to be careful of step handling if there is
Expand All @@ -216,6 +216,9 @@ def teardown_class(self):

"""
if len(self.problems) > 0:
# Attempt to dump device attribute data for debugging when problems are found
self._dump_device_attributes_on_failure()

LOGGER.info("###########################################################")
LOGGER.info("Problems found:")
LOGGER.info("===============")
Expand All @@ -224,6 +227,42 @@ def teardown_class(self):
LOGGER.info("###########################################################")
super().teardown_class()

def _dump_device_attributes_on_failure(self):
"""
Dump device attribute data when problems are found for debugging purposes.

This method attempts to generate a device attribute dump if the test has
collected endpoint data. It's designed to be safe and not interfere with
the original test failure reporting.
"""
try:
# Check if we have endpoints_tlv data (from BasicCompositionTests or similar)
if hasattr(self, 'endpoints_tlv') and self.endpoints_tlv:
# Check if we have the dump_wildcard method (from BasicCompositionTests)
if hasattr(self, 'dump_wildcard'):
_, txt_str = self.dump_wildcard(None)
# Only dump the text format - it's more readable for debugging
self._log_structured_data('==== FAILURE_DUMP_txt: ', txt_str)
except (AttributeError, KeyError, ValueError, TypeError):
# Don't let data access or serialization errors interfere with the original test failure
pass

def _log_structured_data(self, start_tag: str, dump_string: str):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems like a duplicate of the function in basic_composition.py

Copy link
Contributor Author

@j-ororke j-ororke Nov 6, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for catching that!
I’ve removed this function from the basic_composition support module. There’s a test, test_TC_IDM_12_1 in the TC_DeviceBasicComposition module, that uses this function directly; it originally came from there (previously embedded inside that test).
I’ve now updated that test to inherit the function from here, and renamed this function so that it doesn't appear to be a private function anymore.

"""Log structured data with a clear start and end marker.

This function is used to output device attribute dumps and other structured
data to logs in a format that can be easily extracted for debugging.

Args:
start_tag: A prefix tag to identify the type of data being logged
dump_string: The data to be logged
"""
lines = dump_string.splitlines()
LOGGER.info(f'{start_tag}BEGIN ({len(lines)} lines)====')
for line in lines:
LOGGER.info(f'{start_tag}{line}')
LOGGER.info(f'{start_tag}END ====')

def setup_test(self):
"""Set up for each individual test execution.

Expand Down
Loading