From 29233773fbe440c0765bea231fbe8bc7bada1274 Mon Sep 17 00:00:00 2001 From: C Freeman Date: Tue, 11 Jul 2023 16:46:55 -0400 Subject: [PATCH] TC-TIMESYNC-2.1 (#26400) * TC-TIMESYNC-2.1 NOTE: not yet added to the CI since the SDK impl hasn't landed yet. * Appease the linter * Restyled by isort * zap regen. * zap regen --------- Co-authored-by: Restyled.io --- src/python_testing/TC_TIMESYNC_2_1.py | 192 ++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 src/python_testing/TC_TIMESYNC_2_1.py diff --git a/src/python_testing/TC_TIMESYNC_2_1.py b/src/python_testing/TC_TIMESYNC_2_1.py new file mode 100644 index 00000000000000..9413f5ee41b008 --- /dev/null +++ b/src/python_testing/TC_TIMESYNC_2_1.py @@ -0,0 +1,192 @@ +# +# Copyright (c) 2023 Project CHIP Authors +# All rights reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +import ipaddress +from datetime import timedelta + +import chip.clusters as Clusters +from chip.clusters.Types import NullValue +from matter_testing_support import MatterBaseTest, async_test_body, default_matter_test_main, utc_time_in_matter_epoch +from mobly import asserts + + +class TC_TIMESYNC_2_1(MatterBaseTest): + async def read_ts_attribute_expect_success(self, endpoint, attribute): + cluster = Clusters.Objects.TimeSynchronization + return await self.read_single_attribute_check_success(endpoint=endpoint, cluster=cluster, attribute=attribute) + + @async_test_body + async def test_TC_TIMESYNC_2_1(self): + + endpoint = self.user_params.get("endpoint", 0) + + self.print_step(1, "Commissioning, already done") + attributes = Clusters.TimeSynchronization.Attributes + + self.print_step(2, "Read Granularity attribute") + if self.check_pics("TIMESYNC.S.A0001"): + granularity_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.Granularity) + asserts.assert_less(granularity_dut, Clusters.TimeSynchronization.Enums.GranularityEnum.kUnknownEnumValue, + "Granularity is not in valid range") + else: + asserts.assert_true(False, "Granularity is a mandatory attribute and must be present in the PICS file") + + self.print_step(3, "Read TimeSource") + if self.check_pics("TIMESYNC.S.A0002"): + time_source = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.TimeSource) + asserts.assert_less(time_source, Clusters.TimeSynchronization.Enums.TimeSourceEnum.kUnknownEnumValue, + "TimeSource is not in valid range") + else: + asserts.assert_true(False, "TimeSource is a mandatory attribute and must be present in the PICS file") + + self.print_step(4, "Read TrustedTimeSource") + if self.check_pics("TIMESYNC.S.A0003"): + trusted_time_source = await self.read_ts_attribute_expect_success(endpoint=endpoint, + attribute=attributes.TrustedTimeSource) + if trusted_time_source is not NullValue: + asserts.assert_less_equal(trusted_time_source.fabricIndex, 0xFE, + "FabricIndex for the TrustedTimeSource is out of range") + asserts.assert_greater_equal(trusted_time_source.fabricIndex, 1, + "FabricIndex for the TrustedTimeSource is out of range") + elif self.check_pics("TIMESYNC.S.F03"): + asserts.assert_true(False, "TrustedTimeSource is mandatory if the TSC feature (TIMESYNC.S.F03) is supported") + + self.print_step(5, "Read DefaultNTP") + if self.check_pics("TIMESYNC.S.A0004"): + default_ntp = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.DefaultNTP) + if default_ntp is not NullValue: + asserts.assert_less_equal(len(default_ntp), 128, "DefaultNTP length must be less than 128") + # Assume this is a valid web address if it has at least one . in the name + is_web_addr = default_ntp.find('.') != -1 + try: + ipaddress.IPv6Address(default_ntp) + is_ip_addr = True + except ipaddress.AddressValueError: + is_ip_addr = False + pass + asserts.assert_true(is_web_addr or is_ip_addr, "Returned DefaultNTP value is not a IP address or web address") + elif self.check_pics("TIMESYNC.S.F01"): + asserts.assert_true(False, "DefaultNTP is mandatory if the NTPC (TIMESYNC.S.F01) feature is supported") + + self.print_step(6, "Read TimeZone") + if self.check_pics("TIMESYNC.S.A0005"): + tz_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.TimeZone) + asserts.assert_greater_equal(len(tz_dut), 1, "TimeZone must have at least one entry in the list") + asserts.assert_less_equal(len(tz_dut), 2, "TimeZone may have a maximum of two entries in the list") + for entry in tz_dut: + asserts.assert_greater_equal(entry.offset, -43200, "TimeZone offset is out of range") + asserts.assert_less_equal(entry.offset, 50400, "TimeZone offset is out of range") + if entry.name: + asserts.assert_less_equal(len(entry.name), 64, "TimeZone name is too long") + + asserts.assert_equal(tz_dut[0].validAt, 0, "TimeZone list first entry must have a 0 ValidAt time") + if len(tz_dut) > 1: + asserts.assert_not_equal(tz_dut[1].validAt, 0, "TimeZone list second entry must have a non-zero ValidAt time") + elif self.check_pics("TIMESYNC.S.F00"): + asserts.assert_true(False, "TimeZone is mandatory if the TZ (TIMESYNC.S.F00) feature is supported") + + self.print_step(7, "Read DSTOffset") + if self.check_pics("TIMESYNC.S.A0006"): + dst_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.DSTOffset) + last_valid_until = -1 + last_valid_starting = -1 + for dst in dst_dut: + asserts.assert_greater(dst.validStarting, last_valid_starting, + "DSTOffset list must be sorted by ValidStarting time") + last_valid_starting = dst.validStarting + asserts.assert_greater_equal(dst.validStarting, last_valid_until, + "DSTOffset list must have every ValidStarting > ValidUntil of the previous entry") + last_valid_until = dst.validUntil + if dst.validUntil is NullValue or dst.validUntil is None: + asserts.assert_equal(dst, dst_dut[-1], "DSTOffset list must have Null ValidUntil at the end") + elif self.check_pics("TIMESYNC.S.F00"): + asserts.assert_true(False, "DSTOffset is mandatory if the TZ (TIMESYNC.S.F00) feature is supported") + + self.print_step(8, "Read UTCTime") + if self.check_pics("TIMESYNC.S.A0000"): + utc_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.UTCTime) + if utc_dut is NullValue: + asserts.assert_equal(granularity_dut, Clusters.TimeSynchronization.Enums.GranularityEnum.kNoTimeGranularity) + else: + asserts.assert_not_equal(granularity_dut, Clusters.TimeSynchronization.Enums.GranularityEnum.kNoTimeGranularity) + if granularity_dut is Clusters.TimeSynchronization.Enums.GranularityEnum.kMinutesGranularity: + toleranace = timedelta(minutes=10) + else: + toleranace = timedelta(minutes=1) + delta_us = abs(utc_dut - utc_time_in_matter_epoch()) + delta = timedelta(microseconds=delta_us) + asserts.assert_less_equal(delta, toleranace, "UTC time is not within tolerance of TH") + else: + asserts.assert_true(False, "UTCTime is a mandatory attribute and must be present in the PICS file") + + self.print_step(9, "Read LocalTime") + if self.check_pics("TIMESYNC.S.A0007"): + utc_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.UTCTime) + local_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.LocalTime) + if utc_dut is NullValue: + asserts.assert_true(local_dut is NullValue, "LocalTime must be Null if UTC time is Null") + elif len(dst_dut) == 0: + asserts.assert_true(local_dut is NullValue, "LocalTime must be Null if the DST table is empty") + else: + local_calculated = utc_dut + dst_dut[0].offset + tz_dut[0].offset + delta_us = abs(local_dut, local_calculated) + delta = timedelta(microseconds=delta_us) + toleranace = timedelta(minutes=1) + asserts.assert_less_equal(delta, toleranace, "Local time caluclation is not within tolerance of calculated value") + elif self.check_pics("TIMESYNC.S.F00"): + asserts.assert_true(False, "LocalTime is mandatory if the TZ (TIMESYNC.S.F00) feature is supported") + + self.print_step(10, "Read TimeZoneDatabase") + if self.check_pics("TIMESYNC.S.A0008"): + tz_db_dut = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.TimeZoneDatabase) + asserts.assert_less(tz_db_dut, Clusters.TimeSynchronization.Enums.TimeZoneDatabaseEnum.kUnknownEnumValue, + "TimeZoneDatabase is not in valid range") + elif self.check_pics("TIMESYNC.S.F00"): + asserts.assert_true(False, "TimeZoneDatabase is mandatory if the TZ (TIMESYNC.S.F00) feature is supported") + + self.print_step(11, "Read NTPServerAvailable") + if self.check_pics("TIMESYNC.S.A0009"): + # bool typechecking happens in the test read functions, so all we need to do here is do the read + await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.NTPServerAvailable) + elif self.check_pics("TIMESYNC.S.F02"): + asserts.assert_true(False, "NTPServerAvailable is mandatory if the NTPS (TIMESYNC.S.F02) feature is supported") + + self.print_step(12, "Read TimeZoneListMaxSize") + if self.check_pics("TIMESYNC.S.A000A"): + size = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.TimeZoneListMaxSize) + asserts.assert_greater_equal(size, 1, "TimeZoneListMaxSize must be at least 1") + asserts.assert_less_equal(size, 2, "TimeZoneListMaxSize must be max 2") + elif self.check_pics("TIMESYNC.S.F00"): + asserts.assert_true(False, "TimeZoneListMaxSize is mandatory if the TZ (TIMESYNC.S.F00) feature is supported") + + self.print_step(13, "Read DSTOffsetListMaxSize") + if self.check_pics("TIMESYNC.S.A000B"): + size = await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.DSTOffsetListMaxSize) + asserts.assert_greater_equal(size, 1, "DSTOffsetListMaxSize must be at least 1") + elif self.check_pics("TIMESYNC.S.F00"): + asserts.assert_true(False, "DSTOffsetListMaxSize is mandatory if the TZ (TIMESYNC.S.F00) feature is supported") + + self.print_step(14, "Read SupportsDNSResolve") + if self.check_pics("TIMESYNC.S.A0004"): + # bool typechecking happens in the test read functions, so all we need to do here is do the read + await self.read_ts_attribute_expect_success(endpoint=endpoint, attribute=attributes.SupportsDNSResolve) + elif self.check_pics("TIMESYNC.S.F01"): + asserts.assert_true(False, "SupportsDNSResolve is mandatory if the NTPC (TIMESYNC.S.F01) feature is supported") + + +if __name__ == "__main__": + default_matter_test_main()