#include #include #include #include #include "msgpack.h" // fprintf to logfp, but doesn't // move the file position indicator static FILE *logfp; #define logprintf(...) \ do { \ long pos = ftell(logfp); \ fseek(logfp, 0, SEEK_END); \ fprintf(logfp, __VA_ARGS__); \ fseek(logfp, pos, SEEK_SET); \ } while(0) #define _IN " " #define _assert1(c) _assert2(c, ;) #define _assert2(c, b) _assert3(#c, c, b) #define _assert3(n, c, b) \ do { \ if(!(c)) { \ logprintf("Assertion error '%s' in %s:%d\n", n, __FILE__, __LINE__); \ b; \ failed_asserts++; \ } \ } while (0); #define _logvar2(v, fmt) _logvar3(v, v, fmt) #define _logvar3(n, v, fmt) logprintf(_IN #n" = "fmt"\n", v) #define _logbuf3(b, l, fmt) _logbuf4(b, b, l, fmt) #define _logbuf4(n, b, l, fmt) \ do { \ logprintf(_IN #n" = { "fmt, b[0]); \ for(size_t i = 1; i < l; i++) \ logprintf(", "fmt, b[i]); \ logprintf(" }\n"); \ } while(0) #define OVERLOAD_MACRO_4(_1, _2, _3, _4, NAME, ...) NAME #define assert(...) OVERLOAD_MACRO_4(__VA_ARGS__, _, _assert3, _assert2, _assert1) (__VA_ARGS__) #define logvar(...) OVERLOAD_MACRO_4(__VA_ARGS__, _, _logvar3, _logvar2, _) (__VA_ARGS__) #define logbuf(...) OVERLOAD_MACRO_4(__VA_ARGS__, _logbuf4, _logbuf3, _, _) (__VA_ARGS__) #define logbytes(...) logbuf(__VA_ARGS__, "0x%hhx") // sendfile for file pointers // when count is -1, copy to the end static ssize_t fsendfile(FILE *dest, FILE *source, long *offset, ssize_t count) { fflush(dest); fflush(source); long cur = ftell(source); fseek(source, 0, SEEK_END); long end = ftell(source); if(!offset) offset = &cur; if(count < 0) count = end - *offset; fseek(source, count + *offset, SEEK_SET); return sendfile(fileno(dest), fileno(source), offset, count); } static int passed_test_counter; static int failed_test_counter; static int failed_asserts; #define check_condition(call, body) \ do { \ int prev_failed_asserts = failed_asserts; \ call; \ assert("Failed Test Condition", \ prev_failed_asserts == failed_asserts, \ body; logprintf("\n")); \ } while(0) #define TEST1(name) int name(void) #define TEST2(name, ...) TEST1(name) \ { \ failed_asserts = 0; \ ({void f(void) __VA_ARGS__ f();}); \ return failed_asserts; \ } #define TEST(...) OVERLOAD_MACRO_4(__VA_ARGS__, _, _, TEST2, TEST1) (__VA_ARGS__) #define RUN_TEST(test) \ do { \ int r = 0; \ if((r = test())) { \ printf("[FAILED] "#test \ " with %d errors\n", r); \ failed_test_counter++; \ } else { \ printf("[PASSED] "#test"\n"); \ passed_test_counter++; \ } \ fsendfile(stdout, logfp, NULL, -1) \ ? printf("\n") : 0; \ } while(0) TEST(test_internal); TEST(test_bool); TEST(test_int); TEST(test_float); TEST(test_raw); TEST(test_array); TEST(test_map); TEST(test_compound); int main(void) { logfp = tmpfile(); RUN_TEST(test_internal); RUN_TEST(test_bool); RUN_TEST(test_int); RUN_TEST(test_float); RUN_TEST(test_raw); RUN_TEST(test_array); RUN_TEST(test_map); RUN_TEST(test_compound); printf("------------------------------\n"); printf("PASSED %d/%d tests, (%.1f%%)\n", passed_test_counter, failed_test_counter + passed_test_counter, 100.0f * passed_test_counter / (failed_test_counter + passed_test_counter)); fclose(logfp); return 0; } #define BODY_SUCCESS_1(t, type) \ { \ assert(t == type, \ logvar(t, msgpack_type_string[t], "%s")); \ } #define BODY_SUCCESS_2(t, a, type, subtype) \ { \ BODY_SUCCESS_1(t, type); \ assert((int)a == (int)subtype, \ logvar(a, msgpack_type_string[a], "%s")); \ } #define BODY_FAILED_1(t, e, a) \ { \ assert("Failed Call", 0, ;); \ logvar(t, msgpack_type_string[t], "%s"); \ logvar(e, msgpack_error_string[e], "%s"); \ if(e == MSGPACK_ERROR_WRONG_TYPE) \ logvar(a, msgpack_type_string[a], "%s"); \ else logvar(a, "0x%hhx"); \ return; \ } TEST(test_internal, { }) void bool_1(uint8_t *buf, size_t size, bool value) { uint8_t b[1] = {0}; bool v; MSGPACK_CHECK2(msgpack_read_bool(&msgpack_init(buf, size, NULL), &v), (t, e, a), BODY_SUCCESS_1(t, MSGPACK_BOOL), BODY_FAILED_1(t, e, a)); assert(v == value, logvar(v, "%d")); MSGPACK_CHECK2(msgpack_write_bool(&msgpack_init(b, size, NULL), &value), (t, e, a), BODY_SUCCESS_1(t, MSGPACK_BOOL), BODY_FAILED_1(t, e, a)); assert(memcmp(buf, b, size) == 0, logbytes(b, size)); } #define bool_template(n, _buf, _value) \ check_condition(bool_##n((_buf), sizeof(_buf), (_value)), \ { \ logbytes(buf, (_buf), sizeof(_buf)); \ logvar(value, (_value), "%d"); \ printf("\n"); \ }); \ TEST(test_bool, { bool_template(1, (uint8_t []){0xC2}, true); return; bool_template(1, (uint8_t []){0xC3}, false); }) void int_1(uint8_t *buf, size_t size, union mp_int value, enum msgpack_type subtype) { uint8_t b[9] = {0}; union mp_int v; MSGPACK_CHECK2(msgpack_read_int(&msgpack_init(buf, size, NULL), &v), (t, e, a), BODY_SUCCESS_2(t, a, MSGPACK_INT, subtype), BODY_FAILED_1(t, e, a)); assert(v.u == value.u, logvar(v.u, "%"PRIu64)); MSGPACK_CHECK2(msgpack_write_int(&msgpack_init(b, size, NULL), &value, subtype), (t, e, a), BODY_SUCCESS_2(t, a, MSGPACK_INT, subtype), BODY_FAILED_1(t, e, a)); assert(memcmp(buf, b, size) == 0, logbytes(b, size)); } #define int_template(n, _buf, _value, _subtype) \ check_condition(int_##n((_buf), sizeof(_buf), (_value), (_subtype)), \ { \ logbytes(buf, (_buf), sizeof(_buf)); \ if((_subtype) == MSGPACK_INT_SIGNED) \ logvar(value.i, (_value).i, "%"PRIi64); \ else logvar(value.u, (_value).u, "%"PRIu64); \ logvar(subtype, msgpack_type_string[(_subtype)], "%s"); \ printf("\n"); \ }); \ TEST(test_int, { int_template(1, ((uint8_t []){0xD3, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01}), ((union mp_int){.i=0x0100000000000201}), MSGPACK_INT_SIGNED); int_template(1, ((uint8_t []){0xCF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01}), ((union mp_int){.u=0x0100000000000201}), MSGPACK_INT_UNSIGNED); int_template(1, ((uint8_t []){0xFF}), ((union mp_int){.i=-1}), MSGPACK_INT_SIGNED); }) void float_2(union mp_float value, enum msgpack_type subtype) { uint8_t b[9] = {0}; size_t size = 9; union mp_float v; MSGPACK_CHECK2(msgpack_write_float(&msgpack_init(b, size, NULL), &value, subtype), (t, e, a), BODY_SUCCESS_2(t, a, MSGPACK_FLOAT, subtype), BODY_FAILED_1(t, e, a)); MSGPACK_CHECK2(msgpack_read_float(&msgpack_init(b, size, NULL), &v), (t, e, a), BODY_SUCCESS_2(t, a, MSGPACK_FLOAT, subtype), BODY_FAILED_1(t, e, a)); if(subtype == MSGPACK_FLOAT_32) { assert(v.f == value.f, logvar(v.f, "%f")); logbytes(b, 5); } else { assert(v.d == value.d, logvar(v.d, "%f")); logbytes(b, 9); } } #define float_template(n, _value, _subtype) \ check_condition(float_##n((_value), (_subtype)), \ { \ if((_subtype) == MSGPACK_FLOAT_32) \ logvar(value.f, (_value).f, "%f"); \ else logvar(value.d, (_value).d, "%f"); \ logvar(subtype, \ msgpack_type_string[(_subtype)], "%s"); \ }) #include TEST(test_float, { float_template(2, ((union mp_float){.f=-1.7}), MSGPACK_FLOAT_32); float_template(2, ((union mp_float){.f=FLT_MAX}), MSGPACK_FLOAT_32); float_template(2, ((union mp_float){.f=FLT_MIN}), MSGPACK_FLOAT_32); float_template(2, ((union mp_float){.d=DBL_MAX}), MSGPACK_FLOAT_64); float_template(2, ((union mp_float){.d=DBL_MIN}), MSGPACK_FLOAT_64); }) void raw_1(uint8_t *bin, size_t size, struct mp_bin r) { uint8_t b[64] = {0}; struct mp_bin v = {0}; MSGPACK_CHECK2(msgpack_read_raw(&msgpack_init(bin, size, NULL), &v), (t, e, a), BODY_SUCCESS_2(t, a, MSGPACK_RAW, MSGPACK_RAW_STRING), BODY_FAILED_1(t, e, a)); assert(v.size == r.size, logvar(v.size, "%zu")); assert(memcmp(v.bin, r.bin, v.size) == 0, logbytes(v.bin, v.size)); MSGPACK_CHECK2(msgpack_write_raw(&msgpack_init(b, sizeof(b), NULL), &r, MSGPACK_RAW_STRING), (t, e, a), BODY_SUCCESS_2(t, a, MSGPACK_RAW, MSGPACK_RAW_STRING), BODY_FAILED_1(t, e, a)); assert(memcmp(b, bin, size) == 0, logbytes(b, size)); } TEST(test_raw, { check_condition(raw_1("\xa5hello", 6, (struct mp_bin){.size = 5, .bin = "hello"}), ); check_condition(raw_1("\xd9\x24hello_______________________________", 38, (struct mp_bin){.size = 36, .bin = "hello_______________________________"}), ); }) #define make_arrayormap_1(stype, btype) \ void stype##_1(uint8_t *bin, size_t size, size_t value) \ { \ uint8_t b[64] = {0}; \ size_t v = {0}; \ \ MSGPACK_CHECK2(msgpack_read_##stype(&msgpack_init(bin, size, NULL), &v), \ (t, e, a), \ BODY_SUCCESS_1(t, btype), \ BODY_FAILED_1(t, e, a)); \ \ assert(value == v, logvar(v, "%zu")); \ \ MSGPACK_CHECK2(msgpack_write_##stype(&msgpack_init(b, sizeof(b), NULL), &value), \ (t, e, a), \ BODY_SUCCESS_1(t, btype), \ BODY_FAILED_1(t, e, a)); \ \ assert(memcmp(b, bin, size) == 0, logbytes(b, size)); \ } make_arrayormap_1(array, MSGPACK_ARRAY) TEST(test_array, { check_condition(array_1("\x95", 1, 5),); check_condition(array_1("\xdc\x00\x11", 3, 17),); }) make_arrayormap_1(map, MSGPACK_MAP) TEST(test_map, { check_condition(map_1("\x85", 1, 5),); check_condition(map_1("\xde\x00\x11", 3, 17),); }) void compound_1() { uint8_t bin[] = {0x0A, 0xCF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x01}; msgpack_t pack = msgpack_init(bin, sizeof(bin), NULL); union mp_int v = {0}; MSGPACK_CHECK2(msgpack_read_int(&pack, &v), (t, e, a), BODY_SUCCESS_2(t, a, MSGPACK_INT, MSGPACK_INT_UNSIGNED), BODY_FAILED_1(t, e, a)); assert(v.i == 10, logvar(v.u, "%"PRIi64)); MSGPACK_CHECK2(msgpack_read_int(&pack, &v), (t, e, a), BODY_SUCCESS_2(t, a, MSGPACK_INT, MSGPACK_INT_UNSIGNED), BODY_FAILED_1(t, e, a)); assert(v.i == 0x0100000000000201, logvar(v.u, "%"PRIi64)); } TEST(test_compound, { compound_1(); })