diff --git a/examples/chip-tool/BUILD.gn b/examples/chip-tool/BUILD.gn index db1bdce4a54119..65704ea910a580 100644 --- a/examples/chip-tool/BUILD.gn +++ b/examples/chip-tool/BUILD.gn @@ -69,6 +69,7 @@ static_library("chip-tool-utils") { public_deps = [ "${chip_root}/src/app/server", + "${chip_root}/src/app/tests/suites/commands/delay", "${chip_root}/src/app/tests/suites/commands/discovery", "${chip_root}/src/app/tests/suites/commands/log", "${chip_root}/src/app/tests/suites/commands/system", diff --git a/examples/chip-tool/commands/tests/TestCommand.cpp b/examples/chip-tool/commands/tests/TestCommand.cpp index addc4ff4efb57c..5be9c79305bd2d 100644 --- a/examples/chip-tool/commands/tests/TestCommand.cpp +++ b/examples/chip-tool/commands/tests/TestCommand.cpp @@ -56,17 +56,6 @@ void TestCommand::OnDeviceConnectionFailureFn(void * context, PeerId peerId, CHI command->ContinueOnChipMainThread(); } -void TestCommand::OnWaitForMsFn(chip::System::Layer * systemLayer, void * context) -{ - auto * command = static_cast(context); - command->NextTest(); -} - -CHIP_ERROR TestCommand::Wait(chip::System::Clock::Timeout duration) -{ - return chip::DeviceLayer::SystemLayer().StartTimer(duration, OnWaitForMsFn, this); -} - void TestCommand::Exit(std::string message) { ChipLogError(chipTool, " ***** Test Failure: %s\n", message.c_str()); diff --git a/examples/chip-tool/commands/tests/TestCommand.h b/examples/chip-tool/commands/tests/TestCommand.h index f798082eb23b86..e9707d65d4ad84 100644 --- a/examples/chip-tool/commands/tests/TestCommand.h +++ b/examples/chip-tool/commands/tests/TestCommand.h @@ -19,6 +19,7 @@ #pragma once #include "../common/CHIPCommand.h" +#include #include #include #include @@ -36,7 +37,8 @@ class TestCommand : public CHIPCommand, public PICSChecker, public LogCommands, public DiscoveryCommands, - public SystemCommands + public SystemCommands, + public DelayCommands { public: TestCommand(const char * commandName, CredentialIssuerCommands * credsIssuerConfig) : @@ -56,18 +58,16 @@ class TestCommand : public CHIPCommand, virtual void NextTest() = 0; - /////////// GlobalCommands Interface ///////// - CHIP_ERROR Wait(chip::System::Clock::Timeout ms); - CHIP_ERROR WaitForMs(uint16_t ms) { return Wait(chip::System::Clock::Milliseconds32(ms)); } - CHIP_ERROR WaitForCommissionee(); - protected: + /////////// DelayCommands Interface ///////// + CHIP_ERROR WaitForCommissionee() override; + void OnWaitForMs() override { NextTest(); }; + std::map mDevices; chip::NodeId mNodeId; static void OnDeviceConnectedFn(void * context, chip::OperationalDeviceProxy * device); static void OnDeviceConnectionFailureFn(void * context, PeerId peerId, CHIP_ERROR error); - static void OnWaitForMsFn(chip::System::Layer * systemLayer, void * context); CHIP_ERROR ContinueOnChipMainThread() override { return WaitForMs(0); }; diff --git a/examples/chip-tool/templates/tests.js b/examples/chip-tool/templates/tests.js index 9fcf9875a52069..53bb34a8d8da06 100644 --- a/examples/chip-tool/templates/tests.js +++ b/examples/chip-tool/templates/tests.js @@ -250,6 +250,7 @@ function getTests() 'TestClusterComplexTypes', 'TestConstraints', 'TestDelayCommands', + 'TestDiscovery', 'TestLogCommands', 'TestSaveAs', 'TestConfigVariables', diff --git a/examples/placeholder/linux/BUILD.gn b/examples/placeholder/linux/BUILD.gn index dbcfb549509fe2..eb556088aded57 100644 --- a/examples/placeholder/linux/BUILD.gn +++ b/examples/placeholder/linux/BUILD.gn @@ -41,6 +41,7 @@ executable("chip-${chip_tests_zap_config}") { deps = [ ":configuration", "${chip_root}/examples/platform/linux:app-main", + "${chip_root}/src/app/tests/suites/commands/delay", "${chip_root}/src/app/tests/suites/commands/discovery", "${chip_root}/src/app/tests/suites/commands/log", "${chip_root}/src/app/tests/suites/pics", diff --git a/examples/placeholder/linux/include/TestCommand.h b/examples/placeholder/linux/include/TestCommand.h index e03f5e39ce4d54..5f50e3f33565bd 100644 --- a/examples/placeholder/linux/include/TestCommand.h +++ b/examples/placeholder/linux/include/TestCommand.h @@ -23,6 +23,7 @@ #include #include +#include #include #include #include @@ -35,18 +36,14 @@ constexpr const char kIdentityAlpha[] = ""; constexpr const char kIdentityBeta[] = ""; constexpr const char kIdentityGamma[] = ""; -class TestCommand : public PICSChecker, public LogCommands, public DiscoveryCommands +class TestCommand : public PICSChecker, public LogCommands, public DiscoveryCommands, public DelayCommands { public: TestCommand(const char * commandName) : mCommandPath(0, 0, 0), mAttributePath(0, 0, 0) {} virtual ~TestCommand() {} virtual void NextTest() = 0; - CHIP_ERROR WaitMS(chip::System::Clock::Timeout ms) - { - return chip::DeviceLayer::SystemLayer().StartTimer(ms, OnWaitForMsFn, this); - } - CHIP_ERROR WaitForMs(uint16_t ms) { return WaitMS(chip::System::Clock::Milliseconds32(ms)); } + void SetCommandExitStatus(CHIP_ERROR status) { chip::DeviceLayer::PlatformMgr().StopEventLoopTask(); @@ -85,12 +82,6 @@ class TestCommand : public PICSChecker, public LogCommands, public DiscoveryComm chip::DeviceLayer::PlatformMgr().RemoveEventHandler(OnPlatformEvent, context); } - CHIP_ERROR WaitForCommissioning() - { - isRunning = false; - return chip::DeviceLayer::PlatformMgr().AddEventHandler(OnPlatformEvent, reinterpret_cast(this)); - } - static void OnPlatformEvent(const chip::DeviceLayer::ChipDeviceEvent * event, intptr_t arg) { switch (event->Type) @@ -131,11 +122,6 @@ class TestCommand : public PICSChecker, public LogCommands, public DiscoveryComm mCommandPath = chip::app::ConcreteCommandPath(0, 0, 0); mAttributePath = chip::app::ConcreteAttributePath(0, 0, 0); } - static void OnWaitForMsFn(chip::System::Layer * systemLayer, void * context) - { - auto * command = static_cast(context); - command->NextTest(); - } std::atomic_bool isRunning{ true }; @@ -145,4 +131,13 @@ class TestCommand : public PICSChecker, public LogCommands, public DiscoveryComm chip::Optional mEndpointId; void SetIdentity(const char * name){}; void Wait(){}; + + /////////// DelayCommands Interface ///////// + void OnWaitForMs() override { NextTest(); } + + CHIP_ERROR WaitForCommissioning() override + { + isRunning = false; + return chip::DeviceLayer::PlatformMgr().AddEventHandler(OnPlatformEvent, reinterpret_cast(this)); + } }; diff --git a/scripts/tests/chiptest/accessories.py b/scripts/tests/chiptest/accessories.py index cc6b095ed531b3..e538d0e8fae165 100644 --- a/scripts/tests/chiptest/accessories.py +++ b/scripts/tests/chiptest/accessories.py @@ -91,6 +91,18 @@ def factoryReset(self, name): return accessory.factoryReset() return False + def waitForCommissionableAdvertisement(self, name): + accessory = self.__accessories[name] + if accessory: + return accessory.waitForCommissionableAdvertisement() + return False + + def waitForOperationalAdvertisement(self, name): + accessory = self.__accessories[name] + if accessory: + return accessory.waitForOperationalAdvertisement() + return False + def ping(self): return True @@ -101,6 +113,10 @@ def __startXMLRPCServer(self): self.server.register_function(self.stop, 'stop') self.server.register_function(self.reboot, 'reboot') self.server.register_function(self.factoryReset, 'factoryReset') + self.server.register_function( + self.waitForCommissionableAdvertisement, 'waitForCommissionableAdvertisement') + self.server.register_function( + self.waitForOperationalAdvertisement, 'waitForOperationalAdvertisement') self.server.register_function(self.ping, 'ping') self.server_thread = threading.Thread(target=self.__handle_request) diff --git a/scripts/tests/chiptest/runner.py b/scripts/tests/chiptest/runner.py index b6acca0bdc7999..0429c763f36054 100644 --- a/scripts/tests/chiptest/runner.py +++ b/scripts/tests/chiptest/runner.py @@ -47,8 +47,11 @@ def __init__(self, level, capture_delegate=None, name=None): self.start() - def CapturedLogContains(self, txt: str): - return any(txt in l for l in self.captured_logs) + def CapturedLogContains(self, txt: str, index=0): + for i, line in enumerate(self.captured_logs[index:]): + if txt in line: + return True, i + return False, len(self.captured_logs) def FindLastMatchingLine(self, matcher): for l in reversed(self.captured_logs): diff --git a/scripts/tests/chiptest/test_definition.py b/scripts/tests/chiptest/test_definition.py index 71020b0eae4736..cd9b8162fcee2b 100644 --- a/scripts/tests/chiptest/test_definition.py +++ b/scripts/tests/chiptest/test_definition.py @@ -32,18 +32,21 @@ class App: def __init__(self, runner, command): self.process = None + self.outpipe = None self.runner = runner self.command = command self.stopped = False + self.lastLogIndex = 0 def start(self, discriminator): if not self.process: self.process = None process, outpipe, errpipe = self.__startServer( self.runner, self.command, discriminator) - self.__waitForServerReady(process, outpipe) + self.waitForAnyAdvertisement(process, outpipe) self.__updateSetUpCode(outpipe) self.process = process + self.outpipe = outpipe self.stopped = False return True return False @@ -54,6 +57,7 @@ def stop(self): self.process.kill() self.process.wait(10) self.process = None + self.outpipe = None return True return False @@ -71,6 +75,19 @@ def factoryReset(self): return True + def waitForAnyAdvertisement(self, process, outpipe): + self.__waitFor("mDNS service published:", process, outpipe) + + def waitForCommissionableAdvertisement(self): + self.__waitFor("mDNS service published: _matterc._udp", + self.process, self.outpipe) + return True + + def waitForOperationalAdvertisement(self): + self.__waitFor("mDNS service published: _matter._tcp", + self.process, self.outpipe) + return True + def poll(self): # When the server is manually stopped, process polling is overriden so the other # processes that depends on the accessory beeing alive does not stop. @@ -92,21 +109,25 @@ def __startServer(self, runner, command, discriminator): app_cmd = command + ['--discriminator', str(discriminator)] return runner.RunSubprocess(app_cmd, name='APP ', wait=False) - def __waitForServerReady(self, server_process, outpipe): - logging.debug('Waiting for server to listen.') + def __waitFor(self, waitForString, server_process, outpipe): + logging.debug('Waiting for %s' % waitForString) + start_time = time.time() - server_is_listening = outpipe.CapturedLogContains("Server Listening") - while not server_is_listening: + ready, self.lastLogIndex = outpipe.CapturedLogContains( + waitForString, self.lastLogIndex) + while not ready: if server_process.poll() is not None: - died_str = 'Server died during startup, returncode %d' % server_process.returncode + died_str = 'Server died while waiting for %s, returncode %d' % ( + waitForString, server_process.returncode) logging.error(died_str) raise Exception(died_str) if time.time() - start_time > 10: - raise Exception('Timeout for server listening') + raise Exception('Timeout while waiting for %s' % waitForString) time.sleep(0.1) - server_is_listening = outpipe.CapturedLogContains( - "Server Listening") - logging.debug('Server is listening. Can proceed.') + ready, self.lastLogIndex = outpipe.CapturedLogContains( + waitForString, self.lastLogIndex) + + logging.debug('Success waiting for: %s' % waitForString) def __updateSetUpCode(self, outpipe): qrLine = outpipe.FindLastMatchingLine('.*SetupQRCode: *\\[(.*)]') diff --git a/src/app/tests/suites/TestDiscovery.yaml b/src/app/tests/suites/TestDiscovery.yaml index 2987f8ee77908b..a55914949a10b2 100644 --- a/src/app/tests/suites/TestDiscovery.yaml +++ b/src/app/tests/suites/TestDiscovery.yaml @@ -24,7 +24,7 @@ config: defaultValue: 65521 productId: type: INT16U - defaultValue: 32768 + defaultValue: 32769 deviceType: type: INT16U defaultValue: 5 @@ -51,13 +51,9 @@ tests: - name: "CommissioningTimeout" value: 120 - - label: "Wait 1000ms for the mdns advertisement to be published" + - label: "Wait Commissionable advertisement" cluster: "DelayCommands" - command: "WaitForMs" - arguments: - values: - - name: "ms" - value: 1000 + command: "WaitForCommissionableAdvertisement" - label: "Check Instance Name" cluster: "DiscoveryCommands" @@ -292,13 +288,9 @@ tests: - name: "CommissioningTimeout" value: 120 - - label: "Wait 1000ms for the mdns advertisement to be published" + - label: "Wait Commissionable advertisement" cluster: "DelayCommands" - command: "WaitForMs" - arguments: - values: - - name: "ms" - value: 1000 + command: "WaitForCommissionableAdvertisement" - label: "Check Instance Name" cluster: "DiscoveryCommands" diff --git a/src/app/tests/suites/commands/delay/BUILD.gn b/src/app/tests/suites/commands/delay/BUILD.gn new file mode 100644 index 00000000000000..b744edcf7a8cbb --- /dev/null +++ b/src/app/tests/suites/commands/delay/BUILD.gn @@ -0,0 +1,33 @@ +# Copyright (c) 2022 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("//build_overrides/build.gni") +import("//build_overrides/chip.gni") + +static_library("delay") { + output_name = "libDelayCommands" + + sources = [ + "DelayCommands.cpp", + "DelayCommands.h", + ] + + cflags = [ "-Wconversion" ] + + public_deps = [ + "${chip_root}/src/lib/support", + "${chip_root}/src/platform", + "${chip_root}/src/system", + ] +} diff --git a/src/app/tests/suites/commands/delay/DelayCommands.cpp b/src/app/tests/suites/commands/delay/DelayCommands.cpp new file mode 100644 index 00000000000000..141067f45b7b2a --- /dev/null +++ b/src/app/tests/suites/commands/delay/DelayCommands.cpp @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2022 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. + * + */ + +#include "DelayCommands.h" + +namespace { +const char basePath[] = "./src/app/tests/suites/commands/delay/scripts/"; +const char * getScriptsFolder() +{ + return basePath; +} +} // namespace + +CHIP_ERROR DelayCommands::WaitForMs(uint16_t ms) +{ + const auto duration = chip::System::Clock::Milliseconds32(ms); + return chip::DeviceLayer::SystemLayer().StartTimer(duration, OnWaitForMsFn, this); +} + +void DelayCommands::OnWaitForMsFn(chip::System::Layer * systemLayer, void * context) +{ + auto * command = static_cast(context); + command->OnWaitForMs(); +} + +CHIP_ERROR DelayCommands::WaitForCommissionableAdvertisement() +{ + const char * scriptDir = getScriptsFolder(); + constexpr const char * scriptName = "WaitForCommissionableAdvertisement.py"; + + char command[128]; + VerifyOrReturnError(snprintf(command, sizeof(command), "%s%s", scriptDir, scriptName) >= 0, CHIP_ERROR_INTERNAL); + return RunInternal(command); +} + +CHIP_ERROR DelayCommands::WaitForOperationalAdvertisement() +{ + const char * scriptDir = getScriptsFolder(); + constexpr const char * scriptName = "WaitForOperationalAdvertisement.py"; + + char command[128]; + VerifyOrReturnError(snprintf(command, sizeof(command), "%s%s", scriptDir, scriptName) >= 0, CHIP_ERROR_INTERNAL); + return RunInternal(command); +} + +CHIP_ERROR DelayCommands::RunInternal(const char * command) +{ + VerifyOrReturnError(system(command) == 0, CHIP_ERROR_INTERNAL); + return ContinueOnChipMainThread(); +} diff --git a/src/app/tests/suites/commands/delay/DelayCommands.h b/src/app/tests/suites/commands/delay/DelayCommands.h new file mode 100644 index 00000000000000..324ed483497a48 --- /dev/null +++ b/src/app/tests/suites/commands/delay/DelayCommands.h @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2022 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. + * + */ + +#pragma once + +#include +#include +#include + +class DelayCommands +{ +public: + DelayCommands(){}; + virtual ~DelayCommands(){}; + + virtual CHIP_ERROR ContinueOnChipMainThread() = 0; + virtual void OnWaitForMs() = 0; + + virtual CHIP_ERROR WaitForCommissionee() { return CHIP_ERROR_NOT_IMPLEMENTED; }; + virtual CHIP_ERROR WaitForCommissioning() { return CHIP_ERROR_NOT_IMPLEMENTED; }; + CHIP_ERROR WaitForMs(uint16_t ms); + CHIP_ERROR WaitForCommissionableAdvertisement(); + CHIP_ERROR WaitForOperationalAdvertisement(); + +private: + static void OnWaitForMsFn(chip::System::Layer * systemLayer, void * context); + CHIP_ERROR RunInternal(const char * command); +}; diff --git a/src/app/tests/suites/commands/delay/scripts/WaitForCommissionableAdvertisement.py b/src/app/tests/suites/commands/delay/scripts/WaitForCommissionableAdvertisement.py new file mode 100755 index 00000000000000..b4223552abf9e1 --- /dev/null +++ b/src/app/tests/suites/commands/delay/scripts/WaitForCommissionableAdvertisement.py @@ -0,0 +1,27 @@ +#!/usr/bin/env -S python3 -B + +# Copyright (c) 2022 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 sys +import xmlrpc.client + +IP = '127.0.0.1' +PORT = 9000 + +if sys.platform == 'linux': + IP = '10.10.10.5' + +with xmlrpc.client.ServerProxy('http://' + IP + ':' + str(PORT) + '/', allow_none=True) as proxy: + proxy.waitForCommissionableAdvertisement('default') diff --git a/src/app/tests/suites/commands/delay/scripts/WaitForOperationalAdvertisement.py b/src/app/tests/suites/commands/delay/scripts/WaitForOperationalAdvertisement.py new file mode 100755 index 00000000000000..bc4635f53f257b --- /dev/null +++ b/src/app/tests/suites/commands/delay/scripts/WaitForOperationalAdvertisement.py @@ -0,0 +1,27 @@ +#!/usr/bin/env -S python3 -B + +# Copyright (c) 2022 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 sys +import xmlrpc.client + +IP = '127.0.0.1' +PORT = 9000 + +if sys.platform == 'linux': + IP = '10.10.10.5' + +with xmlrpc.client.ServerProxy('http://' + IP + ':' + str(PORT) + '/', allow_none=True) as proxy: + proxy.waitForOperationalAdvertisement('default') diff --git a/src/app/zap-templates/common/simulated-clusters/clusters/DelayCommands.js b/src/app/zap-templates/common/simulated-clusters/clusters/DelayCommands.js index a2026851141905..7c064e38395eb8 100644 --- a/src/app/zap-templates/common/simulated-clusters/clusters/DelayCommands.js +++ b/src/app/zap-templates/common/simulated-clusters/clusters/DelayCommands.js @@ -42,8 +42,21 @@ const WaitForCommissionee = { response : { arguments : [] } }; -const name = 'DelayCommands'; -const commands = [ WaitForMs, WaitForCommissioning, WaitForCommissionee ]; +const WaitForCommissionableAdvertisement = { + name : 'WaitForCommissionableAdvertisement', + arguments : [], + response : { arguments : [] } +}; + +const WaitForOperationalAdvertisement = { + name : 'WaitForOperationalAdvertisement', + arguments : [], + response : { arguments : [] } +}; + +const name = 'DelayCommands'; +const commands = + [ WaitForMs, WaitForCommissioning, WaitForCommissionee, WaitForCommissionableAdvertisement, WaitForOperationalAdvertisement ]; const DelayCommands = { name, diff --git a/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp b/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp index e9b9137c45eaa5..7ef2e5affae4a0 100644 --- a/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp +++ b/src/lib/dnssd/Advertiser_ImplMinimalMdns.cpp @@ -420,6 +420,8 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const OperationalAdvertisingParameters & // TODO - Don't announce records that haven't been updated. AdvertiseRecords(); + ChipLogProgress(Discovery, "mDNS service published: %s.%s", instanceName.names[1], instanceName.names[2]); + return CHIP_NO_ERROR; } @@ -609,6 +611,8 @@ CHIP_ERROR AdvertiserMinMdns::Advertise(const CommissionAdvertisingParameters & // TODO - Don't announce records that haven't been updated. AdvertiseRecords(); + ChipLogProgress(Discovery, "mDNS service published: %s.%s", instanceName.names[1], instanceName.names[2]); + return CHIP_NO_ERROR; } diff --git a/src/lib/dnssd/Discovery_ImplPlatform.cpp b/src/lib/dnssd/Discovery_ImplPlatform.cpp index 3584e8f47a5178..90f1149370a46f 100644 --- a/src/lib/dnssd/Discovery_ImplPlatform.cpp +++ b/src/lib/dnssd/Discovery_ImplPlatform.cpp @@ -398,6 +398,18 @@ CHIP_ERROR DiscoveryImplPlatform::GetCommissionableInstanceName(char * instanceN instanceName, maxLength); } +void DiscoveryImplPlatform::HandleDnssdPublish(void * context, const char * type, CHIP_ERROR error) +{ + if (CHIP_NO_ERROR == error) + { + ChipLogProgress(Discovery, "mDNS service published: %s", type); + } + else + { + ChipLogProgress(Discovery, "mDNS service published error: %s", chip::ErrorStr(error)); + } +} + CHIP_ERROR DiscoveryImplPlatform::PublishService(const char * serviceType, TextEntry * textEntries, size_t textEntrySize, const char ** subTypes, size_t subTypeSize, const OperationalAdvertisingParameters & params) @@ -435,7 +447,7 @@ CHIP_ERROR DiscoveryImplPlatform::PublishService(const char * serviceType, TextE service.mSubTypes = subTypes; service.mSubTypeSize = subTypeSize; - ReturnErrorOnFailure(ChipDnssdPublishService(&service)); + ReturnErrorOnFailure(ChipDnssdPublishService(&service, HandleDnssdPublish, this)); #ifdef DETAIL_LOGGING printf("printEntries port=%u, mTextEntrySize=%zu, mSubTypeSize=%zu\n", port, textEntrySize, subTypeSize); diff --git a/src/lib/dnssd/Discovery_ImplPlatform.h b/src/lib/dnssd/Discovery_ImplPlatform.h index 2aa8246c6e5251..70ae983fb5982c 100644 --- a/src/lib/dnssd/Discovery_ImplPlatform.h +++ b/src/lib/dnssd/Discovery_ImplPlatform.h @@ -68,6 +68,7 @@ class DiscoveryImplPlatform : public ServiceAdvertiser, public Resolver static void HandleDnssdInit(void * context, CHIP_ERROR initError); static void HandleDnssdError(void * context, CHIP_ERROR initError); + static void HandleDnssdPublish(void * context, const char * type, CHIP_ERROR error); static CHIP_ERROR GenerateRotatingDeviceId(char rotatingDeviceIdHexBuffer[], size_t & rotatingDeviceIdHexBufferSize); CHIP_ERROR PublishService(const char * serviceType, TextEntry * textEntries, size_t textEntrySize, const char ** subTypes, size_t subTypeSize, const OperationalAdvertisingParameters & params); diff --git a/src/lib/dnssd/platform/Dnssd.h b/src/lib/dnssd/platform/Dnssd.h index 756bd4752fec2f..add47b59bd65bb 100644 --- a/src/lib/dnssd/platform/Dnssd.h +++ b/src/lib/dnssd/platform/Dnssd.h @@ -107,6 +107,21 @@ using DnssdResolveCallback = void (*)(void * context, DnssdService * result, CHI */ using DnssdBrowseCallback = void (*)(void * context, DnssdService * services, size_t servicesSize, CHIP_ERROR error); +/** + * The callback function for mDNS publish. + * + * Will be called when publishing succeeds or fails. + * + * The callback function SHALL NOT take the ownership of the service pointer or + * any pointer inside this structure. + * + * @param[in] context The context passed to ChipDnssdPublish. + * @param[in] type The published type if no errors has occured, nullptr otherwise. + * @param[in] error The error code. + * + */ +using DnssdPublishCallback = void (*)(void * context, const char * type, CHIP_ERROR error); + using DnssdAsyncReturnCallback = void (*)(void * context, CHIP_ERROR error); /** @@ -150,13 +165,15 @@ CHIP_ERROR ChipDnssdRemoveServices(); * This function will NOT take the ownership of service->mTextEntries memory. * * @param[in] service The service entry. + * @param[in] callback The callback to call when the service is published. + * @param[in] context The context passed to the callback. * * @retval CHIP_NO_ERROR The publish succeeds. * @retval CHIP_ERROR_INVALID_ARGUMENT The service is nullptr. * @retval Error code The publish fails. * */ -CHIP_ERROR ChipDnssdPublishService(const DnssdService * service); +CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback = nullptr, void * context = nullptr); /** * Finalizes updating advertised services. diff --git a/src/platform/Ameba/DnssdImpl.cpp b/src/platform/Ameba/DnssdImpl.cpp index f6f700e03c1ab7..9d885676abd488 100755 --- a/src/platform/Ameba/DnssdImpl.cpp +++ b/src/platform/Ameba/DnssdImpl.cpp @@ -61,7 +61,7 @@ const char * GetProtocolString(DnssdServiceProtocol protocol) return protocol == DnssdServiceProtocol::kDnssdProtocolTcp ? "_tcp" : "_udp"; } -CHIP_ERROR ChipDnssdPublishService(const DnssdService * service) +CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback, void * context) { CHIP_ERROR error = CHIP_NO_ERROR; diff --git a/src/platform/Darwin/DnssdImpl.cpp b/src/platform/Darwin/DnssdImpl.cpp index 931631307b3cd0..186cde4fe47bda 100644 --- a/src/platform/Darwin/DnssdImpl.cpp +++ b/src/platform/Darwin/DnssdImpl.cpp @@ -236,9 +236,11 @@ bool CheckForSuccess(GenericContext * context, const char * name, DNSServiceErro { switch (context->type) { - case ContextType::Register: - // Nothing special to do. Maybe ChipDnssdPublishService should take a callback ? + case ContextType::Register: { + RegisterContext * registerContext = reinterpret_cast(context); + registerContext->callback(registerContext->context, nullptr, CHIP_ERROR_INTERNAL); break; + } case ContextType::Browse: { BrowseContext * browseContext = reinterpret_cast(context); browseContext->callback(browseContext->context, nullptr, 0, CHIP_ERROR_INTERNAL); @@ -275,9 +277,12 @@ static void OnRegister(DNSServiceRef sdRef, DNSServiceFlags flags, DNSServiceErr VerifyOrReturn(CheckForSuccess(sdCtx, __func__, err)); ChipLogDetail(DeviceLayer, "Mdns: %s name: %s, type: %s, domain: %s, flags: %d", __func__, name, type, domain, flags); + + sdCtx->callback(sdCtx->context, type, CHIP_NO_ERROR); }; -CHIP_ERROR Register(uint32_t interfaceId, const char * type, const char * name, uint16_t port, TXTRecordRef * recordRef) +CHIP_ERROR Register(void * context, DnssdPublishCallback callback, uint32_t interfaceId, const char * type, const char * name, + uint16_t port, TXTRecordRef * recordRef) { DNSServiceErrorType err; DNSServiceRef sdRef; @@ -294,7 +299,7 @@ CHIP_ERROR Register(uint32_t interfaceId, const char * type, const char * name, return CHIP_NO_ERROR; } - sdCtx = chip::Platform::New(type, nullptr); + sdCtx = chip::Platform::New(type, callback, context); err = DNSServiceRegister(&sdRef, 0 /* flags */, interfaceId, name, type, kLocalDot, NULL, ntohs(port), recordLen, recordBytesPtr, OnRegister, sdCtx); TXTRecordDeallocate(recordRef); @@ -541,10 +546,11 @@ CHIP_ERROR ChipDnssdShutdown() return CHIP_NO_ERROR; } -CHIP_ERROR ChipDnssdPublishService(const DnssdService * service) +CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback, void * context) { VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT); if (strcmp(service->mHostName, "") != 0) { @@ -559,7 +565,7 @@ CHIP_ERROR ChipDnssdPublishService(const DnssdService * service) ChipLogProgress(DeviceLayer, "Publishing service %s on port %u with type: %s on interface id: %" PRIu32, service->mName, service->mPort, regtype.c_str(), interfaceId); - return Register(interfaceId, regtype.c_str(), service->mName, service->mPort, &record); + return Register(context, callback, interfaceId, regtype.c_str(), service->mName, service->mPort, &record); } CHIP_ERROR ChipDnssdRemoveServices() diff --git a/src/platform/Darwin/DnssdImpl.h b/src/platform/Darwin/DnssdImpl.h index 030e7fabe3b42f..a0f1bdc4be78cb 100644 --- a/src/platform/Darwin/DnssdImpl.h +++ b/src/platform/Darwin/DnssdImpl.h @@ -42,12 +42,15 @@ struct GenericContext struct RegisterContext : public GenericContext { + DnssdPublishCallback callback; char mType[kDnssdTypeMaxSize + 1]; - RegisterContext(const char * sType, void * cbContext) + + RegisterContext(const char * sType, DnssdPublishCallback cb, void * cbContext) { type = ContextType::Register; strncpy(mType, sType, sizeof(mType)); - context = cbContext; + context = cbContext; + callback = cb; } bool matches(const char * sType) { return (strcmp(mType, sType) == 0); } diff --git a/src/platform/ESP32/DnssdImpl.cpp b/src/platform/ESP32/DnssdImpl.cpp index 2442c3cf3154d6..5391f2bb0d24ca 100644 --- a/src/platform/ESP32/DnssdImpl.cpp +++ b/src/platform/ESP32/DnssdImpl.cpp @@ -65,7 +65,7 @@ static const char * GetProtocolString(DnssdServiceProtocol protocol) return protocol == DnssdServiceProtocol::kDnssdProtocolTcp ? "_tcp" : "_udp"; } -CHIP_ERROR ChipDnssdPublishService(const DnssdService * service) +CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback, void * context) { CHIP_ERROR error = CHIP_NO_ERROR; mdns_txt_item_t * items = nullptr; diff --git a/src/platform/Linux/DnssdImpl.cpp b/src/platform/Linux/DnssdImpl.cpp index 7709a8eee6a0c1..5c25004fbd62e9 100644 --- a/src/platform/Linux/DnssdImpl.cpp +++ b/src/platform/Linux/DnssdImpl.cpp @@ -453,7 +453,7 @@ void MdnsAvahi::HandleGroupState(AvahiEntryGroup * group, AvahiEntryGroupState s } } -CHIP_ERROR MdnsAvahi::PublishService(const DnssdService & service) +CHIP_ERROR MdnsAvahi::PublishService(const DnssdService & service, DnssdPublishCallback callback, void * context) { std::ostringstream keyBuilder; std::string key; @@ -505,9 +505,18 @@ CHIP_ERROR MdnsAvahi::PublishService(const DnssdService & service) { avahi_string_list_free(text); } - if (error != CHIP_NO_ERROR) + + // Ideally the callback would be called from `HandleGroupState` when the `AVAHI_ENTRY_GROUP_ESTABLISHED` state + // is received. But the current code use the `userdata` field to pass a pointer to the current MdnsAvahi instance + // and this is all comes from MdnsAvahi::Init that does not have any clue about the `type` that *will* be published. + // The code needs to be updated to support that callback properly. + if (CHIP_NO_ERROR == error) { - ChipLogError(DeviceLayer, "Avahi publish service failed: %" CHIP_ERROR_FORMAT, error.Format()); + callback(context, type.c_str(), CHIP_NO_ERROR); + } + else + { + callback(context, nullptr, error); } return error; @@ -811,13 +820,16 @@ CHIP_ERROR ChipDnssdShutdown() return MdnsAvahi::GetInstance().Shutdown(); } -CHIP_ERROR ChipDnssdPublishService(const DnssdService * service) +CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback, void * context) { + VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT); + VerifyOrReturnError(callback != nullptr, CHIP_ERROR_INVALID_ARGUMENT); if (strcmp(service->mHostName, "") != 0) { ReturnErrorOnFailure(MdnsAvahi::GetInstance().SetHostname(service->mHostName)); } - return MdnsAvahi::GetInstance().PublishService(*service); + + return MdnsAvahi::GetInstance().PublishService(*service, callback, context); } CHIP_ERROR ChipDnssdRemoveServices() diff --git a/src/platform/Linux/DnssdImpl.h b/src/platform/Linux/DnssdImpl.h index 369ebfc569f602..ffc6d47a2b5d3c 100644 --- a/src/platform/Linux/DnssdImpl.h +++ b/src/platform/Linux/DnssdImpl.h @@ -107,7 +107,7 @@ class MdnsAvahi CHIP_ERROR Init(DnssdAsyncReturnCallback initCallback, DnssdAsyncReturnCallback errorCallback, void * context); CHIP_ERROR Shutdown(); CHIP_ERROR SetHostname(const char * hostname); - CHIP_ERROR PublishService(const DnssdService & service); + CHIP_ERROR PublishService(const DnssdService & service, DnssdPublishCallback callback, void * context); CHIP_ERROR StopPublish(); CHIP_ERROR Browse(const char * type, DnssdServiceProtocol protocol, chip::Inet::IPAddressType addressType, chip::Inet::InterfaceId interface, DnssdBrowseCallback callback, void * context); diff --git a/src/platform/OpenThread/DnssdImpl.cpp b/src/platform/OpenThread/DnssdImpl.cpp index bffc0fdcc96950..0faed87df2958e 100644 --- a/src/platform/OpenThread/DnssdImpl.cpp +++ b/src/platform/OpenThread/DnssdImpl.cpp @@ -49,7 +49,7 @@ const char * GetProtocolString(DnssdServiceProtocol protocol) return protocol == DnssdServiceProtocol::kDnssdProtocolUdp ? "_udp" : "_tcp"; } -CHIP_ERROR ChipDnssdPublishService(const DnssdService * service) +CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback, void * context) { #if CHIP_DEVICE_CONFIG_ENABLE_THREAD_SRP_CLIENT ReturnErrorCodeIf(service == nullptr, CHIP_ERROR_INVALID_ARGUMENT); diff --git a/src/platform/Tizen/DnssdImpl.cpp b/src/platform/Tizen/DnssdImpl.cpp index 7eaaeb409e6f30..00b9177e1ba374 100644 --- a/src/platform/Tizen/DnssdImpl.cpp +++ b/src/platform/Tizen/DnssdImpl.cpp @@ -724,7 +724,7 @@ CHIP_ERROR ChipDnssdSetHostname(const char * hostname) return CHIP_NO_ERROR; } -CHIP_ERROR ChipDnssdPublishService(const DnssdService * service) +CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback, void * context) { VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(IsSupportedProtocol(service->mProtocol), CHIP_ERROR_INVALID_ARGUMENT); diff --git a/src/platform/android/DnssdImpl.cpp b/src/platform/android/DnssdImpl.cpp index f0a0bace59ebb7..429ff609bf6dd2 100644 --- a/src/platform/android/DnssdImpl.cpp +++ b/src/platform/android/DnssdImpl.cpp @@ -81,7 +81,7 @@ CHIP_ERROR ChipDnssdRemoveServices() return CHIP_NO_ERROR; } -CHIP_ERROR ChipDnssdPublishService(const DnssdService * service) +CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback, void * context) { VerifyOrReturnError(service != nullptr, CHIP_ERROR_INVALID_ARGUMENT); VerifyOrReturnError(sResolverObject != nullptr && sPublishMethod != nullptr, CHIP_ERROR_INCORRECT_STATE); diff --git a/src/platform/fake/DnssdImpl.cpp b/src/platform/fake/DnssdImpl.cpp index 553d04b461e030..2dd189dd22accd 100644 --- a/src/platform/fake/DnssdImpl.cpp +++ b/src/platform/fake/DnssdImpl.cpp @@ -101,7 +101,7 @@ CHIP_ERROR ChipDnssdShutdown() return CHIP_NO_ERROR; } -CHIP_ERROR ChipDnssdPublishService(const DnssdService * service) +CHIP_ERROR ChipDnssdPublishService(const DnssdService * service, DnssdPublishCallback callback, void * context) { return test::CheckExpected(test::CallType::kStart, service); } diff --git a/src/platform/tests/TestDnssd.cpp b/src/platform/tests/TestDnssd.cpp index c34c0ced39747e..5a252f8ee1ca17 100644 --- a/src/platform/tests/TestDnssd.cpp +++ b/src/platform/tests/TestDnssd.cpp @@ -46,6 +46,8 @@ static void HandleBrowse(void * context, DnssdService * services, size_t service } } +static void HandlePublish(void * context, const char * type, CHIP_ERROR error) {} + static void InitCallback(void * context, CHIP_ERROR error) { DnssdService service; @@ -70,7 +72,7 @@ static void InitCallback(void * context, CHIP_ERROR error) service.mSubTypes = nullptr; service.mSubTypeSize = 0; - NL_TEST_ASSERT(suite, ChipDnssdPublishService(&service) == CHIP_NO_ERROR); + NL_TEST_ASSERT(suite, ChipDnssdPublishService(&service, HandlePublish) == CHIP_NO_ERROR); ChipDnssdBrowse("_mock", DnssdServiceProtocol::kDnssdProtocolTcp, chip::Inet::IPAddressType::kAny, chip::Inet::InterfaceId::Null(), HandleBrowse, suite); } diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index 9080bb54a768c9..612302bdd36e89 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -157,6 +157,7 @@ class TestList : public Command printf("TestClusterComplexTypes\n"); printf("TestConstraints\n"); printf("TestDelayCommands\n"); + printf("TestDiscovery\n"); printf("TestLogCommands\n"); printf("TestSaveAs\n"); printf("TestConfigVariables\n"); @@ -71801,6 +71802,543 @@ class TestDelayCommands : public TestCommand } }; +class TestDiscovery : public TestCommand +{ +public: + TestDiscovery(CredentialIssuerCommands * credsIssuerConfig) : TestCommand("TestDiscovery", credsIssuerConfig), mTestIndex(0) + { + AddArgument("endpoint", 0, UINT16_MAX, &mEndpoint); + AddArgument("discriminator", 0, UINT16_MAX, &mDiscriminator); + AddArgument("vendorId", 0, UINT16_MAX, &mVendorId); + AddArgument("productId", 0, UINT16_MAX, &mProductId); + AddArgument("deviceType", 0, UINT16_MAX, &mDeviceType); + } + + ~TestDiscovery() + { + if (deviceInstanceNameBeforeRebootBuffer != nullptr) + { + chip::Platform::MemoryFree(deviceInstanceNameBeforeRebootBuffer); + deviceInstanceNameBeforeRebootBuffer = nullptr; + } + } + + /////////// TestCommand Interface ///////// + void NextTest() override + { + CHIP_ERROR err = CHIP_NO_ERROR; + + if (0 == mTestIndex) + { + ChipLogProgress(chipTool, " **** Test Start: TestDiscovery\n"); + } + + if (mTestCount == mTestIndex) + { + ChipLogProgress(chipTool, " **** Test Complete: TestDiscovery\n"); + SetCommandExitStatus(CHIP_NO_ERROR); + return; + } + + Wait(); + + // Ensure we increment mTestIndex before we start running the relevant + // command. That way if we lose the timeslice after we send the message + // but before our function call returns, we won't end up with an + // incorrect mTestIndex value observed when we get the response. + switch (mTestIndex++) + { + case 0: + ChipLogProgress(chipTool, " ***** Test Step 0 : Reboot target device\n"); + err = TestRebootTargetDevice_0(); + break; + case 1: + ChipLogProgress(chipTool, " ***** Test Step 1 : Wait for the commissioned device to be retrieved\n"); + err = TestWaitForTheCommissionedDeviceToBeRetrieved_1(); + break; + case 2: + ChipLogProgress(chipTool, " ***** Test Step 2 : Open Commissioning Window\n"); + err = TestOpenCommissioningWindow_2(); + break; + case 3: + ChipLogProgress(chipTool, " ***** Test Step 3 : Wait Commissionable advertisement\n"); + err = TestWaitCommissionableAdvertisement_3(); + break; + case 4: + ChipLogProgress(chipTool, " ***** Test Step 4 : Check Instance Name\n"); + err = TestCheckInstanceName_4(); + break; + case 5: + ChipLogProgress(chipTool, " ***** Test Step 5 : Check Long Discriminator _L\n"); + err = TestCheckLongDiscriminatorL_5(); + break; + case 6: + ChipLogProgress(chipTool, " ***** Test Step 6 : Check Short Discriminator (_S)\n"); + err = TestCheckShortDiscriminatorS_6(); + break; + case 7: + ChipLogProgress(chipTool, " ***** Test Step 7 : Check Commissioning Mode (_CM)\n"); + err = TestCheckCommissioningModeCm_7(); + break; + case 8: + ChipLogProgress(chipTool, " ***** Test Step 8 : Check Vendor ID (_V)\n"); + if (ShouldSkip("VENDOR_SUBTYPE")) + { + NextTest(); + return; + } + err = TestCheckVendorIdV_8(); + break; + case 9: + ChipLogProgress(chipTool, " ***** Test Step 9 : TXT key for discriminator (D)\n"); + err = TestTxtKeyForDiscriminatorD_9(); + break; + case 10: + ChipLogProgress(chipTool, " ***** Test Step 10 : TXT key for Vendor ID and Product ID (VP)\n"); + if (ShouldSkip("VP_KEY")) + { + NextTest(); + return; + } + err = TestTxtKeyForVendorIdAndProductIdVp_10(); + break; + case 11: + ChipLogProgress(chipTool, " ***** Test Step 11 : TXT key for Vendor ID and Product ID (VP)\n"); + if (ShouldSkip("VP_KEY")) + { + NextTest(); + return; + } + err = TestTxtKeyForVendorIdAndProductIdVp_11(); + break; + case 12: + ChipLogProgress(chipTool, " ***** Test Step 12 : Optional TXT key for MRP Retry Interval Idle (CRI)\n"); + if (ShouldSkip("CRI_COMM_DISCOVERY_KEY")) + { + NextTest(); + return; + } + err = TestOptionalTxtKeyForMrpRetryIntervalIdleCri_12(); + break; + case 13: + ChipLogProgress(chipTool, " ***** Test Step 13 : Optional TXT key for MRP Retry Interval Active (CRA)\n"); + if (ShouldSkip("CRA_COMM_DISCOVERY_KEY")) + { + NextTest(); + return; + } + err = TestOptionalTxtKeyForMrpRetryIntervalActiveCra_13(); + break; + case 14: + ChipLogProgress(chipTool, " ***** Test Step 14 : TXT key for commissioning mode (CM)\n"); + err = TestTxtKeyForCommissioningModeCm_14(); + break; + case 15: + ChipLogProgress(chipTool, " ***** Test Step 15 : Optional TXT key for device name (DN)\n"); + if (ShouldSkip("DN_KEY")) + { + NextTest(); + return; + } + err = TestOptionalTxtKeyForDeviceNameDn_15(); + break; + case 16: + ChipLogProgress(chipTool, " ***** Test Step 16 : Optional TXT key for rotating device identifier (RI)\n"); + if (ShouldSkip("RI_KEY")) + { + NextTest(); + return; + } + err = TestOptionalTxtKeyForRotatingDeviceIdentifierRi_16(); + break; + case 17: + ChipLogProgress(chipTool, " ***** Test Step 17 : Optional TXT key for pairing hint (PH)\n"); + if (ShouldSkip("PH_KEY")) + { + NextTest(); + return; + } + err = TestOptionalTxtKeyForPairingHintPh_17(); + break; + case 18: + ChipLogProgress(chipTool, " ***** Test Step 18 : Optional TXT key for pairing instructions (PI)\n"); + if (ShouldSkip("PI_KEY")) + { + NextTest(); + return; + } + err = TestOptionalTxtKeyForPairingInstructionsPi_18(); + break; + case 19: + ChipLogProgress(chipTool, " ***** Test Step 19 : Check IPs\n"); + err = TestCheckIPs_19(); + break; + case 20: + ChipLogProgress(chipTool, " ***** Test Step 20 : Reboot target device\n"); + err = TestRebootTargetDevice_20(); + break; + case 21: + ChipLogProgress(chipTool, " ***** Test Step 21 : Wait for the commissioned device to be retrieved\n"); + err = TestWaitForTheCommissionedDeviceToBeRetrieved_21(); + break; + case 22: + ChipLogProgress(chipTool, " ***** Test Step 22 : Open Commissioning Window\n"); + err = TestOpenCommissioningWindow_22(); + break; + case 23: + ChipLogProgress(chipTool, " ***** Test Step 23 : Wait Commissionable advertisement\n"); + err = TestWaitCommissionableAdvertisement_23(); + break; + case 24: + ChipLogProgress(chipTool, " ***** Test Step 24 : Check Instance Name\n"); + err = TestCheckInstanceName_24(); + break; + } + + if (CHIP_NO_ERROR != err) + { + ChipLogError(chipTool, " ***** Test Failure: %s\n", chip::ErrorStr(err)); + SetCommandExitStatus(err); + } + } + +private: + std::atomic_uint16_t mTestIndex; + const uint16_t mTestCount = 25; + + chip::Optional mEndpoint; + chip::Optional mDiscriminator; + chip::Optional mVendorId; + chip::Optional mProductId; + chip::Optional mDeviceType; + + char * deviceInstanceNameBeforeRebootBuffer = nullptr; + chip::CharSpan deviceInstanceNameBeforeReboot; + + void OnDiscoveryCommandsResults(const DiscoveryCommandResult & nodeData) override + { + bool isExpectedDnssdResult = false; + if ((mTestIndex - 1) == 4) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckConstraintMinLength("instanceName", nodeData.instanceName.size(), 16)); + VerifyOrReturn(CheckConstraintMaxLength("instanceName", nodeData.instanceName.size(), 16)); + + if (deviceInstanceNameBeforeRebootBuffer != nullptr) + { + chip::Platform::MemoryFree(deviceInstanceNameBeforeRebootBuffer); + } + deviceInstanceNameBeforeRebootBuffer = static_cast(chip::Platform::MemoryAlloc(nodeData.instanceName.size())); + memcpy(deviceInstanceNameBeforeRebootBuffer, nodeData.instanceName.data(), nodeData.instanceName.size()); + deviceInstanceNameBeforeReboot = chip::CharSpan(deviceInstanceNameBeforeRebootBuffer, nodeData.instanceName.size()); + } + if ((mTestIndex - 1) == 5) + { + isExpectedDnssdResult = true; + } + if ((mTestIndex - 1) == 6) + { + isExpectedDnssdResult = true; + } + if ((mTestIndex - 1) == 7) + { + isExpectedDnssdResult = true; + } + if ((mTestIndex - 1) == 8) + { + isExpectedDnssdResult = true; + } + if ((mTestIndex - 1) == 9) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckValue("longDiscriminator", nodeData.longDiscriminator, + mDiscriminator.HasValue() ? mDiscriminator.Value() : GetUniqueDiscriminator())); + + VerifyOrReturn(CheckConstraintMinValue("longDiscriminator", nodeData.longDiscriminator, 0U)); + VerifyOrReturn(CheckConstraintMaxValue("longDiscriminator", nodeData.longDiscriminator, 4096U)); + } + if ((mTestIndex - 1) == 10) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckValue("vendorId", nodeData.vendorId, mVendorId.HasValue() ? mVendorId.Value() : 65521U)); + } + if ((mTestIndex - 1) == 11) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckValue("productId", nodeData.productId, mProductId.HasValue() ? mProductId.Value() : 32769U)); + } + if ((mTestIndex - 1) == 12) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckValuePresent("mrpRetryIntervalIdle", nodeData.mrpRetryIntervalIdle)); + + VerifyOrReturn( + CheckConstraintMaxValue("mrpRetryIntervalIdle", nodeData.mrpRetryIntervalIdle.Value(), 3600000UL)); + } + if ((mTestIndex - 1) == 13) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckValuePresent("mrpRetryIntervalActive", nodeData.mrpRetryIntervalActive)); + + VerifyOrReturn( + CheckConstraintMaxValue("mrpRetryIntervalActive", nodeData.mrpRetryIntervalActive.Value(), 3600000UL)); + } + if ((mTestIndex - 1) == 14) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckValue("commissioningMode", nodeData.commissioningMode, 1)); + } + if ((mTestIndex - 1) == 15) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckConstraintMaxLength("deviceName", nodeData.deviceName.size(), 32)); + } + if ((mTestIndex - 1) == 16) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckConstraintMaxValue("rotatingIdLen", nodeData.rotatingIdLen, 100ULL)); + } + if ((mTestIndex - 1) == 17) + { + isExpectedDnssdResult = true; + } + if ((mTestIndex - 1) == 18) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckConstraintMaxLength("pairingInstruction", nodeData.pairingInstruction.size(), 128)); + } + if ((mTestIndex - 1) == 19) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckConstraintMinValue("numIPs", nodeData.numIPs, 1)); + } + if ((mTestIndex - 1) == 24) + { + isExpectedDnssdResult = true; + + VerifyOrReturn(CheckConstraintMinLength("instanceName", nodeData.instanceName.size(), 16)); + VerifyOrReturn(CheckConstraintMaxLength("instanceName", nodeData.instanceName.size(), 16)); + + VerifyOrReturn(CheckConstraintNotValue("instanceName", nodeData.instanceName, deviceInstanceNameBeforeReboot)); + } + + VerifyOrReturn(isExpectedDnssdResult, Exit("An unexpected dnssd result has been received")); + NextTest(); + } + + // + // Tests methods + // + + CHIP_ERROR TestRebootTargetDevice_0() + { + SetIdentity(kIdentityAlpha); + return Reboot(mDiscriminator.HasValue() ? mDiscriminator.Value() : GetUniqueDiscriminator()); + } + + CHIP_ERROR TestWaitForTheCommissionedDeviceToBeRetrieved_1() + { + SetIdentity(kIdentityAlpha); + return WaitForCommissionee(); + } + + CHIP_ERROR TestOpenCommissioningWindow_2() + { + const chip::EndpointId endpoint = mEndpoint.HasValue() ? mEndpoint.Value() : 0; + using RequestType = chip::app::Clusters::AdministratorCommissioning::Commands::OpenBasicCommissioningWindow::Type; + + RequestType request; + request.commissioningTimeout = 120U; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_2(); + }; + + auto failure = [](void * context, CHIP_ERROR error) { + (static_cast(context))->OnFailureResponse_2(error); + }; + + ReturnErrorOnFailure( + chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request, 10000)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_2(CHIP_ERROR error) + { + chip::app::StatusIB status(error); + ThrowFailureResponse(); + } + + void OnSuccessResponse_2() { NextTest(); } + + CHIP_ERROR TestWaitCommissionableAdvertisement_3() + { + SetIdentity(kIdentityAlpha); + return WaitForCommissionableAdvertisement(); + } + + CHIP_ERROR TestCheckInstanceName_4() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestCheckLongDiscriminatorL_5() + { + SetIdentity(kIdentityAlpha); + return FindCommissionableByLongDiscriminator(mDiscriminator.HasValue() ? mDiscriminator.Value() : GetUniqueDiscriminator()); + } + + CHIP_ERROR TestCheckShortDiscriminatorS_6() + { + SetIdentity(kIdentityAlpha); + return FindCommissionableByShortDiscriminator(mDiscriminator.HasValue() ? mDiscriminator.Value() + : GetUniqueDiscriminator()); + } + + CHIP_ERROR TestCheckCommissioningModeCm_7() + { + SetIdentity(kIdentityAlpha); + return FindCommissionableByCommissioningMode(); + } + + CHIP_ERROR TestCheckVendorIdV_8() + { + SetIdentity(kIdentityAlpha); + return FindCommissionableByVendorId(mVendorId.HasValue() ? mVendorId.Value() : 65521U); + } + + CHIP_ERROR TestTxtKeyForDiscriminatorD_9() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestTxtKeyForVendorIdAndProductIdVp_10() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestTxtKeyForVendorIdAndProductIdVp_11() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestOptionalTxtKeyForMrpRetryIntervalIdleCri_12() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestOptionalTxtKeyForMrpRetryIntervalActiveCra_13() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestTxtKeyForCommissioningModeCm_14() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestOptionalTxtKeyForDeviceNameDn_15() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestOptionalTxtKeyForRotatingDeviceIdentifierRi_16() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestOptionalTxtKeyForPairingHintPh_17() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestOptionalTxtKeyForPairingInstructionsPi_18() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestCheckIPs_19() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } + + CHIP_ERROR TestRebootTargetDevice_20() + { + SetIdentity(kIdentityAlpha); + return Reboot(mDiscriminator.HasValue() ? mDiscriminator.Value() : GetUniqueDiscriminator()); + } + + CHIP_ERROR TestWaitForTheCommissionedDeviceToBeRetrieved_21() + { + SetIdentity(kIdentityAlpha); + return WaitForCommissionee(); + } + + CHIP_ERROR TestOpenCommissioningWindow_22() + { + const chip::EndpointId endpoint = mEndpoint.HasValue() ? mEndpoint.Value() : 0; + using RequestType = chip::app::Clusters::AdministratorCommissioning::Commands::OpenBasicCommissioningWindow::Type; + + RequestType request; + request.commissioningTimeout = 120U; + + auto success = [](void * context, const typename RequestType::ResponseType & data) { + (static_cast(context))->OnSuccessResponse_22(); + }; + + auto failure = [](void * context, CHIP_ERROR error) { + (static_cast(context))->OnFailureResponse_22(error); + }; + + ReturnErrorOnFailure( + chip::Controller::InvokeCommand(mDevices[kIdentityAlpha], this, success, failure, endpoint, request, 10000)); + return CHIP_NO_ERROR; + } + + void OnFailureResponse_22(CHIP_ERROR error) + { + chip::app::StatusIB status(error); + ThrowFailureResponse(); + } + + void OnSuccessResponse_22() { NextTest(); } + + CHIP_ERROR TestWaitCommissionableAdvertisement_23() + { + SetIdentity(kIdentityAlpha); + return WaitForCommissionableAdvertisement(); + } + + CHIP_ERROR TestCheckInstanceName_24() + { + SetIdentity(kIdentityAlpha); + return FindCommissionable(); + } +}; + class TestLogCommands : public TestCommand { public: @@ -91632,6 +92170,7 @@ void registerCommandsTests(Commands & commands, CredentialIssuerCommands * creds make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), + make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), make_unique(credsIssuerConfig), make_unique(credsIssuerConfig),