From 871a4dc8b5ff838a17a1d8a9ff282e15b871c2de Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Stefan=20B=C3=BChler?= Date: Sat, 5 Dec 2009 15:56:14 +0100 Subject: [PATCH] Add unit-testing for range-parser and fix the parser --- src/CMakeLists.txt | 1 + src/main/http_range_parser.rl | 32 +++++++-- src/unittests/test-range-parser.c | 106 ++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+), 7 deletions(-) create mode 100644 src/unittests/test-range-parser.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ccc6ff1..1be9a50 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -395,5 +395,6 @@ IF(BUILD_UNIT_TESTS) ADD_TEST_BINARY(Utils-UnitTest test-utils unittests/test-utils.c) ADD_TEST_BINARY(Chunk-UnitTest test-chunk unittests/test-chunk.c) + ADD_TEST_BINARY(RangeParser-UnitTest test-range-parser unittests/test-range-parser.c) ENDIF(BUILD_UNIT_TESTS) \ No newline at end of file diff --git a/src/main/http_range_parser.rl b/src/main/http_range_parser.rl index 0c61424..6a23f04 100644 --- a/src/main/http_range_parser.rl +++ b/src/main/http_range_parser.rl @@ -33,18 +33,24 @@ } action last_byte { s->range_end = tmp; + found = TRUE; } action last_byte_empty { - s->range_end = s->limit; + s->range_end = s->limit - 1; + found = TRUE; } action suffix_range { s->range_end = s->limit - 1; s->range_start = s->limit - tmp; + found = TRUE; + } + action range_complete { + fbreak; } - range = (int %first_byte "-" (int %last_byte | "" %last_byte_empty) | "-" int %suffix_range); + range = (int %first_byte ws* "-" ws** (int %last_byte | "" %last_byte_empty) | "-" ws* int %suffix_range) ; - main := ws* "bytes" "=" range ("," >{fbreak;} range)**; + main := ws* "bytes" ws* "=" (ws | ",")* range ( ws* "," >range_complete (ws | ",")* range)** (ws | ",")*; write data; }%% @@ -53,21 +59,33 @@ liParseHttpRangeResult li_parse_http_range_next(liParseHttpRangeState* s) { const char *pe, *eof; goffset tmp = 0; + if (s->cs == http_range_parser_error) { + return LI_PARSE_HTTP_RANGE_INVALID; + } + eof = pe = s->data->str + s->data->len; for ( ;; ) { - if (s->cs >= http_range_parser_first_final) { + gboolean found = FALSE; + + if (s->data_pos >= eof) { return s->found_valid_range ? LI_PARSE_HTTP_RANGE_DONE : LI_PARSE_HTTP_RANGE_NOT_SATISFIABLE; } %% write exec; - if (s->cs >= http_range_parser_first_final) { - s->last_range = TRUE; - } else if (s->cs == http_range_parser_error) { + if (s->cs == http_range_parser_error) { return LI_PARSE_HTTP_RANGE_INVALID; } + if (s->data_pos >= eof) { + s->last_range = TRUE; + } + + if (!found) { + return s->found_valid_range ? LI_PARSE_HTTP_RANGE_DONE : LI_PARSE_HTTP_RANGE_NOT_SATISFIABLE; + } + if (s->range_end >= s->limit) { s->range_end = s->limit - 1; } diff --git a/src/unittests/test-range-parser.c b/src/unittests/test-range-parser.c new file mode 100644 index 0000000..98c6349 --- /dev/null +++ b/src/unittests/test-range-parser.c @@ -0,0 +1,106 @@ + +#include +#include + +#define LIMIT_EXAMPLE (1409328ul) + +typedef struct { + liParseHttpRangeResult res; + goffset start, end; +} rangeentry; + +static const gchar* rangeresult_str(liParseHttpRangeResult res) { + switch (res) { + case LI_PARSE_HTTP_RANGE_OK: return "Range-OK"; + case LI_PARSE_HTTP_RANGE_DONE: return "Range-Done"; + case LI_PARSE_HTTP_RANGE_INVALID: return "Range-Invalid"; + case LI_PARSE_HTTP_RANGE_NOT_SATISFIABLE: return "Range-NotSatisfiable"; + default: return "RangeResult-Unknown"; + } +} + +#define range_error(fmt, ...) g_error("li_parse_http_range_next error in round %i for '%s' (remaining '%s'): " fmt, round, range->str, s.data_pos, __VA_ARGS__) + +static void test_range(GString *range, goffset limit, const rangeentry *results) { + liParseHttpRangeState s; + liParseHttpRangeResult res = LI_PARSE_HTTP_RANGE_OK; + guint round = 1; + + li_parse_http_range_init(&s, range, limit); + + for ( ; res == LI_PARSE_HTTP_RANGE_OK; results++, round++ ) { +#if 0 + g_print("Round %i, parse '%s', state: %i\n", round, s.data_pos, s.cs); +#endif + res = li_parse_http_range_next(&s); + if (res != results->res) { + range_error("unexpected parse result '%s' (expected '%s')", rangeresult_str(res), rangeresult_str(results->res)); + } + if (res != LI_PARSE_HTTP_RANGE_OK) break; + + if (s.range_length != s.range_end - s.range_start + 1) { + range_error("unexpected range length %"L_GOFFSET_FORMAT" (expected %"L_GOFFSET_FORMAT")", s.range_length, s.range_end - s.range_start + 1); + } else if (s.range_start != results->start) { + range_error("unexpected range start %"L_GOFFSET_FORMAT" (expected %"L_GOFFSET_FORMAT")", s.range_start, results->start); + } else if (s.range_end != results->end) { + range_error("unexpected range end %"L_GOFFSET_FORMAT" (expected %"L_GOFFSET_FORMAT")", s.range_end, results->end); + } + } + + li_parse_http_range_clear(&s); +} + + +static void test_range_parser_ex1(void) { + static const rangeentry results[] = { + { LI_PARSE_HTTP_RANGE_OK, LIMIT_EXAMPLE - 500, LIMIT_EXAMPLE - 1 }, + { LI_PARSE_HTTP_RANGE_OK, 10, LIMIT_EXAMPLE - 1 }, + { LI_PARSE_HTTP_RANGE_OK, 5, 9 }, + { LI_PARSE_HTTP_RANGE_DONE, -1, -1 } + }; + GString range = li_const_gstring(CONST_STR_LEN("bytes=-500,,10-,5-9,")); + + test_range(&range, LIMIT_EXAMPLE, results); +} + +static void test_range_parser_ex2(void) { + static const rangeentry results[] = { + { LI_PARSE_HTTP_RANGE_OK, LIMIT_EXAMPLE - 500, LIMIT_EXAMPLE - 1 }, + { LI_PARSE_HTTP_RANGE_OK, 10, LIMIT_EXAMPLE - 1 }, + { LI_PARSE_HTTP_RANGE_OK, 5, 9 }, + { LI_PARSE_HTTP_RANGE_DONE, -1, -1 } + }; + GString range = li_const_gstring(CONST_STR_LEN("bytes = , -500, ,, ,10- ,5-9 ,,")); + + test_range(&range, LIMIT_EXAMPLE, results); +} + +static void test_range_parser_ex3(void) { + static const rangeentry results[] = { + { LI_PARSE_HTTP_RANGE_NOT_SATISFIABLE, -1, -1 } + }; + GString range = li_const_gstring(CONST_STR_LEN("bytes=0")); + + test_range(&range, LIMIT_EXAMPLE, results); +} + +static void test_range_parser_ex4(void) { + static const rangeentry results[] = { + { LI_PARSE_HTTP_RANGE_OK, 0, LIMIT_EXAMPLE - 1 }, + { LI_PARSE_HTTP_RANGE_DONE, -1, -1 } + }; + GString range = li_const_gstring(CONST_STR_LEN("bytes=0-")); + + test_range(&range, LIMIT_EXAMPLE, results); +} + +int main(int argc, char **argv) { + g_test_init(&argc, &argv, NULL); + + g_test_add_func("/range-parser/range_example_1", test_range_parser_ex1); + g_test_add_func("/range-parser/range_example_2", test_range_parser_ex2); + g_test_add_func("/range-parser/range_example_3", test_range_parser_ex3); + g_test_add_func("/range-parser/range_example_4", test_range_parser_ex4); + + return g_test_run(); +}