From 3784453536376c2f956dcde3a7bb71ceeb80968d Mon Sep 17 00:00:00 2001 From: Vivien Nicolas Date: Mon, 31 Jan 2022 17:46:31 +0100 Subject: [PATCH] [YAML] Expose methods to start/stop/reboot the accessory used for testing (#14240) * [Test Suites] Expose methods to start/stop/reboot the accessory used for testing * Add SystemCommands interface to src/app/tests/suites/commands/system * [chip-tool] Add TestSystemCommands.yaml * Update generated test code --- examples/chip-tool/BUILD.gn | 1 + .../chip-tool/commands/tests/TestCommand.h | 8 +- examples/chip-tool/templates/tests.js | 1 + scripts/tests/chiptest/accessories.py | 108 +++++++++++++++++ scripts/tests/chiptest/linux.py | 7 ++ scripts/tests/chiptest/test_definition.py | 112 +++++++++++++----- scripts/tests/run_test_suite.py | 8 +- src/app/tests/suites/TestSystemCommands.yaml | 41 +++++++ src/app/tests/suites/commands/system/BUILD.gn | 29 +++++ .../suites/commands/system/SystemCommands.cpp | 64 ++++++++++ .../suites/commands/system/SystemCommands.h | 34 ++++++ .../suites/commands/system/scripts/Reboot.py | 27 +++++ .../suites/commands/system/scripts/Start.py | 27 +++++ .../suites/commands/system/scripts/Stop.py | 27 +++++ .../clusters/SystemCommands.js | 53 +++++++++ .../chip-tool/zap-generated/test/Commands.h | 100 ++++++++++++++++ 16 files changed, 617 insertions(+), 30 deletions(-) create mode 100644 scripts/tests/chiptest/accessories.py create mode 100644 src/app/tests/suites/TestSystemCommands.yaml create mode 100644 src/app/tests/suites/commands/system/BUILD.gn create mode 100644 src/app/tests/suites/commands/system/SystemCommands.cpp create mode 100644 src/app/tests/suites/commands/system/SystemCommands.h create mode 100755 src/app/tests/suites/commands/system/scripts/Reboot.py create mode 100755 src/app/tests/suites/commands/system/scripts/Start.py create mode 100755 src/app/tests/suites/commands/system/scripts/Stop.py create mode 100644 src/app/zap-templates/common/simulated-clusters/clusters/SystemCommands.js diff --git a/examples/chip-tool/BUILD.gn b/examples/chip-tool/BUILD.gn index 3eacc2f0898772..f8c0af9df1bad5 100644 --- a/examples/chip-tool/BUILD.gn +++ b/examples/chip-tool/BUILD.gn @@ -70,6 +70,7 @@ static_library("chip-tool-utils") { public_deps = [ "${chip_root}/src/app/server", "${chip_root}/src/app/tests/suites/commands/log", + "${chip_root}/src/app/tests/suites/commands/system", "${chip_root}/src/app/tests/suites/pics", "${chip_root}/src/controller/data_model", "${chip_root}/src/lib", diff --git a/examples/chip-tool/commands/tests/TestCommand.h b/examples/chip-tool/commands/tests/TestCommand.h index 6445fae4ba7a08..a6aa4fff92d9a4 100644 --- a/examples/chip-tool/commands/tests/TestCommand.h +++ b/examples/chip-tool/commands/tests/TestCommand.h @@ -20,6 +20,7 @@ #include "../common/CHIPCommand.h" #include +#include #include #include #include @@ -28,7 +29,12 @@ constexpr uint16_t kTimeoutInSeconds = 90; -class TestCommand : public CHIPCommand, public ValueChecker, public ConstraintsChecker, public PICSChecker, public LogCommands +class TestCommand : public CHIPCommand, + public ValueChecker, + public ConstraintsChecker, + public PICSChecker, + public LogCommands, + public SystemCommands { public: TestCommand(const char * commandName, CredentialIssuerCommands * credsIssuerConfig) : diff --git a/examples/chip-tool/templates/tests.js b/examples/chip-tool/templates/tests.js index e1328555c68527..e099c988c9b84f 100644 --- a/examples/chip-tool/templates/tests.js +++ b/examples/chip-tool/templates/tests.js @@ -234,6 +234,7 @@ function getTests() 'TestIdentifyCluster', 'TestOperationalCredentialsCluster', 'TestModeSelectCluster', + 'TestSystemCommands', ]; const SoftwareDiagnostics = [ diff --git a/scripts/tests/chiptest/accessories.py b/scripts/tests/chiptest/accessories.py new file mode 100644 index 00000000000000..a91af7bdaf6211 --- /dev/null +++ b/scripts/tests/chiptest/accessories.py @@ -0,0 +1,108 @@ +# +# Copyright (c) 2021 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 time +import threading +import sys +from random import randrange +from xmlrpc.server import SimpleXMLRPCServer +from xmlrpc.client import ServerProxy + +IP = '127.0.0.1' +PORT = 9000 + +if sys.platform == 'linux': + IP = '10.10.10.5' + + +class AppsRegister: + _instance = None + __accessories = {} + + def init(self): + self.__startXMLRPCServer() + + def uninit(self): + self.__stopXMLRPCServer() + + def add(self, name, accessory): + self.__accessories[name] = accessory + + def remove(self, name): + self.__accessories.pop(name) + + def removeAll(self): + self.__accessories = {} + + def poll(self): + for accessory in self.__accessories.values(): + status = accessory.poll() + if status is not None: + return status + return None + + def kill(self, name): + accessory = self.__accessories[name] + if accessory: + accessory.kill() + + def killAll(self): + for accessory in self.__accessories.values(): + accessory.kill() + + def start(self, name, discriminator): + accessory = self.__accessories[name] + if accessory: + return accessory.start(discriminator) + return False + + def stop(self, name): + accessory = self.__accessories[name] + if accessory: + return accessory.stop() + return False + + def reboot(self, name, discriminator): + accessory = self.__accessories[name] + if accessory: + return accessory.stop() and accessory.start(discriminator) + return False + + def ping(self): + return True + + def __startXMLRPCServer(self): + self.server = SimpleXMLRPCServer((IP, PORT)) + + self.server.register_function(self.start, 'start') + self.server.register_function(self.stop, 'stop') + self.server.register_function(self.reboot, 'reboot') + self.server.register_function(self.ping, 'ping') + + self.server_thread = threading.Thread(target=self.__handle_request) + self.server_thread.start() + + def __handle_request(self): + self.__should_handle_requests = True + while self.__should_handle_requests: + self.server.handle_request() + + def __stopXMLRPCServer(self): + self.__should_handle_requests = False + # handle_request will wait until it receives a message, so let's send a ping to the server + client = ServerProxy('http://' + IP + ':' + + str(PORT) + '/', allow_none=True) + client.ping() diff --git a/scripts/tests/chiptest/linux.py b/scripts/tests/chiptest/linux.py index 79fdff88d10b80..b8bcac50373f3d 100644 --- a/scripts/tests/chiptest/linux.py +++ b/scripts/tests/chiptest/linux.py @@ -68,6 +68,7 @@ def CreateNamespacesForAppTest(): # create links for switch to net connections "ip link add eth-app type veth peer name eth-app-switch", "ip link add eth-tool type veth peer name eth-tool-switch", + "ip link add eth-ci type veth peer name eth-ci-switch", # link the connections together "ip link set eth-app netns app", @@ -77,6 +78,7 @@ def CreateNamespacesForAppTest(): "ip link set br1 up", "ip link set eth-app-switch master br1", "ip link set eth-tool-switch master br1", + "ip link set eth-ci-switch master br1", # mark connections up "ip netns exec app ip addr add 10.10.10.1/24 dev eth-app", @@ -94,6 +96,11 @@ def CreateNamespacesForAppTest(): "ip netns exec app ip -6 addr flush eth-app", "ip netns exec tool ip -6 a add fd00:0:1:1::2/64 dev eth-tool", "ip netns exec app ip -6 a add fd00:0:1:1::3/64 dev eth-app", + + # create link between virtual host 'tool' and the test runner + "ip addr add 10.10.10.5/24 dev eth-ci", + "ip link set dev eth-ci up", + "ip link set dev eth-ci-switch up", ] for command in COMMANDS: diff --git a/scripts/tests/chiptest/test_definition.py b/scripts/tests/chiptest/test_definition.py index a5ae512d659a1c..6d0791a25130fb 100644 --- a/scripts/tests/chiptest/test_definition.py +++ b/scripts/tests/chiptest/test_definition.py @@ -29,6 +29,81 @@ TEST_NODE_ID = '0x12344321' +class App: + def __init__(self, runner, command): + self.process = None + self.runner = runner + self.command = command + self.stopped = False + + def start(self, discriminator): + if not self.process: + self.process = None + process, outpipe, errpipe = self.__startServer( + self.runner, self.command, discriminator) + self.__waitForServerReady(outpipe) + self.__updateSetUpCode(outpipe) + self.process = process + self.stopped = False + return True + return False + + def stop(self): + if self.process: + self.stopped = True + self.process.kill() + self.process.wait(10) + self.process = None + return True + return False + + def reboot(self, discriminator): + if self.process: + self.stop() + self.start(discriminator) + return True + return False + + 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. + if self.stopped: + return None + return self.process.poll() + + def kill(self): + if self.process: + self.process.kill() + + def wait(self, duration): + if self.process: + self.process.wait(duration) + + def __startServer(self, runner, command, discriminator): + logging.debug( + 'Executing application under test with discriminator %s.' % discriminator) + app_cmd = command + ['--discriminator', str(discriminator)] + return runner.RunSubprocess(app_cmd, name='APP ', wait=False) + + def __waitForServerReady(self, outpipe): + logging.debug('Waiting for server to listen.') + start_time = time.time() + server_is_listening = outpipe.CapturedLogContains("Server Listening") + while not server_is_listening: + if time.time() - start_time > 10: + raise Exception('Timeout for server listening') + time.sleep(0.1) + server_is_listening = outpipe.CapturedLogContains( + "Server Listening") + logging.debug('Server is listening. Can proceed.') + + def __updateSetUpCode(self, outpipe): + qrLine = outpipe.FindLastMatchingLine('.*SetupQRCode: *\\[(.*)]') + if not qrLine: + raise Exception("Unable to find QR code") + self.setupCode = qrLine.group(1) + + class TestTarget(Enum): ALL_CLUSTERS = auto() TV = auto() @@ -87,9 +162,8 @@ class TestDefinition: run_name: str target: TestTarget - def Run(self, runner, paths: ApplicationPaths): + def Run(self, runner, apps_register, paths: ApplicationPaths): """Executes the given test case using the provided runner for execution.""" - app_process = None runner.capture_delegate = ExecutionCapture() try: @@ -127,37 +201,19 @@ def Run(self, runner, paths: ApplicationPaths): if os.path.exists(str(Path.home()) + '/Documents/chip.store'): os.unlink(str(Path.home()) + '/Documents/chip.store') - discriminator = str(randrange(1, 4096)) - logging.debug( - 'Executing application under test with discriminator %s.' % discriminator) - app_process, outpipe, errpipe = runner.RunSubprocess( - app_cmd + ['--discriminator', discriminator], name='APP ', wait=False) + app = App(runner, app_cmd) + app.start(str(randrange(1, 4096))) + apps_register.add("default", app) - logging.debug('Waiting for server to listen.') - start_time = time.time() - server_is_listening = outpipe.CapturedLogContains( - "Server Listening") - while not server_is_listening: - if time.time() - start_time > 10: - raise Exception('Timeout for server listening') - time.sleep(0.1) - server_is_listening = outpipe.CapturedLogContains( - "Server Listening") - logging.debug('Server is listening. Can proceed.') - qrLine = outpipe.FindLastMatchingLine('.*SetupQRCode: *\\[(.*)]') - if not qrLine: - raise Exception("Unable to find QR code") - - runner.RunSubprocess(tool_cmd + ['pairing', 'qrcode', TEST_NODE_ID, qrLine.group(1)], - name='PAIR', dependencies=[app_process]) + runner.RunSubprocess(tool_cmd + ['pairing', 'qrcode', TEST_NODE_ID, app.setupCode], + name='PAIR', dependencies=[apps_register]) runner.RunSubprocess(tool_cmd + ['tests', self.run_name, TEST_NODE_ID], - name='TEST', dependencies=[app_process]) + name='TEST', dependencies=[apps_register]) except: logging.error("!!!!!!!!!!!!!!!!!!!! ERROR !!!!!!!!!!!!!!!!!!!!!!") runner.capture_delegate.LogContents() raise finally: - if app_process: - app_process.kill() - app_process.wait(10) + apps_register.killAll() + apps_register.removeAll() diff --git a/scripts/tests/run_test_suite.py b/scripts/tests/run_test_suite.py index 688778423f038a..f89514a48fa076 100755 --- a/scripts/tests/run_test_suite.py +++ b/scripts/tests/run_test_suite.py @@ -14,6 +14,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from chiptest.accessories import AppsRegister import coloredlogs import click import logging @@ -195,12 +196,15 @@ def cmd_run(context, iterations, all_clusters_app, tv_app): logging.info("Each test will be executed %d times" % iterations) + apps_register = AppsRegister() + apps_register.init() + for i in range(iterations): logging.info("Starting iteration %d" % (i+1)) for test in context.obj.tests: test_start = time.time() try: - test.Run(runner, paths) + test.Run(runner, apps_register, paths) test_end = time.time() logging.info('%-20s - Completed in %0.2f seconds' % (test.name, (test_end - test_start))) @@ -210,6 +214,8 @@ def cmd_run(context, iterations, all_clusters_app, tv_app): (test.name, (test_end - test_start))) sys.exit(2) + apps_register.uninit() + # On linux, allow an execution shell to be prepared if sys.platform == 'linux': diff --git a/src/app/tests/suites/TestSystemCommands.yaml b/src/app/tests/suites/TestSystemCommands.yaml new file mode 100644 index 00000000000000..4a84cd273e3716 --- /dev/null +++ b/src/app/tests/suites/TestSystemCommands.yaml @@ -0,0 +1,41 @@ +# 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. + +name: System Commands Tests + +config: + cluster: "SystemCommands" + endpoint: 0 + +tests: + - label: "Wait for the commissioned device to be retrieved" + cluster: "DelayCommands" + command: "WaitForCommissionee" + + - label: "Stop the accessory" + command: "Stop" + + - label: "Start the accessory with a given discriminator" + command: "Start" + arguments: + values: + - name: "discriminator" + value: 1111 + + - label: "Reboot the accessory with an other given discriminator" + command: "Reboot" + arguments: + values: + - name: "discriminator" + value: 2222 diff --git a/src/app/tests/suites/commands/system/BUILD.gn b/src/app/tests/suites/commands/system/BUILD.gn new file mode 100644 index 00000000000000..4843944f3a8b1e --- /dev/null +++ b/src/app/tests/suites/commands/system/BUILD.gn @@ -0,0 +1,29 @@ +# 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("system") { + output_name = "libSystemCommands" + + sources = [ + "SystemCommands.cpp", + "SystemCommands.h", + ] + + cflags = [ "-Wconversion" ] + + public_deps = [ "${chip_root}/src/lib/support" ] +} diff --git a/src/app/tests/suites/commands/system/SystemCommands.cpp b/src/app/tests/suites/commands/system/SystemCommands.cpp new file mode 100644 index 00000000000000..7fb1eb23fcd6cb --- /dev/null +++ b/src/app/tests/suites/commands/system/SystemCommands.cpp @@ -0,0 +1,64 @@ +/* + * 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 "SystemCommands.h" + +namespace { +const char basePath[] = "./src/app/tests/suites/commands/system/scripts/"; +const char * getScriptsFolder() +{ + return basePath; +} +} // namespace + +CHIP_ERROR SystemCommands::Start(uint16_t discriminator) +{ + const char * scriptDir = getScriptsFolder(); + constexpr const char * scriptName = "Start.py"; + + char command[128]; + VerifyOrReturnError(snprintf(command, sizeof(command), "%s%s %u", scriptDir, scriptName, discriminator) >= 0, + CHIP_ERROR_INTERNAL); + VerifyOrReturnError(system(command) == 0, CHIP_ERROR_INTERNAL); + return ContinueOnChipMainThread(); +} + +CHIP_ERROR SystemCommands::Stop() +{ + const char * scriptDir = getScriptsFolder(); + constexpr const char * scriptName = "Stop.py"; + + char command[128]; + VerifyOrReturnError(snprintf(command, sizeof(command), "%s%s", scriptDir, scriptName) >= 0, CHIP_ERROR_INTERNAL); + + VerifyOrReturnError(system(command) == 0, CHIP_ERROR_INTERNAL); + return ContinueOnChipMainThread(); +} + +CHIP_ERROR SystemCommands::Reboot(uint16_t discriminator) +{ + const char * scriptDir = getScriptsFolder(); + constexpr const char * scriptName = "Reboot.py"; + + char command[128]; + VerifyOrReturnError(snprintf(command, sizeof(command), "%s%s %u", scriptDir, scriptName, discriminator) >= 0, + CHIP_ERROR_INTERNAL); + + VerifyOrReturnError(system(command) == 0, CHIP_ERROR_INTERNAL); + return ContinueOnChipMainThread(); +} diff --git a/src/app/tests/suites/commands/system/SystemCommands.h b/src/app/tests/suites/commands/system/SystemCommands.h new file mode 100644 index 00000000000000..3f5e249572dc8b --- /dev/null +++ b/src/app/tests/suites/commands/system/SystemCommands.h @@ -0,0 +1,34 @@ +/* + * 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 + +class SystemCommands +{ +public: + SystemCommands(){}; + virtual ~SystemCommands(){}; + + virtual CHIP_ERROR ContinueOnChipMainThread() = 0; + + CHIP_ERROR Start(uint16_t discriminator); + CHIP_ERROR Stop(); + CHIP_ERROR Reboot(uint16_t discriminator); +}; diff --git a/src/app/tests/suites/commands/system/scripts/Reboot.py b/src/app/tests/suites/commands/system/scripts/Reboot.py new file mode 100755 index 00000000000000..eb8414a5ea6827 --- /dev/null +++ b/src/app/tests/suites/commands/system/scripts/Reboot.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.reboot('default', sys.argv[1]) diff --git a/src/app/tests/suites/commands/system/scripts/Start.py b/src/app/tests/suites/commands/system/scripts/Start.py new file mode 100755 index 00000000000000..ae340f1ca5691e --- /dev/null +++ b/src/app/tests/suites/commands/system/scripts/Start.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.start('default', sys.argv[1]) diff --git a/src/app/tests/suites/commands/system/scripts/Stop.py b/src/app/tests/suites/commands/system/scripts/Stop.py new file mode 100755 index 00000000000000..247a8f491e52fc --- /dev/null +++ b/src/app/tests/suites/commands/system/scripts/Stop.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.stop('default') diff --git a/src/app/zap-templates/common/simulated-clusters/clusters/SystemCommands.js b/src/app/zap-templates/common/simulated-clusters/clusters/SystemCommands.js new file mode 100644 index 00000000000000..246b8285643345 --- /dev/null +++ b/src/app/zap-templates/common/simulated-clusters/clusters/SystemCommands.js @@ -0,0 +1,53 @@ +/* + * + * 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. + */ + +/* + * This file declares test suite utility methods for system commands. + * + * Each method declared in this file needs to be implemented on a per-language + * basis and allows exposing methods to the test suites that are not part + * of the regular cluster set of APIs. + * + */ + +const Start = { + name : 'Start', + arguments : [ { 'name' : 'discriminator', type : 'INT16U' } ], + response : { arguments : [] } +}; + +const Stop = { + name : 'Stop', + arguments : [], + response : { arguments : [] } +}; + +const Reboot = { + name : 'Reboot', + arguments : [ { 'name' : 'discriminator', type : 'INT16U' } ], + response : { arguments : [] } +}; + +const SystemCommands = { + name : 'SystemCommands', + commands : [ Start, Stop, Reboot ], +}; + +// +// Module exports +// +exports.cluster = SystemCommands; diff --git a/zzz_generated/chip-tool/zap-generated/test/Commands.h b/zzz_generated/chip-tool/zap-generated/test/Commands.h index c13e736359162a..b9a0c120149b4c 100644 --- a/zzz_generated/chip-tool/zap-generated/test/Commands.h +++ b/zzz_generated/chip-tool/zap-generated/test/Commands.h @@ -170,6 +170,7 @@ class TestList : public Command printf("TestIdentifyCluster\n"); printf("TestOperationalCredentialsCluster\n"); printf("TestModeSelectCluster\n"); + printf("TestSystemCommands\n"); printf("Test_TC_SWDIAG_1_1\n"); printf("Test_TC_SWDIAG_2_1\n"); printf("Test_TC_SWDIAG_3_1\n"); @@ -73022,6 +73023,104 @@ class TestModeSelectCluster : public TestCommand void OnSuccessResponse_8() { ThrowSuccessResponse(); } }; +class TestSystemCommands : public TestCommand +{ +public: + TestSystemCommands(CredentialIssuerCommands * credsIssuerConfig) : + TestCommand("TestSystemCommands", credsIssuerConfig), mTestIndex(0) + { + AddArgument("cluster", &mCluster); + AddArgument("endpoint", 0, UINT16_MAX, &mEndpoint); + } + + ~TestSystemCommands() {} + + /////////// TestCommand Interface ///////// + void NextTest() override + { + CHIP_ERROR err = CHIP_NO_ERROR; + + if (0 == mTestIndex) + { + ChipLogProgress(chipTool, " **** Test Start: TestSystemCommands\n"); + } + + if (mTestCount == mTestIndex) + { + ChipLogProgress(chipTool, " **** Test Complete: TestSystemCommands\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 : Wait for the commissioned device to be retrieved\n"); + err = TestWaitForTheCommissionedDeviceToBeRetrieved_0(); + break; + case 1: + ChipLogProgress(chipTool, " ***** Test Step 1 : Stop the accessory\n"); + err = TestStopTheAccessory_1(); + break; + case 2: + ChipLogProgress(chipTool, " ***** Test Step 2 : Start the accessory with a given discriminator\n"); + err = TestStartTheAccessoryWithAGivenDiscriminator_2(); + break; + case 3: + ChipLogProgress(chipTool, " ***** Test Step 3 : Reboot the accessory with an other given discriminator\n"); + err = TestRebootTheAccessoryWithAnOtherGivenDiscriminator_3(); + 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 = 4; + + chip::Optional mCluster; + chip::Optional mEndpoint; + + // + // Tests methods + // + + CHIP_ERROR TestWaitForTheCommissionedDeviceToBeRetrieved_0() + { + SetIdentity(kIdentityAlpha); + return WaitForCommissionee(); + } + + CHIP_ERROR TestStopTheAccessory_1() + { + SetIdentity(kIdentityAlpha); + return Stop(); + } + + CHIP_ERROR TestStartTheAccessoryWithAGivenDiscriminator_2() + { + SetIdentity(kIdentityAlpha); + return Start(1111); + } + + CHIP_ERROR TestRebootTheAccessoryWithAnOtherGivenDiscriminator_3() + { + SetIdentity(kIdentityAlpha); + return Reboot(2222); + } +}; + class Test_TC_SWDIAG_1_1 : public TestCommand { public: @@ -85238,6 +85337,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),