Create unit test framework for subprocesses

This commit is contained in:
Justine Tunney 2022-09-03 18:01:38 -07:00
parent 263711965f
commit c5659b93f8
10 changed files with 279 additions and 77 deletions

66
libc/testlib/subprocess.h Normal file
View file

@ -0,0 +1,66 @@
#ifndef COSMOPOLITAN_LIBC_TESTLIB_SUBPROCESS_H_
#define COSMOPOLITAN_LIBC_TESTLIB_SUBPROCESS_H_
#include "libc/calls/calls.h"
#include "libc/macros.internal.h"
#include "libc/runtime/runtime.h"
#include "libc/testlib/testlib.h"
#if !(__ASSEMBLER__ + __LINKER__ + 0)
COSMOPOLITAN_C_START_
/**
* @fileoverview Test Subprocess Helper Macros
*
* These macros may be used to run unit tests in a subprocess. It's
* particularly useful when testing code that causes the process to die:
*
* TEST(term, test) {
* SPAWN(fork);
* raise(SIGUSR1);
* TERMS(SIGUSR1);
* }
*
* The default action for both EXITS() and TERMS() is _Exit(0), unless
* an EXPECT_FOO() macro failed, in which case the status is non-zero.
*
* TEST(my, test) {
* SPAWN(fork);
* // do stuff
* EXITS(0);
* }
*
* You can also nest subprocesses:
*
* TEST(exit, test) {
* SPAWN(fork);
* SPAWN(vfork);
* _Exit(2);
* EXITS(2);
* _Exit(1);
* EXITS(1);
* }
*/
#define SPAWN(METHOD) \
{ \
int _pid, _failed = g_testlib_failed; \
ASSERT_NE(-1, (_pid = METHOD())); \
if (!_pid) {
#define EXITS(rc) \
_Exit(MAX(0, MIN(255, g_testlib_failed - _failed))); \
} \
testlib_waitforexit(__FILE__, __LINE__, #rc, rc, _pid); \
}
#define TERMS(sig) \
_Exit(MAX(0, MIN(255, g_testlib_failed - _failed))); \
} \
testlib_waitforterm(__FILE__, __LINE__, #sig, sig, _pid); \
}
void testlib_waitforexit(const char *, int, const char *, int, int);
void testlib_waitforterm(const char *, int, const char *, int, int);
COSMOPOLITAN_C_END_
#endif /* !(__ASSEMBLER__ + __LINKER__ + 0) */
#endif /* COSMOPOLITAN_LIBC_TESTLIB_SUBPROCESS_H_ */

View file

@ -21,24 +21,24 @@ LIBC_TESTLIB_A_ASSETS = \
LIBC_TESTLIB_A_HDRS = \
libc/testlib/bench.h \
libc/testlib/blocktronics.h \
libc/testlib/viewables.h \
libc/testlib/ezbench.h \
libc/testlib/fastrandomstring.h \
libc/testlib/hyperion.h \
libc/testlib/moby.h \
libc/testlib/testlib.h
libc/testlib/subprocess.h \
libc/testlib/testlib.h \
libc/testlib/viewables.h
LIBC_TESTLIB_A_SRCS_S = \
libc/testlib/bench.S \
libc/testlib/blocktronics.S \
libc/testlib/viewables.S \
libc/testlib/combo.S \
libc/testlib/fixture.S \
libc/testlib/hyperion.S \
libc/testlib/moby.S \
libc/testlib/polluteregisters.S \
libc/testlib/testcase.S \
libc/testlib/thrashcodecache.S \
libc/testlib/polluteregisters.S \
libc/testlib/thunks/assert_eq.S \
libc/testlib/thunks/assert_false.S \
libc/testlib/thunks/assert_ne.S \
@ -48,26 +48,20 @@ LIBC_TESTLIB_A_SRCS_S = \
libc/testlib/thunks/expect_ne.S \
libc/testlib/thunks/expect_true.S \
libc/testlib/thunks/free.S \
libc/testlib/thunks/jump.S
libc/testlib/thunks/jump.S \
libc/testlib/viewables.S
LIBC_TESTLIB_A_SRCS_C = \
libc/testlib/almostequallongdouble.c \
libc/testlib/benchrunner.c \
libc/testlib/getcore.c \
libc/testlib/geterrno.c \
libc/testlib/seterrno.c \
libc/testlib/strerror.c \
libc/testlib/getinterrupts.c \
libc/testlib/ezbenchwarn.c \
libc/testlib/binequals.c \
libc/testlib/quota.c \
libc/testlib/clearxmmregisters.c \
libc/testlib/comborunner.c \
libc/testlib/contains.c \
libc/testlib/endswith.c \
libc/testlib/yield.c \
libc/testlib/ezbenchcontrol.c \
libc/testlib/ezbenchreport.c \
libc/testlib/ezbenchwarn.c \
libc/testlib/fixturerunner.c \
libc/testlib/formatbinaryasglyphs.c \
libc/testlib/formatbinaryashex.c \
@ -76,15 +70,24 @@ LIBC_TESTLIB_A_SRCS_C = \
libc/testlib/formatint.c \
libc/testlib/formatrange.c \
libc/testlib/formatstr.c \
libc/testlib/getcore.c \
libc/testlib/geterrno.c \
libc/testlib/getinterrupts.c \
libc/testlib/globals.c \
libc/testlib/hexequals.c \
libc/testlib/incrementfailed.c \
libc/testlib/quota.c \
libc/testlib/seterrno.c \
libc/testlib/shoulddebugbreak.c \
libc/testlib/showerror.c \
libc/testlib/startswith.c \
libc/testlib/strcaseequals.c \
libc/testlib/strequals.c \
libc/testlib/testrunner.c
libc/testlib/strerror.c \
libc/testlib/testrunner.c \
libc/testlib/waitforexit.c \
libc/testlib/waitforterm.c \
libc/testlib/yield.c
LIBC_TESTLIB_A_SRCS = \
$(LIBC_TESTLIB_A_SRCS_S) \

View file

@ -83,7 +83,7 @@ void testlib_error_leave(void) {
wontreturn void testlib_abort(void) {
testlib_finish();
__restorewintty();
_Exit(MIN(255, g_testlib_failed));
_Exit(MAX(1, MIN(255, g_testlib_failed)));
}
static void SetupTmpDir(void) {

View file

@ -0,0 +1,65 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
STATIC_YOINK("strsignal");
void testlib_waitforexit(const char *file, int line, const char *code, int rc,
int pid) {
int ws;
char host[64];
ASSERT_NE(-1, wait(&ws));
if (WIFEXITED(ws)) {
if (WEXITSTATUS(ws) == rc) {
return;
}
kprintf("%s:%d: test failed\n"
"\tEXITS(%s)\n"
"\t want WEXITSTATUS(%d)\n"
"\t got WEXITSTATUS(%d)\n",
file, line, code, rc, WEXITSTATUS(ws));
} else if (WIFSIGNALED(ws)) {
kprintf("%s:%d: test failed\n"
"\tEXITS(%s)\n"
"\t want _Exit(%d)\n"
"\t got WTERMSIG(%G)\n",
file, line, code, rc, WTERMSIG(ws));
} else if (WIFSTOPPED(ws)) {
kprintf("%s:%d: test failed\n"
"\tEXITS(%s)\n"
"\t want _Exit(%d)\n"
"\t got WSTOPSIG(%G)\n",
file, line, code, rc, WSTOPSIG(ws));
} else {
kprintf("%s:%d: test failed\n"
"\tEXITS(%s)\n"
"\t want _Exit(%d)\n"
"\t got ws=%#x\n",
file, line, code, rc, ws);
}
if (gethostname(host, sizeof(host))) {
strcpy(host, "unknown");
}
kprintf("\t%s\n", host);
exit(1);
}

View file

@ -0,0 +1,65 @@
/*-*- mode:c;indent-tabs-mode:nil;c-basic-offset:2;tab-width:8;coding:utf-8 -*-│
vi: set net ft=c ts=2 sts=2 sw=2 fenc=utf-8 :vi
Copyright 2022 Justine Alexandra Roberts Tunney
Permission to use, copy, modify, and/or distribute this software for
any purpose with or without fee is hereby granted, provided that the
above copyright notice and this permission notice appear in all copies.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL
WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE
AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include "libc/calls/calls.h"
#include "libc/intrin/kprintf.h"
#include "libc/runtime/runtime.h"
#include "libc/str/str.h"
#include "libc/testlib/testlib.h"
STATIC_YOINK("strsignal");
void testlib_waitforterm(const char *file, int line, const char *code, int sig,
int pid) {
int ws;
char host[64];
ASSERT_NE(-1, waitpid(pid, &ws, 0));
if (WIFSIGNALED(ws)) {
if (WTERMSIG(ws) == sig) {
return;
}
kprintf("%s:%d: test failed\n"
"\tTERMS(%s)\n"
"\t want WTERMSIG(%G)\n"
"\t got WTERMSIG(%G)\n",
file, line, code, sig, WTERMSIG(ws));
} else if (WIFEXITED(ws)) {
kprintf("%s:%d: test failed\n"
"\tTERMS(%s)\n"
"\t want WTERMSIG(%G)\n"
"\t got _Exit(%d)\n",
file, line, code, sig, WEXITSTATUS(ws));
} else if (WIFSTOPPED(ws)) {
kprintf("%s:%d: test failed\n"
"\tTERMS(%s)\n"
"\t want WTERMSIG(%G)\n"
"\t got WSTOPSIG(%G)\n",
file, line, code, sig, WSTOPSIG(ws));
} else {
kprintf("%s:%d: test failed\n"
"\tTERMS(%s)\n"
"\t want WTERMSIG(%G)\n"
"\t got ws=%#x\n",
file, line, code, sig, ws);
}
if (gethostname(host, sizeof(host))) {
strcpy(host, "unknown");
}
kprintf("\t%s\n", host);
exit(1);
}