kit

kit
git clone https://git.ryansepassi.com/git/kit.git
Log | Files | Refs | README

build_pure_test.c (23047B)


      1 #include "build/build.h"
      2 #include "build/bundle.h"
      3 #include "build/cfg.h"
      4 #include "build/defn.h"
      5 #include "build/protocol.h"
      6 #include "build/trace.h"
      7 #include "lib/kit_unit.h"
      8 
      9 #include <string.h>
     10 
     11 static KitUnit g_u;
     12 
     13 #define EXPECT(c, ...) CU_EXPECT(&g_u, c, __VA_ARGS__)
     14 
     15 static int emit_to_slice(int (*emit)(void*, KitWriter*), void* obj,
     16                          KitSlice* out, KitWriter** writer_out) {
     17   KitWriter* w = NULL;
     18   const uint8_t* bytes;
     19   size_t len;
     20   if (kit_writer_mem(&g_u.heap, &w) != KIT_OK || !w) return 0;
     21   if (emit(obj, w) != BUILD_OK || kit_writer_status(w) != KIT_OK) {
     22     kit_writer_close(w);
     23     return 0;
     24   }
     25   bytes = kit_writer_mem_bytes(w, &len);
     26   out->data = bytes;
     27   out->len = len;
     28   *writer_out = w;
     29   return 1;
     30 }
     31 
     32 static int emit_config_adapter(void* obj, KitWriter* w) {
     33   return build_config_emit((const BuildConfig*)obj, w);
     34 }
     35 
     36 static int emit_argv_adapter(void* obj, KitWriter* w) {
     37   return build_argv_emit((const BuildArgv*)obj, w);
     38 }
     39 
     40 static void fill_id(uint8_t id[BUILD_HASH_LEN], uint8_t seed) {
     41   size_t i;
     42   for (i = 0; i < BUILD_HASH_LEN; ++i) id[i] = (uint8_t)(seed + i);
     43 }
     44 
     45 static int bytes_contains(const void* hay, size_t hay_len, const char* needle) {
     46   const uint8_t* h = (const uint8_t*)hay;
     47   size_t n = strlen(needle);
     48   size_t i;
     49   if (!needle || n == 0u) return 1;
     50   if (!hay || hay_len < n) return 0;
     51   for (i = 0; i + n <= hay_len; ++i)
     52     if (memcmp(h + i, needle, n) == 0) return 1;
     53   return 0;
     54 }
     55 
     56 static void test_config_and_argv(void) {
     57   BuildConfigEntry cfg_storage[4], parsed_storage[4], over_storage[4];
     58   char argv_storage[3][BUILD_VAL_MAX], parsed_argv_storage[3][BUILD_VAL_MAX];
     59   BuildConfig cfg, parsed, over;
     60   BuildArgv argv, parsed_argv;
     61   KitBuildKV ov[2];
     62   KitSlice bytes;
     63   KitWriter* w = NULL;
     64   KitSlice val;
     65   int present = 0;
     66   uint8_t id1[BUILD_HASH_LEN], id2[BUILD_HASH_LEN];
     67   static const char bad[] = "kit-build-config 1\nz 1\na 2\n";
     68 
     69   build_config_init(&cfg, cfg_storage, 4);
     70   build_config_init(&parsed, parsed_storage, 4);
     71   build_config_init(&over, over_storage, 4);
     72   EXPECT(build_config_set(&cfg, KIT_SLICE_LIT("opt"), KIT_SLICE_LIT("2")) ==
     73              BUILD_OK,
     74          "set opt");
     75   EXPECT(build_config_set(&cfg, KIT_SLICE_LIT("arch"), KIT_SLICE_LIT("aa64")) ==
     76              BUILD_OK,
     77          "set arch");
     78   EXPECT(emit_to_slice(emit_config_adapter, &cfg, &bytes, &w),
     79          "emit config");
     80   EXPECT(bytes.len == strlen("kit-build-config 1\narch aa64\nopt 2\n") &&
     81              memcmp(bytes.data, "kit-build-config 1\narch aa64\nopt 2\n",
     82                     bytes.len) == 0,
     83          "canonical config bytes");
     84   EXPECT(build_config_parse(bytes.data, bytes.len, &parsed, NULL, 0) ==
     85              BUILD_OK,
     86          "parse emitted config");
     87   EXPECT(build_config_get(&parsed, KIT_SLICE_LIT("arch"), &val, &present) ==
     88              BUILD_OK &&
     89              present && kit_slice_eq(val, KIT_SLICE_LIT("aa64")),
     90          "get parsed config");
     91   EXPECT(build_config_id(&g_u.heap, &cfg, id1) == BUILD_OK &&
     92              build_config_id(&g_u.heap, &parsed, id2) == BUILD_OK &&
     93              build_id_eq(id1, id2),
     94          "config id roundtrip");
     95   kit_writer_close(w);
     96 
     97   EXPECT(build_config_parse((const uint8_t*)bad, sizeof bad - 1u, &parsed, NULL,
     98                             0) == BUILD_ERR,
     99          "reject non-canonical config");
    100 
    101   ov[0].key = KIT_SLICE_LIT("opt");
    102   ov[0].value = KIT_SLICE_LIT("3");
    103   ov[1].key = KIT_SLICE_LIT("mode");
    104   ov[1].value = KIT_SLICE_LIT("dbg");
    105   EXPECT(build_config_overlay(&cfg, ov, 2, &over) == BUILD_OK,
    106          "overlay config");
    107   EXPECT(build_config_get(&over, KIT_SLICE_LIT("opt"), &val, &present) ==
    108              BUILD_OK &&
    109              present && kit_slice_eq(val, KIT_SLICE_LIT("3")),
    110          "overlay replaces");
    111 
    112   build_argv_init(&argv, argv_storage, 3);
    113   build_argv_init(&parsed_argv, parsed_argv_storage, 3);
    114   {
    115     KitSlice args[2];
    116     args[0] = KIT_SLICE_LIT("--emit");
    117     args[1] = KIT_SLICE_LIT("obj");
    118     EXPECT(build_argv_set(&argv, args, 2) == BUILD_OK, "set argv");
    119   }
    120   EXPECT(emit_to_slice(emit_argv_adapter, &argv, &bytes, &w), "emit argv");
    121   EXPECT(bytes.len == strlen("kit-build-argv 1\n--emit\nobj\n") &&
    122              memcmp(bytes.data, "kit-build-argv 1\n--emit\nobj\n",
    123                     bytes.len) == 0,
    124          "argv preserves order");
    125   EXPECT(build_argv_parse(bytes.data, bytes.len, &parsed_argv, NULL, 0) ==
    126              BUILD_OK &&
    127              parsed_argv.n == 2 &&
    128              strcmp(parsed_argv.args[0], "--emit") == 0 &&
    129              strcmp(parsed_argv.args[1], "obj") == 0,
    130          "parse argv");
    131   kit_writer_close(w);
    132 }
    133 
    134 static void test_defn(void) {
    135   static const char text[] = "kit-build 1\n"
    136                              "[target bin]\n"
    137                              "recipe //recipes/app.sh\n"
    138                              "[target core]\n"
    139                              "recipe recipes/lib.sh\n";
    140   static const char text2[] = "kit-build 2\n"
    141                               "\n"
    142                               "[default c.object]\n"
    143                               "recipe //recipes/c-object.sh\n"
    144                               "\n"
    145                               "[rule object]\n"
    146                               "match *.o\n"
    147                               "type c.object\n"
    148                               "\n"
    149                               "[redo-defaults]\n"
    150                               "enabled true\n"
    151                               "search . do\n"
    152                               "walk-parents true\n";
    153   static const char unsorted[] = "kit-build 1\n"
    154                                  "[target z]\n"
    155                                  "recipe recipes/z.sh\n"
    156                                  "[target a]\n"
    157                                  "recipe recipes/a.sh\n";
    158   BuildTargetDefn storage[4];
    159   BuildDefaultDefn defaults[2];
    160   BuildRuleDefn rules[2];
    161   BuildDefn defn;
    162   const BuildTargetDefn* t;
    163   const BuildRuleDefn* r;
    164   const BuildDefaultDefn* d;
    165   char label[BUILD_TARGET_MAX];
    166   char package[BUILD_PATH_MAX];
    167   char local[BUILD_TARGET_MAX];
    168   int ambiguous = 0;
    169 
    170   memset(&defn, 0, sizeof defn);
    171   defn.targets = storage;
    172   defn.cap_targets = 4;
    173   defn.defaults = defaults;
    174   defn.cap_defaults = 2;
    175   defn.rules = rules;
    176   defn.cap_rules = 2;
    177   EXPECT(build_defn_parse((const uint8_t*)text, sizeof text - 1u, &defn, NULL,
    178                           0) == BUILD_OK,
    179          "parse defn");
    180   t = build_defn_find(&defn, KIT_SLICE_LIT("core"));
    181   EXPECT(t && strcmp(t->recipe_path, "recipes/lib.sh") == 0, "find target");
    182   EXPECT(build_defn_find(&defn, KIT_SLICE_LIT("none")) == NULL,
    183          "missing target");
    184   EXPECT(build_defn_parse((const uint8_t*)unsorted, sizeof unsorted - 1u, &defn,
    185                           NULL, 0) == BUILD_ERR,
    186          "reject unsorted defn");
    187   EXPECT(build_defn_parse((const uint8_t*)text2, sizeof text2 - 1u, &defn,
    188                           NULL, 0) == BUILD_OK,
    189          "parse defn v2");
    190   d = build_defn_default_find(&defn, KIT_SLICE_LIT("c.object"));
    191   EXPECT(d && strcmp(d->recipe_path, "//recipes/c-object.sh") == 0,
    192          "find default recipe");
    193   r = build_defn_rule_match(&defn, KIT_SLICE_LIT("foo.o"), &ambiguous);
    194   EXPECT(r && !ambiguous && strcmp(r->type, "c.object") == 0,
    195          "match rule");
    196   EXPECT(defn.redo.enabled && defn.redo.walk_parents &&
    197              defn.redo.n_search == 2 &&
    198              strcmp(defn.redo.search[0], ".") == 0 &&
    199              strcmp(defn.redo.search[1], "do") == 0,
    200          "parse redo defaults");
    201   EXPECT(build_target_canonicalize(KIT_SLICE_LIT("//app"), KIT_SLICE_NULL,
    202                                    KIT_SLICE_NULL, label, NULL, 0) ==
    203                  BUILD_OK &&
    204              strcmp(label, "//app:app") == 0,
    205          "canonical package shorthand");
    206   EXPECT(build_target_canonicalize(KIT_SLICE_LIT(":lib"),
    207                                    KIT_SLICE_NULL, KIT_SLICE_LIT("pkg/sub"),
    208                                    label, NULL, 0) ==
    209              BUILD_OK &&
    210              strcmp(label, "//pkg/sub:lib") == 0,
    211          "canonical relative label");
    212   EXPECT(build_target_canonicalize(KIT_SLICE_LIT("//dep:lib"),
    213                                    KIT_SLICE_LIT("ext"),
    214                                    KIT_SLICE_LIT("pkg/sub"), label, NULL, 0) ==
    215              BUILD_OK &&
    216              strcmp(label, "@ext//dep:lib") == 0,
    217          "canonical repo-rooted label inside external repo");
    218   EXPECT(build_target_split_repo(KIT_SLICE_LIT("@ext//dep:lib"), label,
    219                                  package, local) == BUILD_OK &&
    220              strcmp(label, "ext") == 0 && strcmp(package, "dep") == 0 &&
    221              strcmp(local, "lib") == 0,
    222          "split external canonical target");
    223   EXPECT(build_target_canonicalize(KIT_SLICE_LIT("@child//dep:lib"),
    224                                    KIT_SLICE_LIT("ext"),
    225                                    KIT_SLICE_LIT("pkg/sub"), label, NULL, 0) ==
    226              BUILD_OK &&
    227              strcmp(label, "@ext+child//dep:lib") == 0,
    228          "canonical nested external repo label");
    229   EXPECT(build_target_split(KIT_SLICE_LIT("//pkg/sub:lib"), package, local) ==
    230              BUILD_OK &&
    231              strcmp(package, "pkg/sub") == 0 && strcmp(local, "lib") == 0,
    232          "split canonical target");
    233 }
    234 
    235 static void test_trace(void) {
    236   BuildShallowTrace st;
    237   BuildConfigLeaf keys[2];
    238   BuildConfigLeaf ds_configs[2];
    239   BuildSourceLeaf sources[2], parsed_sources[2];
    240   BuildGlobLeaf globs[1], parsed_globs[1];
    241   BuildDepEdge deps[1], parsed_deps[1];
    242   BuildConfigLeaf parsed_keys[2];
    243   BuildDeepSet parsed_ds;
    244   BuildShallowTrace parsed;
    245   BuildDeepTrace deep, parsed_deep;
    246   BuildTargetRecord rec;
    247   BuildRecordRow rows[20], row;
    248   KitWriter* w = NULL;
    249   KitSlice bytes;
    250   uint8_t trace_id[BUILD_HASH_LEN];
    251   char err[128];
    252   static const char dup_source[] =
    253       "kit-build-shallow 1\n"
    254       "target //app:bin\n"
    255       "recipe "
    256       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\n"
    257       "output "
    258       "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\n"
    259       "argv "
    260       "030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122\n"
    261       "[config]\n"
    262       "[source]\n"
    263       "a.c -\n"
    264       "a.c -\n"
    265       "[glob]\n"
    266       "[dep]\n";
    267   static const char bad_config[] =
    268       "kit-build-shallow 1\n"
    269       "target //app:bin\n"
    270       "recipe "
    271       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\n"
    272       "output "
    273       "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\n"
    274       "argv "
    275       "030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122\n"
    276       "[config]\n"
    277       "z present "
    278       "0a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526272829 -\n"
    279       "a unset - -\n"
    280       "[source]\n"
    281       "[glob]\n"
    282       "[dep]\n";
    283   static const char bad_deepset_config[] =
    284       "kit-build-deepset 1\n"
    285       "target //app:bin\n"
    286       "recipe "
    287       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f\n"
    288       "[config]\n"
    289       "z present "
    290       "0a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20212223242526272829 -\n"
    291       "a unset - -\n"
    292       "[source]\n"
    293       "[glob]\n"
    294       "[blob]\n"
    295       "[child]\n";
    296 
    297   memset(&st, 0, sizeof st);
    298   strcpy(st.target, "//app:bin");
    299   fill_id(st.recipe, 0);
    300   fill_id(st.output, 1);
    301   fill_id(st.argv, 3);
    302   st.configs = keys;
    303   st.n_configs = 2;
    304   st.cap_configs = 2;
    305   strcpy(keys[0].key, "z");
    306   keys[0].present = 1;
    307   keys[0].has_default = 0;
    308   fill_id(keys[0].value_hash, 10);
    309   memset(keys[0].default_hash, 0, sizeof keys[0].default_hash);
    310   strcpy(keys[1].key, "a");
    311   keys[1].present = 0;
    312   keys[1].has_default = 0;
    313   memset(keys[1].value_hash, 0, sizeof keys[1].value_hash);
    314   memset(keys[1].default_hash, 0, sizeof keys[1].default_hash);
    315   st.sources = sources;
    316   st.n_sources = 2;
    317   st.cap_sources = 2;
    318   strcpy(sources[0].path, "src/z.c");
    319   fill_id(sources[0].blob, 4);
    320   sources[0].absent = 0;
    321   strcpy(sources[1].path, "src/a.c");
    322   sources[1].absent = 1;
    323   st.globs = globs;
    324   st.n_globs = 1;
    325   st.cap_globs = 1;
    326   strcpy(globs[0].pattern, "src/*.c");
    327   fill_id(globs[0].result_hash, 5);
    328   st.deps = deps;
    329   st.n_deps = 1;
    330   st.cap_deps = 1;
    331   strcpy(deps[0].name, "//lib:core");
    332   fill_id(deps[0].overlay_id, 6);
    333   fill_id(deps[0].argv_id, 8);
    334   fill_id(deps[0].output_tree, 9);
    335 
    336   EXPECT(kit_writer_mem(&g_u.heap, &w) == KIT_OK && w, "trace writer");
    337   EXPECT(build_shallow_emit(&st, w, err, sizeof err) == BUILD_OK,
    338          "emit shallow");
    339   bytes.data = kit_writer_mem_bytes(w, &bytes.len);
    340   build_trace_id(bytes.data, bytes.len, trace_id);
    341   EXPECT(!bytes_contains(bytes.data, bytes.len, "\nconfig "),
    342          "shallow omits redundant effective config id");
    343   EXPECT(bytes_contains(
    344              bytes.data, bytes.len,
    345              "\n[dep]\n//lib:core "
    346              "060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425 "
    347              "08090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f2021222324252627 "
    348              "090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728\n"),
    349          "shallow canonical dep row");
    350 
    351   memset(&parsed, 0, sizeof parsed);
    352   parsed.configs = parsed_keys;
    353   parsed.cap_configs = 2;
    354   parsed.sources = parsed_sources;
    355   parsed.cap_sources = 2;
    356   parsed.globs = parsed_globs;
    357   parsed.cap_globs = 1;
    358   parsed.deps = parsed_deps;
    359   parsed.cap_deps = 1;
    360   EXPECT(build_shallow_parse(bytes.data, bytes.len, &parsed, err, sizeof err) ==
    361              BUILD_OK,
    362          "parse shallow");
    363   EXPECT(strcmp(parsed.configs[0].key, "a") == 0 &&
    364              strcmp(parsed.sources[0].path, "src/a.c") == 0 &&
    365              parsed.sources[0].absent &&
    366              memcmp(parsed.deps[0].overlay_id, deps[0].overlay_id,
    367                     BUILD_HASH_LEN) == 0 &&
    368              memcmp(parsed.deps[0].argv_id, deps[0].argv_id,
    369                     BUILD_HASH_LEN) == 0 &&
    370              memcmp(parsed.deps[0].output_tree, deps[0].output_tree,
    371                     BUILD_HASH_LEN) == 0,
    372          "shallow canonical sections");
    373   EXPECT(build_shallow_parse((const uint8_t*)dup_source, sizeof dup_source - 1u,
    374                              &parsed, err, sizeof err) == BUILD_ERR,
    375          "reject duplicate source");
    376   EXPECT(build_shallow_parse((const uint8_t*)bad_config,
    377                              sizeof bad_config - 1u, &parsed, err,
    378                              sizeof err) == BUILD_ERR,
    379          "reject non-canonical shallow config");
    380   kit_writer_close(w);
    381 
    382   w = NULL;
    383   EXPECT(kit_writer_mem(&g_u.heap, &w) == KIT_OK && w, "test trace writer");
    384   EXPECT(build_test_shallow_emit(&st, w, err, sizeof err) == BUILD_OK,
    385          "emit test shallow");
    386   bytes.data = kit_writer_mem_bytes(w, &bytes.len);
    387   EXPECT(bytes.len > strlen(BUILD_TEST_SHALLOW_MAGIC) &&
    388              memcmp(bytes.data, BUILD_TEST_SHALLOW_MAGIC,
    389                     strlen(BUILD_TEST_SHALLOW_MAGIC)) == 0 &&
    390              bytes_contains(bytes.data, bytes.len, "\nresult ") &&
    391              !bytes_contains(bytes.data, bytes.len, "\noutput "),
    392          "test shallow uses result field");
    393   memset(&parsed, 0, sizeof parsed);
    394   parsed.configs = parsed_keys;
    395   parsed.cap_configs = 2;
    396   parsed.sources = parsed_sources;
    397   parsed.cap_sources = 2;
    398   parsed.globs = parsed_globs;
    399   parsed.cap_globs = 1;
    400   parsed.deps = parsed_deps;
    401   parsed.cap_deps = 1;
    402   EXPECT(build_test_shallow_parse(bytes.data, bytes.len, &parsed, err,
    403                                   sizeof err) == BUILD_OK &&
    404              build_id_eq(parsed.output, st.output),
    405          "parse test shallow");
    406   EXPECT(build_shallow_parse(bytes.data, bytes.len, &parsed, err, sizeof err) ==
    407              BUILD_ERR,
    408          "build shallow rejects test magic");
    409   kit_writer_close(w);
    410 
    411   memset(&deep, 0, sizeof deep);
    412   strcpy(deep.target, "//app:bin");
    413   fill_id(deep.recipe, 0);
    414   fill_id(deep.output, 1);
    415   fill_id(deep.argv, 3);
    416   fill_id(deep.deepset, 4);
    417   w = NULL;
    418   EXPECT(kit_writer_mem(&g_u.heap, &w) == KIT_OK && w, "test deep writer");
    419   EXPECT(build_test_deep_emit(&deep, w, err, sizeof err) == BUILD_OK,
    420          "emit test deep");
    421   bytes.data = kit_writer_mem_bytes(w, &bytes.len);
    422   EXPECT(bytes_contains(bytes.data, bytes.len, "\nresult ") &&
    423              !bytes_contains(bytes.data, bytes.len, "\noutput "),
    424          "test deep uses result field");
    425   memset(&parsed_deep, 0, sizeof parsed_deep);
    426   EXPECT(build_test_deep_parse(bytes.data, bytes.len, &parsed_deep, err,
    427                                sizeof err) == BUILD_OK &&
    428              build_id_eq(parsed_deep.output, deep.output),
    429          "parse test deep");
    430   EXPECT(build_deep_parse(bytes.data, bytes.len, &parsed_deep, err,
    431                           sizeof err) == BUILD_ERR,
    432          "build deep rejects test magic");
    433   kit_writer_close(w);
    434 
    435   memset(&parsed_ds, 0, sizeof parsed_ds);
    436   parsed_ds.configs = ds_configs;
    437   parsed_ds.cap_configs = 2;
    438   EXPECT(build_deepset_parse((const uint8_t*)bad_deepset_config,
    439                              sizeof bad_deepset_config - 1u, &parsed_ds, err,
    440                              sizeof err) == BUILD_ERR,
    441          "reject non-canonical deepset config");
    442 
    443   memset(&rec, 0, sizeof rec);
    444   strcpy(rec.target, "//app:bin");
    445   rec.rows = rows;
    446   rec.cap_rows = sizeof rows / sizeof rows[0];
    447   row.kind = (uint8_t)BUILD_TRACE_DEEP;
    448   memcpy(row.trace_id, trace_id, BUILD_HASH_LEN);
    449   EXPECT(build_record_prepend(&rec, &row) == BUILD_OK && rec.n_rows == 1,
    450          "record prepend");
    451   EXPECT(build_record_prepend(&rec, &row) == BUILD_OK && rec.n_rows == 1,
    452          "record dedup");
    453 }
    454 
    455 static int glob_seen(void* user, KitSlice path) {
    456   int* n = (int*)user;
    457   if (kit_slice_eq(path, KIT_SLICE_LIT("src/a.c"))) ++*n;
    458   return 0;
    459 }
    460 
    461 static void test_protocol(void) {
    462   uint8_t buf[BUILD_FRAME_MAX];
    463   size_t n = 0;
    464   KitBuildKV overrides[1], dec_overrides[1];
    465   KitSlice argv[1], dec_argv[1];
    466   BuildReq req, dec_req;
    467   BuildResp resp, dec_resp;
    468   int seen = 0;
    469 
    470   memset(&req, 0, sizeof req);
    471   req.cmd = BUILD_CMD_NEED;
    472   req.arg = KIT_SLICE_LIT("//lib:core");
    473   overrides[0].key = KIT_SLICE_LIT("opt");
    474   overrides[0].value = KIT_SLICE_LIT("2");
    475   req.overrides = overrides;
    476   req.noverrides = 1;
    477   argv[0] = KIT_SLICE_LIT("--fast");
    478   req.argv = argv;
    479   req.argc = 1;
    480   EXPECT(build_proto_encode_req(&req, buf, sizeof buf, &n) == BUILD_OK,
    481          "encode need");
    482   EXPECT(build_proto_decode_req(buf, n, &dec_req, dec_overrides, 1, dec_argv,
    483                                 1) == BUILD_OK,
    484          "decode need");
    485   EXPECT(dec_req.cmd == BUILD_CMD_NEED &&
    486              kit_slice_eq(dec_req.arg, KIT_SLICE_LIT("//lib:core")) &&
    487              dec_req.noverrides == 1 &&
    488              kit_slice_eq(dec_req.overrides[0].value, KIT_SLICE_LIT("2")) &&
    489              dec_req.argc == 1 &&
    490              kit_slice_eq(dec_req.argv[0], KIT_SLICE_LIT("--fast")),
    491          "need roundtrip");
    492 
    493   memset(&req, 0, sizeof req);
    494   req.cmd = BUILD_CMD_CONFIG_GET;
    495   req.arg = KIT_SLICE_LIT("mode");
    496   argv[0] = KIT_SLICE_LIT("fallback");
    497   req.argv = argv;
    498   req.argc = 1;
    499   EXPECT(build_proto_encode_req(&req, buf, sizeof buf, &n) == BUILD_OK &&
    500              build_proto_decode_req(buf, n, &dec_req, dec_overrides, 1,
    501                                     dec_argv, 1) == BUILD_OK &&
    502              dec_req.cmd == BUILD_CMD_CONFIG_GET &&
    503              dec_req.argc == 1 &&
    504              kit_slice_eq(dec_req.argv[0], KIT_SLICE_LIT("fallback")),
    505          "config default request roundtrip");
    506   memset(&resp, 0, sizeof resp);
    507   resp.status = BUILD_RESP_DEFAULT;
    508   resp.text = KIT_SLICE_LIT("fallback");
    509   EXPECT(build_proto_encode_resp(&req, &resp, buf, sizeof buf, &n) ==
    510              BUILD_OK &&
    511              build_proto_decode_resp(buf, n, BUILD_CMD_CONFIG_GET, &dec_resp,
    512                                      NULL, NULL) == BUILD_OK &&
    513              dec_resp.status == BUILD_RESP_DEFAULT &&
    514              kit_slice_eq(dec_resp.text, KIT_SLICE_LIT("fallback")),
    515          "config default response roundtrip");
    516 
    517   memset(&req, 0, sizeof req);
    518   req.cmd = BUILD_CMD_GLOB;
    519   memset(&resp, 0, sizeof resp);
    520   resp.status = BUILD_RESP_OK;
    521   resp.text = KIT_SLICE_LIT("src/a.c");
    522   EXPECT(build_proto_encode_resp(&req, &resp, buf, sizeof buf, &n) == BUILD_OK,
    523          "encode glob batch");
    524   EXPECT(build_proto_decode_resp(buf, n, BUILD_CMD_GLOB, &dec_resp, glob_seen,
    525                                  &seen) == BUILD_OK &&
    526              seen == 1,
    527          "decode glob batch");
    528 }
    529 
    530 static void test_bundle_manifest(void) {
    531   BuildTraceClaim claims[2], parsed[2];
    532   KitWriter* w = NULL;
    533   KitSlice bytes;
    534   size_t n = 0;
    535   char err[128];
    536   static const char bad[] =
    537       "kit-build-traces 1\n"
    538       "//z deep "
    539       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f "
    540       "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\n"
    541       "//a deep "
    542       "000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f "
    543       "0102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f20\n";
    544 
    545   memset(claims, 0, sizeof claims);
    546   strcpy(claims[0].target, "//z");
    547   claims[0].kind = (uint8_t)BUILD_TRACE_SHALLOW;
    548   fill_id(claims[0].trace_id, 1);
    549   fill_id(claims[0].output_tree, 2);
    550   strcpy(claims[1].target, "//a");
    551   claims[1].kind = (uint8_t)BUILD_TRACE_DEEP;
    552   fill_id(claims[1].trace_id, 3);
    553   fill_id(claims[1].output_tree, 4);
    554 
    555   EXPECT(kit_writer_mem(&g_u.heap, &w) == KIT_OK && w, "bundle writer");
    556   EXPECT(build_bundle_manifest_emit(claims, 2, w) == BUILD_OK,
    557          "emit bundle manifest");
    558   bytes.data = kit_writer_mem_bytes(w, &bytes.len);
    559   EXPECT(build_bundle_manifest_parse(bytes.data, bytes.len, parsed, 2, &n, err,
    560                                      sizeof err) == BUILD_OK &&
    561              n == 2 && strcmp(parsed[0].target, "//a") == 0 &&
    562              parsed[0].kind == (uint8_t)BUILD_TRACE_DEEP,
    563          "parse sorted bundle manifest");
    564   EXPECT(build_bundle_manifest_parse((const uint8_t*)bad, sizeof bad - 1u,
    565                                      parsed, 2, &n, err,
    566                                      sizeof err) == BUILD_ERR,
    567          "reject unsorted bundle manifest");
    568   kit_writer_close(w);
    569 }
    570 
    571 int main(void) {
    572   uint8_t key1[BUILD_HASH_LEN], key2[BUILD_HASH_LEN], test_key[BUILD_HASH_LEN];
    573   BuildPathBlob paths[1];
    574   uint8_t glob_id[BUILD_HASH_LEN];
    575 
    576   kit_unit_init(&g_u);
    577 
    578   EXPECT(build_target_key(KIT_SLICE_LIT("//app:bin"), key1) == BUILD_OK,
    579          "target key");
    580   EXPECT(build_target_key(KIT_SLICE_LIT("//app:bin"), key2) == BUILD_OK &&
    581              build_id_eq(key1, key2),
    582          "target key stable");
    583   EXPECT(build_test_target_key(KIT_SLICE_LIT("//app:bin"), test_key) ==
    584              BUILD_OK &&
    585              !build_id_eq(key1, test_key),
    586          "test target key differs from build target key");
    587   strcpy(paths[0].path, "src/a.c");
    588   fill_id(paths[0].blob, 9);
    589   EXPECT(build_glob_result_hash(&g_u.heap, paths, 1, glob_id) == BUILD_OK,
    590          "glob result hash");
    591 
    592   test_config_and_argv();
    593   test_defn();
    594   test_trace();
    595   test_protocol();
    596   test_bundle_manifest();
    597 
    598   kit_unit_summary(&g_u, "build_pure_test");
    599   return kit_unit_status(&g_u);
    600 }