Skip to content

Commit

Permalink
Merge pull request #2675 from niol/testsuite
Browse files Browse the repository at this point in the history
Add basic integration tests for pypy, php and python plugin
  • Loading branch information
xrmx authored Sep 15, 2024
2 parents b9618b6 + 621d006 commit b738f55
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 56 deletions.
8 changes: 6 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
4 changes: 4 additions & 0 deletions buildconf/integration-tests.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[uwsgi]
inherit = base
main_plugin =
plugins = python,php,pypy
3 changes: 3 additions & 0 deletions t/php/config.ini
Original file line number Diff line number Diff line change
@@ -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

Expand Down
7 changes: 7 additions & 0 deletions t/pypy/config.ini
Original file line number Diff line number Diff line change
@@ -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
3 changes: 3 additions & 0 deletions t/python/helloapp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
def application(env, start_response):
start_response("200 OK", [("Content-Type", "text/html")])
return [b"Hello World"]
170 changes: 116 additions & 54 deletions t/runner
Original file line number Diff line number Diff line change
@@ -1,82 +1,144 @@
#!/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
import requests
import signal
import socket
import subprocess
import sys
import time
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}"


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,
)
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):
self.testserver = subprocess.Popen(
[UWSGI_BINARY] + args,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
)

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()

@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 StaticTest(BaseTest.UwsgiServerTest):

ARGS = [
"--plugin",
"python3", # provide a request plugin if no embedded request plugin
os.path.join(TESTS_DIR, "static", "config.ini"),
]

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()

@unittest.skipUnless(*plugins_available(["python"]))
def test_static_expires(self):
self.start_listen_server(
[
"--plugin",
"python", # provide a 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)

@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()

0 comments on commit b738f55

Please sign in to comment.