diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f11d86d3..dbfe3034 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -15,6 +15,7 @@ jobs: make: name: "${{ matrix.os }} with make" runs-on: "${{ matrix.os }}" + timeout-minutes: 15 strategy: fail-fast: false matrix: @@ -40,10 +41,20 @@ jobs: env: CC: gcc CXX: g++ + - name: Set up Python for tests + uses: actions/setup-python@v6 + with: + python-version: '3.14' + cache: 'pip' + - name: Install test dependencies + run: pip install -r requirements.txt + - name: Test + run: make test cmake: name: "${{ matrix.os }} with cmake" runs-on: "${{ matrix.os }}" + timeout-minutes: 15 strategy: fail-fast: false matrix: @@ -61,6 +72,15 @@ jobs: run: | cmake -B build cmake --build build -j${{ steps.cpu-cores.outputs.count }} + - name: Set up Python for tests + uses: actions/setup-python@v6 + with: + python-version: '3.14' + cache: 'pip' + - name: Install test dependencies + run: pip install -r requirements.txt + - name: Test + run: ctest --test-dir build --verbose # A dummy job that you can mark as a required check instead of each individual test test-suite: diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ced349f..4e6bf271 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -731,3 +731,6 @@ add_custom_command(TARGET doc-for-web POST_BUILD ${PROJECT_SOURCE_DIR}/Documentation/es/documentation-es ${PROJECT_BINARY_DIR}/doc/es/documentation-es ) + +include(CTest) +add_test(NAME pytest COMMAND pytest -vv ${CMAKE_SOURCE_DIR}/tests) diff --git a/Makefile b/Makefile index c102fe12..cf09983d 100644 --- a/Makefile +++ b/Makefile @@ -109,8 +109,8 @@ clean: clean-sqlite-shell: make -C Storage/sqlite clean -test: - make -C test +test: railcontrol + pytest -vv tools: make -C tools diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..547de5c5 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,2 @@ +pytest +requests diff --git a/test/Makefile b/test/Makefile deleted file mode 100644 index 02fe8e7a..00000000 --- a/test/Makefile +++ /dev/null @@ -1,18 +0,0 @@ - -CC=g++ - -#CPPFLAGS=-g -O2 -Wall -#CPPFLAGS=-g -O0 -Wall -std=c++11 -CPPFLAGS=-I. -g -O0 -Wall -std=c++11 -LDFLAGS=-g -LIBS=-lpthread -ldl - -TESTS= \ - testloco - -all: $(TESTS) - @./testloco - -clean: - rm -f $(TESTS) *.o - diff --git a/test/functions.php b/test/functions.php deleted file mode 100644 index 778e26e4..00000000 --- a/test/functions.php +++ /dev/null @@ -1,52 +0,0 @@ - diff --git a/test/startrailcontrol.sh b/test/startrailcontrol.sh deleted file mode 100755 index b6b4ae62..00000000 --- a/test/startrailcontrol.sh +++ /dev/null @@ -1,5 +0,0 @@ -#/bin/bash - -echo Starting railcontrol -cd .. -./railcontrol test/testconfig.conf 2> /dev/null > /dev/null & diff --git a/test/stoprailcontrol.sh b/test/stoprailcontrol.sh deleted file mode 100755 index 16c570df..00000000 --- a/test/stoprailcontrol.sh +++ /dev/null @@ -1,11 +0,0 @@ -#/bin/bash - -echo s | ncat localhost 2222 - -while [ `ps auxwf|grep ./railcontrol|wc -l` -gt 1 ] ; do - echo Waiting on railcontrol exit - sleep 1 -done -rm /tmp/railcontrol.sqlite -echo Railcontrol stopped - diff --git a/test/testconfig.conf b/test/testconfig.conf deleted file mode 100644 index 3a88891f..00000000 --- a/test/testconfig.conf +++ /dev/null @@ -1,13 +0,0 @@ -# This is the config file of RailControl -# Lines starting with # are comments -# before and after the = has to be placed a space - -dbengine = sqlite -dbfilename = /tmp/railcontrol.sqlite -dbusername = none -dbpassword = none -dbhost = none - -webserverport = 8080 - -consoleport = 2222 diff --git a/test/testcontrols.php b/test/testcontrols.php deleted file mode 100755 index 676702be..00000000 --- a/test/testcontrols.php +++ /dev/null @@ -1,70 +0,0 @@ -#!/usr/bin/php - diff --git a/test/testlocos.php b/test/testlocos.php deleted file mode 100755 index 355d327e..00000000 --- a/test/testlocos.php +++ /dev/null @@ -1,24 +0,0 @@ -#!/usr/bin/php - diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 00000000..3ca2ed1e --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,62 @@ +import os +import subprocess +from pathlib import Path + +import pytest +import requests +import time + + +class Client: + def __init__(self, url: str): + self.url = url + self.session = requests.Session() + + def cmd(self, cmd: str, **kwargs): + params = kwargs.copy() + params['cmd'] = cmd + response = self.session.get(self.url, params=params) + response.raise_for_status() + return response + + def ping(self): + print('Pinging', self.url) + try: + response = self.session.get(self.url, timeout=10) + response.raise_for_status() + except requests.exceptions.ConnectionError: + return False + + return True + + +@pytest.fixture +def service(tmpdir: Path, port: int = 8022): + config = "dbfilename = 'railcontrol.sqlite'\n" \ + f"webserverport = {port}\n" \ + "numkeepbackups = 0\n" + + configfile = tmpdir / 'config.conf' + configfile.write_text(config, 'UTF-8') + + url = f"http://localhost:{port}" + + command = [os.path.join(os.getcwd(), 'railcontrol'), '--config', str(configfile)] + print("Starting railcontrol") + with subprocess.Popen(command, cwd=tmpdir) as proc: + print("Started", proc.pid) + client = Client(url) + + for _ in range(10): + print("Polling", proc.pid) + if proc.poll(): + pytest.fail(f"Server exited with code {proc.returncode}") + if client.ping(): + yield client + break + time.sleep(0.5) + else: + proc.kill() + pytest.fail("Could not connect to service") + + proc.kill() diff --git a/tests/loco_test.py b/tests/loco_test.py new file mode 100644 index 00000000..21d8117e --- /dev/null +++ b/tests/loco_test.py @@ -0,0 +1,26 @@ +# locolist + +def test_cmd_getlocolist_no_locos(service): + response = service.cmd('getlocolist') + + assert response.headers['Content-Type'] == 'text/csv; charset=utf-8' + assert not response.text + + +# locosave + +def test_cmd_locosave_no_data(service): + response = service.cmd('locosave') + + assert 'eControl does not exist' in response.text + +# loco + +def test_cmd_loco_without_loco(service): + response = service.cmd('loco') + assert 'Please select a locomotive' in response.text + + +def test_cmd_loco_with_loco_does_not_exist(service): + response = service.cmd('loco', loco=1) + assert 'Locomotive does not exist' in response.text