diff --git a/.github/workflows/L2-tests.yml b/.github/workflows/L2-tests.yml new file mode 100644 index 00000000..67021167 --- /dev/null +++ b/.github/workflows/L2-tests.yml @@ -0,0 +1,66 @@ +name: L2 Integration Tests for systemtimemgr + +on: + pull_request: + branches: [ develop, feature/RDK-48831-sysTimeMgr-L2 ] + +env: + AUTOMATICS_UNAME: ${{ secrets.AUTOMATICS_UNAME }} + AUTOMATICS_PASSCODE: ${{ secrets.AUTOMATICS_PASSCODE }} + +jobs: + execute-l2-tests-on-pr: + name: Execute L2 Test in L2 Container Environment + runs-on: ubuntu-latest + + steps: + - name: Checkout systemtimemgr Code + uses: actions/checkout@v4 + + - name: Log in to GitHub Container Registry + uses: docker/login-action@v2 + with: + registry: ghcr.io + username: ${{ github.actor }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v1 + + - name: Pull Mock XConf, Native Platform, and Docker RDK CI Images + run: | + docker pull ghcr.io/rdkcentral/docker-device-mgt-service-test/mockxconf:latest + docker pull ghcr.io/rdkcentral/docker-device-mgt-service-test/native-platform:latest + docker pull ghcr.io/rdkcentral/docker-rdk-ci:latest + + - name: Start mock-xconf service + run: | + docker run -d --name mockxconf -p 50050:50050 -p 50051:50051 -p 50052:50052 -p 50053:50053 -v ${{ github.workspace }}:/mnt/L2_CONTAINER_SHARED_VOLUME ghcr.io/rdkcentral/docker-device-mgt-service-test/mockxconf:latest + + - name: Start l2-container service + run: | + docker run -d --name native-platform --link mockxconf -v ${{ github.workspace }}:/mnt/L2_CONTAINER_SHARED_VOLUME ghcr.io/rdkcentral/docker-device-mgt-service-test/native-platform:latest + + - name: Build systemtimemgr and Run L2 inside Native Platform Container + run: | + docker exec -i native-platform /bin/bash -c "cd /mnt/L2_CONTAINER_SHARED_VOLUME/ && sh ./cov_build.sh && export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/lib/x86_64-linux-gnu:/lib/aarch64-linux-gnu:/usr/local/lib && sh run_l2.sh" + + - name: Copy L2 Test report to Host Runner + run: | + docker cp native-platform:/tmp/l2_test_report /tmp/L2_TEST_RESULTS + ls -lh /tmp/L2_TEST_RESULTS + + - name: Setup Docker Buildx + uses: docker/setup-buildx-action@v1 + with: + install: true + + - name: Run RDK CI Container + run: | + docker run -d --name ci-container -e AUTOMATICS_UNAME=${{ secrets.AUTOMATICS_UNAME }} -e AUTOMATICS_PASSCODE=${{ secrets.AUTOMATICS_PASSCODE }} -v /tmp/L2_TEST_RESULTS:/tmp/L2_TEST_RESULTS ghcr.io/rdkcentral/docker-rdk-ci:latest tail -f /dev/null + + - name: Upload Results to Automatics + if: github.repository_owner == 'rdkcentral' + run: | + docker cp /tmp/L2_TEST_RESULTS ci-container:/tmp/L2_TEST_RESULTS + docker exec -i ci-container bash -c "echo 'Contents in workspace directory' && ls -l && echo '===============================' && echo 'Contents in /tmp/L2_TEST_RESULTS' && ls -l /tmp/L2_TEST_RESULTS && echo '===============================' && git config --global --add safe.directory /mnt/L2_CONTAINER_SHARED_VOLUME && gtest-json-result-push.py /tmp/L2_TEST_RESULTS https://rdkeorchestrationservice.apps.cloud.comcast.net/rdke_orchestration_api/push_unit_test_results /mnt/L2_CONTAINER_SHARED_VOLUME" diff --git a/cov_build.sh b/cov_build.sh index 6b9cd5da..7606d07f 100644 --- a/cov_build.sh +++ b/cov_build.sh @@ -39,3 +39,4 @@ export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH export LDFLAGS="-L/usr/local/lib -lpthread -lsystimerfactory -lrdkloggers -lsecure_wrapper" ./configure --prefix=${INSTALL_DIR} && make && make install +ls -l /usr/local/bin diff --git a/run_l2.sh b/run_l2.sh new file mode 100644 index 00000000..605ba6cb --- /dev/null +++ b/run_l2.sh @@ -0,0 +1,38 @@ +#!/bin/sh +#################################################################################### +# If not stated otherwise in this file or this component's Licenses.txt file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# 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. +#################################################################################### + +RESULT_DIR="/tmp/l2_test_report" +mkdir -p "$RESULT_DIR" + +apt-get update && apt-get install -y libjsonrpccpp-dev + +touch /usr/local/bin/journalctl +chmod -R 777 /usr/local/bin/journalctl +ln -s /usr/local/bin/journalctl /usr/bin/journalctl + +rm -rf /opt/logs/systimemgr.log* + +# Run L2 Test cases +pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/systimemgr_single_instance.json test/functional-tests/tests/test_systemtimemgr_single_instance.py +pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/systimemgr_initialisation.json test/functional-tests/tests/test_systemtimemgr_initialisation.py +pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/systimemgr_set_time.json test/functional-tests/tests/test_systemtimemgr_set_time.py +pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/systimemgr_get_time.json test/functional-tests/tests/test_systemtimemgr_get_time.py +pytest --json-report --json-report-summary --json-report-file $RESULT_DIR/systimemgr_check_logs.json test/functional-tests/tests/test_systemtimemgr_check_logs.py + diff --git a/test/functional-tests/features/systemtimemgr_single_instance.feature b/test/functional-tests/features/systemtimemgr_single_instance.feature new file mode 100644 index 00000000..26041e81 --- /dev/null +++ b/test/functional-tests/features/systemtimemgr_single_instance.feature @@ -0,0 +1,27 @@ +#################################################################################### +# If not stated otherwise in this file or this component's Licenses.txt file the +# following copyright and licenses apply: +# +# Copyright 2024 RDK Management +# +# 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. +#################################################################################### + +Feature: SystemTimeManager runs only one instance + + Scenario: SystemTimeManager exits if another instance is invoked + Given the SystemTimeManager is not already running + When the SystemTimeManager binary is invoked + Then the SystemTimeManager should be started + And when the SystemTimeManager is attempted to be started again + Then the SystemTimeManager should not start another instance diff --git a/test/functional-tests/tests/helper_functions.py b/test/functional-tests/tests/helper_functions.py new file mode 100644 index 00000000..e5baa289 --- /dev/null +++ b/test/functional-tests/tests/helper_functions.py @@ -0,0 +1,74 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# 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 subprocess +import requests +import os +import time +import re +import signal +import shutil +from time import sleep + +LOG_FILE = "/opt/logs/sysTimeMgr.log" + +def remove_logfile(): + try: + if os.path.exists(LOG_FILE): + os.remove(LOG_FILE) + print(f"Log file {LOG_FILE} removed.") + else: + print(f"Log file {LOG_FILE} does not exist.") + except Exception as e: + print(f"Could not remove log file {LOG_FILE}: {e}") + +def kill_sysTimeMgr(signal: int=9): + print(f"Received Signal to kill systimemgr {signal} with pid {get_pid('sysTimeMgr')}") + resp = subprocess.run(f"kill -{signal} {get_pid('sysTimeMgr')}", shell=True, capture_output=True) + print(resp.stdout.decode('utf-8')) + print(resp.stderr.decode('utf-8')) + return "" + +def grep_sysTimeMgrlogs(search: str): + search_result = "" + search_pattern = re.compile(re.escape(search), re.IGNORECASE) + try: + with open(LOG_FILE, 'r', encoding='utf-8', errors='ignore') as file: + for line_number, line in enumerate(file, start=1): + if search_pattern.search(line): + search_result = search_result + " \n" + line + except Exception as e: + print(f"Could not read file {LOG_FILE}: {e}") + return search_result + +def get_pid(name: str): + return subprocess.run(f"pidof {name}", shell=True, capture_output=True).stdout.decode('utf-8').strip() + +def run_shell_silent(command): + subprocess.run(command, shell=True, capture_output=False, text=False) + return + +def run_shell_command(command): + result = subprocess.run(command, shell=True, capture_output=True, text=True) + return result.stdout.strip() + +def is_systemtimemgr_running(): + command_to_check = "ps aux | grep sysTimeMgr | grep -v grep" + result = run_shell_command(command_to_check) + return result != "" diff --git a/test/functional-tests/tests/test_systemtimemgr_check_logs.py b/test/functional-tests/tests/test_systemtimemgr_check_logs.py new file mode 100644 index 00000000..eefe6245 --- /dev/null +++ b/test/functional-tests/tests/test_systemtimemgr_check_logs.py @@ -0,0 +1,37 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# 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. +########################################################################## +from helper_functions import * + +def test_check_and_start_systemtimemgr(): + kill_sysTimeMgr() + remove_logfile() + print("Starting systemtimemgr process") + command_to_start = "nohup /usr/local/bin/sysTimeMgr > /dev/null 2>&1 &" + run_shell_silent(command_to_start) + command_to_get_pid = "pidof sysTimeMgr" + pid = run_shell_command(command_to_get_pid) + assert pid != "", "sysTimeMgr process did not start" + + +sleep(5) + +QUERY_MSG = "Quality =" +assert QUERY_MSG in grep_sysTimeMgrlogs(QUERY_MSG) + + diff --git a/test/functional-tests/tests/test_systemtimemgr_get_time.py b/test/functional-tests/tests/test_systemtimemgr_get_time.py new file mode 100644 index 00000000..90aa7784 --- /dev/null +++ b/test/functional-tests/tests/test_systemtimemgr_get_time.py @@ -0,0 +1,33 @@ + +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# 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 os +from datetime import datetime + +CLOCK_FILE = "/opt/secure/clock.txt" + + +def get_last_known_time(): +    if os.path.exists(CLOCK_FILE): +        with open(CLOCK_FILE, 'r') as file: +            last_time = file.read().strip() +            return last_time +    else: +        return None diff --git a/test/functional-tests/tests/test_systemtimemgr_initialisation.py b/test/functional-tests/tests/test_systemtimemgr_initialisation.py new file mode 100644 index 00000000..c3b370a8 --- /dev/null +++ b/test/functional-tests/tests/test_systemtimemgr_initialisation.py @@ -0,0 +1,23 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# 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. +########################################################################## + +def is_systimemgr_running(): + command_to_check = "ps aux | grep sysTimeMgr | grep -v grep" + result = run_shell_command(command_to_check) + return result != "" diff --git a/test/functional-tests/tests/test_systemtimemgr_set_time.py b/test/functional-tests/tests/test_systemtimemgr_set_time.py new file mode 100644 index 00000000..f97fd7cd --- /dev/null +++ b/test/functional-tests/tests/test_systemtimemgr_set_time.py @@ -0,0 +1,30 @@ + +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# 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 os +from datetime import datetime + +CLOCK_FILE = "/opt/secure/clock.txt" + +def write_time(): + current_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S") + with open(CLOCK_FILE, 'w') as file: + file.write(current_time) + diff --git a/test/functional-tests/tests/test_systemtimemgr_single_instance.py b/test/functional-tests/tests/test_systemtimemgr_single_instance.py new file mode 100644 index 00000000..766d7e52 --- /dev/null +++ b/test/functional-tests/tests/test_systemtimemgr_single_instance.py @@ -0,0 +1,53 @@ +########################################################################## +# If not stated otherwise in this file or this component's LICENSE +# file the following copyright and licenses apply: +# +# Copyright 2018 RDK Management +# +# 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. +########################################################################## + +from time import sleep +from helper_functions import * + +def test_check_systemtimemgr_is_starting(): + kill_sysTimeMgr() + remove_logfile() + print("Starting systemtimemgr process") + command_to_start = "nohup /usr/local/bin/sysTimeMgr > /dev/null 2>&1 &" + run_shell_silent(command_to_start) + command_to_get_pid = "pidof sysTimeMgr" + pid = run_shell_command(command_to_get_pid) + assert pid != "", "sysTimeMgr process did not start" + +def test_second_systemtimemgr_instance_is_not_started(): + command_to_get_pid = "pidof sysTimeMgr" + pid1 = run_shell_command(command_to_get_pid) + + if is_systemtimemgr_running(): + print("systemtimemgr process is already running") + else: + command_to_start = "nohup /usr/local/bin/sysTimeMgr > /dev/null 2>&1 &" + run_shell_silent(command_to_start) + sleep(2) + + pid2 = run_shell_command(command_to_get_pid) + assert pid1 == pid2, "A second instance of sysTimeMgr was started." + +def test_tear_down(): + command_to_stop = "kill -9 `pidof sysTimeMgr`" + run_shell_command(command_to_stop) + command_to_get_pid = "pidof sysTimeMgr" + remove_logfile() + pid = run_shell_command(command_to_get_pid) + assert pid == ""