diff --git a/test/README.pytest b/test/README.pytest index 474030bdc88..c3675bb3ad2 100644 --- a/test/README.pytest +++ b/test/README.pytest @@ -35,6 +35,42 @@ also raises the error log level of the tested modules. > pytest -vvv -k test_h2_004_01 run the specific test with mod_http2 at log level TRACE2. +There is an option to archive the results across different +modules and httpd versions. The archiving will preserve +error_log.log, access_log.log, and all config files (*.conf) +from that pytest run. The following command will store the files +inside of '/path/to/archive/${HTTPD_VERSION}/modules/core'. +> pytest -k test_core --archive=/path/to/archive/ + +To archive the entire /test/gen/ directory (logs, confs, certs, htdocs, etc.) +use --archive-all instead: +> pytest -k test_core --archive-all=/path/to/archive/ + +If you don't provide any specific module, pytest will execute all of them +and the already archived folders and files will be replaced by the new ones. +> pytest --archive=/path/to/archive + +Always use --archive= or --archive-all= +(with =, not a space) to avoid pytest rootdir issues. + +Using the --archive OR --archive-all option having installed a different httpd version +will preserve any results from previous executions so you would +end up with a different folder structure for each httpd version. +You can also archive the results of different modules and that will +work incrementally, which means it will add a new folder per module to the structure. + +The following structure shows the archived results from 3 different httpd versions: + ├── /path/to/archive + │ ├── 2.4.65 + │ │ ├── core + │ │ ├── http1 + │ │ ├── http2 + │ ├── 2.4.66 + │ │ └── core + │ └── 2.5.1 + │ └── http2 + + By default, test cases will configure httpd with mpm_event. You can change that with the invocation: > MPM=worker pytest test/modules/http2 diff --git a/test/conftest.py b/test/conftest.py index ac0a7c553d8..ee98f315ba3 100644 --- a/test/conftest.py +++ b/test/conftest.py @@ -1,5 +1,6 @@ import sys import os +import warnings import pytest @@ -15,6 +16,10 @@ def pytest_addoption(parser): parser.addoption("--repeat", action="store", type=int, default=1, help='Number of times to repeat each test') parser.addoption("--all", action="store_true") + parser.addoption("--archive", action="store", default=None, + help='Archive logs and configs after each test package to the specified folder') + parser.addoption("--archive-all", action="store", default=None, + help='Archive the entire gen/ directory after each test package to the specified folder') def pytest_generate_tests(metafunc): @@ -39,9 +44,18 @@ def _module_scope(env): @pytest.fixture(autouse=True, scope="package") -def _package_scope(env): +def _package_scope(env, request): env.httpd_error_log.clear_ignored_matches() env.httpd_error_log.clear_ignored_lognos() yield assert env.apache_stop() == 0 env.check_error_log() + archive_all_dir = request.config.getoption("--archive-all") + archive_dir = archive_all_dir or request.config.getoption("--archive") + if archive_dir == "": + warnings.warn("--archive/--archive-all option was empty, skipping archiving") + if archive_dir: + fspath = str(request.fspath) + parts = fspath.split('modules/') + package_name = parts[1].split('/')[0] + env.archive_logs(package_name, archive_dir, copy_all=bool(archive_all_dir)) \ No newline at end of file diff --git a/test/pyhttpd/env.py b/test/pyhttpd/env.py index 8a20d928432..62b7cf978d6 100644 --- a/test/pyhttpd/env.py +++ b/test/pyhttpd/env.py @@ -1,3 +1,4 @@ +import glob import importlib import inspect import logging @@ -341,6 +342,22 @@ def check_error_log(self): f"apache logged {len(errors)} errors and {len(warnings)} warnings: \n"\ "{0}\n{1}\n".format("\n".join(errors), "\n".join(warnings)) + def archive_logs(self, package_name, archive_dir, copy_all=False): + version = self.get_httpd_version() + dest = os.path.join(archive_dir, version, package_name) + if os.path.isdir(dest): + shutil.rmtree(dest) + if copy_all: + shutil.copytree(self._gen_dir, dest) + else: + os.makedirs(dest) + for log_file in ['error_log', 'access_log']: + log_fpath = os.path.join(self._server_logs_dir, log_file) + if os.path.isfile(log_fpath) and os.path.getsize(log_fpath) > 0: + shutil.copy(log_fpath, os.path.join(dest, f"{log_file}.log")) + for conf_file in glob.glob(os.path.join(self._server_conf_dir, '*.conf')): + shutil.copy(conf_file, os.path.join(dest, os.path.basename(conf_file))) + @property def curl(self) -> str: return self._curl