/* Host test runner: checks firmware/engine against test/vectors (the same files check-vectors.js uses). Usage: check_vectors [vectors-dir] (default ../../test/vectors). Exit 0 = every check passes. */ #define _POSIX_C_SOURCE 200809L /* strdup, opendir */ #include "../engine/engine.h" #include "json.h" #include #include #include #include static int fail, n, max_events; static uint32_t hex(const jval *v){ return (uint32_t)strtoul(v->str, NULL, 16); } static long long num(const jval *v){ return v ? v->num : -999999; } static void check_primitives(const char *dir){ char path[1024]; jval *root, *a; int i, j; snprintf(path, sizeof path, "%s/primitives.json", dir); root = json_parse_file(path); if(!root){ printf("cannot read %s\n", path); fail++; return; } a = json_get(root, "mix"); for(i = 0; i < a->n; i++){ jval *v = a->items[i]; uint32_t got = sb_mix(hex(json_get(v, "a")), (uint32_t)(int32_t)num(json_get(v, "b"))); n++; if(got != hex(json_get(v, "out"))){ fail++; printf("mix %s %lld differs: got 0x%08X\n", json_get(v, "a")->str, num(json_get(v, "b")), got); } } a = json_get(root, "rng"); for(i = 0; i < a->n; i++){ jval *v = a->items[i], *seq = json_get(v, "next"), *bl = json_get(v, "below_1_to_16"), *ch = json_get(v, "chance_i_times_4096"); uint32_t seed = hex(json_get(v, "seed")); sb_rng r; n++; sb_rng_init(&r, seed); for(j = 0; j < seq->n; j++) if(sb_rng_next(&r) != hex(seq->items[j])){ fail++; printf("rng 0x%08X next step %d differs\n", seed, j); break; } n++; sb_rng_init(&r, seed); for(j = 0; j < bl->n; j++) if(sb_rng_below(&r, j + 1) != num(bl->items[j])){ fail++; printf("rng 0x%08X below step %d differs\n", seed, j); break; } n++; sb_rng_init(&r, seed); for(j = 0; j < ch->n; j++) if(sb_rng_chance(&r, j * 4096) != num(ch->items[j])){ fail++; printf("rng 0x%08X chance step %d differs\n", seed, j); break; } } a = json_get(root, "genomes"); for(i = 0; i < a->n; i++){ jval *v = a->items[i], *gj = json_get(v, "genome"); uint32_t seed = hex(json_get(v, "seed")); sb_genome g; int bad = 0, k; n++; sb_genome_make(seed, &g); for(k = 0; k < gj->n; k++){ jval *f = gj->items[k]; const char *key = f->key; #define INT(name) else if(!strcmp(key, #name)){ if(num(f) != g.name) bad = 1; } #define ARR(name, len) else if(!strcmp(key, #name)){ if(f->n != len) bad = 1; else for(j = 0; j < len; j++) if(num(f->items[j]) != g.name[j]) bad = 1; } if(!strcmp(key, "formName")){ if(strcmp(f->str, sb_form_name(g.form_idx))) bad = 1; } INT(steps) INT(scale) INT(root) INT(fillP) INT(monoLen) INT(monoRange) INT(rot) INT(hocket) INT(bassDeg) INT(swing) INT(arc) INT(motifLen) INT(accentCycle) INT(layback) INT(frame) INT(freeK) INT(freeRot) ARR(form, 4) ARR(prog, 4) ARR(dens, 2) else { printf("genome field %s is not checked by the C runner\n", key); bad = 1; } #undef INT #undef ARR if(bad){ printf("genome 0x%08X differs at %s\n", seed, key); break; } } if(bad) fail++; } json_free(root); } static int cmp_str(const void *a, const void *b){ return strcmp(*(char *const *)a, *(char *const *)b); } static void print_event(const char *tag, const sb_event *e){ printf(" %s [%d,%d,%d,%d,%d,%d]\n", tag, e->t, e->lane, e->v, e->vel, e->len, e->tr); } static void check_case(const jval *c){ jval *bars = json_get(c, "bars"), *salts = json_get(c, "salts"), *fb = json_get(c, "fillBars"); sb_state st; sb_params p; sb_bar out; uint32_t h = SB_FNV_INIT; int b, i, firstBad = -1; n++; st.seed = hex(json_get(c, "seed")); st.salts[0] = (uint32_t)num(salts->items[0]); st.salts[1] = (uint32_t)num(salts->items[1]); p.gen = (int)num(json_get(c, "gen")); p.energy = (int)num(json_get(c, "energy")); p.drift = (int)num(json_get(c, "drift")); p.scale = (int)num(json_get(c, "scale")); p.root = (int)num(json_get(c, "root")); for(b = 0; b < bars->n; b++){ jval *jb = bars->items[b], *ev = json_get(jb, "events"); p.fill = 0; for(i = 0; i < fb->n; i++) if(num(fb->items[i]) == b) p.fill = 1; sb_render_bar(&st, &p, b, &out); if(out.n > max_events) max_events = out.n; for(i = 0; i < out.n; i++) h = sb_checksum_event(h, &out.ev[i]); if(firstBad >= 0) continue; if(out.overflow){ firstBad = b; printf("FAIL %s bar %d: event buffer overflow\n", json_get(c, "id")->str, b); continue; } if(out.sec != num(json_get(jb, "sec")) || out.fill != num(json_get(jb, "fill")) || out.barTicks != num(json_get(jb, "barTicks"))){ firstBad = b; printf("FAIL %s bar %d: sec/fill/barTicks got %d/%d/%d want %lld/%lld/%lld\n", json_get(c, "id")->str, b, out.sec, out.fill, out.barTicks, num(json_get(jb, "sec")), num(json_get(jb, "fill")), num(json_get(jb, "barTicks"))); continue; } for(i = 0; i < out.n || i < ev->n; i++){ sb_event want = {0, 0, 0, 0, 0, 0}; int have = i < ev->n, same; if(have){ jval *row = ev->items[i]; want.t = (int16_t)num(row->items[0]); want.lane = (uint8_t)num(row->items[1]); want.v = (uint8_t)num(row->items[2]); want.vel = (uint8_t)num(row->items[3]); want.len = (int16_t)num(row->items[4]); want.tr = (uint8_t)num(row->items[5]); } same = have && i < out.n && !memcmp(&want, &out.ev[i], sizeof want); if(!same){ firstBad = b; printf("FAIL %s first difference in bar %d, event %d (got %d events, want %d)\n", json_get(c, "id")->str, b, i, out.n, ev->n); if(have) print_event("want", &want); if(i < out.n) print_event("got ", &out.ev[i]); break; } } } if(h != hex(json_get(c, "checksum")) || firstBad >= 0){ fail++; if(firstBad < 0) printf("FAIL %s checksum 0x%08X want %s (events matched?)\n", json_get(c, "id")->str, h, json_get(c, "checksum")->str); } } int main(int argc, char **argv){ const char *dir = argc > 1 ? argv[1] : "../../test/vectors"; char *names[64]; int nn = 0, i, k; DIR *d; struct dirent *de; check_primitives(dir); d = opendir(dir); if(!d){ printf("cannot open %s\n", dir); return 1; } while((de = readdir(d)) && nn < 64) if(!strncmp(de->d_name, "gen", 3) && de->d_name[3] >= '0' && de->d_name[3] <= '9') names[nn++] = strdup(de->d_name); closedir(d); qsort(names, (size_t)nn, sizeof names[0], cmp_str); if(!nn){ printf("no gen*.json in %s\n", dir); fail++; } for(i = 0; i < nn; i++){ char path[1024]; jval *root, *cases; snprintf(path, sizeof path, "%s/%s", dir, names[i]); root = json_parse_file(path); if(!root){ printf("cannot read %s\n", path); fail++; continue; } cases = json_get(root, "cases"); for(k = 0; k < cases->n; k++) check_case(cases->items[k]); json_free(root); free(names[i]); } printf("max events in a bar: %d (buffer %d)\n", max_events, SB_MAX_EVENTS); if(fail) printf("%d of %d checks FAILED\n", fail, n); else printf("all %d checks pass\n", n); return fail ? 1 : 0; }