From 9e2dfe26093b2d55007704ceaf2152c63b571d5f Mon Sep 17 00:00:00 2001 From: mkardous-silabs <84793247+mkardous-silabs@users.noreply.github.com> Date: Tue, 1 Aug 2023 13:47:34 -0400 Subject: [PATCH] [ICD] Add default ICD Behavior to readhandler when establishing a subscription (#28375) * add default ICD behavior to readhandler * Add comments * Update src/app/ReadHandler.cpp Co-authored-by: Boris Zbarsky * move check to top * Add ICD unit tests for ReadHandler * check to avoid division by 0 * fix dependency chain * restyle * remove deps for manager srcs --------- Co-authored-by: Boris Zbarsky --- src/app/ReadHandler.cpp | 51 +++ src/app/icd/BUILD.gn | 4 +- src/app/tests/BUILD.gn | 7 +- src/app/tests/TestReadInteraction.cpp | 406 ++++++++++++++++++ .../suites/TestIcdManagementCluster.yaml | 2 +- src/lib/core/CHIPConfig.h | 2 +- .../chip-tool/zap-generated/test/Commands.h | 2 +- .../zap-generated/test/Commands.h | 2 +- 8 files changed, 468 insertions(+), 8 deletions(-) diff --git a/src/app/ReadHandler.cpp b/src/app/ReadHandler.cpp index 1c3a40cc962273..4e9e8fccdf5646 100644 --- a/src/app/ReadHandler.cpp +++ b/src/app/ReadHandler.cpp @@ -713,6 +713,57 @@ CHIP_ERROR ReadHandler::ProcessSubscribeRequest(System::PacketBufferHandle && aP ReturnErrorOnFailure(subscribeRequestParser.GetMaxIntervalCeilingSeconds(&mMaxInterval)); VerifyOrReturnError(mMinIntervalFloorSeconds <= mMaxInterval, CHIP_ERROR_INVALID_ARGUMENT); +#if CHIP_CONFIG_ENABLE_ICD_SERVER + + // Default behavior for ICDs where the wanted MaxInterval for a subscription is the IdleModeInterval + // defined in the ICD Management Cluster. + // Behavior can be changed with the OnSubscriptionRequested function defined in the application callbacks + + // Default Behavior Steps : + // If MinInterval > IdleModeInterval, try to set the MaxInterval to the first interval of IdleModeIntervals above the + // MinInterval. + // If the next interval is greater than the MaxIntervalCeiling, use the MaxIntervalCeiling. + // Otherwise, use IdleModeInterval as MaxInterval + + // GetPublisherSelectedIntervalLimit() returns the IdleModeInterval if the device is an ICD + uint32_t decidedMaxInterval = GetPublisherSelectedIntervalLimit(); + + // Check if the PublisherSelectedIntervalLimit is 0. If so, set decidedMaxInterval to MaxIntervalCeiling + if (decidedMaxInterval == 0) + { + decidedMaxInterval = mMaxInterval; + } + + // If requestedMinInterval is greater than the IdleTimeInterval, select next active up time as max interval + if (mMinIntervalFloorSeconds > decidedMaxInterval) + { + uint16_t ratio = mMinIntervalFloorSeconds / static_cast(decidedMaxInterval); + if (mMinIntervalFloorSeconds % decidedMaxInterval) + { + ratio++; + } + + decidedMaxInterval *= ratio; + } + + // Verify that decidedMaxInterval is an acceptable value (overflow) + if (decidedMaxInterval > System::Clock::Seconds16::max().count()) + { + decidedMaxInterval = System::Clock::Seconds16::max().count(); + } + + // Verify that the decidedMaxInterval respects MAX(GetPublisherSelectedIntervalLimit(), MaxIntervalCeiling) + uint16_t maximumMaxInterval = std::max(GetPublisherSelectedIntervalLimit(), mMaxInterval); + if (decidedMaxInterval > maximumMaxInterval) + { + decidedMaxInterval = maximumMaxInterval; + } + + // Set max interval of the subscription + mMaxInterval = static_cast(decidedMaxInterval); + +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER + // // Notify the application (if requested) of the impending subscription and check whether we should still proceed to set it up. // This also provides the application an opportunity to modify the negotiated min/max intervals set above. diff --git a/src/app/icd/BUILD.gn b/src/app/icd/BUILD.gn index 18aea3f155d845..c0034a88e7b9b8 100644 --- a/src/app/icd/BUILD.gn +++ b/src/app/icd/BUILD.gn @@ -29,11 +29,11 @@ source_set("manager-srcs") { "ICDManager.h", ] - deps = [ + public_deps = [ ":cluster-srcs", ":observer-srcs", + "${chip_root}/src/credentials:credentials", ] - public_deps = [ "${chip_root}/src/credentials:credentials" ] } # ICD management cluster source-set is broken out of the main source-set to enable unit tests diff --git a/src/app/tests/BUILD.gn b/src/app/tests/BUILD.gn index 5378b199aa17f7..90b4796fef3c47 100644 --- a/src/app/tests/BUILD.gn +++ b/src/app/tests/BUILD.gn @@ -17,6 +17,7 @@ import("//build_overrides/chip.gni") import("//build_overrides/nlunit_test.gni") import("${chip_root}/build/chip/chip_test_suite.gni") +import("${chip_root}/src/app/icd/icd.gni") import("${chip_root}/src/platform/device.gni") static_library("helpers") { @@ -186,7 +187,6 @@ chip_test_suite("tests") { ":time-sync-data-provider-test-srcs", "${chip_root}/src/app", "${chip_root}/src/app/common:cluster-objects", - "${chip_root}/src/app/icd:cluster-srcs", "${chip_root}/src/app/icd:manager-srcs", "${chip_root}/src/app/tests:helpers", "${chip_root}/src/app/util/mock:mock_ember", @@ -195,7 +195,10 @@ chip_test_suite("tests") { "${nlunit_test_root}:nlunit-test", ] - if (chip_config_network_layer_ble && + # Do not run TestCommissionManager when running ICD specific unit tests. + # ICDManager has a dependency on the Accessors.h file which causes a link error + # when building the TestCommissionManager + if (!chip_enable_icd_server && chip_config_network_layer_ble && (chip_device_platform == "linux" || chip_device_platform == "darwin")) { test_sources += [ "TestCommissionManager.cpp" ] public_deps += [ "${chip_root}/src/app/server" ] diff --git a/src/app/tests/TestReadInteraction.cpp b/src/app/tests/TestReadInteraction.cpp index a818ee3d23e3a4..88571b040e866d 100644 --- a/src/app/tests/TestReadInteraction.cpp +++ b/src/app/tests/TestReadInteraction.cpp @@ -322,6 +322,13 @@ class TestReadInteraction static void TestReadClientInvalidReport(nlTestSuite * apSuite, void * apContext); static void TestReadHandlerInvalidAttributePath(nlTestSuite * apSuite, void * apContext); static void TestProcessSubscribeRequest(nlTestSuite * apSuite, void * apContext); +#if CHIP_CONFIG_ENABLE_ICD_SERVER + static void TestICDProcessSubscribeRequestSupMaxIntervalCeiling(nlTestSuite * apSuite, void * apContext); + static void TestICDProcessSubscribeRequestInfMaxIntervalCeiling(nlTestSuite * apSuite, void * apContext); + static void TestICDProcessSubscribeRequestSupMinInterval(nlTestSuite * apSuite, void * apContext); + static void TestICDProcessSubscribeRequestMaxMinInterval(nlTestSuite * apSuite, void * apContext); + static void TestICDProcessSubscribeRequestInvalidIdleModeInterval(nlTestSuite * apSuite, void * apContext); +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER static void TestReadRoundtrip(nlTestSuite * apSuite, void * apContext); static void TestPostSubscribeRoundtripChunkReport(nlTestSuite * apSuite, void * apContext); static void TestReadRoundtripWithDataVersionFilter(nlTestSuite * apSuite, void * apContext); @@ -1491,6 +1498,385 @@ void TestReadInteraction::TestProcessSubscribeRequest(nlTestSuite * apSuite, voi NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); } +#if CHIP_CONFIG_ENABLE_ICD_SERVER +/** + * @brief Test validates that an ICD will choose its IdleModeInterval (GetPublisherSelectedIntervalLimit) + * as MaxInterval when the MaxIntervalCeiling is superior. + */ +void TestReadInteraction::TestICDProcessSubscribeRequestSupMaxIntervalCeiling(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TestContext & ctx = *static_cast(apContext); + System::PacketBufferTLVWriter writer; + System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); + SubscribeRequestMessage::Builder subscribeRequestBuilder; + MockInteractionModelApp delegate; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + uint16_t kMinInterval = 0; + uint16_t kMaxIntervalCeiling = 1; + + Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); + + { + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, + app::reporting::GetDefaultReportScheduler()); + + writer.Init(std::move(subscribeRequestbuf)); + err = subscribeRequestBuilder.Init(&writer); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + subscribeRequestBuilder.KeepSubscriptions(true); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + subscribeRequestBuilder.MinIntervalFloorSeconds(kMinInterval); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + subscribeRequestBuilder.MaxIntervalCeilingSeconds(kMaxIntervalCeiling); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + AttributePathIBs::Builder & attributePathListBuilder = subscribeRequestBuilder.CreateAttributeRequests(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + + AttributePathIB::Builder & attributePathBuilder = attributePathListBuilder.CreatePath(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + + attributePathBuilder.Node(1).Endpoint(2).Cluster(3).Attribute(4).ListIndex(5).EndOfAttributePathIB(); + err = attributePathBuilder.GetError(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + attributePathListBuilder.EndOfAttributePathIBs(); + err = attributePathListBuilder.GetError(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + subscribeRequestBuilder.IsFabricFiltered(false).EndOfSubscribeRequestMessage(); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + err = writer.Finalize(&subscribeRequestbuf); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + err = readHandler.ProcessSubscribeRequest(std::move(subscribeRequestbuf)); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + uint16_t idleModeInterval = readHandler.GetPublisherSelectedIntervalLimit(); + + uint16_t minInterval; + uint16_t maxInterval; + readHandler.GetReportingIntervals(minInterval, maxInterval); + + NL_TEST_ASSERT(apSuite, minInterval == kMinInterval); + NL_TEST_ASSERT(apSuite, maxInterval == idleModeInterval); + } + engine->Shutdown(); + + NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); +} + +/** + * @brief Test validates that an ICD will choose its IdleModeInterval (GetPublisherSelectedIntervalLimit) + * as MaxInterval when the MaxIntervalCeiling is inferior. + */ +void TestReadInteraction::TestICDProcessSubscribeRequestInfMaxIntervalCeiling(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TestContext & ctx = *static_cast(apContext); + System::PacketBufferTLVWriter writer; + System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); + SubscribeRequestMessage::Builder subscribeRequestBuilder; + MockInteractionModelApp delegate; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + uint16_t kMinInterval = 0; + uint16_t kMaxIntervalCeiling = 1; + + Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); + + { + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, + app::reporting::GetDefaultReportScheduler()); + + writer.Init(std::move(subscribeRequestbuf)); + err = subscribeRequestBuilder.Init(&writer); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + subscribeRequestBuilder.KeepSubscriptions(true); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + subscribeRequestBuilder.MinIntervalFloorSeconds(kMinInterval); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + subscribeRequestBuilder.MaxIntervalCeilingSeconds(kMaxIntervalCeiling); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + AttributePathIBs::Builder & attributePathListBuilder = subscribeRequestBuilder.CreateAttributeRequests(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + + AttributePathIB::Builder & attributePathBuilder = attributePathListBuilder.CreatePath(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + + attributePathBuilder.Node(1).Endpoint(2).Cluster(3).Attribute(4).ListIndex(5).EndOfAttributePathIB(); + err = attributePathBuilder.GetError(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + attributePathListBuilder.EndOfAttributePathIBs(); + err = attributePathListBuilder.GetError(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + subscribeRequestBuilder.IsFabricFiltered(false).EndOfSubscribeRequestMessage(); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + err = writer.Finalize(&subscribeRequestbuf); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + err = readHandler.ProcessSubscribeRequest(std::move(subscribeRequestbuf)); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + uint16_t idleModeInterval = readHandler.GetPublisherSelectedIntervalLimit(); + + uint16_t minInterval; + uint16_t maxInterval; + readHandler.GetReportingIntervals(minInterval, maxInterval); + + NL_TEST_ASSERT(apSuite, minInterval == kMinInterval); + NL_TEST_ASSERT(apSuite, maxInterval == idleModeInterval); + } + engine->Shutdown(); + + NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); +} + +/** + * @brief Test validates that an ICD will choose a multiple of its IdleModeInterval (GetPublisherSelectedIntervalLimit) + * as MaxInterval when the MinInterval > IdleModeInterval. + */ +void TestReadInteraction::TestICDProcessSubscribeRequestSupMinInterval(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TestContext & ctx = *static_cast(apContext); + System::PacketBufferTLVWriter writer; + System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); + SubscribeRequestMessage::Builder subscribeRequestBuilder; + MockInteractionModelApp delegate; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + uint16_t kMinInterval = 3; + uint16_t kMaxIntervalCeiling = 5; + + Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); + + { + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, + app::reporting::GetDefaultReportScheduler()); + + writer.Init(std::move(subscribeRequestbuf)); + err = subscribeRequestBuilder.Init(&writer); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + subscribeRequestBuilder.KeepSubscriptions(true); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + subscribeRequestBuilder.MinIntervalFloorSeconds(kMinInterval); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + subscribeRequestBuilder.MaxIntervalCeilingSeconds(kMaxIntervalCeiling); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + AttributePathIBs::Builder & attributePathListBuilder = subscribeRequestBuilder.CreateAttributeRequests(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + + AttributePathIB::Builder & attributePathBuilder = attributePathListBuilder.CreatePath(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + + attributePathBuilder.Node(1).Endpoint(2).Cluster(3).Attribute(4).ListIndex(5).EndOfAttributePathIB(); + err = attributePathBuilder.GetError(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + attributePathListBuilder.EndOfAttributePathIBs(); + err = attributePathListBuilder.GetError(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + subscribeRequestBuilder.IsFabricFiltered(false).EndOfSubscribeRequestMessage(); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + err = writer.Finalize(&subscribeRequestbuf); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + err = readHandler.ProcessSubscribeRequest(std::move(subscribeRequestbuf)); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + uint16_t idleModeInterval = readHandler.GetPublisherSelectedIntervalLimit(); + + uint16_t minInterval; + uint16_t maxInterval; + readHandler.GetReportingIntervals(minInterval, maxInterval); + + NL_TEST_ASSERT(apSuite, minInterval == kMinInterval); + NL_TEST_ASSERT(apSuite, maxInterval == (2 * idleModeInterval)); + } + engine->Shutdown(); + + NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); +} + +/** + * @brief Test validates that an ICD will choose a maximal value for an uint16 if the multiple of the IdleModeInterval + * is greater than variable size. + */ +void TestReadInteraction::TestICDProcessSubscribeRequestMaxMinInterval(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TestContext & ctx = *static_cast(apContext); + System::PacketBufferTLVWriter writer; + System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); + SubscribeRequestMessage::Builder subscribeRequestBuilder; + MockInteractionModelApp delegate; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + uint16_t kMinInterval = System::Clock::Seconds16::max().count(); + uint16_t kMaxIntervalCeiling = System::Clock::Seconds16::max().count(); + + Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); + + { + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, + app::reporting::GetDefaultReportScheduler()); + + writer.Init(std::move(subscribeRequestbuf)); + err = subscribeRequestBuilder.Init(&writer); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + subscribeRequestBuilder.KeepSubscriptions(true); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + subscribeRequestBuilder.MinIntervalFloorSeconds(kMinInterval); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + subscribeRequestBuilder.MaxIntervalCeilingSeconds(kMaxIntervalCeiling); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + AttributePathIBs::Builder & attributePathListBuilder = subscribeRequestBuilder.CreateAttributeRequests(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + + AttributePathIB::Builder & attributePathBuilder = attributePathListBuilder.CreatePath(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + + attributePathBuilder.Node(1).Endpoint(2).Cluster(3).Attribute(4).ListIndex(5).EndOfAttributePathIB(); + err = attributePathBuilder.GetError(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + attributePathListBuilder.EndOfAttributePathIBs(); + err = attributePathListBuilder.GetError(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + subscribeRequestBuilder.IsFabricFiltered(false).EndOfSubscribeRequestMessage(); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + err = writer.Finalize(&subscribeRequestbuf); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + err = readHandler.ProcessSubscribeRequest(std::move(subscribeRequestbuf)); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + uint16_t minInterval; + uint16_t maxInterval; + readHandler.GetReportingIntervals(minInterval, maxInterval); + + NL_TEST_ASSERT(apSuite, minInterval == kMinInterval); + NL_TEST_ASSERT(apSuite, maxInterval == kMaxIntervalCeiling); + } + engine->Shutdown(); + + NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); +} + +/** + * @brief Test validates that an ICD will choose the MaxIntervalCeiling as MaxInterval if the next multiple after the MinInterval + * is greater than the IdleModeInterval and MaxIntervalCeiling + */ +void TestReadInteraction::TestICDProcessSubscribeRequestInvalidIdleModeInterval(nlTestSuite * apSuite, void * apContext) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + TestContext & ctx = *static_cast(apContext); + System::PacketBufferTLVWriter writer; + System::PacketBufferHandle subscribeRequestbuf = System::PacketBufferHandle::New(System::PacketBuffer::kMaxSize); + SubscribeRequestMessage::Builder subscribeRequestBuilder; + MockInteractionModelApp delegate; + auto * engine = chip::app::InteractionModelEngine::GetInstance(); + err = engine->Init(&ctx.GetExchangeManager(), &ctx.GetFabricTable(), app::reporting::GetDefaultReportScheduler()); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + uint16_t kMinInterval = 3; + uint16_t kMaxIntervalCeiling = 3; + + Messaging::ExchangeContext * exchangeCtx = ctx.NewExchangeToAlice(nullptr, false); + + { + ReadHandler readHandler(*engine, exchangeCtx, chip::app::ReadHandler::InteractionType::Read, + app::reporting::GetDefaultReportScheduler()); + + writer.Init(std::move(subscribeRequestbuf)); + err = subscribeRequestBuilder.Init(&writer); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + subscribeRequestBuilder.KeepSubscriptions(true); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + subscribeRequestBuilder.MinIntervalFloorSeconds(kMinInterval); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + subscribeRequestBuilder.MaxIntervalCeilingSeconds(kMaxIntervalCeiling); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + AttributePathIBs::Builder & attributePathListBuilder = subscribeRequestBuilder.CreateAttributeRequests(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + + AttributePathIB::Builder & attributePathBuilder = attributePathListBuilder.CreatePath(); + NL_TEST_ASSERT(apSuite, attributePathListBuilder.GetError() == CHIP_NO_ERROR); + + attributePathBuilder.Node(1).Endpoint(2).Cluster(3).Attribute(4).ListIndex(5).EndOfAttributePathIB(); + err = attributePathBuilder.GetError(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + attributePathListBuilder.EndOfAttributePathIBs(); + err = attributePathListBuilder.GetError(); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + subscribeRequestBuilder.IsFabricFiltered(false).EndOfSubscribeRequestMessage(); + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + + NL_TEST_ASSERT(apSuite, subscribeRequestBuilder.GetError() == CHIP_NO_ERROR); + err = writer.Finalize(&subscribeRequestbuf); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + err = readHandler.ProcessSubscribeRequest(std::move(subscribeRequestbuf)); + NL_TEST_ASSERT(apSuite, err == CHIP_NO_ERROR); + + uint16_t minInterval; + uint16_t maxInterval; + readHandler.GetReportingIntervals(minInterval, maxInterval); + + NL_TEST_ASSERT(apSuite, minInterval == kMinInterval); + NL_TEST_ASSERT(apSuite, maxInterval == kMaxIntervalCeiling); + } + engine->Shutdown(); + + NL_TEST_ASSERT(apSuite, ctx.GetExchangeManager().GetNumActiveExchanges() == 0); +} + +#endif // CHIP_CONFIG_ENABLE_ICD_SERVER + void TestReadInteraction::TestSubscribeRoundtrip(nlTestSuite * apSuite, void * apContext) { TestContext & ctx = *static_cast(apContext); @@ -4419,6 +4805,18 @@ const nlTest sTests[] = NL_TEST_DEF("TestReadClientInvalidReport", chip::app::TestReadInteraction::TestReadClientInvalidReport), NL_TEST_DEF("TestReadHandlerInvalidAttributePath", chip::app::TestReadInteraction::TestReadHandlerInvalidAttributePath), NL_TEST_DEF("TestProcessSubscribeRequest", chip::app::TestReadInteraction::TestProcessSubscribeRequest), + /* + We need to figure out a way to run unit tests with an ICD build without affecting + all the standard unit tests + https://github.com/project-chip/connectedhomeip/issues/28446 + */ +#if CHIP_CONFIG_ENABLE_ICD_SERVER + NL_TEST_DEF("TestICDProcessSubscribeRequestSupMaxIntervalCeiling", chip::app::TestReadInteraction::TestICDProcessSubscribeRequestSupMaxIntervalCeiling), + NL_TEST_DEF("TestICDProcessSubscribeRequestInfMaxIntervalCeiling", chip::app::TestReadInteraction::TestICDProcessSubscribeRequestInfMaxIntervalCeiling), + NL_TEST_DEF("TestICDProcessSubscribeRequestSupMinInterval", chip::app::TestReadInteraction::TestICDProcessSubscribeRequestSupMinInterval), + NL_TEST_DEF("TestICDProcessSubscribeRequestMaxMinInterval", chip::app::TestReadInteraction::TestICDProcessSubscribeRequestMaxMinInterval), + NL_TEST_DEF("TestICDProcessSubscribeRequestInvalidIdleModeInterval", chip::app::TestReadInteraction::TestICDProcessSubscribeRequestInvalidIdleModeInterval), +#endif // #if CHIP_CONFIG_ENABLE_ICD_SERVER NL_TEST_DEF("TestSubscribeRoundtrip", chip::app::TestReadInteraction::TestSubscribeRoundtrip), NL_TEST_DEF("TestPostSubscribeRoundtripChunkReport", chip::app::TestReadInteraction::TestPostSubscribeRoundtripChunkReport), NL_TEST_DEF("TestReadClientReceiveInvalidMessage", chip::app::TestReadInteraction::TestReadClientReceiveInvalidMessage), @@ -4437,7 +4835,15 @@ const nlTest sTests[] = NL_TEST_DEF("TestReadHandlerInvalidSubscribeRequest", chip::app::TestReadInteraction::TestReadHandlerInvalidSubscribeRequest), NL_TEST_DEF("TestSubscribeInvalidateFabric", chip::app::TestReadInteraction::TestSubscribeInvalidateFabric), NL_TEST_DEF("TestShutdownSubscription", chip::app::TestReadInteraction::TestShutdownSubscription), + /* + Disable test when running the ICD specific unit tests. + Test tests reporting feature with hard coded time jumps which don't take into account that an ICD + can change the requested MaxInterval during the subscription response / request process + https://github.com/project-chip/connectedhomeip/issues/28419 + */ +#if CHIP_CONFIG_ENABLE_ICD_SERVER != 1 NL_TEST_DEF("TestSubscribeUrgentWildcardEvent", chip::app::TestReadInteraction::TestSubscribeUrgentWildcardEvent), +#endif NL_TEST_DEF("TestSubscribeWildcard", chip::app::TestReadInteraction::TestSubscribeWildcard), NL_TEST_DEF("TestSubscribePartialOverlap", chip::app::TestReadInteraction::TestSubscribePartialOverlap), NL_TEST_DEF("TestSubscribeSetDirtyFullyOverlap", chip::app::TestReadInteraction::TestSubscribeSetDirtyFullyOverlap), diff --git a/src/app/tests/suites/TestIcdManagementCluster.yaml b/src/app/tests/suites/TestIcdManagementCluster.yaml index 6b3d29c1fc5f6b..fb52bd71a48a0b 100644 --- a/src/app/tests/suites/TestIcdManagementCluster.yaml +++ b/src/app/tests/suites/TestIcdManagementCluster.yaml @@ -32,7 +32,7 @@ tests: command: "readAttribute" attribute: "IdleModeInterval" response: - value: 500 + value: 2000 - label: "Read ActiveModeInterval" command: "readAttribute" diff --git a/src/lib/core/CHIPConfig.h b/src/lib/core/CHIPConfig.h index d1e6dc45cd8c7b..80b3d18cfe410f 100644 --- a/src/lib/core/CHIPConfig.h +++ b/src/lib/core/CHIPConfig.h @@ -1470,7 +1470,7 @@ extern const char CHIP_NON_PRODUCTION_MARKER[]; * @brief Default value for the ICD Management cluster IdleModeInterval attribute, in milliseconds */ #ifndef CHIP_CONFIG_ICD_IDLE_MODE_INTERVAL -#define CHIP_CONFIG_ICD_IDLE_MODE_INTERVAL 500 +#define CHIP_CONFIG_ICD_IDLE_MODE_INTERVAL 2000 #endif /** diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index 7198be4927de21..0ab94dafd7c628 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -32078,7 +32078,7 @@ class TestIcdManagementClusterSuite : public TestCommand { uint32_t value; VerifyOrReturn(CheckDecodeValue(chip::app::DataModel::Decode(*data, value))); - VerifyOrReturn(CheckValue("idleModeInterval", value, 500UL)); + VerifyOrReturn(CheckValue("idleModeInterval", value, 2000UL)); } break; case 2: diff --git a/zzz_generated/darwin-framework-tool/zap-generated/test/Commands.h b/zzz_generated/darwin-framework-tool/zap-generated/test/Commands.h index 5acafa1f5f6831..5f4ad1bcda5777 100644 --- a/zzz_generated/darwin-framework-tool/zap-generated/test/Commands.h +++ b/zzz_generated/darwin-framework-tool/zap-generated/test/Commands.h @@ -43148,7 +43148,7 @@ class TestIcdManagementCluster : public TestCommandBridge { { id actualValue = value; - VerifyOrReturn(CheckValue("IdleModeInterval", actualValue, 500UL)); + VerifyOrReturn(CheckValue("IdleModeInterval", actualValue, 2000UL)); } NextTest();