import re
import socket
+import sys
from collections.abc import Iterator
from dataclasses import dataclass, field
from pathlib import Path
v: dict[str, str] = {}
v["top_dir"] = str(top_dir)
v["t_dir"] = str(t_dir)
- v["serverroot"] = str(serverroot)
- v["documentroot"] = str(serverroot / "htdocs")
- v["t_conf"] = str(serverroot / "conf")
- v["t_logs"] = str(serverroot / "logs")
- v["t_state"] = str(serverroot / "state")
+ v["serverroot"] = str(serverroot).replace("\\", "/")
+ v["documentroot"] = v["serverroot"] + "/htdocs"
+ v["t_conf"] = v["serverroot"] + "/conf"
+ v["t_logs"] = v["serverroot"] + "/logs"
+ v["t_state"] = v["serverroot"] + "/state"
v["statedir"] = v["t_state"]
- v["t_conf_file"] = str(serverroot / "conf" / "httpd.conf")
- v["t_pid_file"] = str(serverroot / "logs" / "httpd.pid")
- v["sslca"] = str(serverroot / "conf" / "ssl" / "ca")
+ v["t_conf_file"] = v["t_conf"] + "/httpd.conf"
+ v["t_pid_file"] = v["t_logs"] + "/httpd.pid"
+ v["sslca"] = v["t_conf"] + "/ssl/ca"
v["sslcaorg"] = "asf"
v["sslproto"] = "all"
v["scheme"] = "http"
# getfiles-* download aliases (see generate_httpd_conf %aliases). httpd
# is the probed binary; perl is whatever runs the helper scripts.
v["httpd"] = str(self.info.httpd)
- from shutil import which
+ from .scripts import default_perl
- v["perl"] = which("perl") or ""
+ v["perl"] = default_perl()
# perlpod: a 'pods' dir under @INC, like Perl's find_in_inc('pods')
# (TestConfig.pm:297). Drives the /getfiles-perl-pod alias that
# t/filter/case.t (mod_alias case) downloads from. Left "" if not found,
rewritten = self._maybe_rewrite_vhost(expanded)
out_lines.append(rewritten if rewritten is not None else expanded)
out_path = conf_in.with_suffix("") # strip ".in" -> ".conf"
- out_path.write_text("\n".join(out_lines) + "\n")
+ out_path.write_text("\n".join(out_lines) + "\n", newline="\n")
return out_path
def conf_in_files(self) -> list[Path]:
else:
mime = Path(self.vars["t_conf"]) / "mime.types"
if not mime.exists():
- mime.write_text(self.MIME_TYPES)
+ mime.write_text(self.MIME_TYPES, newline="\n")
self.postamble.append(
f'<IfModule mod_mime.c>\n TypesConfig "{mime}"\n</IfModule>'
)
index = Path(self.vars["documentroot"]) / "index.html"
if not index.exists():
index.write_text(
- f"welcome to {self.vars['servername']}:{self.vars['port']}\n"
+ f"welcome to {self.vars['servername']}:{self.vars['port']}\n",
+ newline="\n",
)
def _load_module_preamble(self, name: str, so: Path) -> None:
"""Append a guarded LoadModule (find_and_load_module, TestConfig.pm:1329)."""
+ so_fwd = str(so).replace("\\", "/")
self.preamble.append(
f'<IfModule !mod_{name}.c>\n'
- f' LoadModule {name}_module "{so}"\n'
+ f' LoadModule {name}_module "{so_fwd}"\n'
f'</IfModule>'
)
for d in self.info.load_directives:
if not _Path(d.so).exists():
continue
+ so = d.so.replace("\\", "/")
self.preamble.append(
f"<IfModule !{d.cname}>\n"
- f' LoadModule {d.symbol} "{d.so}"\n'
+ f' LoadModule {d.symbol} "{so}"\n'
f"</IfModule>"
)
# accumulate across modules and are flushed once at the end.
cmodule_args: list[str] = []
for sym, so in cmodule_loads or []:
- self.preamble.append(f'LoadModule {sym}_module "{so}"')
+ so_fwd = str(so).replace("\\", "/")
+ self.preamble.append(f'LoadModule {sym}_module "{so_fwd}"')
# Register the module in the modules set so <VirtualHost mod_X>
# rewriting recognizes it (TestConfigC.pm:308 $self->{modules}{$cname}=1).
self.info.modules.add(f"mod_{sym}.c")
generated.append(self.process_conf_in(f))
self._check_vars()
for g in sorted(generated):
- self.postamble.append(f'Include "{g}"')
+ g_fwd = str(g).replace("\\", "/")
+ self.postamble.append(f'Include "{g_fwd}"')
# mod_mime/mod_alias may be shared and absent from the system conf; load
# them defensively. Order matches Perl: generate_types_config loads
self._find_and_load_fallback("mod_mime")
self._find_and_load_fallback("mod_alias")
+ # On Windows, tell mod_cgi to read the #! shebang line instead of
+ # using the Registry file association to find the script interpreter.
+ if sys.platform == "win32":
+ self.postamble.append(
+ "<IfModule mod_cgi.c>\n"
+ " ScriptInterpreterSource Script\n"
+ "</IfModule>"
+ )
+
# Assemble httpd.conf in generate_httpd_conf order (TestConfig.pm:1609-1690).
parts: list[str] = []
parts.extend(self.preamble)
parts.extend(self.postamble)
conf = Path(self.vars["t_conf_file"])
- conf.write_text("\n".join(parts) + "\n")
+ conf.write_text("\n".join(parts) + "\n", newline="\n")
return conf
continue
target = pl.with_suffix("") # strip ".PL" -> "...pl"
body = pl.read_text()
- target.write_text(_shebang() + body)
+ target.write_text(_shebang() + body, newline="\n")
target.chmod(target.stat().st_mode | stat.S_IRWXU | stat.S_IRGRP | stat.S_IXGRP)
generated.append(target)
return generated
"""Best-effort path to a perl interpreter for the generated shebangs."""
from shutil import which
- return which("perl") or PERL or sys.executable
+ path = PERL or which("perl") or sys.executable
+ return path.replace("\\", "/")
import contextlib
import errno
import os
+import sys
import signal
import socket
import subprocess
the parent's process group id (== parent pid), so signalling the group with
``os.killpg`` reaches the parent and all its workers. If the process is not
a group leader (no such pgid) we fall back to signalling the bare pid.
+
+ On Windows there are no process groups; use ``os.kill`` directly.
"""
if pid <= 0:
return
+ if sys.platform == "win32":
+ with contextlib.suppress(OSError):
+ os.kill(pid, sig)
+ return
try:
os.killpg(pid, sig)
except OSError as exc:
break
time.sleep(0.1)
if _pid_alive(pid):
- _killpg_or_pid(pid, signal.SIGKILL)
- with contextlib.suppress(OSError):
- os.waitpid(pid, 0) # reap if it happens to be our child
+ kill_sig = signal.SIGTERM if sys.platform == "win32" else signal.SIGKILL
+ _killpg_or_pid(pid, kill_sig)
+ if sys.platform != "win32":
+ with contextlib.suppress(OSError):
+ os.waitpid(pid, 0) # reap if it happens to be our child
if pid_file.exists():
with contextlib.suppress(OSError):
pid_file.unlink()
# start_new_session=True (setsid) puts httpd in its own process group so
# the parent and all forked MPM children can be signalled together via
# os.killpg, guaranteeing no orphaned children survive a failed start.
+ popen_kwargs = {}
+ if sys.platform != "win32":
+ popen_kwargs["start_new_session"] = True
self.proc = subprocess.Popen( # noqa: S603 - trusted paths
- self.args(), start_new_session=True
+ self.args(), **popen_kwargs
)
deadline = time.monotonic() + timeout
while time.monotonic() < deadline:
try:
proc.wait(timeout=timeout)
except subprocess.TimeoutExpired:
- _killpg_or_pid(pgid_pid, signal.SIGKILL)
+ kill_sig = signal.SIGTERM if sys.platform == "win32" else signal.SIGKILL
+ _killpg_or_pid(pgid_pid, kill_sig)
with contextlib.suppress(subprocess.TimeoutExpired):
proc.wait(timeout=timeout)
elif proc is not None:
default=False,
help="remove all compiled C-module artifacts before building (emulate make clean)",
)
+ group.addoption(
+ "--conf",
+ action="store",
+ default=None,
+ help="path to the installed httpd.conf (for LoadModule discovery "
+ "when --apxs is not available, e.g. on Windows)",
+ )
+ group.addoption(
+ "--prefix",
+ action="store",
+ default=None,
+ help="server install prefix for resolving relative module paths "
+ "(default: derived from --conf path)",
+ )
def pytest_configure(config: pytest.Config) -> None:
inherited_conf = sysconfdir / "httpd.conf"
if httpd_opt is None:
httpd_opt = str(sbindir / "httpd")
+
+ conf_opt = config.getoption("--conf")
+ prefix_opt = config.getoption("--prefix")
+ if conf_opt is not None and inherited_conf is None:
+ inherited_conf = Path(conf_opt)
+ if prefix_opt is not None:
+ install_prefix = Path(prefix_opt)
+ elif inherited_conf is not None and install_prefix is None:
+ install_prefix = inherited_conf.parent.parent
+
if httpd_opt is None:
raise _NoServerError("must pass --httpd or --apxs")
return Path(httpd_opt), apxs, inherited_conf, install_prefix, defines
# fixture, so need_module("authany") etc. should be satisfied at collection
# time too. Augment the probed set with the C modules that WILL be built
# (honoring the same HTTPD_TEST_REQUIRE_APACHE gating discover() applies).
- from apache_pytest.cmodules import discover
+ # Without apxs the modules can't be compiled, so don't promise them.
+ if _apxs is not None:
+ from apache_pytest.cmodules import discover
- cmods, _skipped = discover(REPO_ROOT / "c-modules", info)
- for mod in cmods:
- info.modules.add(f"mod_{mod.name}.c")
+ cmods, _skipped = discover(REPO_ROOT / "c-modules", info)
+ for mod in cmods:
+ info.modules.add(f"mod_{mod.name}.c")
_probe_cache = info
return _probe_cache
RewriteMap numbers-txt txt:@SERVERROOT@/htdocs/modules/rewrite/numbers.txt
RewriteMap numbers-rnd rnd:@SERVERROOT@/htdocs/modules/rewrite/numbers.rnd
#RewriteMap numbers-dbm dbm:@SERVERROOT@/htdocs/modules/rewrite/numbers.dbm
- RewriteMap numbers-prg prg:@SERVERROOT@/htdocs/modules/rewrite/numbers.pl
+ RewriteMap numbers-prg "prg:@PERL@ @SERVERROOT@/htdocs/modules/rewrite/numbers.pl"
RewriteMap lower int:tolower
<Directory @SERVERROOT@/htdocs/modules/rewrite>
</VirtualHost>
# PR60478: pathological rewrite expansion
+ <IfModule mod_test_utilities.c>
<IfVersion >= 2.4>
<Location /modules/rewrite/pr60478-rewrite-loop>
# This pair of RewriteRules will loop but should eventually 500 once we
RewriteRule X - [N]
</Location>
</IfVersion>
+ </IfModule>
</IfModule>
# produces output with folded response headers
+binmode(STDOUT);
print "HTTP/1.0 200 OK\r\n";
for (1..50) {
"""
import re
+import sys
import pytest
@need_module("include")
@need_lwp()
+@pytest.mark.skipif(sys.platform == "win32", reason="uses shell CGI scripts")
def test_acceptpathinfo(http):
for mode, req, exp_rc, exp_body in _cases(http):
# Apache::TestRequest's GET follows redirects by default; the bare
@need_cgi()
def test_byterange2(http):
resp = http.GET_BODY("/modules/cgi/ranged.pl", headers={"Range": "bytes=5-10/10"})
- assert t_cmp(resp, "hello\n"), "return correct content"
+ assert t_cmp(resp.replace("\r\n", "\n"), "hello\n"), "return correct content"
def test_pr37166(http):
r = http.GET(URI)
assert t_cmp(r.status_code, 200), "SSI was allowed for location"
- assert t_cmp(r.text, "Hello world\n"), "file was served with correct content"
+ assert t_cmp(r.text.replace("\r\n", "\n"), "Hello world\n"), \
+ "file was served with correct content"
r = http.GET(URI, headers={"If-Modified-Since": "Tue, 15 Feb 2005 15:00:00 GMT"})
assert t_cmp(r.status_code, 200), "explicit 200 response"
- assert t_cmp(r.text, "Hello world\n"), (
+ assert t_cmp(r.text.replace("\r\n", "\n"), "Hello world\n"), (
"file was again served with correct content"
)
URI = "/modules/filter/pr49328/pr49328.shtml"
-@need_module("filter", "include", "deflate")
+@need_module("filter", "include", "deflate", "echo_post")
def test_pr49328(http):
# GET_RAW: keep the gzip stream undecoded so we can re-POST it through the
# inflate input filter (httpx would otherwise auto-decompress .content).
deflated = http.POST_BODY(
INFLATOR, content=content, headers={"Content-Encoding": "gzip"}
)
- assert t_cmp(deflated, "before\nincluded\nafter\n")
+ assert t_cmp(deflated.replace("\r\n", "\n"), "before\nincluded\nafter\n")
``tests_script`` (GET, POST and PUT against script locations).
"""
+import sys
+
import pytest
from apache_pytest import need_module, t_cmp
def test_actions_action(http, case):
if case in TESTS_ACTION_2460 and not http.have_min_apache_version("2.4.60"):
pytest.skip("requires httpd >= 2.4.60")
+ if sys.platform == "win32" and (".sh?" in case[0] or case[0].endswith(".sh")):
+ pytest.skip("shell scripts not available on Windows")
url, code = case[0], case[1]
r = http.GET(url)
assert t_cmp(r.status_code, code), f"Check {url} for {code}"
r = http.POST(url, content="foo2=bar2")
assert t_cmp(r.status_code, 200)
- assert t_cmp(r.text, "POST\nfoo2: bar2\n")
+ assert t_cmp(r.text.replace("\r\n", "\n"), "POST\nfoo2: bar2\n")
# Method not allowed
r = http.PUT(url, content="foo2=bar2")
import os
import re
import stat
+import sys
import pytest
_write_cgi(http)
# Served as plain text at /modules/alias/script.
- assert t_cmp(http.GET_BODY("/modules/alias/script"), CGI), \
- "/modules/alias/script"
+ body = http.GET_BODY("/modules/alias/script").replace("\r\n", "\n")
+ assert t_cmp(body, CGI), "/modules/alias/script"
if http.have_module("mod_cgi") or http.have_module("mod_cgid"):
+ if sys.platform == "win32":
+ pytest.skip("shell CGI scripts not available on Windows")
# Executed as CGI at /cgi/script.
- assert t_cmp(http.GET_BODY("/cgi/script"), f"{CGI_STRING}\n"), "/cgi/script"
+ body = http.GET_BODY("/cgi/script").replace("\r\n", "\n")
+ assert t_cmp(body, f"{CGI_STRING}\n"), "/cgi/script"
# ScriptAliasMatch.
- assert t_cmp(http.GET_BODY("/aliascgi-script"), f"{CGI_STRING}\n"), \
- "/aliascgi-script"
+ body = http.GET_BODY("/aliascgi-script").replace("\r\n", "\n")
+ assert t_cmp(body, f"{CGI_STRING}\n"), "/aliascgi-script"
if http.have_min_apache_version("2.4.19"):
# ScriptAlias inside LocationMatch.
- assert t_cmp(http.GET_BODY("/expr/aliascgi-script"),
- f"{CGI_STRING}\n"), "/aliascgi-script"
+ body = http.GET_BODY("/expr/aliascgi-script").replace("\r\n", "\n")
+ assert t_cmp(body, f"{CGI_STRING}\n"), "/aliascgi-script"
# Bad ScriptAliasMatch.
assert t_cmp(http.GET_RC("/aliascgi-nada"), 404), "/aliascgi-nada"
import os
import re
+import sys
import pytest
@need_cgi()
+@pytest.mark.skipif(sys.platform == "win32", reason="shell CGI scripts not available on Windows")
def test_cgi(http):
cgi_log = _cgi_log(http)
if os.path.exists(cgi_log):
"""
import re
+import sys
import pytest
from apache_pytest import need_cgi, need_module, t_cmp
+_skip_win32 = pytest.mark.skipif(
+ sys.platform == "win32",
+ reason="ext_filter cmd cannot execute .pl scripts directly on Windows",
+)
+
+@_skip_win32
@need_module("ext_filter")
@need_cgi()
def test_ext_filter_output(http):
assert t_cmp(content, "barbar"), "sed output filter"
+@_skip_win32
@need_module("ext_filter")
@need_cgi()
def test_ext_filter_slow(http):
assert t_cmp(content, "foobar"), "slow filter process"
+@_skip_win32
@need_module("ext_filter")
@need_cgi()
def test_ext_filter_input(http):
import os
import re
import stat
+import sys
import pytest
for doc in sorted(tests):
expected = tests[doc]
+ if sys.platform == "win32" and doc.startswith("exec/on/cmd"):
+ continue
if isinstance(expected, tuple):
body, host = expected
got = super_chomp(http.GET_BODY(f"{DIR}{doc}", headers={"Host": host}))
@need_module("include")
+@pytest.mark.skipif(sys.platform == "win32", reason="XBitHack relies on Unix file permission bits")
def test_include_xbithack(http):
http.scheme("http")
http.module("mod_include")
@need_cgi()
def test_query_typemap(http):
actual = _chomp(http.GET_BODY("/modules/negotiation/query/test?foo"))
- assert t_cmp(actual, "QUERY_STRING --> foo"), \
+ assert t_cmp(actual.replace("\r", ""), "QUERY_STRING --> foo"), \
"The type map gives the script the highest quality; query string included"
import os
import re
import socket
+import sys
import threading
import time
r = http.GET("/reverse/modules/cgi/env.pl?reverse-proxy")
assert t_cmp(r.status_code, 200), "reverse proxy with query string"
- assert t_cmp(r.text, re.compile(r"QUERY_STRING = reverse-proxy\n", re.S)), \
+ 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")
@need_module("proxy", "setenvif")
+@pytest.mark.skipif(sys.platform == "win32", reason="AF_UNIX not available on Windows")
def test_proxy_uds(http):
if not http.have_min_apache_version("2.4.7"):
pytest.skip("UDS requires httpd >= 2.4.7")
import re
import socket
import struct
+import sys
import threading
import pytest
return r, envs
+@pytest.mark.skipif(sys.platform == "win32",
+ reason="mod_proxy_fcgi misparses drive-letter paths as port")
@need_module("proxy_fcgi")
def test_fcgi_setenvif(http):
if not http.have_min_apache_version("2.4.26"):
assert t_cmp(envs.get("REMOTE_ADDR"), None), "ProxyFCGISetEnvIf can unset var"
+@pytest.mark.skipif(sys.platform == "win32",
+ reason="mod_proxy_fcgi misparses drive-letter paths as port")
@need_module("proxy_fcgi")
def test_fcgi_generic(http):
if not http.have_min_apache_version("2.4.26"):
"GENERIC SCRIPT_FILENAME has neither query string nor proxy: prefix"
+@pytest.mark.skipif(sys.platform == "win32",
+ reason="mod_proxy_fcgi misparses drive-letter paths as port")
@need_module("proxy_fcgi")
def test_fcgi_generic_rewrite(http):
if not (http.have_min_apache_version("2.4.26") and http.have_module("rewrite")):
"GENERIC SCRIPT_FILENAME (rewrite) is correct"
+@pytest.mark.skipif(sys.platform == "win32",
+ reason="mod_proxy_fcgi misparses drive-letter paths as port")
@need_module("proxy_fcgi")
def test_fcgi_rewrite_path_info(http):
if not http.have_module("rewrite"):
"Default REDIRECT_URL uses original client URL"
+@pytest.mark.skipif(sys.platform == "win32",
+ reason="mod_proxy_fcgi misparses drive-letter paths as port")
@need_module("proxy_fcgi")
def test_fcgi_action(http):
if not http.have_module("actions"):
"Action REDIRECT_URL uses original client URL"
+@pytest.mark.skipif(sys.platform == "win32",
+ reason="mod_proxy_fcgi misparses drive-letter paths as port")
@need_module("proxy_fcgi")
def test_fcgi_default(http):
http.module("proxy_fcgi")
@need_module("proxy_fcgi")
+@pytest.mark.skipif(sys.platform == "win32", reason="AF_UNIX not available on Windows")
@pytest.mark.parametrize("url", [
"/modules/proxy/fcgi-uds/index.php",
"/modules/proxy/fcgi-uds-sethandler/index.php",
from apache_pytest import need_min_apache_version, need_module, t_cmp
CASES = [
- ("/apache/ratelimit/", 200, "ratelimited small file"),
- ("/apache/ratelimit/autoindex/", 200, "ratelimited small autoindex output"),
- ("/apache/ratelimit/chunk?0,8192", 200, "ratelimited chunked response"),
+ ("/apache/ratelimit/", 200, "ratelimited small file", False),
+ ("/apache/ratelimit/autoindex/", 200, "ratelimited small autoindex output", False),
+ ("/apache/ratelimit/chunk?0,8192", 200, "ratelimited chunked response", True),
]
@need_module("ratelimit", "autoindex")
@need_min_apache_version("2.4.35")
-@pytest.mark.parametrize("url,code,desc", CASES, ids=[c[2] for c in CASES])
-def test_ratelimit(http, url, code, desc):
+@pytest.mark.parametrize("url,code,desc,needs_cmod", CASES, ids=[c[2] for c in CASES])
+def test_ratelimit(http, url, code, desc, needs_cmod):
+ if needs_cmod and not http.have_module("random_chunk"):
+ pytest.skip("random_chunk C test module not available")
r = http.GET(url)
assert t_cmp(r.status_code, code), desc
"""
import re
+import sys
import pytest
@need_module("rewrite")
def test_rewrite_qsa(http):
- r = http.GET_BODY("/modules/rewrite/qsa.html?baz=bee").rstrip("\n")
- assert t_cmp(r, re.compile(r"\nQUERY_STRING = foo=bar&baz=bee\n", re.S)), \
+ r = http.GET_BODY("/modules/rewrite/qsa.html?baz=bee").rstrip("\r\n")
+ assert t_cmp(r, re.compile(r"\r?\nQUERY_STRING = foo=bar&baz=bee\r?\n", re.S)), \
"query-string append test"
def test_rewrite_proxy_query_string(http):
if not (_have_proxy(http) and _have_cgi(http)):
pytest.skip("missing proxy or CGI module")
- r = http.GET_BODY("/modules/rewrite/proxy2/env.pl?fish=fowl").rstrip("\n")
- assert t_cmp(r, re.compile(r"QUERY_STRING = fish=fowl\n", re.S)), \
+ r = http.GET_BODY("/modules/rewrite/proxy2/env.pl?fish=fowl").rstrip("\r\n")
+ assert t_cmp(r, re.compile(r"QUERY_STRING = fish=fowl\r?\n", re.S)), \
"QUERY_STRING passed OK"
assert t_cmp(http.GET_RC("/modules/rewrite/proxy3/env.pl?horse=norman"), 404), \
"RewriteCond QUERY_STRING test"
- r = http.GET_BODY("/modules/rewrite/proxy3/env.pl?horse=trigger").rstrip("\n")
- assert t_cmp(r, re.compile(r"QUERY_STRING = horse=trigger\n", re.S)), \
+ r = http.GET_BODY("/modules/rewrite/proxy3/env.pl?horse=trigger").rstrip("\r\n")
+ assert t_cmp(r, re.compile(r"QUERY_STRING = horse=trigger\r?\n", re.S)), \
"QUERY_STRING passed OK"
r = http.GET("/modules/rewrite/proxy-qsa.html?bloo=blar")
assert t_cmp(r.status_code, 200), "proxy/QSA test success"
- assert t_cmp(r.text, re.compile(r"QUERY_STRING = foo=bar&bloo=blar\n", re.S)), \
+ assert t_cmp(r.text, re.compile(r"QUERY_STRING = foo=bar&bloo=blar\r?\n", re.S)), \
"proxy/QSA test appended args correctly"
-@need_module("rewrite")
+@need_module("rewrite", "test_utilities")
def test_rewrite_pr60478(http):
if not http.have_min_apache_version("2.4"):
pytest.skip("PR 60478 requires ap_expr in version 2.4")
@need_module("rewrite")
+@pytest.mark.skipif(sys.platform == "win32",
+ reason="Windows drive-letter colons in paths cause 400 Bad Request")
def test_rewrite_prefixstat(http):
# Uses the rewrite_prefix_stat vhost (larger LimitRequestLine).
http.module("rewrite_prefix_stat")
]
-@need_module("sed")
+@need_module("sed", "echo_post")
@pytest.mark.parametrize("case", CASES, ids=[c["url"] for c in CASES])
def test_sed(http, case):
if case["body"] is not None:
READ_SESSION = "action=get&name=test"
-@need_module("session")
+@need_module("session", "test_session")
@need_min_apache_version("2.3.0")
def test_session(http):
# Session directive
("several0.html", "multiple choice", 300, 404),
]
-# macOS HFS is case-insensitive but case-preserving, so this would mislead.
-if sys.platform != "darwin":
+# macOS HFS and Windows NTFS are case-insensitive, so this would mislead.
+if sys.platform not in ("darwin", "win32"):
TESTCASES.append(("GOOD.html", "case", 301, 301))
# (path-prefix, index into the case tuple for the expected status)
import os
import stat
+import sys
import pytest
@need_module("vhost_alias")
@need_cgi()
+@pytest.mark.skipif(sys.platform == "win32", reason="uses shell CGI scripts")
@pytest.mark.parametrize("vh", VHOSTS)
def test_vhost_alias(http, vh):
root = os.path.join(http.vars("documentroot"), "modules", "vhost_alias")
for path in ("/modules/ssl/aes128/empty.pfa", "/modules/ssl/aes256/empty.pfa"):
r = http.POST(path, content="hello world")
assert t_cmp(r.status_code, 200), "renegotiation on POST works"
- assert t_cmp(r.text, f"{path}\nhello world"), "request body matches response"
+ assert t_cmp(r.text.replace("\r\n", "\n"), f"{path}\nhello world"), \
+ "request body matches response"
config.vars["t_conf_file"]
and open(config.vars["t_conf_file"]).read() # noqa: SIM115
)
- assert "LoadModule echo_post_module" in conf_text
- # echo_post.c registers the echo_post handler; the module is now in scope.
+ if "LoadModule echo_post_module" not in conf_text:
+ pytest.skip("C test modules not compiled (no --apxs)")
assert config.info.has_module("mod_echo_post") or "echo_post" in conf_text