diff options
author | Egor Tensin <egor@tensin.name> | 2023-12-28 01:00:45 +0100 |
---|---|---|
committer | Egor Tensin <egor@tensin.name> | 2023-12-28 01:00:45 +0100 |
commit | a9ae33c4b9dbe566d2ada07affb8b5a135f9b6eb (patch) | |
tree | 72b47c96da722d5e034af789f4af432e3fdfa051 /test/src/conftest.py | |
parent | json: factor out json_object_put into json_free (diff) | |
download | cimple-a9ae33c4b9dbe566d2ada07affb8b5a135f9b6eb.tar.gz cimple-a9ae33c4b9dbe566d2ada07affb8b5a135f9b6eb.zip |
test/py/ -> test/src/
Diffstat (limited to 'test/src/conftest.py')
-rw-r--r-- | test/src/conftest.py | 232 |
1 files changed, 232 insertions, 0 deletions
diff --git a/test/src/conftest.py b/test/src/conftest.py new file mode 100644 index 0000000..6c34f4d --- /dev/null +++ b/test/src/conftest.py @@ -0,0 +1,232 @@ +# Copyright (c) 2023 Egor Tensin <egor@tensin.name> +# This file is part of the "cimple" project. +# For details, see https://github.com/egor-tensin/cimple. +# Distributed under the MIT License. + +from collections import namedtuple +import logging +import os + +from pytest import fixture + +from lib import test_repo as repo +from lib.db import Database +from lib.net import random_unused_port +from lib.process import CmdLine + + +class Param: + def __init__(self, codename, help_string, required=True): + self.codename = codename + self.help_string = help_string + self.required = required + + @property + def cmd_line(self): + return f"--{self.codename.replace('_', '-')}" + + def add_to_parser(self, parser): + parser.addoption(self.cmd_line, required=self.required, help=self.help_string) + + +PARAMS = [ + Param(f'{name}', f'cimple-{name} binary path') + for name in ('server', 'worker', 'client') +] +PARAMS += [ + Param('sigsegv', 'sigsegv binary path'), + Param('project_version', 'project version'), + Param('valgrind', 'path to valgrind.sh', required=False), + Param('flamegraph', 'path to flamegraph.sh', required=False), + Param('flame_graphs_dir', 'directory to store flame graphs', required=False), +] + + +def pytest_addoption(parser): + for opt in PARAMS: + opt.add_to_parser(parser) + + +class Params: + def __init__(self, pytestconfig): + for opt in PARAMS: + setattr(self, opt.codename, None) + for opt in PARAMS: + path = pytestconfig.getoption(opt.codename) + if path is None: + continue + logging.info("'%s' parameter value: %s", opt.codename, path) + setattr(self, opt.codename, path) + + +@fixture(scope='session') +def params(pytestconfig): + return Params(pytestconfig) + + +class CmdLineValgrind(CmdLine): + def __init__(self, binary): + # Signal to Valgrind that ci scripts should obviously be exempt from + # memory leak checking: + super().__init__(binary, '--trace-children-skip=*/ci', '--') + + +@fixture(scope='session') +def base_cmd_line(params): + cmd_line = CmdLine.unbuffered() + valgrind = params.valgrind + if valgrind is not None: + cmd_line = CmdLine.wrap(CmdLineValgrind(valgrind), cmd_line) + return cmd_line + + +@fixture(scope='session') +def version(params): + return params.project_version + + +@fixture(scope='session') +def server_port(): + return str(random_unused_port()) + + +@fixture +def sqlite_path(tmp_path): + return os.path.join(tmp_path, 'cimple.sqlite') + + +@fixture +def sqlite_db(server, sqlite_path): + return Database(sqlite_path) + + +class CmdLineServer(CmdLine): + def log_line_means_process_ready(self, line): + return line.endswith('Waiting for new connections') + + +class CmdLineWorker(CmdLine): + def log_line_means_process_ready(self, line): + return line.endswith('Waiting for a new command') + + +@fixture +def server_exe(params): + return CmdLineServer(params.server) + + +@fixture +def worker_exe(params): + return CmdLineWorker(params.worker) + + +@fixture +def client_exe(params): + return CmdLine(params.client) + + +@fixture +def server_cmd(base_cmd_line, params, server_port, sqlite_path): + args = ['--port', server_port, '--sqlite', sqlite_path] + return CmdLineServer.wrap(base_cmd_line, CmdLine(params.server, *args)) + + +@fixture +def worker_cmd(base_cmd_line, params, server_port): + args = ['--host', '127.0.0.1', '--port', server_port] + return CmdLineWorker.wrap(base_cmd_line, CmdLine(params.worker, *args)) + + +@fixture +def client(base_cmd_line, params, server_port): + args = ['--host', '127.0.0.1', '--port', server_port] + return CmdLine.wrap(base_cmd_line, CmdLine(params.client, *args)) + + +@fixture +def sigsegv(params): + return CmdLine(params.sigsegv) + + +@fixture +def server(server_cmd): + with server_cmd.run_async() as server: + yield server + assert server.returncode == 0 + + +@fixture +def workers(worker_cmd): + with worker_cmd.run_async() as worker1, \ + worker_cmd.run_async() as worker2: + yield [worker1, worker2] + assert worker1.returncode == 0 + assert worker2.returncode == 0 + + +@fixture +def flame_graph_svg(params, tmp_path, flame_graph_repo): + dir = params.flame_graphs_dir + if dir is None: + return os.path.join(tmp_path, 'flame_graph.svg') + os.makedirs(dir, exist_ok=True) + return os.path.join(dir, f'flame_graph_{flame_graph_repo.codename()}.svg') + + +@fixture +def profiler(params, server, workers, flame_graph_svg): + pids = [server.pid] + [worker.pid for worker in workers] + pids = map(str, pids) + cmd_line = CmdLine(params.flamegraph, flame_graph_svg, *pids) + with cmd_line.run_async() as proc: + yield + assert proc.returncode == 0 + + +@fixture +def repo_path(tmp_path): + return os.path.join(tmp_path, 'repo') + + +TEST_REPOS = [ + repo.TestRepoOutputSimple, + repo.TestRepoOutputEmpty, + repo.TestRepoOutputLong, + repo.TestRepoOutputNull, + repo.TestRepoSegfault, +] + +STRESS_TEST_REPOS = [ + repo.TestRepoOutputSimple, + repo.TestRepoOutputLong, +] + + +def _make_repo(repo_path, params, cls): + args = [repo_path] + if cls is repo.TestRepoSegfault: + args += [params.sigsegv] + return cls(*args) + + +@fixture(params=TEST_REPOS, ids=[repo.codename() for repo in TEST_REPOS]) +def test_repo(repo_path, params, request): + return _make_repo(repo_path, params, request.param) + + +@fixture(params=STRESS_TEST_REPOS, ids=[repo.codename() for repo in STRESS_TEST_REPOS]) +def stress_test_repo(repo_path, params, request): + return _make_repo(repo_path, params, request.param) + + +@fixture +def flame_graph_repo(stress_test_repo): + return stress_test_repo + + +Env = namedtuple('Env', ['server', 'workers', 'client', 'db']) + + +@fixture +def env(server, workers, client, sqlite_db): + return Env(server, workers, client, sqlite_db) |