-
Notifications
You must be signed in to change notification settings - Fork 2.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* 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 <[email protected]>
- Loading branch information
Showing
1 changed file
with
192 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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() |