From 6c96a7d41662c8abde287b6689a5ee782ebd5746 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 6 Jan 2025 22:54:35 -0800 Subject: [PATCH 01/53] non-zero percent validation --- src/python_testing/TC_FAN_3_1.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index cc1f28b788a348..f7ff00dcd9a60d 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -79,9 +79,12 @@ async def test_TC_FAN_3_1(self): self.print_step("2a", "Read from the DUT the FanMode attribute and store") existing_fan_mode = await self.read_fan_mode(endpoint=endpoint) + print(f"\n\n\n\n\t\t\t [FANS] existing_fan_mode: {existing_fan_mode.name}\n\n\n\n\n") self.print_step("2b", "Write High to FanMode") status = await self.write_fan_mode(endpoint=endpoint, fan_mode=Clusters.FanControl.Enums.FanModeEnum.kHigh) + print(f"\n\n\n\n\t\t\t [FANS] write fan mode status: {status.name}\n\n\n\n\n") + status_ok = (status == Status.Success) or (status == Status.InvalidInState) asserts.assert_true(status_ok, "FanMode write did not return a value of Success or InvalidInState") @@ -89,6 +92,7 @@ async def test_TC_FAN_3_1(self): time.sleep(3) new_fan_mode = await self.read_fan_mode(endpoint=endpoint) + print(f"\n\n\n\n\t\t\t [FANS] new_fan_mode: {new_fan_mode.name}\n\n\n\n\n") if status == Status.Success: asserts.assert_equal(new_fan_mode, Clusters.FanControl.Enums.FanModeEnum.kHigh, "FanMode is not High") @@ -97,6 +101,11 @@ async def test_TC_FAN_3_1(self): self.print_step("3a", "Read from the DUT the PercentSetting attribute and store") existing_percent_setting = await self.read_percent_setting(endpoint=endpoint) + print(f"\n\n\n\n\t\t\t [FANS] existing_percent_setting: {existing_percent_setting.value}\n\n\n\n\n") + + # If mode was set, verify that the percent value is non-zero + if status == Status.Success: + asserts.assert_greater(existing_percent_setting, 0, "Percentage value cannot be 0 when a mode other than off is set.") self.print_step("3b", "Write Off to Fan Mode") status = await self.write_fan_mode(endpoint=endpoint, fan_mode=Clusters.FanControl.Enums.FanModeEnum.kOff) From 62f7df7e4252ad1bdb51c4c38084f0c453513aec Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 7 Jan 2025 00:32:03 -0800 Subject: [PATCH 02/53] debugging --- src/python_testing/TC_FAN_3_1.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index f7ff00dcd9a60d..0f56d3f4970dfb 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -101,7 +101,9 @@ async def test_TC_FAN_3_1(self): self.print_step("3a", "Read from the DUT the PercentSetting attribute and store") existing_percent_setting = await self.read_percent_setting(endpoint=endpoint) - print(f"\n\n\n\n\t\t\t [FANS] existing_percent_setting: {existing_percent_setting.value}\n\n\n\n\n") + existing_percent_current = await self.read_percent_current(endpoint=endpoint) + print(f"\n\n\n\n\t\t\t [FANS] existing_percent_setting: {existing_percent_setting}\n\n\n\n\n") + print(f"\n\n\n\n\t\t\t [FANS] existing_percent_current: {existing_percent_current}\n\n\n\n\n") # If mode was set, verify that the percent value is non-zero if status == Status.Success: From 643c405a1b7eb6be7915bc691f387191fe9efab1 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Wed, 8 Jan 2025 17:11:12 -0800 Subject: [PATCH 03/53] progress --- src/python_testing/TC_FAN_3_1.py | 321 +++++++++++++++++++++++-------- 1 file changed, 246 insertions(+), 75 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 0f56d3f4970dfb..e7a26bdb7c03c9 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -39,13 +39,37 @@ import chip.clusters as Clusters from chip.interaction_model import Status -from chip.testing.matter_testing import MatterBaseTest, async_test_body, default_matter_test_main +from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main from mobly import asserts +import pdb + logger = logging.getLogger(__name__) class TC_FAN_3_1(MatterBaseTest): + + def steps_TC_FAN_3_1(self): + return [TestStep("1", "Commissioning, already done."), + TestStep("2", "TH reads from the DUT the FanMode attribute and stores it", + "Verify that the DUT response contains a FanModeEnum"), + TestStep("3", + "If supports_icd is true, TH reads ActiveModeThreshold from the ICD Management cluster on EP0 and saves as active_mode_threshold.", ""), + TestStep("4", "If supports_icd is true, TH reads FeatureMap from the ICD Management cluster on EP0. If the LITS feature is set, set supports_lit to true. Otherwise set supports_lit to false.", ""), + TestStep("5", "TH constructs the instance name for the DUT as the 64-bit compressed Fabric identifier, and the assigned 64-bit Node identifier, each expressed as a fixed-length sixteen-character hexadecimal string, encoded as ASCII (UTF-8) text using capital letters, separated by a hyphen.", ""), + TestStep("6", "TH performs a query for the SRV record against the qname instance_qname.", + "Verify SRV record is returned"), + # TestStep(7, "TH performs a query for the TXT record against the qname instance_qname.", + # "Verify TXT record is returned"), + # TestStep(8, "TH performs a query for the AAAA record against the target listed in the SRV record", + # "Verify AAAA record is returned"), + # TestStep(9, "TH verifies the following from the returned records:", + # "TH verifies the following from the returned records: The hostname must be a fixed-length twelve-character (or sixteen-character) hexadecimal string, encoded as ASCII (UTF-8) text using capital letters.. ICD TXT key: • If supports_lit is false, verify that the ICD key is NOT present in the TXT record • If supports_lit is true, verify the ICD key IS present in the TXT record, and it has the value of 0 or 1 (ASCII) SII TXT key: • If supports_icd is true and supports_lit is false, set sit_mode to true • If supports_icd is true and supports_lit is true, set sit_mode to true if ICD=0 otherwise set sit_mode to false • If supports_icd is false, set sit_mode to false • If sit_mode is true, verify that the SII key IS present in the TXT record • if the SII key is present, verify it is a decimal value with no leading zeros and is less than or equal to 3600000 (1h in ms) SAI TXT key: • if supports_icd is true, verify that the SAI key is present in the TXT record • If the SAI key is present, verify it is a decimal value with no leading zeros and is less than or equal to 3600000 (1h in ms)"), + # TestStep(10, "TH performs a DNS-SD browse for _I._sub._matter._tcp.local, where is the 64-bit compressed Fabric identifier, expressed as a fixed-length, sixteencharacter hexadecimal string, encoded as ASCII (UTF-8) text using capital letters.", + # "Verify DUT returns a PTR record with DNS-SD instance name set to instance_name"), + # TestStep(11, "TH performs a DNS-SD browse for _matter._tcp.local", + # "Verify DUT returns a PTR record with DNS-SD instance name set to instance_name"), + ] async def read_fc_attribute_expect_success(self, endpoint, attribute): cluster = Clusters.Objects.FanControl @@ -60,6 +84,15 @@ async def read_percent_setting(self, endpoint): async def read_percent_current(self, endpoint): return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.PercentCurrent) + async def read_speed_setting(self, endpoint): + return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.SpeedSetting) + + async def read_speed_current(self, endpoint): + return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.SpeedCurrent) + + async def read_fan_mode_sequence(self, endpoint): + return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.FanModeSequence) + async def write_fan_mode(self, endpoint, fan_mode) -> Status: result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, Clusters.FanControl.Attributes.FanMode(fan_mode))]) return result[0].Status @@ -74,107 +107,245 @@ def pics_TC_FAN_3_1(self) -> list[str]: @async_test_body async def test_TC_FAN_3_1(self): endpoint = self.get_endpoint(default=1) + speed_attributes_present = self.check_pics("FAN.S.F00") + print(f"\n\n\n\t\t\t[FANS] speed_attributes_present: {speed_attributes_present}\n\n\n") + + existing_speed_setting = await self.read_speed_setting(endpoint=endpoint) + print(f"\n\n\n\t\t\t[FANS] existing_speed_setting: {existing_speed_setting}\n\n\n") + + existing_speed_current = await self.read_speed_current(endpoint=endpoint) + print(f"\n\n\n\t\t\t[FANS] existing_speed_current: {existing_speed_current}\n\n\n") + + + pdb.set_trace() + + + # *** STEP 1 *** + # Commissioning, already done. + self.step("1") + + # *** STEP 2 *** + # The TH reads the initial FanMode attribute value from the DUT and stores it + # Verify that the DUT response contains a FanModeEnum value + self.step("2") + initial_fan_mode = await self.read_fan_mode(endpoint=endpoint) + asserts.assert_in(initial_fan_mode, Clusters.FanControl.Enums.FanModeEnum, "Response doesn't contain a FanModeEnum value") + + initial_percent_setting = await self.read_percent_setting(endpoint=endpoint) + initial_percent_current = await self.read_percent_current(endpoint=endpoint) + + print(f"\n\n\n\t\t\t[FANS] initial_fan_mode: {initial_fan_mode}\n\n\n") + print(f"\n\n\n\t\t\t[FANS] initial_percent_setting: {initial_percent_setting}\n\n\n") + print(f"\n\n\n\t\t\t[FANS] initial_percent_current: {initial_percent_current}\n\n\n") + + # *** STEP 3 *** + # The TH writes the Off value to the FanMode attribute on the DUT and reads it back after a few seconds + # The DUT shall return either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS, the fan mode read should match the fan mode written, verify that the PercentSetting and + # PercentCurrent values are zero + # If INVALID_IN_STATE, the fan mode read should match the initial fan mode + self.step("3") + fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kOff + write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) + write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") + time.sleep(3) + fan_mode_read = await self.read_fan_mode(endpoint=endpoint) + if write_status == Status.Success: + asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Off") + percent_setting_off = await self.read_percent_setting(endpoint=endpoint) + percent_current_off = await self.read_percent_current(endpoint=endpoint) + print(f"\n\n\n\t\t\t [FANS] OFF percent_setting: {percent_setting_off}") + print(f"\n\n\n\t\t\t [FANS] OFF percent_setting: {percent_current_off}") + + asserts.assert_equal(percent_setting_off, 0, "PercentSetting value must be 0 when a fan mode Off is set.") + asserts.assert_equal(percent_current_off, 0, "PercentCurrent value must be 0 when a fan mode Off is set.") + else: + asserts.assert_equal(fan_mode_read, initial_fan_mode, "FanMode is not unchanged") + + # *** STEP 4 *** + # The TH writes the Low value to the FanMode attribute on the DUT and reads it back after a few seconds + # The DUT shall return either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS, the fan mode read should match the fan mode written, verify that the PercentSetting and + # PercentCurrent values are non-zero and greater than the ones from the Off fan mode from the previous step + # If INVALID_IN_STATE, the fan mode read should match the initial fan mode + self.step("4") + fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kLow + write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) + write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") + time.sleep(3) + fan_mode_read = await self.read_fan_mode(endpoint=endpoint) + if write_status == Status.Success: + asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Low") + percent_setting_low = await self.read_percent_setting(endpoint=endpoint) + percent_current_low = await self.read_percent_current(endpoint=endpoint) + print(f"\n\n\n\t\t\t [FANS] LOW percent_setting: {percent_setting_low}") + print(f"\n\n\n\t\t\t [FANS] LOW percent_setting: {percent_current_low}") + + asserts.assert_greater(percent_setting_low, 0, "PercentSetting value must be greater than 0.") + asserts.assert_greater(percent_current_low, 0, "PercentCurrent value must be greater than 0.") + asserts.assert_greater(percent_setting_low, percent_setting_off, "PercentSetting value in Low fan mode must be greater than in Off fan mode.") + asserts.assert_greater(percent_current_low, percent_current_off, "PercentCurrent value in Low fan mode must be greater than in Off fan mode.") + else: + asserts.assert_equal(fan_mode_read, initial_fan_mode, "FanMode is not unchanged") + + # *** STEP 5 *** + # The TH writes the Medium value to the FanMode attribute on the DUT and reads it back after a few seconds + # The DUT shall return either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS, the fan mode read should match the fan mode written, verify that the PercentSetting and + # PercentCurrent values are non-zero and greater than the ones from the Low fan mode from the previous step + # If INVALID_IN_STATE, the fan mode read should match the initial fan mode + self.step("5") + fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kMedium + write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) + write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") + time.sleep(3) + fan_mode_read = await self.read_fan_mode(endpoint=endpoint) + if write_status == Status.Success: + asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Medium") + percent_setting_medium = await self.read_percent_setting(endpoint=endpoint) + percent_current_medium = await self.read_percent_current(endpoint=endpoint) + print(f"\n\n\n\t\t\t [FANS] MEDIUM percent_setting: {percent_setting_medium}") + print(f"\n\n\n\t\t\t [FANS] MEDIUM percent_setting: {percent_current_medium}") + + asserts.assert_greater(percent_setting_medium, 0, "PercentSetting value must be greater than 0.") + asserts.assert_greater(percent_current_medium, 0, "PercentCurrent value must be greater than 0.") + asserts.assert_greater(percent_setting_medium, percent_setting_low, "PercentSetting value in Medium fan mode must be greater than in Low fan mode.") + asserts.assert_greater(percent_current_medium, percent_current_low, "PercentCurrent value in Medium fan mode must be greater than in Low fan mode.") + else: + asserts.assert_equal(fan_mode_read, initial_fan_mode, "FanMode is not unchanged") + + # # *** STEP 6 *** + # The TH writes the High value to the FanMode attribute on the DUT and reads it back after a few seconds + # The DUT shall return either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS, the fan mode read should match the fan mode written, verify that the PercentSetting and + # PercentCurrent values are non-zero and greater than the ones from the Medium fan mode from the previous step + # If INVALID_IN_STATE, the fan mode read should match the initial fan mode + self.step("6") + fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kHigh + write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) + write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") + time.sleep(3) + fan_mode_read = await self.read_fan_mode(endpoint=endpoint) + if write_status == Status.Success: + asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to High") + percent_setting_high = await self.read_percent_setting(endpoint=endpoint) + percent_current_high = await self.read_percent_current(endpoint=endpoint) + print(f"\n\n\n\t\t\t [FANS] HIGH percent_setting: {percent_setting_high}") + print(f"\n\n\n\t\t\t [FANS] HIGH percent_setting: {percent_current_high}") + + asserts.assert_greater(percent_setting_high, 0, "PercentSetting value must be greater than 0.") + asserts.assert_greater(percent_current_high, 0, "PercentCurrent value must be greater than 0.") + asserts.assert_greater(percent_setting_high, percent_setting_medium, "PercentSetting value in High fan mode must be greater than in Medium fan mode.") + asserts.assert_greater(percent_current_high, percent_current_medium, "PercentCurrent value in High fan mode must be greater than in Medium fan mode.") + else: + asserts.assert_equal(fan_mode_read, initial_fan_mode, "FanMode is not unchanged") - self.print_step(1, "Commissioning, already done") - self.print_step("2a", "Read from the DUT the FanMode attribute and store") - existing_fan_mode = await self.read_fan_mode(endpoint=endpoint) - print(f"\n\n\n\n\t\t\t [FANS] existing_fan_mode: {existing_fan_mode.name}\n\n\n\n\n") + - self.print_step("2b", "Write High to FanMode") - status = await self.write_fan_mode(endpoint=endpoint, fan_mode=Clusters.FanControl.Enums.FanModeEnum.kHigh) - print(f"\n\n\n\n\t\t\t [FANS] write fan mode status: {status.name}\n\n\n\n\n") - status_ok = (status == Status.Success) or (status == Status.InvalidInState) - asserts.assert_true(status_ok, "FanMode write did not return a value of Success or InvalidInState") - self.print_step("2c", "After a few seconds, read from the DUT the FanMode attribute") - time.sleep(3) - new_fan_mode = await self.read_fan_mode(endpoint=endpoint) - print(f"\n\n\n\n\t\t\t [FANS] new_fan_mode: {new_fan_mode.name}\n\n\n\n\n") - if status == Status.Success: - asserts.assert_equal(new_fan_mode, Clusters.FanControl.Enums.FanModeEnum.kHigh, "FanMode is not High") - else: - asserts.assert_equal(new_fan_mode, existing_fan_mode, "FanMode is not unchanged") - self.print_step("3a", "Read from the DUT the PercentSetting attribute and store") - existing_percent_setting = await self.read_percent_setting(endpoint=endpoint) - existing_percent_current = await self.read_percent_current(endpoint=endpoint) - print(f"\n\n\n\n\t\t\t [FANS] existing_percent_setting: {existing_percent_setting}\n\n\n\n\n") - print(f"\n\n\n\n\t\t\t [FANS] existing_percent_current: {existing_percent_current}\n\n\n\n\n") - # If mode was set, verify that the percent value is non-zero - if status == Status.Success: - asserts.assert_greater(existing_percent_setting, 0, "Percentage value cannot be 0 when a mode other than off is set.") - self.print_step("3b", "Write Off to Fan Mode") - status = await self.write_fan_mode(endpoint=endpoint, fan_mode=Clusters.FanControl.Enums.FanModeEnum.kOff) - status_ok = (status == Status.Success) or (status == Status.InvalidInState) - asserts.assert_true(status_ok, "FanMode write did not return a value of Success or InvalidInState") - self.print_step("3c", "After a few seconds, read from the DUT the PercentSetting attribute") - time.sleep(3) - new_percent_setting = await self.read_percent_setting(endpoint=endpoint) - if status == Status.Success: - asserts.assert_equal(new_percent_setting, Clusters.FanControl.Enums.FanModeEnum.kOff, "PercentSetting is not Off") - else: - asserts.assert_equal(new_percent_setting, existing_percent_setting, "PercentSetting is not unchanged") - self.print_step("3d", "Read from the DUT the PercentCurrent attribute") - percent_current = await self.read_percent_current(endpoint=endpoint) - if status == Status.Success: - asserts.assert_equal(percent_current, 0, "PercentCurrent is not 0") - else: - asserts.assert_equal(percent_current, existing_percent_setting, "PercentCurrent is not unchanged") - self.print_step("4a", "Read from the DUT the PercentSetting attribute and store") - existing_percent_setting = await self.read_percent_setting(endpoint=endpoint) - self.print_step("4b", "Write PercentSetting to 30") - status = await self.write_percent_setting(endpoint=endpoint, percent_setting=30) - status_ok = (status == Status.Success) or (status == Status.InvalidInState) - asserts.assert_true(status_ok, "PercentSetting write did not return a value of Success or InvalidInState") + # fan_mode_sequence = await self.read_fan_mode_sequence(endpoint=endpoint) + # print(f"\n\n\n\n\t\t\t [FANS] fan_mode_sequence: {fan_mode_sequence}\n\n\n\n\n") - self.print_step("4c", "After a few seconds, read from the DUT the PercentSetting attribute") - time.sleep(3) - new_percent_setting = await self.read_percent_setting(endpoint=endpoint) - if status == Status.Success: - asserts.assert_equal(new_percent_setting, 30, "PercentSetting is not 30") - else: - asserts.assert_equal(new_percent_setting, existing_percent_setting, "PercentSetting is not unchanged") - self.print_step("4d", "Read from the DUT the PercentCurrent attribute") - percent_current = await self.read_percent_current(endpoint=endpoint) - if status == Status.Success: - asserts.assert_equal(percent_current, 30, "PercentCurrent is not 30") - else: - asserts.assert_equal(percent_current, existing_percent_setting, "PercentCurrent is not unchanged") - self.print_step("5a", "Read from the DUT the FanMode attribute and store") - existing_fan_mode = await self.read_fan_mode(endpoint=endpoint) - self.print_step("5b", "Write PercentSetting to 0") - status = await self.write_percent_setting(endpoint=endpoint, percent_setting=0) - status_ok = (status == Status.Success) or (status == Status.InvalidInState) - asserts.assert_true(status_ok, "PercentSetting write did not return a value of Success or InvalidInState") - self.print_step("5c", "After a few seconds, read from the DUT the FanMode attribute") - time.sleep(3) - new_fan_mode = await self.read_fan_mode(endpoint=endpoint) - if status == Status.Success: - asserts.assert_equal(new_fan_mode, Clusters.FanControl.Enums.FanModeEnum.kOff, "FanMode is not Off") - else: - asserts.assert_equal(new_fan_mode, existing_fan_mode, "FanMode is not unchanged") + + + + + + + + # self.print_step("3b", "Write Off to Fan Mode") + # status = await self.write_fan_mode(endpoint=endpoint, fan_mode=Clusters.FanControl.Enums.FanModeEnum.kOff) + # status_ok = (status == Status.Success) or (status == Status.InvalidInState) + # asserts.assert_true(status_ok, "FanMode write did not return a value of Success or InvalidInState") + + # self.print_step("3c", "After a few seconds, read from the DUT the PercentSetting attribute") + # time.sleep(3) + + # new_percent_setting = await self.read_percent_setting(endpoint=endpoint) + + # if status == Status.Success: + # asserts.assert_equal(new_percent_setting, Clusters.FanControl.Enums.FanModeEnum.kOff, "PercentSetting is not Off") + # else: + # asserts.assert_equal(new_percent_setting, existing_percent_setting, "PercentSetting is not unchanged") + + # self.print_step("3d", "Read from the DUT the PercentCurrent attribute") + # percent_current = await self.read_percent_current(endpoint=endpoint) + + # if status == Status.Success: + # asserts.assert_equal(percent_current, 0, "PercentCurrent is not 0") + # else: + # asserts.assert_equal(percent_current, existing_percent_setting, "PercentCurrent is not unchanged") + + # self.print_step("4a", "Read from the DUT the PercentSetting attribute and store") + # existing_percent_setting = await self.read_percent_setting(endpoint=endpoint) + + # self.print_step("4b", "Write PercentSetting to 30") + # status = await self.write_percent_setting(endpoint=endpoint, percent_setting=30) + # status_ok = (status == Status.Success) or (status == Status.InvalidInState) + # asserts.assert_true(status_ok, "PercentSetting write did not return a value of Success or InvalidInState") + + # self.print_step("4c", "After a few seconds, read from the DUT the PercentSetting attribute") + # time.sleep(3) + + # new_percent_setting = await self.read_percent_setting(endpoint=endpoint) + + # if status == Status.Success: + # asserts.assert_equal(new_percent_setting, 30, "PercentSetting is not 30") + # else: + # asserts.assert_equal(new_percent_setting, existing_percent_setting, "PercentSetting is not unchanged") + + # self.print_step("4d", "Read from the DUT the PercentCurrent attribute") + # percent_current = await self.read_percent_current(endpoint=endpoint) + + # if status == Status.Success: + # asserts.assert_equal(percent_current, 30, "PercentCurrent is not 30") + # else: + # asserts.assert_equal(percent_current, existing_percent_setting, "PercentCurrent is not unchanged") + + # self.print_step("5a", "Read from the DUT the FanMode attribute and store") + # current_fan_mode = await self.read_fan_mode(endpoint=endpoint) + + # self.print_step("5b", "Write PercentSetting to 0") + # status = await self.write_percent_setting(endpoint=endpoint, percent_setting=0) + # status_ok = (status == Status.Success) or (status == Status.InvalidInState) + # asserts.assert_true(status_ok, "PercentSetting write did not return a value of Success or InvalidInState") + + # self.print_step("5c", "After a few seconds, read from the DUT the FanMode attribute") + # time.sleep(3) + + # new_fan_mode = await self.read_fan_mode(endpoint=endpoint) + + # if status == Status.Success: + # asserts.assert_equal(new_fan_mode, Clusters.FanControl.Enums.FanModeEnum.kOff, "FanMode is not Off") + # else: + # asserts.assert_equal(new_fan_mode, current_fan_mode, "FanMode is not unchanged") if __name__ == "__main__": From 6d4969ce1b86df47905aa8e6c0d0391a4d254db4 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Thu, 9 Jan 2025 01:55:59 -0800 Subject: [PATCH 04/53] progress --- src/python_testing/TC_FAN_3_1.py | 503 +++++++++++++++++++------------ 1 file changed, 313 insertions(+), 190 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index e7a26bdb7c03c9..6d0164e12f11da 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -36,13 +36,14 @@ import logging import time +import random import chip.clusters as Clusters from chip.interaction_model import Status from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main from mobly import asserts -import pdb +# import pdb logger = logging.getLogger(__name__) @@ -50,25 +51,27 @@ class TC_FAN_3_1(MatterBaseTest): def steps_TC_FAN_3_1(self): - return [TestStep("1", "Commissioning, already done."), - TestStep("2", "TH reads from the DUT the FanMode attribute and stores it", - "Verify that the DUT response contains a FanModeEnum"), - TestStep("3", - "If supports_icd is true, TH reads ActiveModeThreshold from the ICD Management cluster on EP0 and saves as active_mode_threshold.", ""), - TestStep("4", "If supports_icd is true, TH reads FeatureMap from the ICD Management cluster on EP0. If the LITS feature is set, set supports_lit to true. Otherwise set supports_lit to false.", ""), - TestStep("5", "TH constructs the instance name for the DUT as the 64-bit compressed Fabric identifier, and the assigned 64-bit Node identifier, each expressed as a fixed-length sixteen-character hexadecimal string, encoded as ASCII (UTF-8) text using capital letters, separated by a hyphen.", ""), - TestStep("6", "TH performs a query for the SRV record against the qname instance_qname.", - "Verify SRV record is returned"), - # TestStep(7, "TH performs a query for the TXT record against the qname instance_qname.", - # "Verify TXT record is returned"), - # TestStep(8, "TH performs a query for the AAAA record against the target listed in the SRV record", - # "Verify AAAA record is returned"), - # TestStep(9, "TH verifies the following from the returned records:", - # "TH verifies the following from the returned records: The hostname must be a fixed-length twelve-character (or sixteen-character) hexadecimal string, encoded as ASCII (UTF-8) text using capital letters.. ICD TXT key: • If supports_lit is false, verify that the ICD key is NOT present in the TXT record • If supports_lit is true, verify the ICD key IS present in the TXT record, and it has the value of 0 or 1 (ASCII) SII TXT key: • If supports_icd is true and supports_lit is false, set sit_mode to true • If supports_icd is true and supports_lit is true, set sit_mode to true if ICD=0 otherwise set sit_mode to false • If supports_icd is false, set sit_mode to false • If sit_mode is true, verify that the SII key IS present in the TXT record • if the SII key is present, verify it is a decimal value with no leading zeros and is less than or equal to 3600000 (1h in ms) SAI TXT key: • if supports_icd is true, verify that the SAI key is present in the TXT record • If the SAI key is present, verify it is a decimal value with no leading zeros and is less than or equal to 3600000 (1h in ms)"), - # TestStep(10, "TH performs a DNS-SD browse for _I._sub._matter._tcp.local, where is the 64-bit compressed Fabric identifier, expressed as a fixed-length, sixteencharacter hexadecimal string, encoded as ASCII (UTF-8) text using capital letters.", - # "Verify DUT returns a PTR record with DNS-SD instance name set to instance_name"), - # TestStep(11, "TH performs a DNS-SD browse for _matter._tcp.local", - # "Verify DUT returns a PTR record with DNS-SD instance name set to instance_name"), + return [TestStep("1", "Commissioning already done."), + TestStep("2", "Action", + "Verification"), + TestStep("3", "Action", + "Verification"), + TestStep("4", "Action", + "Verification"), + TestStep("5", "Action", + "Verification"), + TestStep("6", "Action", + "Verification"), + TestStep("7", "Action", + "Verification"), + TestStep("8", "Action", + "Verification"), + TestStep("9", "Action", + "Verification"), + TestStep("10", "Action", + "Verification"), + TestStep("11", "Action", + "Verification"), ] async def read_fc_attribute_expect_success(self, endpoint, attribute): @@ -106,246 +109,366 @@ def pics_TC_FAN_3_1(self) -> list[str]: @async_test_body async def test_TC_FAN_3_1(self): + # pdb.set_trace() + endpoint = self.get_endpoint(default=1) speed_attributes_present = self.check_pics("FAN.S.F00") - print(f"\n\n\n\t\t\t[FANS] speed_attributes_present: {speed_attributes_present}\n\n\n") - - existing_speed_setting = await self.read_speed_setting(endpoint=endpoint) - print(f"\n\n\n\t\t\t[FANS] existing_speed_setting: {existing_speed_setting}\n\n\n") - - existing_speed_current = await self.read_speed_current(endpoint=endpoint) - print(f"\n\n\n\t\t\t[FANS] existing_speed_current: {existing_speed_current}\n\n\n") - - - pdb.set_trace() - - + seconds_delay = 1 + + # FanMode-PercentSetting mapping provided by the Air Purifier app + # +------------+----------------+----------------------+ + # | | PercentSetting | PercentSetting after | + # | FanMode | range | setting FanMode | + # +------------+----------------+----------------------+ + # | 0 (Off) | 0 | 0 | + # +------------+----------------+----------------------+ + # | 1 (Low) | 1 - 30 | 10 | + # +------------+----------------+----------------------+ + # | 2 (Medium) | 31 - 70 | 40 | + # +------------+----------------+----------------------+ + # | 3 (High) | 71 - 100 | 80 | + # +------------+----------------+----------------------+ + percent_setting_range_off = 0 + percent_setting_range_low = random.randint(1, 30) + percent_setting_range_medium = random.randint(31, 70) + percent_setting_range_high = random.randint(71, 100) + # *** STEP 1 *** - # Commissioning, already done. + # Commissioning already done self.step("1") # *** STEP 2 *** - # The TH reads the initial FanMode attribute value from the DUT and stores it + # The TH reads the FanMode attribute and stores it before attempting to write to it # Verify that the DUT response contains a FanModeEnum value + # The TH writes the Low value to the FanMode attribute on the DUT and reads it back after a few seconds, + # and stores it + # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS + # Verify that the FanMode read matches the FanMode written + # Verify that the PercentSetting and PercentCurrent values are non-zero + # Verify that the PercentSetting and PercentCurrent values are greater than the Off FanMode value + # If INVALID_IN_STATE + # Verify that the FanMode matches the FanMode before write attempt self.step("2") - initial_fan_mode = await self.read_fan_mode(endpoint=endpoint) - asserts.assert_in(initial_fan_mode, Clusters.FanControl.Enums.FanModeEnum, "Response doesn't contain a FanModeEnum value") - - initial_percent_setting = await self.read_percent_setting(endpoint=endpoint) - initial_percent_current = await self.read_percent_current(endpoint=endpoint) - - print(f"\n\n\n\t\t\t[FANS] initial_fan_mode: {initial_fan_mode}\n\n\n") - print(f"\n\n\n\t\t\t[FANS] initial_percent_setting: {initial_percent_setting}\n\n\n") - print(f"\n\n\n\t\t\t[FANS] initial_percent_current: {initial_percent_current}\n\n\n") + fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) + asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") + fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kLow + write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) + write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") + time.sleep(seconds_delay) + fan_mode_read = await self.read_fan_mode(endpoint=endpoint) + if write_status == Status.Success: + asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Low") + percent_setting_low = await self.read_percent_setting(endpoint=endpoint) + percent_current_low = await self.read_percent_current(endpoint=endpoint) + asserts.assert_greater(percent_setting_low, 0, "PercentSetting value must be greater than 0.") + asserts.assert_greater(percent_current_low, 0, "PercentCurrent value must be greater than 0.") + asserts.assert_greater(percent_setting_low, percent_setting_off, "PercentSetting value in Low FanMode must be greater than in Off FanMode.") + asserts.assert_greater(percent_current_low, percent_current_off, "PercentCurrent value in Low FanMode must be greater than in Off FanMode.") + else: + asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") # *** STEP 3 *** - # The TH writes the Off value to the FanMode attribute on the DUT and reads it back after a few seconds - # The DUT shall return either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS, the fan mode read should match the fan mode written, verify that the PercentSetting and - # PercentCurrent values are zero - # If INVALID_IN_STATE, the fan mode read should match the initial fan mode + # The TH reads the FanMode attribute and stores it before attempting to write to it + # Verify that the DUT response contains a FanModeEnum value + # The TH writes the Medium value to the FanMode attribute on the DUT and reads it back after a few seconds, + # and stores it + # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS + # Verify that the FanMode read matches the FanMode written + # Verify that the PercentSetting and PercentCurrent values are non-zero + # Verify that the PercentSetting and PercentCurrent values are greater than the Low FanMode values + # If INVALID_IN_STATE + # Verify that the FanMode matches the FanMode before write attempt self.step("3") - fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kOff + fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) + asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") + fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kMedium write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") - time.sleep(3) + time.sleep(seconds_delay) fan_mode_read = await self.read_fan_mode(endpoint=endpoint) if write_status == Status.Success: - asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Off") - percent_setting_off = await self.read_percent_setting(endpoint=endpoint) - percent_current_off = await self.read_percent_current(endpoint=endpoint) - print(f"\n\n\n\t\t\t [FANS] OFF percent_setting: {percent_setting_off}") - print(f"\n\n\n\t\t\t [FANS] OFF percent_setting: {percent_current_off}") - - asserts.assert_equal(percent_setting_off, 0, "PercentSetting value must be 0 when a fan mode Off is set.") - asserts.assert_equal(percent_current_off, 0, "PercentCurrent value must be 0 when a fan mode Off is set.") + asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Medium") + percent_setting_medium = await self.read_percent_setting(endpoint=endpoint) + percent_current_medium = await self.read_percent_current(endpoint=endpoint) + asserts.assert_greater(percent_setting_medium, 0, "PercentSetting value must be greater than 0.") + asserts.assert_greater(percent_current_medium, 0, "PercentCurrent value must be greater than 0.") + asserts.assert_greater(percent_setting_medium, percent_setting_low, "PercentSetting value in Medium FanMode must be greater than in Low FanMode.") + asserts.assert_greater(percent_current_medium, percent_current_low, "PercentCurrent value in Medium FanMode must be greater than in Low FanMode.") else: - asserts.assert_equal(fan_mode_read, initial_fan_mode, "FanMode is not unchanged") + asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") # *** STEP 4 *** - # The TH writes the Low value to the FanMode attribute on the DUT and reads it back after a few seconds - # The DUT shall return either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS, the fan mode read should match the fan mode written, verify that the PercentSetting and - # PercentCurrent values are non-zero and greater than the ones from the Off fan mode from the previous step - # If INVALID_IN_STATE, the fan mode read should match the initial fan mode + # The TH reads the FanMode attribute and stores it before attempting to write to it + # Verify that the DUT response contains a FanModeEnum value + # The TH writes the High value to the FanMode attribute on the DUT and reads it back after a few seconds, + # and stores it + # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS + # Verify that the FanMode read matches the FanMode written + # Verify that the PercentSetting and PercentCurrent values are non-zero + # Verify that the PercentSetting and PercentCurrent values are less than the High FanMode values + # If INVALID_IN_STATE + # Verify that the FanMode matches the FanMode before write attempt self.step("4") - fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kLow + fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) + asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") + fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kHigh write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") - time.sleep(3) + time.sleep(seconds_delay) fan_mode_read = await self.read_fan_mode(endpoint=endpoint) if write_status == Status.Success: - asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Low") - percent_setting_low = await self.read_percent_setting(endpoint=endpoint) - percent_current_low = await self.read_percent_current(endpoint=endpoint) - print(f"\n\n\n\t\t\t [FANS] LOW percent_setting: {percent_setting_low}") - print(f"\n\n\n\t\t\t [FANS] LOW percent_setting: {percent_current_low}") - - asserts.assert_greater(percent_setting_low, 0, "PercentSetting value must be greater than 0.") - asserts.assert_greater(percent_current_low, 0, "PercentCurrent value must be greater than 0.") - asserts.assert_greater(percent_setting_low, percent_setting_off, "PercentSetting value in Low fan mode must be greater than in Off fan mode.") - asserts.assert_greater(percent_current_low, percent_current_off, "PercentCurrent value in Low fan mode must be greater than in Off fan mode.") + asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to High") + percent_setting_high = await self.read_percent_setting(endpoint=endpoint) + percent_current_high = await self.read_percent_current(endpoint=endpoint) + asserts.assert_greater(percent_setting_high, 0, "PercentSetting value must be greater than 0.") + asserts.assert_greater(percent_current_high, 0, "PercentCurrent value must be greater than 0.") + asserts.assert_greater(percent_setting_high, percent_setting_medium, "PercentSetting value in High FanMode must be greater than in Medium FanMode.") + asserts.assert_greater(percent_current_high, percent_current_medium, "PercentCurrent value in High FanMode must be greater than in Medium FanMode.") else: - asserts.assert_equal(fan_mode_read, initial_fan_mode, "FanMode is not unchanged") + asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") # *** STEP 5 *** - # The TH writes the Medium value to the FanMode attribute on the DUT and reads it back after a few seconds - # The DUT shall return either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS, the fan mode read should match the fan mode written, verify that the PercentSetting and - # PercentCurrent values are non-zero and greater than the ones from the Low fan mode from the previous step - # If INVALID_IN_STATE, the fan mode read should match the initial fan mode + # The TH reads the FanMode attribute and stores it before attempting to write to it + # Verify that the DUT response contains a FanModeEnum value + # The TH writes the Medium value to the FanMode attribute on the DUT and reads it back after a few seconds, + # and stores it + # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS + # Verify that the FanMode read matches the FanMode written + # Verify that the PercentSetting and PercentCurrent values are non-zero + # Verify that the PercentSetting and PercentCurrent values are less than the High FanMode values + # If INVALID_IN_STATE + # Verify that the FanMode matches the FanMode before write attempt self.step("5") + fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) + asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kMedium write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") - time.sleep(3) + time.sleep(seconds_delay) fan_mode_read = await self.read_fan_mode(endpoint=endpoint) if write_status == Status.Success: asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Medium") percent_setting_medium = await self.read_percent_setting(endpoint=endpoint) percent_current_medium = await self.read_percent_current(endpoint=endpoint) - print(f"\n\n\n\t\t\t [FANS] MEDIUM percent_setting: {percent_setting_medium}") - print(f"\n\n\n\t\t\t [FANS] MEDIUM percent_setting: {percent_current_medium}") - asserts.assert_greater(percent_setting_medium, 0, "PercentSetting value must be greater than 0.") asserts.assert_greater(percent_current_medium, 0, "PercentCurrent value must be greater than 0.") - asserts.assert_greater(percent_setting_medium, percent_setting_low, "PercentSetting value in Medium fan mode must be greater than in Low fan mode.") - asserts.assert_greater(percent_current_medium, percent_current_low, "PercentCurrent value in Medium fan mode must be greater than in Low fan mode.") + asserts.assert_less(percent_setting_medium, percent_setting_high, "PercentSetting value in Medium FanMode must be less than in High FanMode.") + asserts.assert_less(percent_current_medium, percent_current_high, "PercentCurrent value in Medium FanMode must be less than in High FanMode.") else: - asserts.assert_equal(fan_mode_read, initial_fan_mode, "FanMode is not unchanged") - - # # *** STEP 6 *** - # The TH writes the High value to the FanMode attribute on the DUT and reads it back after a few seconds - # The DUT shall return either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS, the fan mode read should match the fan mode written, verify that the PercentSetting and - # PercentCurrent values are non-zero and greater than the ones from the Medium fan mode from the previous step - # If INVALID_IN_STATE, the fan mode read should match the initial fan mode + asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") + + # *** STEP 6 *** + # The TH reads the FanMode attribute and stores it before attempting to write to it + # Verify that the DUT response contains a FanModeEnum value + # The TH writes the Low value to the FanMode attribute on the DUT and reads it back after a few seconds, + # and stores it + # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS + # Verify that the FanMode read matches the FanMode written + # Verify that the PercentSetting and PercentCurrent values are non-zero + # Verify that the PercentSetting and PercentCurrent values are less than the Medium FanMode values + # If INVALID_IN_STATE + # Verify that the FanMode matches the FanMode before write attempt self.step("6") - fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kHigh + fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) + asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") + fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kLow write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") - time.sleep(3) + time.sleep(seconds_delay) fan_mode_read = await self.read_fan_mode(endpoint=endpoint) if write_status == Status.Success: - asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to High") - percent_setting_high = await self.read_percent_setting(endpoint=endpoint) - percent_current_high = await self.read_percent_current(endpoint=endpoint) - print(f"\n\n\n\t\t\t [FANS] HIGH percent_setting: {percent_setting_high}") - print(f"\n\n\n\t\t\t [FANS] HIGH percent_setting: {percent_current_high}") - - asserts.assert_greater(percent_setting_high, 0, "PercentSetting value must be greater than 0.") - asserts.assert_greater(percent_current_high, 0, "PercentCurrent value must be greater than 0.") - asserts.assert_greater(percent_setting_high, percent_setting_medium, "PercentSetting value in High fan mode must be greater than in Medium fan mode.") - asserts.assert_greater(percent_current_high, percent_current_medium, "PercentCurrent value in High fan mode must be greater than in Medium fan mode.") + asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Low") + percent_setting_low = await self.read_percent_setting(endpoint=endpoint) + percent_current_low = await self.read_percent_current(endpoint=endpoint) + asserts.assert_greater(percent_setting_low, 0, "PercentSetting value must be greater than 0.") + asserts.assert_greater(percent_current_low, 0, "PercentCurrent value must be greater than 0.") + asserts.assert_less(percent_setting_low, percent_setting_medium, "PercentSetting value in Low FanMode must be less than in Low FanMode.") + asserts.assert_less(percent_current_low, percent_current_medium, "PercentCurrent value in Low FanMode must be less than in Low FanMode.") else: - asserts.assert_equal(fan_mode_read, initial_fan_mode, "FanMode is not unchanged") - - - - - - - - - - - - - - - - - - - # fan_mode_sequence = await self.read_fan_mode_sequence(endpoint=endpoint) - # print(f"\n\n\n\n\t\t\t [FANS] fan_mode_sequence: {fan_mode_sequence}\n\n\n\n\n") - - - - - - - - - - - - + asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") + # *** STEP 7 *** + # The TH reads the FanMode attribute and stores it before attempting to write to it + # Verify that the DUT response contains a FanModeEnum value + # The TH writes the Off value to the FanMode attribute on the DUT and reads it back after a few seconds, + # and stores it + # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS + # Verify that the FanMode read matches the FanMode written + # Verify that the PercentSetting and PercentCurrent values are 0 + # If INVALID_IN_STATE + # Verify that the FanMode matches the FanMode before write attempt + self.step("7") + fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) + asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") + fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kOff + write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) + write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") + time.sleep(seconds_delay) + fan_mode_read = await self.read_fan_mode(endpoint=endpoint) + if write_status == Status.Success: + asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Off") + percent_setting_off = await self.read_percent_setting(endpoint=endpoint) + percent_current_off = await self.read_percent_current(endpoint=endpoint) + asserts.assert_equal(percent_setting_off, 0, "PercentSetting value must be 0 when a FanMode Off is set.") + asserts.assert_equal(percent_current_off, 0, "PercentCurrent value must be 0 when a FanMode Off is set.") + else: + asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") - # self.print_step("3b", "Write Off to Fan Mode") - # status = await self.write_fan_mode(endpoint=endpoint, fan_mode=Clusters.FanControl.Enums.FanModeEnum.kOff) - # status_ok = (status == Status.Success) or (status == Status.InvalidInState) - # asserts.assert_true(status_ok, "FanMode write did not return a value of Success or InvalidInState") - # self.print_step("3c", "After a few seconds, read from the DUT the PercentSetting attribute") - # time.sleep(3) - # new_percent_setting = await self.read_percent_setting(endpoint=endpoint) - # if status == Status.Success: - # asserts.assert_equal(new_percent_setting, Clusters.FanControl.Enums.FanModeEnum.kOff, "PercentSetting is not Off") - # else: - # asserts.assert_equal(new_percent_setting, existing_percent_setting, "PercentSetting is not unchanged") - # self.print_step("3d", "Read from the DUT the PercentCurrent attribute") - # percent_current = await self.read_percent_current(endpoint=endpoint) - # if status == Status.Success: - # asserts.assert_equal(percent_current, 0, "PercentCurrent is not 0") - # else: - # asserts.assert_equal(percent_current, existing_percent_setting, "PercentCurrent is not unchanged") - # self.print_step("4a", "Read from the DUT the PercentSetting attribute and store") - # existing_percent_setting = await self.read_percent_setting(endpoint=endpoint) - # self.print_step("4b", "Write PercentSetting to 30") - # status = await self.write_percent_setting(endpoint=endpoint, percent_setting=30) - # status_ok = (status == Status.Success) or (status == Status.InvalidInState) - # asserts.assert_true(status_ok, "PercentSetting write did not return a value of Success or InvalidInState") - # self.print_step("4c", "After a few seconds, read from the DUT the PercentSetting attribute") - # time.sleep(3) - # new_percent_setting = await self.read_percent_setting(endpoint=endpoint) - # if status == Status.Success: - # asserts.assert_equal(new_percent_setting, 30, "PercentSetting is not 30") - # else: - # asserts.assert_equal(new_percent_setting, existing_percent_setting, "PercentSetting is not unchanged") - # self.print_step("4d", "Read from the DUT the PercentCurrent attribute") - # percent_current = await self.read_percent_current(endpoint=endpoint) - # if status == Status.Success: - # asserts.assert_equal(percent_current, 30, "PercentCurrent is not 30") - # else: - # asserts.assert_equal(percent_current, existing_percent_setting, "PercentCurrent is not unchanged") - # self.print_step("5a", "Read from the DUT the FanMode attribute and store") - # current_fan_mode = await self.read_fan_mode(endpoint=endpoint) - # self.print_step("5b", "Write PercentSetting to 0") - # status = await self.write_percent_setting(endpoint=endpoint, percent_setting=0) - # status_ok = (status == Status.Success) or (status == Status.InvalidInState) - # asserts.assert_true(status_ok, "PercentSetting write did not return a value of Success or InvalidInState") - # self.print_step("5c", "After a few seconds, read from the DUT the FanMode attribute") - # time.sleep(3) + # *** STEP 8 *** + # The TH reads the PercentSetting attribute and stores it before attempting to write to it + # The TH writes 0 to the PercentSetting attribute on the DUT, reads it back after a few + # seconds, and stores it + # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS + # Verify that the PercentSetting read matches the PercentSetting written + # Verify that the PercentCurrent read matches the PercentSetting written (after a few seconds) + # Verify that FanMode = Off + # If INVALID_IN_STATE + # Verify that the PercentSetting read matches the PercentSetting before write attempt + # Verify that the PercentCurrent read matches the PercentSetting before write attempt + self.step("8") + percent_setting_before_write = await self.read_percent_setting(endpoint=endpoint) + percent_setting_write = percent_setting_range_off + write_status = await self.write_percent_setting(endpoint=endpoint, percent_setting=percent_setting_write) + write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") + time.sleep(seconds_delay) + percent_setting_read = await self.read_percent_setting(endpoint=endpoint) + if write_status == Status.Success: + asserts.assert_equal(percent_setting_read, percent_setting_write, f"PercentSetting not set to {percent_setting_write}") + fan_mode_read = await self.read_fan_mode(endpoint=endpoint) + time.sleep(seconds_delay) + percent_current_read = await self.read_percent_current(endpoint=endpoint) + asserts.assert_equal(percent_current_read, percent_setting_write, f"PercentCurrent not set to {percent_setting_write}") + asserts.assert_equal(fan_mode_read, Clusters.FanControl.Enums.FanModeEnum.kOff, "FanMode must be Off.") + else: + asserts.assert_equal(percent_setting_read, percent_setting_before_write, "PercentSetting is not unchanged") + asserts.assert_equal(percent_current_read, percent_setting_before_write, "PercentCurrent is not unchanged") + + # *** STEP 9 *** + # The TH reads the PercentSetting attribute and stores it before attempting to write to it + # The TH writes a value corresponding to the Low FanMode range to the PercentSetting attribute + # on the DUT, reads it back after a few seconds, and stores it + # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS + # Verify that the PercentSetting read matches the PercentSetting written + # Verify that the PercentCurrent read matches the PercentSetting written (after a few seconds) + # Verify that FanMode = Low + # If INVALID_IN_STATE + # Verify that the PercentSetting read matches the PercentSetting before write attempt + # Verify that the PercentCurrent read matches the PercentSetting before write attempt + self.step("9") + percent_setting_before_write = await self.read_percent_setting(endpoint=endpoint) + percent_setting_write = percent_setting_range_low + write_status = await self.write_percent_setting(endpoint=endpoint, percent_setting=percent_setting_write) + write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") + time.sleep(seconds_delay) + percent_setting_read = await self.read_percent_setting(endpoint=endpoint) + if write_status == Status.Success: + asserts.assert_equal(percent_setting_read, percent_setting_write, f"PercentSetting not set to {percent_setting_write}") + fan_mode_read = await self.read_fan_mode(endpoint=endpoint) + time.sleep(seconds_delay) + percent_current_read = await self.read_percent_current(endpoint=endpoint) + asserts.assert_equal(percent_current_read, percent_setting_write, f"PercentCurrent not set to {percent_setting_write}") + asserts.assert_equal(fan_mode_read, Clusters.FanControl.Enums.FanModeEnum.kLow, "FanMode must be Low.") + else: + asserts.assert_equal(percent_setting_read, percent_setting_before_write, "PercentSetting is not unchanged") + asserts.assert_equal(percent_current_read, percent_setting_before_write, "PercentCurrent is not unchanged") + + # *** STEP 10 *** + # The TH reads the PercentSetting attribute and stores it before attempting to write to it + # The TH writes a value corresponding to the Medium FanMode range to the PercentSetting attribute + # on the DUT, reads it back after a few seconds, and stores it + # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS + # Verify that the PercentSetting read matches the PercentSetting written + # Verify that the PercentCurrent read matches the PercentSetting written (after a few seconds) + # Verify that FanMode = Medium + # If INVALID_IN_STATE + # Verify that the PercentSetting read matches the PercentSetting before write attempt + # Verify that the PercentCurrent read matches the PercentSetting before write attempt + self.step("10") + percent_setting_before_write = await self.read_percent_setting(endpoint=endpoint) + percent_setting_write = percent_setting_range_medium + write_status = await self.write_percent_setting(endpoint=endpoint, percent_setting=percent_setting_write) + write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") + time.sleep(seconds_delay) + percent_setting_read = await self.read_percent_setting(endpoint=endpoint) + if write_status == Status.Success: + asserts.assert_equal(percent_setting_read, percent_setting_write, f"PercentSetting not set to {percent_setting_write}") + fan_mode_read = await self.read_fan_mode(endpoint=endpoint) + time.sleep(seconds_delay) + percent_current_read = await self.read_percent_current(endpoint=endpoint) + asserts.assert_equal(percent_current_read, percent_setting_write, f"PercentCurrent not set to {percent_setting_write}") + asserts.assert_equal(fan_mode_read, Clusters.FanControl.Enums.FanModeEnum.kMedium, "FanMode must be Medium.") + else: + asserts.assert_equal(percent_setting_read, percent_setting_before_write, "PercentSetting is not unchanged") + asserts.assert_equal(percent_current_read, percent_setting_before_write, "PercentCurrent is not unchanged") + + # *** STEP 11 *** + # The TH reads the PercentSetting attribute and stores it before attempting to write to it + # The TH writes a value corresponding to the High FanMode range to the PercentSetting attribute + # on the DUT, reads it back after a few seconds, and stores it + # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code + # If SUCCESS + # Verify that the PercentSetting read matches the PercentSetting written + # Verify that the PercentCurrent read matches the PercentSetting written (after a few seconds) + # Verify that FanMode = High + # If INVALID_IN_STATE + # Verify that the PercentSetting read matches the PercentSetting before write attempt + # Verify that the PercentCurrent read matches the PercentSetting before write attempt + self.step("11") + percent_setting_before_write = await self.read_percent_setting(endpoint=endpoint) + percent_setting_write = percent_setting_range_high + write_status = await self.write_percent_setting(endpoint=endpoint, percent_setting=percent_setting_write) + write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") + time.sleep(seconds_delay) + percent_setting_read = await self.read_percent_setting(endpoint=endpoint) + if write_status == Status.Success: + asserts.assert_equal(percent_setting_read, percent_setting_write, f"PercentSetting not set to {percent_setting_write}") + fan_mode_read = await self.read_fan_mode(endpoint=endpoint) + time.sleep(seconds_delay) + percent_current_read = await self.read_percent_current(endpoint=endpoint) + asserts.assert_equal(percent_current_read, percent_setting_write, f"PercentCurrent not set to {percent_setting_write}") + asserts.assert_equal(fan_mode_read, Clusters.FanControl.Enums.FanModeEnum.kHigh, "FanMode must be High.") + else: + asserts.assert_equal(percent_setting_read, percent_setting_before_write, "PercentSetting is not unchanged") + asserts.assert_equal(percent_current_read, percent_setting_before_write, "PercentCurrent is not unchanged") - # new_fan_mode = await self.read_fan_mode(endpoint=endpoint) - # if status == Status.Success: - # asserts.assert_equal(new_fan_mode, Clusters.FanControl.Enums.FanModeEnum.kOff, "FanMode is not Off") - # else: - # asserts.assert_equal(new_fan_mode, current_fan_mode, "FanMode is not unchanged") if __name__ == "__main__": From f8979b6d6e75c9d6001fba63144e6669b72203b3 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Thu, 16 Jan 2025 15:43:49 -0800 Subject: [PATCH 05/53] update loop test --- src/python_testing/TC_FAN_3_1.py | 543 +++++------------- .../chip/testing/matter_testing.py | 38 ++ 2 files changed, 188 insertions(+), 393 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 6d0164e12f11da..58bf184b4f3b0e 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -34,68 +34,52 @@ # quiet: true # === END CI TEST ARGUMENTS === +import asyncio import logging import time import random +import queue import chip.clusters as Clusters from chip.interaction_model import Status from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main from mobly import asserts +from matter_testing_infrastructure.chip.testing.matter_testing import AttributeChangeCallbackFan, AttributeValue, ClusterAttributeChangeAccumulator +from chip.ChipDeviceCtrl import ChipDeviceController +from chip.clusters.Attribute import AsyncReadTransaction, AttributePath, SubscriptionTransaction, TypedAttributePath + + +# import chip.clusters as Clusters +from chip.ChipDeviceCtrl import ChipDeviceController +from chip.clusters import ClusterObjects as ClusterObjects +from chip.clusters.Attribute import AsyncReadTransaction, AttributePath, SubscriptionTransaction, TypedAttributePath +from chip.clusters.enum import MatterIntEnum +from chip.exceptions import ChipStackError +from chip.interaction_model import Status +from chip.testing.basic_composition import BasicCompositionTests +from chip.testing.matter_testing import (AttributeChangeCallback, EventChangeCallback, MatterBaseTest, TestStep, async_test_body, + default_matter_test_main) + # import pdb logger = logging.getLogger(__name__) - class TC_FAN_3_1(MatterBaseTest): - - def steps_TC_FAN_3_1(self): - return [TestStep("1", "Commissioning already done."), - TestStep("2", "Action", - "Verification"), - TestStep("3", "Action", - "Verification"), - TestStep("4", "Action", - "Verification"), - TestStep("5", "Action", - "Verification"), - TestStep("6", "Action", - "Verification"), - TestStep("7", "Action", - "Verification"), - TestStep("8", "Action", - "Verification"), - TestStep("9", "Action", - "Verification"), - TestStep("10", "Action", - "Verification"), - TestStep("11", "Action", - "Verification"), - ] - + # def steps_TC_FAN_3_1(self): + # return [TestStep("1", "Commissioning already done."), + # TestStep("2", "Action", "Verification"), + # ] async def read_fc_attribute_expect_success(self, endpoint, attribute): - cluster = Clusters.Objects.FanControl + cluster = Clusters.Objects.BasicInformation return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) async def read_fan_mode(self, endpoint): return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.FanMode) - async def read_percent_setting(self, endpoint): - return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.PercentSetting) - async def read_percent_current(self, endpoint): return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.PercentCurrent) - async def read_speed_setting(self, endpoint): - return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.SpeedSetting) - - async def read_speed_current(self, endpoint): - return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.SpeedCurrent) - - async def read_fan_mode_sequence(self, endpoint): - return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.FanModeSequence) - async def write_fan_mode(self, endpoint, fan_mode) -> Status: result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, Clusters.FanControl.Attributes.FanMode(fan_mode))]) return result[0].Status @@ -107,367 +91,140 @@ async def write_percent_setting(self, endpoint, percent_setting) -> Status: def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] + @async_test_body async def test_TC_FAN_3_1(self): # pdb.set_trace() - endpoint = self.get_endpoint(default=1) - speed_attributes_present = self.check_pics("FAN.S.F00") - seconds_delay = 1 + # endpoint = self.get_endpoint(default=1) + # cluster = Clusters.FanControl + # fan_mode_off = cluster.Enums.FanModeEnum.kOff + # fan_mode_low = cluster.Enums.FanModeEnum.kLow + # fan_mode_medium = cluster.Enums.FanModeEnum.kMedium + # fan_mode_high = cluster.Enums.FanModeEnum.kHigh + # fan_mode_attribute = cluster.Attributes.FanMode + # attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + + + + + + + + + + + + + + + + # Sub info + TH: ChipDeviceController = self.default_controller + min_interval_floor_sec: int = 0 + max_interval_ceiling_sec: int = 15 + + # Define attributes + # endpoint = 1 + # attr_to_sub = Clusters.FanControl.Attributes.FanMode + # attr_to_sub_path = [(endpoint, attr_to_sub)] + # attr_to_write = Clusters.FanControl.Attributes.PercentSetting + + # Switcharoo to Node Label + endpoint = 0 + attr_to_sub = Clusters.BasicInformation.Attributes.NodeLabel + attr_to_sub_path = [(endpoint, attr_to_sub)] + attr_to_write = attr_to_sub + + # Subscribe to attribute + sub = await TH.ReadAttribute( + nodeid=self.dut_node_id, + attributes=attr_to_sub_path, + reportInterval=(min_interval_floor_sec, max_interval_ceiling_sec), + keepSubscriptions=True + ) + + # Set attribute update callback + update_cb = AttributeChangeCallbackFan(attr_to_sub) + sub.SetAttributeUpdateCallback(update_cb) + + # Updates loop + for value_to_write in range(1, 11): + + # Write to attribute + result = await TH.WriteAttribute( + self.dut_node_id, + [(endpoint, attr_to_write(value=value_to_write * 10))] + ) + print(f"\t\t [FANS] write_status: {result[0].Status.name}\n\n\n\n") + + # Wait for update callback and attribute value + update_callback_start_time = time.time() + attr_value = update_cb.wait_for_report() + update_callback_end_time = time.time() + update_callback_total_time = update_callback_end_time - update_callback_start_time + print(f"\t\t [FANS] ** update_callback_total_time: {update_callback_total_time}, value written: {value_to_write}, attribute value: {attr_value} **") + + + + + + + + + + + + + + - # FanMode-PercentSetting mapping provided by the Air Purifier app - # +------------+----------------+----------------------+ - # | | PercentSetting | PercentSetting after | - # | FanMode | range | setting FanMode | - # +------------+----------------+----------------------+ - # | 0 (Off) | 0 | 0 | - # +------------+----------------+----------------------+ - # | 1 (Low) | 1 - 30 | 10 | - # +------------+----------------+----------------------+ - # | 2 (Medium) | 31 - 70 | 40 | - # +------------+----------------+----------------------+ - # | 3 (High) | 71 - 100 | 80 | - # +------------+----------------+----------------------+ - percent_setting_range_off = 0 - percent_setting_range_low = random.randint(1, 30) - percent_setting_range_medium = random.randint(31, 70) - percent_setting_range_high = random.randint(71, 100) - # *** STEP 1 *** - # Commissioning already done - self.step("1") - - # *** STEP 2 *** - # The TH reads the FanMode attribute and stores it before attempting to write to it - # Verify that the DUT response contains a FanModeEnum value - # The TH writes the Low value to the FanMode attribute on the DUT and reads it back after a few seconds, - # and stores it - # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS - # Verify that the FanMode read matches the FanMode written - # Verify that the PercentSetting and PercentCurrent values are non-zero - # Verify that the PercentSetting and PercentCurrent values are greater than the Off FanMode value - # If INVALID_IN_STATE - # Verify that the FanMode matches the FanMode before write attempt - self.step("2") - fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) - asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") - fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kLow - write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) - write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") - time.sleep(seconds_delay) - fan_mode_read = await self.read_fan_mode(endpoint=endpoint) - if write_status == Status.Success: - asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Low") - percent_setting_low = await self.read_percent_setting(endpoint=endpoint) - percent_current_low = await self.read_percent_current(endpoint=endpoint) - asserts.assert_greater(percent_setting_low, 0, "PercentSetting value must be greater than 0.") - asserts.assert_greater(percent_current_low, 0, "PercentCurrent value must be greater than 0.") - asserts.assert_greater(percent_setting_low, percent_setting_off, "PercentSetting value in Low FanMode must be greater than in Off FanMode.") - asserts.assert_greater(percent_current_low, percent_current_off, "PercentCurrent value in Low FanMode must be greater than in Off FanMode.") - else: - asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") - - # *** STEP 3 *** - # The TH reads the FanMode attribute and stores it before attempting to write to it - # Verify that the DUT response contains a FanModeEnum value - # The TH writes the Medium value to the FanMode attribute on the DUT and reads it back after a few seconds, - # and stores it - # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS - # Verify that the FanMode read matches the FanMode written - # Verify that the PercentSetting and PercentCurrent values are non-zero - # Verify that the PercentSetting and PercentCurrent values are greater than the Low FanMode values - # If INVALID_IN_STATE - # Verify that the FanMode matches the FanMode before write attempt - self.step("3") - fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) - asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") - fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kMedium - write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) - write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") - time.sleep(seconds_delay) - fan_mode_read = await self.read_fan_mode(endpoint=endpoint) - if write_status == Status.Success: - asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Medium") - percent_setting_medium = await self.read_percent_setting(endpoint=endpoint) - percent_current_medium = await self.read_percent_current(endpoint=endpoint) - asserts.assert_greater(percent_setting_medium, 0, "PercentSetting value must be greater than 0.") - asserts.assert_greater(percent_current_medium, 0, "PercentCurrent value must be greater than 0.") - asserts.assert_greater(percent_setting_medium, percent_setting_low, "PercentSetting value in Medium FanMode must be greater than in Low FanMode.") - asserts.assert_greater(percent_current_medium, percent_current_low, "PercentCurrent value in Medium FanMode must be greater than in Low FanMode.") - else: - asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") - - # *** STEP 4 *** - # The TH reads the FanMode attribute and stores it before attempting to write to it - # Verify that the DUT response contains a FanModeEnum value - # The TH writes the High value to the FanMode attribute on the DUT and reads it back after a few seconds, - # and stores it - # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS - # Verify that the FanMode read matches the FanMode written - # Verify that the PercentSetting and PercentCurrent values are non-zero - # Verify that the PercentSetting and PercentCurrent values are less than the High FanMode values - # If INVALID_IN_STATE - # Verify that the FanMode matches the FanMode before write attempt - self.step("4") - fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) - asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") - fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kHigh - write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) - write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") - time.sleep(seconds_delay) - fan_mode_read = await self.read_fan_mode(endpoint=endpoint) - if write_status == Status.Success: - asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to High") - percent_setting_high = await self.read_percent_setting(endpoint=endpoint) - percent_current_high = await self.read_percent_current(endpoint=endpoint) - asserts.assert_greater(percent_setting_high, 0, "PercentSetting value must be greater than 0.") - asserts.assert_greater(percent_current_high, 0, "PercentCurrent value must be greater than 0.") - asserts.assert_greater(percent_setting_high, percent_setting_medium, "PercentSetting value in High FanMode must be greater than in Medium FanMode.") - asserts.assert_greater(percent_current_high, percent_current_medium, "PercentCurrent value in High FanMode must be greater than in Medium FanMode.") - else: - asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") - - # *** STEP 5 *** - # The TH reads the FanMode attribute and stores it before attempting to write to it - # Verify that the DUT response contains a FanModeEnum value - # The TH writes the Medium value to the FanMode attribute on the DUT and reads it back after a few seconds, - # and stores it - # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS - # Verify that the FanMode read matches the FanMode written - # Verify that the PercentSetting and PercentCurrent values are non-zero - # Verify that the PercentSetting and PercentCurrent values are less than the High FanMode values - # If INVALID_IN_STATE - # Verify that the FanMode matches the FanMode before write attempt - self.step("5") - fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) - asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") - fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kMedium - write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) - write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") - time.sleep(seconds_delay) - fan_mode_read = await self.read_fan_mode(endpoint=endpoint) - if write_status == Status.Success: - asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Medium") - percent_setting_medium = await self.read_percent_setting(endpoint=endpoint) - percent_current_medium = await self.read_percent_current(endpoint=endpoint) - asserts.assert_greater(percent_setting_medium, 0, "PercentSetting value must be greater than 0.") - asserts.assert_greater(percent_current_medium, 0, "PercentCurrent value must be greater than 0.") - asserts.assert_less(percent_setting_medium, percent_setting_high, "PercentSetting value in Medium FanMode must be less than in High FanMode.") - asserts.assert_less(percent_current_medium, percent_current_high, "PercentCurrent value in Medium FanMode must be less than in High FanMode.") - else: - asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") - - # *** STEP 6 *** - # The TH reads the FanMode attribute and stores it before attempting to write to it - # Verify that the DUT response contains a FanModeEnum value - # The TH writes the Low value to the FanMode attribute on the DUT and reads it back after a few seconds, - # and stores it - # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS - # Verify that the FanMode read matches the FanMode written - # Verify that the PercentSetting and PercentCurrent values are non-zero - # Verify that the PercentSetting and PercentCurrent values are less than the Medium FanMode values - # If INVALID_IN_STATE - # Verify that the FanMode matches the FanMode before write attempt - self.step("6") - fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) - asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") - fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kLow - write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) - write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") - time.sleep(seconds_delay) - fan_mode_read = await self.read_fan_mode(endpoint=endpoint) - if write_status == Status.Success: - asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Low") - percent_setting_low = await self.read_percent_setting(endpoint=endpoint) - percent_current_low = await self.read_percent_current(endpoint=endpoint) - asserts.assert_greater(percent_setting_low, 0, "PercentSetting value must be greater than 0.") - asserts.assert_greater(percent_current_low, 0, "PercentCurrent value must be greater than 0.") - asserts.assert_less(percent_setting_low, percent_setting_medium, "PercentSetting value in Low FanMode must be less than in Low FanMode.") - asserts.assert_less(percent_current_low, percent_current_medium, "PercentCurrent value in Low FanMode must be less than in Low FanMode.") - else: - asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") - - # *** STEP 7 *** - # The TH reads the FanMode attribute and stores it before attempting to write to it - # Verify that the DUT response contains a FanModeEnum value - # The TH writes the Off value to the FanMode attribute on the DUT and reads it back after a few seconds, - # and stores it - # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS - # Verify that the FanMode read matches the FanMode written - # Verify that the PercentSetting and PercentCurrent values are 0 - # If INVALID_IN_STATE - # Verify that the FanMode matches the FanMode before write attempt - self.step("7") - fan_mode_before_write = await self.read_fan_mode(endpoint=endpoint) - asserts.assert_in(fan_mode_before_write, Clusters.FanControl.Enums.FanModeEnum, "The FanMode read response does not contain a FanModeEnum value.") - fan_mode_write = Clusters.FanControl.Enums.FanModeEnum.kOff - write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_write) - write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_ok, "FanMode write did not return a value of Success or InvalidInState") - time.sleep(seconds_delay) - fan_mode_read = await self.read_fan_mode(endpoint=endpoint) - if write_status == Status.Success: - asserts.assert_equal(fan_mode_read, fan_mode_write, f"FanMode is not set to Off") - percent_setting_off = await self.read_percent_setting(endpoint=endpoint) - percent_current_off = await self.read_percent_current(endpoint=endpoint) - asserts.assert_equal(percent_setting_off, 0, "PercentSetting value must be 0 when a FanMode Off is set.") - asserts.assert_equal(percent_current_off, 0, "PercentCurrent value must be 0 when a FanMode Off is set.") - else: - asserts.assert_equal(fan_mode_read, fan_mode_before_write, "FanMode is not unchanged") - - - - - - - - - - - - - - - - - - - - # *** STEP 8 *** - # The TH reads the PercentSetting attribute and stores it before attempting to write to it - # The TH writes 0 to the PercentSetting attribute on the DUT, reads it back after a few - # seconds, and stores it - # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS - # Verify that the PercentSetting read matches the PercentSetting written - # Verify that the PercentCurrent read matches the PercentSetting written (after a few seconds) - # Verify that FanMode = Off - # If INVALID_IN_STATE - # Verify that the PercentSetting read matches the PercentSetting before write attempt - # Verify that the PercentCurrent read matches the PercentSetting before write attempt - self.step("8") - percent_setting_before_write = await self.read_percent_setting(endpoint=endpoint) - percent_setting_write = percent_setting_range_off - write_status = await self.write_percent_setting(endpoint=endpoint, percent_setting=percent_setting_write) - write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") - time.sleep(seconds_delay) - percent_setting_read = await self.read_percent_setting(endpoint=endpoint) - if write_status == Status.Success: - asserts.assert_equal(percent_setting_read, percent_setting_write, f"PercentSetting not set to {percent_setting_write}") - fan_mode_read = await self.read_fan_mode(endpoint=endpoint) - time.sleep(seconds_delay) - percent_current_read = await self.read_percent_current(endpoint=endpoint) - asserts.assert_equal(percent_current_read, percent_setting_write, f"PercentCurrent not set to {percent_setting_write}") - asserts.assert_equal(fan_mode_read, Clusters.FanControl.Enums.FanModeEnum.kOff, "FanMode must be Off.") - else: - asserts.assert_equal(percent_setting_read, percent_setting_before_write, "PercentSetting is not unchanged") - asserts.assert_equal(percent_current_read, percent_setting_before_write, "PercentCurrent is not unchanged") - - # *** STEP 9 *** - # The TH reads the PercentSetting attribute and stores it before attempting to write to it - # The TH writes a value corresponding to the Low FanMode range to the PercentSetting attribute - # on the DUT, reads it back after a few seconds, and stores it - # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS - # Verify that the PercentSetting read matches the PercentSetting written - # Verify that the PercentCurrent read matches the PercentSetting written (after a few seconds) - # Verify that FanMode = Low - # If INVALID_IN_STATE - # Verify that the PercentSetting read matches the PercentSetting before write attempt - # Verify that the PercentCurrent read matches the PercentSetting before write attempt - self.step("9") - percent_setting_before_write = await self.read_percent_setting(endpoint=endpoint) - percent_setting_write = percent_setting_range_low - write_status = await self.write_percent_setting(endpoint=endpoint, percent_setting=percent_setting_write) - write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") - time.sleep(seconds_delay) - percent_setting_read = await self.read_percent_setting(endpoint=endpoint) - if write_status == Status.Success: - asserts.assert_equal(percent_setting_read, percent_setting_write, f"PercentSetting not set to {percent_setting_write}") - fan_mode_read = await self.read_fan_mode(endpoint=endpoint) - time.sleep(seconds_delay) - percent_current_read = await self.read_percent_current(endpoint=endpoint) - asserts.assert_equal(percent_current_read, percent_setting_write, f"PercentCurrent not set to {percent_setting_write}") - asserts.assert_equal(fan_mode_read, Clusters.FanControl.Enums.FanModeEnum.kLow, "FanMode must be Low.") - else: - asserts.assert_equal(percent_setting_read, percent_setting_before_write, "PercentSetting is not unchanged") - asserts.assert_equal(percent_current_read, percent_setting_before_write, "PercentCurrent is not unchanged") - - # *** STEP 10 *** - # The TH reads the PercentSetting attribute and stores it before attempting to write to it - # The TH writes a value corresponding to the Medium FanMode range to the PercentSetting attribute - # on the DUT, reads it back after a few seconds, and stores it - # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS - # Verify that the PercentSetting read matches the PercentSetting written - # Verify that the PercentCurrent read matches the PercentSetting written (after a few seconds) - # Verify that FanMode = Medium - # If INVALID_IN_STATE - # Verify that the PercentSetting read matches the PercentSetting before write attempt - # Verify that the PercentCurrent read matches the PercentSetting before write attempt - self.step("10") - percent_setting_before_write = await self.read_percent_setting(endpoint=endpoint) - percent_setting_write = percent_setting_range_medium - write_status = await self.write_percent_setting(endpoint=endpoint, percent_setting=percent_setting_write) - write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") - time.sleep(seconds_delay) - percent_setting_read = await self.read_percent_setting(endpoint=endpoint) - if write_status == Status.Success: - asserts.assert_equal(percent_setting_read, percent_setting_write, f"PercentSetting not set to {percent_setting_write}") - fan_mode_read = await self.read_fan_mode(endpoint=endpoint) - time.sleep(seconds_delay) - percent_current_read = await self.read_percent_current(endpoint=endpoint) - asserts.assert_equal(percent_current_read, percent_setting_write, f"PercentCurrent not set to {percent_setting_write}") - asserts.assert_equal(fan_mode_read, Clusters.FanControl.Enums.FanModeEnum.kMedium, "FanMode must be Medium.") - else: - asserts.assert_equal(percent_setting_read, percent_setting_before_write, "PercentSetting is not unchanged") - asserts.assert_equal(percent_current_read, percent_setting_before_write, "PercentCurrent is not unchanged") - - # *** STEP 11 *** - # The TH reads the PercentSetting attribute and stores it before attempting to write to it - # The TH writes a value corresponding to the High FanMode range to the PercentSetting attribute - # on the DUT, reads it back after a few seconds, and stores it - # Verify that the DUT returns either a SUCCESS or INVALID_IN_STATE status code - # If SUCCESS - # Verify that the PercentSetting read matches the PercentSetting written - # Verify that the PercentCurrent read matches the PercentSetting written (after a few seconds) - # Verify that FanMode = High - # If INVALID_IN_STATE - # Verify that the PercentSetting read matches the PercentSetting before write attempt - # Verify that the PercentCurrent read matches the PercentSetting before write attempt - self.step("11") - percent_setting_before_write = await self.read_percent_setting(endpoint=endpoint) - percent_setting_write = percent_setting_range_high - write_status = await self.write_percent_setting(endpoint=endpoint, percent_setting=percent_setting_write) - write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") - time.sleep(seconds_delay) - percent_setting_read = await self.read_percent_setting(endpoint=endpoint) - if write_status == Status.Success: - asserts.assert_equal(percent_setting_read, percent_setting_write, f"PercentSetting not set to {percent_setting_write}") - fan_mode_read = await self.read_fan_mode(endpoint=endpoint) - time.sleep(seconds_delay) - percent_current_read = await self.read_percent_current(endpoint=endpoint) - asserts.assert_equal(percent_current_read, percent_setting_write, f"PercentCurrent not set to {percent_setting_write}") - asserts.assert_equal(fan_mode_read, Clusters.FanControl.Enums.FanModeEnum.kHigh, "FanMode must be High.") - else: - asserts.assert_equal(percent_setting_read, percent_setting_before_write, "PercentSetting is not unchanged") - asserts.assert_equal(percent_current_read, percent_setting_before_write, "PercentCurrent is not unchanged") + + + + + + + + + + + + + + + # # Initializa to FanMode Off + # write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_off) + # write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + # asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") + # # Set initial conditions + # last_fan_mode = fan_mode_off + # last_percent_current = 0 + + # for percent_to_write in range(1, 101): + # # Write PercentSetting to DUT + # write_status = await self.write_percent_setting(endpoint=endpoint, percent_setting=percent_to_write) + # write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) + # asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") + + # # Wait + # # await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) + # # expected_fan_mode = AttributeValue(endpoint_id=endpoint, attribute=fan_mode_attribute, value=fan_mode_low) + # # attribute_subscription.await_all_final_values_reported(expected_final_values=[expected_fan_mode], timeout_sec=1) + + # current_fan_mode = await self.read_fan_mode(endpoint=endpoint) + # current_percent_current = await self.read_percent_current(endpoint=endpoint) + + # if current_fan_mode != last_fan_mode: + # print(f"\n\n\n\n\t\t\t\t\t [FANS] fan mode has changed to: {current_fan_mode}") + # print(f"\t\t\t\t\t [FANS] read_percent_current: {current_percent_current} \n\n\n\n\t\t\t\t\t") + # asserts.assert_greater(current_percent_current, last_percent_current) + # last_fan_mode = current_fan_mode + # last_percent_current = current_percent_current + # else: + # print(f"\n\n\n\n\t\t\t\t\t [FANS] fan mode is the same: {current_fan_mode}") diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py index 46c32a2e178b79..b63a7cadcb7856 100644 --- a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py +++ b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py @@ -344,6 +344,44 @@ def wait_for_report(self): asserts.fail(f"[AttributeChangeCallback] Attribute {self._expected_attribute} not found in returned report") +class AttributeChangeCallbackFan: # COPY of AttributeChangeCallback to fiddle with + def __init__(self, expected_attribute: ClusterObjects.ClusterAttributeDescriptor): + self._output: queue.Queue = queue.Queue() + self._expected_attribute = expected_attribute + print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan __init__ queue: {self._output.queue}") + + def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransaction): + """This is the subscription callback when an attribute is updated. + It checks the passed in attribute is the same as the subscribed to attribute and + then posts it into the queue for later processing.""" + + asserts.assert_equal(path.AttributeType, self._expected_attribute, + f"[AttributeChangeCallback] Attribute mismatch. Expected: {self._expected_attribute}, received: {path.AttributeType}") + logging.debug(f"[AttributeChangeCallback] Attribute update callback for {path.AttributeType}") + q = (path, transaction) + self._output.put(q) + print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan __call__ queue: {self._output.queue}") + + def wait_for_report(self): + print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan wait_for_report START") + + try: + path, transaction = self._output.get(block=True, timeout=10) + except queue.Empty: + asserts.fail( + f"[AttributeChangeCallback] Failed to receive a report for the {self._expected_attribute} attribute change") + + asserts.assert_equal(path.AttributeType, self._expected_attribute, + f"[AttributeChangeCallback] Received incorrect report. Expected: {self._expected_attribute}, received: {path.AttributeType}") + try: + attribute_value = transaction.GetAttribute(path) + logging.info( + f"[AttributeChangeCallback] Got attribute subscription report. Attribute {path.AttributeType}. Updated value: {attribute_value}. SubscriptionId: {transaction.subscriptionId}") + except KeyError: + asserts.fail(f"[AttributeChangeCallback] Attribute {self._expected_attribute} not found in returned report") + + return attribute_value + def clear_queue(report_queue: queue.Queue): """Flush all contents of a report queue. Useful to get back to empty point.""" while not report_queue.empty(): From 7665ff6e5dfeb149c845c15edee3a14d26d8bb54 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Thu, 16 Jan 2025 22:54:22 -0800 Subject: [PATCH 06/53] update loop test --- .../chip/testing/matter_testing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py index b63a7cadcb7856..9ed763145de27c 100644 --- a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py +++ b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py @@ -348,7 +348,7 @@ class AttributeChangeCallbackFan: # COPY of AttributeChangeCallback to fiddle wi def __init__(self, expected_attribute: ClusterObjects.ClusterAttributeDescriptor): self._output: queue.Queue = queue.Queue() self._expected_attribute = expected_attribute - print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan __init__ queue: {self._output.queue}") + # print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan __init__ queue: {self._output.queue}") def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransaction): """This is the subscription callback when an attribute is updated. @@ -363,7 +363,7 @@ def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransactio print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan __call__ queue: {self._output.queue}") def wait_for_report(self): - print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan wait_for_report START") + print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan wait_for_report") try: path, transaction = self._output.get(block=True, timeout=10) From eea9420f850a03efdda2508d10d1b1f17899268e Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 27 Jan 2025 11:21:18 -0800 Subject: [PATCH 07/53] wip --- src/python_testing/TC_FAN_3_1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 58bf184b4f3b0e..4addec90370cd6 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -154,7 +154,7 @@ async def test_TC_FAN_3_1(self): # Write to attribute result = await TH.WriteAttribute( self.dut_node_id, - [(endpoint, attr_to_write(value=value_to_write * 10))] + [(endpoint, attr_to_write(value=value_to_write))] ) print(f"\t\t [FANS] write_status: {result[0].Status.name}\n\n\n\n") From 25fa3d6b96ee653f35e5e3734e94dfe3c49da8a2 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 28 Jan 2025 09:30:30 -0800 Subject: [PATCH 08/53] wip --- src/python_testing/TC_FAN_3_1.py | 210 +++++++++++++++++++++++++------ 1 file changed, 171 insertions(+), 39 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 4addec90370cd6..0048cfffea5626 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -39,10 +39,12 @@ import time import random import queue +from typing import Any import chip.clusters as Clusters +from chip.clusters.Types import NullValue from chip.interaction_model import Status -from chip.testing.matter_testing import MatterBaseTest, TestStep, async_test_body, default_matter_test_main +from chip.testing.matter_testing import MatterBaseTest, AttributeValue, ClusterAttributeChangeAccumulator, TestStep, async_test_body, default_matter_test_main from mobly import asserts from matter_testing_infrastructure.chip.testing.matter_testing import AttributeChangeCallbackFan, AttributeValue, ClusterAttributeChangeAccumulator @@ -87,6 +89,54 @@ async def write_fan_mode(self, endpoint, fan_mode) -> Status: async def write_percent_setting(self, endpoint, percent_setting) -> Status: result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, Clusters.FanControl.Attributes.PercentSetting(percent_setting))]) return result[0].Status + + + + + async def read_valcc_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.ValveConfigurationAndControl + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + + + + + + + def await_sequence_of_reports_fan(report_queue: queue.Queue, endpoint_id: int, attribute: TypedAttributePath, timeout_sec: float) -> None: + start_time = time.time() + elapsed = 0.0 + time_remaining = timeout_sec + + while time_remaining > 0: + logging.info(f"Waiting for {timeout_sec:.1f} seconds for attribute.") + try: + item: AttributeValue = report_queue.get(block=True, timeout=time_remaining) + + # Track arrival of all values for the given attribute. + if item.endpoint_id == endpoint_id and item.attribute == attribute: + + print(f"\n\n\n\n\t\t\t [FANS] Got attr: {item}\n\n\n\n") + + except queue.Empty: + # No error, we update timeouts and keep going + pass + + elapsed = time.time() - start_time + time_remaining = timeout_sec - elapsed + + + + + + + + + + + + + def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] @@ -94,6 +144,7 @@ def pics_TC_FAN_3_1(self) -> list[str]: @async_test_body async def test_TC_FAN_3_1(self): + print(f"\n\n\n\n\t\t\t [FANS] Script Start!\n\n\n\n") # pdb.set_trace() # endpoint = self.get_endpoint(default=1) @@ -106,6 +157,66 @@ async def test_TC_FAN_3_1(self): # attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + endpoint = self.get_endpoint(default=1) + TH: ChipDeviceController = self.default_controller + + cluster = Clusters.FanControl + attr_to_sub = cluster.Attributes.FanMode + attr_to_sub_path = [(endpoint, attr_to_sub)] + attr_to_write = cluster.Attributes.PercentSetting + attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + await attribute_subscription.start(TH, self.dut_node_id, endpoint) + + print(f"\n\n\n\n\t\t\t [FANS] Let's loop!\n\n\n\n") + + for value_to_write in range(1, 101): + # value_to_write = value_to_write * 10 + + attribute_subscription.get_last_report() + + result = await TH.WriteAttribute( + self.dut_node_id, + [(endpoint, attr_to_write(value=value_to_write))] + ) + + timeout_sec = 1 + start_time = time.time() + elapsed = 0.0 + time_remaining = timeout_sec + + print(f"[FANS] Looking for FanMode... {value_to_write}") + break_out = False + while time_remaining > 0: + q: queue.Queue = attribute_subscription.attribute_queue + + for item in list(q.queue): + if item.attribute == attr_to_sub: + print(f"[FANS] item: {item.attribute}, value: {item.value}") + break_out = True + break + + elapsed = time.time() - start_time + time_remaining = timeout_sec - elapsed + + if break_out: + break + + total_time = time.time() - start_time + print(f"[FANS] total_time: {total_time}") + + + + + + + + + + + + + + @@ -119,51 +230,72 @@ async def test_TC_FAN_3_1(self): - # Sub info - TH: ChipDeviceController = self.default_controller - min_interval_floor_sec: int = 0 - max_interval_ceiling_sec: int = 15 - # Define attributes + + + + + + + + + + + + + + + + + + + + + # # Sub info + # TH: ChipDeviceController = self.default_controller + # min_interval_floor_sec: int = 0 + # max_interval_ceiling_sec: int = 15 + + # # Define attributes # endpoint = 1 # attr_to_sub = Clusters.FanControl.Attributes.FanMode # attr_to_sub_path = [(endpoint, attr_to_sub)] # attr_to_write = Clusters.FanControl.Attributes.PercentSetting - # Switcharoo to Node Label - endpoint = 0 - attr_to_sub = Clusters.BasicInformation.Attributes.NodeLabel - attr_to_sub_path = [(endpoint, attr_to_sub)] - attr_to_write = attr_to_sub - - # Subscribe to attribute - sub = await TH.ReadAttribute( - nodeid=self.dut_node_id, - attributes=attr_to_sub_path, - reportInterval=(min_interval_floor_sec, max_interval_ceiling_sec), - keepSubscriptions=True - ) - - # Set attribute update callback - update_cb = AttributeChangeCallbackFan(attr_to_sub) - sub.SetAttributeUpdateCallback(update_cb) + # # Switcharoo to Node Label + # # endpoint = 0 + # # attr_to_sub = Clusters.BasicInformation.Attributes.NodeLabel + # # attr_to_sub_path = [(endpoint, attr_to_sub)] + # # attr_to_write = attr_to_sub + + # # Subscribe to attribute + # sub = await TH.ReadAttribute( + # nodeid=self.dut_node_id, + # attributes=attr_to_sub_path, + # reportInterval=(min_interval_floor_sec, max_interval_ceiling_sec), + # keepSubscriptions=True + # ) + + # # Set attribute update callback + # update_cb = AttributeChangeCallbackFan(attr_to_sub) + # sub.SetAttributeUpdateCallback(update_cb) - # Updates loop - for value_to_write in range(1, 11): - - # Write to attribute - result = await TH.WriteAttribute( - self.dut_node_id, - [(endpoint, attr_to_write(value=value_to_write))] - ) - print(f"\t\t [FANS] write_status: {result[0].Status.name}\n\n\n\n") - - # Wait for update callback and attribute value - update_callback_start_time = time.time() - attr_value = update_cb.wait_for_report() - update_callback_end_time = time.time() - update_callback_total_time = update_callback_end_time - update_callback_start_time - print(f"\t\t [FANS] ** update_callback_total_time: {update_callback_total_time}, value written: {value_to_write}, attribute value: {attr_value} **") + # # Updates loop + # for value_to_write in range(1, 11): + + # # Write to attribute + # result = await TH.WriteAttribute( + # self.dut_node_id, + # [(endpoint, attr_to_write(value=value_to_write))] + # ) + # print(f"\t\t [FANS] write_status: {result[0].Status.name}\n\n\n\n") + + # # Wait for update callback and attribute value + # update_callback_start_time = time.time() + # attr_value = update_cb.wait_for_report() + # update_callback_end_time = time.time() + # update_callback_total_time = update_callback_end_time - update_callback_start_time + # print(f"\t\t [FANS] ** update_callback_total_time: {update_callback_total_time}, value written: {value_to_write}, attribute value: {attr_value} **") From bacfb76f397dccc3ec46a5cba9dae7e1525343ed Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 28 Jan 2025 17:38:48 -0800 Subject: [PATCH 09/53] wip --- src/python_testing/TC_FAN_3_1.py | 266 +++++-------------------------- 1 file changed, 37 insertions(+), 229 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 0048cfffea5626..2cb36019744e4c 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -49,7 +49,7 @@ from matter_testing_infrastructure.chip.testing.matter_testing import AttributeChangeCallbackFan, AttributeValue, ClusterAttributeChangeAccumulator from chip.ChipDeviceCtrl import ChipDeviceController -from chip.clusters.Attribute import AsyncReadTransaction, AttributePath, SubscriptionTransaction, TypedAttributePath +from chip.clusters.Attribute import AttributeStatus, AsyncReadTransaction, AttributePath, SubscriptionTransaction, TypedAttributePath # import chip.clusters as Clusters @@ -73,7 +73,7 @@ class TC_FAN_3_1(MatterBaseTest): # TestStep("2", "Action", "Verification"), # ] async def read_fc_attribute_expect_success(self, endpoint, attribute): - cluster = Clusters.Objects.BasicInformation + cluster = Clusters.Objects.FanControl return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) async def read_fan_mode(self, endpoint): @@ -90,274 +90,82 @@ async def write_percent_setting(self, endpoint, percent_setting) -> Status: result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, Clusters.FanControl.Attributes.PercentSetting(percent_setting))]) return result[0].Status - - - async def read_valcc_attribute_expect_success(self, endpoint, attribute): cluster = Clusters.Objects.ValveConfigurationAndControl return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) - - - - - - - def await_sequence_of_reports_fan(report_queue: queue.Queue, endpoint_id: int, attribute: TypedAttributePath, timeout_sec: float) -> None: - start_time = time.time() - elapsed = 0.0 - time_remaining = timeout_sec - - while time_remaining > 0: - logging.info(f"Waiting for {timeout_sec:.1f} seconds for attribute.") - try: - item: AttributeValue = report_queue.get(block=True, timeout=time_remaining) - - # Track arrival of all values for the given attribute. - if item.endpoint_id == endpoint_id and item.attribute == attribute: - - print(f"\n\n\n\n\t\t\t [FANS] Got attr: {item}\n\n\n\n") - - except queue.Empty: - # No error, we update timeouts and keep going - pass - - elapsed = time.time() - start_time - time_remaining = timeout_sec - elapsed - - - - - - - - - - - - - - def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] @async_test_body async def test_TC_FAN_3_1(self): - print(f"\n\n\n\n\t\t\t [FANS] Script Start!\n\n\n\n") - # pdb.set_trace() - - # endpoint = self.get_endpoint(default=1) - # cluster = Clusters.FanControl - # fan_mode_off = cluster.Enums.FanModeEnum.kOff - # fan_mode_low = cluster.Enums.FanModeEnum.kLow - # fan_mode_medium = cluster.Enums.FanModeEnum.kMedium - # fan_mode_high = cluster.Enums.FanModeEnum.kHigh - # fan_mode_attribute = cluster.Attributes.FanMode - # attribute_subscription = ClusterAttributeChangeAccumulator(cluster) - endpoint = self.get_endpoint(default=1) - TH: ChipDeviceController = self.default_controller - cluster = Clusters.FanControl attr_to_sub = cluster.Attributes.FanMode - attr_to_sub_path = [(endpoint, attr_to_sub)] attr_to_write = cluster.Attributes.PercentSetting attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + TH: ChipDeviceController = self.default_controller + timeout_sec = 0.1 + + # Read FanMode and verify starting value of Off + fan_mode_initial = await self.read_fan_mode(endpoint=endpoint) + asserts.assert_in(fan_mode_initial, cluster.Enums.FanModeEnum, "FanMode read response doesn't contain a FanModeEnum") + + print(f"\n\n\n\n\t\t\t [FANS] Start sub...\n\n\n\n") await attribute_subscription.start(TH, self.dut_node_id, endpoint) print(f"\n\n\n\n\t\t\t [FANS] Let's loop!\n\n\n\n") + fan_mode_current = fan_mode_initial + fan_mode_previous = fan_mode_initial - for value_to_write in range(1, 101): - # value_to_write = value_to_write * 10 + # Increment the PercentSetting attribute from 1 to 100, one by one + for value_to_write in range(1, 11): + value_to_write = value_to_write * 10 + # Clear the queue attribute_subscription.get_last_report() - result = await TH.WriteAttribute( + # Write to attribute + write_result = await TH.WriteAttribute( self.dut_node_id, [(endpoint, attr_to_write(value=value_to_write))] ) + write_status = write_result[0].Status + write_status_success = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_success, "PercentSetting write did not return a value of either Success or InvalidInState") + print(f"[FANS] Wrote value: {value_to_write}") - timeout_sec = 1 + # Wait for the FanMode attribute to be present in the queue + break_out = False start_time = time.time() - elapsed = 0.0 + elapsed = 0 time_remaining = timeout_sec - - print(f"[FANS] Looking for FanMode... {value_to_write}") - break_out = False while time_remaining > 0: q: queue.Queue = attribute_subscription.attribute_queue - for item in list(q.queue): - if item.attribute == attr_to_sub: - print(f"[FANS] item: {item.attribute}, value: {item.value}") + for q_item in list(q.queue): + if q_item.attribute == attr_to_sub: + fan_mode_current = q_item.value + + # Verify the current FanMode is greater than the previous FanMode + asserts.assert_greater(fan_mode_current, fan_mode_previous, "Current FanMode must be greater than previous FanMode") + print(f"\t\t\t[FANS] FanMode change from {fan_mode_previous} to {fan_mode_current}") + fan_mode_previous = fan_mode_current + break_out = True break elapsed = time.time() - start_time time_remaining = timeout_sec - elapsed - + if break_out: break - - total_time = time.time() - start_time - print(f"[FANS] total_time: {total_time}") - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Sub info - # TH: ChipDeviceController = self.default_controller - # min_interval_floor_sec: int = 0 - # max_interval_ceiling_sec: int = 15 - - # # Define attributes - # endpoint = 1 - # attr_to_sub = Clusters.FanControl.Attributes.FanMode - # attr_to_sub_path = [(endpoint, attr_to_sub)] - # attr_to_write = Clusters.FanControl.Attributes.PercentSetting - - # # Switcharoo to Node Label - # # endpoint = 0 - # # attr_to_sub = Clusters.BasicInformation.Attributes.NodeLabel - # # attr_to_sub_path = [(endpoint, attr_to_sub)] - # # attr_to_write = attr_to_sub - - # # Subscribe to attribute - # sub = await TH.ReadAttribute( - # nodeid=self.dut_node_id, - # attributes=attr_to_sub_path, - # reportInterval=(min_interval_floor_sec, max_interval_ceiling_sec), - # keepSubscriptions=True - # ) - - # # Set attribute update callback - # update_cb = AttributeChangeCallbackFan(attr_to_sub) - # sub.SetAttributeUpdateCallback(update_cb) - - # # Updates loop - # for value_to_write in range(1, 11): - - # # Write to attribute - # result = await TH.WriteAttribute( - # self.dut_node_id, - # [(endpoint, attr_to_write(value=value_to_write))] - # ) - # print(f"\t\t [FANS] write_status: {result[0].Status.name}\n\n\n\n") - - # # Wait for update callback and attribute value - # update_callback_start_time = time.time() - # attr_value = update_cb.wait_for_report() - # update_callback_end_time = time.time() - # update_callback_total_time = update_callback_end_time - update_callback_start_time - # print(f"\t\t [FANS] ** update_callback_total_time: {update_callback_total_time}, value written: {value_to_write}, attribute value: {attr_value} **") - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # # Initializa to FanMode Off - # write_status = await self.write_fan_mode(endpoint=endpoint, fan_mode=fan_mode_off) - # write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - # asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") - - # # Set initial conditions - # last_fan_mode = fan_mode_off - # last_percent_current = 0 - - # for percent_to_write in range(1, 101): - # # Write PercentSetting to DUT - # write_status = await self.write_percent_setting(endpoint=endpoint, percent_setting=percent_to_write) - # write_status_ok = (write_status == Status.Success) or (write_status == Status.InvalidInState) - # asserts.assert_true(write_status_ok, "PercentSetting write did not return a value of Success or InvalidInState") - - # # Wait - # # await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) - # # expected_fan_mode = AttributeValue(endpoint_id=endpoint, attribute=fan_mode_attribute, value=fan_mode_low) - # # attribute_subscription.await_all_final_values_reported(expected_final_values=[expected_fan_mode], timeout_sec=1) - - # current_fan_mode = await self.read_fan_mode(endpoint=endpoint) - # current_percent_current = await self.read_percent_current(endpoint=endpoint) + total_time = time.time() - start_time + # print(f"[FANS] total_time: {total_time}") - # if current_fan_mode != last_fan_mode: - # print(f"\n\n\n\n\t\t\t\t\t [FANS] fan mode has changed to: {current_fan_mode}") - # print(f"\t\t\t\t\t [FANS] read_percent_current: {current_percent_current} \n\n\n\n\t\t\t\t\t") - # asserts.assert_greater(current_percent_current, last_percent_current) - # last_fan_mode = current_fan_mode - # last_percent_current = current_percent_current - # else: - # print(f"\n\n\n\n\t\t\t\t\t [FANS] fan mode is the same: {current_fan_mode}") - if __name__ == "__main__": From 89f8416b68396a8ededcd3ee3dce1a0358dd42bc Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 28 Jan 2025 18:27:12 -0800 Subject: [PATCH 10/53] wip --- src/python_testing/TC_FAN_3_1.py | 47 ++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 15 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 2cb36019744e4c..255c17c4f90081 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -98,37 +98,38 @@ def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] - @async_test_body - async def test_TC_FAN_3_1(self): - endpoint = self.get_endpoint(default=1) + + + + async def verify_fan_mode(self, attr_to_write, endpoint, timeout_sec): cluster = Clusters.FanControl attr_to_sub = cluster.Attributes.FanMode - attr_to_write = cluster.Attributes.PercentSetting attribute_subscription = ClusterAttributeChangeAccumulator(cluster) - TH: ChipDeviceController = self.default_controller - timeout_sec = 0.1 - + # TH: ChipDeviceController = self.default_controller + + await self.write_fan_mode(endpoint, cluster.Enums.FanModeEnum.kOff) + # Read FanMode and verify starting value of Off fan_mode_initial = await self.read_fan_mode(endpoint=endpoint) asserts.assert_in(fan_mode_initial, cluster.Enums.FanModeEnum, "FanMode read response doesn't contain a FanModeEnum") print(f"\n\n\n\n\t\t\t [FANS] Start sub...\n\n\n\n") - await attribute_subscription.start(TH, self.dut_node_id, endpoint) + await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) print(f"\n\n\n\n\t\t\t [FANS] Let's loop!\n\n\n\n") fan_mode_current = fan_mode_initial fan_mode_previous = fan_mode_initial - + # Increment the PercentSetting attribute from 1 to 100, one by one for value_to_write in range(1, 11): value_to_write = value_to_write * 10 - + # Clear the queue attribute_subscription.get_last_report() - + # Write to attribute - write_result = await TH.WriteAttribute( + write_result = await self.default_controller.WriteAttribute( self.dut_node_id, [(endpoint, attr_to_write(value=value_to_write))] ) @@ -163,9 +164,25 @@ async def test_TC_FAN_3_1(self): if break_out: break - total_time = time.time() - start_time - # print(f"[FANS] total_time: {total_time}") - + await attribute_subscription.cancel() + + + + + @async_test_body + async def test_TC_FAN_3_1(self): + + endpoint = self.get_endpoint(default=1) + cluster = Clusters.FanControl + attr_to_write = cluster.Attributes.PercentSetting + timeout_sec = 0.1 + + await self.verify_fan_mode(attr_to_write, endpoint, timeout_sec) + await self.verify_fan_mode(attr_to_write, endpoint, timeout_sec) + + + + if __name__ == "__main__": From 8877b048d5ff76cacef75f5286ad8c546e5d84d5 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 28 Jan 2025 22:24:13 -0800 Subject: [PATCH 11/53] wip --- src/python_testing/TC_FAN_3_1.py | 83 ++++++++++++++++++-------------- 1 file changed, 47 insertions(+), 36 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 255c17c4f90081..48f988ec6e2167 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -35,6 +35,7 @@ # === END CI TEST ARGUMENTS === import asyncio +from enum import Enum import logging import time import random @@ -65,6 +66,10 @@ # import pdb +class DirectionEnum(Enum): + Ascending = 1 + Descending = 2 + logger = logging.getLogger(__name__) class TC_FAN_3_1(MatterBaseTest): @@ -86,8 +91,8 @@ async def write_fan_mode(self, endpoint, fan_mode) -> Status: result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, Clusters.FanControl.Attributes.FanMode(fan_mode))]) return result[0].Status - async def write_percent_setting(self, endpoint, percent_setting) -> Status: - result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, Clusters.FanControl.Attributes.PercentSetting(percent_setting))]) + async def write_setting(self, endpoint, attr_to_write, value) -> Status: + result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attr_to_write(value))]) return result[0].Status async def read_valcc_attribute_expect_success(self, endpoint, attribute): @@ -102,59 +107,66 @@ def pics_TC_FAN_3_1(self) -> list[str]: - async def verify_fan_mode(self, attr_to_write, endpoint, timeout_sec): + async def verify_fan_mode(self, attr_to_write, endpoint, direction, timeout_sec): + logging.info(f"[FANS] Monitoring FanMode by writing to {attr_to_write.__name__} in {direction.name} order") + + # Setup cluster = Clusters.FanControl - attr_to_sub = cluster.Attributes.FanMode + attr_to_monitor = cluster.Attributes.FanMode attribute_subscription = ClusterAttributeChangeAccumulator(cluster) - # TH: ChipDeviceController = self.default_controller + fan_mode_off = cluster.Enums.FanModeEnum.kOff + fan_mode_high = cluster.Enums.FanModeEnum.kHigh + + # Determine if values will be written ascending or descending + fan_mode_init = fan_mode_off if direction == DirectionEnum.Ascending else fan_mode_high + range_loop = range(1, 101) if direction == DirectionEnum.Ascending else range(100, -1, -1) - await self.write_fan_mode(endpoint, cluster.Enums.FanModeEnum.kOff) + # Write initial FanMode + write_result = await self.write_fan_mode(endpoint, fan_mode_init) + write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) + asserts.assert_true(write_status_success, f"FanMode write did not return a value of either SUCCESS or INVALID_IN_STATE ({write_result.name})") - # Read FanMode and verify starting value of Off + # Read FanMode back and verify written value fan_mode_initial = await self.read_fan_mode(endpoint=endpoint) asserts.assert_in(fan_mode_initial, cluster.Enums.FanModeEnum, "FanMode read response doesn't contain a FanModeEnum") - print(f"\n\n\n\n\t\t\t [FANS] Start sub...\n\n\n\n") await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) - print(f"\n\n\n\n\t\t\t [FANS] Let's loop!\n\n\n\n") + # Write to attribute iteratively within a range of 100, one at a time fan_mode_current = fan_mode_initial fan_mode_previous = fan_mode_initial - - # Increment the PercentSetting attribute from 1 to 100, one by one - for value_to_write in range(1, 11): - value_to_write = value_to_write * 10 - + for value_to_write in range_loop: # Clear the queue attribute_subscription.get_last_report() # Write to attribute - write_result = await self.default_controller.WriteAttribute( - self.dut_node_id, - [(endpoint, attr_to_write(value=value_to_write))] - ) - write_status = write_result[0].Status - write_status_success = (write_status == Status.Success) or (write_status == Status.InvalidInState) - asserts.assert_true(write_status_success, "PercentSetting write did not return a value of either Success or InvalidInState") - print(f"[FANS] Wrote value: {value_to_write}") - - # Wait for the FanMode attribute to be present in the queue - break_out = False + write_result = await self.write_setting(endpoint, attr_to_write, value_to_write) + write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) + asserts.assert_true(write_status_success, f"Attribute write did not return a value of either Success or InvalidInState") + logging.info(f"[FANS] Attribute value written: {value_to_write}") + + # Wait for the FanMode attribute to appear in the queue until the specified timeout start_time = time.time() elapsed = 0 time_remaining = timeout_sec + break_out = False while time_remaining > 0: - q: queue.Queue = attribute_subscription.attribute_queue + q = attribute_subscription.attribute_queue + # Iterate through the queue items to search for the FanMode attribute for q_item in list(q.queue): - if q_item.attribute == attr_to_sub: + if q_item.attribute == attr_to_monitor: fan_mode_current = q_item.value - # Verify the current FanMode is greater than the previous FanMode - asserts.assert_greater(fan_mode_current, fan_mode_previous, "Current FanMode must be greater than previous FanMode") - print(f"\t\t\t[FANS] FanMode change from {fan_mode_previous} to {fan_mode_current}") + if direction == DirectionEnum.Ascending: + # Verify the current FanMode is greater than the previous FanMode + asserts.assert_greater(fan_mode_current, fan_mode_previous, "Current FanMode must be greater than previous FanMode") + else: + # Verify the current FanMode is less than the previous FanMode + asserts.assert_less(fan_mode_current, fan_mode_previous, "Current FanMode must be less than previous FanMode") + + logging.info(f"[FANS] FanMode change from {fan_mode_previous.name}({fan_mode_previous}) to {fan_mode_current.name}({fan_mode_current})") fan_mode_previous = fan_mode_current - break_out = True break @@ -173,12 +185,11 @@ async def verify_fan_mode(self, attr_to_write, endpoint, timeout_sec): async def test_TC_FAN_3_1(self): endpoint = self.get_endpoint(default=1) - cluster = Clusters.FanControl - attr_to_write = cluster.Attributes.PercentSetting - timeout_sec = 0.1 + attr_to_write = Clusters.FanControl.Attributes.PercentSetting + timeout_sec = 0.2 - await self.verify_fan_mode(attr_to_write, endpoint, timeout_sec) - await self.verify_fan_mode(attr_to_write, endpoint, timeout_sec) + await self.verify_fan_mode(attr_to_write, endpoint, DirectionEnum.Ascending, timeout_sec) + await self.verify_fan_mode(attr_to_write, endpoint, DirectionEnum.Descending, timeout_sec) From b1e4eb1abd8fb61f604ddbac2237dd44e66131d2 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 28 Jan 2025 23:27:19 -0800 Subject: [PATCH 12/53] wip --- src/python_testing/TC_FAN_3_1.py | 106 ++++++++++++------------------- 1 file changed, 39 insertions(+), 67 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 48f988ec6e2167..9a7d6c2adda8ae 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -34,44 +34,24 @@ # quiet: true # === END CI TEST ARGUMENTS === -import asyncio -from enum import Enum import logging import time -import random -import queue -from typing import Any - +from enum import Enum import chip.clusters as Clusters -from chip.clusters.Types import NullValue from chip.interaction_model import Status -from chip.testing.matter_testing import MatterBaseTest, AttributeValue, ClusterAttributeChangeAccumulator, TestStep, async_test_body, default_matter_test_main +from chip.testing.matter_testing import MatterBaseTest, ClusterAttributeChangeAccumulator, TestStep, async_test_body, default_matter_test_main from mobly import asserts - -from matter_testing_infrastructure.chip.testing.matter_testing import AttributeChangeCallbackFan, AttributeValue, ClusterAttributeChangeAccumulator -from chip.ChipDeviceCtrl import ChipDeviceController -from chip.clusters.Attribute import AttributeStatus, AsyncReadTransaction, AttributePath, SubscriptionTransaction, TypedAttributePath - - -# import chip.clusters as Clusters -from chip.ChipDeviceCtrl import ChipDeviceController +from matter_testing_infrastructure.chip.testing.matter_testing import ClusterAttributeChangeAccumulator from chip.clusters import ClusterObjects as ClusterObjects -from chip.clusters.Attribute import AsyncReadTransaction, AttributePath, SubscriptionTransaction, TypedAttributePath -from chip.clusters.enum import MatterIntEnum -from chip.exceptions import ChipStackError from chip.interaction_model import Status -from chip.testing.basic_composition import BasicCompositionTests -from chip.testing.matter_testing import (AttributeChangeCallback, EventChangeCallback, MatterBaseTest, TestStep, async_test_body, - default_matter_test_main) - -# import pdb -class DirectionEnum(Enum): +class OrderEnum(Enum): Ascending = 1 Descending = 2 logger = logging.getLogger(__name__) + class TC_FAN_3_1(MatterBaseTest): # def steps_TC_FAN_3_1(self): # return [TestStep("1", "Commissioning already done."), @@ -81,53 +61,39 @@ async def read_fc_attribute_expect_success(self, endpoint, attribute): cluster = Clusters.Objects.FanControl return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) - async def read_fan_mode(self, endpoint): - return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.FanMode) - - async def read_percent_current(self, endpoint): - return await self.read_fc_attribute_expect_success(endpoint, Clusters.FanControl.Attributes.PercentCurrent) - - async def write_fan_mode(self, endpoint, fan_mode) -> Status: - result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, Clusters.FanControl.Attributes.FanMode(fan_mode))]) - return result[0].Status + async def read_setting(self, endpoint, attribute): + return await self.read_fc_attribute_expect_success(endpoint, attribute) async def write_setting(self, endpoint, attr_to_write, value) -> Status: result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attr_to_write(value))]) return result[0].Status - - async def read_valcc_attribute_expect_success(self, endpoint, attribute): - cluster = Clusters.Objects.ValveConfigurationAndControl - return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) - - def pics_TC_FAN_3_1(self) -> list[str]: - return ["FAN.S"] - - - + async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): + logging.info(f"[FANS] Monitoring FanMode by writing to {attr_to_write.__name__} in {order.name} order") - - async def verify_fan_mode(self, attr_to_write, endpoint, direction, timeout_sec): - logging.info(f"[FANS] Monitoring FanMode by writing to {attr_to_write.__name__} in {direction.name} order") - # Setup cluster = Clusters.FanControl - attr_to_monitor = cluster.Attributes.FanMode + fan_mode_attr = cluster.Attributes.FanMode attribute_subscription = ClusterAttributeChangeAccumulator(cluster) fan_mode_off = cluster.Enums.FanModeEnum.kOff fan_mode_high = cluster.Enums.FanModeEnum.kHigh + # As per the spec, the max values for PercentSetting and SpeedSetting are 100 and SpeedMax respectively + max_value = 100 + if attr_to_write == cluster.Attributes.SpeedSetting: + max_value = await self.read_setting(endpoint, cluster.Attributes.SpeedMax) + # Determine if values will be written ascending or descending - fan_mode_init = fan_mode_off if direction == DirectionEnum.Ascending else fan_mode_high - range_loop = range(1, 101) if direction == DirectionEnum.Ascending else range(100, -1, -1) + fan_mode_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high + range_loop = range(1, max_value + 1) if order == OrderEnum.Ascending else range(max_value, -1, -1) # Write initial FanMode - write_result = await self.write_fan_mode(endpoint, fan_mode_init) + write_result = await self.write_setting(endpoint, fan_mode_attr, fan_mode_init) write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) asserts.assert_true(write_status_success, f"FanMode write did not return a value of either SUCCESS or INVALID_IN_STATE ({write_result.name})") # Read FanMode back and verify written value - fan_mode_initial = await self.read_fan_mode(endpoint=endpoint) + fan_mode_initial = await self.read_setting(endpoint, fan_mode_attr) asserts.assert_in(fan_mode_initial, cluster.Enums.FanModeEnum, "FanMode read response doesn't contain a FanModeEnum") await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) @@ -155,17 +121,17 @@ async def verify_fan_mode(self, attr_to_write, endpoint, direction, timeout_sec) # Iterate through the queue items to search for the FanMode attribute for q_item in list(q.queue): - if q_item.attribute == attr_to_monitor: + if q_item.attribute == fan_mode_attr: fan_mode_current = q_item.value - if direction == DirectionEnum.Ascending: + if order == OrderEnum.Ascending: # Verify the current FanMode is greater than the previous FanMode asserts.assert_greater(fan_mode_current, fan_mode_previous, "Current FanMode must be greater than previous FanMode") else: # Verify the current FanMode is less than the previous FanMode asserts.assert_less(fan_mode_current, fan_mode_previous, "Current FanMode must be less than previous FanMode") - logging.info(f"[FANS] FanMode change from {fan_mode_previous.name}({fan_mode_previous}) to {fan_mode_current.name}({fan_mode_current})") + logging.info(f"[FANS] FanMode changed from {fan_mode_previous.name}({fan_mode_previous}) to {fan_mode_current.name}({fan_mode_current})") fan_mode_previous = fan_mode_current break_out = True break @@ -177,23 +143,29 @@ async def verify_fan_mode(self, attr_to_write, endpoint, direction, timeout_sec) break await attribute_subscription.cancel() - - - + + def pics_TC_FAN_3_1(self) -> list[str]: + return ["FAN.S"] @async_test_body async def test_TC_FAN_3_1(self): - + # Setup endpoint = self.get_endpoint(default=1) - attr_to_write = Clusters.FanControl.Attributes.PercentSetting + percent_setting_attr = Clusters.FanControl.Attributes.PercentSetting + speed_setting_attr = Clusters.FanControl.Attributes.SpeedSetting timeout_sec = 0.2 - await self.verify_fan_mode(attr_to_write, endpoint, DirectionEnum.Ascending, timeout_sec) - await self.verify_fan_mode(attr_to_write, endpoint, DirectionEnum.Descending, timeout_sec) - - - - + # TH writes to the DUT the PercentSetting iteratively within a range of 1 to 100 one at a time in ascending order + await self.verify_fan_mode(endpoint, percent_setting_attr, OrderEnum.Ascending, timeout_sec) + + # TH writes to the DUT the PercentSetting iteratively within a range of 1 to 100 one at a time in descending order + await self.verify_fan_mode(endpoint, percent_setting_attr, OrderEnum.Descending, timeout_sec) + + # TH writes to the DUT the SpeedSetting iteratively within a range of 1 to SpeedMax one at a time in ascending order + await self.verify_fan_mode(endpoint, speed_setting_attr, OrderEnum.Ascending, timeout_sec) + + # TH writes to the DUT the SpeedSetting iteratively within a range of 1 to SpeedMax one at a time in descending order + await self.verify_fan_mode(endpoint, speed_setting_attr, OrderEnum.Descending, timeout_sec) if __name__ == "__main__": From 0f4dc8db864ef1f3f2fe5f3673e2768ef0ce3046 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 28 Jan 2025 23:41:08 -0800 Subject: [PATCH 13/53] wip --- src/python_testing/TC_FAN_3_1.py | 23 ++- .../chip/testing/matter_testing.py | 160 ++++++------------ 2 files changed, 70 insertions(+), 113 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 9a7d6c2adda8ae..73b4ab923b61a0 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -37,18 +37,20 @@ import logging import time from enum import Enum + import chip.clusters as Clusters -from chip.interaction_model import Status -from chip.testing.matter_testing import MatterBaseTest, ClusterAttributeChangeAccumulator, TestStep, async_test_body, default_matter_test_main -from mobly import asserts -from matter_testing_infrastructure.chip.testing.matter_testing import ClusterAttributeChangeAccumulator from chip.clusters import ClusterObjects as ClusterObjects from chip.interaction_model import Status +from chip.testing.matter_testing import (ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, async_test_body, + default_matter_test_main) +from mobly import asserts + class OrderEnum(Enum): Ascending = 1 Descending = 2 + logger = logging.getLogger(__name__) @@ -90,7 +92,8 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): # Write initial FanMode write_result = await self.write_setting(endpoint, fan_mode_attr, fan_mode_init) write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) - asserts.assert_true(write_status_success, f"FanMode write did not return a value of either SUCCESS or INVALID_IN_STATE ({write_result.name})") + asserts.assert_true(write_status_success, + f"FanMode write did not return a value of either SUCCESS or INVALID_IN_STATE ({write_result.name})") # Read FanMode back and verify written value fan_mode_initial = await self.read_setting(endpoint, fan_mode_attr) @@ -126,12 +129,14 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): if order == OrderEnum.Ascending: # Verify the current FanMode is greater than the previous FanMode - asserts.assert_greater(fan_mode_current, fan_mode_previous, "Current FanMode must be greater than previous FanMode") + asserts.assert_greater(fan_mode_current, fan_mode_previous, + "Current FanMode must be greater than previous FanMode") else: # Verify the current FanMode is less than the previous FanMode asserts.assert_less(fan_mode_current, fan_mode_previous, "Current FanMode must be less than previous FanMode") - - logging.info(f"[FANS] FanMode changed from {fan_mode_previous.name}({fan_mode_previous}) to {fan_mode_current.name}({fan_mode_current})") + + logging.info( + f"[FANS] FanMode changed from {fan_mode_previous.name}({fan_mode_previous}) to {fan_mode_current.name}({fan_mode_current})") fan_mode_previous = fan_mode_current break_out = True break @@ -143,7 +148,7 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): break await attribute_subscription.cancel() - + def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py index 166bb7b9d5a155..46c32a2e178b79 100644 --- a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py +++ b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py @@ -344,44 +344,6 @@ def wait_for_report(self): asserts.fail(f"[AttributeChangeCallback] Attribute {self._expected_attribute} not found in returned report") -class AttributeChangeCallbackFan: # COPY of AttributeChangeCallback to fiddle with - def __init__(self, expected_attribute: ClusterObjects.ClusterAttributeDescriptor): - self._output: queue.Queue = queue.Queue() - self._expected_attribute = expected_attribute - # print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan __init__ queue: {self._output.queue}") - - def __call__(self, path: TypedAttributePath, transaction: SubscriptionTransaction): - """This is the subscription callback when an attribute is updated. - It checks the passed in attribute is the same as the subscribed to attribute and - then posts it into the queue for later processing.""" - - asserts.assert_equal(path.AttributeType, self._expected_attribute, - f"[AttributeChangeCallback] Attribute mismatch. Expected: {self._expected_attribute}, received: {path.AttributeType}") - logging.debug(f"[AttributeChangeCallback] Attribute update callback for {path.AttributeType}") - q = (path, transaction) - self._output.put(q) - print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan __call__ queue: {self._output.queue}") - - def wait_for_report(self): - print(f"\n\n\n\t\t [FANS] AttributeChangeCallbackFan wait_for_report") - - try: - path, transaction = self._output.get(block=True, timeout=10) - except queue.Empty: - asserts.fail( - f"[AttributeChangeCallback] Failed to receive a report for the {self._expected_attribute} attribute change") - - asserts.assert_equal(path.AttributeType, self._expected_attribute, - f"[AttributeChangeCallback] Received incorrect report. Expected: {self._expected_attribute}, received: {path.AttributeType}") - try: - attribute_value = transaction.GetAttribute(path) - logging.info( - f"[AttributeChangeCallback] Got attribute subscription report. Attribute {path.AttributeType}. Updated value: {attribute_value}. SubscriptionId: {transaction.subscriptionId}") - except KeyError: - asserts.fail(f"[AttributeChangeCallback] Attribute {self._expected_attribute} not found in returned report") - - return attribute_value - def clear_queue(report_queue: queue.Queue): """Flush all contents of a report queue. Useful to get back to empty point.""" while not report_queue.empty(): @@ -1010,6 +972,18 @@ def __init__(self, *args): # The named pipe name must be set in the derived classes self.app_pipe = None + async def commission_devices(self) -> bool: + conf = self.matter_test_config + + for commission_idx, node_id in enumerate(conf.dut_node_ids): + logging.info( + f"Starting commissioning for root index {conf.root_of_trust_index}, fabric ID 0x{conf.fabric_id:016X}, node ID 0x{node_id:016X}") + logging.info(f"Commissioning method: {conf.commissioning_method}") + + await CommissionDeviceTest.commission_device(self, commission_idx) + + return True + def get_test_steps(self, test: str) -> list[TestStep]: ''' Retrieves the test step list for the given test @@ -1168,6 +1142,8 @@ def setup_class(self): self.current_step_index = 0 self.step_start_time = datetime.now(timezone.utc) self.step_skipped = False + self.global_wildcard = asyncio.wait_for(self.default_controller.Read(self.dut_node_id, [(Clusters.Descriptor), Attribute.AttributePath(None, None, GlobalAttributeIds.ATTRIBUTE_LIST_ID), Attribute.AttributePath( + None, None, GlobalAttributeIds.FEATURE_MAP_ID), Attribute.AttributePath(None, None, GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID)]), timeout=60) # self.stored_global_wildcard stores value of self.global_wildcard after first async call. # Because setup_class can be called before commissioning, this variable is lazy-initialized # where the read is deferred until the first guard function call that requires global attributes. @@ -1194,14 +1170,17 @@ def setup_test(self): self.step(1) def teardown_class(self): - """Final teardown after all tests: log all problems.""" - if len(self.problems) > 0: - logging.info("###########################################################") - logging.info("Problems found:") - logging.info("===============") - for problem in self.problems: - logging.info(str(problem)) - logging.info("###########################################################") + """Final teardown after all tests: log all problems""" + if len(self.problems) == 0: + return + + logging.info("###########################################################") + logging.info("Problems found:") + logging.info("===============") + for problem in self.problems: + logging.info(str(problem)) + logging.info("###########################################################") + super().teardown_class() def check_pics(self, pics_key: str) -> bool: @@ -1500,13 +1479,6 @@ def pics_guard(self, pics_condition: bool): self.mark_current_step_skipped() return pics_condition - async def _populate_wildcard(self): - """ Populates self.stored_global_wildcard if not already filled. """ - if self.stored_global_wildcard is None: - global_wildcard = asyncio.wait_for(self.default_controller.Read(self.dut_node_id, [(Clusters.Descriptor), Attribute.AttributePath(None, None, GlobalAttributeIds.ATTRIBUTE_LIST_ID), Attribute.AttributePath( - None, None, GlobalAttributeIds.FEATURE_MAP_ID), Attribute.AttributePath(None, None, GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID)]), timeout=60) - self.stored_global_wildcard = await global_wildcard - async def attribute_guard(self, endpoint: int, attribute: ClusterObjects.ClusterAttributeDescriptor): """Similar to pics_guard above, except checks a condition and if False marks the test step as skipped and returns False using attributes against attributes_list, otherwise returns True. @@ -1520,7 +1492,8 @@ async def attribute_guard(self, endpoint: int, attribute: ClusterObjects.Cluster if self.attribute_guard(condition2_needs_to_be_false_to_skip_step): # skip step 2 if condition not met """ - await self._populate_wildcard() + if self.stored_global_wildcard is None: + self.stored_global_wildcard = await self.global_wildcard attr_condition = _has_attribute(wildcard=self.stored_global_wildcard, endpoint=endpoint, attribute=attribute) if not attr_condition: self.mark_current_step_skipped() @@ -1539,7 +1512,8 @@ async def command_guard(self, endpoint: int, command: ClusterObjects.ClusterComm if self.command_guard(condition2_needs_to_be_false_to_skip_step): # skip step 2 if condition not met """ - await self._populate_wildcard() + if self.stored_global_wildcard is None: + self.stored_global_wildcard = await self.global_wildcard cmd_condition = _has_command(wildcard=self.stored_global_wildcard, endpoint=endpoint, command=command) if not cmd_condition: self.mark_current_step_skipped() @@ -1558,7 +1532,8 @@ async def feature_guard(self, endpoint: int, cluster: ClusterObjects.ClusterObje if self.feature_guard(condition2_needs_to_be_false_to_skip_step): # skip step 2 if condition not met """ - await self._populate_wildcard() + if self.stored_global_wildcard is None: + self.stored_global_wildcard = await self.global_wildcard feat_condition = _has_feature(wildcard=self.stored_global_wildcard, endpoint=endpoint, cluster=cluster, feature=feature_int) if not feat_condition: self.mark_current_step_skipped() @@ -2130,7 +2105,8 @@ def parse_matter_test_args(argv: Optional[List[str]] = None) -> MatterTestConfig def _async_runner(body, self: MatterBaseTest, *args, **kwargs): timeout = self.matter_test_config.timeout if self.matter_test_config.timeout is not None else self.default_timeout - return self.event_loop.run_until_complete(asyncio.wait_for(body(self, *args, **kwargs), timeout=timeout)) + runner_with_timeout = asyncio.wait_for(body(self, *args, **kwargs), timeout=timeout) + return asyncio.run(runner_with_timeout) def async_test_body(body): @@ -2323,7 +2299,7 @@ def run_on_singleton_matching_endpoint(accept_function: EndpointCheckFunction): def run_on_singleton_matching_endpoint_internal(body): def matching_runner(self: MatterBaseTest, *args, **kwargs): runner_with_timeout = asyncio.wait_for(_get_all_matching_endpoints(self, accept_function), timeout=30) - matching = self.event_loop.run_until_complete(runner_with_timeout) + matching = asyncio.run(runner_with_timeout) asserts.assert_less_equal(len(matching), 1, "More than one matching endpoint found for singleton test.") if not matching: logging.info("Test is not applicable to any endpoint - skipping test") @@ -2370,7 +2346,7 @@ def run_if_endpoint_matches(accept_function: EndpointCheckFunction): def run_if_endpoint_matches_internal(body): def per_endpoint_runner(self: MatterBaseTest, *args, **kwargs): runner_with_timeout = asyncio.wait_for(should_run_test_on_endpoint(self, accept_function), timeout=60) - should_run_test = self.event_loop.run_until_complete(runner_with_timeout) + should_run_test = asyncio.run(runner_with_timeout) if not should_run_test: logging.info("Test is not applicable to this endpoint - skipping test") asserts.skip('Endpoint does not match test requirements') @@ -2389,35 +2365,27 @@ def __init__(self, *args): self.is_commissioning = True def test_run_commissioning(self): - if not self.event_loop.run_until_complete(self.commission_devices()): - raise signals.TestAbortAll("Failed to commission node(s)") - - async def commission_devices(self) -> bool: - conf = self.matter_test_config - - commissioned = [] - setup_payloads = self.get_setup_payload_info() - for node_id, setup_payload in zip(conf.dut_node_ids, setup_payloads): - logging.info(f"Starting commissioning for root index {conf.root_of_trust_index}, " - f"fabric ID 0x{conf.fabric_id:016X}, node ID 0x{node_id:016X}") - logging.info(f"Commissioning method: {conf.commissioning_method}") - commissioned.append(await self.commission_device(node_id, setup_payload)) + if not asyncio.run(self.commission_devices()): + raise signals.TestAbortAll("Failed to commission node") - return all(commissioned) + async def commission_device(instance: MatterBaseTest, i) -> bool: + dev_ctrl = instance.default_controller + conf = instance.matter_test_config - async def commission_device(self, node_id: int, info: SetupPayloadInfo) -> bool: - dev_ctrl = self.default_controller - conf = self.matter_test_config + info = instance.get_setup_payload_info()[i] if conf.tc_version_to_simulate is not None and conf.tc_user_response_to_simulate is not None: logging.debug( f"Setting TC Acknowledgements to version {conf.tc_version_to_simulate} with user response {conf.tc_user_response_to_simulate}.") dev_ctrl.SetTCAcknowledgements(conf.tc_version_to_simulate, conf.tc_user_response_to_simulate) + dev_ctrl.SetTCRequired(True) + else: + dev_ctrl.SetTCRequired(False) if conf.commissioning_method == "on-network": try: await dev_ctrl.CommissionOnNetwork( - nodeId=node_id, + nodeId=conf.dut_node_ids[i], setupPinCode=info.passcode, filterType=info.filter_type, filter=info.filter_value @@ -2431,7 +2399,7 @@ async def commission_device(self, node_id: int, info: SetupPayloadInfo) -> bool: await dev_ctrl.CommissionWiFi( info.filter_value, info.passcode, - node_id, + conf.dut_node_ids[i], conf.wifi_ssid, conf.wifi_passphrase, isShortDiscriminator=(info.filter_type == DiscoveryFilterType.SHORT_DISCRIMINATOR) @@ -2445,7 +2413,7 @@ async def commission_device(self, node_id: int, info: SetupPayloadInfo) -> bool: await dev_ctrl.CommissionThread( info.filter_value, info.passcode, - node_id, + conf.dut_node_ids[i], conf.thread_operational_dataset, isShortDiscriminator=(info.filter_type == DiscoveryFilterType.SHORT_DISCRIMINATOR) ) @@ -2458,8 +2426,7 @@ async def commission_device(self, node_id: int, info: SetupPayloadInfo) -> bool: logging.warning("==== USING A DIRECT IP COMMISSIONING METHOD NOT SUPPORTED IN THE LONG TERM ====") await dev_ctrl.CommissionIP( ipaddr=conf.commissionee_ip_address_just_for_testing, - setupPinCode=info.passcode, - nodeid=node_id, + setupPinCode=info.passcode, nodeid=conf.dut_node_ids[i] ) return True except ChipStackError as e: @@ -2475,10 +2442,10 @@ def default_matter_test_main(): In this case, only one test class in a test script is allowed. To make your test script executable, add the following to your file: .. code-block:: python - from chip.testing.matter_testing import default_matter_test_main + from chip.testing.matter_testing.py import default_matter_test_main ... if __name__ == '__main__': - default_matter_test_main() + default_matter_test_main.main() """ matter_test_config = parse_matter_test_args() @@ -2507,15 +2474,7 @@ def get_test_info(test_class: MatterBaseTest, matter_test_config: MatterTestConf return info -def run_tests_no_exit(test_class: MatterBaseTest, matter_test_config: MatterTestConfig, - event_loop: asyncio.AbstractEventLoop, hooks: TestRunnerHooks, - default_controller=None, external_stack=None) -> bool: - - # NOTE: It's not possible to pass event loop via Mobly TestRunConfig user params, because the - # Mobly deep copies the user params before passing them to the test class and the event - # loop is not serializable. So, we are setting the event loop as a test class member. - CommissionDeviceTest.event_loop = event_loop - test_class.event_loop = event_loop +def run_tests_no_exit(test_class: MatterBaseTest, matter_test_config: MatterTestConfig, hooks: TestRunnerHooks, default_controller=None, external_stack=None) -> bool: get_test_info(test_class, matter_test_config) @@ -2595,13 +2554,9 @@ def run_tests_no_exit(test_class: MatterBaseTest, matter_test_config: MatterTest duration = (datetime.now(timezone.utc) - runner_start_time) / timedelta(microseconds=1) hooks.stop(duration=duration) + # Shutdown the stack when all done if not external_stack: - async def shutdown(): - stack.Shutdown() - # Shutdown the stack when all done. Use the async runner to ensure that - # during the shutdown callbacks can use tha same async context which was used - # during the initialization. - event_loop.run_until_complete(shutdown()) + stack.Shutdown() if ok: logging.info("Final result: PASS !") @@ -2610,9 +2565,6 @@ async def shutdown(): return ok -def run_tests(test_class: MatterBaseTest, matter_test_config: MatterTestConfig, - hooks: TestRunnerHooks, default_controller=None, external_stack=None) -> None: - with asyncio.Runner() as runner: - if not run_tests_no_exit(test_class, matter_test_config, runner.get_loop(), - hooks, default_controller, external_stack): - sys.exit(1) +def run_tests(test_class: MatterBaseTest, matter_test_config: MatterTestConfig, hooks: TestRunnerHooks, default_controller=None, external_stack=None) -> None: + if not run_tests_no_exit(test_class, matter_test_config, hooks, default_controller, external_stack): + sys.exit(1) From 9e78462f080a9e38458fbe8da42fc18681c08513 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 28 Jan 2025 23:53:06 -0800 Subject: [PATCH 14/53] wip --- src/python_testing/TC_FAN_3_1.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 73b4ab923b61a0..d75a9288e71405 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -133,7 +133,8 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): "Current FanMode must be greater than previous FanMode") else: # Verify the current FanMode is less than the previous FanMode - asserts.assert_less(fan_mode_current, fan_mode_previous, "Current FanMode must be less than previous FanMode") + asserts.assert_less(fan_mode_current, fan_mode_previous, + "Current FanMode must be less than previous FanMode") logging.info( f"[FANS] FanMode changed from {fan_mode_previous.name}({fan_mode_previous}) to {fan_mode_current.name}({fan_mode_current})") From 962574518266e24e0a4b151ad4e2ee04f40a8ee6 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 28 Jan 2025 23:55:36 -0800 Subject: [PATCH 15/53] wip --- .../chip/testing/matter_testing.py | 122 ++++++++++-------- 1 file changed, 66 insertions(+), 56 deletions(-) diff --git a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py index 46c32a2e178b79..1054463ea64489 100644 --- a/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py +++ b/src/python_testing/matter_testing_infrastructure/chip/testing/matter_testing.py @@ -972,18 +972,6 @@ def __init__(self, *args): # The named pipe name must be set in the derived classes self.app_pipe = None - async def commission_devices(self) -> bool: - conf = self.matter_test_config - - for commission_idx, node_id in enumerate(conf.dut_node_ids): - logging.info( - f"Starting commissioning for root index {conf.root_of_trust_index}, fabric ID 0x{conf.fabric_id:016X}, node ID 0x{node_id:016X}") - logging.info(f"Commissioning method: {conf.commissioning_method}") - - await CommissionDeviceTest.commission_device(self, commission_idx) - - return True - def get_test_steps(self, test: str) -> list[TestStep]: ''' Retrieves the test step list for the given test @@ -1142,8 +1130,6 @@ def setup_class(self): self.current_step_index = 0 self.step_start_time = datetime.now(timezone.utc) self.step_skipped = False - self.global_wildcard = asyncio.wait_for(self.default_controller.Read(self.dut_node_id, [(Clusters.Descriptor), Attribute.AttributePath(None, None, GlobalAttributeIds.ATTRIBUTE_LIST_ID), Attribute.AttributePath( - None, None, GlobalAttributeIds.FEATURE_MAP_ID), Attribute.AttributePath(None, None, GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID)]), timeout=60) # self.stored_global_wildcard stores value of self.global_wildcard after first async call. # Because setup_class can be called before commissioning, this variable is lazy-initialized # where the read is deferred until the first guard function call that requires global attributes. @@ -1170,17 +1156,14 @@ def setup_test(self): self.step(1) def teardown_class(self): - """Final teardown after all tests: log all problems""" - if len(self.problems) == 0: - return - - logging.info("###########################################################") - logging.info("Problems found:") - logging.info("===============") - for problem in self.problems: - logging.info(str(problem)) - logging.info("###########################################################") - + """Final teardown after all tests: log all problems.""" + if len(self.problems) > 0: + logging.info("###########################################################") + logging.info("Problems found:") + logging.info("===============") + for problem in self.problems: + logging.info(str(problem)) + logging.info("###########################################################") super().teardown_class() def check_pics(self, pics_key: str) -> bool: @@ -1479,6 +1462,13 @@ def pics_guard(self, pics_condition: bool): self.mark_current_step_skipped() return pics_condition + async def _populate_wildcard(self): + """ Populates self.stored_global_wildcard if not already filled. """ + if self.stored_global_wildcard is None: + global_wildcard = asyncio.wait_for(self.default_controller.Read(self.dut_node_id, [(Clusters.Descriptor), Attribute.AttributePath(None, None, GlobalAttributeIds.ATTRIBUTE_LIST_ID), Attribute.AttributePath( + None, None, GlobalAttributeIds.FEATURE_MAP_ID), Attribute.AttributePath(None, None, GlobalAttributeIds.ACCEPTED_COMMAND_LIST_ID)]), timeout=60) + self.stored_global_wildcard = await global_wildcard + async def attribute_guard(self, endpoint: int, attribute: ClusterObjects.ClusterAttributeDescriptor): """Similar to pics_guard above, except checks a condition and if False marks the test step as skipped and returns False using attributes against attributes_list, otherwise returns True. @@ -1492,8 +1482,7 @@ async def attribute_guard(self, endpoint: int, attribute: ClusterObjects.Cluster if self.attribute_guard(condition2_needs_to_be_false_to_skip_step): # skip step 2 if condition not met """ - if self.stored_global_wildcard is None: - self.stored_global_wildcard = await self.global_wildcard + await self._populate_wildcard() attr_condition = _has_attribute(wildcard=self.stored_global_wildcard, endpoint=endpoint, attribute=attribute) if not attr_condition: self.mark_current_step_skipped() @@ -1512,8 +1501,7 @@ async def command_guard(self, endpoint: int, command: ClusterObjects.ClusterComm if self.command_guard(condition2_needs_to_be_false_to_skip_step): # skip step 2 if condition not met """ - if self.stored_global_wildcard is None: - self.stored_global_wildcard = await self.global_wildcard + await self._populate_wildcard() cmd_condition = _has_command(wildcard=self.stored_global_wildcard, endpoint=endpoint, command=command) if not cmd_condition: self.mark_current_step_skipped() @@ -1532,8 +1520,7 @@ async def feature_guard(self, endpoint: int, cluster: ClusterObjects.ClusterObje if self.feature_guard(condition2_needs_to_be_false_to_skip_step): # skip step 2 if condition not met """ - if self.stored_global_wildcard is None: - self.stored_global_wildcard = await self.global_wildcard + await self._populate_wildcard() feat_condition = _has_feature(wildcard=self.stored_global_wildcard, endpoint=endpoint, cluster=cluster, feature=feature_int) if not feat_condition: self.mark_current_step_skipped() @@ -2105,8 +2092,7 @@ def parse_matter_test_args(argv: Optional[List[str]] = None) -> MatterTestConfig def _async_runner(body, self: MatterBaseTest, *args, **kwargs): timeout = self.matter_test_config.timeout if self.matter_test_config.timeout is not None else self.default_timeout - runner_with_timeout = asyncio.wait_for(body(self, *args, **kwargs), timeout=timeout) - return asyncio.run(runner_with_timeout) + return self.event_loop.run_until_complete(asyncio.wait_for(body(self, *args, **kwargs), timeout=timeout)) def async_test_body(body): @@ -2299,7 +2285,7 @@ def run_on_singleton_matching_endpoint(accept_function: EndpointCheckFunction): def run_on_singleton_matching_endpoint_internal(body): def matching_runner(self: MatterBaseTest, *args, **kwargs): runner_with_timeout = asyncio.wait_for(_get_all_matching_endpoints(self, accept_function), timeout=30) - matching = asyncio.run(runner_with_timeout) + matching = self.event_loop.run_until_complete(runner_with_timeout) asserts.assert_less_equal(len(matching), 1, "More than one matching endpoint found for singleton test.") if not matching: logging.info("Test is not applicable to any endpoint - skipping test") @@ -2346,7 +2332,7 @@ def run_if_endpoint_matches(accept_function: EndpointCheckFunction): def run_if_endpoint_matches_internal(body): def per_endpoint_runner(self: MatterBaseTest, *args, **kwargs): runner_with_timeout = asyncio.wait_for(should_run_test_on_endpoint(self, accept_function), timeout=60) - should_run_test = asyncio.run(runner_with_timeout) + should_run_test = self.event_loop.run_until_complete(runner_with_timeout) if not should_run_test: logging.info("Test is not applicable to this endpoint - skipping test") asserts.skip('Endpoint does not match test requirements') @@ -2365,27 +2351,35 @@ def __init__(self, *args): self.is_commissioning = True def test_run_commissioning(self): - if not asyncio.run(self.commission_devices()): - raise signals.TestAbortAll("Failed to commission node") + if not self.event_loop.run_until_complete(self.commission_devices()): + raise signals.TestAbortAll("Failed to commission node(s)") + + async def commission_devices(self) -> bool: + conf = self.matter_test_config + + commissioned = [] + setup_payloads = self.get_setup_payload_info() + for node_id, setup_payload in zip(conf.dut_node_ids, setup_payloads): + logging.info(f"Starting commissioning for root index {conf.root_of_trust_index}, " + f"fabric ID 0x{conf.fabric_id:016X}, node ID 0x{node_id:016X}") + logging.info(f"Commissioning method: {conf.commissioning_method}") + commissioned.append(await self.commission_device(node_id, setup_payload)) - async def commission_device(instance: MatterBaseTest, i) -> bool: - dev_ctrl = instance.default_controller - conf = instance.matter_test_config + return all(commissioned) - info = instance.get_setup_payload_info()[i] + async def commission_device(self, node_id: int, info: SetupPayloadInfo) -> bool: + dev_ctrl = self.default_controller + conf = self.matter_test_config if conf.tc_version_to_simulate is not None and conf.tc_user_response_to_simulate is not None: logging.debug( f"Setting TC Acknowledgements to version {conf.tc_version_to_simulate} with user response {conf.tc_user_response_to_simulate}.") dev_ctrl.SetTCAcknowledgements(conf.tc_version_to_simulate, conf.tc_user_response_to_simulate) - dev_ctrl.SetTCRequired(True) - else: - dev_ctrl.SetTCRequired(False) if conf.commissioning_method == "on-network": try: await dev_ctrl.CommissionOnNetwork( - nodeId=conf.dut_node_ids[i], + nodeId=node_id, setupPinCode=info.passcode, filterType=info.filter_type, filter=info.filter_value @@ -2399,7 +2393,7 @@ async def commission_device(instance: MatterBaseTest, i) -> bool: await dev_ctrl.CommissionWiFi( info.filter_value, info.passcode, - conf.dut_node_ids[i], + node_id, conf.wifi_ssid, conf.wifi_passphrase, isShortDiscriminator=(info.filter_type == DiscoveryFilterType.SHORT_DISCRIMINATOR) @@ -2413,7 +2407,7 @@ async def commission_device(instance: MatterBaseTest, i) -> bool: await dev_ctrl.CommissionThread( info.filter_value, info.passcode, - conf.dut_node_ids[i], + node_id, conf.thread_operational_dataset, isShortDiscriminator=(info.filter_type == DiscoveryFilterType.SHORT_DISCRIMINATOR) ) @@ -2426,7 +2420,8 @@ async def commission_device(instance: MatterBaseTest, i) -> bool: logging.warning("==== USING A DIRECT IP COMMISSIONING METHOD NOT SUPPORTED IN THE LONG TERM ====") await dev_ctrl.CommissionIP( ipaddr=conf.commissionee_ip_address_just_for_testing, - setupPinCode=info.passcode, nodeid=conf.dut_node_ids[i] + setupPinCode=info.passcode, + nodeid=node_id, ) return True except ChipStackError as e: @@ -2442,10 +2437,10 @@ def default_matter_test_main(): In this case, only one test class in a test script is allowed. To make your test script executable, add the following to your file: .. code-block:: python - from chip.testing.matter_testing.py import default_matter_test_main + from chip.testing.matter_testing import default_matter_test_main ... if __name__ == '__main__': - default_matter_test_main.main() + default_matter_test_main() """ matter_test_config = parse_matter_test_args() @@ -2474,7 +2469,15 @@ def get_test_info(test_class: MatterBaseTest, matter_test_config: MatterTestConf return info -def run_tests_no_exit(test_class: MatterBaseTest, matter_test_config: MatterTestConfig, hooks: TestRunnerHooks, default_controller=None, external_stack=None) -> bool: +def run_tests_no_exit(test_class: MatterBaseTest, matter_test_config: MatterTestConfig, + event_loop: asyncio.AbstractEventLoop, hooks: TestRunnerHooks, + default_controller=None, external_stack=None) -> bool: + + # NOTE: It's not possible to pass event loop via Mobly TestRunConfig user params, because the + # Mobly deep copies the user params before passing them to the test class and the event + # loop is not serializable. So, we are setting the event loop as a test class member. + CommissionDeviceTest.event_loop = event_loop + test_class.event_loop = event_loop get_test_info(test_class, matter_test_config) @@ -2554,9 +2557,13 @@ def run_tests_no_exit(test_class: MatterBaseTest, matter_test_config: MatterTest duration = (datetime.now(timezone.utc) - runner_start_time) / timedelta(microseconds=1) hooks.stop(duration=duration) - # Shutdown the stack when all done if not external_stack: - stack.Shutdown() + async def shutdown(): + stack.Shutdown() + # Shutdown the stack when all done. Use the async runner to ensure that + # during the shutdown callbacks can use tha same async context which was used + # during the initialization. + event_loop.run_until_complete(shutdown()) if ok: logging.info("Final result: PASS !") @@ -2565,6 +2572,9 @@ def run_tests_no_exit(test_class: MatterBaseTest, matter_test_config: MatterTest return ok -def run_tests(test_class: MatterBaseTest, matter_test_config: MatterTestConfig, hooks: TestRunnerHooks, default_controller=None, external_stack=None) -> None: - if not run_tests_no_exit(test_class, matter_test_config, hooks, default_controller, external_stack): - sys.exit(1) +def run_tests(test_class: MatterBaseTest, matter_test_config: MatterTestConfig, + hooks: TestRunnerHooks, default_controller=None, external_stack=None) -> None: + with asyncio.Runner() as runner: + if not run_tests_no_exit(test_class, matter_test_config, runner.get_loop(), + hooks, default_controller, external_stack): + sys.exit(1) From 73060a5803e3ee65b2d9e62e1515453ea0f97bfa Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Wed, 29 Jan 2025 00:03:00 -0800 Subject: [PATCH 16/53] wip --- src/python_testing/TC_FAN_3_1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index d75a9288e71405..e22468279600ff 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -93,7 +93,7 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): write_result = await self.write_setting(endpoint, fan_mode_attr, fan_mode_init) write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) asserts.assert_true(write_status_success, - f"FanMode write did not return a value of either SUCCESS or INVALID_IN_STATE ({write_result.name})") + f"FanMode write did not return a result of either SUCCESS or INVALID_IN_STATE ({write_result.name})") # Read FanMode back and verify written value fan_mode_initial = await self.read_setting(endpoint, fan_mode_attr) @@ -111,7 +111,7 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): # Write to attribute write_result = await self.write_setting(endpoint, attr_to_write, value_to_write) write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) - asserts.assert_true(write_status_success, f"Attribute write did not return a value of either Success or InvalidInState") + asserts.assert_true(write_status_success, "Attribute write did not return a result of either SUCCESS or INVALID_IN_STATE") logging.info(f"[FANS] Attribute value written: {value_to_write}") # Wait for the FanMode attribute to appear in the queue until the specified timeout From f431b8817bd61f34666fdc50381bbfdf68668bf2 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Wed, 29 Jan 2025 00:06:19 -0800 Subject: [PATCH 17/53] wip --- src/python_testing/TC_FAN_3_1.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index e22468279600ff..61d3aa9d823362 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -111,7 +111,8 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): # Write to attribute write_result = await self.write_setting(endpoint, attr_to_write, value_to_write) write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) - asserts.assert_true(write_status_success, "Attribute write did not return a result of either SUCCESS or INVALID_IN_STATE") + asserts.assert_true(write_status_success, + "Attribute write did not return a result of either SUCCESS or INVALID_IN_STATE") logging.info(f"[FANS] Attribute value written: {value_to_write}") # Wait for the FanMode attribute to appear in the queue until the specified timeout From 5659b55786caa275a6eae24e87cfaddc89bbd5da Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Thu, 30 Jan 2025 18:20:16 -0800 Subject: [PATCH 18/53] wip --- src/python_testing/TC_FAN_3_1.py | 151 ++++++++++++++++++++----------- 1 file changed, 98 insertions(+), 53 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 61d3aa9d823362..3b2280090b0eac 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -34,15 +34,17 @@ # quiet: true # === END CI TEST ARGUMENTS === +import enum import logging import time from enum import Enum +from typing import Any import chip.clusters as Clusters from chip.clusters import ClusterObjects as ClusterObjects from chip.interaction_model import Status from chip.testing.matter_testing import (ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, async_test_body, - default_matter_test_main) + default_matter_test_main, has_feature) from mobly import asserts @@ -70,40 +72,59 @@ async def write_setting(self, endpoint, attr_to_write, value) -> Status: result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attr_to_write(value))]) return result[0].Status + async def supports_speed(self) -> bool: + # TODO: Implement + time.sleep(0.1) + return True + async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): logging.info(f"[FANS] Monitoring FanMode by writing to {attr_to_write.__name__} in {order.name} order") # Setup cluster = Clusters.FanControl fan_mode_attr = cluster.Attributes.FanMode + speed_setting_attr = cluster.Attributes.SpeedSetting + speed_max_attr = cluster.Attributes.SpeedMax attribute_subscription = ClusterAttributeChangeAccumulator(cluster) fan_mode_off = cluster.Enums.FanModeEnum.kOff fan_mode_high = cluster.Enums.FanModeEnum.kHigh + percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec + supports_speed = await self.supports_speed() - # As per the spec, the max values for PercentSetting and SpeedSetting are 100 and SpeedMax respectively - max_value = 100 - if attr_to_write == cluster.Attributes.SpeedSetting: - max_value = await self.read_setting(endpoint, cluster.Attributes.SpeedMax) - - # Determine if values will be written ascending or descending + # Determine if PercentSetting values will be written in ascending or descending order fan_mode_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high - range_loop = range(1, max_value + 1) if order == OrderEnum.Ascending else range(max_value, -1, -1) + speed_max = await self.read_setting(endpoint, speed_max_attr) + speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max + range_loop = range(1, percent_setting_max_value + 1) if order == OrderEnum.Ascending else range(percent_setting_max_value, -1, -1) - # Write initial FanMode + # Write and read back the initialization value for FanMode + # Verify that the write status is either SUCCESS or INVALID_IN_STATE + # Verify written and read value match write_result = await self.write_setting(endpoint, fan_mode_attr, fan_mode_init) write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) asserts.assert_true(write_status_success, f"FanMode write did not return a result of either SUCCESS or INVALID_IN_STATE ({write_result.name})") + fan_mode_read = await self.read_setting(endpoint, fan_mode_attr) + asserts.assert_in(fan_mode_read, cluster.Enums.FanModeEnum, "FanMode read response doesn't contain a FanModeEnum") - # Read FanMode back and verify written value - fan_mode_initial = await self.read_setting(endpoint, fan_mode_attr) - asserts.assert_in(fan_mode_initial, cluster.Enums.FanModeEnum, "FanMode read response doesn't contain a FanModeEnum") + # Write and read back the initialization value for SpeedSetting + # Verify that the write status is either SUCCESS or INVALID_IN_STATE + # Verify written and read value match + write_result = await self.write_setting(endpoint, speed_setting_attr, speed_setting_init) + write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) + asserts.assert_true(write_status_success, + f"SpeedSetting write did not return a result of either SUCCESS or INVALID_IN_STATE ({write_result.name})") + speed_setting_read = await self.read_setting(endpoint, speed_setting_attr) + asserts.assert_equal(speed_setting_read, speed_setting_init, + "Mismatch between written and read SpeedSetting") await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) # Write to attribute iteratively within a range of 100, one at a time - fan_mode_current = fan_mode_initial - fan_mode_previous = fan_mode_initial + fan_mode_current = fan_mode_read + fan_mode_previous = fan_mode_read + speed_setting_current = speed_setting_read + speed_setting_previous = speed_setting_read for value_to_write in range_loop: # Clear the queue attribute_subscription.get_last_report() @@ -115,42 +136,67 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): "Attribute write did not return a result of either SUCCESS or INVALID_IN_STATE") logging.info(f"[FANS] Attribute value written: {value_to_write}") - # Wait for the FanMode attribute to appear in the queue until the specified timeout - start_time = time.time() - elapsed = 0 - time_remaining = timeout_sec - break_out = False - while time_remaining > 0: - q = attribute_subscription.attribute_queue - - # Iterate through the queue items to search for the FanMode attribute - for q_item in list(q.queue): - if q_item.attribute == fan_mode_attr: - fan_mode_current = q_item.value - - if order == OrderEnum.Ascending: - # Verify the current FanMode is greater than the previous FanMode - asserts.assert_greater(fan_mode_current, fan_mode_previous, - "Current FanMode must be greater than previous FanMode") - else: - # Verify the current FanMode is less than the previous FanMode - asserts.assert_less(fan_mode_current, fan_mode_previous, - "Current FanMode must be less than previous FanMode") - - logging.info( - f"[FANS] FanMode changed from {fan_mode_previous.name}({fan_mode_previous}) to {fan_mode_current.name}({fan_mode_current})") - fan_mode_previous = fan_mode_current - break_out = True - break - - elapsed = time.time() - start_time - time_remaining = timeout_sec - elapsed - - if break_out: - break + # Get the subscription queue + queue = attribute_subscription.attribute_queue.queue + + # Verifying FanMode + fan_mode_current = await self.get_attribute_value_from_queue(queue, fan_mode_attr, timeout_sec) + if fan_mode_current is not None: + fan_mode_current = cluster.Enums.FanModeEnum(fan_mode_current) + if order == OrderEnum.Ascending: + # Verify the current FanMode is greater than the previous FanMode + asserts.assert_greater(fan_mode_current, fan_mode_previous, + "Current FanMode must be greater than previous FanMode") + else: + # Verify the current FanMode is less than the previous FanMode + asserts.assert_less(fan_mode_current, fan_mode_previous, + "Current FanMode must be less than previous FanMode") + logging.info( + f"[FANS] FanMode changed from {fan_mode_previous.name}({fan_mode_previous}) to {fan_mode_current.name}({fan_mode_current})") + fan_mode_previous = fan_mode_current + + # Verifying SpeedSetting (if supported) + if supports_speed: + speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) + if speed_setting_current is not None: + if order == OrderEnum.Ascending: + # Verify the current SpeedSetting is greater than the previous SpeedSetting + asserts.assert_greater(speed_setting_current, speed_setting_previous, + "Current SpeedSetting must be greater than previous SpeedSetting") + else: + # Verify the current SpeedSetting is less than the previous SpeedSetting + asserts.assert_less(speed_setting_current, speed_setting_previous, + "Current SpeedSetting must be less than previous SpeedSetting") + logging.info( + f"[FANS] SpeedSetting changed from {speed_setting_previous} to {speed_setting_current}") + speed_setting_previous = speed_setting_current await attribute_subscription.cancel() + @staticmethod + async def get_attribute_value_from_queue(queue, attribute, timeout_sec) -> Any: + start_time = time.time() + elapsed = 0 + time_remaining = timeout_sec + break_out = False + value = None + + # Wait for the attribute to appear in the queue until the specified timeout + while time_remaining > 0: + for q in list(queue): + if q.attribute == attribute: + value = q.value + break_out = True + break + + elapsed = time.time() - start_time + time_remaining = timeout_sec - elapsed + + if break_out: + break + + return value + def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] @@ -159,8 +205,7 @@ async def test_TC_FAN_3_1(self): # Setup endpoint = self.get_endpoint(default=1) percent_setting_attr = Clusters.FanControl.Attributes.PercentSetting - speed_setting_attr = Clusters.FanControl.Attributes.SpeedSetting - timeout_sec = 0.2 + timeout_sec = 0.25 # TH writes to the DUT the PercentSetting iteratively within a range of 1 to 100 one at a time in ascending order await self.verify_fan_mode(endpoint, percent_setting_attr, OrderEnum.Ascending, timeout_sec) @@ -168,11 +213,11 @@ async def test_TC_FAN_3_1(self): # TH writes to the DUT the PercentSetting iteratively within a range of 1 to 100 one at a time in descending order await self.verify_fan_mode(endpoint, percent_setting_attr, OrderEnum.Descending, timeout_sec) - # TH writes to the DUT the SpeedSetting iteratively within a range of 1 to SpeedMax one at a time in ascending order - await self.verify_fan_mode(endpoint, speed_setting_attr, OrderEnum.Ascending, timeout_sec) + # # TH writes to the DUT the SpeedSetting iteratively within a range of 1 to SpeedMax one at a time in ascending order + # await self.verify_fan_mode(endpoint, speed_setting_attr, OrderEnum.Ascending, timeout_sec) - # TH writes to the DUT the SpeedSetting iteratively within a range of 1 to SpeedMax one at a time in descending order - await self.verify_fan_mode(endpoint, speed_setting_attr, OrderEnum.Descending, timeout_sec) + # # TH writes to the DUT the SpeedSetting iteratively within a range of 1 to SpeedMax one at a time in descending order + # await self.verify_fan_mode(endpoint, speed_setting_attr, OrderEnum.Descending, timeout_sec) if __name__ == "__main__": From 8ad4a17e4c0baa3c071e07d87bdb4429b78e9343 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Thu, 30 Jan 2025 18:22:50 -0800 Subject: [PATCH 19/53] wip --- src/python_testing/TC_FAN_3_1.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 3b2280090b0eac..b1c1ae4649adb7 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -72,7 +72,7 @@ async def write_setting(self, endpoint, attr_to_write, value) -> Status: result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attr_to_write(value))]) return result[0].Status - async def supports_speed(self) -> bool: + async def _supports_speed(self) -> bool: # TODO: Implement time.sleep(0.1) return True @@ -89,7 +89,6 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): fan_mode_off = cluster.Enums.FanModeEnum.kOff fan_mode_high = cluster.Enums.FanModeEnum.kHigh percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec - supports_speed = await self.supports_speed() # Determine if PercentSetting values will be written in ascending or descending order fan_mode_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high @@ -156,7 +155,7 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): fan_mode_previous = fan_mode_current # Verifying SpeedSetting (if supported) - if supports_speed: + if self.supports_speed: speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) if speed_setting_current is not None: if order == OrderEnum.Ascending: @@ -205,6 +204,8 @@ async def test_TC_FAN_3_1(self): # Setup endpoint = self.get_endpoint(default=1) percent_setting_attr = Clusters.FanControl.Attributes.PercentSetting + self.supports_speed = await self._supports_speed() + timeout_sec = 0.25 # TH writes to the DUT the PercentSetting iteratively within a range of 1 to 100 one at a time in ascending order From 90fb3df2c3315b2d260357a9afb12c2a8d19f044 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Fri, 31 Jan 2025 11:17:58 -0800 Subject: [PATCH 20/53] wip --- src/python_testing/TC_FAN_3_1.py | 77 +++++++++++++++++--------------- 1 file changed, 40 insertions(+), 37 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index b1c1ae4649adb7..635c5a23664c6f 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -78,7 +78,7 @@ async def _supports_speed(self) -> bool: return True async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): - logging.info(f"[FANS] Monitoring FanMode by writing to {attr_to_write.__name__} in {order.name} order") + logging.info(f"\n[F] Updating {attr_to_write.__name__} {order.name}, verifying FanMode and SpeedSetting (if supported)") # Setup cluster = Clusters.FanControl @@ -99,24 +99,14 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): # Write and read back the initialization value for FanMode # Verify that the write status is either SUCCESS or INVALID_IN_STATE # Verify written and read value match - write_result = await self.write_setting(endpoint, fan_mode_attr, fan_mode_init) - write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) - asserts.assert_true(write_status_success, - f"FanMode write did not return a result of either SUCCESS or INVALID_IN_STATE ({write_result.name})") - fan_mode_read = await self.read_setting(endpoint, fan_mode_attr) - asserts.assert_in(fan_mode_read, cluster.Enums.FanModeEnum, "FanMode read response doesn't contain a FanModeEnum") + fan_mode_read = await self.write_and_verify_attribute(endpoint, fan_mode_attr, fan_mode_init) # Write and read back the initialization value for SpeedSetting # Verify that the write status is either SUCCESS or INVALID_IN_STATE # Verify written and read value match - write_result = await self.write_setting(endpoint, speed_setting_attr, speed_setting_init) - write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) - asserts.assert_true(write_status_success, - f"SpeedSetting write did not return a result of either SUCCESS or INVALID_IN_STATE ({write_result.name})") - speed_setting_read = await self.read_setting(endpoint, speed_setting_attr) - asserts.assert_equal(speed_setting_read, speed_setting_init, - "Mismatch between written and read SpeedSetting") + speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) + # Start subscription await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) # Write to attribute iteratively within a range of 100, one at a time @@ -133,7 +123,7 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) asserts.assert_true(write_status_success, "Attribute write did not return a result of either SUCCESS or INVALID_IN_STATE") - logging.info(f"[FANS] Attribute value written: {value_to_write}") + logging.info(f"\n[F] Value written: {value_to_write}") # Get the subscription queue queue = attribute_subscription.attribute_queue.queue @@ -141,37 +131,50 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): # Verifying FanMode fan_mode_current = await self.get_attribute_value_from_queue(queue, fan_mode_attr, timeout_sec) if fan_mode_current is not None: - fan_mode_current = cluster.Enums.FanModeEnum(fan_mode_current) - if order == OrderEnum.Ascending: - # Verify the current FanMode is greater than the previous FanMode - asserts.assert_greater(fan_mode_current, fan_mode_previous, - "Current FanMode must be greater than previous FanMode") - else: - # Verify the current FanMode is less than the previous FanMode - asserts.assert_less(fan_mode_current, fan_mode_previous, - "Current FanMode must be less than previous FanMode") - logging.info( - f"[FANS] FanMode changed from {fan_mode_previous.name}({fan_mode_previous}) to {fan_mode_current.name}({fan_mode_current})") + self.verify_attribute_progression(fan_mode_attr, fan_mode_current, fan_mode_previous, order) fan_mode_previous = fan_mode_current # Verifying SpeedSetting (if supported) if self.supports_speed: speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) if speed_setting_current is not None: - if order == OrderEnum.Ascending: - # Verify the current SpeedSetting is greater than the previous SpeedSetting - asserts.assert_greater(speed_setting_current, speed_setting_previous, - "Current SpeedSetting must be greater than previous SpeedSetting") - else: - # Verify the current SpeedSetting is less than the previous SpeedSetting - asserts.assert_less(speed_setting_current, speed_setting_previous, - "Current SpeedSetting must be less than previous SpeedSetting") - logging.info( - f"[FANS] SpeedSetting changed from {speed_setting_previous} to {speed_setting_current}") + self.verify_attribute_progression(speed_setting_attr, speed_setting_current, speed_setting_previous, order) speed_setting_previous = speed_setting_current await attribute_subscription.cancel() + async def write_and_verify_attribute(self, endpoint, attribute, value_init): + value_init = self.get_enum(value_init) + + write_result = await self.write_setting(endpoint, attribute, value_init) + write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) + asserts.assert_true(write_status_success, + f"{attribute.__name__} write did not return a result of either SUCCESS or INVALID_IN_STATE ({write_result.name})") + value_read = await self.read_setting(endpoint, attribute) + asserts.assert_equal(value_read, value_init, + f"Mismatch between written and read {attribute.__name__}") + return value_read + + def verify_attribute_progression(self, attribute, value_current, value_previous, order): + value_current = self.get_enum(value_current) + if order == OrderEnum.Ascending: + # Verify the current FanMode is greater than the previous FanMode + asserts.assert_greater(value_current, value_previous, + f"Current {attribute.__name__} must be greater than previous {attribute.__name__}") + else: + # Verify the current FanMode is less than the previous FanMode + asserts.assert_less(value_current, value_previous, + f"Current {attribute.__name__} must be less than previous {attribute.__name__}") + logging.info( + f"\n\t[F] {attribute.__name__} changed from ({value_previous}) to ({value_current})") + + @staticmethod + def get_enum(value): + if isinstance(value, enum.Enum): + enum_type = type(value) + value = enum_type(value) + return value + @staticmethod async def get_attribute_value_from_queue(queue, attribute, timeout_sec) -> Any: start_time = time.time() @@ -206,7 +209,7 @@ async def test_TC_FAN_3_1(self): percent_setting_attr = Clusters.FanControl.Attributes.PercentSetting self.supports_speed = await self._supports_speed() - timeout_sec = 0.25 + timeout_sec = 0.333 # TH writes to the DUT the PercentSetting iteratively within a range of 1 to 100 one at a time in ascending order await self.verify_fan_mode(endpoint, percent_setting_attr, OrderEnum.Ascending, timeout_sec) From 73136905eb1e224cf4663e2b6597a63363806643 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Fri, 31 Jan 2025 11:20:55 -0800 Subject: [PATCH 21/53] wip --- src/python_testing/TC_FAN_3_1.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 635c5a23664c6f..c07cb8c42d6c61 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -101,10 +101,11 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): # Verify written and read value match fan_mode_read = await self.write_and_verify_attribute(endpoint, fan_mode_attr, fan_mode_init) - # Write and read back the initialization value for SpeedSetting + # Write and read back the initialization value for SpeedSetting (if supported) # Verify that the write status is either SUCCESS or INVALID_IN_STATE # Verify written and read value match - speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) + if self.supports_speed: + speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) # Start subscription await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) From 635ce7353cb3a9ab8b14e068548f26e2feb5c4a8 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Fri, 31 Jan 2025 11:22:03 -0800 Subject: [PATCH 22/53] wip --- src/python_testing/TC_FAN_3_1.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index c07cb8c42d6c61..4da9f620064922 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -169,13 +169,6 @@ def verify_attribute_progression(self, attribute, value_current, value_previous, logging.info( f"\n\t[F] {attribute.__name__} changed from ({value_previous}) to ({value_current})") - @staticmethod - def get_enum(value): - if isinstance(value, enum.Enum): - enum_type = type(value) - value = enum_type(value) - return value - @staticmethod async def get_attribute_value_from_queue(queue, attribute, timeout_sec) -> Any: start_time = time.time() @@ -200,6 +193,13 @@ async def get_attribute_value_from_queue(queue, attribute, timeout_sec) -> Any: return value + @staticmethod + def get_enum(value): + if isinstance(value, enum.Enum): + enum_type = type(value) + value = enum_type(value) + return value + def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] From 5f8ed6bc792ccb6659510447ea41c128bce03eb6 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Fri, 31 Jan 2025 15:34:33 -0800 Subject: [PATCH 23/53] wip --- src/python_testing/TC_FAN_3_1.py | 209 +++++++++++++++++++++++++++++-- 1 file changed, 197 insertions(+), 12 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 4da9f620064922..3669bebbd3a963 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -144,6 +144,163 @@ async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): await attribute_subscription.cancel() + async def verify_fan_mode_spaz(self, endpoint, attr_to_write, order, timeout_sec): + logging.info(f"\n[F] Updating {attr_to_write.__name__} {order.name}, verifying PercentSetting and SpeedSetting (if supported)") + + # Setup + cluster = Clusters.FanControl + fan_mode_attr = cluster.Attributes.FanMode + + percent_setting_attr = cluster.Attributes.PercentSetting + speed_setting_attr = cluster.Attributes.SpeedSetting + speed_max_attr = cluster.Attributes.SpeedMax + attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + fan_mode_off = cluster.Enums.FanModeEnum.kOff + fan_mode_high = cluster.Enums.FanModeEnum.kHigh + percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec + + maxfan = 3 + + # Determine if PercentSetting values will be written in ascending or descending order + # fan_mode_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high + percent_setting_init = 0 if order == OrderEnum.Ascending else 100 + speed_max = await self.read_setting(endpoint, speed_max_attr) + speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max + range_loop = range(1, maxfan + 1) if order == OrderEnum.Ascending else range(maxfan -1, -1, -1) + + # Write and read back the initialization value for PercentSetting + # Verify that the write status is either SUCCESS or INVALID_IN_STATE + # Verify written and read value match + percent_setting_read = await self.write_and_verify_attribute(endpoint, percent_setting_attr, percent_setting_init) + + # Write and read back the initialization value for SpeedSetting (if supported) + # Verify that the write status is either SUCCESS or INVALID_IN_STATE + # Verify written and read value match + if self.supports_speed: + speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) + + fm = await self.read_setting(endpoint, fan_mode_attr) + logging.info(f"\n[F] Initial state: ps({percent_setting_read}) ss({speed_setting_read}) fm({fm})") + + # Start subscription + await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) + + # Write to attribute iteratively within a range of 100, one at a time + percent_setting_current = percent_setting_read + percent_setting_previous = percent_setting_read + speed_setting_current = speed_setting_read + speed_setting_previous = speed_setting_read + for value_to_write in range_loop: + # Clear the queue + attribute_subscription.get_last_report() + + # Write to attribute + value_to_write = cluster.Enums.FanModeEnum(value_to_write) + write_result = await self.write_setting(endpoint, fan_mode_attr, value_to_write) + write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) + asserts.assert_true(write_status_success, + "Attribute write did not return a result of either SUCCESS or INVALID_IN_STATE") + logging.info(f"\n[F] Value written: {value_to_write}") + + # Get the subscription queue + queue = attribute_subscription.attribute_queue.queue + + # Verifying PercentSetting + percent_setting_current = await self.get_attribute_value_from_queue(queue, percent_setting_attr, timeout_sec) + if percent_setting_current is not None: + self.verify_attribute_progression(percent_setting_attr, percent_setting_current, percent_setting_previous, order) + percent_setting_previous = percent_setting_current + + # Verifying SpeedSetting (if supported) + if self.supports_speed: + speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) + if speed_setting_current is not None: + self.verify_attribute_progression(speed_setting_attr, speed_setting_current, speed_setting_previous, order) + speed_setting_previous = speed_setting_current + + await attribute_subscription.cancel() + + async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, order, timeout_sec): + # Setup + cluster = Clusters.FanControl + fan_mode_attr = cluster.Attributes.FanMode + percent_setting_attr = cluster.Attributes.PercentSetting + speed_setting_attr = cluster.Attributes.SpeedSetting + speed_max_attr = cluster.Attributes.SpeedMax + attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + fan_mode_off = cluster.Enums.FanModeEnum.kOff + fan_mode_high = cluster.Enums.FanModeEnum.kHigh + percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec + + # Sets the 'for loop' range based on the attribute that will be written to + # and the specified order (ascending or descending) + # Sets which mandatory attribute is to be verified (FanMode or PercentSetting) + speed_max = await self.read_setting(endpoint, speed_max_attr) + speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max + range_loop = range(1, percent_setting_max_value + 1) if order == OrderEnum.Ascending else range(percent_setting_max_value -1, -1, -1) + value_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high + attr_to_verify = fan_mode_attr + if attr_to_write == fan_mode_attr: + range_loop = range(1, fan_mode_high.value + 1) if order == OrderEnum.Ascending else range(fan_mode_high.value -1, -1, -1) + value_init = 0 if order == OrderEnum.Ascending else percent_setting_max_value + attr_to_verify = percent_setting_attr + + logging.info(f"\n[F] Updating {attr_to_write.__name__} {order.name}, verifying {attr_to_verify.__name__} and SpeedSetting (if supported)") + + # Write and read back the initialization value for the attribute to verify + # Verify that the write status is either SUCCESS or INVALID_IN_STATE + # Verify written and read value match + attr_value_read = await self.write_and_verify_attribute(endpoint, attr_to_verify, value_init) + + # Write and read back the initialization value for SpeedSetting (if supported) + # Verify that the write status is either SUCCESS or INVALID_IN_STATE + # Verify written and read value match + if self.supports_speed: + speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) + + # Start subscription + await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) + + # Log FanMode, PercentSetting, and SpeedSetting initialization values + fm = await self.read_setting(endpoint, fan_mode_attr) + ps = await self.read_setting(endpoint, percent_setting_attr) + logging.info(f"\n[F] Initialization state: FanMode({fm}) PercentSetting({ps}) SpeedSetting({speed_setting_read})") + + # Write to attribute iteratively within a range of 100, one at a time + attr_value_current = attr_value_read + attr_value_previous = attr_value_read + speed_setting_current = speed_setting_read + speed_setting_previous = speed_setting_read + for value_to_write in range_loop: + # Clear the queue + attribute_subscription.get_last_report() + + # Write to attribute + value_to_write = self.get_enum(value_to_write) + write_result = await self.write_setting(endpoint, attr_to_write, value_to_write) + write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) + asserts.assert_true(write_status_success, + f"{attr_to_write.__name__} write did not return a result of either SUCCESS or INVALID_IN_STATE") + logging.info(f"\n[F] {attr_to_write.__name__} written: {value_to_write}") + + # Get the subscription queue + queue = attribute_subscription.attribute_queue.queue + + # Verifying PercentSetting + attr_value_current = await self.get_attribute_value_from_queue(queue, attr_to_verify, timeout_sec) + if attr_value_current is not None: + self.verify_attribute_progression(attr_to_verify, attr_value_current, attr_value_previous, order) + attr_value_previous = attr_value_current + + # Verifying SpeedSetting (if supported) + if self.supports_speed: + speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) + if speed_setting_current is not None: + self.verify_attribute_progression(speed_setting_attr, speed_setting_current, speed_setting_previous, order) + speed_setting_previous = speed_setting_current + + await attribute_subscription.cancel() + async def write_and_verify_attribute(self, endpoint, attribute, value_init): value_init = self.get_enum(value_init) @@ -208,21 +365,49 @@ async def test_TC_FAN_3_1(self): # Setup endpoint = self.get_endpoint(default=1) percent_setting_attr = Clusters.FanControl.Attributes.PercentSetting + fan_mode_attr = Clusters.FanControl.Attributes.FanMode self.supports_speed = await self._supports_speed() - timeout_sec = 0.333 - # TH writes to the DUT the PercentSetting iteratively within a range of 1 to 100 one at a time in ascending order - await self.verify_fan_mode(endpoint, percent_setting_attr, OrderEnum.Ascending, timeout_sec) - - # TH writes to the DUT the PercentSetting iteratively within a range of 1 to 100 one at a time in descending order - await self.verify_fan_mode(endpoint, percent_setting_attr, OrderEnum.Descending, timeout_sec) - - # # TH writes to the DUT the SpeedSetting iteratively within a range of 1 to SpeedMax one at a time in ascending order - # await self.verify_fan_mode(endpoint, speed_setting_attr, OrderEnum.Ascending, timeout_sec) - - # # TH writes to the DUT the SpeedSetting iteratively within a range of 1 to SpeedMax one at a time in descending order - # await self.verify_fan_mode(endpoint, speed_setting_attr, OrderEnum.Descending, timeout_sec) + # TH writes to the DUT the PercentSetting attribute iteratively within + # a range of 1 to 100 one at a time in ascending order + # Verifies that FanMode and SpeedSetting values (if supported) are being + # updated accordingly (greater or less than the previous values) + await self.verify_fan_control_attribute_values( + endpoint=endpoint, + attr_to_write=percent_setting_attr, + order=OrderEnum.Ascending, + timeout_sec=timeout_sec) + + # TH writes to the DUT the PercentSetting attribute iteratively within + # a range of 1 to 100 one at a time in descending order + # Verifies that FanMode and SpeedSetting values (if supported) are being + # updated accordingly (greater or less than the previous values) + await self.verify_fan_control_attribute_values( + endpoint=endpoint, + attr_to_write=percent_setting_attr, + order=OrderEnum.Descending, + timeout_sec=timeout_sec) + + # TH writes to the DUT the FanMode attribute iteratively within a range of + # the number of available fan modes one at a time in ascending order + # Verifies that PercentSetting and SpeedSetting values (if supported) are being + # updated accordingly (greater or less than the previous values) + await self.verify_fan_control_attribute_values( + endpoint=endpoint, + attr_to_write=fan_mode_attr, + order=OrderEnum.Ascending, + timeout_sec=timeout_sec) + + # TH writes to the DUT the FanMode attribute iteratively within a range of + # the number of available fan modes one at a time in descending order + # Verifies that PercentSetting and SpeedSetting values (if supported) are being + # updated accordingly (greater or less than the previous values) + await self.verify_fan_control_attribute_values( + endpoint=endpoint, + attr_to_write=fan_mode_attr, + order=OrderEnum.Descending, + timeout_sec=timeout_sec) if __name__ == "__main__": From 43ea7dc422f75955d5d5aaff49323d9b4dcdf3a1 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Fri, 31 Jan 2025 15:35:55 -0800 Subject: [PATCH 24/53] wip --- src/python_testing/TC_FAN_3_1.py | 143 ------------------------------- 1 file changed, 143 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 3669bebbd3a963..c44fc4ebd0845d 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -77,149 +77,6 @@ async def _supports_speed(self) -> bool: time.sleep(0.1) return True - async def verify_fan_mode(self, endpoint, attr_to_write, order, timeout_sec): - logging.info(f"\n[F] Updating {attr_to_write.__name__} {order.name}, verifying FanMode and SpeedSetting (if supported)") - - # Setup - cluster = Clusters.FanControl - fan_mode_attr = cluster.Attributes.FanMode - speed_setting_attr = cluster.Attributes.SpeedSetting - speed_max_attr = cluster.Attributes.SpeedMax - attribute_subscription = ClusterAttributeChangeAccumulator(cluster) - fan_mode_off = cluster.Enums.FanModeEnum.kOff - fan_mode_high = cluster.Enums.FanModeEnum.kHigh - percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec - - # Determine if PercentSetting values will be written in ascending or descending order - fan_mode_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high - speed_max = await self.read_setting(endpoint, speed_max_attr) - speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max - range_loop = range(1, percent_setting_max_value + 1) if order == OrderEnum.Ascending else range(percent_setting_max_value, -1, -1) - - # Write and read back the initialization value for FanMode - # Verify that the write status is either SUCCESS or INVALID_IN_STATE - # Verify written and read value match - fan_mode_read = await self.write_and_verify_attribute(endpoint, fan_mode_attr, fan_mode_init) - - # Write and read back the initialization value for SpeedSetting (if supported) - # Verify that the write status is either SUCCESS or INVALID_IN_STATE - # Verify written and read value match - if self.supports_speed: - speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) - - # Start subscription - await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) - - # Write to attribute iteratively within a range of 100, one at a time - fan_mode_current = fan_mode_read - fan_mode_previous = fan_mode_read - speed_setting_current = speed_setting_read - speed_setting_previous = speed_setting_read - for value_to_write in range_loop: - # Clear the queue - attribute_subscription.get_last_report() - - # Write to attribute - write_result = await self.write_setting(endpoint, attr_to_write, value_to_write) - write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) - asserts.assert_true(write_status_success, - "Attribute write did not return a result of either SUCCESS or INVALID_IN_STATE") - logging.info(f"\n[F] Value written: {value_to_write}") - - # Get the subscription queue - queue = attribute_subscription.attribute_queue.queue - - # Verifying FanMode - fan_mode_current = await self.get_attribute_value_from_queue(queue, fan_mode_attr, timeout_sec) - if fan_mode_current is not None: - self.verify_attribute_progression(fan_mode_attr, fan_mode_current, fan_mode_previous, order) - fan_mode_previous = fan_mode_current - - # Verifying SpeedSetting (if supported) - if self.supports_speed: - speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) - if speed_setting_current is not None: - self.verify_attribute_progression(speed_setting_attr, speed_setting_current, speed_setting_previous, order) - speed_setting_previous = speed_setting_current - - await attribute_subscription.cancel() - - async def verify_fan_mode_spaz(self, endpoint, attr_to_write, order, timeout_sec): - logging.info(f"\n[F] Updating {attr_to_write.__name__} {order.name}, verifying PercentSetting and SpeedSetting (if supported)") - - # Setup - cluster = Clusters.FanControl - fan_mode_attr = cluster.Attributes.FanMode - - percent_setting_attr = cluster.Attributes.PercentSetting - speed_setting_attr = cluster.Attributes.SpeedSetting - speed_max_attr = cluster.Attributes.SpeedMax - attribute_subscription = ClusterAttributeChangeAccumulator(cluster) - fan_mode_off = cluster.Enums.FanModeEnum.kOff - fan_mode_high = cluster.Enums.FanModeEnum.kHigh - percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec - - maxfan = 3 - - # Determine if PercentSetting values will be written in ascending or descending order - # fan_mode_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high - percent_setting_init = 0 if order == OrderEnum.Ascending else 100 - speed_max = await self.read_setting(endpoint, speed_max_attr) - speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max - range_loop = range(1, maxfan + 1) if order == OrderEnum.Ascending else range(maxfan -1, -1, -1) - - # Write and read back the initialization value for PercentSetting - # Verify that the write status is either SUCCESS or INVALID_IN_STATE - # Verify written and read value match - percent_setting_read = await self.write_and_verify_attribute(endpoint, percent_setting_attr, percent_setting_init) - - # Write and read back the initialization value for SpeedSetting (if supported) - # Verify that the write status is either SUCCESS or INVALID_IN_STATE - # Verify written and read value match - if self.supports_speed: - speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) - - fm = await self.read_setting(endpoint, fan_mode_attr) - logging.info(f"\n[F] Initial state: ps({percent_setting_read}) ss({speed_setting_read}) fm({fm})") - - # Start subscription - await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) - - # Write to attribute iteratively within a range of 100, one at a time - percent_setting_current = percent_setting_read - percent_setting_previous = percent_setting_read - speed_setting_current = speed_setting_read - speed_setting_previous = speed_setting_read - for value_to_write in range_loop: - # Clear the queue - attribute_subscription.get_last_report() - - # Write to attribute - value_to_write = cluster.Enums.FanModeEnum(value_to_write) - write_result = await self.write_setting(endpoint, fan_mode_attr, value_to_write) - write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) - asserts.assert_true(write_status_success, - "Attribute write did not return a result of either SUCCESS or INVALID_IN_STATE") - logging.info(f"\n[F] Value written: {value_to_write}") - - # Get the subscription queue - queue = attribute_subscription.attribute_queue.queue - - # Verifying PercentSetting - percent_setting_current = await self.get_attribute_value_from_queue(queue, percent_setting_attr, timeout_sec) - if percent_setting_current is not None: - self.verify_attribute_progression(percent_setting_attr, percent_setting_current, percent_setting_previous, order) - percent_setting_previous = percent_setting_current - - # Verifying SpeedSetting (if supported) - if self.supports_speed: - speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) - if speed_setting_current is not None: - self.verify_attribute_progression(speed_setting_attr, speed_setting_current, speed_setting_previous, order) - speed_setting_previous = speed_setting_current - - await attribute_subscription.cancel() - async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, order, timeout_sec): # Setup cluster = Clusters.FanControl From cd3045ff9f35aba55010319b9871e825bc3a3916 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Sun, 2 Feb 2025 04:33:17 -0800 Subject: [PATCH 25/53] Implements speed feature availability check --- src/python_testing/TC_FAN_3_1.py | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index c44fc4ebd0845d..1d89f10bad77b2 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -72,10 +72,9 @@ async def write_setting(self, endpoint, attr_to_write, value) -> Status: result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attr_to_write(value))]) return result[0].Status - async def _supports_speed(self) -> bool: - # TODO: Implement - time.sleep(0.1) - return True + @staticmethod + def _supports_speed(feature_map) -> bool: + return feature_map & Clusters.FanControl.Bitmaps.Feature.kMultiSpeed async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, order, timeout_sec): # Setup @@ -102,7 +101,9 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord value_init = 0 if order == OrderEnum.Ascending else percent_setting_max_value attr_to_verify = percent_setting_attr - logging.info(f"\n[F] Updating {attr_to_write.__name__} {order.name}, verifying {attr_to_verify.__name__} and SpeedSetting (if supported)") + # Logging exactly what is being tested + if_speed_supported = " and SpeedSetting" if self.supports_speed else "" + logging.info(f"\n[F] Updating {attr_to_write.__name__} {order.name}, verifying {attr_to_verify.__name__}{if_speed_supported}") # Write and read back the initialization value for the attribute to verify # Verify that the write status is either SUCCESS or INVALID_IN_STATE @@ -223,9 +224,11 @@ async def test_TC_FAN_3_1(self): endpoint = self.get_endpoint(default=1) percent_setting_attr = Clusters.FanControl.Attributes.PercentSetting fan_mode_attr = Clusters.FanControl.Attributes.FanMode - self.supports_speed = await self._supports_speed() + feature_map_attr = Clusters.FanControl.Attributes.FeatureMap + feature_map = await self.read_fc_attribute_expect_success(endpoint=endpoint, attribute=feature_map_attr) + self.supports_speed = self._supports_speed(feature_map) timeout_sec = 0.333 - + # TH writes to the DUT the PercentSetting attribute iteratively within # a range of 1 to 100 one at a time in ascending order # Verifies that FanMode and SpeedSetting values (if supported) are being From 6ab091206d7f2e8c3dee9e2612edf77549702f30 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Sun, 2 Feb 2025 18:47:43 -0800 Subject: [PATCH 26/53] Refactor, improve logging --- src/python_testing/TC_FAN_3_1.py | 69 +++++++++++++------------------- 1 file changed, 27 insertions(+), 42 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 1d89f10bad77b2..ff03b525736358 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -34,6 +34,7 @@ # quiet: true # === END CI TEST ARGUMENTS === +import asyncio import enum import logging import time @@ -76,7 +77,7 @@ async def write_setting(self, endpoint, attr_to_write, value) -> Status: def _supports_speed(feature_map) -> bool: return feature_map & Clusters.FanControl.Bitmaps.Feature.kMultiSpeed - async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, order, timeout_sec): + async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, order): # Setup cluster = Clusters.FanControl fan_mode_attr = cluster.Attributes.FanMode @@ -87,6 +88,7 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord fan_mode_off = cluster.Enums.FanModeEnum.kOff fan_mode_high = cluster.Enums.FanModeEnum.kHigh percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec + timeout_sec = 0.1 # Timeout given for item retreival from the attribute queue # Sets the 'for loop' range based on the attribute that will be written to # and the specified order (ascending or descending) @@ -101,9 +103,9 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord value_init = 0 if order == OrderEnum.Ascending else percent_setting_max_value attr_to_verify = percent_setting_attr - # Logging exactly what is being tested + # Logging what's being tested if_speed_supported = " and SpeedSetting" if self.supports_speed else "" - logging.info(f"\n[F] Updating {attr_to_write.__name__} {order.name}, verifying {attr_to_verify.__name__}{if_speed_supported}") + logging.info(f"[FC] Updating {attr_to_write.__name__} {order.name}, verifying {attr_to_verify.__name__}{if_speed_supported}") # Write and read back the initialization value for the attribute to verify # Verify that the write status is either SUCCESS or INVALID_IN_STATE @@ -119,10 +121,10 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord # Start subscription await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) - # Log FanMode, PercentSetting, and SpeedSetting initialization values - fm = await self.read_setting(endpoint, fan_mode_attr) + # Logging FanMode, PercentSetting, and SpeedSetting initialization values + fm = cluster.Enums.FanModeEnum(await self.read_setting(endpoint, fan_mode_attr)) ps = await self.read_setting(endpoint, percent_setting_attr) - logging.info(f"\n[F] Initialization state: FanMode({fm}) PercentSetting({ps}) SpeedSetting({speed_setting_read})") + logging.info(f"[FC] Initialization values: FanMode({fm}:{fm.name}) PercentSetting({ps}) SpeedSetting({speed_setting_read})") # Write to attribute iteratively within a range of 100, one at a time attr_value_current = attr_value_read @@ -139,7 +141,7 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) asserts.assert_true(write_status_success, f"{attr_to_write.__name__} write did not return a result of either SUCCESS or INVALID_IN_STATE") - logging.info(f"\n[F] {attr_to_write.__name__} written: {value_to_write}") + logging.info(f"[FC] {attr_to_write.__name__} written: {value_to_write}") # Get the subscription queue queue = attribute_subscription.attribute_queue.queue @@ -147,21 +149,20 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord # Verifying PercentSetting attr_value_current = await self.get_attribute_value_from_queue(queue, attr_to_verify, timeout_sec) if attr_value_current is not None: - self.verify_attribute_progression(attr_to_verify, attr_value_current, attr_value_previous, order) + self.verify_attribute_transition(attr_to_verify, attr_value_current, attr_value_previous, order) attr_value_previous = attr_value_current # Verifying SpeedSetting (if supported) if self.supports_speed: speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) if speed_setting_current is not None: - self.verify_attribute_progression(speed_setting_attr, speed_setting_current, speed_setting_previous, order) + self.verify_attribute_transition(speed_setting_attr, speed_setting_current, speed_setting_previous, order) speed_setting_previous = speed_setting_current await attribute_subscription.cancel() async def write_and_verify_attribute(self, endpoint, attribute, value_init): value_init = self.get_enum(value_init) - write_result = await self.write_setting(endpoint, attribute, value_init) write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) asserts.assert_true(write_status_success, @@ -171,8 +172,7 @@ async def write_and_verify_attribute(self, endpoint, attribute, value_init): f"Mismatch between written and read {attribute.__name__}") return value_read - def verify_attribute_progression(self, attribute, value_current, value_previous, order): - value_current = self.get_enum(value_current) + def verify_attribute_transition(self, attribute, value_current, value_previous, order): if order == OrderEnum.Ascending: # Verify the current FanMode is greater than the previous FanMode asserts.assert_greater(value_current, value_previous, @@ -181,32 +181,22 @@ def verify_attribute_progression(self, attribute, value_current, value_previous, # Verify the current FanMode is less than the previous FanMode asserts.assert_less(value_current, value_previous, f"Current {attribute.__name__} must be less than previous {attribute.__name__}") - logging.info( - f"\n\t[F] {attribute.__name__} changed from ({value_previous}) to ({value_current})") + + # Logging attribute value change details + if_fan_mode = f"({value_previous}) to ({value_current})" + if attribute == Clusters.FanControl.Attributes.FanMode: + if_fan_mode = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" + logging.info(f"\t\t[FC] {attribute.__name__} changed from {if_fan_mode}") @staticmethod - async def get_attribute_value_from_queue(queue, attribute, timeout_sec) -> Any: + async def get_attribute_value_from_queue(queue, attribute, timeout_sec: float) -> Any: start_time = time.time() - elapsed = 0 - time_remaining = timeout_sec - break_out = False - value = None - - # Wait for the attribute to appear in the queue until the specified timeout - while time_remaining > 0: + while time.time() - start_time < timeout_sec: for q in list(queue): if q.attribute == attribute: - value = q.value - break_out = True - break - - elapsed = time.time() - start_time - time_remaining = timeout_sec - elapsed - - if break_out: - break - - return value + return q.value + await asyncio.sleep(0.01) + return None @staticmethod def get_enum(value): @@ -227,7 +217,6 @@ async def test_TC_FAN_3_1(self): feature_map_attr = Clusters.FanControl.Attributes.FeatureMap feature_map = await self.read_fc_attribute_expect_success(endpoint=endpoint, attribute=feature_map_attr) self.supports_speed = self._supports_speed(feature_map) - timeout_sec = 0.333 # TH writes to the DUT the PercentSetting attribute iteratively within # a range of 1 to 100 one at a time in ascending order @@ -236,8 +225,7 @@ async def test_TC_FAN_3_1(self): await self.verify_fan_control_attribute_values( endpoint=endpoint, attr_to_write=percent_setting_attr, - order=OrderEnum.Ascending, - timeout_sec=timeout_sec) + order=OrderEnum.Ascending) # TH writes to the DUT the PercentSetting attribute iteratively within # a range of 1 to 100 one at a time in descending order @@ -246,8 +234,7 @@ async def test_TC_FAN_3_1(self): await self.verify_fan_control_attribute_values( endpoint=endpoint, attr_to_write=percent_setting_attr, - order=OrderEnum.Descending, - timeout_sec=timeout_sec) + order=OrderEnum.Descending) # TH writes to the DUT the FanMode attribute iteratively within a range of # the number of available fan modes one at a time in ascending order @@ -256,8 +243,7 @@ async def test_TC_FAN_3_1(self): await self.verify_fan_control_attribute_values( endpoint=endpoint, attr_to_write=fan_mode_attr, - order=OrderEnum.Ascending, - timeout_sec=timeout_sec) + order=OrderEnum.Ascending) # TH writes to the DUT the FanMode attribute iteratively within a range of # the number of available fan modes one at a time in descending order @@ -266,8 +252,7 @@ async def test_TC_FAN_3_1(self): await self.verify_fan_control_attribute_values( endpoint=endpoint, attr_to_write=fan_mode_attr, - order=OrderEnum.Descending, - timeout_sec=timeout_sec) + order=OrderEnum.Descending) if __name__ == "__main__": From 50598a61ecc7701a6f94f3b3d29f001294c4d033 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Sun, 2 Feb 2025 18:51:40 -0800 Subject: [PATCH 27/53] Trim --- src/python_testing/TC_FAN_3_1.py | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index ff03b525736358..cec91ed47645e5 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -211,48 +211,36 @@ def pics_TC_FAN_3_1(self) -> list[str]: @async_test_body async def test_TC_FAN_3_1(self): # Setup - endpoint = self.get_endpoint(default=1) + ep = self.get_endpoint(default=1) percent_setting_attr = Clusters.FanControl.Attributes.PercentSetting fan_mode_attr = Clusters.FanControl.Attributes.FanMode feature_map_attr = Clusters.FanControl.Attributes.FeatureMap - feature_map = await self.read_fc_attribute_expect_success(endpoint=endpoint, attribute=feature_map_attr) + feature_map = await self.read_fc_attribute_expect_success(endpoint=ep, attribute=feature_map_attr) self.supports_speed = self._supports_speed(feature_map) # TH writes to the DUT the PercentSetting attribute iteratively within # a range of 1 to 100 one at a time in ascending order # Verifies that FanMode and SpeedSetting values (if supported) are being # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values( - endpoint=endpoint, - attr_to_write=percent_setting_attr, - order=OrderEnum.Ascending) + await self.verify_fan_control_attribute_values(ep, percent_setting_attr, OrderEnum.Ascending) # TH writes to the DUT the PercentSetting attribute iteratively within # a range of 1 to 100 one at a time in descending order # Verifies that FanMode and SpeedSetting values (if supported) are being # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values( - endpoint=endpoint, - attr_to_write=percent_setting_attr, - order=OrderEnum.Descending) + await self.verify_fan_control_attribute_values(ep, percent_setting_attr, OrderEnum.Descending) # TH writes to the DUT the FanMode attribute iteratively within a range of # the number of available fan modes one at a time in ascending order # Verifies that PercentSetting and SpeedSetting values (if supported) are being # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values( - endpoint=endpoint, - attr_to_write=fan_mode_attr, - order=OrderEnum.Ascending) + await self.verify_fan_control_attribute_values(ep, fan_mode_attr, OrderEnum.Ascending) # TH writes to the DUT the FanMode attribute iteratively within a range of # the number of available fan modes one at a time in descending order # Verifies that PercentSetting and SpeedSetting values (if supported) are being # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values( - endpoint=endpoint, - attr_to_write=fan_mode_attr, - order=OrderEnum.Descending) + await self.verify_fan_control_attribute_values(ep, fan_mode_attr, OrderEnum.Descending) if __name__ == "__main__": From 9f957ac8a58c9e075fc7a651d45c864837c4c814 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Tue, 4 Feb 2025 21:29:12 -0800 Subject: [PATCH 28/53] refactor, avaluates invalid in state --- src/python_testing/TC_FAN_3_1.py | 122 +++++++++++++++---------------- 1 file changed, 61 insertions(+), 61 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index cec91ed47645e5..3e8cc75f2bcc66 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -62,22 +62,70 @@ class TC_FAN_3_1(MatterBaseTest): # return [TestStep("1", "Commissioning already done."), # TestStep("2", "Action", "Verification"), # ] - async def read_fc_attribute_expect_success(self, endpoint, attribute): - cluster = Clusters.Objects.FanControl - return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) - async def read_setting(self, endpoint, attribute): - return await self.read_fc_attribute_expect_success(endpoint, attribute) + @staticmethod + async def get_attribute_value_from_queue(queue, attribute, timeout_sec: float) -> Any: + start_time = time.time() + while time.time() - start_time < timeout_sec: + for q in list(queue): + if q.attribute == attribute: + return q.value + await asyncio.sleep(0.01) + return None - async def write_setting(self, endpoint, attr_to_write, value) -> Status: - result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attr_to_write(value))]) - return result[0].Status + @staticmethod + def get_enum(value): + if isinstance(value, enum.Enum): + enum_type = type(value) + value = enum_type(value) + return value @staticmethod def _supports_speed(feature_map) -> bool: return feature_map & Clusters.FanControl.Bitmaps.Feature.kMultiSpeed - async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, order): + async def read_setting(self, endpoint, attribute): + cluster = Clusters.Objects.FanControl + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + async def write_setting(self, endpoint, attribute, value) -> Status: + value = self.get_enum(value) + result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attribute(value))]) + write_status = result[0].Status + write_status_success = (write_status == Status.Success) or (write_status == Status.InvalidInState) + asserts.assert_true(write_status_success, + f"{attribute.__name__} write did not return a result of either SUCCESS or INVALID_IN_STATE ({write_status.name})") + return write_status + + async def write_and_verify_attribute(self, endpoint, attribute, value) -> Any: + await self.write_setting(endpoint, attribute, value) + value_read = await self.read_setting(endpoint, attribute) + asserts.assert_equal(value_read, value, + f"Mismatch between written and read {attribute.__name__}") + return value_read + + def verify_attribute_transition(self, attribute, value_current, value_previous, status, order) -> None: + if status == Status.Success: + if order == OrderEnum.Ascending: + # Verify the current attribute value is greater than the previous attribute value + asserts.assert_greater(value_current, value_previous, + f"Current {attribute.__name__} must be greater than previous {attribute.__name__}") + else: + # Verify the current attribute value is less than the previous attribute value + asserts.assert_less(value_current, value_previous, + f"Current {attribute.__name__} must be less than previous {attribute.__name__}") + elif status == Status.InvalidInState: + # Verify the current attribute value is the same than the previous attribute value + asserts.assert_equal(value_current, value_previous, + f"Current {attribute.__name__} must be equal to previous {attribute.__name__}") + + # Logging attribute value change details + if_fan_mode = f"({value_previous}) to ({value_current})" + if attribute == Clusters.FanControl.Attributes.FanMode: + if_fan_mode = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" + logging.info(f"\t\t[FC] {attribute.__name__} changed from {if_fan_mode}") + + async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, order) -> None: # Setup cluster = Clusters.FanControl fan_mode_attr = cluster.Attributes.FanMode @@ -136,11 +184,7 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord attribute_subscription.get_last_report() # Write to attribute - value_to_write = self.get_enum(value_to_write) - write_result = await self.write_setting(endpoint, attr_to_write, value_to_write) - write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) - asserts.assert_true(write_status_success, - f"{attr_to_write.__name__} write did not return a result of either SUCCESS or INVALID_IN_STATE") + write_status = await self.write_setting(endpoint, attr_to_write, value_to_write) logging.info(f"[FC] {attr_to_write.__name__} written: {value_to_write}") # Get the subscription queue @@ -149,62 +193,18 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord # Verifying PercentSetting attr_value_current = await self.get_attribute_value_from_queue(queue, attr_to_verify, timeout_sec) if attr_value_current is not None: - self.verify_attribute_transition(attr_to_verify, attr_value_current, attr_value_previous, order) + self.verify_attribute_transition(attr_to_verify, attr_value_current, attr_value_previous, write_status, order) attr_value_previous = attr_value_current # Verifying SpeedSetting (if supported) if self.supports_speed: speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) if speed_setting_current is not None: - self.verify_attribute_transition(speed_setting_attr, speed_setting_current, speed_setting_previous, order) + self.verify_attribute_transition(speed_setting_attr, speed_setting_current, speed_setting_previous, write_status, order) speed_setting_previous = speed_setting_current await attribute_subscription.cancel() - async def write_and_verify_attribute(self, endpoint, attribute, value_init): - value_init = self.get_enum(value_init) - write_result = await self.write_setting(endpoint, attribute, value_init) - write_status_success = (write_result == Status.Success) or (write_result == Status.InvalidInState) - asserts.assert_true(write_status_success, - f"{attribute.__name__} write did not return a result of either SUCCESS or INVALID_IN_STATE ({write_result.name})") - value_read = await self.read_setting(endpoint, attribute) - asserts.assert_equal(value_read, value_init, - f"Mismatch between written and read {attribute.__name__}") - return value_read - - def verify_attribute_transition(self, attribute, value_current, value_previous, order): - if order == OrderEnum.Ascending: - # Verify the current FanMode is greater than the previous FanMode - asserts.assert_greater(value_current, value_previous, - f"Current {attribute.__name__} must be greater than previous {attribute.__name__}") - else: - # Verify the current FanMode is less than the previous FanMode - asserts.assert_less(value_current, value_previous, - f"Current {attribute.__name__} must be less than previous {attribute.__name__}") - - # Logging attribute value change details - if_fan_mode = f"({value_previous}) to ({value_current})" - if attribute == Clusters.FanControl.Attributes.FanMode: - if_fan_mode = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" - logging.info(f"\t\t[FC] {attribute.__name__} changed from {if_fan_mode}") - - @staticmethod - async def get_attribute_value_from_queue(queue, attribute, timeout_sec: float) -> Any: - start_time = time.time() - while time.time() - start_time < timeout_sec: - for q in list(queue): - if q.attribute == attribute: - return q.value - await asyncio.sleep(0.01) - return None - - @staticmethod - def get_enum(value): - if isinstance(value, enum.Enum): - enum_type = type(value) - value = enum_type(value) - return value - def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] @@ -215,7 +215,7 @@ async def test_TC_FAN_3_1(self): percent_setting_attr = Clusters.FanControl.Attributes.PercentSetting fan_mode_attr = Clusters.FanControl.Attributes.FanMode feature_map_attr = Clusters.FanControl.Attributes.FeatureMap - feature_map = await self.read_fc_attribute_expect_success(endpoint=ep, attribute=feature_map_attr) + feature_map = await self.read_setting(endpoint=ep, attribute=feature_map_attr) self.supports_speed = self._supports_speed(feature_map) # TH writes to the DUT the PercentSetting attribute iteratively within From 5cb339348df0ca439e373eedaf76016fd1147d77 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Wed, 5 Feb 2025 19:22:25 -0800 Subject: [PATCH 29/53] lint/restyle --- src/python_testing/TC_FAN_3_1.py | 32 ++++++++++++++++++-------------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 3e8cc75f2bcc66..730b062622f953 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -45,7 +45,7 @@ from chip.clusters import ClusterObjects as ClusterObjects from chip.interaction_model import Status from chip.testing.matter_testing import (ClusterAttributeChangeAccumulator, MatterBaseTest, TestStep, async_test_body, - default_matter_test_main, has_feature) + default_matter_test_main) from mobly import asserts @@ -58,10 +58,8 @@ class OrderEnum(Enum): class TC_FAN_3_1(MatterBaseTest): - # def steps_TC_FAN_3_1(self): - # return [TestStep("1", "Commissioning already done."), - # TestStep("2", "Action", "Verification"), - # ] + def steps_TC_FAN_3_1(self): + return [TestStep("1", "Commissioning already done.")] @staticmethod async def get_attribute_value_from_queue(queue, attribute, timeout_sec: float) -> Any: @@ -103,13 +101,13 @@ async def write_and_verify_attribute(self, endpoint, attribute, value) -> Any: asserts.assert_equal(value_read, value, f"Mismatch between written and read {attribute.__name__}") return value_read - + def verify_attribute_transition(self, attribute, value_current, value_previous, status, order) -> None: if status == Status.Success: if order == OrderEnum.Ascending: # Verify the current attribute value is greater than the previous attribute value asserts.assert_greater(value_current, value_previous, - f"Current {attribute.__name__} must be greater than previous {attribute.__name__}") + f"Current {attribute.__name__} must be greater than previous {attribute.__name__}") else: # Verify the current attribute value is less than the previous attribute value asserts.assert_less(value_current, value_previous, @@ -135,25 +133,28 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord attribute_subscription = ClusterAttributeChangeAccumulator(cluster) fan_mode_off = cluster.Enums.FanModeEnum.kOff fan_mode_high = cluster.Enums.FanModeEnum.kHigh - percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec - timeout_sec = 0.1 # Timeout given for item retreival from the attribute queue + percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec + timeout_sec = 0.1 # Timeout given for item retreival from the attribute queue # Sets the 'for loop' range based on the attribute that will be written to # and the specified order (ascending or descending) # Sets which mandatory attribute is to be verified (FanMode or PercentSetting) speed_max = await self.read_setting(endpoint, speed_max_attr) speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max - range_loop = range(1, percent_setting_max_value + 1) if order == OrderEnum.Ascending else range(percent_setting_max_value -1, -1, -1) + range_loop = range(1, percent_setting_max_value + + 1) if order == OrderEnum.Ascending else range(percent_setting_max_value -1, -1, -1) value_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high attr_to_verify = fan_mode_attr if attr_to_write == fan_mode_attr: - range_loop = range(1, fan_mode_high.value + 1) if order == OrderEnum.Ascending else range(fan_mode_high.value -1, -1, -1) + range_loop = range(1, fan_mode_high.value + + 1) if order == OrderEnum.Ascending else range(fan_mode_high.value -1, -1, -1) value_init = 0 if order == OrderEnum.Ascending else percent_setting_max_value attr_to_verify = percent_setting_attr # Logging what's being tested if_speed_supported = " and SpeedSetting" if self.supports_speed else "" - logging.info(f"[FC] Updating {attr_to_write.__name__} {order.name}, verifying {attr_to_verify.__name__}{if_speed_supported}") + logging.info( + f"[FC] Updating {attr_to_write.__name__} {order.name}, verifying {attr_to_verify.__name__}{if_speed_supported}") # Write and read back the initialization value for the attribute to verify # Verify that the write status is either SUCCESS or INVALID_IN_STATE @@ -200,7 +201,8 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord if self.supports_speed: speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) if speed_setting_current is not None: - self.verify_attribute_transition(speed_setting_attr, speed_setting_current, speed_setting_previous, write_status, order) + self.verify_attribute_transition(speed_setting_attr, speed_setting_current, + speed_setting_previous, write_status, order) speed_setting_previous = speed_setting_current await attribute_subscription.cancel() @@ -217,7 +219,9 @@ async def test_TC_FAN_3_1(self): feature_map_attr = Clusters.FanControl.Attributes.FeatureMap feature_map = await self.read_setting(endpoint=ep, attribute=feature_map_attr) self.supports_speed = self._supports_speed(feature_map) - + + self.step("1") + # TH writes to the DUT the PercentSetting attribute iteratively within # a range of 1 to 100 one at a time in ascending order # Verifies that FanMode and SpeedSetting values (if supported) are being From b37d24a9eb26c2af191fb461e3eb0b77455ee527 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Fri, 14 Feb 2025 01:25:08 -0800 Subject: [PATCH 30/53] Adds test steps, updates write status check, refactor --- src/python_testing/TC_FAN_3_1.py | 207 +++++++++++++++++++------------ 1 file changed, 127 insertions(+), 80 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 730b062622f953..da93e75420e6ba 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -59,7 +59,26 @@ class OrderEnum(Enum): class TC_FAN_3_1(MatterBaseTest): def steps_TC_FAN_3_1(self): - return [TestStep("1", "Commissioning already done.")] + return [TestStep(1, "[FC] Commissioning already done.", is_commissioning=True), + TestStep(2, "[FC] TH checks the DUT for the presence of the SpeedSetting feature.", "Save the result for future use."), + TestStep(3, "[FC] TH subscribes to the DUT's FanControl cluster.", "Enables the TH to receive attribute updates."), + TestStep(4, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in ascending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", + "Set the value of the FanMode attribute to 0 (Off), and if the SpeedSetting feature is supported, set its attribute value to 0. Read the attribute values back and verify that they match the written values."), + TestStep(5, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in ascending order (from 1 to 100). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", + "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + TestStep(6, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in descending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", + "Set the value of the FanMode attribute to 3 (High), and if the SpeedSetting feature is supported, read the SpeedMax attribute value and set it as the value for the SpeedSetting attribute. Read the attribute values back and verify that they match the written values."), + TestStep(7, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in descending order (from 99 to 0). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", + "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + TestStep(8, "[FC] TH configures the testing setup for the following scenario: Updating the FanMode attribute in ascending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes.", + "Set the value of the PercentSetting attribute to 0, and if the SpeedSetting feature is supported, set its attribute value to 0. Read the attribute values back and verify that they match the written values."), + TestStep(9, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in ascending order (from 0 (Off) to 3 (High)). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", + "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + TestStep(10, "[FC] TH configures the testing setup for the following scenario: Updating the FanMode attribute in descending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes.", + "Set the value of the PercentSetting attribute to 100, and if the SpeedSetting feature is supported, read the SpeedMax attribute value and set it as the value for the SpeedSetting attribute. Read the attribute values back and verify that they match the written values"), + TestStep(11, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in descending order (from 3 (High) to 0 (Off)) For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", + "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + ] @staticmethod async def get_attribute_value_from_queue(queue, attribute, timeout_sec: float) -> Any: @@ -78,10 +97,6 @@ def get_enum(value): value = enum_type(value) return value - @staticmethod - def _supports_speed(feature_map) -> bool: - return feature_map & Clusters.FanControl.Bitmaps.Feature.kMultiSpeed - async def read_setting(self, endpoint, attribute): cluster = Clusters.Objects.FanControl return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) @@ -92,69 +107,70 @@ async def write_setting(self, endpoint, attribute, value) -> Status: write_status = result[0].Status write_status_success = (write_status == Status.Success) or (write_status == Status.InvalidInState) asserts.assert_true(write_status_success, - f"{attribute.__name__} write did not return a result of either SUCCESS or INVALID_IN_STATE ({write_status.name})") + f"[FC] {attribute.__name__} write did not return a result of either SUCCESS or INVALID_IN_STATE ({write_status.name})") return write_status async def write_and_verify_attribute(self, endpoint, attribute, value) -> Any: await self.write_setting(endpoint, attribute, value) value_read = await self.read_setting(endpoint, attribute) asserts.assert_equal(value_read, value, - f"Mismatch between written and read {attribute.__name__}") + f"[FC] Mismatch between written and read attribute value ({attribute.__name__})") return value_read - def verify_attribute_transition(self, attribute, value_current, value_previous, status, order) -> None: - if status == Status.Success: - if order == OrderEnum.Ascending: - # Verify the current attribute value is greater than the previous attribute value - asserts.assert_greater(value_current, value_previous, - f"Current {attribute.__name__} must be greater than previous {attribute.__name__}") - else: - # Verify the current attribute value is less than the previous attribute value - asserts.assert_less(value_current, value_previous, - f"Current {attribute.__name__} must be less than previous {attribute.__name__}") - elif status == Status.InvalidInState: - # Verify the current attribute value is the same than the previous attribute value - asserts.assert_equal(value_current, value_previous, - f"Current {attribute.__name__} must be equal to previous {attribute.__name__}") + def verify_attribute_transition(self, attr_to_verify, value_current, value_previous, order) -> None: + if order == OrderEnum.Ascending: + # Verify that the current attribute value is greater than the previous attribute value + asserts.assert_greater(value_current, value_previous, + f"[FC] Current {attr_to_verify.__name__} must be greater than previous {attr_to_verify.__name__}") + else: + # Verify that the current attribute value is less than the previous attribute value + asserts.assert_less(value_current, value_previous, + f"[FC] Current {attr_to_verify.__name__} must be less than previous {attr_to_verify.__name__}") # Logging attribute value change details - if_fan_mode = f"({value_previous}) to ({value_current})" - if attribute == Clusters.FanControl.Attributes.FanMode: - if_fan_mode = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" - logging.info(f"\t\t[FC] {attribute.__name__} changed from {if_fan_mode}") + sub_text = f"({value_previous}) to ({value_current})" + if attr_to_verify == Clusters.FanControl.Attributes.FanMode: + sub_text = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" + logging.info(f"\t\t[FC] {attr_to_verify.__name__} changed from {sub_text}") - async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, order) -> None: + async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, order) -> None: # Setup cluster = Clusters.FanControl fan_mode_attr = cluster.Attributes.FanMode percent_setting_attr = cluster.Attributes.PercentSetting speed_setting_attr = cluster.Attributes.SpeedSetting speed_max_attr = cluster.Attributes.SpeedMax - attribute_subscription = ClusterAttributeChangeAccumulator(cluster) fan_mode_off = cluster.Enums.FanModeEnum.kOff fan_mode_high = cluster.Enums.FanModeEnum.kHigh percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec timeout_sec = 0.1 # Timeout given for item retreival from the attribute queue - # Sets the 'for loop' range based on the attribute that will be written to - # and the specified order (ascending or descending) + # *** NEXT STEP *** + # TH configures the testing setup for the one of the following scenarios: + # - Updating the PercentSetting attribute in ascending order and monitoring the FanMode (and SpeedSetting, if supported) attributes + # - Updating the PercentSetting attribute in descending order and monitoring the FanMode (and SpeedSetting, if supported) attributes + # - Updating the FanMode attribute in aescending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes + # - Updating the FanMode attribute in descending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes + self.step(self.current_step_index + 1) + + # Sets the 'for loop' range for attribute updates based on the attribute that + # will be updated and the order (ascending or descending) # Sets which mandatory attribute is to be verified (FanMode or PercentSetting) - speed_max = await self.read_setting(endpoint, speed_max_attr) - speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max - range_loop = range(1, percent_setting_max_value + - 1) if order == OrderEnum.Ascending else range(percent_setting_max_value -1, -1, -1) - value_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high - attr_to_verify = fan_mode_attr - if attr_to_write == fan_mode_attr: + if attr_to_update == percent_setting_attr: + attr_to_verify = fan_mode_attr + range_loop = range(1, percent_setting_max_value + + 1) if order == OrderEnum.Ascending else range(percent_setting_max_value -1, -1, -1) + value_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high + elif attr_to_update == fan_mode_attr: + attr_to_verify = percent_setting_attr range_loop = range(1, fan_mode_high.value + 1) if order == OrderEnum.Ascending else range(fan_mode_high.value -1, -1, -1) value_init = 0 if order == OrderEnum.Ascending else percent_setting_max_value - attr_to_verify = percent_setting_attr # Logging what's being tested - if_speed_supported = " and SpeedSetting" if self.supports_speed else "" + sub_text = " and SpeedSetting" if self.supports_speed else "" logging.info( - f"[FC] Updating {attr_to_write.__name__} {order.name}, verifying {attr_to_verify.__name__}{if_speed_supported}") + f"[FC] Updating {attr_to_update.__name__} {order.name}, verifying {attr_to_verify.__name__}{sub_text}") # Write and read back the initialization value for the attribute to verify # Verify that the write status is either SUCCESS or INVALID_IN_STATE @@ -163,49 +179,70 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_write, ord # Write and read back the initialization value for SpeedSetting (if supported) # Verify that the write status is either SUCCESS or INVALID_IN_STATE - # Verify written and read value match + # Verify that the written and read value match if self.supports_speed: + speed_max = await self.read_setting(endpoint, speed_max_attr) + speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) - # Start subscription - await attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) + # Logging FanMode, PercentSetting, and SpeedSetting (if supported) initialization values + fan_mode_log = cluster.Enums.FanModeEnum(await self.read_setting(endpoint, fan_mode_attr)) + percent_setting_log = await self.read_setting(endpoint, percent_setting_attr) + sub_text = f" SpeedSetting({speed_setting_read})" if self.supports_speed else "" + logging.info(f"[FC] Initialization values: FanMode({fan_mode_log}:{fan_mode_log.name}) PercentSetting({percent_setting_log}){sub_text}") - # Logging FanMode, PercentSetting, and SpeedSetting initialization values - fm = cluster.Enums.FanModeEnum(await self.read_setting(endpoint, fan_mode_attr)) - ps = await self.read_setting(endpoint, percent_setting_attr) - logging.info(f"[FC] Initialization values: FanMode({fm}:{fm.name}) PercentSetting({ps}) SpeedSetting({speed_setting_read})") + # *** NEXT STEP *** + # TH performs testing based on the scenario setup from the previous step + self.step(self.current_step_index + 1) - # Write to attribute iteratively within a range of 100, one at a time + # Write to attribute iteratively within the configured range attr_value_current = attr_value_read attr_value_previous = attr_value_read speed_setting_current = speed_setting_read speed_setting_previous = speed_setting_read for value_to_write in range_loop: - # Clear the queue - attribute_subscription.get_last_report() + # Clear the queue before each update to avoid attribute report duplicates + self.attribute_subscription.get_last_report() # Write to attribute - write_status = await self.write_setting(endpoint, attr_to_write, value_to_write) - logging.info(f"[FC] {attr_to_write.__name__} written: {value_to_write}") - - # Get the subscription queue - queue = attribute_subscription.attribute_queue.queue - - # Verifying PercentSetting - attr_value_current = await self.get_attribute_value_from_queue(queue, attr_to_verify, timeout_sec) - if attr_value_current is not None: - self.verify_attribute_transition(attr_to_verify, attr_value_current, attr_value_previous, write_status, order) - attr_value_previous = attr_value_current - - # Verifying SpeedSetting (if supported) - if self.supports_speed: - speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) - if speed_setting_current is not None: - self.verify_attribute_transition(speed_setting_attr, speed_setting_current, - speed_setting_previous, write_status, order) - speed_setting_previous = speed_setting_current - - await attribute_subscription.cancel() + write_status = await self.write_setting(endpoint, attr_to_update, value_to_write) + logging.info(f"[FC] {attr_to_update.__name__} written: {value_to_write}") + + # If the write status is SUCCESS, it means the write operation occurred + # Verify that the current attribute value is greater than the previous + # one if order was set to Ascending, or less if order was set to Descending + if write_status == Status.Success: + # Get current attribute value + queue = self.attribute_subscription.attribute_queue.queue + attr_value_current = await self.get_attribute_value_from_queue(queue, attr_to_verify, timeout_sec) + + # Verify attribute value + if attr_value_current is not None: + self.verify_attribute_transition(attr_to_verify, attr_value_current, attr_value_previous, order) + attr_value_previous = attr_value_current + + # Verify the same for the SpeedSetting attribute value (if supported) + if self.supports_speed: + speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) + if speed_setting_current is not None: + self.verify_attribute_transition(speed_setting_attr, speed_setting_current, speed_setting_previous, order) + speed_setting_previous = speed_setting_current + + # If the write status is INVALID_IN_STATE, it means no write operation occurred + # Verify that the current attribute value is equal to the previous one + elif write_status == Status.InvalidInState: + # Get current attribute value + attr_value_current = await self.read_setting(endpoint, attr_to_verify) + + # Verify attribute value + asserts.assert_equal(attr_value_current, attr_value_previous, + f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to its previous value") + + # Verify the same for the SpeedSetting attribute value (if supported) + if self.supports_speed: + speed_setting_current = await self.read_setting(endpoint, speed_setting_attr) + asserts.assert_equal(speed_setting_current, speed_setting_previous, + "Current SpeedSetting attribute value must be equal to its previous value") def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] @@ -214,37 +251,47 @@ def pics_TC_FAN_3_1(self) -> list[str]: async def test_TC_FAN_3_1(self): # Setup ep = self.get_endpoint(default=1) - percent_setting_attr = Clusters.FanControl.Attributes.PercentSetting - fan_mode_attr = Clusters.FanControl.Attributes.FanMode - feature_map_attr = Clusters.FanControl.Attributes.FeatureMap - feature_map = await self.read_setting(endpoint=ep, attribute=feature_map_attr) - self.supports_speed = self._supports_speed(feature_map) + cluster = Clusters.FanControl + + # *** STEP 1 *** + # Commissioning already done + self.step(1) + + # *** STEP 2 *** + # TH checks for the existence of the SpeedSetting feature + self.step(2) + feature_map = await self.read_setting(ep, cluster.Attributes.FeatureMap) + self.supports_speed = bool(feature_map & cluster.Bitmaps.Feature.kMultiSpeed) - self.step("1") + # *** STEP 3 *** + # TH subscribes to the DUT's FanControl cluster + self.step(3) + self.attribute_subscription = ClusterAttributeChangeAccumulator(cluster) + await self.attribute_subscription.start(self.default_controller, self.dut_node_id, ep) # TH writes to the DUT the PercentSetting attribute iteratively within # a range of 1 to 100 one at a time in ascending order # Verifies that FanMode and SpeedSetting values (if supported) are being # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values(ep, percent_setting_attr, OrderEnum.Ascending) + await self.verify_fan_control_attribute_values(ep, cluster.Attributes.PercentSetting, OrderEnum.Ascending) # TH writes to the DUT the PercentSetting attribute iteratively within # a range of 1 to 100 one at a time in descending order # Verifies that FanMode and SpeedSetting values (if supported) are being # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values(ep, percent_setting_attr, OrderEnum.Descending) + await self.verify_fan_control_attribute_values(ep, cluster.Attributes.PercentSetting, OrderEnum.Descending) # TH writes to the DUT the FanMode attribute iteratively within a range of # the number of available fan modes one at a time in ascending order # Verifies that PercentSetting and SpeedSetting values (if supported) are being # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values(ep, fan_mode_attr, OrderEnum.Ascending) + await self.verify_fan_control_attribute_values(ep, cluster.Attributes.FanMode, OrderEnum.Ascending) # TH writes to the DUT the FanMode attribute iteratively within a range of # the number of available fan modes one at a time in descending order # Verifies that PercentSetting and SpeedSetting values (if supported) are being # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values(ep, fan_mode_attr, OrderEnum.Descending) + await self.verify_fan_control_attribute_values(ep, cluster.Attributes.FanMode, OrderEnum.Descending) if __name__ == "__main__": From 553e2e788598b2319d431399b0ba5178e9167149 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Fri, 14 Feb 2025 01:36:57 -0800 Subject: [PATCH 31/53] Fix restyle --- src/python_testing/TC_FAN_3_1.py | 34 +++++++++++++++++--------------- 1 file changed, 18 insertions(+), 16 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index da93e75420e6ba..b859a8c1ec21d0 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -60,24 +60,25 @@ class OrderEnum(Enum): class TC_FAN_3_1(MatterBaseTest): def steps_TC_FAN_3_1(self): return [TestStep(1, "[FC] Commissioning already done.", is_commissioning=True), - TestStep(2, "[FC] TH checks the DUT for the presence of the SpeedSetting feature.", "Save the result for future use."), + TestStep(2, "[FC] TH checks the DUT for the presence of the SpeedSetting feature.", + "Save the result for future use."), TestStep(3, "[FC] TH subscribes to the DUT's FanControl cluster.", "Enables the TH to receive attribute updates."), TestStep(4, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in ascending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", - "Set the value of the FanMode attribute to 0 (Off), and if the SpeedSetting feature is supported, set its attribute value to 0. Read the attribute values back and verify that they match the written values."), + "Set the value of the FanMode attribute to 0 (Off), and if the SpeedSetting feature is supported, set its attribute value to 0. Read the attribute values back and verify that they match the written values."), TestStep(5, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in ascending order (from 1 to 100). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", - "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), TestStep(6, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in descending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", - "Set the value of the FanMode attribute to 3 (High), and if the SpeedSetting feature is supported, read the SpeedMax attribute value and set it as the value for the SpeedSetting attribute. Read the attribute values back and verify that they match the written values."), + "Set the value of the FanMode attribute to 3 (High), and if the SpeedSetting feature is supported, read the SpeedMax attribute value and set it as the value for the SpeedSetting attribute. Read the attribute values back and verify that they match the written values."), TestStep(7, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in descending order (from 99 to 0). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", - "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), TestStep(8, "[FC] TH configures the testing setup for the following scenario: Updating the FanMode attribute in ascending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes.", - "Set the value of the PercentSetting attribute to 0, and if the SpeedSetting feature is supported, set its attribute value to 0. Read the attribute values back and verify that they match the written values."), + "Set the value of the PercentSetting attribute to 0, and if the SpeedSetting feature is supported, set its attribute value to 0. Read the attribute values back and verify that they match the written values."), TestStep(9, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in ascending order (from 0 (Off) to 3 (High)). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", - "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), TestStep(10, "[FC] TH configures the testing setup for the following scenario: Updating the FanMode attribute in descending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes.", - "Set the value of the PercentSetting attribute to 100, and if the SpeedSetting feature is supported, read the SpeedMax attribute value and set it as the value for the SpeedSetting attribute. Read the attribute values back and verify that they match the written values"), + "Set the value of the PercentSetting attribute to 100, and if the SpeedSetting feature is supported, read the SpeedMax attribute value and set it as the value for the SpeedSetting attribute. Read the attribute values back and verify that they match the written values"), TestStep(11, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in descending order (from 3 (High) to 0 (Off)) For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", - "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), ] @staticmethod @@ -121,11 +122,11 @@ def verify_attribute_transition(self, attr_to_verify, value_current, value_previ if order == OrderEnum.Ascending: # Verify that the current attribute value is greater than the previous attribute value asserts.assert_greater(value_current, value_previous, - f"[FC] Current {attr_to_verify.__name__} must be greater than previous {attr_to_verify.__name__}") + f"[FC] Current {attr_to_verify.__name__} must be greater than previous {attr_to_verify.__name__}") else: # Verify that the current attribute value is less than the previous attribute value asserts.assert_less(value_current, value_previous, - f"[FC] Current {attr_to_verify.__name__} must be less than previous {attr_to_verify.__name__}") + f"[FC] Current {attr_to_verify.__name__} must be less than previous {attr_to_verify.__name__}") # Logging attribute value change details sub_text = f"({value_previous}) to ({value_current})" @@ -159,12 +160,12 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or if attr_to_update == percent_setting_attr: attr_to_verify = fan_mode_attr range_loop = range(1, percent_setting_max_value + - 1) if order == OrderEnum.Ascending else range(percent_setting_max_value -1, -1, -1) + 1) if order == OrderEnum.Ascending else range(percent_setting_max_value - 1, -1, -1) value_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high elif attr_to_update == fan_mode_attr: attr_to_verify = percent_setting_attr range_loop = range(1, fan_mode_high.value + - 1) if order == OrderEnum.Ascending else range(fan_mode_high.value -1, -1, -1) + 1) if order == OrderEnum.Ascending else range(fan_mode_high.value - 1, -1, -1) value_init = 0 if order == OrderEnum.Ascending else percent_setting_max_value # Logging what's being tested @@ -189,7 +190,8 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or fan_mode_log = cluster.Enums.FanModeEnum(await self.read_setting(endpoint, fan_mode_attr)) percent_setting_log = await self.read_setting(endpoint, percent_setting_attr) sub_text = f" SpeedSetting({speed_setting_read})" if self.supports_speed else "" - logging.info(f"[FC] Initialization values: FanMode({fan_mode_log}:{fan_mode_log.name}) PercentSetting({percent_setting_log}){sub_text}") + logging.info( + f"[FC] Initialization values: FanMode({fan_mode_log}:{fan_mode_log.name}) PercentSetting({percent_setting_log}){sub_text}") # *** NEXT STEP *** # TH performs testing based on the scenario setup from the previous step @@ -236,13 +238,13 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or # Verify attribute value asserts.assert_equal(attr_value_current, attr_value_previous, - f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to its previous value") + f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to its previous value") # Verify the same for the SpeedSetting attribute value (if supported) if self.supports_speed: speed_setting_current = await self.read_setting(endpoint, speed_setting_attr) asserts.assert_equal(speed_setting_current, speed_setting_previous, - "Current SpeedSetting attribute value must be equal to its previous value") + "Current SpeedSetting attribute value must be equal to its previous value") def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] From cce3abdc7567c3eb5880ea532ed6a48113495bd5 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Fri, 14 Feb 2025 11:15:19 -0800 Subject: [PATCH 32/53] Fixes tests commented description --- src/python_testing/TC_FAN_3_1.py | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index b859a8c1ec21d0..294b0d3b22ea13 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -252,7 +252,7 @@ def pics_TC_FAN_3_1(self) -> list[str]: @async_test_body async def test_TC_FAN_3_1(self): # Setup - ep = self.get_endpoint(default=1) + endpoint = self.get_endpoint(default=1) cluster = Clusters.FanControl # *** STEP 1 *** @@ -262,38 +262,38 @@ async def test_TC_FAN_3_1(self): # *** STEP 2 *** # TH checks for the existence of the SpeedSetting feature self.step(2) - feature_map = await self.read_setting(ep, cluster.Attributes.FeatureMap) + feature_map = await self.read_setting(endpoint, cluster.Attributes.FeatureMap) self.supports_speed = bool(feature_map & cluster.Bitmaps.Feature.kMultiSpeed) # *** STEP 3 *** # TH subscribes to the DUT's FanControl cluster self.step(3) self.attribute_subscription = ClusterAttributeChangeAccumulator(cluster) - await self.attribute_subscription.start(self.default_controller, self.dut_node_id, ep) + await self.attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) # TH writes to the DUT the PercentSetting attribute iteratively within # a range of 1 to 100 one at a time in ascending order # Verifies that FanMode and SpeedSetting values (if supported) are being - # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values(ep, cluster.Attributes.PercentSetting, OrderEnum.Ascending) + # updated accordingly (current values greater than previous values) + await self.verify_fan_control_attribute_values(endpoint, cluster.Attributes.PercentSetting, OrderEnum.Ascending) # TH writes to the DUT the PercentSetting attribute iteratively within - # a range of 1 to 100 one at a time in descending order + # a range of 100 to 0 one at a time in descending order # Verifies that FanMode and SpeedSetting values (if supported) are being - # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values(ep, cluster.Attributes.PercentSetting, OrderEnum.Descending) + # updated accordingly (current values less than previous values) + await self.verify_fan_control_attribute_values(endpoint, cluster.Attributes.PercentSetting, OrderEnum.Descending) # TH writes to the DUT the FanMode attribute iteratively within a range of # the number of available fan modes one at a time in ascending order # Verifies that PercentSetting and SpeedSetting values (if supported) are being - # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values(ep, cluster.Attributes.FanMode, OrderEnum.Ascending) + # updated accordingly (current values greater than previous values) + await self.verify_fan_control_attribute_values(endpoint, cluster.Attributes.FanMode, OrderEnum.Ascending) # TH writes to the DUT the FanMode attribute iteratively within a range of # the number of available fan modes one at a time in descending order # Verifies that PercentSetting and SpeedSetting values (if supported) are being - # updated accordingly (greater or less than the previous values) - await self.verify_fan_control_attribute_values(ep, cluster.Attributes.FanMode, OrderEnum.Descending) + # updated accordingly (current values less than previous values) + await self.verify_fan_control_attribute_values(endpoint, cluster.Attributes.FanMode, OrderEnum.Descending) if __name__ == "__main__": From 07da5e0ca0a7286fcebd64a7f09218b0b2818433 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Sun, 16 Feb 2025 18:55:02 -0800 Subject: [PATCH 33/53] Updates initialization step --- src/python_testing/TC_FAN_3_1.py | 80 ++++++++++++++++---------------- 1 file changed, 40 insertions(+), 40 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 294b0d3b22ea13..4edbea44756662 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -49,11 +49,6 @@ from mobly import asserts -class OrderEnum(Enum): - Ascending = 1 - Descending = 2 - - logger = logging.getLogger(__name__) @@ -64,19 +59,19 @@ def steps_TC_FAN_3_1(self): "Save the result for future use."), TestStep(3, "[FC] TH subscribes to the DUT's FanControl cluster.", "Enables the TH to receive attribute updates."), TestStep(4, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in ascending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", - "Set the value of the FanMode attribute to 0 (Off), and if the SpeedSetting feature is supported, set its attribute value to 0. Read the attribute values back and verify that they match the written values."), + "Initialize attribute values: - FanMode: 0 (Off). - PercentSetting: 0. - SpeedSetting (if supported): 0. Read the attribute values back and verify that they match the written values."), TestStep(5, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in ascending order (from 1 to 100). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), TestStep(6, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in descending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", - "Set the value of the FanMode attribute to 3 (High), and if the SpeedSetting feature is supported, read the SpeedMax attribute value and set it as the value for the SpeedSetting attribute. Read the attribute values back and verify that they match the written values."), + "Initialize attribute values: - FanMode: 3 (High). - PercentSetting: 100. - SpeedSetting (if supported): SpeedMax attribute value. Read the attribute values back and verify that they match the written values."), TestStep(7, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in descending order (from 99 to 0). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), TestStep(8, "[FC] TH configures the testing setup for the following scenario: Updating the FanMode attribute in ascending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes.", - "Set the value of the PercentSetting attribute to 0, and if the SpeedSetting feature is supported, set its attribute value to 0. Read the attribute values back and verify that they match the written values."), + "Initialize attribute values: - FanMode: 0 (Off). - PercentSetting: 0. - SpeedSetting (if supported): 0. Read the attribute values back and verify that they match the written values."), TestStep(9, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in ascending order (from 0 (Off) to 3 (High)). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), TestStep(10, "[FC] TH configures the testing setup for the following scenario: Updating the FanMode attribute in descending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes.", - "Set the value of the PercentSetting attribute to 100, and if the SpeedSetting feature is supported, read the SpeedMax attribute value and set it as the value for the SpeedSetting attribute. Read the attribute values back and verify that they match the written values"), + "Initialize attribute values: - FanMode: 3 (High). - PercentSetting: 100. - SpeedSetting (if supported): SpeedMax attribute value. Read the attribute values back and verify that they match the written values."), TestStep(11, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in descending order (from 3 (High) to 0 (Off)) For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), ] @@ -118,8 +113,9 @@ async def write_and_verify_attribute(self, endpoint, attribute, value) -> Any: f"[FC] Mismatch between written and read attribute value ({attribute.__name__})") return value_read - def verify_attribute_transition(self, attr_to_verify, value_current, value_previous, order) -> None: - if order == OrderEnum.Ascending: + def verify_attribute_transition(self, attr_to_verify, value_current, value_previous, step_direction) -> None: + step_direction_enum = Clusters.FanControl.Enums.StepDirectionEnum + if step_direction == step_direction_enum.kIncrease: # Verify that the current attribute value is greater than the previous attribute value asserts.assert_greater(value_current, value_previous, f"[FC] Current {attr_to_verify.__name__} must be greater than previous {attr_to_verify.__name__}") @@ -134,9 +130,10 @@ def verify_attribute_transition(self, attr_to_verify, value_current, value_previ sub_text = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" logging.info(f"\t\t[FC] {attr_to_verify.__name__} changed from {sub_text}") - async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, order) -> None: + async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, step_direction) -> None: # Setup cluster = Clusters.FanControl + step_direction_enum = cluster.Enums.StepDirectionEnum fan_mode_attr = cluster.Attributes.FanMode percent_setting_attr = cluster.Attributes.PercentSetting speed_setting_attr = cluster.Attributes.SpeedSetting @@ -160,43 +157,44 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or if attr_to_update == percent_setting_attr: attr_to_verify = fan_mode_attr range_loop = range(1, percent_setting_max_value + - 1) if order == OrderEnum.Ascending else range(percent_setting_max_value - 1, -1, -1) - value_init = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high + 1) if step_direction == step_direction_enum.kIncrease else range(percent_setting_max_value - 1, -1, -1) + value_init_verify = fan_mode_off if step_direction == step_direction_enum.kIncrease else fan_mode_high + value_init_update = 0 if step_direction == step_direction_enum.kIncrease else percent_setting_max_value elif attr_to_update == fan_mode_attr: attr_to_verify = percent_setting_attr range_loop = range(1, fan_mode_high.value + - 1) if order == OrderEnum.Ascending else range(fan_mode_high.value - 1, -1, -1) - value_init = 0 if order == OrderEnum.Ascending else percent_setting_max_value + 1) if step_direction == step_direction_enum.kIncrease else range(fan_mode_high.value - 1, -1, -1) + value_init_verify = 0 if step_direction == step_direction_enum.kIncrease else percent_setting_max_value + value_init_update = fan_mode_off if step_direction == step_direction_enum.kIncrease else fan_mode_high - # Logging what's being tested + # Logging scenario being tested sub_text = " and SpeedSetting" if self.supports_speed else "" logging.info( - f"[FC] Updating {attr_to_update.__name__} {order.name}, verifying {attr_to_verify.__name__}{sub_text}") + f"[FC] Updating {attr_to_update.__name__} {step_direction.name}, verifying {attr_to_verify.__name__}{sub_text}") + + # Initializatization of the attribute to update (Write and read back verification) + await self.write_and_verify_attribute(endpoint, attr_to_update, value_init_update) - # Write and read back the initialization value for the attribute to verify - # Verify that the write status is either SUCCESS or INVALID_IN_STATE - # Verify written and read value match - attr_value_read = await self.write_and_verify_attribute(endpoint, attr_to_verify, value_init) + # Initializatization of the attribute to verify (Write and read back verification) + attr_value_read = await self.write_and_verify_attribute(endpoint, attr_to_verify, value_init_verify) - # Write and read back the initialization value for SpeedSetting (if supported) - # Verify that the write status is either SUCCESS or INVALID_IN_STATE - # Verify that the written and read value match + # Initializatization of the SpeedSetting attribute, if supported (Write and read back verification) if self.supports_speed: speed_max = await self.read_setting(endpoint, speed_max_attr) - speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max + speed_setting_init = 0 if step_direction == step_direction_enum.kIncrease else speed_max speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) - # Logging FanMode, PercentSetting, and SpeedSetting (if supported) initialization values + # *** NEXT STEP *** + # TH performs testing based on the scenario setup from the previous step + self.step(self.current_step_index + 1) + + # Logging initialization values (FanMode, PercentSetting, and SpeedSetting (if supported)) fan_mode_log = cluster.Enums.FanModeEnum(await self.read_setting(endpoint, fan_mode_attr)) percent_setting_log = await self.read_setting(endpoint, percent_setting_attr) sub_text = f" SpeedSetting({speed_setting_read})" if self.supports_speed else "" logging.info( f"[FC] Initialization values: FanMode({fan_mode_log}:{fan_mode_log.name}) PercentSetting({percent_setting_log}){sub_text}") - # *** NEXT STEP *** - # TH performs testing based on the scenario setup from the previous step - self.step(self.current_step_index + 1) - # Write to attribute iteratively within the configured range attr_value_current = attr_value_read attr_value_previous = attr_value_read @@ -220,14 +218,14 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or # Verify attribute value if attr_value_current is not None: - self.verify_attribute_transition(attr_to_verify, attr_value_current, attr_value_previous, order) + self.verify_attribute_transition(attr_to_verify, attr_value_current, attr_value_previous, step_direction) attr_value_previous = attr_value_current # Verify the same for the SpeedSetting attribute value (if supported) if self.supports_speed: speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) if speed_setting_current is not None: - self.verify_attribute_transition(speed_setting_attr, speed_setting_current, speed_setting_previous, order) + self.verify_attribute_transition(speed_setting_attr, speed_setting_current, speed_setting_previous, step_direction) speed_setting_previous = speed_setting_current # If the write status is INVALID_IN_STATE, it means no write operation occurred @@ -254,6 +252,8 @@ async def test_TC_FAN_3_1(self): # Setup endpoint = self.get_endpoint(default=1) cluster = Clusters.FanControl + attributes = cluster.Attributes + step_direction_enum = cluster.Enums.StepDirectionEnum # *** STEP 1 *** # Commissioning already done @@ -262,7 +262,7 @@ async def test_TC_FAN_3_1(self): # *** STEP 2 *** # TH checks for the existence of the SpeedSetting feature self.step(2) - feature_map = await self.read_setting(endpoint, cluster.Attributes.FeatureMap) + feature_map = await self.read_setting(endpoint, attributes.FeatureMap) self.supports_speed = bool(feature_map & cluster.Bitmaps.Feature.kMultiSpeed) # *** STEP 3 *** @@ -275,25 +275,25 @@ async def test_TC_FAN_3_1(self): # a range of 1 to 100 one at a time in ascending order # Verifies that FanMode and SpeedSetting values (if supported) are being # updated accordingly (current values greater than previous values) - await self.verify_fan_control_attribute_values(endpoint, cluster.Attributes.PercentSetting, OrderEnum.Ascending) + await self.verify_fan_control_attribute_values(endpoint, attributes.PercentSetting, step_direction_enum.kIncrease) # TH writes to the DUT the PercentSetting attribute iteratively within - # a range of 100 to 0 one at a time in descending order + # a range of 99 to 0 one at a time in descending order # Verifies that FanMode and SpeedSetting values (if supported) are being # updated accordingly (current values less than previous values) - await self.verify_fan_control_attribute_values(endpoint, cluster.Attributes.PercentSetting, OrderEnum.Descending) + await self.verify_fan_control_attribute_values(endpoint, attributes.PercentSetting, step_direction_enum.kDecrease) # TH writes to the DUT the FanMode attribute iteratively within a range of - # the number of available fan modes one at a time in ascending order + # 0 (Off) to the number of available fan modes one at a time in ascending order # Verifies that PercentSetting and SpeedSetting values (if supported) are being # updated accordingly (current values greater than previous values) - await self.verify_fan_control_attribute_values(endpoint, cluster.Attributes.FanMode, OrderEnum.Ascending) + await self.verify_fan_control_attribute_values(endpoint, attributes.FanMode, step_direction_enum.kIncrease) # TH writes to the DUT the FanMode attribute iteratively within a range of # the number of available fan modes one at a time in descending order # Verifies that PercentSetting and SpeedSetting values (if supported) are being # updated accordingly (current values less than previous values) - await self.verify_fan_control_attribute_values(endpoint, cluster.Attributes.FanMode, OrderEnum.Descending) + await self.verify_fan_control_attribute_values(endpoint, attributes.FanMode, step_direction_enum.kDecrease) if __name__ == "__main__": From 47429e090c11d60bc256b3a4ab6a5541261d489b Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Sun, 16 Feb 2025 22:38:14 -0800 Subject: [PATCH 34/53] Incorporates FanModeSequence --- src/python_testing/TC_FAN_3_1.py | 166 +++++++++++++++++++++---------- 1 file changed, 115 insertions(+), 51 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 4edbea44756662..c267f3a60bdc43 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -49,6 +49,11 @@ from mobly import asserts +class OrderEnum(Enum): + Ascending = 1 + Descending = 2 + + logger = logging.getLogger(__name__) @@ -87,7 +92,19 @@ async def get_attribute_value_from_queue(queue, attribute, timeout_sec: float) - return None @staticmethod - def get_enum(value): + def get_enum_value(value) -> int: + """ + Retrieves the numeric value of an Enum instance. + + - If the input is an Enum instance, it returns its corresponding numeric value. + - If the input is already a numeric value, it returns it unchanged. + + Args: + value (enum.Enum or int): The input value to be processed. + + Returns: + int: The numeric value of the Enum instance or the original integer. + """ if isinstance(value, enum.Enum): enum_type = type(value) value = enum_type(value) @@ -98,7 +115,7 @@ async def read_setting(self, endpoint, attribute): return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) async def write_setting(self, endpoint, attribute, value) -> Status: - value = self.get_enum(value) + value = self.get_enum_value(value) result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attribute(value))]) write_status = result[0].Status write_status_success = (write_status == Status.Success) or (write_status == Status.InvalidInState) @@ -113,9 +130,65 @@ async def write_and_verify_attribute(self, endpoint, attribute, value) -> Any: f"[FC] Mismatch between written and read attribute value ({attribute.__name__})") return value_read - def verify_attribute_transition(self, attr_to_verify, value_current, value_previous, step_direction) -> None: - step_direction_enum = Clusters.FanControl.Enums.StepDirectionEnum - if step_direction == step_direction_enum.kIncrease: + async def get_fan_modes(self, endpoint, exclude_auto: bool = False) -> list[int]: + # Read FanModeSequence attribute value + fan_mode_sequence_attr = Clusters.FanControl.Attributes.FanModeSequence + fm_en = Clusters.FanControl.Enums.FanModeEnum + fan_mode_sequence = await self.read_setting(endpoint, fan_mode_sequence_attr) + + # There are only 6 valid FanModeSequence values (0, 1, 2, 3, 4, 5) + # Verifying read FanModeSequence attribute value is valid + asserts.assert_in(fan_mode_sequence, range(0, 5), f"Unsupported FanModeSequence: {fan_mode_sequence}") + + fan_mode_sequence_enum_value = self.get_enum_value(fan_mode_sequence) + logging.info(f"[FC] Supported fan modes: {fan_mode_sequence_enum_value.name}") + + fan_modes = None + if fan_mode_sequence == 0: + fan_modes = [fm_en.kOff.value, fm_en.kLow.value, fm_en.kMedium.value, fm_en.kHigh.value] + elif fan_mode_sequence == 1: + fan_modes = [fm_en.kOff.value, fm_en.kLow.value, fm_en.kHigh.value] + elif fan_mode_sequence == 2: + fan_modes = [fm_en.kOff.value, fm_en.kLow.value, fm_en.kMedium.value, fm_en.kHigh.value, fm_en.kAuto.value] + elif fan_mode_sequence == 3: + fan_modes = [fm_en.kOff.value, fm_en.kLow.value, fm_en.kHigh.value, fm_en.kAuto.value] + elif fan_mode_sequence == 4: + fan_modes = [fm_en.kOff.value, fm_en.kHigh.value, fm_en.kAuto.value] + elif fan_mode_sequence == 5: + fan_modes = [fm_en.kOff.value, fm_en.kHigh.value] + + if exclude_auto and fm_en.kAuto.value in fan_modes: + fan_modes.remove(fm_en.kAuto.value) + + return fan_modes + + async def get_initialization_parametes(self, endpoint, attr_to_update, order): + cluster = Clusters.FanControl + fan_mode_attr = cluster.Attributes.FanMode + percent_setting_attr = cluster.Attributes.PercentSetting + fan_mode_off = cluster.Enums.FanModeEnum.kOff + fan_mode_high = cluster.Enums.FanModeEnum.kHigh + percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec + + # Sets the number of iterations for attribute updates + # Sets the update order (ascending or descending) + if attr_to_update == percent_setting_attr: + attr_to_verify = fan_mode_attr + iteration_range = range(1, percent_setting_max_value + + 1) if order == OrderEnum.Ascending else range(percent_setting_max_value - 1, -1, -1) + value_init_verify = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high + value_init_update = 0 if order == OrderEnum.Ascending else percent_setting_max_value + elif attr_to_update == fan_mode_attr: + attr_to_verify = percent_setting_attr + fan_modes = await self.get_fan_modes(endpoint, exclude_auto=True) + iteration_range = range(1, len(fan_modes)) if order == OrderEnum.Ascending else range(len(fan_modes) - 2, -1, -1) + value_init_verify = 0 if order == OrderEnum.Ascending else percent_setting_max_value + value_init_update = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high + + return attr_to_verify, value_init_verify, value_init_update, iteration_range + + def verify_attribute_progression(self, attr_to_verify, value_current, value_previous, order) -> None: + if order == OrderEnum.Ascending: # Verify that the current attribute value is greater than the previous attribute value asserts.assert_greater(value_current, value_previous, f"[FC] Current {attr_to_verify.__name__} must be greater than previous {attr_to_verify.__name__}") @@ -130,17 +203,28 @@ def verify_attribute_transition(self, attr_to_verify, value_current, value_previ sub_text = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" logging.info(f"\t\t[FC] {attr_to_verify.__name__} changed from {sub_text}") - async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, step_direction) -> None: - # Setup + async def log_scenario(self, endpoint, attr_to_update, attr_to_verify, speed_setting_read, order): cluster = Clusters.FanControl - step_direction_enum = cluster.Enums.StepDirectionEnum fan_mode_attr = cluster.Attributes.FanMode percent_setting_attr = cluster.Attributes.PercentSetting + + # Logging the scenario being tested + sub_text = " and SpeedSetting" if self.supports_speed else "" + logging.info( + f"[FC] Update {attr_to_update.__name__} {order.name}, verify {attr_to_verify.__name__}{sub_text}") + + # Logging initialization values (FanMode, PercentSetting, and SpeedSetting (if supported)) + fan_mode_log = cluster.Enums.FanModeEnum(await self.read_setting(endpoint, fan_mode_attr)) + percent_setting_log = await self.read_setting(endpoint, percent_setting_attr) + sub_text = f" SpeedSetting({speed_setting_read})" if self.supports_speed else "" + logging.info( + f"[FC] Initialization values: FanMode({fan_mode_log}:{fan_mode_log.name}) PercentSetting({percent_setting_log}){sub_text}") + + async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, order) -> None: + # Setup + cluster = Clusters.FanControl speed_setting_attr = cluster.Attributes.SpeedSetting speed_max_attr = cluster.Attributes.SpeedMax - fan_mode_off = cluster.Enums.FanModeEnum.kOff - fan_mode_high = cluster.Enums.FanModeEnum.kHigh - percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec timeout_sec = 0.1 # Timeout given for item retreival from the attribute queue # *** NEXT STEP *** @@ -151,26 +235,11 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, st # - Updating the FanMode attribute in descending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes self.step(self.current_step_index + 1) - # Sets the 'for loop' range for attribute updates based on the attribute that - # will be updated and the order (ascending or descending) - # Sets which mandatory attribute is to be verified (FanMode or PercentSetting) - if attr_to_update == percent_setting_attr: - attr_to_verify = fan_mode_attr - range_loop = range(1, percent_setting_max_value + - 1) if step_direction == step_direction_enum.kIncrease else range(percent_setting_max_value - 1, -1, -1) - value_init_verify = fan_mode_off if step_direction == step_direction_enum.kIncrease else fan_mode_high - value_init_update = 0 if step_direction == step_direction_enum.kIncrease else percent_setting_max_value - elif attr_to_update == fan_mode_attr: - attr_to_verify = percent_setting_attr - range_loop = range(1, fan_mode_high.value + - 1) if step_direction == step_direction_enum.kIncrease else range(fan_mode_high.value - 1, -1, -1) - value_init_verify = 0 if step_direction == step_direction_enum.kIncrease else percent_setting_max_value - value_init_update = fan_mode_off if step_direction == step_direction_enum.kIncrease else fan_mode_high - - # Logging scenario being tested - sub_text = " and SpeedSetting" if self.supports_speed else "" - logging.info( - f"[FC] Updating {attr_to_update.__name__} {step_direction.name}, verifying {attr_to_verify.__name__}{sub_text}") + # Get initialization parameters + attr_to_verify, value_init_verify, value_init_update, iteration_range = \ + await self.get_initialization_parametes( + endpoint, attr_to_update, order + ) # Initializatization of the attribute to update (Write and read back verification) await self.write_and_verify_attribute(endpoint, attr_to_update, value_init_update) @@ -181,26 +250,22 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, st # Initializatization of the SpeedSetting attribute, if supported (Write and read back verification) if self.supports_speed: speed_max = await self.read_setting(endpoint, speed_max_attr) - speed_setting_init = 0 if step_direction == step_direction_enum.kIncrease else speed_max + speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) # *** NEXT STEP *** - # TH performs testing based on the scenario setup from the previous step + # TH performs the testing scenario defined in the previous step self.step(self.current_step_index + 1) - # Logging initialization values (FanMode, PercentSetting, and SpeedSetting (if supported)) - fan_mode_log = cluster.Enums.FanModeEnum(await self.read_setting(endpoint, fan_mode_attr)) - percent_setting_log = await self.read_setting(endpoint, percent_setting_attr) - sub_text = f" SpeedSetting({speed_setting_read})" if self.supports_speed else "" - logging.info( - f"[FC] Initialization values: FanMode({fan_mode_log}:{fan_mode_log.name}) PercentSetting({percent_setting_log}){sub_text}") + # Logging the scenario being tested + await self.log_scenario(endpoint, attr_to_update, attr_to_verify, speed_setting_read, order) - # Write to attribute iteratively within the configured range + # Write to attribute iteratively attr_value_current = attr_value_read attr_value_previous = attr_value_read speed_setting_current = speed_setting_read speed_setting_previous = speed_setting_read - for value_to_write in range_loop: + for value_to_write in iteration_range: # Clear the queue before each update to avoid attribute report duplicates self.attribute_subscription.get_last_report() @@ -216,16 +281,16 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, st queue = self.attribute_subscription.attribute_queue.queue attr_value_current = await self.get_attribute_value_from_queue(queue, attr_to_verify, timeout_sec) - # Verify attribute value + # Verify attribute value progression if attr_value_current is not None: - self.verify_attribute_transition(attr_to_verify, attr_value_current, attr_value_previous, step_direction) + self.verify_attribute_progression(attr_to_verify, attr_value_current, attr_value_previous, order) attr_value_previous = attr_value_current # Verify the same for the SpeedSetting attribute value (if supported) if self.supports_speed: speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) if speed_setting_current is not None: - self.verify_attribute_transition(speed_setting_attr, speed_setting_current, speed_setting_previous, step_direction) + self.verify_attribute_progression(speed_setting_attr, speed_setting_current, speed_setting_previous, order) speed_setting_previous = speed_setting_current # If the write status is INVALID_IN_STATE, it means no write operation occurred @@ -236,13 +301,13 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, st # Verify attribute value asserts.assert_equal(attr_value_current, attr_value_previous, - f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to its previous value") + f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the previous one") # Verify the same for the SpeedSetting attribute value (if supported) if self.supports_speed: speed_setting_current = await self.read_setting(endpoint, speed_setting_attr) asserts.assert_equal(speed_setting_current, speed_setting_previous, - "Current SpeedSetting attribute value must be equal to its previous value") + "Current SpeedSetting attribute value must be equal to the previous one") def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] @@ -253,7 +318,6 @@ async def test_TC_FAN_3_1(self): endpoint = self.get_endpoint(default=1) cluster = Clusters.FanControl attributes = cluster.Attributes - step_direction_enum = cluster.Enums.StepDirectionEnum # *** STEP 1 *** # Commissioning already done @@ -275,25 +339,25 @@ async def test_TC_FAN_3_1(self): # a range of 1 to 100 one at a time in ascending order # Verifies that FanMode and SpeedSetting values (if supported) are being # updated accordingly (current values greater than previous values) - await self.verify_fan_control_attribute_values(endpoint, attributes.PercentSetting, step_direction_enum.kIncrease) + await self.verify_fan_control_attribute_values(endpoint, attributes.PercentSetting, OrderEnum.Ascending) # TH writes to the DUT the PercentSetting attribute iteratively within # a range of 99 to 0 one at a time in descending order # Verifies that FanMode and SpeedSetting values (if supported) are being # updated accordingly (current values less than previous values) - await self.verify_fan_control_attribute_values(endpoint, attributes.PercentSetting, step_direction_enum.kDecrease) + await self.verify_fan_control_attribute_values(endpoint, attributes.PercentSetting, OrderEnum.Descending) # TH writes to the DUT the FanMode attribute iteratively within a range of # 0 (Off) to the number of available fan modes one at a time in ascending order # Verifies that PercentSetting and SpeedSetting values (if supported) are being # updated accordingly (current values greater than previous values) - await self.verify_fan_control_attribute_values(endpoint, attributes.FanMode, step_direction_enum.kIncrease) + await self.verify_fan_control_attribute_values(endpoint, attributes.FanMode, OrderEnum.Ascending) # TH writes to the DUT the FanMode attribute iteratively within a range of # the number of available fan modes one at a time in descending order # Verifies that PercentSetting and SpeedSetting values (if supported) are being # updated accordingly (current values less than previous values) - await self.verify_fan_control_attribute_values(endpoint, attributes.FanMode, step_direction_enum.kDecrease) + await self.verify_fan_control_attribute_values(endpoint, attributes.FanMode, OrderEnum.Descending) if __name__ == "__main__": From bcf08d88fc0b77bc4b9e2f97959f5243eca5a4e5 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Sun, 16 Feb 2025 22:40:33 -0800 Subject: [PATCH 35/53] Fix restyled --- src/python_testing/TC_FAN_3_1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index c267f3a60bdc43..8fd9de5d693f24 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -52,7 +52,7 @@ class OrderEnum(Enum): Ascending = 1 Descending = 2 - + logger = logging.getLogger(__name__) @@ -175,7 +175,7 @@ async def get_initialization_parametes(self, endpoint, attr_to_update, order): if attr_to_update == percent_setting_attr: attr_to_verify = fan_mode_attr iteration_range = range(1, percent_setting_max_value + - 1) if order == OrderEnum.Ascending else range(percent_setting_max_value - 1, -1, -1) + 1) if order == OrderEnum.Ascending else range(percent_setting_max_value - 1, -1, -1) value_init_verify = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high value_init_update = 0 if order == OrderEnum.Ascending else percent_setting_max_value elif attr_to_update == fan_mode_attr: From 5c5ca26d4976425e5fef081645187968633b4550 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Sun, 16 Feb 2025 22:54:01 -0800 Subject: [PATCH 36/53] cosmetics --- src/python_testing/TC_FAN_3_1.py | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 8fd9de5d693f24..bd6ebb3673415c 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -266,7 +266,7 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or speed_setting_current = speed_setting_read speed_setting_previous = speed_setting_read for value_to_write in iteration_range: - # Clear the queue before each update to avoid attribute report duplicates + # Clear the attribute report queue before each update to avoid duplicates self.attribute_subscription.get_last_report() # Write to attribute @@ -277,16 +277,14 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or # Verify that the current attribute value is greater than the previous # one if order was set to Ascending, or less if order was set to Descending if write_status == Status.Success: - # Get current attribute value + # Get current attribute value and verify progression queue = self.attribute_subscription.attribute_queue.queue attr_value_current = await self.get_attribute_value_from_queue(queue, attr_to_verify, timeout_sec) - - # Verify attribute value progression if attr_value_current is not None: self.verify_attribute_progression(attr_to_verify, attr_value_current, attr_value_previous, order) attr_value_previous = attr_value_current - # Verify the same for the SpeedSetting attribute value (if supported) + # Get current SpeedSetting attribute value and verify progression (if supported) if self.supports_speed: speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) if speed_setting_current is not None: @@ -296,14 +294,12 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or # If the write status is INVALID_IN_STATE, it means no write operation occurred # Verify that the current attribute value is equal to the previous one elif write_status == Status.InvalidInState: - # Get current attribute value + # Get current attribute value and verify it's equal to the previous value attr_value_current = await self.read_setting(endpoint, attr_to_verify) - - # Verify attribute value asserts.assert_equal(attr_value_current, attr_value_previous, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the previous one") - # Verify the same for the SpeedSetting attribute value (if supported) + # Get current SpeedSetting attribute value and verify it's equal to the previous value (if supported) if self.supports_speed: speed_setting_current = await self.read_setting(endpoint, speed_setting_attr) asserts.assert_equal(speed_setting_current, speed_setting_previous, From 5baa83a5b4cfd31eeea1c158c2e0c9340fcd11be Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 17 Feb 2025 11:28:58 -0800 Subject: [PATCH 37/53] Updates test steps, refactors dut reads that only need to be read once --- src/python_testing/TC_FAN_3_1.py | 117 ++++++++++++++++--------------- 1 file changed, 61 insertions(+), 56 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index bd6ebb3673415c..0a091a4b6fe39c 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -60,24 +60,26 @@ class OrderEnum(Enum): class TC_FAN_3_1(MatterBaseTest): def steps_TC_FAN_3_1(self): return [TestStep(1, "[FC] Commissioning already done.", is_commissioning=True), - TestStep(2, "[FC] TH checks the DUT for the presence of the SpeedSetting feature.", - "Save the result for future use."), - TestStep(3, "[FC] TH subscribes to the DUT's FanControl cluster.", "Enables the TH to receive attribute updates."), - TestStep(4, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in ascending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", + TestStep(2, "[FC] TH reads the FanModeSequence attribute to retreive the available fan modes.", + "Save the result for future reference."), + TestStep(3, "[FC] TH reads the FeatureMap attribute from the DUT.", + "Check for the existence of the SpeedSetting feature. If the SpeedSetting feature is supported, read the SpeedMax attribute and save the result for future reference."), + TestStep(4, "[FC] TH subscribes to the DUT's FanControl cluster.", "Enables the TH to receive attribute updates."), + TestStep(5, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in ascending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", "Initialize attribute values: - FanMode: 0 (Off). - PercentSetting: 0. - SpeedSetting (if supported): 0. Read the attribute values back and verify that they match the written values."), - TestStep(5, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in ascending order (from 1 to 100). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", + TestStep(6, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in ascending order (from 1 to 100). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), - TestStep(6, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in descending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", + TestStep(7, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in descending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", "Initialize attribute values: - FanMode: 3 (High). - PercentSetting: 100. - SpeedSetting (if supported): SpeedMax attribute value. Read the attribute values back and verify that they match the written values."), - TestStep(7, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in descending order (from 99 to 0). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", + TestStep(8, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in descending order (from 99 to 0). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), - TestStep(8, "[FC] TH configures the testing setup for the following scenario: Updating the FanMode attribute in ascending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes.", + TestStep(9, "[FC] TH updates the DUT's FanMode attribute value iteratively, in accordance with the available fan modes provided by the FanModeSequence attribute, in ascending order from the fan mode subsequent to 0 (Off) to 3 (High). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", "Initialize attribute values: - FanMode: 0 (Off). - PercentSetting: 0. - SpeedSetting (if supported): 0. Read the attribute values back and verify that they match the written values."), - TestStep(9, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in ascending order (from 0 (Off) to 3 (High)). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", + TestStep(10, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in ascending order (from 0 (Off) to 3 (High)). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), - TestStep(10, "[FC] TH configures the testing setup for the following scenario: Updating the FanMode attribute in descending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes.", + TestStep(11, "[FC] TH updates the DUT's FanMode attribute value iteratively, in accordance with the available fan modes provided by the FanModeSequence attribute, in descending order from the fan mode prior to 3 (High) to 0 (Off). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", "Initialize attribute values: - FanMode: 3 (High). - PercentSetting: 100. - SpeedSetting (if supported): SpeedMax attribute value. Read the attribute values back and verify that they match the written values."), - TestStep(11, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in descending order (from 3 (High) to 0 (Off)) For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", + TestStep(12, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in descending order (from 3 (High) to 0 (Off)) For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), ] @@ -162,7 +164,7 @@ async def get_fan_modes(self, endpoint, exclude_auto: bool = False) -> list[int] return fan_modes - async def get_initialization_parametes(self, endpoint, attr_to_update, order): + async def get_initialization_parametes(self, attr_to_update, order): cluster = Clusters.FanControl fan_mode_attr = cluster.Attributes.FanMode percent_setting_attr = cluster.Attributes.PercentSetting @@ -180,14 +182,13 @@ async def get_initialization_parametes(self, endpoint, attr_to_update, order): value_init_update = 0 if order == OrderEnum.Ascending else percent_setting_max_value elif attr_to_update == fan_mode_attr: attr_to_verify = percent_setting_attr - fan_modes = await self.get_fan_modes(endpoint, exclude_auto=True) - iteration_range = range(1, len(fan_modes)) if order == OrderEnum.Ascending else range(len(fan_modes) - 2, -1, -1) + iteration_range = range(1, len(self.fan_modes)) if order == OrderEnum.Ascending else range(len(self.fan_modes) - 2, -1, -1) value_init_verify = 0 if order == OrderEnum.Ascending else percent_setting_max_value value_init_update = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high return attr_to_verify, value_init_verify, value_init_update, iteration_range - def verify_attribute_progression(self, attr_to_verify, value_current, value_previous, order) -> None: + def verify_attribute_change(self, attr_to_verify, value_current, value_previous, order) -> None: if order == OrderEnum.Ascending: # Verify that the current attribute value is greater than the previous attribute value asserts.assert_greater(value_current, value_previous, @@ -220,7 +221,7 @@ async def log_scenario(self, endpoint, attr_to_update, attr_to_verify, speed_set logging.info( f"[FC] Initialization values: FanMode({fan_mode_log}:{fan_mode_log.name}) PercentSetting({percent_setting_log}){sub_text}") - async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, order) -> None: + async def verify_fan_control_attribute_progression(self, endpoint, attr_to_update, order) -> None: # Setup cluster = Clusters.FanControl speed_setting_attr = cluster.Attributes.SpeedSetting @@ -237,9 +238,7 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or # Get initialization parameters attr_to_verify, value_init_verify, value_init_update, iteration_range = \ - await self.get_initialization_parametes( - endpoint, attr_to_update, order - ) + await self.get_initialization_parametes(attr_to_update, order) # Initializatization of the attribute to update (Write and read back verification) await self.write_and_verify_attribute(endpoint, attr_to_update, value_init_update) @@ -249,8 +248,7 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or # Initializatization of the SpeedSetting attribute, if supported (Write and read back verification) if self.supports_speed: - speed_max = await self.read_setting(endpoint, speed_max_attr) - speed_setting_init = 0 if order == OrderEnum.Ascending else speed_max + speed_setting_init = 0 if order == OrderEnum.Ascending else self.speed_max speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) # *** NEXT STEP *** @@ -277,18 +275,18 @@ async def verify_fan_control_attribute_values(self, endpoint, attr_to_update, or # Verify that the current attribute value is greater than the previous # one if order was set to Ascending, or less if order was set to Descending if write_status == Status.Success: - # Get current attribute value and verify progression + # Get current attribute value and verify correct increment/decrement queue = self.attribute_subscription.attribute_queue.queue attr_value_current = await self.get_attribute_value_from_queue(queue, attr_to_verify, timeout_sec) if attr_value_current is not None: - self.verify_attribute_progression(attr_to_verify, attr_value_current, attr_value_previous, order) + self.verify_attribute_change(attr_to_verify, attr_value_current, attr_value_previous, order) attr_value_previous = attr_value_current - # Get current SpeedSetting attribute value and verify progression (if supported) + # Get current SpeedSetting attribute value and verify correct increment/decrement (if supported) if self.supports_speed: speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) if speed_setting_current is not None: - self.verify_attribute_progression(speed_setting_attr, speed_setting_current, speed_setting_previous, order) + self.verify_attribute_change(speed_setting_attr, speed_setting_current, speed_setting_previous, order) speed_setting_previous = speed_setting_current # If the write status is INVALID_IN_STATE, it means no write operation occurred @@ -311,49 +309,56 @@ def pics_TC_FAN_3_1(self) -> list[str]: @async_test_body async def test_TC_FAN_3_1(self): # Setup - endpoint = self.get_endpoint(default=1) + ep = self.get_endpoint(default=1) cluster = Clusters.FanControl attributes = cluster.Attributes - + # *** STEP 1 *** # Commissioning already done self.step(1) # *** STEP 2 *** - # TH checks for the existence of the SpeedSetting feature + # TH reads the FanModeSequence attribute to retreive the available fan modes self.step(2) - feature_map = await self.read_setting(endpoint, attributes.FeatureMap) - self.supports_speed = bool(feature_map & cluster.Bitmaps.Feature.kMultiSpeed) + self.fan_modes = await self.get_fan_modes(ep, exclude_auto=True) # *** STEP 3 *** - # TH subscribes to the DUT's FanControl cluster + # TH reads the FeatureMap attribute to check for support of the SpeedSetting feature self.step(3) + feature_map = await self.read_setting(ep, attributes.FeatureMap) + self.supports_speed = bool(feature_map & cluster.Bitmaps.Feature.kMultiSpeed) + if self.supports_speed: + self.speed_max = await self.read_setting(ep, attributes.SpeedMax) + + # *** STEP 4 *** + # TH subscribes to the DUT's FanControl cluster + self.step(4) self.attribute_subscription = ClusterAttributeChangeAccumulator(cluster) - await self.attribute_subscription.start(self.default_controller, self.dut_node_id, endpoint) - - # TH writes to the DUT the PercentSetting attribute iteratively within - # a range of 1 to 100 one at a time in ascending order - # Verifies that FanMode and SpeedSetting values (if supported) are being - # updated accordingly (current values greater than previous values) - await self.verify_fan_control_attribute_values(endpoint, attributes.PercentSetting, OrderEnum.Ascending) - - # TH writes to the DUT the PercentSetting attribute iteratively within - # a range of 99 to 0 one at a time in descending order - # Verifies that FanMode and SpeedSetting values (if supported) are being - # updated accordingly (current values less than previous values) - await self.verify_fan_control_attribute_values(endpoint, attributes.PercentSetting, OrderEnum.Descending) - - # TH writes to the DUT the FanMode attribute iteratively within a range of - # 0 (Off) to the number of available fan modes one at a time in ascending order - # Verifies that PercentSetting and SpeedSetting values (if supported) are being - # updated accordingly (current values greater than previous values) - await self.verify_fan_control_attribute_values(endpoint, attributes.FanMode, OrderEnum.Ascending) - - # TH writes to the DUT the FanMode attribute iteratively within a range of - # the number of available fan modes one at a time in descending order - # Verifies that PercentSetting and SpeedSetting values (if supported) are being - # updated accordingly (current values less than previous values) - await self.verify_fan_control_attribute_values(endpoint, attributes.FanMode, OrderEnum.Descending) + await self.attribute_subscription.start(self.default_controller, self.dut_node_id, ep) + + # TH updates the DUT's PercentSetting attribute value iteratively, in ascending order (from 1 to 100) + # and monitors the FanMode (and SpeedSetting, if supported) attribute value's correct progression + # (current value greater than previous value) + await self.verify_fan_control_attribute_progression(ep, attributes.PercentSetting, OrderEnum.Ascending) + + # TH updates the DUT's PercentSetting attribute value iteratively, in descending order (from 99 to 0) + # and monitors the FanMode (and SpeedSetting, if supported) attribute value's correct progression + # (current value less than previous value) + await self.verify_fan_control_attribute_progression(ep, attributes.PercentSetting, OrderEnum.Descending) + + # TH updates the DUT's FanMode attribute value iteratively, in accordance with the + # available fan modes provided by the FanModeSequence attribute, in ascending order + # from the fan mode subsequent to 0 (Off) to 3 (High) and monitors the PercentSetting + # (and SpeedSetting, if supported) attribute value's correct progression + # (current value greater than previous value) + await self.verify_fan_control_attribute_progression(ep, attributes.FanMode, OrderEnum.Ascending) + + # TH updates the DUT's FanMode attribute value iteratively, in accordance with the + # available fan modes provided by the FanModeSequence attribute, in descending order + # from the fan mode prior to 3 (High) to 0 (Off) and monitors the PercentSetting + # (and SpeedSetting, if supported) attribute value's correct progression + # (current value less than previous value) + await self.verify_fan_control_attribute_progression(ep, attributes.FanMode, OrderEnum.Descending) if __name__ == "__main__": From e000d0fa96847d0011d3fc89bf6a3a2795504da2 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 17 Feb 2025 11:31:34 -0800 Subject: [PATCH 38/53] fix restyle --- src/python_testing/TC_FAN_3_1.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 0a091a4b6fe39c..61701fd9a0ebda 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -182,7 +182,8 @@ async def get_initialization_parametes(self, attr_to_update, order): value_init_update = 0 if order == OrderEnum.Ascending else percent_setting_max_value elif attr_to_update == fan_mode_attr: attr_to_verify = percent_setting_attr - iteration_range = range(1, len(self.fan_modes)) if order == OrderEnum.Ascending else range(len(self.fan_modes) - 2, -1, -1) + iteration_range = range(1, len(self.fan_modes)) if order == OrderEnum.Ascending else range( + len(self.fan_modes) - 2, -1, -1) value_init_verify = 0 if order == OrderEnum.Ascending else percent_setting_max_value value_init_update = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high @@ -312,7 +313,7 @@ async def test_TC_FAN_3_1(self): ep = self.get_endpoint(default=1) cluster = Clusters.FanControl attributes = cluster.Attributes - + # *** STEP 1 *** # Commissioning already done self.step(1) From 1cf7cd8f56abf439618113ce991fc709c1109bd5 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 17 Feb 2025 17:49:50 -0800 Subject: [PATCH 39/53] fix lint, comments --- src/python_testing/TC_FAN_3_1.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 61701fd9a0ebda..2557397be172b3 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -224,9 +224,7 @@ async def log_scenario(self, endpoint, attr_to_update, attr_to_verify, speed_set async def verify_fan_control_attribute_progression(self, endpoint, attr_to_update, order) -> None: # Setup - cluster = Clusters.FanControl - speed_setting_attr = cluster.Attributes.SpeedSetting - speed_max_attr = cluster.Attributes.SpeedMax + speed_setting_attr = Clusters.FanControl.Attributes.SpeedSetting timeout_sec = 0.1 # Timeout given for item retreival from the attribute queue # *** NEXT STEP *** From 3485ae03ed8f221924423d72985deece52820b82 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 17 Feb 2025 17:55:41 -0800 Subject: [PATCH 40/53] Adds test description --- src/python_testing/TC_FAN_3_1.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 2557397be172b3..7039d2e93d8b7c 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -58,6 +58,9 @@ class OrderEnum(Enum): class TC_FAN_3_1(MatterBaseTest): + def desc_TC_FAN_3_3(self) -> str: + return "[TC-FAN-3.1] Mandatory functionality with DUT as Server" + def steps_TC_FAN_3_1(self): return [TestStep(1, "[FC] Commissioning already done.", is_commissioning=True), TestStep(2, "[FC] TH reads the FanModeSequence attribute to retreive the available fan modes.", @@ -229,10 +232,10 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat # *** NEXT STEP *** # TH configures the testing setup for the one of the following scenarios: - # - Updating the PercentSetting attribute in ascending order and monitoring the FanMode (and SpeedSetting, if supported) attributes - # - Updating the PercentSetting attribute in descending order and monitoring the FanMode (and SpeedSetting, if supported) attributes - # - Updating the FanMode attribute in aescending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes - # - Updating the FanMode attribute in descending order and monitoring the PercentSetting (and SpeedSetting, if supported) attributes + # - Update the PercentSetting attribute in ascending order and monitor the FanMode (and SpeedSetting, if supported) attributes + # - Update the PercentSetting attribute in descending order and monitor the FanMode (and SpeedSetting, if supported) attributes + # - Update the FanMode attribute in aescending order and monitor the PercentSetting (and SpeedSetting, if supported) attributes + # - Update the FanMode attribute in descending order and monitor the PercentSetting (and SpeedSetting, if supported) attributes self.step(self.current_step_index + 1) # Get initialization parameters From 5e4c9b6dfa1809355c5f82d786c5a65da7ecd70a Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 17 Feb 2025 17:57:51 -0800 Subject: [PATCH 41/53] fix test description --- src/python_testing/TC_FAN_3_1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 7039d2e93d8b7c..ef26d305b848e5 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -58,7 +58,7 @@ class OrderEnum(Enum): class TC_FAN_3_1(MatterBaseTest): - def desc_TC_FAN_3_3(self) -> str: + def desc_TC_FAN_3_1(self) -> str: return "[TC-FAN-3.1] Mandatory functionality with DUT as Server" def steps_TC_FAN_3_1(self): From e992e64d8129db88602593e12f4dda61d3cf5a64 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 17 Feb 2025 17:59:54 -0800 Subject: [PATCH 42/53] fix restyle --- src/python_testing/TC_FAN_3_1.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index ef26d305b848e5..775b6aa9c348f9 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -59,8 +59,8 @@ class OrderEnum(Enum): class TC_FAN_3_1(MatterBaseTest): def desc_TC_FAN_3_1(self) -> str: - return "[TC-FAN-3.1] Mandatory functionality with DUT as Server" - + return "[TC-FAN-3.1] Mandatory functionality with DUT as Server" + def steps_TC_FAN_3_1(self): return [TestStep(1, "[FC] Commissioning already done.", is_commissioning=True), TestStep(2, "[FC] TH reads the FanModeSequence attribute to retreive the available fan modes.", From 49906109dfb5e46ca642fcdf3ff3f2c0ddd7f567 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Wed, 19 Feb 2025 14:03:46 -0800 Subject: [PATCH 43/53] updates log_scenario, no extra attribute reads necesary --- src/python_testing/TC_FAN_3_1.py | 39 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 19 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 775b6aa9c348f9..473689ff6e6f40 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -208,22 +208,22 @@ def verify_attribute_change(self, attr_to_verify, value_current, value_previous, sub_text = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" logging.info(f"\t\t[FC] {attr_to_verify.__name__} changed from {sub_text}") - async def log_scenario(self, endpoint, attr_to_update, attr_to_verify, speed_setting_read, order): - cluster = Clusters.FanControl - fan_mode_attr = cluster.Attributes.FanMode - percent_setting_attr = cluster.Attributes.PercentSetting - + async def log_scenario(self, attr_to_update, attr_to_verify, attr_to_update_value, attr_to_verify_value, speed_setting_value, order): # Logging the scenario being tested - sub_text = " and SpeedSetting" if self.supports_speed else "" + speed_setting_scenario = " and SpeedSetting" if self.supports_speed else "" logging.info( - f"[FC] Update {attr_to_update.__name__} {order.name}, verify {attr_to_verify.__name__}{sub_text}") + f"[FC] Update {attr_to_update.__name__} {order.name}, verify {attr_to_verify.__name__}{speed_setting_scenario}") # Logging initialization values (FanMode, PercentSetting, and SpeedSetting (if supported)) - fan_mode_log = cluster.Enums.FanModeEnum(await self.read_setting(endpoint, fan_mode_attr)) - percent_setting_log = await self.read_setting(endpoint, percent_setting_attr) - sub_text = f" SpeedSetting({speed_setting_read})" if self.supports_speed else "" + if attr_to_update == Clusters.FanControl.Attributes.PercentSetting: + percent_setting_log = attr_to_update_value + fan_mode_log = f"{attr_to_verify_value}:{attr_to_verify_value.name}" + else: + percent_setting_log = attr_to_verify_value + fan_mode_log = f"{attr_to_update_value}:{attr_to_update_value.name}" + speed_setting_log = f" SpeedSetting({speed_setting_value})" if self.supports_speed else "" logging.info( - f"[FC] Initialization values: FanMode({fan_mode_log}:{fan_mode_log.name}) PercentSetting({percent_setting_log}){sub_text}") + f"[FC] Initialization values: FanMode({fan_mode_log}) PercentSetting({percent_setting_log}){speed_setting_log}") async def verify_fan_control_attribute_progression(self, endpoint, attr_to_update, order) -> None: # Setup @@ -243,28 +243,29 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat await self.get_initialization_parametes(attr_to_update, order) # Initializatization of the attribute to update (Write and read back verification) - await self.write_and_verify_attribute(endpoint, attr_to_update, value_init_update) + attr_to_update_value = await self.write_and_verify_attribute(endpoint, attr_to_update, value_init_update) # Initializatization of the attribute to verify (Write and read back verification) - attr_value_read = await self.write_and_verify_attribute(endpoint, attr_to_verify, value_init_verify) + attr_to_verify_value = await self.write_and_verify_attribute(endpoint, attr_to_verify, value_init_verify) # Initializatization of the SpeedSetting attribute, if supported (Write and read back verification) if self.supports_speed: speed_setting_init = 0 if order == OrderEnum.Ascending else self.speed_max - speed_setting_read = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) + speed_setting_value = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) # *** NEXT STEP *** # TH performs the testing scenario defined in the previous step self.step(self.current_step_index + 1) # Logging the scenario being tested - await self.log_scenario(endpoint, attr_to_update, attr_to_verify, speed_setting_read, order) + # await self.log_scenario(endpoint, attr_to_update, attr_to_verify, speed_setting_read, order) + await self.log_scenario(attr_to_update, attr_to_verify, attr_to_update_value, attr_to_verify_value, speed_setting_value, order) # Write to attribute iteratively - attr_value_current = attr_value_read - attr_value_previous = attr_value_read - speed_setting_current = speed_setting_read - speed_setting_previous = speed_setting_read + attr_value_current = attr_to_verify_value + attr_value_previous = attr_to_verify_value + speed_setting_current = speed_setting_value + speed_setting_previous = speed_setting_value for value_to_write in iteration_range: # Clear the attribute report queue before each update to avoid duplicates self.attribute_subscription.get_last_report() From 9f928918c29cec4603957a5c44dda53d663f9b9c Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Wed, 19 Feb 2025 14:14:44 -0800 Subject: [PATCH 44/53] updates use of FanModeEnum --- src/python_testing/TC_FAN_3_1.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 473689ff6e6f40..43eed702c43935 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -138,7 +138,7 @@ async def write_and_verify_attribute(self, endpoint, attribute, value) -> Any: async def get_fan_modes(self, endpoint, exclude_auto: bool = False) -> list[int]: # Read FanModeSequence attribute value fan_mode_sequence_attr = Clusters.FanControl.Attributes.FanModeSequence - fm_en = Clusters.FanControl.Enums.FanModeEnum + fm_enum = Clusters.FanControl.Enums.FanModeEnum fan_mode_sequence = await self.read_setting(endpoint, fan_mode_sequence_attr) # There are only 6 valid FanModeSequence values (0, 1, 2, 3, 4, 5) @@ -150,20 +150,20 @@ async def get_fan_modes(self, endpoint, exclude_auto: bool = False) -> list[int] fan_modes = None if fan_mode_sequence == 0: - fan_modes = [fm_en.kOff.value, fm_en.kLow.value, fm_en.kMedium.value, fm_en.kHigh.value] + fan_modes = [fm_enum.kOff, fm_enum.kLow, fm_enum.kMedium, fm_enum.kHigh] elif fan_mode_sequence == 1: - fan_modes = [fm_en.kOff.value, fm_en.kLow.value, fm_en.kHigh.value] + fan_modes = [fm_enum.kOff, fm_enum.kLow, fm_enum.kHigh] elif fan_mode_sequence == 2: - fan_modes = [fm_en.kOff.value, fm_en.kLow.value, fm_en.kMedium.value, fm_en.kHigh.value, fm_en.kAuto.value] + fan_modes = [fm_enum.kOff, fm_enum.kLow, fm_enum.kMedium, fm_enum.kHigh, fm_enum.kAuto] elif fan_mode_sequence == 3: - fan_modes = [fm_en.kOff.value, fm_en.kLow.value, fm_en.kHigh.value, fm_en.kAuto.value] + fan_modes = [fm_enum.kOff, fm_enum.kLow, fm_enum.kHigh, fm_enum.kAuto] elif fan_mode_sequence == 4: - fan_modes = [fm_en.kOff.value, fm_en.kHigh.value, fm_en.kAuto.value] + fan_modes = [fm_enum.kOff, fm_enum.kHigh, fm_enum.kAuto] elif fan_mode_sequence == 5: - fan_modes = [fm_en.kOff.value, fm_en.kHigh.value] + fan_modes = [fm_enum.kOff, fm_enum.kHigh] - if exclude_auto and fm_en.kAuto.value in fan_modes: - fan_modes.remove(fm_en.kAuto.value) + if exclude_auto and fm_enum.kAuto in fan_modes: + fan_modes.remove(fm_enum.kAuto) return fan_modes From 80cad24201c4539d98cae18da70e483756706d10 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Wed, 19 Feb 2025 14:20:21 -0800 Subject: [PATCH 45/53] Removes get_enum_value function --- src/python_testing/TC_FAN_3_1.py | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 43eed702c43935..d4403c92ed195b 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -96,31 +96,11 @@ async def get_attribute_value_from_queue(queue, attribute, timeout_sec: float) - await asyncio.sleep(0.01) return None - @staticmethod - def get_enum_value(value) -> int: - """ - Retrieves the numeric value of an Enum instance. - - - If the input is an Enum instance, it returns its corresponding numeric value. - - If the input is already a numeric value, it returns it unchanged. - - Args: - value (enum.Enum or int): The input value to be processed. - - Returns: - int: The numeric value of the Enum instance or the original integer. - """ - if isinstance(value, enum.Enum): - enum_type = type(value) - value = enum_type(value) - return value - async def read_setting(self, endpoint, attribute): cluster = Clusters.Objects.FanControl return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) async def write_setting(self, endpoint, attribute, value) -> Status: - value = self.get_enum_value(value) result = await self.default_controller.WriteAttribute(self.dut_node_id, [(endpoint, attribute(value))]) write_status = result[0].Status write_status_success = (write_status == Status.Success) or (write_status == Status.InvalidInState) @@ -145,8 +125,7 @@ async def get_fan_modes(self, endpoint, exclude_auto: bool = False) -> list[int] # Verifying read FanModeSequence attribute value is valid asserts.assert_in(fan_mode_sequence, range(0, 5), f"Unsupported FanModeSequence: {fan_mode_sequence}") - fan_mode_sequence_enum_value = self.get_enum_value(fan_mode_sequence) - logging.info(f"[FC] Supported fan modes: {fan_mode_sequence_enum_value.name}") + logging.info(f"[FC] Supported fan modes: {fan_mode_sequence.name}") fan_modes = None if fan_mode_sequence == 0: From 68bece07deabb8809140896d82bd6caf01549136 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Wed, 19 Feb 2025 14:26:06 -0800 Subject: [PATCH 46/53] cleanup commented line --- src/python_testing/TC_FAN_3_1.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index d4403c92ed195b..b6c25c867049c9 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -237,7 +237,6 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat self.step(self.current_step_index + 1) # Logging the scenario being tested - # await self.log_scenario(endpoint, attr_to_update, attr_to_verify, speed_setting_read, order) await self.log_scenario(attr_to_update, attr_to_verify, attr_to_update_value, attr_to_verify_value, speed_setting_value, order) # Write to attribute iteratively From bc3900b5bc70f6719ad656123376575cc37ef60b Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Fri, 21 Feb 2025 21:14:51 -0800 Subject: [PATCH 47/53] big refactor, omits attribute initialization, updates iteration loop ranges to inclue 0 --- src/python_testing/TC_FAN_3_1.py | 219 ++++++++++++++++++++----------- 1 file changed, 145 insertions(+), 74 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index b6c25c867049c9..ef22d894bd1b2a 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -53,7 +53,6 @@ class OrderEnum(Enum): Ascending = 1 Descending = 2 - logger = logging.getLogger(__name__) @@ -68,18 +67,22 @@ def steps_TC_FAN_3_1(self): TestStep(3, "[FC] TH reads the FeatureMap attribute from the DUT.", "Check for the existence of the SpeedSetting feature. If the SpeedSetting feature is supported, read the SpeedMax attribute and save the result for future reference."), TestStep(4, "[FC] TH subscribes to the DUT's FanControl cluster.", "Enables the TH to receive attribute updates."), + TestStep(5, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in ascending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", "Initialize attribute values: - FanMode: 0 (Off). - PercentSetting: 0. - SpeedSetting (if supported): 0. Read the attribute values back and verify that they match the written values."), TestStep(6, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in ascending order (from 1 to 100). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + TestStep(7, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in descending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", "Initialize attribute values: - FanMode: 3 (High). - PercentSetting: 100. - SpeedSetting (if supported): SpeedMax attribute value. Read the attribute values back and verify that they match the written values."), TestStep(8, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in descending order (from 99 to 0). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + TestStep(9, "[FC] TH updates the DUT's FanMode attribute value iteratively, in accordance with the available fan modes provided by the FanModeSequence attribute, in ascending order from the fan mode subsequent to 0 (Off) to 3 (High). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", "Initialize attribute values: - FanMode: 0 (Off). - PercentSetting: 0. - SpeedSetting (if supported): 0. Read the attribute values back and verify that they match the written values."), TestStep(10, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in ascending order (from 0 (Off) to 3 (High)). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + TestStep(11, "[FC] TH updates the DUT's FanMode attribute value iteratively, in accordance with the available fan modes provided by the FanModeSequence attribute, in descending order from the fan mode prior to 3 (High) to 0 (Off). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", "Initialize attribute values: - FanMode: 3 (High). - PercentSetting: 100. - SpeedSetting (if supported): SpeedMax attribute value. Read the attribute values back and verify that they match the written values."), TestStep(12, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in descending order (from 3 (High) to 0 (Off)) For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", @@ -93,7 +96,7 @@ async def get_attribute_value_from_queue(queue, attribute, timeout_sec: float) - for q in list(queue): if q.attribute == attribute: return q.value - await asyncio.sleep(0.01) + await asyncio.sleep(0.0001) return None async def read_setting(self, endpoint, attribute): @@ -109,11 +112,12 @@ async def write_setting(self, endpoint, attribute, value) -> Status: return write_status async def write_and_verify_attribute(self, endpoint, attribute, value) -> Any: - await self.write_setting(endpoint, attribute, value) - value_read = await self.read_setting(endpoint, attribute) - asserts.assert_equal(value_read, value, - f"[FC] Mismatch between written and read attribute value ({attribute.__name__})") - return value_read + write_status = await self.write_setting(endpoint, attribute, value) + if write_status == Status.Success: + value_read = await self.read_setting(endpoint, attribute) + asserts.assert_equal(value_read, value, + f"[FC] Mismatch between written and read attribute value ({attribute.__name__} - written: {value}, read: {value_read})") + return write_status async def get_fan_modes(self, endpoint, exclude_auto: bool = False) -> list[int]: # Read FanModeSequence attribute value @@ -146,40 +150,38 @@ async def get_fan_modes(self, endpoint, exclude_auto: bool = False) -> list[int] return fan_modes - async def get_initialization_parametes(self, attr_to_update, order): + async def get_initialization_parametes(self, endpoint, attr_to_update, order): cluster = Clusters.FanControl - fan_mode_attr = cluster.Attributes.FanMode - percent_setting_attr = cluster.Attributes.PercentSetting - fan_mode_off = cluster.Enums.FanModeEnum.kOff - fan_mode_high = cluster.Enums.FanModeEnum.kHigh + attribute = cluster.Attributes percent_setting_max_value = 100 # PercentSetting max value is 100 as per the spec # Sets the number of iterations for attribute updates # Sets the update order (ascending or descending) - if attr_to_update == percent_setting_attr: - attr_to_verify = fan_mode_attr - iteration_range = range(1, percent_setting_max_value + - 1) if order == OrderEnum.Ascending else range(percent_setting_max_value - 1, -1, -1) - value_init_verify = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high - value_init_update = 0 if order == OrderEnum.Ascending else percent_setting_max_value - elif attr_to_update == fan_mode_attr: - attr_to_verify = percent_setting_attr - iteration_range = range(1, len(self.fan_modes)) if order == OrderEnum.Ascending else range( - len(self.fan_modes) - 2, -1, -1) - value_init_verify = 0 if order == OrderEnum.Ascending else percent_setting_max_value - value_init_update = fan_mode_off if order == OrderEnum.Ascending else fan_mode_high - - return attr_to_verify, value_init_verify, value_init_update, iteration_range + if attr_to_update == attribute.PercentSetting: + attr_to_verify = attribute.FanMode + iteration_range = range(0, percent_setting_max_value + + 1) if order == OrderEnum.Ascending else range(percent_setting_max_value, -1, -1) + elif attr_to_update == attribute.FanMode: + attr_to_verify = attribute.PercentSetting + iteration_range = range(0, len(self.fan_modes)) if order == OrderEnum.Ascending else range( + len(self.fan_modes) - 1, -1, -1) + + # Get fan initial state (FanMode, PercentSetting, SpeedSetting if supported) + init_fan_mode = await self.read_setting(endpoint, attribute.FanMode) + init_percent_setting = await self.read_setting(endpoint, attribute.PercentSetting) + init_speed_setting = await self.read_setting(endpoint, attribute.SpeedSetting) if self.supports_speed else None + + return attr_to_verify, iteration_range, init_fan_mode, init_percent_setting, init_speed_setting def verify_attribute_change(self, attr_to_verify, value_current, value_previous, order) -> None: if order == OrderEnum.Ascending: # Verify that the current attribute value is greater than the previous attribute value asserts.assert_greater(value_current, value_previous, - f"[FC] Current {attr_to_verify.__name__} must be greater than previous {attr_to_verify.__name__}") + f"[FC] Current {attr_to_verify.__name__} must be greater than previous {attr_to_verify.__name__}") else: # Verify that the current attribute value is less than the previous attribute value asserts.assert_less(value_current, value_previous, - f"[FC] Current {attr_to_verify.__name__} must be less than previous {attr_to_verify.__name__}") + f"[FC] Current {attr_to_verify.__name__} must be less than previous {attr_to_verify.__name__}") # Logging attribute value change details sub_text = f"({value_previous}) to ({value_current})" @@ -187,30 +189,88 @@ def verify_attribute_change(self, attr_to_verify, value_current, value_previous, sub_text = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" logging.info(f"\t\t[FC] {attr_to_verify.__name__} changed from {sub_text}") - async def log_scenario(self, attr_to_update, attr_to_verify, attr_to_update_value, attr_to_verify_value, speed_setting_value, order): + def handle_iteration_one_edge_cases(self, attr_to_update, attr_to_verify, order, attr_to_verify_value_current, init_fan_mode, init_percent_setting, init_speed_setting): + cluster = Clusters.FanControl + attribute = cluster.Attributes + fm_enum = cluster.Enums.FanModeEnum + + # Logging attribute value change details + if attr_to_verify == attribute.PercentSetting: + init_setting = init_percent_setting + elif attr_to_verify == attribute.SpeedSetting: + init_setting = init_speed_setting + elif attr_to_verify == attribute.FanMode: + init_setting = init_fan_mode + sub_text = f"({init_setting}) to ({attr_to_verify_value_current})" + if attr_to_verify == attribute.FanMode: + sub_text = f"({init_setting}:{init_setting.name}) to ({attr_to_verify_value_current}:{attr_to_verify_value_current.name})" + logging.info(f"\t\t[FC] {attr_to_verify.__name__} changed from {sub_text}") + + # The fan's general state is Off + if init_fan_mode == fm_enum.kOff: + if attr_to_update == attribute.PercentSetting: + init_setting = init_speed_setting if attr_to_verify == attribute.SpeedSetting else init_fan_mode + if order == OrderEnum.Ascending: + asserts.assert_equal(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the initial value") + elif order == OrderEnum.Descending: + asserts.assert_greater(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be greater than the initial value") + if attr_to_update == attribute.FanMode: + init_setting = init_speed_setting if attr_to_verify == attribute.SpeedSetting else init_percent_setting + if order == OrderEnum.Ascending: + asserts.assert_equal(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the initial value") + elif order == OrderEnum.Descending: + asserts.assert_greater(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be greater than the initial value") + + # The fan's general state is High + elif init_fan_mode == fm_enum.kHigh: + if attr_to_update == attribute.PercentSetting: + init_setting = self.speed_max if attr_to_verify == attribute.SpeedSetting else init_fan_mode + if order == OrderEnum.Ascending: + asserts.assert_less(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be less than the initial value") + elif order == OrderEnum.Descending: + asserts.assert_equal(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the initial value") + if attr_to_update == attribute.FanMode: + init_setting = self.speed_max if attr_to_verify == attribute.SpeedSetting else init_percent_setting + if order == OrderEnum.Ascending: + asserts.assert_less(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be less than the initial value") + elif order == OrderEnum.Descending: + asserts.assert_equal(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the initial value") + + # The fan's general state is in between Off and High + else: + if attr_to_update == attribute.PercentSetting: + init_setting = init_speed_setting if attr_to_verify == attribute.SpeedSetting else init_fan_mode + if order == OrderEnum.Ascending: + asserts.assert_less(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be less than the initial value") + elif order == OrderEnum.Descending: + asserts.assert_greater(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be greater than the initial value") + if attr_to_update == attribute.FanMode: + init_setting = init_speed_setting if attr_to_verify == attribute.SpeedSetting else init_percent_setting + if order == OrderEnum.Ascending: + asserts.assert_less(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be less than the initial value") + elif order == OrderEnum.Descending: + asserts.assert_greater(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be greater than the initial value") + + async def log_scenario(self, attr_to_update, attr_to_verify, order, init_fan_mode, init_percent_setting, init_speed_setting, ): # Logging the scenario being tested + logging.info("[FC] ====================================================================") speed_setting_scenario = " and SpeedSetting" if self.supports_speed else "" logging.info( - f"[FC] Update {attr_to_update.__name__} {order.name}, verify {attr_to_verify.__name__}{speed_setting_scenario}") + f"[FC] *** Update {attr_to_update.__name__} {order.name}, verify {attr_to_verify.__name__}{speed_setting_scenario}") - # Logging initialization values (FanMode, PercentSetting, and SpeedSetting (if supported)) - if attr_to_update == Clusters.FanControl.Attributes.PercentSetting: - percent_setting_log = attr_to_update_value - fan_mode_log = f"{attr_to_verify_value}:{attr_to_verify_value.name}" - else: - percent_setting_log = attr_to_verify_value - fan_mode_log = f"{attr_to_update_value}:{attr_to_update_value.name}" - speed_setting_log = f" SpeedSetting({speed_setting_value})" if self.supports_speed else "" + # Logging fan initial state (FanMode, PercentSetting, SpeedSetting if supported) + speed_setting_log = f", SpeedSetting({init_speed_setting})" if self.supports_speed else "" logging.info( - f"[FC] Initialization values: FanMode({fan_mode_log}) PercentSetting({percent_setting_log}){speed_setting_log}") + f"[FC] *** Fan initial state: FanMode ({init_fan_mode}:{init_fan_mode.name}), PercentSetting ({init_percent_setting}){speed_setting_log}") async def verify_fan_control_attribute_progression(self, endpoint, attr_to_update, order) -> None: # Setup - speed_setting_attr = Clusters.FanControl.Attributes.SpeedSetting - timeout_sec = 0.1 # Timeout given for item retreival from the attribute queue + cluster = Clusters.FanControl + attribute = cluster.Attributes + timeout_sec = 0.0001 # Timeout given for item retreival from the attribute queue # *** NEXT STEP *** - # TH configures the testing setup for the one of the following scenarios: + # TH configures the testing setup for one of the following scenarios: # - Update the PercentSetting attribute in ascending order and monitor the FanMode (and SpeedSetting, if supported) attributes # - Update the PercentSetting attribute in descending order and monitor the FanMode (and SpeedSetting, if supported) attributes # - Update the FanMode attribute in aescending order and monitor the PercentSetting (and SpeedSetting, if supported) attributes @@ -218,56 +278,67 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat self.step(self.current_step_index + 1) # Get initialization parameters - attr_to_verify, value_init_verify, value_init_update, iteration_range = \ - await self.get_initialization_parametes(attr_to_update, order) - - # Initializatization of the attribute to update (Write and read back verification) - attr_to_update_value = await self.write_and_verify_attribute(endpoint, attr_to_update, value_init_update) + attr_to_verify, iteration_range, init_fan_mode, init_percent_setting, init_speed_setting = \ + await self.get_initialization_parametes(endpoint, attr_to_update, order) - # Initializatization of the attribute to verify (Write and read back verification) - attr_to_verify_value = await self.write_and_verify_attribute(endpoint, attr_to_verify, value_init_verify) - - # Initializatization of the SpeedSetting attribute, if supported (Write and read back verification) - if self.supports_speed: - speed_setting_init = 0 if order == OrderEnum.Ascending else self.speed_max - speed_setting_value = await self.write_and_verify_attribute(endpoint, speed_setting_attr, speed_setting_init) + # Set the initial value of the attribute to verify + init_attr_to_verify_value = init_fan_mode if attr_to_verify == attribute.FanMode else init_percent_setting # *** NEXT STEP *** # TH performs the testing scenario defined in the previous step self.step(self.current_step_index + 1) # Logging the scenario being tested - await self.log_scenario(attr_to_update, attr_to_verify, attr_to_update_value, attr_to_verify_value, speed_setting_value, order) + await self.log_scenario(attr_to_update, attr_to_verify, order, init_fan_mode, init_percent_setting, init_speed_setting) # Write to attribute iteratively - attr_value_current = attr_to_verify_value - attr_value_previous = attr_to_verify_value - speed_setting_current = speed_setting_value - speed_setting_previous = speed_setting_value + attr_value_current = None + attr_value_previous = None + speed_setting_current = None + speed_setting_previous = None + iteration = 0 for value_to_write in iteration_range: + iteration += 1 # Clear the attribute report queue before each update to avoid duplicates self.attribute_subscription.get_last_report() # Write to attribute - write_status = await self.write_setting(endpoint, attr_to_update, value_to_write) + write_status = await self.write_and_verify_attribute(endpoint, attr_to_update, value_to_write) logging.info(f"[FC] {attr_to_update.__name__} written: {value_to_write}") - # If the write status is SUCCESS, it means the write operation occurred - # Verify that the current attribute value is greater than the previous - # one if order was set to Ascending, or less if order was set to Descending + # - If the write status is SUCCESS, it means the write operation + # occurred and it triggered an update in the corresponding attribute + # - Verify the correct progression of the attribute value to monitor if write_status == Status.Success: - # Get current attribute value and verify correct increment/decrement + + # Get the current attribute value from the attribute report queue queue = self.attribute_subscription.attribute_queue.queue attr_value_current = await self.get_attribute_value_from_queue(queue, attr_to_verify, timeout_sec) if attr_value_current is not None: - self.verify_attribute_change(attr_to_verify, attr_value_current, attr_value_previous, order) + # Handle iteration 1 edge cases where: + # - The fan can be in any initial state + # - The attribute's previous-value becomes the + # attributes initial state value or max value + # (as opposed to the value written in the preceding iteration) + if iteration == 1: + self.handle_iteration_one_edge_cases(attr_to_update, attr_to_verify, order, attr_value_current, init_fan_mode, init_percent_setting, init_speed_setting) + else: + # The attribute's previous value may be None in some scenarios, defaulting to the attribute's initial state value if so + attr_value_previous = init_attr_to_verify_value if attr_value_previous is None else attr_value_previous + + # Verify attribute progression + self.verify_attribute_change(attr_to_verify, attr_value_current, attr_value_previous, order) attr_value_previous = attr_value_current - # Get current SpeedSetting attribute value and verify correct increment/decrement (if supported) + # Performing the same verification for SpeedSetting, if supported if self.supports_speed: - speed_setting_current = await self.get_attribute_value_from_queue(queue, speed_setting_attr, timeout_sec) + speed_setting_current = await self.get_attribute_value_from_queue(queue, attribute.SpeedSetting, timeout_sec) if speed_setting_current is not None: - self.verify_attribute_change(speed_setting_attr, speed_setting_current, speed_setting_previous, order) + if iteration == 1: + self.handle_iteration_one_edge_cases(attr_to_update, attribute.SpeedSetting, order, speed_setting_current, init_fan_mode, init_percent_setting, init_speed_setting) + else: + speed_setting_previous = init_speed_setting if speed_setting_previous is None else speed_setting_previous + self.verify_attribute_change(attribute.SpeedSetting, speed_setting_current, speed_setting_previous, order) speed_setting_previous = speed_setting_current # If the write status is INVALID_IN_STATE, it means no write operation occurred @@ -276,13 +347,13 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat # Get current attribute value and verify it's equal to the previous value attr_value_current = await self.read_setting(endpoint, attr_to_verify) asserts.assert_equal(attr_value_current, attr_value_previous, - f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the previous one") + f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the previous one") # Get current SpeedSetting attribute value and verify it's equal to the previous value (if supported) if self.supports_speed: - speed_setting_current = await self.read_setting(endpoint, speed_setting_attr) + speed_setting_current = await self.read_setting(endpoint, attribute.SpeedSetting) asserts.assert_equal(speed_setting_current, speed_setting_previous, - "Current SpeedSetting attribute value must be equal to the previous one") + "Current SpeedSetting attribute value must be equal to the previous one") def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] @@ -322,9 +393,9 @@ async def test_TC_FAN_3_1(self): # (current value greater than previous value) await self.verify_fan_control_attribute_progression(ep, attributes.PercentSetting, OrderEnum.Ascending) - # TH updates the DUT's PercentSetting attribute value iteratively, in descending order (from 99 to 0) - # and monitors the FanMode (and SpeedSetting, if supported) attribute value's correct progression - # (current value less than previous value) + # # TH updates the DUT's PercentSetting attribute value iteratively, in descending order (from 99 to 0) + # # and monitors the FanMode (and SpeedSetting, if supported) attribute value's correct progression + # # (current value less than previous value) await self.verify_fan_control_attribute_progression(ep, attributes.PercentSetting, OrderEnum.Descending) # TH updates the DUT's FanMode attribute value iteratively, in accordance with the From 68d28a171ae09bc5bd8403421f269b486125c7f3 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 24 Feb 2025 02:28:43 -0800 Subject: [PATCH 48/53] refactor, updates steps --- src/python_testing/TC_FAN_3_1.py | 141 +++++++++++++++---------------- 1 file changed, 66 insertions(+), 75 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index ef22d894bd1b2a..26d49f3c42d87f 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -35,7 +35,6 @@ # === END CI TEST ARGUMENTS === import asyncio -import enum import logging import time from enum import Enum @@ -62,31 +61,19 @@ def desc_TC_FAN_3_1(self) -> str: def steps_TC_FAN_3_1(self): return [TestStep(1, "[FC] Commissioning already done.", is_commissioning=True), - TestStep(2, "[FC] TH reads the FanModeSequence attribute to retreive the available fan modes.", - "Save the result for future reference."), - TestStep(3, "[FC] TH reads the FeatureMap attribute from the DUT.", - "Check for the existence of the SpeedSetting feature. If the SpeedSetting feature is supported, read the SpeedMax attribute and save the result for future reference."), + TestStep(2, "[FC] TH reads the FanModeSequence attribute from the DUT. This attribute specifies the available fan modes.", + "Verify that the DUT response contains a FanModeSequenceEnum and store."), + TestStep(3, "[FC] TH checks the DUT for support of the MultiSpeed feature.", + "If the MultiSpeed feature is supported the SpeedSetting attribute will be present."), TestStep(4, "[FC] TH subscribes to the DUT's FanControl cluster.", "Enables the TH to receive attribute updates."), - - TestStep(5, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in ascending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", - "Initialize attribute values: - FanMode: 0 (Off). - PercentSetting: 0. - SpeedSetting (if supported): 0. Read the attribute values back and verify that they match the written values."), - TestStep(6, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in ascending order (from 1 to 100). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", - "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), - - TestStep(7, "[FC] TH configures the testing setup for the following scenario: Updating the PercentSetting attribute in descending order and monitoring the FanMode (and SpeedSetting, if supported) attributes.", - "Initialize attribute values: - FanMode: 3 (High). - PercentSetting: 100. - SpeedSetting (if supported): SpeedMax attribute value. Read the attribute values back and verify that they match the written values."), - TestStep(8, "[FC] TH updates the value of the PercentSetting attribute of the DUT iteratively, in descending order (from 99 to 0). For each iteration, the TH reads the value of the FanMode attribute (and SpeedSetting, if supported) from the DUT.", - "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the FanMode attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the PercentSetting attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the PercentSetting attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), - - TestStep(9, "[FC] TH updates the DUT's FanMode attribute value iteratively, in accordance with the available fan modes provided by the FanModeSequence attribute, in ascending order from the fan mode subsequent to 0 (Off) to 3 (High). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", - "Initialize attribute values: - FanMode: 0 (Off). - PercentSetting: 0. - SpeedSetting (if supported): 0. Read the attribute values back and verify that they match the written values."), - TestStep(10, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in ascending order (from 0 (Off) to 3 (High)). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", - "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is greater than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), - - TestStep(11, "[FC] TH updates the DUT's FanMode attribute value iteratively, in accordance with the available fan modes provided by the FanModeSequence attribute, in descending order from the fan mode prior to 3 (High) to 0 (Off). For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", - "Initialize attribute values: - FanMode: 3 (High). - PercentSetting: 100. - SpeedSetting (if supported): SpeedMax attribute value. Read the attribute values back and verify that they match the written values."), - TestStep(12, "[FC] TH updates the value of the FanMode attribute of the DUT iteratively, in descending order (from 3 (High) to 0 (Off)) For each iteration, the TH reads the value of the PercentSetting attribute (and SpeedSetting, if supported) from the DUT.", - "For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. Verify the following for the PercentSetting attribute value (and SpeedSetting, if supported): - For iterations where the DUT returns a SUCCESS status code after the FanMode attribute value update, and a change in the attribute value is detected, verify that the current value is less than the previous one. - For iterations where the DUT returns an INVALID_IN_STATE status code after the FanMode attribute value update, which means that no update operation occurred, verify that the current value is the same as the previous one."), + TestStep(5, "[FC] TH tests the following scenario: - Attribute to update: PercentSetting - Attribute to verify: FanMode and SpeedSetting (if present) - Update order: Ascending", + "Update the value of the PercentSetting attribute iteratively, in ascending order, from 0 to 100. For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. If SUCCESS and a FanMode (and/or SpeedSetting if present) attribute value change is triggered: Verify that the current value(s) is greater than the previous one. If INVALID_IN_STATE, no update operation occurred. Verify that the current value(s) is the same as the previous one"), + TestStep(6, "[FC] TH tests the following scenario: - Attribute to update: PercentSetting - Attribute to verify: FanMode and SpeedSetting (if present) - Update order: Descending", + "Update the value of the PercentSetting attribute iteratively, in descending order, from 100 to 0. For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. If SUCCESS and a FanMode (and/or SpeedSetting if present) attribute value change is triggered. Verify that the current value(s) is less than the previous one. If INVALID_IN_STATE, no update operation occurred. Verify that the current value(s) is the same as the previous one"), + TestStep(7, "[FC] TH tests the following scenario: - Attribute to update: FanMode - Attribute to verify: PercentSetting and SpeedSetting (if present) - Update order: Ascending", + "Update the value of the FanMode attribute iteratively, in ascending order, from 0 (Off) to the number of available fan modes specified by the FanModeSequence attribute excluding modes beyond 3 (High). For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. If SUCCESS and a PercentSetting (and/or SpeedSetting if present) attribute value change is triggered. Verify that the current value(s) is greater than the previous one. If INVALID_IN_STATE, no update operation occurred. Verify that the current value(s) is the same as the previous one"), + TestStep(8, "[FC] TH tests the following scenario: - Attribute to update: FanMode - Attribute to verify: PercentSetting and SpeedSetting (if present) - Update order: Descending", + "Update the value of the FanMode attribute iteratively, in descending order, from the number of available fan modes specified by the FanModeSequence attribute excluding modes beyond 3 (High) to 0 (Off). For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. If SUCCESS and a PercentSetting (and/or SpeedSetting if present) attribute value change is triggered. Verify that the current value(s) is less than the previous one. If INVALID_IN_STATE, no update operation occurred. Verify that the current value(s) is the same as the previous one"), ] @staticmethod @@ -119,15 +106,15 @@ async def write_and_verify_attribute(self, endpoint, attribute, value) -> Any: f"[FC] Mismatch between written and read attribute value ({attribute.__name__} - written: {value}, read: {value_read})") return write_status - async def get_fan_modes(self, endpoint, exclude_auto: bool = False) -> list[int]: + async def get_fan_modes(self, endpoint, max_high: bool = False) -> list[int]: # Read FanModeSequence attribute value fan_mode_sequence_attr = Clusters.FanControl.Attributes.FanModeSequence fm_enum = Clusters.FanControl.Enums.FanModeEnum + fms_enum = Clusters.FanControl.Enums.FanModeSequenceEnum fan_mode_sequence = await self.read_setting(endpoint, fan_mode_sequence_attr) - - # There are only 6 valid FanModeSequence values (0, 1, 2, 3, 4, 5) - # Verifying read FanModeSequence attribute value is valid - asserts.assert_in(fan_mode_sequence, range(0, 5), f"Unsupported FanModeSequence: {fan_mode_sequence}") + + # Verify response contains a FanModeSequenceEnum + asserts.assert_is_instance(fan_mode_sequence, fms_enum, f"[FC] FanModeSequence result isn't of enum type {fms_enum.__name__}") logging.info(f"[FC] Supported fan modes: {fan_mode_sequence.name}") @@ -145,8 +132,10 @@ async def get_fan_modes(self, endpoint, exclude_auto: bool = False) -> list[int] elif fan_mode_sequence == 5: fan_modes = [fm_enum.kOff, fm_enum.kHigh] - if exclude_auto and fm_enum.kAuto in fan_modes: - fan_modes.remove(fm_enum.kAuto) + if max_high: + if fm_enum.kHigh in fan_modes: + high_index = fan_modes.index(fm_enum.kHigh) + fan_modes = fan_modes[:high_index + 1] # Keep only modes up to and including kHigh return fan_modes @@ -166,10 +155,10 @@ async def get_initialization_parametes(self, endpoint, attr_to_update, order): iteration_range = range(0, len(self.fan_modes)) if order == OrderEnum.Ascending else range( len(self.fan_modes) - 1, -1, -1) - # Get fan initial state (FanMode, PercentSetting, SpeedSetting if supported) + # Get fan initial state (FanMode, PercentSetting, SpeedSetting if present) init_fan_mode = await self.read_setting(endpoint, attribute.FanMode) init_percent_setting = await self.read_setting(endpoint, attribute.PercentSetting) - init_speed_setting = await self.read_setting(endpoint, attribute.SpeedSetting) if self.supports_speed else None + init_speed_setting = await self.read_setting(endpoint, attribute.SpeedSetting) if self.supports_multispeed else None return attr_to_verify, iteration_range, init_fan_mode, init_percent_setting, init_speed_setting @@ -193,7 +182,7 @@ def handle_iteration_one_edge_cases(self, attr_to_update, attr_to_verify, order, cluster = Clusters.FanControl attribute = cluster.Attributes fm_enum = cluster.Enums.FanModeEnum - + # Logging attribute value change details if attr_to_verify == attribute.PercentSetting: init_setting = init_percent_setting @@ -224,13 +213,16 @@ def handle_iteration_one_edge_cases(self, attr_to_update, attr_to_verify, order, # The fan's general state is High elif init_fan_mode == fm_enum.kHigh: if attr_to_update == attribute.PercentSetting: - init_setting = self.speed_max if attr_to_verify == attribute.SpeedSetting else init_fan_mode + init_setting = init_speed_setting if attr_to_verify == attribute.SpeedSetting else init_fan_mode if order == OrderEnum.Ascending: asserts.assert_less(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be less than the initial value") elif order == OrderEnum.Descending: - asserts.assert_equal(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the initial value") + if attr_to_verify == attribute.SpeedSetting: + asserts.assert_greater_equal(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the initial value") + else: + asserts.assert_equal(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the initial value") if attr_to_update == attribute.FanMode: - init_setting = self.speed_max if attr_to_verify == attribute.SpeedSetting else init_percent_setting + init_setting = init_speed_setting if attr_to_verify == attribute.SpeedSetting else init_percent_setting if order == OrderEnum.Ascending: asserts.assert_less(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be less than the initial value") elif order == OrderEnum.Descending: @@ -254,28 +246,25 @@ def handle_iteration_one_edge_cases(self, attr_to_update, attr_to_verify, order, async def log_scenario(self, attr_to_update, attr_to_verify, order, init_fan_mode, init_percent_setting, init_speed_setting, ): # Logging the scenario being tested logging.info("[FC] ====================================================================") - speed_setting_scenario = " and SpeedSetting" if self.supports_speed else "" + speed_setting_scenario = " and SpeedSetting" if self.supports_multispeed else "" logging.info( f"[FC] *** Update {attr_to_update.__name__} {order.name}, verify {attr_to_verify.__name__}{speed_setting_scenario}") - # Logging fan initial state (FanMode, PercentSetting, SpeedSetting if supported) - speed_setting_log = f", SpeedSetting({init_speed_setting})" if self.supports_speed else "" + # Logging fan initial state (FanMode, PercentSetting, SpeedSetting if present) + speed_setting_log = f", SpeedSetting({init_speed_setting})" if self.supports_multispeed else "" logging.info( f"[FC] *** Fan initial state: FanMode ({init_fan_mode}:{init_fan_mode.name}), PercentSetting ({init_percent_setting}){speed_setting_log}") async def verify_fan_control_attribute_progression(self, endpoint, attr_to_update, order) -> None: - # Setup cluster = Clusters.FanControl attribute = cluster.Attributes timeout_sec = 0.0001 # Timeout given for item retreival from the attribute queue - # *** NEXT STEP *** # TH configures the testing setup for one of the following scenarios: - # - Update the PercentSetting attribute in ascending order and monitor the FanMode (and SpeedSetting, if supported) attributes - # - Update the PercentSetting attribute in descending order and monitor the FanMode (and SpeedSetting, if supported) attributes - # - Update the FanMode attribute in aescending order and monitor the PercentSetting (and SpeedSetting, if supported) attributes - # - Update the FanMode attribute in descending order and monitor the PercentSetting (and SpeedSetting, if supported) attributes - self.step(self.current_step_index + 1) + # - Update the PercentSetting attribute in ascending order and monitor the FanMode (and SpeedSetting, if present) attributes + # - Update the PercentSetting attribute in descending order and monitor the FanMode (and SpeedSetting, if present) attributes + # - Update the FanMode attribute in aescending order and monitor the PercentSetting (and SpeedSetting, if present) attributes + # - Update the FanMode attribute in descending order and monitor the PercentSetting (and SpeedSetting, if present) attributes # Get initialization parameters attr_to_verify, iteration_range, init_fan_mode, init_percent_setting, init_speed_setting = \ @@ -285,7 +274,7 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat init_attr_to_verify_value = init_fan_mode if attr_to_verify == attribute.FanMode else init_percent_setting # *** NEXT STEP *** - # TH performs the testing scenario defined in the previous step + # TH performs the testing scenario defined previously self.step(self.current_step_index + 1) # Logging the scenario being tested @@ -330,8 +319,8 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat self.verify_attribute_change(attr_to_verify, attr_value_current, attr_value_previous, order) attr_value_previous = attr_value_current - # Performing the same verification for SpeedSetting, if supported - if self.supports_speed: + # Performing the same verification for SpeedSetting, if present + if self.supports_multispeed: speed_setting_current = await self.get_attribute_value_from_queue(queue, attribute.SpeedSetting, timeout_sec) if speed_setting_current is not None: if iteration == 1: @@ -349,8 +338,8 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat asserts.assert_equal(attr_value_current, attr_value_previous, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the previous one") - # Get current SpeedSetting attribute value and verify it's equal to the previous value (if supported) - if self.supports_speed: + # Get current SpeedSetting attribute value and verify it's equal to the previous value (if present) + if self.supports_multispeed: speed_setting_current = await self.read_setting(endpoint, attribute.SpeedSetting) asserts.assert_equal(speed_setting_current, speed_setting_previous, "Current SpeedSetting attribute value must be equal to the previous one") @@ -370,17 +359,15 @@ async def test_TC_FAN_3_1(self): self.step(1) # *** STEP 2 *** - # TH reads the FanModeSequence attribute to retreive the available fan modes + # TH reads the FanModeSequence attribute from the DUT self.step(2) - self.fan_modes = await self.get_fan_modes(ep, exclude_auto=True) + self.fan_modes = await self.get_fan_modes(ep, max_high=True) # *** STEP 3 *** - # TH reads the FeatureMap attribute to check for support of the SpeedSetting feature + # TH checks the DUT for support of the MultiSpeed feature self.step(3) feature_map = await self.read_setting(ep, attributes.FeatureMap) - self.supports_speed = bool(feature_map & cluster.Bitmaps.Feature.kMultiSpeed) - if self.supports_speed: - self.speed_max = await self.read_setting(ep, attributes.SpeedMax) + self.supports_multispeed = bool(feature_map & cluster.Bitmaps.Feature.kMultiSpeed) # *** STEP 4 *** # TH subscribes to the DUT's FanControl cluster @@ -388,28 +375,32 @@ async def test_TC_FAN_3_1(self): self.attribute_subscription = ClusterAttributeChangeAccumulator(cluster) await self.attribute_subscription.start(self.default_controller, self.dut_node_id, ep) - # TH updates the DUT's PercentSetting attribute value iteratively, in ascending order (from 1 to 100) - # and monitors the FanMode (and SpeedSetting, if supported) attribute value's correct progression - # (current value greater than previous value) + # *** STEP 5 *** + # TH tests the following scenario: + # - Attribute to update: PercentSetting + # - Attribute to verify: FanMode and SpeedSetting (if present) + # - Update order: Ascending await self.verify_fan_control_attribute_progression(ep, attributes.PercentSetting, OrderEnum.Ascending) - # # TH updates the DUT's PercentSetting attribute value iteratively, in descending order (from 99 to 0) - # # and monitors the FanMode (and SpeedSetting, if supported) attribute value's correct progression - # # (current value less than previous value) + # *** STEP 6 *** + # TH tests the following scenario: + # - Attribute to update: PercentSetting + # - Attribute to verify: FanMode and SpeedSetting (if present) + # - Update order: Descending await self.verify_fan_control_attribute_progression(ep, attributes.PercentSetting, OrderEnum.Descending) - # TH updates the DUT's FanMode attribute value iteratively, in accordance with the - # available fan modes provided by the FanModeSequence attribute, in ascending order - # from the fan mode subsequent to 0 (Off) to 3 (High) and monitors the PercentSetting - # (and SpeedSetting, if supported) attribute value's correct progression - # (current value greater than previous value) + # *** STEP 7 *** + # TH tests the following scenario: + # - Attribute to update: FanMode + # - Attribute to verify: PercentSetting and SpeedSetting (if present) + # - Update order: Ascending await self.verify_fan_control_attribute_progression(ep, attributes.FanMode, OrderEnum.Ascending) - # TH updates the DUT's FanMode attribute value iteratively, in accordance with the - # available fan modes provided by the FanModeSequence attribute, in descending order - # from the fan mode prior to 3 (High) to 0 (Off) and monitors the PercentSetting - # (and SpeedSetting, if supported) attribute value's correct progression - # (current value less than previous value) + # *** STEP 8 *** + # TH tests the following scenario: + # - Attribute to update: FanMode + # - Attribute to verify: PercentSetting and SpeedSetting (if present) + # - Update order: Descending await self.verify_fan_control_attribute_progression(ep, attributes.FanMode, OrderEnum.Descending) From 91d11e1fb962bab6a69aa455c40004964e9525c2 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 24 Feb 2025 08:38:35 -0800 Subject: [PATCH 49/53] minor comments rewording --- src/python_testing/TC_FAN_3_1.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 26d49f3c42d87f..9babba8b424e0a 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -274,7 +274,7 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat init_attr_to_verify_value = init_fan_mode if attr_to_verify == attribute.FanMode else init_percent_setting # *** NEXT STEP *** - # TH performs the testing scenario defined previously + # TH performs one of the testing scenarios previously defined self.step(self.current_step_index + 1) # Logging the scenario being tested @@ -306,13 +306,13 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat if attr_value_current is not None: # Handle iteration 1 edge cases where: # - The fan can be in any initial state - # - The attribute's previous-value becomes the - # attributes initial state value or max value + # - The attribute's previous-value becomes + # the attributes initial-state value # (as opposed to the value written in the preceding iteration) if iteration == 1: self.handle_iteration_one_edge_cases(attr_to_update, attr_to_verify, order, attr_value_current, init_fan_mode, init_percent_setting, init_speed_setting) else: - # The attribute's previous value may be None in some scenarios, defaulting to the attribute's initial state value if so + # The attribute's previous-value may be None in some cases, defaulting to the attribute's initial state value if so attr_value_previous = init_attr_to_verify_value if attr_value_previous is None else attr_value_previous # Verify attribute progression @@ -337,7 +337,7 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat attr_value_current = await self.read_setting(endpoint, attr_to_verify) asserts.assert_equal(attr_value_current, attr_value_previous, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the previous one") - +# TODO: Handle iter 1 cases # Get current SpeedSetting attribute value and verify it's equal to the previous value (if present) if self.supports_multispeed: speed_setting_current = await self.read_setting(endpoint, attribute.SpeedSetting) From ed7b1670a43df75928527cac24a5c5f66360b0f4 Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 24 Feb 2025 09:16:24 -0800 Subject: [PATCH 50/53] minor maintenance --- src/python_testing/TC_FAN_3_1.py | 105 ++++++++++++++++--------------- 1 file changed, 54 insertions(+), 51 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 9babba8b424e0a..ae2572cfbb6a3f 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -76,16 +76,6 @@ def steps_TC_FAN_3_1(self): "Update the value of the FanMode attribute iteratively, in descending order, from the number of available fan modes specified by the FanModeSequence attribute excluding modes beyond 3 (High) to 0 (Off). For each update, the DUT shall return either a SUCCESS or an INVALID_IN_STATE status code. If SUCCESS and a PercentSetting (and/or SpeedSetting if present) attribute value change is triggered. Verify that the current value(s) is less than the previous one. If INVALID_IN_STATE, no update operation occurred. Verify that the current value(s) is the same as the previous one"), ] - @staticmethod - async def get_attribute_value_from_queue(queue, attribute, timeout_sec: float) -> Any: - start_time = time.time() - while time.time() - start_time < timeout_sec: - for q in list(queue): - if q.attribute == attribute: - return q.value - await asyncio.sleep(0.0001) - return None - async def read_setting(self, endpoint, attribute): cluster = Clusters.Objects.FanControl return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) @@ -106,30 +96,38 @@ async def write_and_verify_attribute(self, endpoint, attribute, value) -> Any: f"[FC] Mismatch between written and read attribute value ({attribute.__name__} - written: {value}, read: {value_read})") return write_status - async def get_fan_modes(self, endpoint, max_high: bool = False) -> list[int]: + @staticmethod + async def get_attribute_value_from_queue(queue, attribute, timeout_sec: float) -> Any: + start_time = time.time() + while time.time() - start_time < timeout_sec: + for q in list(queue): + if q.attribute == attribute: + return q.value + await asyncio.sleep(0.0001) + return None + + async def get_fan_modes(self, endpoint, max_high: bool = False): # Read FanModeSequence attribute value fan_mode_sequence_attr = Clusters.FanControl.Attributes.FanModeSequence fm_enum = Clusters.FanControl.Enums.FanModeEnum fms_enum = Clusters.FanControl.Enums.FanModeSequenceEnum - fan_mode_sequence = await self.read_setting(endpoint, fan_mode_sequence_attr) - - # Verify response contains a FanModeSequenceEnum - asserts.assert_is_instance(fan_mode_sequence, fms_enum, f"[FC] FanModeSequence result isn't of enum type {fms_enum.__name__}") + self.fan_mode_sequence = await self.read_setting(endpoint, fan_mode_sequence_attr) - logging.info(f"[FC] Supported fan modes: {fan_mode_sequence.name}") + # Verify response contains a FanModeSequenceEnum + asserts.assert_is_instance(self.fan_mode_sequence, fms_enum, f"[FC] FanModeSequence result isn't of enum type {fms_enum.__name__}") fan_modes = None - if fan_mode_sequence == 0: + if self.fan_mode_sequence == 0: fan_modes = [fm_enum.kOff, fm_enum.kLow, fm_enum.kMedium, fm_enum.kHigh] - elif fan_mode_sequence == 1: + elif self.fan_mode_sequence == 1: fan_modes = [fm_enum.kOff, fm_enum.kLow, fm_enum.kHigh] - elif fan_mode_sequence == 2: + elif self.fan_mode_sequence == 2: fan_modes = [fm_enum.kOff, fm_enum.kLow, fm_enum.kMedium, fm_enum.kHigh, fm_enum.kAuto] - elif fan_mode_sequence == 3: + elif self.fan_mode_sequence == 3: fan_modes = [fm_enum.kOff, fm_enum.kLow, fm_enum.kHigh, fm_enum.kAuto] - elif fan_mode_sequence == 4: + elif self.fan_mode_sequence == 4: fan_modes = [fm_enum.kOff, fm_enum.kHigh, fm_enum.kAuto] - elif fan_mode_sequence == 5: + elif self.fan_mode_sequence == 5: fan_modes = [fm_enum.kOff, fm_enum.kHigh] if max_high: @@ -137,7 +135,7 @@ async def get_fan_modes(self, endpoint, max_high: bool = False) -> list[int]: high_index = fan_modes.index(fm_enum.kHigh) fan_modes = fan_modes[:high_index + 1] # Keep only modes up to and including kHigh - return fan_modes + self.fan_modes = fan_modes async def get_initialization_parametes(self, endpoint, attr_to_update, order): cluster = Clusters.FanControl @@ -162,23 +160,8 @@ async def get_initialization_parametes(self, endpoint, attr_to_update, order): return attr_to_verify, iteration_range, init_fan_mode, init_percent_setting, init_speed_setting - def verify_attribute_change(self, attr_to_verify, value_current, value_previous, order) -> None: - if order == OrderEnum.Ascending: - # Verify that the current attribute value is greater than the previous attribute value - asserts.assert_greater(value_current, value_previous, - f"[FC] Current {attr_to_verify.__name__} must be greater than previous {attr_to_verify.__name__}") - else: - # Verify that the current attribute value is less than the previous attribute value - asserts.assert_less(value_current, value_previous, - f"[FC] Current {attr_to_verify.__name__} must be less than previous {attr_to_verify.__name__}") - - # Logging attribute value change details - sub_text = f"({value_previous}) to ({value_current})" - if attr_to_verify == Clusters.FanControl.Attributes.FanMode: - sub_text = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" - logging.info(f"\t\t[FC] {attr_to_verify.__name__} changed from {sub_text}") - - def handle_iteration_one_edge_cases(self, attr_to_update, attr_to_verify, order, attr_to_verify_value_current, init_fan_mode, init_percent_setting, init_speed_setting): + @staticmethod + def handle_iteration_one_edge_cases(attr_to_update, attr_to_verify, order, attr_to_verify_value_current, init_fan_mode, init_percent_setting, init_speed_setting): cluster = Clusters.FanControl attribute = cluster.Attributes fm_enum = cluster.Enums.FanModeEnum @@ -243,17 +226,22 @@ def handle_iteration_one_edge_cases(self, attr_to_update, attr_to_verify, order, elif order == OrderEnum.Descending: asserts.assert_greater(attr_to_verify_value_current, init_setting, f"[FC] Current {attr_to_verify.__name__} attribute value must be greater than the initial value") - async def log_scenario(self, attr_to_update, attr_to_verify, order, init_fan_mode, init_percent_setting, init_speed_setting, ): - # Logging the scenario being tested - logging.info("[FC] ====================================================================") - speed_setting_scenario = " and SpeedSetting" if self.supports_multispeed else "" - logging.info( - f"[FC] *** Update {attr_to_update.__name__} {order.name}, verify {attr_to_verify.__name__}{speed_setting_scenario}") + @staticmethod + def verify_attribute_change(attr_to_verify, value_current, value_previous, order) -> None: + if order == OrderEnum.Ascending: + # Verify that the current attribute value is greater than the previous attribute value + asserts.assert_greater(value_current, value_previous, + f"[FC] Current {attr_to_verify.__name__} must be greater than previous {attr_to_verify.__name__}") + else: + # Verify that the current attribute value is less than the previous attribute value + asserts.assert_less(value_current, value_previous, + f"[FC] Current {attr_to_verify.__name__} must be less than previous {attr_to_verify.__name__}") - # Logging fan initial state (FanMode, PercentSetting, SpeedSetting if present) - speed_setting_log = f", SpeedSetting({init_speed_setting})" if self.supports_multispeed else "" - logging.info( - f"[FC] *** Fan initial state: FanMode ({init_fan_mode}:{init_fan_mode.name}), PercentSetting ({init_percent_setting}){speed_setting_log}") + # Logging attribute value change details + sub_text = f"({value_previous}) to ({value_current})" + if attr_to_verify == Clusters.FanControl.Attributes.FanMode: + sub_text = f"({value_previous}:{value_previous.name}) to ({value_current}:{value_current.name})" + logging.info(f"\t\t[FC] {attr_to_verify.__name__} changed from {sub_text}") async def verify_fan_control_attribute_progression(self, endpoint, attr_to_update, order) -> None: cluster = Clusters.FanControl @@ -344,6 +332,21 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat asserts.assert_equal(speed_setting_current, speed_setting_previous, "Current SpeedSetting attribute value must be equal to the previous one") + async def log_scenario(self, attr_to_update, attr_to_verify, order, init_fan_mode, init_percent_setting, init_speed_setting): + # Logging the scenario being tested + logging.info("[FC] ====================================================================") + speed_setting_scenario = " and SpeedSetting" if self.supports_multispeed else "" + logging.info( + f"[FC] *** Update {attr_to_update.__name__} {order.name}, verify {attr_to_verify.__name__}{speed_setting_scenario}") + + # Logging fan initial state (FanMode, PercentSetting, SpeedSetting if present) + speed_setting_log = f", SpeedSetting({init_speed_setting})" if self.supports_multispeed else "" + logging.info( + f"[FC] *** Fan initial state: FanMode ({init_fan_mode}:{init_fan_mode.name}), PercentSetting ({init_percent_setting}){speed_setting_log}") + + # Loging supported fan modes + logging.info(f"[FC] *** Supported fan modes: {self.fan_mode_sequence.name}") + def pics_TC_FAN_3_1(self) -> list[str]: return ["FAN.S"] @@ -361,7 +364,7 @@ async def test_TC_FAN_3_1(self): # *** STEP 2 *** # TH reads the FanModeSequence attribute from the DUT self.step(2) - self.fan_modes = await self.get_fan_modes(ep, max_high=True) + await self.get_fan_modes(ep, max_high=True) # *** STEP 3 *** # TH checks the DUT for support of the MultiSpeed feature From 2cf3031ccd020ed985d98206262534519791e69d Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 24 Feb 2025 09:48:41 -0800 Subject: [PATCH 51/53] typo --- src/python_testing/TC_FAN_3_1.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index ae2572cfbb6a3f..3f1950df1c1dcc 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -344,7 +344,7 @@ async def log_scenario(self, attr_to_update, attr_to_verify, order, init_fan_mod logging.info( f"[FC] *** Fan initial state: FanMode ({init_fan_mode}:{init_fan_mode.name}), PercentSetting ({init_percent_setting}){speed_setting_log}") - # Loging supported fan modes + # Logging supported fan modes logging.info(f"[FC] *** Supported fan modes: {self.fan_mode_sequence.name}") def pics_TC_FAN_3_1(self) -> list[str]: From 477feca0472b375e8938591548e8b65a22878d0d Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 24 Feb 2025 10:36:35 -0800 Subject: [PATCH 52/53] adds iteration 1 edge case handling when INVALID_IN_STATE --- src/python_testing/TC_FAN_3_1.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 3f1950df1c1dcc..772c47691a492e 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -323,14 +323,16 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat elif write_status == Status.InvalidInState: # Get current attribute value and verify it's equal to the previous value attr_value_current = await self.read_setting(endpoint, attr_to_verify) + attr_value_previous = init_attr_to_verify_value if iteration == 1 else attr_value_previous asserts.assert_equal(attr_value_current, attr_value_previous, f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the previous one") -# TODO: Handle iter 1 cases + # Get current SpeedSetting attribute value and verify it's equal to the previous value (if present) if self.supports_multispeed: speed_setting_current = await self.read_setting(endpoint, attribute.SpeedSetting) + speed_setting_previous = init_speed_setting if iteration == 1 else speed_setting_previous asserts.assert_equal(speed_setting_current, speed_setting_previous, - "Current SpeedSetting attribute value must be equal to the previous one") + "Current SpeedSetting attribute value must be equal to the previous one") async def log_scenario(self, attr_to_update, attr_to_verify, order, init_fan_mode, init_percent_setting, init_speed_setting): # Logging the scenario being tested From fc800d8d9ba65c4bd97b4075f7883253d2b1671d Mon Sep 17 00:00:00 2001 From: Raul Marquez Date: Mon, 24 Feb 2025 10:42:17 -0800 Subject: [PATCH 53/53] comments/assertion wording minor update --- src/python_testing/TC_FAN_3_1.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/python_testing/TC_FAN_3_1.py b/src/python_testing/TC_FAN_3_1.py index 772c47691a492e..da397006201722 100644 --- a/src/python_testing/TC_FAN_3_1.py +++ b/src/python_testing/TC_FAN_3_1.py @@ -319,20 +319,20 @@ async def verify_fan_control_attribute_progression(self, endpoint, attr_to_updat speed_setting_previous = speed_setting_current # If the write status is INVALID_IN_STATE, it means no write operation occurred - # Verify that the current attribute value is equal to the previous one + # Verify that the current attribute value is equal to the initial value elif write_status == Status.InvalidInState: # Get current attribute value and verify it's equal to the previous value attr_value_current = await self.read_setting(endpoint, attr_to_verify) attr_value_previous = init_attr_to_verify_value if iteration == 1 else attr_value_previous asserts.assert_equal(attr_value_current, attr_value_previous, - f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the previous one") + f"[FC] Current {attr_to_verify.__name__} attribute value must be equal to the initial value") # Get current SpeedSetting attribute value and verify it's equal to the previous value (if present) if self.supports_multispeed: speed_setting_current = await self.read_setting(endpoint, attribute.SpeedSetting) speed_setting_previous = init_speed_setting if iteration == 1 else speed_setting_previous asserts.assert_equal(speed_setting_current, speed_setting_previous, - "Current SpeedSetting attribute value must be equal to the previous one") + "Current SpeedSetting attribute value must be equal to the initial value") async def log_scenario(self, attr_to_update, attr_to_verify, order, init_fan_mode, init_percent_setting, init_speed_setting): # Logging the scenario being tested