mirror of
https://github.com/vercel-labs/scriptc.git
synced 2026-10-28 06:53:42 +08:00
209 lines
7.2 KiB
C
209 lines
7.2 KiB
C
/* Oracle test for node:url's legacy API and the domain pair.
|
|
* Reads the case file given as argv[1] (line format in
|
|
* gen-url-legacy-cases.mjs) and compares url.domainToASCII/domainToUnicode,
|
|
* url.parse (as its JSON), url.format over strings and objects, and
|
|
* url.resolve with Node's answers; a throwing call answers "E:" and the
|
|
* error's code (or name).
|
|
*
|
|
* Exit 0 = all pass; prints each mismatch (capped) and exits 1 otherwise.
|
|
*/
|
|
#include "../src/scr_runtime.h"
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
long scr_str_live_count(void); /* provided by scr_string.c */
|
|
|
|
/* Warnings are not part of the answers (DEP0169 is off: warn = false;
|
|
* DEP0170 still fires); the deprecation unit stays out. */
|
|
ScrDyn *scr_warning_new(ScrStr *message, const char *type, const ScrStr *code) {
|
|
(void)message;
|
|
(void)type;
|
|
(void)code;
|
|
return scr_dyn_new_null();
|
|
}
|
|
void scr_warning_emit_deferred(ScrDyn *warning) { (void)warning; }
|
|
|
|
static int hex_val(char c) {
|
|
if (c >= '0' && c <= '9') return c - '0';
|
|
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
|
return -1;
|
|
}
|
|
|
|
/* An escaped case string (printable ASCII, \uXXXX code units) to a string
|
|
* in the runtime's WTF-8 storage. */
|
|
static ScrStr *decode(const char *text) {
|
|
size_t len = strlen(text);
|
|
uint16_t *units = malloc((len ? len : 1) * sizeof *units);
|
|
size_t n = 0;
|
|
for (size_t i = 0; i < len; i++) {
|
|
if (text[i] == '\\' && text[i + 1] == 'u') {
|
|
const char *h = text + i + 2;
|
|
units[n++] = (uint16_t)(hex_val(h[0]) << 12 | hex_val(h[1]) << 8 | hex_val(h[2]) << 4 | hex_val(h[3]));
|
|
i += 5;
|
|
} else {
|
|
units[n++] = (uint8_t)text[i];
|
|
}
|
|
}
|
|
char *out = malloc(3 * n + 1);
|
|
size_t w = 0;
|
|
bool lone = false;
|
|
for (size_t i = 0; i < n; i++) {
|
|
uint32_t cp = units[i];
|
|
if (cp >= 0xD800 && cp < 0xDC00 && i + 1 < n && units[i + 1] >= 0xDC00 && units[i + 1] < 0xE000) {
|
|
cp = 0x10000 + ((cp - 0xD800) << 10) + (units[i + 1] - 0xDC00);
|
|
i++;
|
|
} else if (cp >= 0xD800 && cp < 0xE000) {
|
|
lone = true;
|
|
}
|
|
if (cp < 0x80) {
|
|
out[w++] = (char)cp;
|
|
} else if (cp < 0x800) {
|
|
out[w++] = (char)(0xC0 | (cp >> 6));
|
|
out[w++] = (char)(0x80 | (cp & 0x3F));
|
|
} else if (cp < 0x10000) {
|
|
out[w++] = (char)(0xE0 | (cp >> 12));
|
|
out[w++] = (char)(0x80 | ((cp >> 6) & 0x3F));
|
|
out[w++] = (char)(0x80 | (cp & 0x3F));
|
|
} else {
|
|
out[w++] = (char)(0xF0 | (cp >> 18));
|
|
out[w++] = (char)(0x80 | ((cp >> 12) & 0x3F));
|
|
out[w++] = (char)(0x80 | ((cp >> 6) & 0x3F));
|
|
out[w++] = (char)(0x80 | (cp & 0x3F));
|
|
}
|
|
}
|
|
if (lone) scr_wtf8_note();
|
|
ScrStr *s = scr_str_new(out, w);
|
|
free(out);
|
|
free(units);
|
|
return s;
|
|
}
|
|
|
|
/* Splits a line at tabs in place; empty fields are kept. */
|
|
static int split(char *line, char **fields, int max) {
|
|
int count = 0;
|
|
fields[count++] = line;
|
|
for (char *c = line; *c; c++)
|
|
if (*c == '\t') {
|
|
*c = 0;
|
|
if (count == max) return -1;
|
|
fields[count++] = c + 1;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
/* "E:<code or name>" for the pending exception (+1). */
|
|
static ScrStr *error_outcome(void) {
|
|
if (!scr_exc_pending()) return scr_str_new("E:<none pending>", 16);
|
|
ScrCaught *c = scr_exc_take();
|
|
ScrError *error = c->payload;
|
|
ScrStr *label = error->code ? scr_str_retain(error->code) : scr_error_default_name(error);
|
|
ScrStr *prefix = scr_str_new("E:", 2);
|
|
ScrStr *out = scr_str_concat(prefix, label);
|
|
scr_str_release(prefix);
|
|
scr_str_release(label);
|
|
scr_caught_release(c);
|
|
return out;
|
|
}
|
|
|
|
static ScrStr *string_outcome(ScrStr *result) { return result ? result : error_outcome(); }
|
|
|
|
static ScrStr *json_outcome(ScrDyn *result) {
|
|
if (!result) return error_outcome();
|
|
ScrJsonBuf b;
|
|
scr_jb_init(&b);
|
|
scr_jb_put_dyn(&b, result);
|
|
scr_dyn_release(result);
|
|
return scr_jb_finish(&b);
|
|
}
|
|
|
|
static long total, failed;
|
|
|
|
static void check(ScrStr *got, const char *expected_text, const char *kind, const char *a, const char *b) {
|
|
ScrStr *expected = decode(expected_text);
|
|
total++;
|
|
if (got->len != expected->len || memcmp(got->data, expected->data, got->len) != 0) {
|
|
failed++;
|
|
if (failed <= 40)
|
|
fprintf(stderr, "MISMATCH %s \"%s\"%s%s%s\n got %.*s\n want %.*s\n", kind, a, b ? " \"" : "",
|
|
b ? b : "", b ? "\"" : "", (int)got->len, got->data, (int)expected->len, expected->data);
|
|
}
|
|
scr_str_release(expected);
|
|
scr_str_release(got);
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
if (argc < 2) return 2;
|
|
FILE *f = fopen(argv[1], "r");
|
|
if (!f) return 2;
|
|
static char line[1 << 20];
|
|
ScrDyn *no = scr_dyn_new_bool(false);
|
|
ScrDyn *yes = scr_dyn_new_bool(true);
|
|
while (fgets(line, sizeof line, f)) {
|
|
size_t len = strlen(line);
|
|
if (len && line[len - 1] == '\n') line[--len] = 0;
|
|
if (!len) continue;
|
|
char *fields[5];
|
|
int count = split(line, fields, 5);
|
|
const char *kind = fields[0];
|
|
if ((strcmp(kind, "a") == 0 || strcmp(kind, "u") == 0) && count == 3) {
|
|
ScrStr *input = decode(fields[1]);
|
|
ScrDyn *boxed = scr_dyn_new_str(input);
|
|
ScrStr *got = kind[0] == 'a' ? scr_url_domain_to_ascii(boxed, false)
|
|
: scr_url_domain_to_unicode(boxed, false);
|
|
check(string_outcome(got), fields[2], kind, fields[1], NULL);
|
|
scr_dyn_release(boxed);
|
|
scr_str_release(input);
|
|
} else if (strcmp(kind, "p") == 0 && count == 4) {
|
|
int flags = atoi(fields[1]);
|
|
ScrStr *input = decode(fields[2]);
|
|
ScrDyn *boxed = scr_dyn_new_str(input);
|
|
ScrDyn *got = scr_url_legacy_parse(boxed, flags & 1 ? yes : no, flags & 2 ? yes : no, false);
|
|
check(json_outcome(got), fields[3], flags & 1 ? (flags & 2 ? "p3" : "p1") : flags & 2 ? "p2" : "p0",
|
|
fields[2], NULL);
|
|
scr_dyn_release(boxed);
|
|
scr_str_release(input);
|
|
} else if (strcmp(kind, "f") == 0 && count == 3) {
|
|
ScrStr *input = decode(fields[1]);
|
|
ScrDyn *boxed = scr_dyn_new_str(input);
|
|
check(string_outcome(scr_url_legacy_format(boxed, scr_dyn_undefined(), false)), fields[2], kind,
|
|
fields[1], NULL);
|
|
scr_dyn_release(boxed);
|
|
scr_str_release(input);
|
|
} else if (strcmp(kind, "o") == 0 && count == 3) {
|
|
ScrStr *text = decode(fields[1]);
|
|
ScrDyn *object = scr_json_parse(text);
|
|
check(string_outcome(scr_url_legacy_format(object, scr_dyn_undefined(), false)), fields[2], kind,
|
|
fields[1], NULL);
|
|
scr_dyn_release(object);
|
|
scr_str_release(text);
|
|
} else if (strcmp(kind, "r") == 0 && count == 4) {
|
|
ScrStr *from = decode(fields[1]);
|
|
ScrStr *to = decode(fields[2]);
|
|
ScrDyn *from_boxed = scr_dyn_new_str(from);
|
|
ScrDyn *to_boxed = scr_dyn_new_str(to);
|
|
check(string_outcome(scr_url_legacy_resolve(from_boxed, to_boxed, false)), fields[3], kind, fields[1],
|
|
fields[2]);
|
|
scr_dyn_release(from_boxed);
|
|
scr_dyn_release(to_boxed);
|
|
scr_str_release(from);
|
|
scr_str_release(to);
|
|
} else {
|
|
fprintf(stderr, "bad line: %s\n", kind);
|
|
return 2;
|
|
}
|
|
}
|
|
fclose(f);
|
|
scr_dyn_release(no);
|
|
scr_dyn_release(yes);
|
|
#ifdef SCR_RC_AUDIT
|
|
if (scr_str_live_count() != 0) {
|
|
fprintf(stderr, "live strings at exit: %ld\n", scr_str_live_count());
|
|
failed++;
|
|
}
|
|
#endif
|
|
fprintf(stderr, "%ld/%ld cases passed\n", total - failed, total);
|
|
return failed == 0 ? 0 : 1;
|
|
}
|