diff --git a/integrations/docker/ci-only-images/chip-cirque-device-base/Dockerfile b/integrations/docker/ci-only-images/chip-cirque-device-base/Dockerfile index 289dcc26fea6cf..77ad40b1f556ab 100644 --- a/integrations/docker/ci-only-images/chip-cirque-device-base/Dockerfile +++ b/integrations/docker/ci-only-images/chip-cirque-device-base/Dockerfile @@ -15,7 +15,8 @@ WORKDIR /app RUN apt-get update \ && apt-get install --no-install-recommends -y sudo git ca-certificates psmisc dhcpcd5 wpasupplicant wireless-tools \ - && apt-get install -y libglib2.0 avahi-daemon libavahi-client3 iproute2 \ + gdb python3 python3-pip libcairo2-dev libjpeg-dev libgif-dev python3-dev \ + && apt-get install -y libglib2.0 avahi-daemon libavahi-client3 avahi-utils iproute2 \ && ln -fs /usr/share/zoneinfo/UTC /etc/localtime \ && git clone https://github.com/openthread/ot-br-posix . \ && git checkout $OT_BR_POSIX_CHECKOUT \ @@ -23,14 +24,8 @@ RUN apt-get update \ && ./script/bootstrap \ && ./script/setup \ && chmod 644 /etc/bind/named.conf.options \ - && mv ./script /tmp \ - && mv ./etc /tmp \ - && find . -delete \ - && rm -rf /usr/include \ - && mv /tmp/script . \ - && mv /tmp/etc . \ - && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false git ca-certificates \ - && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false apt-utils build-essential psmisc ninja-build cmake wget ca-certificates \ + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false git psmisc \ + && apt-get purge -y --auto-remove -o APT::AutoRemove::RecommendsImportant=false psmisc ninja-build cmake wget \ libreadline-dev libncurses-dev libcpputest-dev libdbus-1-dev libavahi-common-dev \ libavahi-client-dev libboost-dev libboost-filesystem-dev libboost-system-dev libjsoncpp-dev \ libnetfilter-queue-dev \ diff --git a/scripts/tests/cirque_tests.sh b/scripts/tests/cirque_tests.sh index 964220aeadf655..c1e07296ec5792 100755 --- a/scripts/tests/cirque_tests.sh +++ b/scripts/tests/cirque_tests.sh @@ -45,7 +45,9 @@ BOLD_RED_TEXT="\033[1;31m" RESET_COLOR="\033[0m" function __screen() { - if [[ "x$GITHUB_ACTION_RUN" != "x1" ]]; then + if [[ "x$GITHUB_ACTION_RUN" == "x1" ]]; then + "$@" + elif which screen; then screen -dm "$@" else "$@" @@ -55,7 +57,7 @@ function __screen() { function __kill_grep() { ps aux | grep "$1" | awk '{print $2}' | sort -k2 -rn | while read -r pid; do - kill -2 "$pid" + kill -2 -"$pid" done } @@ -146,11 +148,11 @@ function cirquetest_run_test() { # Start Cirque flash server export CURRENT_TEST="$1" export DEVICE_LOG_DIR="$LOG_DIR/$CURRENT_TEST"/device_logs + shift mkdir -p "$DEVICE_LOG_DIR" __cirquetest_start_flask & sleep 5 - cd "$TEST_DIR" - ./"$1.sh" + "$TEST_DIR/$CURRENT_TEST.sh" "$@" exitcode=$? __cirquetest_clean_flask # TODO: Do docker system prune, we cannot filter which container diff --git a/src/test_driver/linux-cirque/ManualTest.sh b/src/test_driver/linux-cirque/ManualTest.sh index e74aedd96b4020..3965e5d701ddd9 100755 --- a/src/test_driver/linux-cirque/ManualTest.sh +++ b/src/test_driver/linux-cirque/ManualTest.sh @@ -24,42 +24,10 @@ # ./scripts/tests/cirque_tests.sh run_test ManualTest # under CHIP checkout. -set -e +set -ex SOURCE="${BASH_SOURCE[0]}" SOURCE_DIR="$(cd -P "$(dirname "$SOURCE")" >/dev/null 2>&1 && pwd)" REPO_DIR="$SOURCE_DIR/../../../" -chip_tool_dir=$REPO_DIR/examples/chip-tool -chip_light_dir=$REPO_DIR/examples/lighting-app/linux - -function build_chip_tool() { - # These files should be successfully compiled elsewhere. - source "$REPO_DIR/scripts/activate.sh" >/dev/null - set -x - cd "$chip_tool_dir" - gn gen --check --fail-on-unused-args out/debug >/dev/null - run_ninja -C out/debug - docker build -t chip_tool -f Dockerfile . 2>&1 -} - -function build_chip_lighting() { - source "$REPO_DIR/scripts/activate.sh" >/dev/null - set -x - cd "$chip_light_dir" - gn gen --check --fail-on-unused-args out/debug - run_ninja -C out/debug - docker build -t chip_server -f Dockerfile . 2>&1 - set +x -} - -function main() { - pushd . - build_chip_tool - build_chip_lighting - popd - python3 "$SOURCE_DIR/test-manual.py" -} - -source "$SOURCE_DIR"/shell-helpers.sh -main +python3 "$SOURCE_DIR/test-manual.py" "$@" diff --git a/src/test_driver/linux-cirque/README.md b/src/test_driver/linux-cirque/README.md index 2bb253dda652d3..bbe04047ea1743 100644 --- a/src/test_driver/linux-cirque/README.md +++ b/src/test_driver/linux-cirque/README.md @@ -10,6 +10,15 @@ In Project CHIP, cirque is used for integration tests. There is a script for running cirque tests, you can find it at `scripts/tests/cirque_tests.sh` +## "Docker out of docker" setup + +If you don't want cirque break your local environment, you can enter a +environment insider docker + +``` +integrations/docker/images/chip-build-cirque/run.sh --privileged --volume /dev/pts:/dev/pts --volume /tmp:/tmp -it -- bash +``` + ## Setting up cirque environment After checkout, in your local project chip directory, run: @@ -85,7 +94,14 @@ LOG_DIR=/some/log/directory scripts/tests/cirque_tests.sh run_test OnOffClusterT You can run a ManualTest to setup test topology only: ``` -scripts/tests/cirque_tests.sh run_test ManualTest +./scripts/tests/cirque_tests.sh run_test ManualTest -t +``` + +The topology file is a JSON file, which contains the definition of each node in +the network. + +``` +./scripts/tests/cirque_tests.sh run_test ManualTest -t src/test_driver/linux-cirque/topologies/three_node_with_thread.json ``` It will print the container id in log, you can execute commands inside them. @@ -115,4 +131,5 @@ It will print the container id in log, you can execute commands inside them. After you finished you test, press `Ctrl-C` and it will clean up testing environment. -Refer to `test-manual.py` and `ManualTest.sh` for detail. +Refer to `test-manual.py`, `ManualTest.sh`, and tolologies file under +`topologies` for detail. diff --git a/src/test_driver/linux-cirque/helper/CHIPTestBase.py b/src/test_driver/linux-cirque/helper/CHIPTestBase.py index 460ab82bd8c6bb..d1d99fcbb58f3e 100644 --- a/src/test_driver/linux-cirque/helper/CHIPTestBase.py +++ b/src/test_driver/linux-cirque/helper/CHIPTestBase.py @@ -54,6 +54,7 @@ def __init__(self, cirque_url, device_config): self.device_ids = [] self.devices = [] self.non_ap_devices = [] + self.thread_devices = [] self.ap_devices = [] # The entrance of the whole test @@ -198,7 +199,7 @@ def connect_to_thread_network(self): this network. ''' self.logger.info("Running commands to form default Thread network") - for device in self.non_ap_devices: + for device in self.thread_devices: self.wait_for_device_output(device['id'], "Border router agent started.", 5) otInitCommands = [ @@ -209,7 +210,7 @@ def connect_to_thread_network(self): "ot-ctl thread start", "ot-ctl dataset active", # Emit ] - for device in self.non_ap_devices: + for device in self.thread_devices: # Set default openthread provisioning for cmd in otInitCommands: self.execute_device_cmd(device['id'], cmd) @@ -217,11 +218,11 @@ def connect_to_thread_network(self): threadNetworkFormed = False for i in range(30): roles = list() - for device in self.non_ap_devices: + for device in self.thread_devices: # We can only check the status of ot-agent by query its state. reply = self.execute_device_cmd(device['id'], 'ot-ctl state') roles.append(reply['output'].split()[0]) - threadNetworkFormed = (roles.count('leader') == 1) and (roles.count('leader') + roles.count('router') + roles.count('child') == len(self.non_ap_devices)) + threadNetworkFormed = (roles.count('leader') == 1) and (roles.count('leader') + roles.count('router') + roles.count('child') == len(self.thread_devices)) if threadNetworkFormed: break time.sleep(1) @@ -348,6 +349,8 @@ def initialize_home(self): self.device_ids = [device_id for device_id in self.device_config] self.non_ap_devices = [device for device in self.device_config.values() if device['type'] != 'wifi_ap'] + self.thread_devices = [device for device in self.device_config.values() + if device['capability'].get('Thread', None) is not None] self.ap_devices = [device for device in self.device_config.values() if device['type'] == 'wifi_ap'] diff --git a/src/test_driver/linux-cirque/test-manual.py b/src/test_driver/linux-cirque/test-manual.py index 22ea282cf3edca..be9dabd9e3bec0 100644 --- a/src/test_driver/linux-cirque/test-manual.py +++ b/src/test_driver/linux-cirque/test-manual.py @@ -19,6 +19,8 @@ import os import time import sys +import json +from optparse import OptionParser, OptionValueError from helper.CHIPTestBase import CHIPVirtualHome @@ -30,20 +32,7 @@ # base_image: The image of the container. # capability: A list of capability of the container, Thread+Interactive should fit in most cases. # rcp_mode: This is used for Thread network setup, set it to True for CHIP. -DEVICE_CONFIG = { - 'device0': { - 'type': 'CHIP-Server', - 'base_image': 'chip_server', - 'capability': ['Thread', 'Interactive'], - 'rcp_mode': True, - }, - 'device1': { - 'type': 'CHIP-Tool', - 'base_image': 'chip_tool', - 'capability': ['Thread', 'Interactive'], - 'rcp_mode': True, - } -} +DEVICE_CONFIG = {} # Set this to True to set up a test thread network if you don't want to test network commissioning. # Note: If you enable this, all devices MUST have Thread capability or the script may fail. @@ -57,6 +46,7 @@ ############################################################# CIRQUE_URL = "http://localhost:5000" +CHIP_REPO = os.path.join(os.path.abspath(os.path.dirname(__file__)), "..", "..", "..") logger = logging.getLogger('CHIPCirqueTest') logger.setLevel(logging.INFO) @@ -92,6 +82,48 @@ def wait_for_interrupt(self): except KeyboardInterrupt: self.logger.info("KeyboardInterrupt received, quit now") +def _parse_mount_dir(config): + for v in config.values(): + if "Mount" not in v.get("capability", {}): + continue + _mount_pairs = v.get("mount_pairs", []) + for mount in _mount_pairs: + mount[0] = mount[0].format(chip_repo = CHIP_REPO) + mount[1] = mount[1].format(chip_repo = CHIP_REPO) + v["mount_pairs"] = _mount_pairs + return config if __name__ == "__main__": + optParser = OptionParser() + optParser.add_option( + "-t", + "--topology", + action="store", + dest="topologyFile", + type='str', + default=None, + help="The topology to be set up by cirque framework.", + metavar="", + ) + optParser.add_option( + "--default-thread-network", + action="store_true", + dest="setupDefaultThreadNetwork", + default=False, + help="Setup default thread network to nodes that supports thread." + ) + + (options, remainingArgs) = optParser.parse_args(sys.argv[1:]) + + if not options.topologyFile: + raise Exception("Must specify a topology file!") + + with open(options.topologyFile, "r") as fp: + config_operations = [_parse_mount_dir] + DEVICE_CONFIG = json.load(fp) + for op in config_operations: + DEVICE_CONFIG = op(DEVICE_CONFIG) + + SETUP_TEST_THREAD_NETWORK = options.setupDefaultThreadNetwork + sys.exit(TestManually(DEVICE_CONFIG).run_test()) diff --git a/src/test_driver/linux-cirque/topologies/three_node_with_thread.json b/src/test_driver/linux-cirque/topologies/three_node_with_thread.json new file mode 100644 index 00000000000000..d6892d22942d4c --- /dev/null +++ b/src/test_driver/linux-cirque/topologies/three_node_with_thread.json @@ -0,0 +1,23 @@ +{ + "device0": { + "type": "CHIP-00", + "base_image": "connectedhomeip/chip-cirque-device-base", + "capability": ["Thread", "Interactive", "Mount"], + "mount_pairs": [["{chip_repo}", "{chip_repo}"]], + "rcp_mode": true + }, + "device1": { + "type": "CHIP-01", + "base_image": "connectedhomeip/chip-cirque-device-base", + "capability": ["Thread", "Interactive", "Mount"], + "mount_pairs": [["{chip_repo}", "{chip_repo}"]], + "rcp_mode": true + }, + "device2": { + "type": "CHIP-02", + "base_image": "connectedhomeip/chip-cirque-device-base", + "capability": ["Thread", "Interactive", "Mount"], + "mount_pairs": [["{chip_repo}", "{chip_repo}"]], + "rcp_mode": true + } +} diff --git a/src/test_driver/linux-cirque/topologies/two_node_with_thread.json b/src/test_driver/linux-cirque/topologies/two_node_with_thread.json new file mode 100644 index 00000000000000..15021c4add5177 --- /dev/null +++ b/src/test_driver/linux-cirque/topologies/two_node_with_thread.json @@ -0,0 +1,16 @@ +{ + "device0": { + "type": "CHIP-00", + "base_image": "connectedhomeip/chip-cirque-device-base", + "capability": ["Thread", "Interactive", "Mount"], + "mount_pairs": [["{chip_repo}", "{chip_repo}"]], + "rcp_mode": true + }, + "device1": { + "type": "CHIP-01", + "base_image": "connectedhomeip/chip-cirque-device-base", + "capability": ["Thread", "Interactive", "Mount"], + "mount_pairs": [["{chip_repo}", "{chip_repo}"]], + "rcp_mode": true + } +}