#include "t.h" #include "cf.h" #include "da.h" #include "dc.h" #include "js.h" #include "p.h" #include #include #include #ifndef TOBI_SRC_DIR #define TOBI_SRC_DIR "." #endif static int read_blob(const char *path, uint8_t **data, size_t *len) { FILE *fp = fopen(path, "rb"); if (!fp) { fprintf(stderr, "open %s: %s\n", path, strerror(errno)); return 0; } if (fseek(fp, 0, SEEK_END) != 0) { fclose(fp); return 0; } long n = ftell(fp); if (n < 0) { fclose(fp); return 0; } rewind(fp); *len = (size_t)n; *data = malloc(*len ? *len : 1u); if (!*data) { fclose(fp); return 0; } if (*len && fread(*data, 1, *len, fp) != *len) { free(*data); fclose(fp); return 0; } fclose(fp); return 1; } static int stable_emit_blob(const char *label, const uint8_t *data, size_t len, int strict_should_pass) { int ok = 0; tobi_parse_result a; tobi_parse_result b; tobi_parse_result strict; (void)tobi_parse(data, len, 0, &a); (void)tobi_parse(data, len, 0, &b); int strict_ok = tobi_parse(data, len, 1, &strict); int expected_strict_ok = strict_should_pass && a.diag.len == 0; (void)tobi_cf_recover(a.root, &a.diag); (void)tobi_cf_recover(b.root, &b.diag); char *aj = tobi_js_emit(&a); char *bj = tobi_js_emit(&b); char *ad = tobi_dc_emit(a.root, &a.diag); char *bd = tobi_dc_emit(b.root, &b.diag); char *aa = tobi_da_emit(data, len, &a.meta); char *ba = tobi_da_emit(data, len, &b.meta); ok = aj != NULL && bj != NULL && ad != NULL && bd != NULL && aa != NULL && ba != NULL && strcmp(aj, bj) == 0 && aj[0] == '{' && strstr(aj, "\"meta\"") != NULL && strstr(aj, "\"ir\"") != NULL && strstr(aj, ",}") == NULL && strstr(aj, ",]") == NULL && ad[0] != '\0' && aa[0] != '\0' && strcmp(ad, bd) == 0 && strcmp(aa, ba) == 0; if (expected_strict_ok) { ok = ok && strict_ok && !tobi_diag_has_error(&strict.diag); } else { ok = ok && (!strict_ok || tobi_diag_has_error(&strict.diag)); } if (!ok) { fprintf(stderr, "corpus regression failed for %s\n", label); } free(aa); free(ba); free(ad); free(bd); free(aj); free(bj); tobi_parse_result_free(&a); tobi_parse_result_free(&b); tobi_parse_result_free(&strict); return ok ? 0 : 1; } static int stable_emit(const char *path, int strict_should_pass) { uint8_t *data = NULL; size_t len = 0; int rc = 1; if (!read_blob(path, &data, &len)) { return 1; } rc = stable_emit_blob(path, data, len, strict_should_pass); if (rc == 0 && len > 1) { size_t cuts[] = {1u, len / 2u, len - 1u}; for (size_t i = 0; i < sizeof(cuts) / sizeof(cuts[0]); i++) { if (cuts[i] == 0 || cuts[i] >= len) { continue; } char label[900]; snprintf(label, sizeof(label), "%s truncated:%zu", path, cuts[i]); if (stable_emit_blob(label, data, cuts[i], 0) != 0) { rc = 1; break; } } } free(data); return rc; } static int walk_dir(const char *rel, size_t *count, size_t *bad_count) { char dir_path[512]; snprintf(dir_path, sizeof(dir_path), "%s/%s", TOBI_SRC_DIR, rel); DIR *dir = opendir(dir_path); T_CHECK(dir != NULL); struct dirent *de = NULL; while ((de = readdir(dir)) != NULL) { if (de->d_name[0] == '.') { continue; } char path[768]; snprintf(path, sizeof(path), "%s/%s", dir_path, de->d_name); struct stat st; T_CHECK(stat(path, &st) == 0); if (S_ISREG(st.st_mode)) { int bad = strstr(de->d_name, "bad") != NULL; T_CHECK(stable_emit(path, !bad) == 0); (*count)++; if (bad) { (*bad_count)++; } } } T_CHECK(closedir(dir) == 0); return 0; } int t_corpus(void) { size_t count = 0; size_t bad_count = 0; T_CHECK(walk_dir("sam", &count, &bad_count) == 0); T_CHECK(walk_dir("fuzz/seed", &count, &bad_count) == 0); T_CHECK(count >= 9); T_CHECK(bad_count >= 2); return 0; }