From af59806a42a4d90fd79d5deecf73e5c254f1e1fc Mon Sep 17 00:00:00 2001 From: Justine Tunney Date: Mon, 3 May 2021 01:59:27 -0700 Subject: [PATCH] Add integration test for redbean --- libc/fmt/pflink.h | 28 ++++++++-------- libc/testlib/testlib.h | 3 ++ libc/testlib/testrunner.c | 37 +++++++++++++-------- test/tool/net/redbean_test.c | 63 ++++++++++++++++++++++++++++++++---- test/tool/net/test.mk | 16 +++++---- 5 files changed, 106 insertions(+), 41 deletions(-) diff --git a/libc/fmt/pflink.h b/libc/fmt/pflink.h index 40f7f2af..65c50315 100644 --- a/libc/fmt/pflink.h +++ b/libc/fmt/pflink.h @@ -18,20 +18,20 @@ * format strings are constexprs that only contain directives. */ -#define PFLINK(FMT) \ - ({ \ - if (___PFLINK(FMT, strpbrk, "faAeEgG")) STATIC_YOINK("__fmt_dtoa"); \ - if (___PFLINK(FMT, strpbrk, "cmrqs")) { \ - if (___PFLINK(FMT, strchr, '#')) STATIC_YOINK("kCp437"); \ - if (___PFLINK(FMT, strstr, "%m")) STATIC_YOINK("strerror"); \ - if (!IsTiny() && (___PFLINK(FMT, strstr, "%*") || \ - ___PFLINK(FMT, strpbrk, "0123456789"))) { \ - STATIC_YOINK("strnwidth"); \ - STATIC_YOINK("strnwidth16"); \ - STATIC_YOINK("wcsnwidth"); \ - } \ - } \ - FMT; \ +#define PFLINK(FMT) \ + ({ \ + if (___PFLINK(FMT, strpbrk, "faAeg")) STATIC_YOINK("__fmt_dtoa"); \ + if (___PFLINK(FMT, strpbrk, "cmrqs")) { \ + if (___PFLINK(FMT, strchr, '#')) STATIC_YOINK("kCp437"); \ + if (___PFLINK(FMT, strstr, "%m")) STATIC_YOINK("strerror"); \ + if (!IsTiny() && (___PFLINK(FMT, strstr, "%*") || \ + ___PFLINK(FMT, strpbrk, "0123456789"))) { \ + STATIC_YOINK("strnwidth"); \ + STATIC_YOINK("strnwidth16"); \ + STATIC_YOINK("wcsnwidth"); \ + } \ + } \ + FMT; \ }) #define SFLINK(FMT) \ diff --git a/libc/testlib/testlib.h b/libc/testlib/testlib.h index 408541d1..10a6f1f8 100644 --- a/libc/testlib/testlib.h +++ b/libc/testlib/testlib.h @@ -74,6 +74,7 @@ COSMOPOLITAN_C_START_ * and if the test succeeds it'll be removed along with any contents. */ extern char testlib_enable_tmp_setup_teardown; +extern char testlib_enable_tmp_setup_teardown_once; /** * User-defined test setup function. @@ -82,6 +83,7 @@ extern char testlib_enable_tmp_setup_teardown; * defined by the linkage. */ void SetUp(void); +void SetUpOnce(void); /** * User-defined test cleanup function. @@ -90,6 +92,7 @@ void SetUp(void); * defined by the linkage. */ void TearDown(void); +void TearDownOnce(void); /*───────────────────────────────────────────────────────────────────────────│─╗ │ cosmopolitan § testing library » assert or die ─╬─│┼ diff --git a/libc/testlib/testrunner.c b/libc/testlib/testrunner.c index 74f41d78..50bf8d8a 100644 --- a/libc/testlib/testrunner.c +++ b/libc/testlib/testrunner.c @@ -29,6 +29,10 @@ #include "libc/testlib/testlib.h" #include "libc/x/x.h" +static int x; +static char cwd[PATH_MAX]; +static char tmp[PATH_MAX]; + void testlib_finish(void) { if (g_testlib_failed) { fprintf(stderr, "%u / %u %s\n", g_testlib_failed, g_testlib_ran, @@ -42,6 +46,18 @@ wontreturn void testlib_abort(void) { unreachable; } +static void SetupTmpDir(void) { + snprintf(tmp, sizeof(tmp), "o/tmp/%s.%d.%d", program_invocation_short_name, + getpid(), x++); + CHECK_NE(-1, makedirs(tmp, 0755), "tmp=%s", tmp); + CHECK_NE(-1, chdir(tmp), "tmp=%s", tmp); +} + +static void TearDownTmpDir(void) { + CHECK_NE(-1, chdir(cwd)); + CHECK_NE(-1, rmrf(tmp)); +} + /** * Runs all test case functions in sorted order. */ @@ -62,18 +78,12 @@ testonly void testlib_runtestcases(testfn_t *start, testfn_t *end, * * @see ape/ape.lds */ - int x; - char cwd[PATH_MAX]; - char tmp[PATH_MAX]; const testfn_t *fn; + CHECK_NOTNULL(getcwd(cwd, sizeof(cwd))); + if (weaken(testlib_enable_tmp_setup_teardown_once)) SetupTmpDir(); + if (weaken(SetUpOnce)) weaken(SetUpOnce)(); for (x = 0, fn = start; fn != end; ++fn) { - if (weaken(testlib_enable_tmp_setup_teardown)) { - CHECK_NOTNULL(getcwd(cwd, sizeof(cwd))); - snprintf(tmp, sizeof(tmp), "o/tmp/%s.%d.%d", - program_invocation_short_name, getpid(), x++); - CHECK_NE(-1, makedirs(tmp, 0755), "tmp=%s", tmp); - CHECK_NE(-1, chdir(tmp), "tmp=%s", tmp); - } + if (weaken(testlib_enable_tmp_setup_teardown)) SetupTmpDir(); if (weaken(SetUp)) weaken(SetUp)(); errno = 0; SetLastError(0); @@ -83,9 +93,8 @@ testonly void testlib_runtestcases(testfn_t *start, testfn_t *end, (*fn)(); sys_getpid(); if (weaken(TearDown)) weaken(TearDown)(); - if (weaken(testlib_enable_tmp_setup_teardown)) { - CHECK_NE(-1, chdir(cwd)); - CHECK_NE(-1, rmrf(tmp)); - } + if (weaken(testlib_enable_tmp_setup_teardown)) TearDownTmpDir(); } + if (weaken(TearDownOnce)) weaken(TearDownOnce)(); + if (weaken(testlib_enable_tmp_setup_teardown_once)) TearDownTmpDir(); } diff --git a/test/tool/net/redbean_test.c b/test/tool/net/redbean_test.c index 38c111de..b722e788 100644 --- a/test/tool/net/redbean_test.c +++ b/test/tool/net/redbean_test.c @@ -19,20 +19,30 @@ #include "libc/calls/calls.h" #include "libc/calls/sigbits.h" #include "libc/fmt/conv.h" +#include "libc/log/check.h" #include "libc/runtime/gc.internal.h" #include "libc/runtime/runtime.h" +#include "libc/sock/sock.h" #include "libc/stdio/stdio.h" +#include "libc/sysv/consts/af.h" #include "libc/sysv/consts/auxv.h" +#include "libc/sysv/consts/inaddr.h" +#include "libc/sysv/consts/ipproto.h" #include "libc/sysv/consts/o.h" +#include "libc/sysv/consts/shut.h" #include "libc/sysv/consts/sig.h" +#include "libc/sysv/consts/sock.h" #include "libc/testlib/testlib.h" #include "libc/x/x.h" +#include "third_party/regex/regex.h" STATIC_YOINK("zip_uri_support"); STATIC_YOINK("o/" MODE "/tool/net/redbean.com"); -char testlib_enable_tmp_setup_teardown; +char testlib_enable_tmp_setup_teardown_once; +int port; -void SetUp(void) { +void SetUpOnce(void) { + if (IsWindows()) return; ssize_t n; char buf[1024]; int fdin, fdout; @@ -48,9 +58,41 @@ void SetUp(void) { close(fdin); } -TEST(redbean, test) { +char *SendHttpRequest(const char *s) { + int fd; + char *p; + size_t n; + ssize_t rc; + struct sockaddr_in addr = {AF_INET, htons(port), {htonl(INADDR_LOOPBACK)}}; + EXPECT_NE(-1, (fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP))); + EXPECT_NE(-1, connect(fd, &addr, sizeof(addr))); + n = strlen(s); + EXPECT_EQ(n, write(fd, s, n)); + shutdown(fd, SHUT_WR); + for (p = 0, n = 0;; n += rc) { + p = xrealloc(p, n + 512); + EXPECT_NE(-1, (rc = read(fd, p + n, 512))); + if (rc <= 0) break; + } + p = xrealloc(p, n + 1); + p[n] = 0; + close(fd); + return p; +} + +bool Matches(const char *regex, const char *str) { + bool r; + regex_t re; + CHECK_EQ(REG_OK, regcomp(&re, regex, 0)); + r = regexec(&re, str, 0, 0, 0) == REG_OK; + regfree(&re); + return r; +} + +TEST(redbean, testOptions) { + if (IsWindows()) return; char portbuf[16]; - int pid, port, pipefds[2]; + int pid, pipefds[2]; sigset_t chldmask, savemask; sigaddset(&chldmask, SIGCHLD); sigprocmask(SIG_BLOCK, &chldmask, &savemask); @@ -61,14 +103,21 @@ TEST(redbean, test) { dup2(pipefds[1], 1); sigprocmask(SIG_SETMASK, &savemask, NULL); execv("bin/redbean.com", - (char *const[]){"bin/redbean.com", "-zp0", "-l127.0.0.1", 0}); + (char *const[]){"bin/redbean.com", "-szp0", "-l127.0.0.1", 0}); _exit(127); } EXPECT_NE(-1, close(pipefds[1])); EXPECT_NE(-1, read(pipefds[0], portbuf, sizeof(portbuf))); port = atoi(portbuf); - printf("port %d\n", port); - fflush(stdout); + EXPECT_TRUE(Matches("HTTP/1\\.1 200 OK\r\n" + "Accept: \\*/\\*\r\n" + "Accept-Charset: utf-8,ISO-8859-1;q=0\\.7,\\*;q=0\\.5\r\n" + "Allow: GET, HEAD, POST, PUT, DELETE, OPTIONS\r\n" + "Date: .*\r\n" + "Server: redbean/0\\.4\r\n" + "Content-Length: 0\r\n" + "\r\n", + gc(SendHttpRequest("OPTIONS * HTTP/1.1\n\n")))); EXPECT_NE(-1, kill(pid, SIGTERM)); EXPECT_NE(-1, wait(0)); } diff --git a/test/tool/net/test.mk b/test/tool/net/test.mk index 5dae443a..d1341da9 100644 --- a/test/tool/net/test.mk +++ b/test/tool/net/test.mk @@ -29,17 +29,21 @@ TEST_TOOL_NET_CHECKS = \ TEST_TOOL_NET_DIRECTDEPS = \ LIBC_CALLS \ - LIBC_STUBS \ LIBC_FMT \ - LIBC_STDIO \ LIBC_INTRIN \ - LIBC_NEXGEN32E \ - LIBC_SYSV \ + LIBC_LOG \ LIBC_MEM \ + LIBC_NEXGEN32E \ LIBC_RUNTIME \ - LIBC_X \ + LIBC_SOCK \ + LIBC_STDIO \ + LIBC_STR \ + LIBC_STUBS \ + LIBC_SYSV \ LIBC_TESTLIB \ - LIBC_ZIPOS + LIBC_X \ + LIBC_ZIPOS \ + THIRD_PARTY_REGEX TEST_TOOL_NET_DEPS := \ $(call uniq,$(foreach x,$(TEST_TOOL_NET_DIRECTDEPS),$($(x))))