202 lines
4.8 KiB
C
202 lines
4.8 KiB
C
/*
|
|
* The white-box half must be the -T root: dependency packages lose @test
|
|
* declarations before code generation. Keep it separate from the external
|
|
* regex_test binary because each -T unit owns main and __wwtests.
|
|
*/
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <sys/wait.h>
|
|
|
|
static const char *whitebox[] = {
|
|
"thread_shape",
|
|
"newmatch_discriminates",
|
|
"is_consuming_kinds",
|
|
"delete_thread_middle",
|
|
"add_thread_dedup_inherit",
|
|
"add_thread_dup_independence",
|
|
"run_thread_literal_program",
|
|
"run_thread_anchored_route",
|
|
"search_matches",
|
|
"search_early_exit",
|
|
"search_no_match",
|
|
"find_last_groupstart_cases",
|
|
"shift_direct",
|
|
"run_thread_group_arms",
|
|
"parse_repetition_cases",
|
|
};
|
|
|
|
static int
|
|
runwait(const char *cmd)
|
|
{
|
|
int rc;
|
|
|
|
rc = system(cmd);
|
|
if (rc == -1)
|
|
return -1;
|
|
if (WIFEXITED(rc))
|
|
return WEXITSTATUS(rc);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
runcapture(const char *cmd, char *out, size_t outsz)
|
|
{
|
|
FILE *p;
|
|
size_t n;
|
|
int ch, overflow, status;
|
|
|
|
p = popen(cmd, "r");
|
|
if (p == NULL)
|
|
return -1;
|
|
n = 0;
|
|
overflow = 0;
|
|
while ((ch = fgetc(p)) != EOF) {
|
|
if (n + 1 < outsz)
|
|
out[n++] = ch;
|
|
else
|
|
overflow = 1;
|
|
}
|
|
out[n] = '\0';
|
|
status = pclose(p);
|
|
if (overflow || status == -1)
|
|
return -1;
|
|
if (WIFEXITED(status))
|
|
return WEXITSTATUS(status);
|
|
return 1;
|
|
}
|
|
|
|
static int
|
|
linecount(const char *out, const char *want)
|
|
{
|
|
const char *p, *end;
|
|
size_t n;
|
|
int count;
|
|
|
|
n = strlen(want);
|
|
count = 0;
|
|
for (p = out; *p != '\0'; p = *end == '\0' ? end : end + 1) {
|
|
end = strchr(p, '\n');
|
|
if (end == NULL)
|
|
end = p + strlen(p);
|
|
if ((size_t)(end - p) == n && memcmp(p, want, n) == 0)
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int
|
|
okcount(const char *out)
|
|
{
|
|
const char *p, *end;
|
|
static const char suffix[] = " ... ok";
|
|
size_t n, slen;
|
|
int count;
|
|
|
|
slen = sizeof suffix - 1;
|
|
count = 0;
|
|
for (p = out; *p != '\0'; p = *end == '\0' ? end : end + 1) {
|
|
end = strchr(p, '\n');
|
|
if (end == NULL)
|
|
end = p + strlen(p);
|
|
n = (size_t)(end - p);
|
|
if (n >= slen && memcmp(end - slen, suffix, slen) == 0)
|
|
count++;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int
|
|
checkresult(const char *label, const char *out, int expected,
|
|
const char *const *names, size_t nnames)
|
|
{
|
|
char summary[64], row[256];
|
|
int bad;
|
|
|
|
bad = 0;
|
|
snprintf(summary, sizeof summary, "%d passed, 0 failed", expected);
|
|
if (linecount(out, summary) != 1 || okcount(out) != expected
|
|
|| strstr(out, "FAIL") != NULL || strstr(out, "No tests run") != NULL) {
|
|
fprintf(stderr, "regex_run FAIL: %s result was incomplete\n%s",
|
|
label, out);
|
|
bad = 1;
|
|
}
|
|
for (size_t i = 0; i < nnames; i++) {
|
|
snprintf(row, sizeof row, "%s ... ok", names[i]);
|
|
if (linecount(out, row) != 1) {
|
|
fprintf(stderr, "regex_run FAIL: %s did not execute exactly once\n",
|
|
names[i]);
|
|
bad = 1;
|
|
}
|
|
}
|
|
return bad ? -1 : 0;
|
|
}
|
|
|
|
int
|
|
main(void)
|
|
{
|
|
const char *bin;
|
|
char absbin[1024], cwd[1024], tmp[] = "/tmp/wwregex.XXXXXX";
|
|
char cmd[4096], output[32768], cleanup[1200];
|
|
int rc, failed;
|
|
|
|
bin = getenv("BIN");
|
|
if (bin == NULL)
|
|
bin = "out/bin";
|
|
if (bin[0] != '/') {
|
|
if (getcwd(cwd, sizeof cwd) == NULL)
|
|
return 1;
|
|
if (snprintf(absbin, sizeof absbin, "%s/%s", cwd, bin)
|
|
>= (int)sizeof absbin)
|
|
return 1;
|
|
bin = absbin;
|
|
}
|
|
if (getcwd(cwd, sizeof cwd) == NULL)
|
|
return 1;
|
|
if (mkdtemp(tmp) == NULL)
|
|
return 1;
|
|
|
|
failed = 0;
|
|
/* A unique cwd prevents an unrelated local `regex` path from winning
|
|
* the module lookup; the private cache keeps the source tree untouched. */
|
|
if (snprintf(cmd, sizeof cmd,
|
|
"cd '%s' && WW_PKGCACHE='%s/cache-black' timeout 180 '%s/ww' "
|
|
"test -I '%s/lib' '%s/lib/regex/regex_test.ww'",
|
|
tmp, tmp, bin, cwd, cwd) >= (int)sizeof cmd) {
|
|
failed = 1;
|
|
} else {
|
|
rc = runcapture(cmd, output, sizeof output);
|
|
if (rc != 0 || checkresult("black-box", output, 33, NULL, 0) != 0) {
|
|
fprintf(stderr, "regex_run FAIL: black-box exited %d\n", rc);
|
|
failed = 1;
|
|
} else {
|
|
printf("regex_run: black-box 33 discovered, 33 passed, 0 failed\n");
|
|
}
|
|
}
|
|
|
|
/* Resolving the package directory as the root keeps -T off imported
|
|
* dependencies while making every in-package probe visible to the synth. */
|
|
if (snprintf(cmd, sizeof cmd,
|
|
"cd '%s' && WW_PKGCACHE='%s/cache-white' timeout 180 '%s/ww' "
|
|
"test -I '%s/lib' regex", tmp, tmp, bin, cwd) >= (int)sizeof cmd) {
|
|
failed = 1;
|
|
} else {
|
|
rc = runcapture(cmd, output, sizeof output);
|
|
if (rc != 0 || checkresult("white-box", output, 15, whitebox,
|
|
sizeof whitebox / sizeof whitebox[0]) != 0) {
|
|
fprintf(stderr, "regex_run FAIL: white-box exited %d\n", rc);
|
|
failed = 1;
|
|
} else {
|
|
printf("regex_run: white-box 15 discovered, 15 passed, 0 failed\n");
|
|
}
|
|
}
|
|
|
|
snprintf(cleanup, sizeof cleanup, "rm -rf -- '%s'", tmp);
|
|
if (runwait(cleanup) != 0) {
|
|
fprintf(stderr, "regex_run FAIL: cannot clean temporary directory\n");
|
|
failed = 1;
|
|
}
|
|
return failed ? 1 : 0;
|
|
}
|