diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index c0dade0..486ef1d 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -26,10 +26,10 @@ "postCreateCommand": "git config --global --add safe.directory /workspaces/ATTEST-Testsystem && git config --global --unset user.email && git config --global --unset user.name", "containerEnv": { "PYTHONPATH": "/workspaces/ATTEST-Testsystem:/workspaces/ATTEST-Testsystem/tests/integration_tests", - "RTS_ROOT": "/workspaces/ATTEST-Testsystem", - "RTS_LOG_FILE": "testsystem.log", - "RTS_DB_FILE": "testsystem.db", - "RTS_CONF_FILE": "testsystem.json", - "RTS_TC_ROOT_PATH": "/workspaces/ATTEST-Testsystem/testcases" + "ATTEST_ROOT": "/workspaces/ATTEST-Testsystem", + "ATTEST_LOG_FILE": "testsystem.log", + "ATTEST_DB_FILE": "testsystem.db", + "ATTEST_CONF_FILE": "testsystem.json", + "ATTEST_TC_ROOT_PATH": "/workspaces/ATTEST-Testsystem/testcases" } } \ No newline at end of file diff --git a/.github/workflows/build_doc.yaml b/.github/workflows/build_doc.yaml index 4578411..205f51f 100644 --- a/.github/workflows/build_doc.yaml +++ b/.github/workflows/build_doc.yaml @@ -9,7 +9,7 @@ on: env: PYTHONPATH: .:./tests/integration_tests - RTS_ROOT: . + ATTEST_ROOT: . GIT_PYTHON_REFRESH: quiet jobs: diff --git a/.gitignore b/.gitignore index 439d0c9..f4a0fc1 100644 --- a/.gitignore +++ b/.gitignore @@ -14,6 +14,7 @@ _*.c *__pycache__ config.json +_docker-compose.yml DEPRECATED* TESTARTEFACT* diff --git a/README.md b/README.md index 742353a..d2dab1e 100644 --- a/README.md +++ b/README.md @@ -43,23 +43,25 @@ If you already have an MSP430 or a PicoScope connected to your host, you can che The test system runs in a docker container. We suggest you first build the docker image because this may take a while. ``` -docker build -t rts:latest . +docker build -t attest:latest . ``` When you have the docker image ready, you can inspect the available commands of the test system. ``` -docker run --rm -t rts:latest python3 main.py --help +docker run --rm -t attest:latest python3 main.py --help ``` +The provided ``docker-compos.yml`` file is a ready-to-use setup for the test system. It combines the test system with a MySQL database for persistent storage. Adjust the environment variables and the volumes according to your system setup. The ``docker-compose-template.yml`` is used by the bootstrap script for automatically generating a compose file with the correct device configuration. + ## Documentation For comprehensive documentation, please visit the [project website](https://eas-attest.github.io/ATTEST-Testsystem/index.html). -Or you build the documentation locally by running the following command after you have the docker image ready. The documentation will be generated in the current working directory. +Or you build the documentation locally by running the following command after you have the docker image ready. The documentation will be generated in the current working directory. ``` docker run --rm -t \ - -v "$(pwd)":/host rts:latest \ + -v "$(pwd)":/host attest:latest \ bash -c "make html && cp -R _build/html /host/documentation" ``` diff --git a/bootstrap.sh b/bootstrap.sh index eb90d10..ee994c4 100755 --- a/bootstrap.sh +++ b/bootstrap.sh @@ -24,14 +24,10 @@ #################### CONFIG #################### -DOCKER_IMAGE_NAME="rts" +DOCKER_IMAGE_NAME="attest" DOCKER_IMAGE_TAG="latest" DOCKER_FILE="dockerfile" -DOCKER_CONTAINER_NAME="rts" -# Path to the ssh directory for the test system user on the host -SSH_PATH="/rtos/.ssh" -# Log, DB and Config path -SYS_PATH="/rtos" +DOCKER_CONTAINER_NAME="attest" MSP_SERIAL_PORT_NAME="ttyACM" PICO_SCOPE_USB_NAME="PicoScope" @@ -39,7 +35,9 @@ PICO_SCOPE_USB_NAME="PicoScope" DOCKER_BUILD_CONTEXT="." DOCKER_RUN_ARGS="" -DOCKER_MODE="-it" +DOCKER_MODE="" + +DOCKER_COMPOSE_FILE="_docker-compose.yml" while test $# -gt 0 do @@ -92,7 +90,7 @@ get_device_string(){ for DEVICE in $ACM_DEVICES do ACM_DEVICE_CNT=$(( $ACM_DEVICE_CNT + 1 )) - LINKED_DEVICE_STR="$LINKED_DEVICE_STR --device=/dev/$DEVICE" + LINKED_DEVICE_STR="${LINKED_DEVICE_STR} - /dev/${DEVICE}:/dev/${DEVICE}"$'\n' done echo "[INFO] Found $ACM_DEVICE_CNT possible serial connections to MSP boards." @@ -102,7 +100,7 @@ get_device_string(){ TOKEN=( $LINE ) USB_BUS=${TOKEN[1]} USB_DEVICE=$(echo ${TOKEN[3]} | sed 's/://') - echo " --device=/dev/bus/usb/$USB_BUS/$USB_DEVICE" + echo "/dev/bus/usb/$USB_BUS/$USB_DEVICE" done ) if [ ${#SCOPE_DEVICES[@]} -eq 0 ] && [ $$ACM_DEVICE_CNT -eq 0 ] @@ -115,40 +113,28 @@ get_device_string(){ for DEVICE in $SCOPE_DEVICES do - LINKED_DEVICE_STR="$LINKED_DEVICE_STR $DEVICE" + LINKED_DEVICE_STR="${LINKED_DEVICE_STR} - ${DEVICE}:${DEVICE}"$'\n' done } +get_device_string + +awk -v r="$LINKED_DEVICE_STR" '{gsub(/_DEVICES_/,r)}1' docker-compose-template.yml > $DOCKER_COMPOSE_FILE +sed -i -e "s%_ARGS_%$DOCKER_RUN_ARGS%g" $DOCKER_COMPOSE_FILE +sed -i -e "s%_ATTEST_IMG_%$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG%g" $DOCKER_COMPOSE_FILE + DOCKER_RET_CODE=0 -start_container() { - ARGS="$LINKED_DEVICE_STR --rm $DOCKER_MODE --name $DOCKER_CONTAINER_NAME -v $SYS_PATH:/host -v $SSH_PATH:/root/.ssh" - IMG="$DOCKER_IMAGE_NAME:$DOCKER_IMAGE_TAG" - CMD="python3 main.py" - echo "[INFO] Start docker container." - echo "[INFO] Arguments: $ARGS" - echo "[INFO] Image: $IMG" - echo "[INFO] Command: $CMD" - echo "[INFO] Command Parameters: $DOCKER_RUN_ARGS" - docker run $ARGS $IMG $CMD $DOCKER_RUN_ARGS +start_docker() { + echo "[INFO] Start docker compose." + docker compose -f $DOCKER_COMPOSE_FILE up $DOCKER_MODE DOCKER_RET_CODE=$? - echo "[INFO] Container started. Use 'docker attach rts' to interact with the testsystem or 'docker attach --sig-proxy=false rts' to inspect the log output." + echo "[INFO] Containers started. Use 'docker attach --sig-proxy=false $DOCKER_CONTAINER_NAME' to inspect the log output." } -get_device_string -start_container +start_docker if [ $DOCKER_RET_CODE -eq 0 ] then exit 0 -elif [ $DOCKER_RET_CODE -eq 2 ] -then - echo "[WARNING] Failed to detect PicoScopes. This may happen after connecting the PicoScopes." - echo "[INFO] Trying to get new device names and restarting." - get_device_string - start_container - if [ $DOCKER_RET_CODE -eq 0 ] - then - exit 0 - fi fi -echo "[ERROR] Starting the test system failed with exit code $DOCKER_RET_CODE." +echo "[ERROR] Starting the test system failed with exit code $DOCKER_RET_CODE." \ No newline at end of file diff --git a/doc/configuration.rst b/doc/configuration.rst index e3cc474..d4210bc 100644 --- a/doc/configuration.rst +++ b/doc/configuration.rst @@ -34,8 +34,7 @@ Environment Variables Configuration via environment variables can be used to customize the test system on startup. Environment variables are instrumental when the system runs as a docker image or as part of docker-compose. The naming convention for environment variables is -UPPER_CASE with underscores and prefixed with ``RTS_`` \. The environment variable -prefix RTS stands for **R**\ eal-Time Operating System **T**\ est **S**\ ystem. +UPPER_CASE with underscores and prefixed with ``ATTEST_`` \. .. note:: @@ -48,7 +47,7 @@ environment variable: .. code-block:: - RTS_RUN_HELLO_TESTSYSTEM=True + ATTEST_RUN_HELLO_TESTSYSTEM=True Config File diff --git a/doc/testsystem.rst b/doc/testsystem.rst index 2036890..cf0d943 100644 --- a/doc/testsystem.rst +++ b/doc/testsystem.rst @@ -115,7 +115,7 @@ because it needs to compile the libmsp library from scratch. .. code-block:: - docker build -t rts:latest . + docker build -t attest:latest . To check if the setup works, run the *hello testsystem* program. This is the hello world equivalent of the test system. It should greet you with basic information about the @@ -125,7 +125,7 @@ They will show up in the log. .. code-block:: - docker run --rm -t -v "$(pwd)":/host rts:latest python3 main.py --hello-testsystem + docker run --rm -t -v "$(pwd)":/host attest:latest python3 main.py --hello-testsystem .. note:: @@ -140,7 +140,7 @@ those files. So make sure this directory is mounted somewhere in the host system .. code-block:: - docker run --rm -v "$(pwd)":/host -t rts:latest python3 main.py + docker run --rm -v "$(pwd)":/host -t attest:latest python3 main.py Other Docker Commands @@ -150,33 +150,33 @@ Display available commands from the test system: .. code-block:: - docker run --rm -t rts:latest python3 main.py --help + docker run --rm -t attest:latest python3 main.py --help Get a list of all MSPs and PicoScopes that have ever been connected to the test system: .. code-block:: - docker run --rm -v "$(pwd)":/host -t rts:latest python3 main.py --list-devices + docker run --rm -v "$(pwd)":/host -t attest:latest python3 main.py --list-devices Set the display name for a device. This name is for example shown in the system report: .. code-block:: - docker run --rm -v "$(pwd)":/host -t rts:latest python3 main.py --set-name + docker run --rm -v "$(pwd)":/host -t attest:latest python3 main.py --set-name Build documentation: .. code-block:: docker run --rm -t \ - -v "$(pwd)":/host rts:latest \ + -v "$(pwd)":/host attest:latest \ bash -c "make html && cp -R _build/html /host/documentation" Run unit tests: .. code-block:: - docker run --rm -t rts:latest pytest tests/unit_tests + docker run --rm -t attest:latest pytest tests/unit_tests To successfully run integration tests, make sure to use the correct device paths for MPS and PicoScope. The following command runs integration tests with one test unit: @@ -187,7 +187,7 @@ and PicoScope. The following command runs integration tests with one test unit: --device=/dev/ttyACM0 \ --device=/dev/ttyACM1 \ --device=/dev/bus/usb/001/003 \ - rts:latest pytest tests/integration_tests + attest:latest pytest tests/integration_tests Hardware ======== @@ -260,16 +260,20 @@ command looks similar to this: Bootstrap Script ================ -The bootstrap script automates the build and start of the test system. +The bootstrap script automates the build and start of the production test system with a MySQL database. The test system needs access to the PicoScopes and MSP430 boards connected via USB to the host. -The script builds the test system container if it does not exist, builds the device connection string based on the USB devices present at the host, and starts the test system container. -The script runs the container in detached mode. +The script builds the test system container if it does not exist, +discovers device connections present at the host, +and starts the test system container and a persistent database. +It generates a docker-compose file from the ``docker-compose-template.yml``, +which is used to start the containers with the correct configuration. +The script runs the docker-compose in detached mode. That means the test system no longer uses the terminal after the build process and runs in the background. To reattach the terminal to the test system, use the following command: .. code-block:: - docker attach --sig-proxy=false rts + docker attach --sig-proxy=false attest The previous command only shows the test system output. By pressing ``CTRL`` + ``C``, the terminal gets detached, but the test system continues in the background. To terminate @@ -277,7 +281,7 @@ the test system, either set ``--sig-proxy`` to true when attaching or set the :py:attr:`~testsystem.config.Config.stop` property in the configuration. You can add an arbitrary number of arguments when calling the bootstrap script. -These arguments are forwarded to docker and the test system (exceptions in the following table). +These arguments are forwarded to the test system container (exceptions in the following table). For example, you can call the test systems help menu directly with the bootstrap script: .. code-block:: @@ -288,7 +292,8 @@ For example, you can call the test systems help menu directly with the bootstrap The arguments in this table are an exception to those passed to docker. They will be handled directly by the bootstrap script. You can combine them with an arbitrary number of other arguments you want to pass to the test system. -The order does not matter; the bootstrap script handles arguments from the table, and all others are passed down to docker. +The order does not matter; the bootstrap script handles arguments from the table, +and all others are passed down to docker. .. csv-table:: :header: "Arguments", "Description" diff --git a/docker-compose-template.yml b/docker-compose-template.yml new file mode 100644 index 0000000..d63d45f --- /dev/null +++ b/docker-compose-template.yml @@ -0,0 +1,43 @@ +version: '2' +services: + db: + image: mysql:latest + environment: + MYSQL_ROOT_PASSWORD: 'secure' + MYSQL_DATABASE: 'testsystem' + MYSQL_USER: 'testsystem' + MYSQL_PASSWORD: 'Password1' + volumes: + - data:/var/lib/mysql + ports: + - "3306:3306" + attest: + image: _ATTEST_IMG_ + container_name: attest + depends_on: + - "db" + environment: + ATTEST_START_DELAY: 5 + ATTEST_DB_TYPE: 'mysql' + ATTEST_DB_USER: 'testsystem' + ATTEST_DB_PASSWORD: 'Password1' + ATTEST_DB_DATABASE: 'testsystem' + ATTEST_DB_SERVER: 'db' + ATTEST_GIT_SERVER: 'ssh://git@iti-gitlab.tugraz.at' + ATTEST_GIT_PUBLIC_PATH: 'eas/teaching/RTOS_SS99' + ATTEST_GIT_STUDENT_PATH: 'eas/teaching/RTOS_SS99' + ATTEST_GIT_SYSTEM_PATH: 'eas/teaching/RTOS_SS99/Testsystem_Reports_SS99' + ATTEST_TERM: 'SS99' + ATTEST_GROUP_IDS: '1;2;3;4;5;6;7;8;9;10' + ATTEST_EXERCISE_NR: 1 + volumes: + - host:/host + - type: bind + source: ~/.ssh + target: /root/.ssh + devices: +_DEVICES_ + command: python3 main.py _ARGS_ +volumes: + data: + host: diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..22dc113 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,41 @@ +version: '2' +services: + db: + image: mysql:latest + environment: + MYSQL_ROOT_PASSWORD: 'secure' + MYSQL_DATABASE: 'testsystem' + MYSQL_USER: 'testsystem' + MYSQL_PASSWORD: 'Password1' + volumes: + - data:/var/lib/mysql + ports: + - "3306:3306" + attest: + image: attest:latest + container_name: attest + depends_on: + - "db" + environment: + ATTEST_START_DELAY: 5 + ATTEST_DB_TYPE: 'mysql' + ATTEST_DB_USER: 'testsystem' + ATTEST_DB_PASSWORD: 'Password1' + ATTEST_DB_DATABASE: 'testsystem' + ATTEST_DB_SERVER: 'db' + ATTEST_GIT_SERVER: 'ssh://git@iti-gitlab.tugraz.at' + ATTEST_GIT_PUBLIC_PATH: 'eas/teaching/RTOS_SS99' + ATTEST_GIT_STUDENT_PATH: 'eas/teaching/RTOS_SS99' + ATTEST_GIT_SYSTEM_PATH: 'eas/teaching/RTOS_SS99/Testsystem_Reports_SS99' + ATTEST_TERM: 'SS99' + ATTEST_GROUP_IDS: '1;2;3;4;5;6;7;8;9;10' + ATTEST_EXERCISE_NR: 1 + volumes: + - host:/host + - type: bind + source: ~/.ssh + target: /root/.ssh + command: python3 main.py +volumes: + data: + host: diff --git a/dockerfile b/dockerfile index c088d55..858228b 100644 --- a/dockerfile +++ b/dockerfile @@ -88,23 +88,24 @@ COPY testcases/ . # Add testsystem WORKDIR /testsystem +COPY requirements.txt ./ + +# Install python packages +RUN pip install -r requirements.txt + COPY *.py ./ COPY index.rst ./ COPY Makefile ./ COPY doc/ ./doc/ COPY tests/ ./tests/ COPY testsystem/ ./testsystem/ -COPY requirements.txt ./ - -# Install python packages -RUN pip install -r requirements.txt RUN git config --global user.email "attest@testsystem.com" RUN git config --global user.name "ATTEST-Testsystem" ENV PYTHONPATH=/testsystem:/testsystem/tests/integration_tests ENV OS=unix -ENV INCLUDES=/opt/ti/msp430-gcc/include/ +ENV INCLUDES=/root/ti/msp430-gcc/include/ ENV FLASHTOOL_MSP430=MSP430Flasher -ENV RTS_ROOT=/testsystem +ENV ATTEST_ROOT=/testsystem ENV LC_ALL=C \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 7674ab7..e7a6a94 100644 --- a/requirements.txt +++ b/requirements.txt @@ -10,4 +10,6 @@ SQLAlchemy==1.4.48 pytest pytest-cov black -tqdm \ No newline at end of file +tqdm +pymysql +cryptography \ No newline at end of file diff --git a/test.py b/test.py new file mode 100644 index 0000000..bff1944 --- /dev/null +++ b/test.py @@ -0,0 +1,8 @@ +class Conf: + a: int = 1 + b: list[bool] = [True] + + +t = type(getattr(Conf, "b")) +for x in getattr(Conf, "b"): + print(type(x)) diff --git a/testcases/testbenches/000/Makefile b/testcases/testbenches/000/Makefile new file mode 100644 index 0000000..8bcd1af --- /dev/null +++ b/testcases/testbenches/000/Makefile @@ -0,0 +1,9 @@ +# This test case is just a placeholder. + +CC=gcc + +all: main.c + $(CC) -o main main.c + +clean: + rm -f main \ No newline at end of file diff --git a/testcases/testbenches/000/main.c b/testcases/testbenches/000/main.c new file mode 100644 index 0000000..5a6a736 --- /dev/null +++ b/testcases/testbenches/000/main.c @@ -0,0 +1 @@ +// Default Setup \ No newline at end of file diff --git a/testcases/testbenches/queueCheck.h b/testcases/testbenches/queueCheck.h new file mode 100644 index 0000000..2f4185c --- /dev/null +++ b/testcases/testbenches/queueCheck.h @@ -0,0 +1 @@ +// Other Global Header \ No newline at end of file diff --git a/testcases/testbenches/testsystem.h b/testcases/testbenches/testsystem.h new file mode 100644 index 0000000..6ddf761 --- /dev/null +++ b/testcases/testbenches/testsystem.h @@ -0,0 +1 @@ +// Global Test Header \ No newline at end of file diff --git a/tests/integration_tests/test_framework/config.py b/tests/integration_tests/test_framework/config.py index 4deaefb..7831b01 100644 --- a/tests/integration_tests/test_framework/config.py +++ b/tests/integration_tests/test_framework/config.py @@ -1,21 +1,21 @@ # # Copyright 2023 EAS Group # -# Permission is hereby granted, free of charge, to any person obtaining a copy of this -# software and associated documentation files (the “Software”), to deal in the Software -# without restriction, including without limitation the rights to use, copy, modify, -# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to the following +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the “Software”), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to the following # conditions: # -# The above copyright notice and this permission notice shall be included in all copies +# The above copyright notice and this permission notice shall be included in all copies # or substantial portions of the Software. # -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # @@ -45,13 +45,13 @@ def setup_config(conf_file_dir: str): f'"git_system_path": "{sys_repo_name}"' "}" ) - os.environ["RTS_CONF_FILE"] = conf_file + os.environ["ATTEST_CONF_FILE"] = conf_file return conf_file def delete_config(conf_file: str): cnf.clear_cache() - os.unsetenv("RTS_CONF_FILE") + os.unsetenv("ATTEST_CONF_FILE") os.remove(conf_file) diff --git a/testsystem/config.py b/testsystem/config.py index 15ede54..dc11431 100644 --- a/testsystem/config.py +++ b/testsystem/config.py @@ -1,21 +1,21 @@ # # Copyright 2023 EAS Group # -# Permission is hereby granted, free of charge, to any person obtaining a copy of this -# software and associated documentation files (the “Software”), to deal in the Software -# without restriction, including without limitation the rights to use, copy, modify, -# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to the following +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the “Software”), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to the following # conditions: # -# The above copyright notice and this permission notice shall be included in all copies +# The above copyright notice and this permission notice shall be included in all copies # or substantial portions of the Software. # -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # @@ -57,11 +57,6 @@ class Config: #: displaying some general information. run_hello_testsystem: bool = False - #: | :guilabel:`env` :guilabel:`file` - #: | Set the database type used for persisting data. Available options are: - #: ``sqlite``\ . - db_type: str = "sqlite" - #: | Flag to enable configuration through environment variables. conf_enable_env: bool = True @@ -73,10 +68,31 @@ class Config: #: | Path to the JSON config file. conf_file: str = "/host/config.json" + #: | :guilabel:`env` :guilabel:`file` + #: | Set the database type used for persisting data. Available options are: + #: ``sqlite``, ``mysql`` \ . + db_type: str = "sqlite" + #: | :guilabel:`env` :guilabel:`dyn` #: | Path to the Database file. db_file: str = "/host/testsystem.db" + #: | :guilabel:`env` :guilabel:`dyn` + #: | Database user name. + db_user: str = "testsystem" + + #: | :guilabel:`env` :guilabel:`dyn` + #: | Database password. + db_password: str = "Password1" + + #: | :guilabel:`env` :guilabel:`dyn` + #: | Database name. + db_database: str = "testsystem" + + #: | :guilabel:`env` :guilabel:`dyn` + #: | Database server/host. + db_server: str = "127.0.0.1" + #: | :guilabel:`env` :guilabel:`file` #: | Option to define where to wirte testsystem logs. log_file: str = "/host/testsystem.log" @@ -123,7 +139,7 @@ class Config: #: | :guilabel:`env` :guilabel:`file` :guilabel:`dyn` #: | Git server with all repos. You can use absolute paths to use the filesystem as - # remote. + #: remote. git_server: str = "ssh://git@iti-gitlab.tugraz.at" #: | :guilabel:`env` :guilabel:`file` :guilabel:`dyn` @@ -158,6 +174,10 @@ class Config: #: scheduling. Tag values are case-insensitive. force_test_tags: list[str] = ["ex1", "ex2", "ex3", "ex4", "ex5", "ex6"] + #: | :guilabel:`env` :guilabel:`file` + #: | System start delay in seconds. This might be used to wait for the database container. + start_delay: int = 0 + def get_group_commit_link(self, group_name: str, commit_hash: str) -> str: sub_path = f"{self.git_student_path}/{group_name}/-/tree/{commit_hash}" url = utils.url_builder(self.git_server, sub_path) @@ -170,14 +190,26 @@ def enable_logging(): def get_config_from_env() -> dict: - env_prefix = "RTS_" + env_prefix = "ATTEST_" pattern = re.compile(r"{prefix}\w+".format(prefix=env_prefix)) conf = {} for k, v in os.environ.items(): if pattern.match(k): - if ";" in v: - v = v.split(";") - conf[k.replace(env_prefix, "")] = v + prop = k.replace(env_prefix, "").lower() + if not hasattr(Config, prop): + continue + attr = getattr(Config, prop) + typ = type(attr) + if ";" in v or typ is list: + if typ is not list: + raise TypeError( + f"Config argument '{prop}' does not accept list values." + ) + ltyp = type(attr[0]) + val = [ltyp(e) for e in v.split(";")] + else: + val = typ(v) + conf[prop] = val return conf diff --git a/testsystem/constants.py b/testsystem/constants.py index 04403f4..2ff11ef 100644 --- a/testsystem/constants.py +++ b/testsystem/constants.py @@ -23,10 +23,10 @@ from .pico_status_constants import PICO_ERROR_CODES -TESTSYSTEM_ROOT = os.environ.get("RTS_ROOT") +TESTSYSTEM_ROOT = os.environ.get("ATTEST_ROOT") if TESTSYSTEM_ROOT is None: print( - "[WARNING] Define a path to the test system root directory. Use the RTS_ROOT" + "[WARNING] Define a path to the test system root directory. Use the ATTEST_ROOT" " environment variable to do that." ) TESTSYSTEM_ROOT = "." diff --git a/testsystem/db.py b/testsystem/db.py index 9447b6a..95cb023 100644 --- a/testsystem/db.py +++ b/testsystem/db.py @@ -1,21 +1,21 @@ # # Copyright 2023 EAS Group # -# Permission is hereby granted, free of charge, to any person obtaining a copy of this -# software and associated documentation files (the “Software”), to deal in the Software -# without restriction, including without limitation the rights to use, copy, modify, -# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to the following +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the “Software”), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to the following # conditions: # -# The above copyright notice and this permission notice shall be included in all copies +# The above copyright notice and this permission notice shall be included in all copies # or substantial portions of the Software. # -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # @@ -46,6 +46,17 @@ def init_database(): future=True, connect_args={"timeout": DB_CONN_TIMEOUT_S}, ) + elif config.db_type == "mysql": + engine = sa.create_engine( + "mysql+pymysql://{}:{}@{}/{}?charset=utf8mb4".format( + config.db_user, + config.db_password, + config.db_server, + config.db_database, + ), + echo=False, + future=True, + ) else: raise NotImplementedError( "Database type '{0}' is not supported.".format(config.db_type) diff --git a/testsystem/models/test_result.py b/testsystem/models/test_result.py index 4abbd97..24a6314 100644 --- a/testsystem/models/test_result.py +++ b/testsystem/models/test_result.py @@ -21,7 +21,8 @@ from __future__ import annotations -from sqlalchemy import Column, Integer, String, ForeignKey, Boolean, Float +from sqlalchemy import Column, Integer, String, ForeignKey, Boolean, Float, BigInteger +from sqlalchemy.types import Text from sqlalchemy.orm import relationship import testsystem.db as db @@ -59,12 +60,12 @@ class TestResult(db.Base): result: float | None = Column(Float, nullable=True) # type: ignore test_case_id: int = Column(Integer, nullable=False) # type: ignore successful: bool = Column(Boolean, nullable=False) # type: ignore - output: str = Column(String, nullable=False, default="") # type: ignore - build_output: str | None = Column(String, nullable=True) # type: ignore - build_error: str | None = Column(String, nullable=True) # type: ignore - flash_output: str | None = Column(String, nullable=True) # type: ignore - flash_error: str | None = Column(String, nullable=True) # type: ignore - timestamp: int = Column(Integer, nullable=False) # type: ignore + output: str = Column(Text, nullable=False, default="") # type: ignore + build_output: str | None = Column(Text, nullable=True) # type: ignore + build_error: str | None = Column(Text, nullable=True) # type: ignore + flash_output: str | None = Column(Text, nullable=True) # type: ignore + flash_error: str | None = Column(Text, nullable=True) # type: ignore + timestamp: int = Column(BigInteger, nullable=False) # type: ignore test_set_result = relationship("TestSet", back_populates="test_results") diff --git a/testsystem/models/test_set.py b/testsystem/models/test_set.py index e14f7b5..955b2d7 100644 --- a/testsystem/models/test_set.py +++ b/testsystem/models/test_set.py @@ -1,21 +1,21 @@ # # Copyright 2023 EAS Group # -# Permission is hereby granted, free of charge, to any person obtaining a copy of this -# software and associated documentation files (the “Software”), to deal in the Software -# without restriction, including without limitation the rights to use, copy, modify, -# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to the following +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the “Software”), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to the following # conditions: # -# The above copyright notice and this permission notice shall be included in all copies +# The above copyright notice and this permission notice shall be included in all copies # or substantial portions of the Software. # -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # @@ -26,7 +26,7 @@ import string import testsystem.db as db -from sqlalchemy import Column, Integer, String, ForeignKey, Boolean +from sqlalchemy import Column, Integer, String, ForeignKey, Boolean, BigInteger from sqlalchemy.orm import Session, relationship, joinedload from .test_result import TestResult @@ -134,9 +134,9 @@ class TestSet(db.Base): group_id: int = Column(Integer, ForeignKey("Groups.id"), nullable=False) # type: ignore commit_hash: str = Column(String(40), nullable=False, unique=True) # type: ignore commit_message: str = Column(String(50), nullable=True) # type: ignore - commit_time: int = Column(Integer, nullable=True) # type: ignore + commit_time: int = Column(BigInteger, nullable=True) # type: ignore finished: bool = Column(Boolean, nullable=False, default=False) # type: ignore - timestamp: int = Column(Integer, nullable=False) # type: ignore + timestamp: int = Column(BigInteger, nullable=False) # type: ignore test_results: list[TestResult] = relationship( "TestResult", diff --git a/testsystem/system.py b/testsystem/system.py index 735007d..1cdf91c 100644 --- a/testsystem/system.py +++ b/testsystem/system.py @@ -1,21 +1,21 @@ # # Copyright 2023 EAS Group # -# Permission is hereby granted, free of charge, to any person obtaining a copy of this -# software and associated documentation files (the “Software”), to deal in the Software -# without restriction, including without limitation the rights to use, copy, modify, -# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to the following +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the “Software”), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to the following # conditions: # -# The above copyright notice and this permission notice shall be included in all copies +# The above copyright notice and this permission notice shall be included in all copies # or substantial portions of the Software. # -# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE # OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. # @@ -48,6 +48,8 @@ def _start_handler(func, *args): configure_logging() + c = cnf.get_config() + time.sleep(c.start_delay) print(const.TESTSYSTEM_TITLE) logging.info("Starting testsystem.") try: