From b0a5cd8a28bf1d1883317ceac6cb8967d840d6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Petr=20=C5=A0tetiar?= Date: Tue, 19 Nov 2019 14:31:44 +0100 Subject: add cram based unit tests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit For improved QA etc. For the start with initial test cases for avl, base64, jshn and list components. Moved runqueue and blobmsg from examples to tests. Converted just a few first test cases from json-script example into the new cram based unit test, more to come. Signed-off-by: Petr Štetiar --- tests/CMakeLists.txt | 13 ++++ tests/cram/CMakeLists.txt | 22 ++++++ tests/cram/inputs/json-script.json | 38 ++++++++++ tests/cram/test_avl.t | 11 +++ tests/cram/test_base64.t | 21 +++++ tests/cram/test_blobmsg.t | 17 +++++ tests/cram/test_jshn.t | 25 ++++++ tests/cram/test_json_script.t | 96 +++++++++++++++++++++++ tests/cram/test_list.t | 22 ++++++ tests/cram/test_runqueue.t | 14 ++++ tests/test-avl.c | 87 +++++++++++++++++++++ tests/test-b64.c | 39 ++++++++++ tests/test-blobmsg.c | 152 +++++++++++++++++++++++++++++++++++++ tests/test-json-script.c | 84 ++++++++++++++++++++ tests/test-list.c | 91 ++++++++++++++++++++++ tests/test-runqueue.c | 112 +++++++++++++++++++++++++++ 16 files changed, 844 insertions(+) create mode 100644 tests/CMakeLists.txt create mode 100644 tests/cram/CMakeLists.txt create mode 100644 tests/cram/inputs/json-script.json create mode 100644 tests/cram/test_avl.t create mode 100644 tests/cram/test_base64.t create mode 100644 tests/cram/test_blobmsg.t create mode 100644 tests/cram/test_jshn.t create mode 100644 tests/cram/test_json_script.t create mode 100644 tests/cram/test_list.t create mode 100644 tests/cram/test_runqueue.t create mode 100644 tests/test-avl.c create mode 100644 tests/test-b64.c create mode 100644 tests/test-blobmsg.c create mode 100644 tests/test-json-script.c create mode 100644 tests/test-list.c create mode 100644 tests/test-runqueue.c (limited to 'tests') diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..1c44825 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,13 @@ +ADD_SUBDIRECTORY(cram) + +MACRO(ADD_UNIT_TEST name) + ADD_EXECUTABLE(${name} ${name}.c) + TARGET_LINK_LIBRARIES(${name} ubox blobmsg_json json_script ${json}) + TARGET_INCLUDE_DIRECTORIES(${name} PRIVATE ${PROJECT_SOURCE_DIR}) +ENDMACRO(ADD_UNIT_TEST) + +FILE(GLOB test_cases "test-*.c") +FOREACH(test_case ${test_cases}) + GET_FILENAME_COMPONENT(test_case ${test_case} NAME_WE) + ADD_UNIT_TEST(${test_case}) +ENDFOREACH(test_case) diff --git a/tests/cram/CMakeLists.txt b/tests/cram/CMakeLists.txt new file mode 100644 index 0000000..bebd821 --- /dev/null +++ b/tests/cram/CMakeLists.txt @@ -0,0 +1,22 @@ +FIND_PACKAGE(PythonInterp 3 REQUIRED) +FILE(GLOB test_cases "test_*.t") + +SET(PYTHON_VENV_DIR "${CMAKE_CURRENT_BINARY_DIR}/.venv") +SET(PYTHON_VENV_PIP "${PYTHON_VENV_DIR}/bin/pip") +SET(PYTHON_VENV_CRAM "${PYTHON_VENV_DIR}/bin/cram") + +ADD_CUSTOM_COMMAND( + OUTPUT ${PYTHON_VENV_CRAM} + COMMAND ${PYTHON_EXECUTABLE} -m venv ${PYTHON_VENV_DIR} + COMMAND ${PYTHON_VENV_PIP} install cram +) +ADD_CUSTOM_TARGET(prepare-cram-venv ALL DEPENDS ${PYTHON_VENV_CRAM}) + +ADD_TEST( + NAME cram + COMMAND ${PYTHON_VENV_CRAM} ${test_cases} + WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} +) + +SET_PROPERTY(TEST cram APPEND PROPERTY ENVIRONMENT "JSHN=$") +SET_PROPERTY(TEST cram APPEND PROPERTY ENVIRONMENT "TEST_BIN_DIR=$") diff --git a/tests/cram/inputs/json-script.json b/tests/cram/inputs/json-script.json new file mode 100644 index 0000000..5328e59 --- /dev/null +++ b/tests/cram/inputs/json-script.json @@ -0,0 +1,38 @@ +[ + [ "exec", "%EXECVAR%", "/%%/" ], + [ "if", + [ "eq", "EQVAR", "eqval" ], + [ "exec_if", "%VAR%", "%%", "jk" ] + ], + [ "case", "CASEVAR", { + "caseval0": ["cmd_case_0", "cmd_case_arg0", "case_cmd_arg1"], + "caseval1": ["cmd_case_1", "cmd_case_arg0", "case_cmd_arg1"] + } ], + + [ "if", + [ "and", [ "eq", "EQVAR", "eqval" ], + [ "has", "HASVAR" ], + [ "regex", "REGEXVAR0", "regexval" ], + [ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ], + [ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ], + [ "exec_if_and", "%ANDVAR%" ] + ], + + [ "if", + [ "or", [ "eq", "EQVAR", "eqval" ], + [ "has", "HASVAR" ], + [ "regex", "REGEXVAR0", "regexval" ], + [ "regex", "REGEXVAR1", [ "regexval10", "regexval11" ] ], + [ "not", [ "eq", "NOTEQVAR", "noteqval" ] ] ], + [ "exec_if_or", "%ORVAR%" ] + ], + + [ "if", + [ "isdir", "%ISDIRVAR%" ], + [ "exec_isdir", "%ISDIRVAR%" ] + ], + + [ "return", "foobar" ], + + [ "exec_non_reachable", "Arghhh" ] +] diff --git a/tests/cram/test_avl.t b/tests/cram/test_avl.t new file mode 100644 index 0000000..19a8d21 --- /dev/null +++ b/tests/cram/test_avl.t @@ -0,0 +1,11 @@ +check that avl is producing expected results: + + $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" + $ valgrind --quiet --leak-check=full test-avl + test_basics: insert: 0=zero 0=one 0=two 0=three 0=four 0=five 0=six 0=seven 0=eight 0=nine 0=ten 0=eleven 0=twelve + test_basics: insert duplicate: -1=zero -1=one -1=two -1=three -1=four -1=five -1=six -1=seven -1=eight -1=nine -1=ten -1=eleven -1=twelve + test_basics: first=eight last=zero + test_basics: for each element: eight eleven five four nine one seven six ten three twelve two zero + test_basics: delete 'one' element + test_basics: for each element reverse: zero two twelve three ten six seven nine four five eleven eight + test_basics: delete all elements diff --git a/tests/cram/test_base64.t b/tests/cram/test_base64.t new file mode 100644 index 0000000..4f8809f --- /dev/null +++ b/tests/cram/test_base64.t @@ -0,0 +1,21 @@ +set test bin path: + + $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" + +check that base64 is producing expected results: + + $ valgrind --quiet --leak-check=full test-b64 + 0 + 4 Zg== + 4 Zm8= + 4 Zm9v + 8 Zm9vYg== + 8 Zm9vYmE= + 8 Zm9vYmFy + 0 + 1 f + 2 fo + 3 foo + 4 foob + 5 fooba + 6 foobar diff --git a/tests/cram/test_blobmsg.t b/tests/cram/test_blobmsg.t new file mode 100644 index 0000000..504a056 --- /dev/null +++ b/tests/cram/test_blobmsg.t @@ -0,0 +1,17 @@ +check that blobmsg is producing expected results: + + $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" + $ valgrind --quiet --leak-check=full test-blobmsg + Message: Hello, world! + List: { + 0 + 1 + 2 + 133.700000 + } + Testdata: { + \tdouble : 133.700000 (esc) + \thello : 1 (esc) + \tworld : 2 (esc) + } + json: {"message":"Hello, world!","testdata":{"double":133.700000,"hello":1,"world":"2"},"list":[0,1,2,133.700000]} diff --git a/tests/cram/test_jshn.t b/tests/cram/test_jshn.t new file mode 100644 index 0000000..d228f0e --- /dev/null +++ b/tests/cram/test_jshn.t @@ -0,0 +1,25 @@ +set jshn for convenience: + + $ [ -n "$JSHN" ] && export PATH="$(dirname "$JSHN"):$PATH" + $ alias jshn="valgrind --quiet --leak-check=full jshn" + +check usage: + + $ jshn + Usage: jshn [-n] [-i] -r |-R |-w + [2] + +test bad json: + + $ jshn -r '[]' + Failed to parse message data + [1] + +test good json: + + $ jshn -r '{"foo": "bar", "baz": {"next": "meep"}}' + json_init; + json_add_string 'foo' 'bar'; + json_add_object 'baz'; + json_add_string 'next' 'meep'; + json_close_object; diff --git a/tests/cram/test_json_script.t b/tests/cram/test_json_script.t new file mode 100644 index 0000000..3e80a5c --- /dev/null +++ b/tests/cram/test_json_script.t @@ -0,0 +1,96 @@ +set test bin path: + + $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" + $ export TEST_INPUTS="$TESTDIR/inputs" + $ alias js="valgrind --quiet --leak-check=full test-json-script" + +check that json-script is producing expected results: + + $ js + Usage: test-json-script [VARNAME=value] + [254] + + $ echo '}' > test.json; js test.json + load JSON data from test.json failed. + + $ js nada.json 2>&1 | grep load.*failed + load JSON data from nada.json failed. + + $ echo '[ [ ] [ ] ]' > test.json; js test.json + load JSON data from test.json failed. + +check example json-script: + + $ js $TEST_INPUTS/json-script.json + exec /%/ + exec_if_or + + $ js EXECVAR=meh ORVAR=meep $TEST_INPUTS/json-script.json + exec meh /%/ + exec_if_or meep + +check has expression: + + $ echo ' + > [ + > [ "if", + > [ "has", "VAR" ], + > [ "echo", "bar" ], + > [ "echo", "baz" ] + > ] + > ]' > test.json + + $ js VAR=foo test.json + echo bar + + $ js VAR=bar test.json + echo bar + + $ js test.json + echo baz + +check eq expression: + + $ echo ' + > [ + > [ "if", + > [ "eq", "VAR", "bar" ], + > [ "echo", "foo" ], + > [ "echo", "baz" ] + > ] + > ]' > test.json + + $ js VAR=bar test.json + echo foo + + $ js VAR=xxx test.json + echo baz + + $ js test.json + echo baz + +check regex single expression: + + $ echo ' + > [ + > [ "if", + > [ "regex", "VAR", ".ell." ], + > [ "echo", "bar" ], + > [ "echo", "baz" ] + > ] + > ]' > test.json + + $ js VAR=hello test.json + echo bar + + $ js VAR=.ell. test.json + echo bar + + $ js test.json + echo baz + + $ js VAR= test.json + echo baz + + $ js VAR=hell test.json + echo baz diff --git a/tests/cram/test_list.t b/tests/cram/test_list.t new file mode 100644 index 0000000..f7f18bd --- /dev/null +++ b/tests/cram/test_list.t @@ -0,0 +1,22 @@ +check that list is producing expected results: + + $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" + $ valgrind --quiet --leak-check=full test-list + test_basics: list_empty: yes + test_basics: list_add_tail: zero one two three four five six seven eight nine ten eleven twelve + test_basics: list_empty: no + test_basics: first=zero last=twelve + test_basics: 'zero' is first, yes + test_basics: 'twelve' is last, yes + test_basics: removing 'twelve' and 'zero' + test_basics: first=one last=eleven + test_basics: 'one' is first, yes + test_basics: 'eleven' is last, yes + test_basics: moving 'one' to the tail + test_basics: first=two last=one + test_basics: 'two' is first, yes + test_basics: 'one' is last, yes + test_basics: list_for_each_entry: two three four five six seven eight nine ten eleven one + test_basics: list_for_each_entry_reverse: one eleven ten nine eight seven six five four three two + test_basics: delete all entries + test_basics: list_empty: yes diff --git a/tests/cram/test_runqueue.t b/tests/cram/test_runqueue.t new file mode 100644 index 0000000..4d49110 --- /dev/null +++ b/tests/cram/test_runqueue.t @@ -0,0 +1,14 @@ +check that runqueue is producing expected results: + + $ [ -n "$TEST_BIN_DIR" ] && export PATH="$TEST_BIN_DIR:$PATH" + $ valgrind --quiet --leak-check=full test-runqueue + [1/1] start 'sleep 1' + [1/1] cancel 'sleep 1' + [0/1] finish 'sleep 1' + [1/1] start 'sleep 1' + [1/1] cancel 'sleep 1' + [0/1] finish 'sleep 1' + [1/1] start 'sleep 1' + [1/1] cancel 'sleep 1' + [0/1] finish 'sleep 1' + All done! diff --git a/tests/test-avl.c b/tests/test-avl.c new file mode 100644 index 0000000..18ee9b7 --- /dev/null +++ b/tests/test-avl.c @@ -0,0 +1,87 @@ +#include +#include +#include + +#include "avl.h" +#include "avl-cmp.h" +#include "utils.h" + +#define OUT(fmt, ...) do { \ + fprintf(stdout, "%s: " fmt, __func__, ## __VA_ARGS__); \ +} while (0); + +struct node { + struct avl_node avl; +}; + +static void test_basics() +{ + size_t i; + struct avl_tree t; + struct node *temp; + struct node *elem; + struct node *last; + struct node *first; + const char *vals[] = { + "zero", "one", "two", "three", "four", "five", "six", + "seven", "eight", "nine", "ten", "eleven", "twelve" + }; + + avl_init(&t, avl_strcmp, false, NULL); + + OUT("insert: "); + for (i=0; iavl.key = vals[i]; + + int r = avl_insert(&t, &n->avl); + fprintf(stdout, "%d=%s ", r, (char *)n->avl.key); + } + fprintf(stdout, "\n"); + + OUT("insert duplicate: "); + for (i=0; iavl.key = vals[i]; + + int r = avl_insert(&t, &n->avl); + fprintf(stdout, "%d=%s ", r, (char *)n->avl.key); + + if (r) + free(n); + } + fprintf(stdout, "\n"); + + first = avl_first_element(&t, first, avl); + last = avl_last_element(&t, last, avl); + OUT("first=%s last=%s\n", (char*)first->avl.key, (char*)last->avl.key); + + OUT("for each element: "); + avl_for_each_element(&t, elem, avl) { + fprintf(stdout, "%s ", (char*)elem->avl.key); + } + fprintf(stdout, "\n"); + + OUT("delete 'one' element\n"); + elem = avl_find_element(&t, "one", elem, avl); + avl_delete(&t, &elem->avl); + free(elem); + + OUT("for each element reverse: "); + avl_for_each_element_reverse(&t, elem, avl) { + fprintf(stdout, "%s ", (char*)elem->avl.key); + } + fprintf(stdout, "\n"); + + OUT("delete all elements\n"); + avl_for_each_element_safe(&t, elem, avl, temp) { + avl_delete(&t, &elem->avl); + free(elem); + } +} + +int main() +{ + test_basics(); + return 0; +} diff --git a/tests/test-b64.c b/tests/test-b64.c new file mode 100644 index 0000000..c29b4e2 --- /dev/null +++ b/tests/test-b64.c @@ -0,0 +1,39 @@ +#include +#include + +#include "utils.h" + +static void test_b64_encode(const char *src) +{ + char dst[255] = {0}; + int r = b64_encode(src, strlen(src), dst, sizeof(dst)); + fprintf(stdout, "%d %s\n", r, dst); +} + +static void test_b64_decode(const char *src) +{ + char dst[255] = {0}; + int r = b64_decode(src, dst, sizeof(dst)); + fprintf(stdout, "%d %s\n", r, dst); +} + +int main() +{ + test_b64_encode(""); + test_b64_encode("f"); + test_b64_encode("fo"); + test_b64_encode("foo"); + test_b64_encode("foob"); + test_b64_encode("fooba"); + test_b64_encode("foobar"); + + test_b64_decode(""); + test_b64_decode("Zg=="); + test_b64_decode("Zm8="); + test_b64_decode("Zm9v"); + test_b64_decode("Zm9vYg=="); + test_b64_decode("Zm9vYmE="); + test_b64_decode("Zm9vYmFy"); + + return 0; +} diff --git a/tests/test-blobmsg.c b/tests/test-blobmsg.c new file mode 100644 index 0000000..5e99dc2 --- /dev/null +++ b/tests/test-blobmsg.c @@ -0,0 +1,152 @@ +#include +#include + +#include "blobmsg.h" +#include "blobmsg_json.h" + +static const char *indent_str = "\t\t\t\t\t\t\t\t\t\t\t\t\t"; + +#define indent_printf(indent, ...) do { \ + if (indent > 0) \ + fwrite(indent_str, indent, 1, stderr); \ + fprintf(stderr, __VA_ARGS__); \ +} while(0) + +static void dump_attr_data(struct blob_attr *data, int indent, int next_indent); + +static void +dump_table(struct blob_attr *head, size_t len, int indent, bool array) +{ + struct blob_attr *attr; + struct blobmsg_hdr *hdr; + + indent_printf(indent, "{\n"); + __blob_for_each_attr(attr, head, len) { + hdr = blob_data(attr); + if (!array) + indent_printf(indent + 1, "%s : ", hdr->name); + dump_attr_data(attr, 0, indent + 1); + } + indent_printf(indent, "}\n"); +} + +static void dump_attr_data(struct blob_attr *data, int indent, int next_indent) +{ + int type = blobmsg_type(data); + switch(type) { + case BLOBMSG_TYPE_STRING: + indent_printf(indent, "%s\n", blobmsg_get_string(data)); + break; + case BLOBMSG_TYPE_INT8: + indent_printf(indent, "%d\n", blobmsg_get_u8(data)); + break; + case BLOBMSG_TYPE_INT16: + indent_printf(indent, "%d\n", blobmsg_get_u16(data)); + break; + case BLOBMSG_TYPE_INT32: + indent_printf(indent, "%d\n", blobmsg_get_u32(data)); + break; + case BLOBMSG_TYPE_INT64: + indent_printf(indent, "%"PRIu64"\n", blobmsg_get_u64(data)); + break; + case BLOBMSG_TYPE_DOUBLE: + indent_printf(indent, "%lf\n", blobmsg_get_double(data)); + break; + case BLOBMSG_TYPE_TABLE: + case BLOBMSG_TYPE_ARRAY: + if (!indent) + indent_printf(indent, "\n"); + dump_table(blobmsg_data(data), blobmsg_data_len(data), + next_indent, type == BLOBMSG_TYPE_ARRAY); + break; + } +} + +enum { + FOO_MESSAGE, + FOO_LIST, + FOO_TESTDATA +}; + +static const struct blobmsg_policy pol[] = { + [FOO_MESSAGE] = { + .name = "message", + .type = BLOBMSG_TYPE_STRING, + }, + [FOO_LIST] = { + .name = "list", + .type = BLOBMSG_TYPE_ARRAY, + }, + [FOO_TESTDATA] = { + .name = "testdata", + .type = BLOBMSG_TYPE_TABLE, + }, +}; + +#ifndef ARRAY_SIZE +#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) +#endif + +static void dump_message(struct blob_buf *buf) +{ + struct blob_attr *tb[ARRAY_SIZE(pol)]; + + if (blobmsg_parse(pol, ARRAY_SIZE(pol), tb, blob_data(buf->head), blob_len(buf->head)) != 0) { + fprintf(stderr, "Parse failed\n"); + return; + } + if (tb[FOO_MESSAGE]) + fprintf(stderr, "Message: %s\n", (char *) blobmsg_data(tb[FOO_MESSAGE])); + + if (tb[FOO_LIST]) { + fprintf(stderr, "List: "); + dump_table(blobmsg_data(tb[FOO_LIST]), blobmsg_data_len(tb[FOO_LIST]), 0, true); + } + if (tb[FOO_TESTDATA]) { + fprintf(stderr, "Testdata: "); + dump_table(blobmsg_data(tb[FOO_TESTDATA]), blobmsg_data_len(tb[FOO_TESTDATA]), 0, false); + } +} + +static void +fill_message(struct blob_buf *buf) +{ + void *tbl; + + blobmsg_add_string(buf, "message", "Hello, world!"); + + tbl = blobmsg_open_table(buf, "testdata"); + blobmsg_add_double(buf, "double", 1.337e2); + blobmsg_add_u32(buf, "hello", 1); + blobmsg_add_string(buf, "world", "2"); + blobmsg_close_table(buf, tbl); + + tbl = blobmsg_open_array(buf, "list"); + blobmsg_add_u32(buf, NULL, 0); + blobmsg_add_u32(buf, NULL, 1); + blobmsg_add_u32(buf, NULL, 2); + blobmsg_add_double(buf, "double", 1.337e2); + blobmsg_close_table(buf, tbl); +} + +int main(int argc, char **argv) +{ + char *json = NULL; + static struct blob_buf buf; + + blobmsg_buf_init(&buf); + fill_message(&buf); + dump_message(&buf); + + json = blobmsg_format_json(buf.head, true); + if (!json) + exit(EXIT_FAILURE); + + fprintf(stderr, "json: %s\n", json); + + if (buf.buf) + free(buf.buf); + free(json); + + return 0; +} diff --git a/tests/test-json-script.c b/tests/test-json-script.c new file mode 100644 index 0000000..6d93059 --- /dev/null +++ b/tests/test-json-script.c @@ -0,0 +1,84 @@ +#include +#include + +#include +#include "blobmsg.h" +#include "blobmsg_json.h" +#include "json_script.h" + +struct json_script_ctx jctx; +struct blob_buf b_vars; +struct blob_buf b_script; + +static void handle_command(struct json_script_ctx *ctx, const char *name, + struct blob_attr *data, struct blob_attr *vars) +{ + struct blob_attr *cur; + size_t rem; + + fprintf(stdout, "%s", name); + blobmsg_for_each_attr(cur, data, rem) + fprintf(stdout, " %s", (char *) blobmsg_data(cur)); + fprintf(stdout, "\n"); +} + +static struct json_script_file * +handle_file(struct json_script_ctx *ctx, const char *filename) +{ + json_object *obj; + + obj = json_object_from_file(filename); + if (!obj) { + fprintf(stderr, "load JSON data from %s failed.\n", filename); + return NULL; + } + + blob_buf_init(&b_script, 0); + blobmsg_add_json_element(&b_script, "", obj); + json_object_put(obj); + + return json_script_file_from_blobmsg(filename, + blob_data(b_script.head), blob_len(b_script.head)); +} + +static void usage(const char *prog, int exit_code) +{ + fprintf(stderr, "Usage: %s [VARNAME=value] \n", prog); + exit(exit_code); +} + +int main(int argc, char *argv[]) +{ + int i; + char *file = NULL; + const char *prog = argv[0]; + + blobmsg_buf_init(&b_vars); + blobmsg_buf_init(&b_script); + + json_script_init(&jctx); + jctx.handle_command = handle_command; + jctx.handle_file = handle_file; + + for (i = 1; i < argc; i++) { + char *sep = strchr(argv[i], '='); + if (sep) { + *sep = '\0'; + blobmsg_add_string(&b_vars, argv[i], sep + 1); + } else if (!file) { + file = argv[i]; + } else { + usage(prog, -1); + } + } + if (i < argc || !file) + usage(prog, -2); + + json_script_run(&jctx, file, b_vars.head); + + json_script_free(&jctx); + blob_buf_free(&b_script); + blob_buf_free(&b_vars); + + return 0; +} diff --git a/tests/test-list.c b/tests/test-list.c new file mode 100644 index 0000000..cb0f231 --- /dev/null +++ b/tests/test-list.c @@ -0,0 +1,91 @@ +#include +#include +#include + +#include "list.h" +#include "utils.h" + +struct item { + const char *name; + struct list_head list; +}; + +#define OUT(fmt, ...) do { \ + fprintf(stdout, "%s: " fmt, __func__, ## __VA_ARGS__); \ +} while (0); + +static void test_basics() +{ + size_t i; + struct item *tmp; + struct item *item; + struct item *last; + struct item *first; + static struct list_head test_list = LIST_HEAD_INIT(test_list); + + const char *vals[] = { + "zero", "one", "two", "three", "four", "five", "six", + "seven", "eight", "nine", "ten", "eleven", "twelve" + }; + + OUT("list_empty: %s\n", list_empty(&test_list) ? "yes" : "no"); + OUT("list_add_tail: "); + for (i=0; iname = vals[i]; + list_add_tail(&e->list, &test_list); + fprintf(stdout, "%s ", vals[i]); + } + fprintf(stdout, "\n"); + OUT("list_empty: %s\n", list_empty(&test_list) ? "yes" : "no"); + + first = list_first_entry(&test_list, struct item, list); + last = list_last_entry(&test_list, struct item, list); + OUT("first=%s last=%s\n", first->name, last->name); + OUT("'zero' is first, %s\n", list_is_first(&first->list, &test_list) ? "yes" : "no"); + OUT("'twelve' is last, %s\n", list_is_last(&last->list, &test_list) ? "yes" : "no"); + + OUT("removing 'twelve' and 'zero'\n"); + list_del(&first->list); + list_del(&last->list); + free(first); + free(last); + first = list_first_entry(&test_list, struct item, list); + last = list_last_entry(&test_list, struct item, list); + OUT("first=%s last=%s\n", first->name, last->name); + OUT("'one' is first, %s\n", list_is_first(&first->list, &test_list) ? "yes" : "no"); + OUT("'eleven' is last, %s\n", list_is_last(&last->list, &test_list) ? "yes" : "no"); + + OUT("moving 'one' to the tail\n"); + list_move_tail(&first->list, &test_list); + first = list_first_entry(&test_list, struct item, list); + last = list_last_entry(&test_list, struct item, list); + OUT("first=%s last=%s\n", first->name, last->name); + OUT("'two' is first, %s\n", list_is_first(&first->list, &test_list) ? "yes" : "no"); + OUT("'one' is last, %s\n", list_is_last(&last->list, &test_list) ? "yes" : "no"); + + OUT("list_for_each_entry: "); + list_for_each_entry(item, &test_list, list) { + fprintf(stdout, "%s ", item->name); + } + fprintf(stdout, "\n"); + + OUT("list_for_each_entry_reverse: "); + list_for_each_entry_reverse(item, &test_list, list) { + fprintf(stdout, "%s ", item->name); + } + fprintf(stdout, "\n"); + + OUT("delete all entries\n"); + list_for_each_entry_safe(item, tmp, &test_list, list) { + list_del(&item->list); + free(item); + } + OUT("list_empty: %s\n", list_empty(&test_list) ? "yes" : "no"); +} + +int main() +{ + test_basics(); + return 0; +} diff --git a/tests/test-runqueue.c b/tests/test-runqueue.c new file mode 100644 index 0000000..13ab864 --- /dev/null +++ b/tests/test-runqueue.c @@ -0,0 +1,112 @@ +/* + * runqueue-example.c + * + * Copyright (C) 2013 Felix Fietkau + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include +#include + +#include "uloop.h" +#include "runqueue.h" + +static struct runqueue q; + +struct sleeper { + struct runqueue_process proc; + int val; +}; + +static void q_empty(struct runqueue *q) +{ + fprintf(stderr, "All done!\n"); + uloop_end(); +} + +static void q_sleep_run(struct runqueue *q, struct runqueue_task *t) +{ + struct sleeper *s = container_of(t, struct sleeper, proc.task); + char str[32]; + pid_t pid; + + fprintf(stderr, "[%d/%d] start 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); + + pid = fork(); + if (pid < 0) + return; + + if (pid) { + runqueue_process_add(q, &s->proc, pid); + return; + } + + sprintf(str, "%d", s->val); + execlp("sleep", "sleep", str, NULL); + exit(1); +} + +static void q_sleep_cancel(struct runqueue *q, struct runqueue_task *t, int type) +{ + struct sleeper *s = container_of(t, struct sleeper, proc.task); + + fprintf(stderr, "[%d/%d] cancel 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); + runqueue_process_cancel_cb(q, t, type); +} + +static void q_sleep_complete(struct runqueue *q, struct runqueue_task *p) +{ + struct sleeper *s = container_of(p, struct sleeper, proc.task); + + fprintf(stderr, "[%d/%d] finish 'sleep %d'\n", q->running_tasks, q->max_running_tasks, s->val); + free(s); +} + +static void add_sleeper(int val) +{ + static const struct runqueue_task_type sleeper_type = { + .run = q_sleep_run, + .cancel = q_sleep_cancel, + .kill = runqueue_process_kill_cb, + }; + struct sleeper *s; + + s = calloc(1, sizeof(*s)); + s->proc.task.type = &sleeper_type; + s->proc.task.run_timeout = 500; + s->proc.task.complete = q_sleep_complete; + s->val = val; + runqueue_task_add(&q, &s->proc.task, false); +} + +int main(int argc, char **argv) +{ + uloop_init(); + + runqueue_init(&q); + q.empty_cb = q_empty; + q.max_running_tasks = 1; + + if (argc > 1) + q.max_running_tasks = atoi(argv[1]); + + add_sleeper(1); + add_sleeper(1); + add_sleeper(1); + uloop_run(); + uloop_done(); + + return 0; +} -- cgit v1.2.3