diff --git a/test/pytest_suite/apache_pytest/config.py b/test/pytest_suite/apache_pytest/config.py index 3995cd5753..d6b4afed71 100644 --- a/test/pytest_suite/apache_pytest/config.py +++ b/test/pytest_suite/apache_pytest/config.py @@ -565,14 +565,20 @@ class TestConfig: Mirrors find_and_load_module (TestConfig.pm:1329): emits a guarded LoadModule into the preamble. We reuse the inherited load directive's .so - path when present; otherwise we skip (the module is built-in or absent). + path when present; otherwise scan the install modules directory. """ sym = mod[len("mod_"):] if mod.startswith("mod_") else mod cname = f"mod_{sym}.c" for d in self.info.load_directives: if d.cname == cname and Path(d.so).exists(): self._load_module_preamble(sym, Path(d.so)) + self.info.modules.add(cname) return + modules_dir = self.info.httpd.parent.parent / "modules" + so = modules_dir / f"mod_{sym}.so" + if so.exists(): + self._load_module_preamble(sym, so) + self.info.modules.add(cname) # %aliases map (TestConfig.pm:1604): label -> var holding the path. _GETFILES_ALIASES = { @@ -874,6 +880,10 @@ class TestConfig: # mod_mime first (TestConfig.pm:1394), then mod_alias (TestConfig.pm:1635). self._find_and_load_fallback("mod_mime") self._find_and_load_fallback("mod_alias") + # httpd-built test/experimental modules not in the default httpd.conf + for mod in ("mod_bucketeer", "mod_case_filter", "mod_case_filter_in", + "mod_echo", "mod_log_debug", "mod_reflector", "mod_data"): + self._find_and_load_fallback(mod) # On Windows, tell mod_cgi to read the #! shebang line instead of # using the Registry file association to find the script interpreter. diff --git a/test/pytest_suite/conftest.py b/test/pytest_suite/conftest.py index a0c50fd3f4..005f496bbc 100644 --- a/test/pytest_suite/conftest.py +++ b/test/pytest_suite/conftest.py @@ -79,6 +79,13 @@ def pytest_addoption(parser: pytest.Parser) -> None: help="server install prefix for resolving relative module paths " "(default: derived from --conf path)", ) + group.addoption( + "--with-perl", + action="store", + default=None, + help="path to the perl interpreter for generated scripts and " + "rewrite prg-maps (default: /usr/bin/perl or APACHE_TEST_PERL env)", + ) def pytest_configure(config: pytest.Config) -> None: @@ -237,6 +244,11 @@ def framework(request: pytest.FixtureRequest): info = probe(httpd, inherited_conf, install_prefix) + perl_opt = request.config.getoption("--with-perl") + if perl_opt: + import os + os.environ["APACHE_TEST_PERL"] = perl_opt + # Optional PHP-FPM: if a php-fpm binary was given and mod_proxy_fcgi is # available, route htdocs/php/*.php to a managed FPM daemon. The php-fpm # path/port are the only PHP-specific inputs -- no version is assumed. diff --git a/test/pytest_suite/runtests.bat b/test/pytest_suite/runtests.bat index 0556cabb68..08559675a0 100644 --- a/test/pytest_suite/runtests.bat +++ b/test/pytest_suite/runtests.bat @@ -7,15 +7,17 @@ rem Apache httpd (the server under test) located via apxs, and rem optionally a php-fpm binary to run the PHP tests. rem rem Usage: -rem runtests.bat -- auto-detect apxs/httpd/php-fpm on PATH -rem runtests.bat --apxs C:\path\apxs -- point at a specific build -rem runtests.bat tests\t\modules -- run a subset (any pytest args pass through) -rem runtests.bat -k status -v -- extra pytest args pass through too +rem runtests.bat -- auto-detect apxs/httpd/php-fpm/perl on PATH +rem runtests.bat --apxs C:\path\apxs -- point at a specific build +rem runtests.bat --with-perl C:\path\perl.exe -- point at a specific perl +rem runtests.bat tests\t\modules -- run a subset (any pytest args pass through) +rem runtests.bat -k status -v -- extra pytest args pass through too rem rem Environment overrides (used when the matching --flag is not supplied): -rem APXS path to apxs -rem HTTPD path to httpd -rem PHP_FPM path to php-fpm +rem APXS path to apxs +rem HTTPD path to httpd +rem PHP_FPM path to php-fpm +rem APACHE_TEST_PERL path to perl rem setlocal enabledelayedexpansion @@ -94,6 +96,14 @@ if not defined PHP_FPM ( ) ) +if not defined APACHE_TEST_PERL ( + for %%C in (perl.exe) do ( + for /f "delims=" %%P in ('where %%C 2^>nul') do ( + if "!APACHE_TEST_PERL!"=="" set "APACHE_TEST_PERL=%%P" + ) + ) +) + rem Build the default flag set only for values the user did not pass explicitly. set "AUTO_ARGS=" @@ -130,6 +140,21 @@ if "%HAS_PHP_FLAG%"=="0" ( ) ) +rem Check if user passed --with-perl on the command line. +set "HAS_PERL_FLAG=0" +for %%A in (%*) do ( + echo %%A | findstr /b /c:"--with-perl" >nul 2>&1 && set "HAS_PERL_FLAG=1" +) + +if "%HAS_PERL_FLAG%"=="0" ( + if defined APACHE_TEST_PERL ( + set "AUTO_ARGS=!AUTO_ARGS! --with-perl=!APACHE_TEST_PERL!" + ) else ( + echo runtests.bat: note: no perl found; rewrite prg-map tests may fail. >&2 + echo Pass --with-perl=C:\path\to\perl.exe or set APACHE_TEST_PERL=... >&2 + ) +) + rem A stale mod_cgid socket in t\logs can break a fresh run; clear it first. del /q "%HERE%\t\logs\cgisock*" 2>nul diff --git a/test/pytest_suite/tests/t/ab/test_base.py b/test/pytest_suite/tests/t/ab/test_base.py index 1d7e2e2ee7..3869644b8a 100644 --- a/test/pytest_suite/tests/t/ab/test_base.py +++ b/test/pytest_suite/tests/t/ab/test_base.py @@ -18,7 +18,10 @@ import pytest def _ab_path(http) -> Path: - return Path(http.config.info.httpd).parent / "ab" + ab = Path(http.config.info.httpd).parent / "ab" + if not ab.exists() and ab.with_suffix(".exe").exists(): + return ab.with_suffix(".exe") + return ab def _run_ab(ab: Path, url: str) -> subprocess.CompletedProcess: diff --git a/test/pytest_suite/tests/t/modules/test_access.py b/test/pytest_suite/tests/t/modules/test_access.py index 4ae66ab7b7..d1d1f33823 100644 --- a/test/pytest_suite/tests/t/modules/test_access.py +++ b/test/pytest_suite/tests/t/modules/test_access.py @@ -6,12 +6,12 @@ served (GET_OK, i.e. 200) per the access-control logic. Perl original gated on ``\&need_access`` (the Order/Allow/Deny directives live in mod_access_compat on 2.4); gated here with @need_module. - -Note: the Perl framework exposes vars->{remote_addr}; the Python config does -not (framework-API gap), so we fall back to the loopback 127.0.0.1 the test -server actually connects from. """ +import sys + +import pytest + from apache_pytest import need_module ORDERS = ["deny,allow", "allow,deny", "mutual-failure"] @@ -55,6 +55,8 @@ def _write_htaccess(http, conf): f.write(conf) +@pytest.mark.skipif(sys.platform == "win32", + reason="dual-stack localhost makes Allow/Deny unreliable") @need_module("mod_access_compat") def test_access(http): clauses, addr1, localhost_name = _localhost_clauses(http) diff --git a/test/pytest_suite/tests/t/modules/test_authz_core.py b/test/pytest_suite/tests/t/modules/test_authz_core.py index 26c2cb747f..4165e39912 100644 --- a/test/pytest_suite/tests/t/modules/test_authz_core.py +++ b/test/pytest_suite/tests/t/modules/test_authz_core.py @@ -21,9 +21,9 @@ import httpx from apache_pytest import need_min_apache_version, need_module BASIC1 = ( - "user1:NYSYdf7MU5KpU\n" - "user2:KJ7Yxzr1VVzAI\n" - "user3:xnpSvZ2iqti/c\n" + "user1:$apr1$1Yd.TmyR$s/gcvsI8Ikrgqy6w0fyQW1\n" + "user2:$apr1$YOeJr2Nf$.4bJ1bv6ndkOj1ilkaz76/\n" + "user3:$apr1$5ygaekD5$XbW3dLYcBJ6KF09QMR9ol0\n" ) GROUPS1 = ( "user1:user1\n" diff --git a/test/pytest_suite/tests/t/modules/test_proxy.py b/test/pytest_suite/tests/t/modules/test_proxy.py index b06cced580..a5e16d0a40 100644 --- a/test/pytest_suite/tests/t/modules/test_proxy.py +++ b/test/pytest_suite/tests/t/modules/test_proxy.py @@ -91,11 +91,12 @@ def test_proxy_cgi(http): assert t_cmp(r.text, re.compile(r"QUERY_STRING = reverse-proxy\r?\n", re.S)), \ "reverse proxied query string OK" - r = http.GET("/reverse/modules/cgi/nph-dripfeed.pl") - assert t_cmp(r.status_code, 200), "reverse proxy to dripfeed CGI" - assert t_cmp(r.text, "abcdef"), "reverse proxied to dripfeed CGI content OK" + if sys.platform != "win32": + r = http.GET("/reverse/modules/cgi/nph-dripfeed.pl") + assert t_cmp(r.status_code, 200), "reverse proxy to dripfeed CGI" + assert t_cmp(r.text, "abcdef"), "reverse proxied to dripfeed CGI content OK" - if http.have_min_apache_version("2.1.0"): + if http.have_min_apache_version("2.1.0") and sys.platform != "win32": # nph-102.pl emits a 102 Processing interim response then a 200. # The Perl test asserted the LWP-surfaced code 102 (empty body) and # noted LWP needed fixing for 1xx. httpx correctly consumes the