diff --git a/.github/workflows/java-tests.yaml b/.github/workflows/java-tests.yaml index e3859d5b49f740..9d6d41dcd032c4 100644 --- a/.github/workflows/java-tests.yaml +++ b/.github/workflows/java-tests.yaml @@ -92,6 +92,13 @@ jobs: --target linux-x64-java-matter-controller \ build \ " + - name: Build Kotlin Matter Controller + run: | + ./scripts/run_in_build_env.sh \ + "./scripts/build/build_examples.py \ + --target linux-x64-kotlin-matter-controller \ + build \ + " - name: Run Discover Commissionables Test run: | scripts/run_in_python_env.sh out/venv \ @@ -202,7 +209,51 @@ jobs: --tool-cluster "pairing" \ --tool-args "code --nodeid 1 --setup-payload 34970112332 --discover-once 1 --use-only-onnetwork-discovery 0 -t 1000" \ --factoryreset \ - ' + ' + - name: Run Kotlin IM Invoke Test + run: | + scripts/run_in_python_env.sh out/venv \ + './scripts/tests/run_kotlin_test.py \ + --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \ + --app-args "--discriminator 3840 --interface-id -1" \ + --tool-path out/linux-x64-kotlin-matter-controller \ + --tool-cluster "im" \ + --tool-args "onnetwork-long-im-invoke --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ + --factoryreset \ + ' + - name: Run Kotlin IM Read Test + run: | + scripts/run_in_python_env.sh out/venv \ + './scripts/tests/run_kotlin_test.py \ + --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \ + --app-args "--discriminator 3840 --interface-id -1" \ + --tool-path out/linux-x64-kotlin-matter-controller \ + --tool-cluster "im" \ + --tool-args "onnetwork-long-im-read --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ + --factoryreset \ + ' + - name: Run Kotlin IM Write Test + run: | + scripts/run_in_python_env.sh out/venv \ + './scripts/tests/run_kotlin_test.py \ + --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \ + --app-args "--discriminator 3840 --interface-id -1" \ + --tool-path out/linux-x64-kotlin-matter-controller \ + --tool-cluster "im" \ + --tool-args "onnetwork-long-im-write --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ + --factoryreset \ + ' + - name: Run Kotlin IM Subscribe Test + run: | + scripts/run_in_python_env.sh out/venv \ + './scripts/tests/run_kotlin_test.py \ + --app out/linux-x64-all-clusters-ipv6only-no-ble-no-wifi-tsan-clang-test/chip-all-clusters-app \ + --app-args "--discriminator 3840 --interface-id -1" \ + --tool-path out/linux-x64-kotlin-matter-controller \ + --tool-cluster "im" \ + --tool-args "onnetwork-long-im-subscribe --nodeid 1 --setup-pin-code 20202021 --discriminator 3840 -t 1000" \ + --factoryreset \ + ' - name: Uploading core files uses: actions/upload-artifact@v3 if: ${{ failure() && !env.ACT }} diff --git a/scripts/tests/run_kotlin_test.py b/scripts/tests/run_kotlin_test.py new file mode 100755 index 00000000000000..c4c6c992fd221a --- /dev/null +++ b/scripts/tests/run_kotlin_test.py @@ -0,0 +1,125 @@ +#!/usr/bin/env -S python3 -B + +# Copyright (c) 2023 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 queue +import re +import shlex +import signal +import subprocess +import sys + +import click +import coloredlogs +from colorama import Fore, Style +from java.base import DumpProgramOutputToQueue +from java.im_test import IMTest + + +@click.command() +@click.option("--app", type=click.Path(exists=True), default=None, + help='Path to local application to use, omit to use external apps.') +@click.option("--app-args", type=str, default='', + help='The extra arguments passed to the device.') +@click.option("--tool-path", type=click.Path(exists=True), default=None, + help='Path to java-matter-controller.') +@click.option("--tool-cluster", type=str, default='pairing', + help='The cluster name passed to the java-matter-controller.') +@click.option("--tool-args", type=str, default='', + help='The arguments passed to the java-matter-controller.') +@click.option("--factoryreset", is_flag=True, + help='Remove app configs (/tmp/chip*) before running the tests.') +def main(app: str, app_args: str, tool_path: str, tool_cluster: str, tool_args: str, factoryreset: bool): + logging.info("Execute: {script_command}") + + if factoryreset: + # Remove native app config + retcode = subprocess.call("rm -rf /tmp/chip*", shell=True) + if retcode != 0: + raise Exception("Failed to remove /tmp/chip* for factory reset.") + + print("Contents of test directory: %s" % os.getcwd()) + print(subprocess.check_output(["ls -l"], shell=True).decode('utf-8')) + + # Remove native app KVS if that was used + kvs_match = re.search(r"--KVS (?P[^ ]+)", app_args) + if kvs_match: + kvs_path_to_remove = kvs_match.group("kvs_path") + retcode = subprocess.call("rm -f %s" % kvs_path_to_remove, shell=True) + print("Trying to remove KVS path %s" % kvs_path_to_remove) + if retcode != 0: + raise Exception("Failed to remove %s for factory reset." % kvs_path_to_remove) + + coloredlogs.install(level='INFO') + + log_queue = queue.Queue() + log_cooking_threads = [] + + if tool_path: + if not os.path.exists(tool_path): + if tool_path is None: + raise FileNotFoundError(f"{tool_path} not found") + + app_process = None + if app: + if not os.path.exists(app): + if app is None: + raise FileNotFoundError(f"{app} not found") + app_args = [app] + shlex.split(app_args) + logging.info(f"Execute: {app_args}") + app_process = subprocess.Popen( + app_args, stdout=subprocess.PIPE, stderr=subprocess.PIPE, bufsize=0) + DumpProgramOutputToQueue( + log_cooking_threads, Fore.GREEN + "APP " + Style.RESET_ALL, app_process, log_queue) + + command = ['java', + f'-Djava.library.path={tool_path}/lib/jni', + '-cp', + ':'.join([ + f'{tool_path}/lib/*', + f'{tool_path}/lib/third_party/connectedhomeip/src/controller/java/*', + f'{tool_path}/bin/kotlin-matter-controller', + ]), + 'com.matter.controller.MainKt'] + + if tool_cluster == 'im': + logging.info("Testing IM") + + test = IMTest(log_cooking_threads, log_queue, command, tool_args) + try: + test.RunTest() + except Exception as e: + logging.error(e) + sys.exit(1) + + app_exit_code = 0 + if app_process: + logging.warning("Stopping app with SIGINT") + app_process.send_signal(signal.SIGINT.value) + app_exit_code = app_process.wait() + + # There are some logs not cooked, so we wait until we have processed all logs. + # This procedure should be very fast since the related processes are finished. + for thread in log_cooking_threads: + thread.join() + + # We expect app should exit with 0 + sys.exit(app_exit_code) + + +if __name__ == '__main__': + main()