diff --git a/.trinity/seals/claw_ClawSuggestFeed.json b/.trinity/seals/claw_ClawSuggestFeed.json index 4c5049b486..621e722c2e 100644 --- a/.trinity/seals/claw_ClawSuggestFeed.json +++ b/.trinity/seals/claw_ClawSuggestFeed.json @@ -15,23 +15,23 @@ } ], "config": "gen=c,rust,verilog,zig;tests=zig", - "gen_hash_c": "sha256:e7d532aa35fd41b2c8d4790e155f066518dfbbbe2bc5cd50629d6fd26cab07ad", - "gen_hash_rust": "sha256:7560c931661dab40040b65ed0f62a5cf2bd4dde32b1f6e67effc49ade0669818", - "gen_hash_verilog": "sha256:b0f0b1ba70d52f92446d66525fee99c6a4dd78112344893fc295642354b4235d", - "gen_hash_zig": "sha256:44281c0b2d257b58fcb46becb5adcff4c1cc77c929f3c313a8f49f374d2fc212", + "gen_hash_c": "sha256:4128367d94e05ba2a33fa8838026b272aae1e6c80640b2ee891c86c565aa17ae", + "gen_hash_rust": "sha256:7c6263de0cb346075d16927cb1c06159126f53907e32664fee9fc1e91abbe016", + "gen_hash_verilog": "sha256:977d7213008c62056ba434d407bd49f27c8205d98b91451272f704b4e8fd750f", + "gen_hash_zig": "sha256:a13007ab40e8c73cdb8f04147402fbb9cbf1b5bed186cb9e2c69bcbce518ef25", "module": "ClawSuggestFeed", "ring": 12, "seal_schema": 2, - "sealed_at": "2026-10-19T00:14:01Z", + "sealed_at": "2026-10-19T00:36:45Z", "sealed_by": "t27c-bootstrap@0.5.2", - "spec_hash": "sha256:27121a2d0dad7d0b597fda267aace1ae09ae3d896e4cb6bf59ba5ad1af1733b3", + "spec_hash": "sha256:0b00a26584a03c8d503d5f55e4ac66faeaa9576d6a3d483a36769d08eaf3ae8e", "spec_path": "specs/claw/suggest_feed.t27", "tests": { "failed": 0, "failing": [], "forced": false, - "passed": 19, - "total": 19, + "passed": 36, + "total": 36, "vacuous": 0 }, "toolchain": { diff --git a/gen/c/claw/suggest_feed.c b/gen/c/claw/suggest_feed.c index adb70309b6..bb3e7fedc0 100644 --- a/gen/c/claw/suggest_feed.c +++ b/gen/c/claw/suggest_feed.c @@ -87,6 +87,9 @@ size_t sf_esc(uint8_t* dst, size_t cap, size_t w, uint8_t b); bool sf_same_line(uint8_t* a, size_t os, size_t oe, uint8_t* b, size_t ns, size_t ne); uint32_t sf_lines(uint8_t* t, size_t n); size_t suggest_review(uint8_t* inp, size_t n, uint8_t* out, size_t out_cap, uint32_t* stats); +size_t t_fix_stream(uint8_t* inp, size_t cap); +size_t t_file_head(uint8_t* dst, size_t cap, size_t w, uint32_t len); +size_t t_rep(uint8_t* dst, size_t cap, size_t w, const char* s, size_t n, size_t times); uint8_t byte_at(uint8_t* t, size_t n, size_t i); uint32_t comment_line(uint32_t start, uint32_t count); uint32_t comment_start_line(uint32_t start, uint32_t count); @@ -537,6 +540,28 @@ size_t suggest_review(uint8_t* inp, size_t n, uint8_t* out, size_t out_cap, uint return w; } +size_t t_fix_stream(uint8_t* inp, size_t cap) { + size_t w = put_str(inp, cap, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = put_str(inp, cap, w, "F\tspecs/x.t27\t16\n", 17); + return put_str(inp, cap, w, "a = 1; \nb = 2;\n", 16); +} + +size_t t_file_head(uint8_t* dst, size_t cap, size_t w, uint32_t len) { + size_t x = put_str(dst, cap, w, "F\tspecs/x.t27\t", 14); + x = put_count(dst, cap, x, len); + return put_byte(dst, cap, x, '\n'); +} + +size_t t_rep(uint8_t* dst, size_t cap, size_t w, const char* s, size_t n, size_t times) { + size_t x = w; + size_t k = 0; + while ((k < times)) { + x = put_str(dst, cap, x, s, n); + k += 1; + } + return x; +} + uint8_t byte_at(uint8_t* t, size_t n, size_t i) { if ((i >= n)) { return 0; @@ -1245,6 +1270,285 @@ void test_an_output_buffer_that_is_too_small_is_reported_by_its_size(void) { assert((need > 32)); } +void test_a_length_field_is_one_to_nine_decimal_digits(void) { + assert((sf_num("7", 0, 0) == NO_NUM)); + assert((sf_num("0", 0, 1) == 0)); + assert((sf_num("9", 0, 1) == 9)); + assert((sf_num("123456789", 0, 9) == 123456789)); + assert((sf_num("1234567890", 0, 10) == NO_NUM)); + assert((sf_num("1a", 0, 2) == NO_NUM)); + assert((sf_num("/", 0, 1) == NO_NUM)); + assert((sf_num(":", 0, 1) == NO_NUM)); +} + +void test_a_path_is_five_to_two_hundred_bytes_and_ends_in_t27(void) { + assert((sf_path_ok("specs/x.t27", 0, 0) == false)); + assert((sf_path_ok("a.t27", 0, 5) == true)); + assert((sf_path_ok("z.t27", 0, 5) == true)); + assert((sf_path_ok("Z9.t27", 0, 6) == true)); + assert((sf_path_ok("specs/x.t28", 0, 11) == false)); + assert((sf_path_ok("specs/xxt27", 0, 11) == false)); + uint8_t p[208] = {0}; + size_t w = 0; + while ((w < 197)) { + p[w] = 'a'; + w += 1; + } + w = put_str(&p, 208, w, ".t27", 4); + assert((w == 201)); + assert((sf_path_ok(&p, 1, 201) == true)); + assert((sf_path_ok(&p, 0, 201) == false)); +} + +void test_a_suggested_line_is_printable_ascii_or_tab(void) { + uint8_t b[3] = {0}; + b[0] = 'a'; + b[2] = 'b'; + b[1] = 126; + assert((sf_line_ok(&b, 0, 3) == true)); + b[1] = 32; + assert((sf_line_ok(&b, 0, 3) == true)); + b[1] = 127; + assert((sf_line_ok(&b, 0, 3) == false)); + b[1] = 31; + assert((sf_line_ok(&b, 0, 3) == false)); + b[1] = 1; + assert((sf_line_ok(&b, 0, 3) == false)); +} + +void test_a_control_byte_is_escaped_with_lowercase_hex(void) { + assert((sf_hexd(0) == '0')); + assert((sf_hexd(9) == '9')); + assert((sf_hexd(10) == 'a')); + assert((sf_hexd(11) == 'b')); + assert((sf_hexd(12) == 'c')); + assert((sf_hexd(13) == 'd')); + assert((sf_hexd(14) == 'e')); + assert((sf_hexd(15) == 'f')); + uint8_t o[8] = {0}; + size_t k = sf_esc(&o, 8, 0, 27); + assert((k == 6)); + assert((buf_has(&o, 6, "\\u001b", 6) == true)); +} + +void test_two_lines_are_the_same_only_when_every_byte_is(void) { + assert((sf_same_line("ab", 0, 2, "ab", 0, 2) == true)); + assert((sf_same_line("ab", 0, 2, "ax", 0, 2) == false)); + assert((sf_same_line("ab", 0, 2, "abc", 0, 3) == false)); + assert((sf_same_line("ax", 0, 1, "ay", 0, 1) == true)); +} + +void test_the_line_count_counts_a_last_line_without_a_newline(void) { + assert((sf_lines("a\nb", 3) == 2)); + assert((sf_lines("a\nb\n", 4) == 2)); + assert((sf_lines("a\nb\n", 3) == 2)); + assert((sf_lines("x", 0) == 0)); +} + +void test_a_missing_final_newline_alone_gets_no_suggestion(void) { + uint8_t inp[512] = {0}; + size_t w = put_str(&inp, 512, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = put_str(&inp, 512, w, "F\tspecs/x.t27\t2\n", 16); + w = put_str(&inp, 512, w, "a;", 2); + w = put_str(&inp, 512, w, "H\t@@ -1,1 +1,1 @@\n", 18); + uint8_t out[4096] = {0}; + uint32_t st[9] = {0}; + assert((suggest_review(&inp, w, &out, 4096, &st) == 0)); + assert((st[5] == 1)); + assert((st[6] == 0)); + assert((st[7] == 0)); + assert((((((st[0] + st[1]) + st[2]) + st[3]) + st[4]) == 0)); +} + +void test_one_trailing_blank_line_is_a_one_line_empty_suggestion(void) { + uint8_t inp[512] = {0}; + size_t w = put_str(&inp, 512, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = put_str(&inp, 512, w, "F\tspecs/x.t27\t4\n", 16); + w = put_str(&inp, 512, w, "a;\n\n", 4); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@\n", 18); + uint8_t out[4096] = {0}; + uint32_t st[9] = {0}; + size_t m = suggest_review(&inp, w, &out, 4096, &st); + assert((st[0] == 1)); + assert((buf_has(&out, m, "\"line\":2,\"body\":\"```suggestion\\n```\"}", 37) == true)); + assert((buf_has(&out, m, "start_line", 10) == false)); +} + +void test_two_files_post_two_comments_split_by_one_comma(void) { + uint8_t inp[512] = {0}; + size_t w = t_fix_stream(&inp, 512); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@\n", 18); + w = put_str(&inp, 512, w, "F\tspecs/y.t27\t16\n", 17); + w = put_str(&inp, 512, w, "a = 1; \nb = 2;\n", 16); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@\n", 18); + uint8_t out[4096] = {0}; + uint32_t st[9] = {0}; + size_t m = suggest_review(&inp, w, &out, 4096, &st); + assert((st[0] == 2)); + assert((st[5] == 2)); + assert((st[7] == 0)); + assert((buf_has(&out, m, "\"comments\":[{\"path\":\"specs/x.t27\"", 33) == true)); + assert((buf_has(&out, m, "```\"},{\"path\":\"specs/y.t27\"", 27) == true)); + assert((out[(m - 3)] == '}')); +} + +void test_an_empty_diff_header_withholds_its_file_and_not_the_review(void) { + uint8_t inp[512] = {0}; + size_t w = t_fix_stream(&inp, 512); + w = put_str(&inp, 512, w, "H\t\n", 3); + uint8_t out[4096] = {0}; + uint32_t st[9] = {0}; + assert((suggest_review(&inp, w, &out, 4096, &st) == 0)); + assert((st[0] == 0)); + assert((st[4] == 1)); + assert((st[7] == 0)); +} + +void test_an_unknown_record_shaped_like_a_file_posts_nothing(void) { + uint8_t inp[512] = {0}; + size_t w = t_fix_stream(&inp, 512); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@\n", 18); + w = put_str(&inp, 512, w, "X\tspecs/y.t27\t3\n", 16); + w = put_str(&inp, 512, w, "a;\n", 3); + uint8_t out[4096] = {0}; + uint32_t st[9] = {0}; + assert((suggest_review(&inp, w, &out, 4096, &st) == 0)); + assert((st[7] == 1)); +} + +void test_a_stream_may_end_with_the_last_byte_of_a_file(void) { + uint8_t inp[512] = {0}; + size_t w = t_fix_stream(&inp, 512); + uint8_t out[4096] = {0}; + uint32_t st[9] = {0}; + assert((suggest_review(&inp, w, &out, 4096, &st) == 0)); + assert((st[5] == 1)); + assert((st[7] == 0)); + assert((st[1] == 1)); +} + +void test_a_diff_header_of_127_bytes_is_read_and_128_is_not(void) { + uint8_t inp[512] = {0}; + uint8_t out[4096] = {0}; + uint32_t st[9] = {0}; + size_t w = t_fix_stream(&inp, 512); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@ ", 18); + w = t_rep(&inp, 512, w, "x", 1, 111); + w = put_byte(&inp, 512, w, '\n'); + assert((suggest_review(&inp, w, &out, 4096, &st) > 0)); + assert((st[0] == 1)); + w = t_fix_stream(&inp, 512); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@ ", 18); + w = t_rep(&inp, 512, w, "x", 1, 112); + w = put_byte(&inp, 512, w, '\n'); + assert((suggest_review(&inp, w, &out, 4096, &st) == 0)); + assert((st[0] == 0)); + assert((st[4] == 1)); + assert((st[7] == 0)); +} + +void test_the_range_cap_keeps_2048_and_flags_the_next(void) { + uint8_t inp[37120] = {0}; + uint8_t out[4096] = {0}; + uint32_t st[9] = {0}; + size_t w = t_fix_stream(&inp, 37120); + w = t_rep(&inp, 37120, w, "H\t@@ -1,2 +1,2 @@\n", 18, 2048); + assert((suggest_review(&inp, w, &out, 4096, &st) > 0)); + assert((st[0] == 1)); + assert((st[8] == 0)); + w = t_fix_stream(&inp, 37120); + w = t_rep(&inp, 37120, w, "H\t@@ -1,2 +1,2 @@\n", 18, 2049); + assert((w <= 37120)); + assert((suggest_review(&inp, w, &out, 4096, &st) > 0)); + assert((st[0] == 1)); + assert((st[8] == 1)); +} + +void test_the_hunk_cap_keeps_512_and_flags_the_next_wherever_it_closes(void) { + uint8_t inp[8192] = {0}; + uint8_t out[16384] = {0}; + uint32_t st[9] = {0}; + size_t w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4096); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 512); + assert((suggest_review(&inp, w, &out, 16384, &st) == 0)); + assert((st[1] == 512)); + assert((st[8] == 0)); + w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4104); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 513); + assert((suggest_review(&inp, w, &out, 16384, &st) == 0)); + assert((st[1] == 512)); + assert((st[8] == 1)); + w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4093); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 511); + w = put_str(&inp, 8192, w, "a; \n", 5); + assert((suggest_review(&inp, w, &out, 16384, &st) == 0)); + assert((st[1] == 512)); + assert((st[8] == 0)); + w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4101); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 512); + w = put_str(&inp, 8192, w, "a; \n", 5); + assert((suggest_review(&inp, w, &out, 16384, &st) == 0)); + assert((st[1] == 512)); + assert((st[8] == 1)); + w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4089); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 511); + w = put_str(&inp, 8192, w, "\n", 1); + assert((suggest_review(&inp, w, &out, 16384, &st) == 0)); + assert((st[1] == 512)); + assert((st[8] == 0)); + w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4097); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 512); + w = put_str(&inp, 8192, w, "\n", 1); + assert((suggest_review(&inp, w, &out, 16384, &st) == 0)); + assert((st[1] == 512)); + assert((st[8] == 1)); +} + +void test_the_file_cap_reads_64_files_and_refuses_the_next(void) { + uint8_t inp[2048] = {0}; + uint8_t out[4096] = {0}; + uint32_t st[9] = {0}; + size_t w = put_str(&inp, 2048, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_rep(&inp, 2048, w, "F\tspecs/x.t27\t3\na;\n", 19, 64); + assert((suggest_review(&inp, w, &out, 4096, &st) == 0)); + assert((st[5] == 64)); + assert((st[6] == 0)); + assert((st[7] == 0)); + w = put_str(&inp, 2048, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_rep(&inp, 2048, w, "F\tspecs/x.t27\t3\na;\n", 19, 65); + assert((suggest_review(&inp, w, &out, 4096, &st) == 0)); + assert((st[5] == 65)); + assert((st[6] == 1)); + assert((st[7] == 0)); +} + +void test_the_largest_file_held_is_file_cap_bytes(void) { + uint8_t inp[131200] = {0}; + uint8_t out[4096] = {0}; + uint32_t st[9] = {0}; + size_t w = put_str(&inp, 131200, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 131200, w, 131072); + w = t_rep(&inp, 131200, w, "a;\n", 3, 43690); + w = put_str(&inp, 131200, w, "a;", 2); + assert((suggest_review(&inp, w, &out, 4096, &st) == 0)); + assert((st[5] == 1)); + assert((st[6] == 0)); + assert((st[7] == 0)); + w = put_str(&inp, 131200, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 131200, w, 131073); + w = t_rep(&inp, 131200, w, "a;\n", 3, 43691); + assert((suggest_review(&inp, w, &out, 4096, &st) == 0)); + assert((st[5] == 1)); + assert((st[6] == 1)); + assert((st[7] == 0)); +} + /* ------------------------------------------------------- Test runner (compile with -DT27_TEST_MAIN to execute) @@ -1272,7 +1576,24 @@ int main(void) { test_a_path_that_is_not_a_t27_file_is_refused(); test_twenty_five_fixes_post_twenty_and_count_five_over_the_cap(); test_an_output_buffer_that_is_too_small_is_reported_by_its_size(); - printf("All %d tests passed.\n", 19); + test_a_length_field_is_one_to_nine_decimal_digits(); + test_a_path_is_five_to_two_hundred_bytes_and_ends_in_t27(); + test_a_suggested_line_is_printable_ascii_or_tab(); + test_a_control_byte_is_escaped_with_lowercase_hex(); + test_two_lines_are_the_same_only_when_every_byte_is(); + test_the_line_count_counts_a_last_line_without_a_newline(); + test_a_missing_final_newline_alone_gets_no_suggestion(); + test_one_trailing_blank_line_is_a_one_line_empty_suggestion(); + test_two_files_post_two_comments_split_by_one_comma(); + test_an_empty_diff_header_withholds_its_file_and_not_the_review(); + test_an_unknown_record_shaped_like_a_file_posts_nothing(); + test_a_stream_may_end_with_the_last_byte_of_a_file(); + test_a_diff_header_of_127_bytes_is_read_and_128_is_not(); + test_the_range_cap_keeps_2048_and_flags_the_next(); + test_the_hunk_cap_keeps_512_and_flags_the_next_wherever_it_closes(); + test_the_file_cap_reads_64_files_and_refuses_the_next(); + test_the_largest_file_held_is_file_cap_bytes(); + printf("All %d tests passed.\n", 36); return 0; } #endif /* T27_TEST_MAIN */ diff --git a/specs/claw/suggest_feed.t27 b/specs/claw/suggest_feed.t27 index 945b72538b..61fc7ff223 100644 --- a/specs/claw/suggest_feed.t27 +++ b/specs/claw/suggest_feed.t27 @@ -704,3 +704,309 @@ test an_output_buffer_that_is_too_small_is_reported_by_its_size { const need : usize = suggest_review(&inp, w, &out, 32, &st); assert need > 32; } + +// ... mutation sweep of this file (#10737): each test below kills a mutant the tests above let live. + +fn t_fix_stream(inp : []u8, cap : usize) -> usize { + var w : usize = put_str(inp, cap, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = put_str(inp, cap, w, "F\tspecs/x.t27\t16\n", 17); + return put_str(inp, cap, w, "a = 1; \nb = 2;\n", 16); +} + +fn t_file_head(dst : []u8, cap : usize, w : usize, len : u32) -> usize { + var x : usize = put_str(dst, cap, w, "F\tspecs/x.t27\t", 14); + x = put_count(dst, cap, x, len); + return put_byte(dst, cap, x, '\n'); +} + +fn t_rep(dst : []u8, cap : usize, w : usize, s : str, n : usize, times : usize) -> usize { + var x : usize = w; + var k : usize = 0; + while (k < times) { + x = put_str(dst, cap, x, s, n); + k += 1; + } + return x; +} + +test a_length_field_is_one_to_nine_decimal_digits { + assert sf_num("7", 0, 0) == NO_NUM; + assert sf_num("0", 0, 1) == 0; + assert sf_num("9", 0, 1) == 9; + assert sf_num("123456789", 0, 9) == 123456789; + assert sf_num("1234567890", 0, 10) == NO_NUM; + assert sf_num("1a", 0, 2) == NO_NUM; + assert sf_num("/", 0, 1) == NO_NUM; + assert sf_num(":", 0, 1) == NO_NUM; +} + +test a_path_is_five_to_two_hundred_bytes_and_ends_in_t27 { + assert sf_path_ok("specs/x.t27", 0, 0) == false; + assert sf_path_ok("a.t27", 0, 5) == true; + assert sf_path_ok("z.t27", 0, 5) == true; + assert sf_path_ok("Z9.t27", 0, 6) == true; + assert sf_path_ok("specs/x.t28", 0, 11) == false; + assert sf_path_ok("specs/xxt27", 0, 11) == false; + var p : [208]u8 = undefined; + var w : usize = 0; + while (w < 197) { + p[w] = 'a'; + w += 1; + } + w = put_str(&p, 208, w, ".t27", 4); + assert w == 201; + assert sf_path_ok(&p, 1, 201) == true; + assert sf_path_ok(&p, 0, 201) == false; +} + +test a_suggested_line_is_printable_ascii_or_tab { + var b : [3]u8 = undefined; + b[0] = 'a'; + b[2] = 'b'; + b[1] = 126; + assert sf_line_ok(&b, 0, 3) == true; + b[1] = 32; + assert sf_line_ok(&b, 0, 3) == true; + b[1] = 127; + assert sf_line_ok(&b, 0, 3) == false; + b[1] = 31; + assert sf_line_ok(&b, 0, 3) == false; + b[1] = 1; + assert sf_line_ok(&b, 0, 3) == false; +} + +test a_control_byte_is_escaped_with_lowercase_hex { + assert sf_hexd(0) == '0'; + assert sf_hexd(9) == '9'; + assert sf_hexd(10) == 'a'; + assert sf_hexd(11) == 'b'; + assert sf_hexd(12) == 'c'; + assert sf_hexd(13) == 'd'; + assert sf_hexd(14) == 'e'; + assert sf_hexd(15) == 'f'; + var o : [8]u8 = undefined; + const k : usize = sf_esc(&o, 8, 0, 27); + assert k == 6; + assert buf_has(&o, 6, "\\u001b", 6) == true; +} + +test two_lines_are_the_same_only_when_every_byte_is { + assert sf_same_line("ab", 0, 2, "ab", 0, 2) == true; + assert sf_same_line("ab", 0, 2, "ax", 0, 2) == false; + assert sf_same_line("ab", 0, 2, "abc", 0, 3) == false; + assert sf_same_line("ax", 0, 1, "ay", 0, 1) == true; +} + +test the_line_count_counts_a_last_line_without_a_newline { + assert sf_lines("a\nb", 3) == 2; + assert sf_lines("a\nb\n", 4) == 2; + assert sf_lines("a\nb\n", 3) == 2; + assert sf_lines("x", 0) == 0; +} + +test a_missing_final_newline_alone_gets_no_suggestion { + var inp : [512]u8 = undefined; + var w : usize = put_str(&inp, 512, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = put_str(&inp, 512, w, "F\tspecs/x.t27\t2\n", 16); + w = put_str(&inp, 512, w, "a;", 2); + w = put_str(&inp, 512, w, "H\t@@ -1,1 +1,1 @@\n", 18); + var out : [4096]u8 = undefined; + var st : [9]u32 = undefined; + assert suggest_review(&inp, w, &out, 4096, &st) == 0; + assert st[5] == 1; + assert st[6] == 0; + assert st[7] == 0; + assert st[0] + st[1] + st[2] + st[3] + st[4] == 0; +} + +test one_trailing_blank_line_is_a_one_line_empty_suggestion { + var inp : [512]u8 = undefined; + var w : usize = put_str(&inp, 512, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = put_str(&inp, 512, w, "F\tspecs/x.t27\t4\n", 16); + w = put_str(&inp, 512, w, "a;\n\n", 4); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@\n", 18); + var out : [4096]u8 = undefined; + var st : [9]u32 = undefined; + const m : usize = suggest_review(&inp, w, &out, 4096, &st); + assert st[0] == 1; + assert buf_has(&out, m, "\"line\":2,\"body\":\"```suggestion\\n```\"}", 37) == true; + assert buf_has(&out, m, "start_line", 10) == false; +} + +test two_files_post_two_comments_split_by_one_comma { + var inp : [512]u8 = undefined; + var w : usize = t_fix_stream(&inp, 512); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@\n", 18); + w = put_str(&inp, 512, w, "F\tspecs/y.t27\t16\n", 17); + w = put_str(&inp, 512, w, "a = 1; \nb = 2;\n", 16); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@\n", 18); + var out : [4096]u8 = undefined; + var st : [9]u32 = undefined; + const m : usize = suggest_review(&inp, w, &out, 4096, &st); + assert st[0] == 2; + assert st[5] == 2; + assert st[7] == 0; + assert buf_has(&out, m, "\"comments\":[{\"path\":\"specs/x.t27\"", 33) == true; + assert buf_has(&out, m, "```\"},{\"path\":\"specs/y.t27\"", 27) == true; + assert out[m - 3] == '}'; +} + +test an_empty_diff_header_withholds_its_file_and_not_the_review { + var inp : [512]u8 = undefined; + var w : usize = t_fix_stream(&inp, 512); + w = put_str(&inp, 512, w, "H\t\n", 3); + var out : [4096]u8 = undefined; + var st : [9]u32 = undefined; + assert suggest_review(&inp, w, &out, 4096, &st) == 0; + assert st[0] == 0; + assert st[4] == 1; + assert st[7] == 0; +} + +test an_unknown_record_shaped_like_a_file_posts_nothing { + var inp : [512]u8 = undefined; + var w : usize = t_fix_stream(&inp, 512); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@\n", 18); + w = put_str(&inp, 512, w, "X\tspecs/y.t27\t3\n", 16); + w = put_str(&inp, 512, w, "a;\n", 3); + var out : [4096]u8 = undefined; + var st : [9]u32 = undefined; + assert suggest_review(&inp, w, &out, 4096, &st) == 0; + assert st[7] == 1; +} + +test a_stream_may_end_with_the_last_byte_of_a_file { + var inp : [512]u8 = undefined; + const w : usize = t_fix_stream(&inp, 512); + var out : [4096]u8 = undefined; + var st : [9]u32 = undefined; + assert suggest_review(&inp, w, &out, 4096, &st) == 0; + assert st[5] == 1; + assert st[7] == 0; + assert st[1] == 1; +} + +test a_diff_header_of_127_bytes_is_read_and_128_is_not { + var inp : [512]u8 = undefined; + var out : [4096]u8 = undefined; + var st : [9]u32 = undefined; + var w : usize = t_fix_stream(&inp, 512); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@ ", 18); + w = t_rep(&inp, 512, w, "x", 1, 111); + w = put_byte(&inp, 512, w, '\n'); + assert suggest_review(&inp, w, &out, 4096, &st) > 0; + assert st[0] == 1; + w = t_fix_stream(&inp, 512); + w = put_str(&inp, 512, w, "H\t@@ -1,2 +1,2 @@ ", 18); + w = t_rep(&inp, 512, w, "x", 1, 112); + w = put_byte(&inp, 512, w, '\n'); + assert suggest_review(&inp, w, &out, 4096, &st) == 0; + assert st[0] == 0; + assert st[4] == 1; + assert st[7] == 0; +} + +test the_range_cap_keeps_2048_and_flags_the_next { + var inp : [37120]u8 = undefined; + var out : [4096]u8 = undefined; + var st : [9]u32 = undefined; + var w : usize = t_fix_stream(&inp, 37120); + w = t_rep(&inp, 37120, w, "H\t@@ -1,2 +1,2 @@\n", 18, 2048); + assert suggest_review(&inp, w, &out, 4096, &st) > 0; + assert st[0] == 1; + assert st[8] == 0; + w = t_fix_stream(&inp, 37120); + w = t_rep(&inp, 37120, w, "H\t@@ -1,2 +1,2 @@\n", 18, 2049); + assert w <= 37120; + assert suggest_review(&inp, w, &out, 4096, &st) > 0; + assert st[0] == 1; + assert st[8] == 1; +} + +test the_hunk_cap_keeps_512_and_flags_the_next_wherever_it_closes { + var inp : [8192]u8 = undefined; + var out : [16384]u8 = undefined; + var st : [9]u32 = undefined; + // A run closed by the next line. + var w : usize = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4096); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 512); + assert suggest_review(&inp, w, &out, 16384, &st) == 0; + assert st[1] == 512; + assert st[8] == 0; + w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4104); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 513); + assert suggest_review(&inp, w, &out, 16384, &st) == 0; + assert st[1] == 512; + assert st[8] == 1; + // A run still open at the end of the file. + w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4093); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 511); + w = put_str(&inp, 8192, w, "a; \n", 5); + assert suggest_review(&inp, w, &out, 16384, &st) == 0; + assert st[1] == 512; + assert st[8] == 0; + w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4101); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 512); + w = put_str(&inp, 8192, w, "a; \n", 5); + assert suggest_review(&inp, w, &out, 16384, &st) == 0; + assert st[1] == 512; + assert st[8] == 1; + // The trailing blank lines, the last hunk of a file. + w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4089); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 511); + w = put_str(&inp, 8192, w, "\n", 1); + assert suggest_review(&inp, w, &out, 16384, &st) == 0; + assert st[1] == 512; + assert st[8] == 0; + w = put_str(&inp, 8192, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 8192, w, 4097); + w = t_rep(&inp, 8192, w, "a; \nb;\n", 8, 512); + w = put_str(&inp, 8192, w, "\n", 1); + assert suggest_review(&inp, w, &out, 16384, &st) == 0; + assert st[1] == 512; + assert st[8] == 1; +} + +test the_file_cap_reads_64_files_and_refuses_the_next { + var inp : [2048]u8 = undefined; + var out : [4096]u8 = undefined; + var st : [9]u32 = undefined; + var w : usize = put_str(&inp, 2048, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_rep(&inp, 2048, w, "F\tspecs/x.t27\t3\na;\n", 19, 64); + assert suggest_review(&inp, w, &out, 4096, &st) == 0; + assert st[5] == 64; + assert st[6] == 0; + assert st[7] == 0; + w = put_str(&inp, 2048, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_rep(&inp, 2048, w, "F\tspecs/x.t27\t3\na;\n", 19, 65); + assert suggest_review(&inp, w, &out, 4096, &st) == 0; + assert st[5] == 65; + assert st[6] == 1; + assert st[7] == 0; +} + +test the_largest_file_held_is_file_cap_bytes { + var inp : [131200]u8 = undefined; + var out : [4096]u8 = undefined; + var st : [9]u32 = undefined; + var w : usize = put_str(&inp, 131200, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 131200, w, 131072); + w = t_rep(&inp, 131200, w, "a;\n", 3, 43690); + w = put_str(&inp, 131200, w, "a;", 2); + assert suggest_review(&inp, w, &out, 4096, &st) == 0; + assert st[5] == 1; + assert st[6] == 0; + assert st[7] == 0; + w = put_str(&inp, 131200, 0, "S\t0123456789abcdef0123456789abcdef01234567\n", 43); + w = t_file_head(&inp, 131200, w, 131073); + w = t_rep(&inp, 131200, w, "a;\n", 3, 43691); + assert suggest_review(&inp, w, &out, 4096, &st) == 0; + assert st[5] == 1; + assert st[6] == 1; + assert st[7] == 0; +}