From 505c965e3655b8ae94880a23baf8ecc2092163c4 Mon Sep 17 00:00:00 2001 From: Neels Hofmeyr Date: Tue, 26 Sep 2017 15:24:58 +0200 Subject: CTRL: add unit tests for CTRL command parsing This uncovers some interesting behavior of the CTRL interface which we may want to guard against in subsequent patches: trailing whitespace, ignored tokens, special characters as cmd->id. Change-Id: If7af06d50ca71fd528b08cd70310774d5a53f0f7 --- tests/ctrl/ctrl_test.c | 251 ++++++++++++++++++++++++++++++++++++++++++++++++ tests/ctrl/ctrl_test.ok | 102 ++++++++++++++++++++ 2 files changed, 353 insertions(+) (limited to 'tests/ctrl') diff --git a/tests/ctrl/ctrl_test.c b/tests/ctrl/ctrl_test.c index 08be15f6..b8425c7e 100644 --- a/tests/ctrl/ctrl_test.c +++ b/tests/ctrl/ctrl_test.c @@ -6,6 +6,9 @@ #include #include +#include +#include +#include static void check_type(enum ctrl_type c) { @@ -19,8 +22,254 @@ static void check_type(enum ctrl_type c) printf("-> %d %s\n", v, c != v ? "FAIL" : "OK"); } +struct msgb *msgb_from_string(const char *str) +{ + char *rc; + size_t len = strlen(str) + 1; + /* ctrl_cmd_parse() appends a '\0' to the msgb, allow one more byte. */ + struct msgb *msg = msgb_alloc(len + 1, str); + msg->l2h = msg->head; + rc = (char*)msgb_put(msg, len); + OSMO_ASSERT(rc == (char*)msg->l2h); + strcpy(rc, str); + return msg; +} + +static void *ctx = NULL; + +void print_escaped(const char *str) +{ + if (!str) { + printf("NULL"); + return; + } + + printf("'"); + for (;*str; str++) { + switch (*str) { + case '\n': + printf("\\n"); + break; + case '\r': + printf("\\r"); + break; + case '\t': + printf("\\t"); + break; + default: + printf("%c", *str); + break; + } + } + printf("'"); +} + +void assert_same_str(const char *label, const char *expect, const char *got) +{ + if ((expect == got) || (expect && got && (strcmp(expect, got) == 0))) { + printf("%s = ", label); + print_escaped(got); + printf("\n"); + return; + } + + printf("MISMATCH for '%s':\ngot: ", label); print_escaped(got); + printf("\nexpected: "); print_escaped(expect); + printf("\n"); + OSMO_ASSERT(expect == got); +} + +static void assert_parsing(const char *str, const struct ctrl_cmd *expect) +{ + struct ctrl_cmd *cmd; + struct msgb *msg = msgb_from_string(str); + + printf("test parsing: "); + print_escaped(str); + printf("\n"); + + cmd = ctrl_cmd_parse(ctx, msg); + OSMO_ASSERT(cmd); + + OSMO_ASSERT(expect->type == cmd->type); + +#define ASSERT_SAME_STR(field) \ + assert_same_str(#field, expect->field, cmd->field) + + ASSERT_SAME_STR(id); + ASSERT_SAME_STR(variable); + ASSERT_SAME_STR(value); + ASSERT_SAME_STR(reply); + + talloc_free(cmd); + msgb_free(msg); + + printf("ok\n"); +} + +struct one_parsing_test { + const char *cmd_str; + struct ctrl_cmd expect; +}; + +static const struct one_parsing_test test_parsing_list[] = { + { "GET 1 variable", + { + .type = CTRL_TYPE_GET, + .id = "1", + .variable = "variable", + } + }, + { "GET 1 variable\n", + { + .type = CTRL_TYPE_GET, + .id = "1", + .variable = "variable\n", /* current bug */ + } + }, + { "GET 1 var\ni\nable", + { + .type = CTRL_TYPE_GET, + .id = "1", + .variable = "var\ni\nable", /* current bug */ + } + }, + { "GET 1 variable value", + { + .type = CTRL_TYPE_GET, + .id = "1", + .variable = "variable", + .value = NULL, + } + }, + { "GET 1 variable value\n", + { + .type = CTRL_TYPE_GET, + .id = "1", + .variable = "variable", + .value = NULL, + } + }, + { "GET 1 variable multiple value tokens", + { + .type = CTRL_TYPE_GET, + .id = "1", + .variable = "variable", + .value = NULL, + } + }, + { "GET 1 variable multiple value tokens\n", + { + .type = CTRL_TYPE_GET, + .id = "1", + .variable = "variable", + .value = NULL, + } + }, + { "SET 1 variable value", + { + .type = CTRL_TYPE_SET, + .id = "1", + .variable = "variable", + .value = "value", + } + }, + { "SET 1 variable value\n", + { + .type = CTRL_TYPE_SET, + .id = "1", + .variable = "variable", + .value = "value", + } + }, + { "SET weird_id variable value", + { + .type = CTRL_TYPE_SET, + .id = "weird_id", + .variable = "variable", + .value = "value", + } + }, + { "SET weird_id variable value\n", + { + .type = CTRL_TYPE_SET, + .id = "weird_id", + .variable = "variable", + .value = "value", + } + }, + { "SET 1 variable multiple value tokens", + { + .type = CTRL_TYPE_SET, + .id = "1", + .variable = "variable", + .value = "multiple value tokens", + } + }, + { "SET 1 variable multiple value tokens\n", + { + .type = CTRL_TYPE_SET, + .id = "1", + .variable = "variable", + .value = "multiple value tokens", + } + }, + { "SET 1 variable value_with_trailing_spaces ", + { + .type = CTRL_TYPE_SET, + .id = "1", + .variable = "variable", + .value = "value_with_trailing_spaces ", + } + }, + { "SET 1 variable value_with_trailing_spaces \n", + { + .type = CTRL_TYPE_SET, + .id = "1", + .variable = "variable", + .value = "value_with_trailing_spaces ", + } + }, + { "SET \n special_char_id value", + { + .type = CTRL_TYPE_SET, + .id = "\n", + .variable = "special_char_id", + .value = "value", + } + }, + { "SET \t special_char_id value", + { + .type = CTRL_TYPE_SET, + .id = "\t", + .variable = "special_char_id", + .value = "value", + } + }, +}; + +static void test_parsing() +{ + int i; + + for (i = 0; i < ARRAY_SIZE(test_parsing_list); i++) + assert_parsing(test_parsing_list[i].cmd_str, + &test_parsing_list[i].expect); +} + +static struct log_info_cat test_categories[] = { +}; + +static struct log_info info = { + .cat = test_categories, + .num_cat = ARRAY_SIZE(test_categories), +}; + int main(int argc, char **argv) { + ctx = talloc_named_const(NULL, 1, "ctrl_test"); + osmo_init_logging(&info); + printf("Checking ctrl types...\n"); check_type(CTRL_TYPE_UNKNOWN); @@ -32,5 +281,7 @@ int main(int argc, char **argv) check_type(CTRL_TYPE_ERROR); check_type(64); + test_parsing(); + return 0; } diff --git a/tests/ctrl/ctrl_test.ok b/tests/ctrl/ctrl_test.ok index 8f97a27f..9c8877b2 100644 --- a/tests/ctrl/ctrl_test.ok +++ b/tests/ctrl/ctrl_test.ok @@ -7,3 +7,105 @@ ctrl type 4 is SET_REPLY -> 4 OK ctrl type 5 is TRAP -> 5 OK ctrl type 6 is ERROR -> 6 OK ctrl type 64 is unknown 0x40 [PARSE FAILED] +test parsing: 'GET 1 variable' +id = '1' +variable = 'variable' +value = NULL +reply = NULL +ok +test parsing: 'GET 1 variable\n' +id = '1' +variable = 'variable\n' +value = NULL +reply = NULL +ok +test parsing: 'GET 1 var\ni\nable' +id = '1' +variable = 'var\ni\nable' +value = NULL +reply = NULL +ok +test parsing: 'GET 1 variable value' +id = '1' +variable = 'variable' +value = NULL +reply = NULL +ok +test parsing: 'GET 1 variable value\n' +id = '1' +variable = 'variable' +value = NULL +reply = NULL +ok +test parsing: 'GET 1 variable multiple value tokens' +id = '1' +variable = 'variable' +value = NULL +reply = NULL +ok +test parsing: 'GET 1 variable multiple value tokens\n' +id = '1' +variable = 'variable' +value = NULL +reply = NULL +ok +test parsing: 'SET 1 variable value' +id = '1' +variable = 'variable' +value = 'value' +reply = NULL +ok +test parsing: 'SET 1 variable value\n' +id = '1' +variable = 'variable' +value = 'value' +reply = NULL +ok +test parsing: 'SET weird_id variable value' +id = 'weird_id' +variable = 'variable' +value = 'value' +reply = NULL +ok +test parsing: 'SET weird_id variable value\n' +id = 'weird_id' +variable = 'variable' +value = 'value' +reply = NULL +ok +test parsing: 'SET 1 variable multiple value tokens' +id = '1' +variable = 'variable' +value = 'multiple value tokens' +reply = NULL +ok +test parsing: 'SET 1 variable multiple value tokens\n' +id = '1' +variable = 'variable' +value = 'multiple value tokens' +reply = NULL +ok +test parsing: 'SET 1 variable value_with_trailing_spaces ' +id = '1' +variable = 'variable' +value = 'value_with_trailing_spaces ' +reply = NULL +ok +test parsing: 'SET 1 variable value_with_trailing_spaces \n' +id = '1' +variable = 'variable' +value = 'value_with_trailing_spaces ' +reply = NULL +ok +test parsing: 'SET \n special_char_id value' +id = '\n' +variable = 'special_char_id' +value = 'value' +reply = NULL +ok +test parsing: 'SET \t special_char_id value' +id = '\t' +variable = 'special_char_id' +value = 'value' +reply = NULL +ok -- cgit v1.2.3