mirror of
https://github.com/3proxy/3proxy.git
synced 2026-11-05 01:15:48 +08:00
help: the features of the build listed, and the tests skip what they need
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
Some checks failed
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI Linux / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI MacOS / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI Windows / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (macos-15) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-24.04-arm) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (ubuntu-latest) (push) Has been cancelled
C/C++ CI cmake / ${{ matrix.target }} (windows-2022) (push) Has been cancelled
C/C++ CI cmake / ubuntu-latest (wolfSSL) (push) Has been cancelled
3proxy --help (and the usage message of a bad invocation) now lists what the build was made with: TLS and its backend, RADIUS, PCRE, IPv6, UNIX sockets, the built-in HTTP server, transparency, ODBC and the optional proxy services. The test runner reads the list before the first case and the cases state the features they need with t.skip_unless(), so a build without a feature skips its cases instead of failing them; a binary too old to report anything skips the cases with needs and runs the rest.
This commit is contained in:
parent
a75b488aaf
commit
629a978c00
32
src/3proxy.c
32
src/3proxy.c
@ -382,6 +382,21 @@ void cyclestep(void){
|
|||||||
#define RETURN(x) {res = x; goto CLEARRETURN;}
|
#define RETURN(x) {res = x; goto CLEARRETURN;}
|
||||||
|
|
||||||
|
|
||||||
|
static void printusage(struct extparam *cconf, int argc, char * argv[]){
|
||||||
|
|
||||||
|
fprintf(stderr, "Usage: %s [conffile]\n", argv[0]);
|
||||||
|
#ifdef _WIN32
|
||||||
|
fprintf(stderr, "\n\t%s --install [conffile]\n\tto install as service\n"
|
||||||
|
"\n\t%s --remove\n\tto remove service\n", argv[0], argv[0]);
|
||||||
|
#else
|
||||||
|
fprintf(stderr, "\n if conffile is missing, configuration is expected from stdin\n");
|
||||||
|
#endif
|
||||||
|
fprintf(stderr, "\n\t%s --help\n\tthis message\n", argv[0]);
|
||||||
|
fprintf(stderr, "available socket options:\n\t%s\n", printopts("\n\t"));
|
||||||
|
fprintf(stderr, "built with:\n\t%s\n", printfeatures("\n\t"));
|
||||||
|
fprintf(stderr, "\n%s %s\n%s\n", cconf->stringtable[2], cconf->stringtable[3], copyright);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifndef _WINCE
|
#ifndef _WINCE
|
||||||
int main(int argc, char * argv[]) {
|
int main(int argc, char * argv[]) {
|
||||||
@ -424,6 +439,10 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
conf.stringtable = strings;
|
conf.stringtable = strings;
|
||||||
|
if(argc == 2 && (!strcmp((char *)argv[1], "--help") || !strcmp((char *)argv[1], "-h"))) {
|
||||||
|
printusage(&conf, argc, argv);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
#ifndef _WINCE
|
#ifndef _WINCE
|
||||||
if((argc == 2 || argc == 3)&& !strcmp((char *)argv[1], "--install")) {
|
if((argc == 2 || argc == 3)&& !strcmp((char *)argv[1], "--install")) {
|
||||||
@ -536,18 +555,9 @@ int WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int
|
|||||||
}
|
}
|
||||||
if(argc > 2 || !(fp)) {
|
if(argc > 2 || !(fp)) {
|
||||||
|
|
||||||
fprintf(stderr, "Usage: %s [conffile]\n", argv[0]);
|
printusage(&conf, argc, argv);
|
||||||
#ifdef _WIN32
|
|
||||||
fprintf(stderr, "\n\t%s --install [conffile]\n\tto install as service\n"
|
|
||||||
"\n\t%s --remove\n\tto remove service\n", argv[0], argv[0]);
|
|
||||||
#else
|
|
||||||
fprintf(stderr, "\n if conffile is missing, configuration is expected from stdin\n");
|
|
||||||
#endif
|
|
||||||
fprintf(stderr, "available socket options:\n\t%s\n", printopts("\n\t"));
|
|
||||||
fprintf(stderr, "\n%s %s\n%s\n", conf.stringtable[2], conf.stringtable[3], copyright);
|
|
||||||
|
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
/* before readconfig() starts the services: a client gone while it is
|
/* before readconfig() starts the services: a client gone while it is
|
||||||
|
|||||||
@ -512,6 +512,7 @@ extern struct socketoptions {
|
|||||||
int getopts(const char *s, struct sockoptset *opts);
|
int getopts(const char *s, struct sockoptset *opts);
|
||||||
void setopts(SOCKET s, const struct sockoptset *opts);
|
void setopts(SOCKET s, const struct sockoptset *opts);
|
||||||
char * printopts(char *sep);
|
char * printopts(char *sep);
|
||||||
|
char * printfeatures(char *sep);
|
||||||
|
|
||||||
#ifdef _WINCE
|
#ifdef _WINCE
|
||||||
char * CEToUnicode (const char *str);
|
char * CEToUnicode (const char *str);
|
||||||
|
|||||||
@ -226,6 +226,69 @@ char * printopts(char *sep){
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char featsbuf[1024];
|
||||||
|
|
||||||
|
/* The features of this build, by the names the manual and the test suite
|
||||||
|
know them, so a user can see what a binary was made with. */
|
||||||
|
char * printfeatures(char *sep){
|
||||||
|
int pos=0;
|
||||||
|
|
||||||
|
#define ADDFEATURE(x) pos += sprintf(featsbuf+pos,"%s%s",pos?sep:"",x)
|
||||||
|
#ifdef WITH_SSL
|
||||||
|
ADDFEATURE("ssl");
|
||||||
|
#ifdef WITH_WOLFSSL
|
||||||
|
ADDFEATURE("wolfssl");
|
||||||
|
#else
|
||||||
|
ADDFEATURE("openssl");
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
#ifndef NORADIUS
|
||||||
|
ADDFEATURE("radius");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_PCRE
|
||||||
|
ADDFEATURE("pcre");
|
||||||
|
#endif
|
||||||
|
#ifndef NOIPV6
|
||||||
|
ADDFEATURE("ipv6");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_UN
|
||||||
|
ADDFEATURE("unix");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_HTTPSRV
|
||||||
|
ADDFEATURE("httpsrv");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_TRANSPARENT
|
||||||
|
ADDFEATURE("transparent");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_NETFILTER
|
||||||
|
ADDFEATURE("netfilter");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_PF
|
||||||
|
ADDFEATURE("pf");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_ODBC
|
||||||
|
ADDFEATURE("odbc");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_FTP
|
||||||
|
ADDFEATURE("ftppr");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_POP3P
|
||||||
|
ADDFEATURE("pop3p");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_SMTPP
|
||||||
|
ADDFEATURE("smtpp");
|
||||||
|
#endif
|
||||||
|
#ifdef WITH_IMAPP
|
||||||
|
ADDFEATURE("imapp");
|
||||||
|
#endif
|
||||||
|
#ifndef NOPLUGINS
|
||||||
|
ADDFEATURE("plugins");
|
||||||
|
#endif
|
||||||
|
#undef ADDFEATURE
|
||||||
|
return featsbuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* A comma separated list of options, each NAME or NAME=VALUE; a name alone
|
/* A comma separated list of options, each NAME or NAME=VALUE; a name alone
|
||||||
stands for 1, or for TCP_MAXSEG for the value of maxseg. Options are added
|
stands for 1, or for TCP_MAXSEG for the value of maxseg. Options are added
|
||||||
to what is already set, so a default is changed only by naming it, and
|
to what is already set, so a default is changed only by naming it, and
|
||||||
|
|||||||
@ -13,6 +13,14 @@ skips itself when that is missing or the build has no TLS support. With no
|
|||||||
`--bin` it looks in `bin/`, then `build/bin/`, then the per-configuration
|
`--bin` it looks in `bin/`, then `build/bin/`, then the per-configuration
|
||||||
directories a multi-configuration CMake generator uses.
|
directories a multi-configuration CMake generator uses.
|
||||||
|
|
||||||
|
The binary reports what it was built with in `3proxy --help` (a `built
|
||||||
|
with:` list), and the runner reads that before the first case: a case names
|
||||||
|
the features it needs with `if not t.skip_unless("ssl"): return`, and
|
||||||
|
skips on a build without them instead of failing. A binary too old to
|
||||||
|
report its features skips every case that needs one, and the cases with no
|
||||||
|
feature needs still run; some cases also probe at runtime, as a build can
|
||||||
|
leave a feature compiled in but unusable.
|
||||||
|
|
||||||
The proxy under test is also the origin server the tests talk to: the `http`
|
The proxy under test is also the origin server the tests talk to: the `http`
|
||||||
command's `echo` operation reports back how a request arrived - method, path,
|
command's `echo` operation reports back how a request arrived - method, path,
|
||||||
query, host, and the source port it came from - and `data` generates a body
|
query, host, and the source port it came from - and `data` generates a body
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import time
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
adm = t.free_port()
|
adm = t.free_port()
|
||||||
lim = t.free_port()
|
lim = t.free_port()
|
||||||
server = t.start("admin", f"""
|
server = t.start("admin", f"""
|
||||||
|
|||||||
@ -7,7 +7,9 @@ handshake and the service supplies the port.
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
certs = t.certs()
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
|
certs = t.certs() if t.has("ssl") else None
|
||||||
plain = t.free_port()
|
plain = t.free_port()
|
||||||
port = t.free_port()
|
port = t.free_port()
|
||||||
secure = t.free_port() if certs else None
|
secure = t.free_port() if certs else None
|
||||||
|
|||||||
@ -60,6 +60,8 @@ def reply(f):
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("ftppr"):
|
||||||
|
return
|
||||||
servers = {}
|
servers = {}
|
||||||
for name, (user, password) in LOGINS.items():
|
for name, (user, password) in LOGINS.items():
|
||||||
listener = socket.socket()
|
listener = socket.socket()
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
srv = t.free_port()
|
srv = t.free_port()
|
||||||
openport = t.free_port()
|
openport = t.free_port()
|
||||||
t.start("httpsrv_auth", f"""
|
t.start("httpsrv_auth", f"""
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import os
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
root = os.path.join(t.tmpdir, "web")
|
root = os.path.join(t.tmpdir, "web")
|
||||||
os.makedirs(os.path.join(root, "picts", "set"), exist_ok=True)
|
os.makedirs(os.path.join(root, "picts", "set"), exist_ok=True)
|
||||||
with open(os.path.join(root, "a.html"), "w") as fp:
|
with open(os.path.join(root, "a.html"), "w") as fp:
|
||||||
|
|||||||
@ -10,6 +10,8 @@ import os
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
root = os.path.join(t.tmpdir, "ka")
|
root = os.path.join(t.tmpdir, "ka")
|
||||||
os.makedirs(root, exist_ok=True)
|
os.makedirs(root, exist_ok=True)
|
||||||
with open(os.path.join(root, "a.html"), "w") as fp:
|
with open(os.path.join(root, "a.html"), "w") as fp:
|
||||||
|
|||||||
@ -4,6 +4,8 @@ import time
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
srv = t.free_port()
|
srv = t.free_port()
|
||||||
t.start("httpsrv_ops", f"""
|
t.start("httpsrv_ops", f"""
|
||||||
log
|
log
|
||||||
|
|||||||
@ -6,6 +6,8 @@ most of them away before they ever reached the server.
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
srv = t.free_port()
|
srv = t.free_port()
|
||||||
t.start("httpsrv_parsing", f"""
|
t.start("httpsrv_parsing", f"""
|
||||||
log
|
log
|
||||||
|
|||||||
@ -9,6 +9,8 @@ import os
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
root = os.path.join(t.tmpdir, "pp")
|
root = os.path.join(t.tmpdir, "pp")
|
||||||
os.makedirs(root, exist_ok=True)
|
os.makedirs(root, exist_ok=True)
|
||||||
with open(os.path.join(root, "a.html"), "w") as fp:
|
with open(os.path.join(root, "a.html"), "w") as fp:
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
srv = t.free_port()
|
srv = t.free_port()
|
||||||
srv2 = t.free_port()
|
srv2 = t.free_port()
|
||||||
t.start("httpsrv_rules", f"""
|
t.start("httpsrv_rules", f"""
|
||||||
|
|||||||
@ -7,6 +7,8 @@ nscache6: nscache holds the IPv4 side and nothing else.
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv", "ipv6"):
|
||||||
|
return
|
||||||
if not t.has_ipv6():
|
if not t.has_ipv6():
|
||||||
t.skip("IPv6 (this machine has no IPv6 loopback)")
|
t.skip("IPv6 (this machine has no IPv6 loopback)")
|
||||||
return
|
return
|
||||||
|
|||||||
@ -8,6 +8,8 @@ import time
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
# The origin runs in a process of its own, so the limit only applies to
|
# The origin runs in a process of its own, so the limit only applies to
|
||||||
# the mapper in front of it.
|
# the mapper in front of it.
|
||||||
origin = t.free_port()
|
origin = t.free_port()
|
||||||
|
|||||||
@ -30,6 +30,8 @@ def wait_served(server, needle, count, since=0, timeout=5.0):
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
srv = t.free_port()
|
srv = t.free_port()
|
||||||
good = t.free_port()
|
good = t.free_port()
|
||||||
spare = t.free_port()
|
spare = t.free_port()
|
||||||
|
|||||||
@ -46,6 +46,8 @@ UNHONOURED = (1, 99)
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
srv = t.free_port()
|
srv = t.free_port()
|
||||||
prx = t.free_port()
|
prx = t.free_port()
|
||||||
sks = t.free_port()
|
sks = t.free_port()
|
||||||
|
|||||||
@ -37,6 +37,8 @@ def wait_served(server, needle, count, since=0, timeout=5.0):
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
adm = t.free_port()
|
adm = t.free_port()
|
||||||
prx = t.free_port()
|
prx = t.free_port()
|
||||||
dummy = t.free_port()
|
dummy = t.free_port()
|
||||||
|
|||||||
@ -20,6 +20,8 @@ def _has_pcre(t):
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv", "pcre"):
|
||||||
|
return
|
||||||
if not _has_pcre(t):
|
if not _has_pcre(t):
|
||||||
t.skip("PCRE (this build has no PCRE support)")
|
t.skip("PCRE (this build has no PCRE support)")
|
||||||
return
|
return
|
||||||
|
|||||||
@ -26,6 +26,8 @@ def halfclose_origin(listener):
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
# --- tcppm ---------------------------------------------------------
|
# --- tcppm ---------------------------------------------------------
|
||||||
origin = t.free_port()
|
origin = t.free_port()
|
||||||
mapped = t.free_port()
|
mapped = t.free_port()
|
||||||
|
|||||||
@ -6,6 +6,8 @@ from a clean list.
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
srv = t.free_port()
|
srv = t.free_port()
|
||||||
other = t.free_port()
|
other = t.free_port()
|
||||||
prx = t.free_port()
|
prx = t.free_port()
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
srv = t.free_port()
|
srv = t.free_port()
|
||||||
sks = t.free_port()
|
sks = t.free_port()
|
||||||
sauth = t.free_port()
|
sauth = t.free_port()
|
||||||
|
|||||||
@ -11,6 +11,8 @@ def _no_tls(t, server):
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv", "ssl"):
|
||||||
|
return
|
||||||
certs = t.certs()
|
certs = t.certs()
|
||||||
if not certs:
|
if not certs:
|
||||||
t.skip("TLS (openssl is not available to generate certificates)")
|
t.skip("TLS (openssl is not available to generate certificates)")
|
||||||
|
|||||||
@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv", "ssl"):
|
||||||
|
return
|
||||||
certs = t.certs()
|
certs = t.certs()
|
||||||
if not certs:
|
if not certs:
|
||||||
t.skip("tlspr (openssl is not available to generate certificates)")
|
t.skip("tlspr (openssl is not available to generate certificates)")
|
||||||
|
|||||||
@ -39,6 +39,8 @@ def _config(t, name, body):
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv"):
|
||||||
|
return
|
||||||
# --- the command and its modes ------------------------------------
|
# --- the command and its modes ------------------------------------
|
||||||
# These need no redirection, so they run wherever the feature is built.
|
# These need no redirection, so they run wherever the feature is built.
|
||||||
out = _config(t, "transparent_probe", "log\ntransparent")
|
out = _config(t, "transparent_probe", "log\ntransparent")
|
||||||
|
|||||||
@ -36,6 +36,8 @@ def unix_get(path, url, timeout=10):
|
|||||||
|
|
||||||
|
|
||||||
def run(t):
|
def run(t):
|
||||||
|
if not t.skip_unless("httpsrv", "unix"):
|
||||||
|
return
|
||||||
if not hasattr(socket, "AF_UNIX") or sys.platform.startswith("win"):
|
if not hasattr(socket, "AF_UNIX") or sys.platform.startswith("win"):
|
||||||
t.skip("Unix domain sockets (not on this platform)")
|
t.skip("Unix domain sockets (not on this platform)")
|
||||||
return
|
return
|
||||||
|
|||||||
@ -122,10 +122,11 @@ class Failure(Exception):
|
|||||||
class Tester:
|
class Tester:
|
||||||
"""The API a case runs against: start servers, make requests, assert."""
|
"""The API a case runs against: start servers, make requests, assert."""
|
||||||
|
|
||||||
def __init__(self, binary, tmpdir, case):
|
def __init__(self, binary, tmpdir, case, features=None):
|
||||||
self.binary = binary
|
self.binary = binary
|
||||||
self.tmpdir = tmpdir
|
self.tmpdir = tmpdir
|
||||||
self.case = case
|
self.case = case
|
||||||
|
self.features = features if features is not None else frozenset()
|
||||||
self.servers = []
|
self.servers = []
|
||||||
self.checks = []
|
self.checks = []
|
||||||
self.timeout = 10
|
self.timeout = 10
|
||||||
@ -135,6 +136,25 @@ class Tester:
|
|||||||
self.logs = []
|
self.logs = []
|
||||||
self.udp_servers = []
|
self.udp_servers = []
|
||||||
|
|
||||||
|
# ---- build features ----------------------------------------------
|
||||||
|
|
||||||
|
def has(self, *names):
|
||||||
|
"""Whether the binary reports every named feature."""
|
||||||
|
return all(name in self.features for name in names)
|
||||||
|
|
||||||
|
def skip_unless(self, *names):
|
||||||
|
"""Skip what needs a feature the binary was built without.
|
||||||
|
|
||||||
|
Returns False after recording the skip, so a case says
|
||||||
|
`if not t.skip_unless("ssl"): return` and keeps its checks out of
|
||||||
|
the failed count on a build that cannot pass them.
|
||||||
|
"""
|
||||||
|
missing = [name for name in names if name not in self.features]
|
||||||
|
if not missing:
|
||||||
|
return True
|
||||||
|
self.skip("built without: " + ", ".join(missing))
|
||||||
|
return False
|
||||||
|
|
||||||
# ---- servers -----------------------------------------------------
|
# ---- servers -----------------------------------------------------
|
||||||
|
|
||||||
def has_ipv6(self):
|
def has_ipv6(self):
|
||||||
|
|||||||
35
tests/run.py
35
tests/run.py
@ -47,6 +47,37 @@ def load_case(path):
|
|||||||
return name, module
|
return name, module
|
||||||
|
|
||||||
|
|
||||||
|
def built_features(binary):
|
||||||
|
"""The features the binary reports in `3proxy --help`, as a set.
|
||||||
|
|
||||||
|
A binary too old to have the list, or one that cannot be run at all,
|
||||||
|
reports nothing: every case that needs a feature then skips, and the
|
||||||
|
ones that need none still run.
|
||||||
|
"""
|
||||||
|
import subprocess
|
||||||
|
try:
|
||||||
|
done = subprocess.run([binary, "--help"], stdout=subprocess.PIPE,
|
||||||
|
stderr=subprocess.STDOUT, timeout=15)
|
||||||
|
text = done.stdout.decode("utf-8", "replace")
|
||||||
|
except (OSError, subprocess.SubprocessError):
|
||||||
|
return frozenset()
|
||||||
|
features = set()
|
||||||
|
collecting = False
|
||||||
|
for line in text.splitlines():
|
||||||
|
stripped = line.strip()
|
||||||
|
if line.startswith("built with:"):
|
||||||
|
collecting = True
|
||||||
|
rest = stripped[len("built with:"):].strip()
|
||||||
|
if rest:
|
||||||
|
features.update(rest.split())
|
||||||
|
continue
|
||||||
|
if collecting:
|
||||||
|
if not line[:1].isspace() or not stripped:
|
||||||
|
break
|
||||||
|
features.update(stripped.split())
|
||||||
|
return frozenset(features)
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument("pattern", nargs="?", default="",
|
parser.add_argument("pattern", nargs="?", default="",
|
||||||
@ -75,7 +106,9 @@ def main():
|
|||||||
return 2
|
return 2
|
||||||
|
|
||||||
tmpdir = tempfile.mkdtemp(prefix="3proxy-tests.")
|
tmpdir = tempfile.mkdtemp(prefix="3proxy-tests.")
|
||||||
|
features = built_features(binary)
|
||||||
print(f"3proxy tests: {binary}")
|
print(f"3proxy tests: {binary}")
|
||||||
|
print(f"built with: {' '.join(sorted(features)) or '(nothing reported)'}")
|
||||||
print(f"working in: {tmpdir}\n")
|
print(f"working in: {tmpdir}\n")
|
||||||
|
|
||||||
passed = failed = skipped = 0
|
passed = failed = skipped = 0
|
||||||
@ -85,7 +118,7 @@ def main():
|
|||||||
for path in paths:
|
for path in paths:
|
||||||
name, module = load_case(path)
|
name, module = load_case(path)
|
||||||
print(f" {name}")
|
print(f" {name}")
|
||||||
tester = Tester(binary, tmpdir, name)
|
tester = Tester(binary, tmpdir, name, features)
|
||||||
error = None
|
error = None
|
||||||
try:
|
try:
|
||||||
module.run(tester)
|
module.run(tester)
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user