From 9e9982dc5983c5504499ca9290ff4af20f7f3fb1 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Tue, 28 May 2024 17:48:43 +0200 Subject: [PATCH 1/3] tests: allow log output only if test fails --- t/runner | 113 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 60 insertions(+), 53 deletions(-) diff --git a/t/runner b/t/runner index b9d9ce529..8dc0dc407 100755 --- a/t/runner +++ b/t/runner @@ -6,6 +6,7 @@ import requests import signal import socket import subprocess +import sys import time import unittest @@ -17,63 +18,69 @@ UWSGI_PORT = 8000 UWSGI_HTTP = f"{UWSGI_ADDR}:{UWSGI_PORT}" -class BaseTest: - """ - Container class to avoid base test being run - """ - - class UwsgiServerTest(unittest.TestCase): - """ - Test case with a server instance available on a socket for requests - """ - - @classmethod - def uwsgi_ready(cls): - try: - s = socket.socket() - s.connect( - ( - UWSGI_ADDR, - UWSGI_PORT, - ) - ) - except socket.error: - return False - else: - return True - finally: - s.close() - - @classmethod - def setUpClass(cls): - # launch server - cls.testserver = subprocess.Popen( - [UWSGI_BINARY, "--http-socket", UWSGI_HTTP] + cls.ARGS - ) - - # ensure server is ready - retries = 10 - while not cls.uwsgi_ready() and retries > 0: - time.sleep(0.1) - retries = retries - 1 - if retries == 0: - raise RuntimeError("uwsgi test server is not available") - - @classmethod - def tearDownClass(cls): - cls.testserver.send_signal(signal.SIGTERM) - cls.testserver.wait() +class UwsgiTest(unittest.TestCase): + def start_server(self, args): + self.testserver = subprocess.Popen( + [UWSGI_BINARY] + args, + stdout=subprocess.PIPE, + stderr=subprocess.STDOUT, + text=True, + ) -class StaticTest(BaseTest.UwsgiServerTest): - - ARGS = [ - "--plugin", - "python3", # provide a request plugin if no embedded request plugin - os.path.join(TESTS_DIR, "static", "config.ini"), - ] + def uwsgi_ready(self): + try: + s = socket.socket() + s.connect( + ( + UWSGI_ADDR, + UWSGI_PORT, + ) + ) + except socket.error: + return False + else: + return True + finally: + s.close() + + def start_listen_server(self, args): + self.start_server(["--http-socket", UWSGI_HTTP] + args) + + # ensure server is ready + retries = 10 + while not self.uwsgi_ready() and retries > 0: + time.sleep(0.1) + retries = retries - 1 + if retries == 0: + raise RuntimeError("uwsgi test server is not available") + + def tearDown(self): + if hasattr(self._outcome, "errors"): + # Python 3.4 - 3.10 (These two methods have no side effects) + result = self.defaultTestResult() + self._feedErrorsToResult(result, self._outcome.errors) + else: + # Python 3.11+ + result = self._outcome.result + ok = not (result.errors + result.failures) + + self.testserver.send_signal(signal.SIGTERM) + if not ok: + print(self.testserver.stdout.read(), file=sys.stderr) + + self.testserver.wait() + self.testserver.stdout.close() def test_static_expires(self): + self.start_listen_server( + [ + "--plugin", + "python3", # provide a request plugin if no embedded request plugin + os.path.join(TESTS_DIR, "static", "config.ini"), + ] + ) + with requests.get(f"http://{UWSGI_HTTP}/foobar/config.ini") as r: self.assertTrue("Expires" in r.headers) From 1a3c13c4197560cf7f00cd4024c8009296398ba0 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Tue, 28 May 2024 17:49:44 +0200 Subject: [PATCH 2/3] tests: use UWSGI_PLUGINS env var to define plugins tests --- t/runner | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/t/runner b/t/runner index 8dc0dc407..b0431b7b0 100755 --- a/t/runner +++ b/t/runner @@ -13,11 +13,21 @@ import unittest TESTS_DIR = os.path.dirname(__file__) UWSGI_BINARY = os.getenv("UWSGI_BINARY", os.path.join(TESTS_DIR, "..", "uwsgi")) +UWSGI_PLUGINS = os.getenv("UWSGI_PLUGINS", "all").split(" ") UWSGI_ADDR = "127.0.0.1" UWSGI_PORT = 8000 UWSGI_HTTP = f"{UWSGI_ADDR}:{UWSGI_PORT}" +def plugins_available(plugins): + available = False + if "all" in UWSGI_PLUGINS: + available = True + else: + available = all([plugin in UWSGI_PLUGINS for plugin in plugins]) + return available, f"{plugins} plugins not available but required for this test case" + + class UwsgiTest(unittest.TestCase): def start_server(self, args): @@ -72,6 +82,7 @@ class UwsgiTest(unittest.TestCase): self.testserver.wait() self.testserver.stdout.close() + @unittest.skipUnless(*plugins_available(["python3"])) def test_static_expires(self): self.start_listen_server( [ From 621d00619ebdd8d2e73c99c050f890c4b8bb7a19 Mon Sep 17 00:00:00 2001 From: Alexandre Rossi Date: Mon, 9 Sep 2024 12:02:58 +0200 Subject: [PATCH 3/3] integration tests: add basic test for php, python and pypy plugins --- .github/workflows/test.yml | 8 ++++-- buildconf/integration-tests.ini | 4 +++ t/php/config.ini | 3 +++ t/pypy/config.ini | 7 +++++ t/python/helloapp.py | 3 +++ t/runner | 48 +++++++++++++++++++++++++++++++-- 6 files changed, 69 insertions(+), 4 deletions(-) create mode 100644 buildconf/integration-tests.ini create mode 100644 t/pypy/config.ini create mode 100644 t/python/helloapp.py diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2d4731914..20541b77c 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -22,14 +22,18 @@ jobs: run: make unittests test: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 steps: - name: Install dependencies run: | sudo apt update -qq sudo apt install --no-install-recommends -qqyf \ - libpcre2-dev libjansson-dev libcap2-dev + libpcre2-dev libjansson-dev libcap2-dev \ + php-dev libphp-embed libargon2-dev libsodium-dev \ + pypy3 - uses: actions/checkout@v4 + - name: Set env + run: echo "PROFILE=integration-tests" >> $GITHUB_ENV - name: Run integration tests run: make all tests diff --git a/buildconf/integration-tests.ini b/buildconf/integration-tests.ini new file mode 100644 index 000000000..55f4188ca --- /dev/null +++ b/buildconf/integration-tests.ini @@ -0,0 +1,4 @@ +[uwsgi] +inherit = base +main_plugin = +plugins = python,php,pypy diff --git a/t/php/config.ini b/t/php/config.ini index 35a6dee9f..a94e3abd5 100644 --- a/t/php/config.ini +++ b/t/php/config.ini @@ -1,6 +1,9 @@ [uwsgi] http-socket = :8080 http-socket-modifier1 = 14 +# required for php +need-app = false +plugins = php cache2 = name=session,items=1000,store=/tmp/uwsgi-session-cache,bitmap=1 diff --git a/t/pypy/config.ini b/t/pypy/config.ini new file mode 100644 index 000000000..8643589c4 --- /dev/null +++ b/t/pypy/config.ini @@ -0,0 +1,7 @@ +[uwsgi] +plugin = pypy +need-app = false +pypy-lib = /usr/lib/x86_64-linux-gnu/libpypy3-c.so +pypy-home = /usr/lib/pypy3 +pypy-setup = plugins/pypy/pypy_setup.py +pypy-wsgi-file = t/python/helloapp.py diff --git a/t/python/helloapp.py b/t/python/helloapp.py new file mode 100644 index 000000000..49f1907cc --- /dev/null +++ b/t/python/helloapp.py @@ -0,0 +1,3 @@ +def application(env, start_response): + start_response("200 OK", [("Content-Type", "text/html")]) + return [b"Hello World"] diff --git a/t/runner b/t/runner index b0431b7b0..42d639334 100755 --- a/t/runner +++ b/t/runner @@ -1,4 +1,12 @@ #!/usr/bin/python3 +# +# This test suite runner runs some integration tests for uwsgi, that is +# each test launches a test server with a specific configuration and +# verifies (usually using a HTTP request) that this test server behaves as +# expected. +# +# buildconf/integration-tests.ini holds the build configuration for this +# to run fine. import os @@ -82,12 +90,12 @@ class UwsgiTest(unittest.TestCase): self.testserver.wait() self.testserver.stdout.close() - @unittest.skipUnless(*plugins_available(["python3"])) + @unittest.skipUnless(*plugins_available(["python"])) def test_static_expires(self): self.start_listen_server( [ "--plugin", - "python3", # provide a request plugin if no embedded request plugin + "python", # provide a request plugin os.path.join(TESTS_DIR, "static", "config.ini"), ] ) @@ -95,6 +103,42 @@ class UwsgiTest(unittest.TestCase): with requests.get(f"http://{UWSGI_HTTP}/foobar/config.ini") as r: self.assertTrue("Expires" in r.headers) + @unittest.skipUnless(*plugins_available(["python"])) + def test_python3_helloworld(self): + self.start_listen_server( + [ + "--plugin", + "python", + "--wsgi-file", + os.path.join(TESTS_DIR, "python", "helloapp.py"), + ] + ) + + with requests.get(f"http://{UWSGI_HTTP}/") as r: + self.assertEqual(r.text, "Hello World") + + @unittest.skipUnless(*plugins_available(["pypy"])) + def test_pypy3_helloworld(self): + self.start_listen_server( + [ + os.path.join(TESTS_DIR, "pypy", "config.ini"), + ] + ) + + with requests.get(f"http://{UWSGI_HTTP}/") as r: + self.assertEqual(r.text, "Hello World") + + @unittest.skipUnless(*plugins_available(["php"])) + def test_php_session(self): + self.start_listen_server( + [ + os.path.join(TESTS_DIR, "php", "config.ini"), + ] + ) + + with requests.get(f"http://{UWSGI_HTTP}/test.php") as r: + self.assertEqual(r.text, "PASS\n") + if __name__ == "__main__": unittest.main()