Add records of session establishment for subscription resumption (#31755)
* Add records of session establishment for subscription resumption
* Restyled by clang-format
* review changes
* Schedule subscription resumption when failing to establish the session in SubscriptionResumptionSessionEstablisher
* Add option to set subscription timeout resumption retry interval seconds for Linux app
Add cirque test for subscription resumption timeout
* Restyled by clang-format
* Restyled by autopep8
* Restyled by isort
* fix CI building
* Add test to the test list
* add subscription resumption restries number to SubscriptionInfo struct
* review changes
* make resumption retries persistent
* Restyled by clang-format
* ci build fixes
* try to fix cirque test
---------
Co-authored-by: Restyled.io <commits@restyled.io>
diff --git a/examples/platform/linux/AppMain.cpp b/examples/platform/linux/AppMain.cpp
index 71701f7..b92d76e 100644
--- a/examples/platform/linux/AppMain.cpp
+++ b/examples/platform/linux/AppMain.cpp
@@ -570,7 +570,12 @@
chip::app::InteractionModelEngine::GetInstance()->SetHandlerCapacityForSubscriptions(
LinuxDeviceOptions::GetInstance().subscriptionCapacity);
chip::app::InteractionModelEngine::GetInstance()->SetForceHandlerQuota(true);
-#endif
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+ // Set subscription time resumption retry interval seconds
+ chip::app::InteractionModelEngine::GetInstance()->SetSubscriptionTimeoutResumptionRetryIntervalSeconds(
+ LinuxDeviceOptions::GetInstance().subscriptionResumptionRetryIntervalSec);
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
// Now that the server has started and we are done with our startup logging,
// log our discovery/onboarding information again so it's not lost in the
diff --git a/examples/platform/linux/Options.cpp b/examples/platform/linux/Options.cpp
index 8398c72..3017e77 100644
--- a/examples/platform/linux/Options.cpp
+++ b/examples/platform/linux/Options.cpp
@@ -90,7 +90,10 @@
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
kDeviceOption_SubscriptionCapacity = 0x1024,
#endif
- kDeviceOption_WiFiSupports5g = 0x1025
+ kDeviceOption_WiFiSupports5g = 0x1025,
+#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
+ kDeviceOption_SubscriptionResumptionRetryIntervalSec = 0x1026,
+#endif
};
constexpr unsigned kAppUsageLength = 64;
@@ -151,6 +154,7 @@
#endif
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
{ "subscription-capacity", kArgumentRequired, kDeviceOption_SubscriptionCapacity },
+ { "subscription-resumption-retry-interval", kArgumentRequired, kDeviceOption_SubscriptionResumptionRetryIntervalSec },
#endif
{}
};
@@ -280,6 +284,8 @@
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
" --subscription-capacity\n"
" Max number of subscriptions the device will allow\n"
+ " --subscription-resumption-retry-interval\n"
+ " subscription timeout resumption retry interval in seconds\n"
#endif
"\n";
@@ -547,6 +553,9 @@
case kDeviceOption_SubscriptionCapacity:
LinuxDeviceOptions::GetInstance().subscriptionCapacity = static_cast<int32_t>(atoi(aValue));
break;
+ case kDeviceOption_SubscriptionResumptionRetryIntervalSec:
+ LinuxDeviceOptions::GetInstance().subscriptionResumptionRetryIntervalSec = static_cast<int32_t>(atoi(aValue));
+ break;
#endif
default:
PrintArgError("%s: INTERNAL ERROR: Unhandled option: %s\n", aProgram, aName);
diff --git a/examples/platform/linux/Options.h b/examples/platform/linux/Options.h
index ca8082b..5063d03 100644
--- a/examples/platform/linux/Options.h
+++ b/examples/platform/linux/Options.h
@@ -73,7 +73,8 @@
uint16_t rpcServerPort = 33000;
#endif
#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
- int32_t subscriptionCapacity = CHIP_IM_MAX_NUM_SUBSCRIPTIONS;
+ int32_t subscriptionCapacity = CHIP_IM_MAX_NUM_SUBSCRIPTIONS;
+ int32_t subscriptionResumptionRetryIntervalSec = -1;
#endif
static LinuxDeviceOptions & GetInstance();
};
diff --git a/scripts/tests/cirque_tests.sh b/scripts/tests/cirque_tests.sh
index 0f9fe22..67bf0f2 100755
--- a/scripts/tests/cirque_tests.sh
+++ b/scripts/tests/cirque_tests.sh
@@ -51,6 +51,7 @@
"CommissioningWindowTest"
"SubscriptionResumptionTest"
"SubscriptionResumptionCapacityTest"
+ "SubscriptionResumptionTimeoutTest"
)
BOLD_GREEN_TEXT="\033[1;32m"
diff --git a/src/app/InteractionModelEngine.cpp b/src/app/InteractionModelEngine.cpp
index 8e4b5a8..68ab452 100644
--- a/src/app/InteractionModelEngine.cpp
+++ b/src/app/InteractionModelEngine.cpp
@@ -389,7 +389,11 @@
mReportingEngine.ResetReadHandlerTracker(&apReadObj);
mReadHandlers.ReleaseObject(&apReadObj);
+ TryToResumeSubscriptions();
+}
+void InteractionModelEngine::TryToResumeSubscriptions()
+{
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
if (!mSubscriptionResumptionScheduled && HasSubscriptionsToResume())
{
@@ -398,8 +402,10 @@
mpExchangeMgr->GetSessionManager()->SystemLayer()->StartTimer(
System::Clock::Seconds32(timeTillNextSubscriptionResumptionSecs), ResumeSubscriptionsTimerCallback, this);
mNumSubscriptionResumptionRetries++;
+ ChipLogProgress(InteractionModel, "Schedule subscription resumption when failing to establish session, Retries: %" PRIu32,
+ mNumSubscriptionResumptionRetries);
}
-#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
}
Status InteractionModelEngine::OnInvokeCommandRequest(Messaging::ExchangeContext * apExchangeContext,
@@ -1990,6 +1996,12 @@
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
uint32_t InteractionModelEngine::ComputeTimeSecondsTillNextSubscriptionResumption()
{
+#if CONFIG_BUILD_FOR_HOST_UNIT_TEST
+ if (mSubscriptionResumptionRetrySecondsOverride > 0)
+ {
+ return static_cast<uint32_t>(mSubscriptionResumptionRetrySecondsOverride);
+ }
+#endif
if (mNumSubscriptionResumptionRetries > CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION_MAX_FIBONACCI_STEP_INDEX)
{
return CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION_MAX_RETRY_INTERVAL_SECS;
diff --git a/src/app/InteractionModelEngine.h b/src/app/InteractionModelEngine.h
index 1c05767..22ec1fb 100644
--- a/src/app/InteractionModelEngine.h
+++ b/src/app/InteractionModelEngine.h
@@ -354,6 +354,19 @@
//
void SetForceHandlerQuota(bool forceHandlerQuota) { mForceHandlerQuota = forceHandlerQuota; }
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+ //
+ // Override the subscription timeout resumption retry interval seconds. The default retry interval will be
+ // 300s + GetFibonacciForIndex(retry_times) * 300s, which is too long for unit-tests.
+ //
+ // If -1 is passed in, no override is instituted and default behavior resumes.
+ //
+ void SetSubscriptionTimeoutResumptionRetryIntervalSeconds(int32_t seconds)
+ {
+ mSubscriptionResumptionRetrySecondsOverride = seconds;
+ }
+#endif
+
//
// When testing subscriptions using the high-level APIs in src/controller/ReadInteraction.h,
// they don't provide for the ability to shut down those subscriptions after they've been established.
@@ -392,6 +405,8 @@
void OnDone(CommandHandler & apCommandObj) override;
void OnDone(ReadHandler & apReadObj) override;
+ void TryToResumeSubscriptions();
+
ReadHandler::ApplicationCallback * GetAppCallback() override { return mpReadHandlerApplicationCallback; }
InteractionModelEngine * GetInteractionModelEngine() override { return this; }
@@ -637,7 +652,10 @@
// enforce such check based on the configured size. This flag is used for unit tests only, there is another compare time flag
// CHIP_CONFIG_IM_FORCE_FABRIC_QUOTA_CHECK for stress tests.
bool mForceHandlerQuota = false;
-#endif
+#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+ int mSubscriptionResumptionRetrySecondsOverride = -1;
+#endif // CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+#endif // CONFIG_BUILD_FOR_HOST_UNIT_TEST
#if CHIP_CONFIG_PERSIST_SUBSCRIPTIONS && CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
bool HasSubscriptionsToResume();
diff --git a/src/app/SimpleSubscriptionResumptionStorage.cpp b/src/app/SimpleSubscriptionResumptionStorage.cpp
index f6f9697..e233c18 100644
--- a/src/app/SimpleSubscriptionResumptionStorage.cpp
+++ b/src/app/SimpleSubscriptionResumptionStorage.cpp
@@ -46,6 +46,7 @@
constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kAttributeIdTag;
constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventIdTag;
constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kEventPathTypeTag;
+constexpr TLV::Tag SimpleSubscriptionResumptionStorage::kResumptionRetriesTag;
SimpleSubscriptionResumptionStorage::SimpleSubscriptionInfoIterator::SimpleSubscriptionInfoIterator(
SimpleSubscriptionResumptionStorage & storage) :
@@ -252,6 +253,18 @@
}
ReturnErrorOnFailure(reader.ExitContainer(eventsListType));
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+ // If the reader cannot get resumption retries, set it to 0 for subscriptionInfo
+ if (reader.Next(kResumptionRetriesTag) == CHIP_NO_ERROR)
+ {
+ ReturnErrorOnFailure(reader.Get(subscriptionInfo.mResumptionRetries));
+ }
+ else
+ {
+ subscriptionInfo.mResumptionRetries = 0;
+ }
+#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+
ReturnErrorOnFailure(reader.ExitContainer(subscriptionContainerType));
return CHIP_NO_ERROR;
@@ -307,6 +320,9 @@
ReturnErrorOnFailure(writer.EndContainer(eventContainerType));
}
ReturnErrorOnFailure(writer.EndContainer(eventsListType));
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+ ReturnErrorOnFailure(writer.Put(kResumptionRetriesTag, subscriptionInfo.mResumptionRetries));
+#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
ReturnErrorOnFailure(writer.EndContainer(subscriptionContainerType));
diff --git a/src/app/SimpleSubscriptionResumptionStorage.h b/src/app/SimpleSubscriptionResumptionStorage.h
index 7cd6a0a..729f6f9 100644
--- a/src/app/SimpleSubscriptionResumptionStorage.h
+++ b/src/app/SimpleSubscriptionResumptionStorage.h
@@ -132,6 +132,7 @@
static constexpr TLV::Tag kAttributeIdTag = TLV::ContextTag(13);
static constexpr TLV::Tag kEventIdTag = TLV::ContextTag(14);
static constexpr TLV::Tag kEventPathTypeTag = TLV::ContextTag(16);
+ static constexpr TLV::Tag kResumptionRetriesTag = TLV::ContextTag(17);
PersistentStorageDelegate * mStorage;
ObjectPool<SimpleSubscriptionInfoIterator, kIteratorsMax> mSubscriptionInfoIterators;
diff --git a/src/app/SubscriptionResumptionSessionEstablisher.cpp b/src/app/SubscriptionResumptionSessionEstablisher.cpp
index f0d5356..3c6b396 100644
--- a/src/app/SubscriptionResumptionSessionEstablisher.cpp
+++ b/src/app/SubscriptionResumptionSessionEstablisher.cpp
@@ -50,6 +50,9 @@
mSubscriptionInfo.mMinInterval = subscriptionInfo.mMinInterval;
mSubscriptionInfo.mMaxInterval = subscriptionInfo.mMaxInterval;
mSubscriptionInfo.mFabricFiltered = subscriptionInfo.mFabricFiltered;
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+ mSubscriptionInfo.mResumptionRetries = subscriptionInfo.mResumptionRetries;
+#endif
// Copy the Attribute Paths and Event Paths
if (subscriptionInfo.mAttributePaths.AllocatedSize() > 0)
{
@@ -100,6 +103,15 @@
return;
}
readHandler->OnSubscriptionResumed(sessionHandle, *establisher);
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+ // Reset the resumption retries to 0 if subscription is resumed
+ subscriptionInfo.mResumptionRetries = 0;
+ auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage();
+ if (subscriptionResumptionStorage)
+ {
+ subscriptionResumptionStorage->Save(subscriptionInfo);
+ }
+#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
}
void SubscriptionResumptionSessionEstablisher::HandleDeviceConnectionFailure(void * context, const ScopedNodeId & peerId,
@@ -109,12 +121,25 @@
SubscriptionResumptionStorage::SubscriptionInfo & subscriptionInfo = establisher->mSubscriptionInfo;
ChipLogError(DataManagement, "Failed to establish CASE for subscription-resumption with error '%" CHIP_ERROR_FORMAT "'",
error.Format());
- // If the device fails to establish the session, the subscriber might be offline and its subscription read client will
- // be deleted when the device reconnect to the subscriber. This subscription will be never used again. So clean up
- // the persistent subscription information storage.
auto * subscriptionResumptionStorage = InteractionModelEngine::GetInstance()->GetSubscriptionResumptionStorage();
- if (subscriptionResumptionStorage)
+ if (!subscriptionResumptionStorage)
{
+ ChipLogError(DataManagement, "Failed to get subscription resumption storage");
+ return;
+ }
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+ if (subscriptionInfo.mResumptionRetries <= CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION_MAX_FIBONACCI_STEP_INDEX)
+ {
+ InteractionModelEngine::GetInstance()->TryToResumeSubscriptions();
+ subscriptionInfo.mResumptionRetries++;
+ subscriptionResumptionStorage->Save(subscriptionInfo);
+ }
+ else
+#endif // CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+ {
+ // If the device fails to establish the session several times, the subscriber might be offline and its subscription
+ // read client will be deleted when the device reconnects to the subscriber. This subscription will be never used again.
+ // Clean up the persistent subscription information storage.
subscriptionResumptionStorage->Delete(subscriptionInfo.mNodeId, subscriptionInfo.mFabricIndex,
subscriptionInfo.mSubscriptionId);
}
diff --git a/src/app/SubscriptionResumptionStorage.h b/src/app/SubscriptionResumptionStorage.h
index 1934259..74158ec 100644
--- a/src/app/SubscriptionResumptionStorage.h
+++ b/src/app/SubscriptionResumptionStorage.h
@@ -73,6 +73,9 @@
NodeId mNodeId;
FabricIndex mFabricIndex;
SubscriptionId mSubscriptionId;
+#if CHIP_CONFIG_SUBSCRIPTION_TIMEOUT_RESUMPTION
+ uint32_t mResumptionRetries;
+#endif
uint16_t mMinInterval;
uint16_t mMaxInterval;
bool mFabricFiltered;
diff --git a/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py b/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py
new file mode 100755
index 0000000..1f6411f
--- /dev/null
+++ b/src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py
@@ -0,0 +1,125 @@
+#!/usr/bin/env python3
+
+#
+# Copyright (c) 2024 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.
+#
+
+# Commissioning test.
+
+import os
+import sys
+from optparse import OptionParser
+
+from base import BaseTestHelper, FailIfNot, TestFail, TestTimeout, logger
+
+TEST_DISCRIMINATOR = 3840
+TEST_SETUPPIN = 20202021
+
+TEST_ENDPOINT_ID = 0
+
+
+def main():
+ optParser = OptionParser()
+ optParser.add_option(
+ "-t",
+ "--timeout",
+ action="store",
+ dest="testTimeout",
+ default=90,
+ type='int',
+ help="The program will return with timeout after specified seconds.",
+ metavar="<timeout-second>",
+ )
+ optParser.add_option(
+ "-a",
+ "--address",
+ action="store",
+ dest="deviceAddress",
+ default='',
+ type='str',
+ help="Address of the device",
+ metavar="<device-addr>",
+ )
+ optParser.add_option(
+ "--nodeid",
+ action="store",
+ dest="nodeid",
+ default=1,
+ type=int,
+ help="The Node ID issued to the device",
+ metavar="<nodeid>"
+ )
+ optParser.add_option(
+ "--discriminator",
+ action="store",
+ dest="discriminator",
+ default=TEST_DISCRIMINATOR,
+ type=int,
+ help="Discriminator of the device",
+ metavar="<nodeid>"
+ )
+ optParser.add_option(
+ "--setuppin",
+ action="store",
+ dest="setuppin",
+ default=TEST_SETUPPIN,
+ type=int,
+ help="Setup PIN of the device",
+ metavar="<nodeid>"
+ )
+ optParser.add_option(
+ "-p",
+ "--paa-trust-store-path",
+ action="store",
+ dest="paaTrustStorePath",
+ default='',
+ type='str',
+ help="Path that contains valid and trusted PAA Root Certificates.",
+ metavar="<paa-trust-store-path>"
+ )
+
+ (options, remainingArgs) = optParser.parse_args(sys.argv[1:])
+
+ timeoutTicker = TestTimeout(options.testTimeout)
+ timeoutTicker.start()
+
+ test = BaseTestHelper(
+ nodeid=112233, paaTrustStorePath=options.paaTrustStorePath, testCommissioner=True)
+
+ FailIfNot(
+ test.TestOnNetworkCommissioning(options.discriminator, options.setuppin, options.nodeid, options.deviceAddress),
+ "Failed on on-network commissioing")
+ try:
+ test.devCtrl.ZCLSubscribeAttribute("BasicInformation", "NodeLabel", options.nodeid, TEST_ENDPOINT_ID, 1, 2,
+ keepSubscriptions=True, autoResubscribe=False)
+ except Exception as ex:
+ TestFail(f"Failed to subscribe attribute: {ex}")
+
+ timeoutTicker.stop()
+
+ logger.info("Test finished")
+
+ # TODO: Python device controller cannot be shutdown clean sometimes and will block on AsyncDNSResolverSockets shutdown.
+ # Call os._exit(0) to force close it.
+ os._exit(0)
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except Exception as ex:
+ logger.exception(ex)
+ TestFail("Exception occurred when running tests.")
diff --git a/src/test_driver/linux-cirque/SubscriptionResumptionTimeoutTest.py b/src/test_driver/linux-cirque/SubscriptionResumptionTimeoutTest.py
new file mode 100755
index 0000000..d916504
--- /dev/null
+++ b/src/test_driver/linux-cirque/SubscriptionResumptionTimeoutTest.py
@@ -0,0 +1,136 @@
+#!/usr/bin/env python3
+"""
+Copyright (c) 2024 Project CHIP Authors
+
+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 logging
+import os
+import sys
+import time
+
+from helper.CHIPTestBase import CHIPVirtualHome
+
+"""
+Subscription Resumption Timeout Test to validate that the device will keep resuming subscription
+when it receives no status report from the controller.
+Steps for this test:
+ 1. Subcription an attribute on the controller
+ 2. Shutdown the controller
+ 3. Verify that the server app with keep resuming the subscription
+"""
+
+logger = logging.getLogger('SubscriptionResumptionTest')
+logger.setLevel(logging.INFO)
+
+sh = logging.StreamHandler()
+sh.setFormatter(
+ logging.Formatter(
+ '%(asctime)s [%(name)s] %(levelname)s %(message)s'))
+logger.addHandler(sh)
+
+CHIP_PORT = 5540
+
+CIRQUE_URL = "http://localhost:5000"
+CHIP_REPO = os.path.join(os.path.abspath(
+ os.path.dirname(__file__)), "..", "..", "..")
+TEST_EXTPANID = "fedcba9876543210"
+TEST_DISCRIMINATOR = 3840
+MATTER_DEVELOPMENT_PAA_ROOT_CERTS = "credentials/development/paa-root-certs"
+TEST_END_DEVICE_APP = "chip-all-clusters-app"
+
+DEVICE_CONFIG = {
+ 'device0': {
+ 'type': 'MobileDevice',
+ 'base_image': '@default',
+ 'capability': ['TrafficControl', 'Mount'],
+ 'rcp_mode': True,
+ 'docker_network': 'Ipv6',
+ 'traffic_control': {'latencyMs': 25},
+ "mount_pairs": [[CHIP_REPO, CHIP_REPO]],
+ },
+ 'device1': {
+ 'type': 'CHIPEndDevice',
+ 'base_image': '@default',
+ 'capability': ['Thread', 'TrafficControl', 'Mount'],
+ 'rcp_mode': True,
+ 'docker_network': 'Ipv6',
+ 'traffic_control': {'latencyMs': 25},
+ "mount_pairs": [[CHIP_REPO, CHIP_REPO]],
+ }
+}
+
+
+class TestSubscriptionResumptionTimeout(CHIPVirtualHome):
+ def __init__(self, device_config):
+ super().__init__(CIRQUE_URL, device_config)
+ self.logger = logger
+
+ def setup(self):
+ self.initialize_home()
+
+ def test_routine(self):
+ self.run_subscription_resumption_timeout_test()
+
+ def run_subscription_resumption_timeout_test(self):
+ ethernet_ip = [device['description']['ipv6_addr'] for device in self.non_ap_devices
+ if device['type'] == 'CHIPEndDevice'][0]
+ server_ids = [device['id'] for device in self.non_ap_devices
+ if device['type'] == 'CHIPEndDevice']
+ req_ids = [device['id'] for device in self.non_ap_devices
+ if device['type'] == 'MobileDevice']
+
+ server_device_id = server_ids[0]
+ self.execute_device_cmd(
+ server_device_id,
+ ("CHIPCirqueDaemon.py -- run gdb -batch -return-child-result -q -ex \"set pagination off\" "
+ "-ex run -ex \"thread apply all bt\" --args {} --thread --discriminator {} "
+ "--subscription-resumption-retry-interval 5").format(
+ os.path.join(CHIP_REPO, "out/debug/standalone", TEST_END_DEVICE_APP), TEST_DISCRIMINATOR))
+
+ self.reset_thread_devices(server_ids)
+
+ req_device_id = req_ids[0]
+
+ self.execute_device_cmd(req_device_id, "pip3 install {}".format(os.path.join(
+ CHIP_REPO, "out/debug/linux_x64_gcc/controller/python/chip_clusters-0.0-py3-none-any.whl")))
+ self.execute_device_cmd(req_device_id, "pip3 install {}".format(os.path.join(
+ CHIP_REPO, "out/debug/linux_x64_gcc/controller/python/chip_core-0.0-cp37-abi3-linux_x86_64.whl")))
+ self.execute_device_cmd(req_device_id, "pip3 install {}".format(os.path.join(
+ CHIP_REPO, "out/debug/linux_x64_gcc/controller/python/chip_repl-0.0-py3-none-any.whl")))
+
+ command = ("gdb -batch -return-child-result -q -ex run -ex \"thread apply all bt\" "
+ "--args python3 {} -t 300 -a {} --paa-trust-store-path {}").format(
+ os.path.join(
+ CHIP_REPO, "src/controller/python/test/test_scripts/subscription_resumption_timeout_test.py"), ethernet_ip,
+ os.path.join(CHIP_REPO, MATTER_DEVELOPMENT_PAA_ROOT_CERTS))
+ ret = self.execute_device_cmd(req_device_id, command)
+
+ self.assertEqual(ret['return_code'], '0',
+ "Test failed: non-zero return code")
+
+ # Wait for some time so that the sever will try to resume the subscription for several times
+ time.sleep(120)
+
+ # Check the device can resume subscriptions
+ self.logger.info("checking device log for {}".format(
+ self.get_device_pretty_id(server_device_id)))
+ self.assertTrue(self.sequenceMatch(self.get_device_log(server_device_id).decode('utf-8'), [
+ "Schedule subscription resumption when failing to establish session, Retries: 1",
+ "Schedule subscription resumption when failing to establish session, Retries: 2"]),
+ "SubscriptionResumption test failed: cannot find matching string from device {}".format(server_device_id))
+
+
+if __name__ == "__main__":
+ sys.exit(TestSubscriptionResumptionTimeout(DEVICE_CONFIG).run_test())