diff --git a/src/t_regression.c b/src/t_regression.c index c245eff5b841f28ac47814869b22a950be0069fe..3bf763600962bdcdad6208863ba27f92966195f3 100644 --- a/src/t_regression.c +++ b/src/t_regression.c @@ -1,5 +1,6 @@ #include <glib.h> #include <stdint.h> +#include <stdlib.h> #include "glue.h" #include "hammer.h" #include "test_suite.h" @@ -472,10 +473,12 @@ static void test_issue83() { g_check_cmp_int(r, ==, 0); } +/* + * This is Meg's cut-down bug 60 test case + */ static void test_bug60() { - -//There is probably an even smaller example that shows the issue + /* There is probably an even smaller example that shows the issue */ HParser *zed = NULL; HParser *alpha = NULL; @@ -493,7 +496,7 @@ static void test_bug60() { zed = h_ch('z'); - vchar = h_ch_range(0x79, 0x7a); // allows y and z + vchar = h_ch_range(0x79, 0x7a); /* allows y and z */ alpha = h_ch('a'); @@ -523,6 +526,88 @@ static void test_bug60() { } +/* + * This is the original bug60 test case; cut down from an ABNF parser + */ + +#define BUG60_ABNF_SCAN_UP_TO 64 + +static void test_bug60_abnf() { + HParser *newline = NULL; + HParser *alpha = NULL; + HParser *sp = NULL; + HParser *vchar = NULL; + HParser *equal = NULL; + HParser *semicolon = NULL; + HParser *comment = NULL; + HParser *c_nl = NULL; + HParser *c_sp = NULL; + HParser *defined_as = NULL; + HParser *alphas = NULL; + HParser *rule = NULL; + HParser *rulelist = NULL; + HParser *p = NULL; + int i, j, r, s_size; + char *s = NULL; + const char *test_string_template = "x = y z%s;%s\n\n"; + char buf_1[BUG60_ABNF_SCAN_UP_TO+1]; + char buf_2[2*BUG60_ABNF_SCAN_UP_TO+1]; + + newline = h_ch('\n'); + alpha = h_choice(h_ch_range('A', 'Z'), h_ch_range('a', 'z'), NULL); + sp = h_ch(' '); + vchar = h_ch_range(0x21, 0x7e); + equal = h_ch('='); + semicolon = h_ch(';'); + comment = h_sequence( + semicolon, + h_many(h_choice(sp, vchar, NULL)), + newline, + NULL); + c_nl = h_choice(comment, newline, NULL); + c_sp = h_choice(sp, h_sequence(c_nl, sp, NULL), NULL); + defined_as = h_sequence(h_many(c_sp), equal, h_many(c_sp), NULL); + alphas = h_sequence( + alpha, + h_many(h_sequence(h_many1(c_sp), alpha, NULL)), + h_many(c_sp), + NULL); + rule = h_sequence(alpha, defined_as, alphas, c_nl, NULL); + rulelist = h_many1(h_choice( + rule, + h_sequence(h_many(c_sp), c_nl, NULL), + NULL)); + + p = rulelist; + g_check_compilable(p, PB_GLR, 1); + + /* Have a buffer for the string */ + s_size = strlen(test_string_template) + 3*BUG60_ABNF_SCAN_UP_TO + 1; + s = malloc(s_size); + g_check_cmp_ptr(s, !=, NULL); + + /* + * Try to parse all the different strings according to the template up to + * the scan limit. + * + * Correct behavior: it parses for all values of i, j. + * Bugged behavior: when i % 3 != 0, parse failures begin to occur at + * j == (i / 3) + (i % 3). + */ + for (i = 0; i < BUG60_ABNF_SCAN_UP_TO; ++i) { + memset(buf_1, ' ', i); + buf_1[i] = 0; + for (j = 0; j < 2*BUG60_ABNF_SCAN_UP_TO; ++j) { + memset(buf_2, 'x', j); + buf_2[j] = 0; + snprintf(s, s_size, test_string_template, buf_1, buf_2); + g_check_parse_ok_no_compile(p, s, strlen(s)); + } + } + + free(s); +} + void register_regression_tests(void) { g_test_add_func("/core/regression/bug118", test_bug118); g_test_add_func("/core/regression/seq_index_path", test_seq_index_path); @@ -540,4 +625,5 @@ void register_regression_tests(void) { g_test_add_func("/core/regression/issue92", test_issue92); g_test_add_func("/core/regression/issue83", test_issue83); g_test_add_func("/core/regression/bug60", test_bug60); + g_test_add_func("/core/regression/bug60_abnf", test_bug60_abnf); } diff --git a/src/test_suite.h b/src/test_suite.h index d81d9ea6c70e26020dee800981d886a8426d2701..56fa42c6494bd205996328009ca71dc4470ae337 100644 --- a/src/test_suite.h +++ b/src/test_suite.h @@ -71,24 +71,36 @@ } while(0) #define g_check_compilable(lang, backend, params) do { \ - if (!h_compile(lang, backend, params)) { \ - g_test_message("Language is not %s(%s)", #backend, params); \ + int r = h_compile((HParser *)(lang), (HParserBackend)(backend), (void *)(params)); \ + if (r != 0) { \ + g_test_message("Language is not %s(%s)", #backend, #params); \ g_test_fail(); \ } \ } while(0) - +#define print_arena_stats(arena) do { \ + if (g_test_verbose()) { \ + HArenaStats stats; \ + h_allocator_stats(arena, &stats); \ + g_test_message("Parse used %zd bytes, wasted %zd bytes. " \ + "Inefficiency: %5f%%", \ + stats.used, stats.wasted, \ + stats.wasted * 100. / (stats.used+stats.wasted)); \ + } \ + } while(0) + #define g_check_parse_failed__m(mm__, parser, backend, input, inp_len) do { \ int skip = h_compile__m(mm__, (HParser *)(parser), (HParserBackend)backend, NULL); \ if(skip != 0) { \ - g_test_message("Compile failed"); \ + g_test_message("Compile failed on line %d", __LINE__); \ g_test_fail(); \ break; \ } \ - HParseResult *result = h_parse__m(mm__, parser, (const uint8_t*)input, inp_len); \ - if (NULL != result) { \ - h_parse_result_free(result); \ - g_test_message("Check failed: shouldn't have succeeded, but did"); \ + HParseResult *res = h_parse__m(mm__, parser, (const uint8_t*)input, inp_len); \ + if (NULL != res) { \ + print_arena_stats(res->arena); \ + h_parse_result_free(res); \ + g_test_message("Check failed: parse shouldn't have succeeded, but did on line %d", __LINE__); \ g_test_fail(); \ } \ } while(0) @@ -96,25 +108,27 @@ #define g_check_parse_failed(p, be, input, len) \ g_check_parse_failed__m(&system_allocator, p, be, input, len) -#define print_arena_stats(arena) do { \ - if (g_test_verbose()) { \ - HArenaStats stats; \ - h_allocator_stats(arena, &stats); \ - g_test_message("Parse used %zd bytes, wasted %zd bytes. " \ - "Inefficiency: %5f%%", \ - stats.used, stats.wasted, \ - stats.wasted * 100. / (stats.used+stats.wasted)); \ +#define g_check_parse_failed_no_compile__m(mm__, parser, input, inp_len) do { \ + HParseResult *res = h_parse__m(mm__, parser, (const uint8_t*)input, inp_len); \ + if (NULL != res) { \ + print_arena_stats(res->arena); \ + h_parse_result_free(res); \ + g_test_message("Check failed: shouldn't have succeeded, but did on ;ine %d", __LINE__); \ + g_test_fail(); \ } \ } while(0) -#define g_check_parse_ok(parser, backend, input, inp_len) do { \ - int skip = h_compile((HParser *)(parser), (HParserBackend) backend, NULL); \ +#define g_check_parse_failed_no_compile(p, input, len) \ + g_check_parse_failed__m(&system_allocator, p, input, len) + +#define g_check_parse_ok__m(mm__, parser, backend, input, inp_len) do { \ + int skip = h_compile__m(mm__, (HParser *)(parser), (HParserBackend) backend, NULL); \ if(skip) { \ g_test_message("Compile failed"); \ g_test_fail(); \ break; \ } \ - HParseResult *res = h_parse(parser, (const uint8_t*)input, inp_len); \ + HParseResult *res = h_parse__m(mm__, parser, (const uint8_t*)input, inp_len); \ if (!res) { \ g_test_message("Parse failed on line %d", __LINE__); \ g_test_fail(); \ @@ -124,14 +138,31 @@ } \ } while(0) -#define g_check_parse_match(parser, backend, input, inp_len, result) do { \ - int skip = h_compile((HParser *)(parser), (HParserBackend) backend, NULL); \ +#define g_check_parse_ok(p, be, input, len) \ + g_check_parse_ok__m(&system_allocator, p, be, input, len) + +#define g_check_parse_ok_no_compile__m(mm__, parser, input, inp_len) do { \ + HParseResult *res = h_parse__m(mm__, parser, (const uint8_t*)input, inp_len); \ + if (!res) { \ + g_test_message("Parse failed on line %d", __LINE__); \ + g_test_fail(); \ + } else { \ + print_arena_stats(res->arena); \ + h_parse_result_free(res); \ + } \ + } while(0) + +#define g_check_parse_ok_no_compile(p, input, len) \ + g_check_parse_ok_no_compile__m(&system_allocator, p, input, len) + +#define g_check_parse_match__m(mm__, parser, backend, input, inp_len, result) do { \ + int skip = h_compile__m(mm__, (HParser *)(parser), (HParserBackend) backend, NULL); \ if(skip) { \ g_test_message("Compile failed"); \ g_test_fail(); \ break; \ } \ - HParseResult *res = h_parse(parser, (const uint8_t*)input, inp_len); \ + HParseResult *res = h_parse__m(mm__, (HParser *)(parser), (const uint8_t*)input, inp_len); \ if (!res) { \ g_test_message("Parse failed on line %d", __LINE__); \ g_test_fail(); \ @@ -144,6 +175,26 @@ } \ } while(0) +#define g_check_parse_match(parser, backend, input, inp_len, result) \ + g_check_parse_match__m(&system_allocator, parser, backend, input, inp_len, result) + +#define g_check_parse_match_no_compile__m(mm__, parser, input, inp_len, result) do { \ + HParseResult *res = h_parse__m(mm__, (HParser *)(parser), (const uint8_t*)input, inp_len); \ + if (!res) { \ + g_test_message("Parse failed on line %d", __LINE__); \ + g_test_fail(); \ + } else { \ + char* cres = h_write_result_unamb(res->ast); \ + g_check_string(cres, ==, result); \ + (&system_allocator)->free(&system_allocator, cres); \ + print_arena_stats(res->arena); \ + h_parse_result_free(res); \ + } \ + } while(0) + +#define g_check_parse_match_no_compile(parser, input, inp_len, result) \ + g_check_parse_match_no_compile__m(&system_allocator, parser, input, inp_len, result) + #define g_check_parse_chunks_failed__m(mm__, parser, backend, chunk1, c1_len, chunk2, c2_len) do { \ int skip = h_compile__m(mm__, (HParser *)(parser), (HParserBackend)backend, NULL); \ if(skip) { \ @@ -155,7 +206,7 @@ } while(0) #define g_check_parse_chunks_failed___m(mm__, parser, chunk1, c1_len, chunk2, c2_len) do { \ - HSuspendedParser *s = h_parse_start__m(mm__, parser); \ + HSuspendedParser *s = h_parse_start__m(mm__, (HParser *)(parser)); \ if(!s) { \ g_test_message("Chunk-wise parsing not available"); \ g_test_fail(); \ @@ -188,7 +239,7 @@ } while(0) #define g_check_parse_chunks_ok_(parser, chunk1, c1_len, chunk2, c2_len) do { \ - HSuspendedParser *s = h_parse_start(parser); \ + HSuspendedParser *s = h_parse_start((HParser *)(parser)); \ if(!s) { \ g_test_message("Chunk-wise parsing not available"); \ g_test_fail(); \ @@ -217,7 +268,7 @@ } while(0) #define g_check_parse_chunks_match_(parser, chunk1, c1_len, chunk2, c2_len, result) do { \ - HSuspendedParser *s = h_parse_start(parser); \ + HSuspendedParser *s = h_parse_start((HParser *)(parser)); \ if(!s) { \ g_test_message("Chunk-wise parsing not available"); \ g_test_fail(); \