From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from [87.239.111.99] (localhost [127.0.0.1]) by dev.tarantool.org (Postfix) with ESMTP id AF0BF4A0A18; Mon, 5 Jun 2023 13:46:29 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org AF0BF4A0A18 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=tarantool.org; s=dev; t=1685961989; bh=w/4YEIhqhsTXkXgPUmn3A8eJkrCVhWXCm8N3G2NmknA=; h=To:Date:In-Reply-To:References:Subject:List-Id:List-Unsubscribe: List-Archive:List-Post:List-Help:List-Subscribe:From:Reply-To:Cc: From; b=vSKjN7bmY0v1rOOXi/wWs6IuEQlRqDdtuCuYuJ30z8qQCaSBbgITadTIcUtO32wfV Z3RaPJt1rbmNr3QC0PUoTsW0RWnvv3lAQR7LVvPVxcEEmyNvzq87nFSTSm1MpVn4VW XixqZcYp6SROtNlRuWk4ntzH8vA2ckMT6pEIMR7E= Received: from smtp61.i.mail.ru (smtp61.i.mail.ru [95.163.41.99]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by dev.tarantool.org (Postfix) with ESMTPS id 583BE4A0A1B for ; Mon, 5 Jun 2023 13:45:30 +0300 (MSK) DKIM-Filter: OpenDKIM Filter v2.11.0 dev.tarantool.org 583BE4A0A1B Received: by smtp61.i.mail.ru with esmtpa (envelope-from ) id 1q67iK-00BFih-SB; Mon, 05 Jun 2023 13:45:29 +0300 To: Igor Munkin , Maxim Kokryashkin , Sergey Bronnikov Date: Mon, 5 Jun 2023 13:41:13 +0300 Message-Id: <97194cc5150bc632c5fd566c9f34033413af1518.1685613304.git.skaplun@tarantool.org> X-Mailer: git-send-email 2.34.1 In-Reply-To: References: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Mailru-Src: smtp X-4EC0790: 10 X-7564579A: 78E4E2B564C1792B X-77F55803: 4F1203BC0FB41BD988F25420CCA9469A00ED12DDEDD8E56A1B2FC461420D33EE182A05F5380850405B055E3674AC86FF109EAA918A7060CCEF6B352386E49928AA15DDBAB0C0D2BA X-7FA49CB5: FF5795518A3D127A4AD6D5ED66289B5278DA827A17800CE7E50EC9128971FD6EEA1F7E6F0F101C67BD4B6F7A4D31EC0BCC500DACC3FED6E28638F802B75D45FF8AA50765F79006374CC1EE7F84E0F1BE8638F802B75D45FF36EB9D2243A4F8B5A6FCA7DBDB1FC311F39EFFDF887939037866D6147AF826D81BFE282636A89160989AE305F21428A7117882F44604297287769387670735204B6963042765DA4B2CC0D3CB04F14752D2E47CDBA5A96583BA9C0B312567BB2376E601842F6C81A19E625A9149C048EECCD848CCB6FE560C97A342136F30543AD8FC6C240DEA7642DBF02ECDB25306B2B78CF848AE20165D0A6AB1C7CE11FEE32D01283D1ACF37BAC0837EA9F3D19764C4224003CC836476E2F48590F00D11D6E2021AF6380DFAD1A18204E546F3947C1D471462564A2E192E808ACE2090B5E1725E5C173C3A84C3C5EA940A35A165FF2DBA43225CD8A89F83C798A30B85E16B156CCFE7AF13BCA4B5C8C57E37DE458BEDA766A37F9254B7 X-C1DE0DAB: 0D63561A33F958A54910507BD1E1EC85290AA7B82D7AB259A682C74F777B0925F87CCE6106E1FC07E67D4AC08A07B9B0735DFC8FA7AC12079C5DF10A05D560A950611B66E3DA6D700B0A020F03D25A0997E3FB2386030E77 X-C8649E89: 1C3962B70DF3F0ADE00A9FD3E00BEEDF3FED46C3ACD6F73ED3581295AF09D3DF87807E0823442EA2ED31085941D9CD0AF7F820E7B07EA4CF1B46158D6247088725515E6BCB786377B5DDAB5C0A83AB319F34824BFA7EC7DCDDCE95BB4BF576A6588A46A5EBCE8E375193600175DBE76C9CE3E2D9894C6EAF461A413F07889F2102C26D483E81D6BE5EF9655DD6DEA7D65774BB76CC95456EEC5B5AD62611EEC62B5AFB4261A09AF0 X-D57D3AED: 3ZO7eAau8CL7WIMRKs4sN3D3tLDjz0dLbV79QFUyzQ2Ujvy7cMT6pYYqY16iZVKkSc3dCLJ7zSJH7+u4VD18S7Vl4ZUrpaVfd2+vE6kuoey4m4VkSEu530nj6fImhcD4MUrOEAnl0W826KZ9Q+tr5ycPtXkTV4k65bRjmOUUP8cvGozZ33TWg5HZplvhhXbhDGzqmQDTd6OAevLeAnq3Ra9uf7zvY2zzsIhlcp/Y7m53TZgf2aB4JOg4gkr2biojbL9S8ysBdXhLPelb+eUiUFAas4paNdkM X-Mailru-Sender: 11C2EC085EDE56FAC07928AF2646A769A362B55ADB54669F109EAA918A7060CC84876E3DC5E244DFDEDBA653FF35249392D99EB8CC7091A70E183A470755BFD208F19895AA18418972D6B4FCE48DF648AE208404248635DF X-Mras: Ok Subject: [Tarantool-patches] [PATCH v3 luajit 2/6] test: introduce module for C tests X-BeenThere: tarantool-patches@dev.tarantool.org X-Mailman-Version: 2.1.34 Precedence: list List-Id: Tarantool development patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , From: Sergey Kaplun via Tarantool-patches Reply-To: Sergey Kaplun Cc: tarantool-patches@dev.tarantool.org Errors-To: tarantool-patches-bounces@dev.tarantool.org Sender: "Tarantool-patches" We need an instrument to write tests in plain C for LuaJIT, to be able: * easily test LuaC API * test patches without usage of plain Lua * write unit tests * startup LuaJIT with custom memory allocator, to test some GC issues * maybe, in future, use custom hashing function to test a behavior of LuaJIT tables and so on. The module serves to achieve these goals without too fancy features. It's functionality inspired by CMocka API [1], but only TAP14 [2] protocol is supported (Version of TAP set to 13 to be compatible with old TAP13 harnesses). The group of unit tests is declared like the following: | void *t_state = NULL; | const struct test_unit tgroup[] = { | test_unit_new(test_base), | test_unit_new(test_subtest), | }; | return test_run_group(tgroup, t_state); `test_run_group()` runs the whole group of tests, returns `TEST_EXIT_SUCCESS` or `TEST_EXIT_FAILURE`. If a similar group is declared inside unit test, this group will be considered as a subtest. This library provides an API similar to glibc (3) `assert()` to use inside unit tests. `assert_[true,false]()` are useful for condition checks and `assert_{type}_[not_,]_equal()` are useful for value comparisons. If some assertion fails diagnostic is set, all test considered as failing and finished via `longjmp()`, so these assertions can be used inside custom subroutines. Also, this module provides ability to skip one test or all tests, mark test as todo, bail out all tests. Just use `return skip()`, `skip_all()` or `todo()` for early return. They should be used only in the test body to make skipping clear. `skip_all()` may be used both for the parent test and for a subtest. `bail_out()` prints an error message and exits the process. As a part of this commit, tarantool-c-tests directory is created with the corresponding CMakeLists.txt file to build this test library. Tests to be rewritten in C with this library in the next commit and placed as unit tests are: * lj-49-bad-lightuserdata.test.lua * misclib-getmetrics-capi.test.lua * misclib-sysprof-capi.test.lua For now the tarantool-c-tests target just build the test library without new tests to run. The library itself is tested via some primitive tests for `ok` case, `skip` and `todo` directives. The TAP13 format is tested via prove, that we are using for running our tests. TAP14 format is compatible with TAP13, so there are no other tests required. Also, .c_test suffix is added to the <.gitignore>. [1]: https://github.com/clibs/cmocka [2]: https://testanything.org/tap-version-14-specification.html Part of tarantool/tarantool#7900 --- .gitignore | 1 + test/CMakeLists.txt | 2 + test/tarantool-c-tests/CMakeLists.txt | 66 +++++++ test/tarantool-c-tests/test.c | 251 +++++++++++++++++++++++++ test/tarantool-c-tests/test.h | 217 +++++++++++++++++++++ test/tarantool-c-tests/unit-tap.test.c | 31 +++ 6 files changed, 568 insertions(+) create mode 100644 test/tarantool-c-tests/CMakeLists.txt create mode 100644 test/tarantool-c-tests/test.c create mode 100644 test/tarantool-c-tests/test.h create mode 100644 test/tarantool-c-tests/unit-tap.test.c diff --git a/.gitignore b/.gitignore index b7908aee..dc5ea5fc 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ install_manifest.txt luajit-parse-memprof luajit-parse-sysprof luajit.pc +*.c_test diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a8262b12..47296a22 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -48,12 +48,14 @@ separate_arguments(LUAJIT_TEST_COMMAND) add_subdirectory(LuaJIT-tests) add_subdirectory(PUC-Rio-Lua-5.1-tests) add_subdirectory(lua-Harness-tests) +add_subdirectory(tarantool-c-tests) add_subdirectory(tarantool-tests) add_custom_target(${PROJECT_NAME}-test DEPENDS LuaJIT-tests PUC-Rio-Lua-5.1-tests lua-Harness-tests + tarantool-c-tests tarantool-tests ) diff --git a/test/tarantool-c-tests/CMakeLists.txt b/test/tarantool-c-tests/CMakeLists.txt new file mode 100644 index 00000000..da128457 --- /dev/null +++ b/test/tarantool-c-tests/CMakeLists.txt @@ -0,0 +1,66 @@ +find_program(PROVE prove) +if(NOT PROVE) + message(WARNING "`prove' is not found, so tarantool-c-tests target is not generated") + return() +endif() + +set(C_TEST_SUFFIX .c_test) +set(C_TEST_FLAGS --failures --shuffle) + +if(CMAKE_VERBOSE_MAKEFILE) + list(APPEND C_TEST_FLAGS --verbose) +endif() + +# Build libtest. + +set(TEST_LIB_NAME "test") +add_library(libtest STATIC EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/test.c) +target_include_directories(libtest PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +set_target_properties(libtest PROPERTIES + COMPILE_FLAGS "-Wall -Wextra" + OUTPUT_NAME "${TEST_LIB_NAME}" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" +) + +# TARGET_C_FLAGS is required here to be sure that headers like +# lj_arch.h in compiled test are consistent with the LuaJIT library +# to link. +AppendFlags(TESTS_C_FLAGS ${TARGET_C_FLAGS}) + +set(CTEST_SRC_SUFFIX ".test.c") +file(GLOB tests "${CMAKE_CURRENT_SOURCE_DIR}/*${CTEST_SRC_SUFFIX}") +foreach(test_source ${tests}) + # Get test name without suffix. Needed to set OUTPUT_NAME. + get_filename_component(exe ${test_source} NAME_WE) + add_executable(${exe} EXCLUDE_FROM_ALL ${test_source}) + target_include_directories(${exe} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${LUAJIT_SOURCE_DIR} + ) + set_target_properties(${exe} PROPERTIES + COMPILE_FLAGS "${TESTS_C_FLAGS}" + OUTPUT_NAME "${exe}${C_TEST_SUFFIX}" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}" + ) + target_link_libraries(${exe} libtest ${LUAJIT_LIBRARY}) + LIST(APPEND TESTS_COMPILED ${exe}) +endforeach() + +add_custom_target(tarantool-c-tests + DEPENDS libluajit libtest ${TESTS_COMPILED} +) + +add_custom_command(TARGET tarantool-c-tests + COMMENT "Running Tarantool C tests" + COMMAND + ${PROVE} + ${CMAKE_CURRENT_BINARY_DIR} + --ext ${C_TEST_SUFFIX} + --jobs ${CMAKE_BUILD_PARALLEL_LEVEL} + # Report any TAP parse errors, if any, since test module is + # maintained by us. + --parse + ${C_TEST_FLAGS} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +) + diff --git a/test/tarantool-c-tests/test.c b/test/tarantool-c-tests/test.c new file mode 100644 index 00000000..74cba3a3 --- /dev/null +++ b/test/tarantool-c-tests/test.c @@ -0,0 +1,251 @@ +#include "test.h" + +/* + * Test module, based on TAP 14 specification [1]. + * [1]: https://testanything.org/tap-version-14-specification.html + */ + +/* Need for `PATH_MAX` in diagnostic definition. */ +#include +#include +#include +/* Need for `strchr()` in diagnostic parsing. */ +#include + +/* + * Test level: 0 for the parent test, >0 for any subtests. + */ +static int level = -1; + +/* + * The last diagnostic data to be used in the YAML Diagnostic + * block. + * + * Contains filename, line number and failed expression or assert + * name and "got" and "expected" fields. All entries are separated + * by \n. + * The longest field is filename here, so PATH_MAX * 3 as + * the diagnostic string length should be enough. + * + * The first \0 means the end of diagnostic data. + * + * As far as `strchr()` searches until \0, all previous entries + * are suppressed by the last one. If the first byte is \0 -- + * diagnostic is empty. + */ +#define TEST_DIAG_DATA_MAX (PATH_MAX * 3) +char test_diag_buf[TEST_DIAG_DATA_MAX] = {0}; + +const char *skip_reason = NULL; +const char *todo_reason = NULL; + +/* Indent for the TAP. 4 spaces is default for subtest. */ +static void indent(void) +{ + int i; + for (i = 0; i < level; i++) + printf(" "); +} + +void test_message(const char *fmt, ...) +{ + va_list ap; + indent(); + va_start(ap, fmt); + vprintf(fmt, ap); + printf("\n"); + va_end(ap); +} + +static void test_print_tap_version(void) +{ + /* + * Since several TAP13 parsers in popular usage treat + * a repeated Version declaration as an error, even if the + * Version is indented, Subtests _should not_ include a + * Version, if TAP13 Harness compatibility is + * desirable [1]. + */ + if (level == 0) + test_message("TAP version %d", TAP_VERSION); +} + +static void test_start_comment(const char *t_name) +{ + if (level > -1) + /* + * Inform about starting subtest, easier for + * humans to read. + * Subtest with a name must be terminated by a + * Test Point with a matching Description [1]. + */ + test_comment("Subtest: %s", t_name); +} + +void _test_print_skip_all(const char *group_name, const char *reason) +{ + test_start_comment(group_name); + /* + * XXX: This test isn't started yet, so set indent level + * manually. + */ + level++; + test_print_tap_version(); + /* + * XXX: `SKIP_DIRECTIVE` is not necessary here according + * to the TAP14 specification [1], but some harnesses may + * fail to parse the output without it. + */ + test_message("1..0" SKIP_DIRECTIVE "%s", reason); + level--; +} + +/* Just inform TAP parser how many tests we want to run. */ +static void test_plan(size_t planned) +{ + test_message("1..%lu", planned); +} + +/* Human-readable output how many tests/subtests are failed. */ +static void test_finish(size_t planned, size_t failed) +{ + const char *t_type = level == 0 ? "tests" : "subtests"; + if (failed > 0) + test_comment("Failed %lu %s out of %lu", + failed, t_type, planned); + fflush(stdout); +} + +void test_set_skip_reason(const char *reason) +{ + skip_reason = reason; +} + +void test_set_todo_reason(const char *reason) +{ + todo_reason = reason; +} + +void test_save_diag_data(const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + vsnprintf(test_diag_buf, TEST_DIAG_DATA_MAX, fmt, ap); + va_end(ap); +} + +static void test_clear_diag_data(void) +{ + /* + * Limit buffer with zero byte to show that there is no + * any entry. + */ + test_diag_buf[0] = '\0'; +} + +static int test_diagnostic_is_set(void) +{ + return test_diag_buf[0] != '\0'; +} + +/* + * Parse the last diagnostic data entry and print it in YAML + * format with the corresponding additional half-indent in TAP + * (2 spaces). + * Clear diagnostic message to be sure that it's printed once. + * XXX: \n separators are changed to \0 during parsing and + * printing output for convenience in usage. + */ +static void test_diagnostic(void) +{ + test_message(" ---"); + char *ent = test_diag_buf; + char *ent_end = NULL; + while ((ent_end = strchr(ent, '\n')) != NULL) { + char *next_ent = ent_end + 1; + /* + * Limit string with with the zero byte for + * formatted output. Anyway, don't need this \n + * anymore. + */ + *ent_end = '\0'; + test_message(" %s", ent); + ent = next_ent; + } + test_message(" ..."); + test_clear_diag_data(); +} + +static jmp_buf test_run_env; + +TEST_NORET void _test_exit(int status) +{ + longjmp(test_run_env, status); +} + +static int test_run(const struct test_unit *test, size_t test_number, + void *test_state) +{ + int status = TEST_EXIT_SUCCESS; + /* + * Run unit test. Diagnostic in case of failure setup by + * helpers assert macros defined in the header. + */ + int jmp_status; + if ((jmp_status = setjmp(test_run_env)) == 0) { + if (test->f(test_state) != TEST_EXIT_SUCCESS) + status = TEST_EXIT_FAILURE; + } else { + status = jmp_status - TEST_JMP_STATUS_SHIFT; + } + const char *result = status == TEST_EXIT_SUCCESS ? "ok" : "not ok"; + + /* + * Format suffix of the test message for SKIP or TODO + * directives. + */ +#define SUFFIX_SZ 1024 + char suffix[SUFFIX_SZ] = {0}; + if (skip_reason) { + snprintf(suffix, SUFFIX_SZ, SKIP_DIRECTIVE "%s", skip_reason); + skip_reason = NULL; + } else if (todo_reason) { + /* Prevent count this test as failed. */ + status = TEST_EXIT_SUCCESS; + snprintf(suffix, SUFFIX_SZ, TODO_DIRECTIVE "%s", todo_reason); + todo_reason = NULL; + } +#undef SUFFIX_SZ + + test_message("%s %lu - %s%s", result, test_number, test->name, + suffix); + + if (status && test_diagnostic_is_set()) + test_diagnostic(); + return status; +} + +int _test_run_group(const char *group_name, const struct test_unit tests[], + size_t n_tests, void *test_state) +{ + test_start_comment(group_name); + + level++; + test_print_tap_version(); + + test_plan(n_tests); + + size_t n_failed = 0; + + size_t i; + for (i = 0; i < n_tests; i++) { + size_t test_number = i + 1; + /* Return 1 on failure, 0 on success. */ + n_failed += test_run(&tests[i], test_number, test_state); + } + + test_finish(n_tests, n_failed); + + level--; + return n_failed > 0 ? TEST_EXIT_FAILURE : TEST_EXIT_SUCCESS; +} diff --git a/test/tarantool-c-tests/test.h b/test/tarantool-c-tests/test.h new file mode 100644 index 00000000..047f01a5 --- /dev/null +++ b/test/tarantool-c-tests/test.h @@ -0,0 +1,217 @@ +#ifndef TARANTOOL_LUAJIT_TEST_H +#define TARANTOOL_LUAJIT_TEST_H + +#include +#include + +/* + * Test module, based on TAP 14 specification [1]. + * [1]: https://testanything.org/tap-version-14-specification.html + * Version 13 is set for better compatibility on old machines. + * + * TODO: + * * Helpers assert macros: + * - assert_uint_equal if needed + * - assert_uint_not_equal if needed + * - assert_str_equal if needed + * - assert_str_not_equal if needed + * - assert_memory_equal if needed + * - assert_memory_not_equal if needed + * * Pragmas. + */ + +#define TAP_VERSION 13 + +#define TEST_EXIT_SUCCESS 0 +#define TEST_EXIT_FAILURE 1 + +#define TEST_JMP_STATUS_SHIFT 2 +#define TEST_LJMP_EXIT_SUCCESS (TEST_EXIT_SUCCESS + TEST_JMP_STATUS_SHIFT) +#define TEST_LJMP_EXIT_FAILURE (TEST_EXIT_FAILURE + TEST_JMP_STATUS_SHIFT) + +#define TEST_NORET __attribute__((noreturn)) + +typedef int (*test_func)(void *test_state); +struct test_unit { + const char *name; + test_func f; +}; + +/* API declaration. */ + +/* + * Print formatted message with the corresponding indent. + * If you want to leave a comment, use `test_comment()` instead. + */ +void test_message(const char *fmt, ...); + +/* Need for `skip_all()`, please, don't use it. */ +void _test_print_skip_all(const char *group_name, const char *reason); +/* End test via `longjmp()`, please, don't use it. */ +TEST_NORET void _test_exit(int status); + +void test_set_skip_reason(const char *reason); +void test_set_todo_reason(const char *reason); +/* + * Save formatted diagnostic data. Each entry separated with \n. + */ +void test_save_diag_data(const char *fmt, ...); + +/* Internal, it is better to use `test_run_group()` instead. */ +int _test_run_group(const char *group_name, const struct test_unit tests[], + size_t n_tests, void *test_state); + +/* Initialize `test_unit` structure. */ +#define test_unit_new(f) {#f, f} + +#define lengthof(arr) (sizeof(arr) / sizeof((arr)[0])) + +/* + * __func__ is the name for a test group, "main" for the parent + * test. + */ +#define test_run_group(t_arr, t_state) \ + _test_run_group(__func__, t_arr, lengthof(t_arr), t_state) + +#define SKIP_DIRECTIVE " # SKIP " +#define TODO_DIRECTIVE " # TODO " + +#define skip_all(reason) ({ \ + _test_print_skip_all(__func__, reason); \ + TEST_EXIT_SUCCESS; \ +}) + +static inline int skip(const char *reason) +{ + test_set_skip_reason(reason); + return TEST_EXIT_SUCCESS; +} + +static inline int todo(const char *reason) +{ + test_set_todo_reason(reason); + return TEST_EXIT_FAILURE; +} + +#define bail_out(reason) do { \ + /* \ + * For backwards compatibility with TAP13 Harnesses, \ + * Producers _should_ emit a "Bail out!" line at the root \ + * indentation level whenever a Subtest bails out [1]. \ + */ \ + printf("Bail out! %s\n", reason); \ + exit(TEST_EXIT_FAILURE); \ +} while (0) + +/* `fmt` should always be a format string here. */ +#define test_comment(fmt, ...) test_message("# " fmt, __VA_ARGS__) + +/* + * This is a set of useful assert macros like the standard C + * libary's assert(3) macro. + * + * On an assertion failure an assert macro will save the + * diagnostic to the special buffer, to be reported via YAML + * Diagnostic block and finish a test function with + * `return TEST_EXIT_FAILURE`. + * + * Due to limitations of the C language `assert_true()` and + * `assert_false()` macros can only display the expression that + * caused the assertion failure. Type specific assert macros, + * `assert_{type}_equal()` and `assert_{type}_not_equal()`, save + * the data that caused the assertion failure which increases data + * visibility aiding debugging of failing test cases. + */ + +#define LOCATION_FMT "location:\t%s:%d\n" +#define ASSERT_NAME_FMT(name) "failed_assertion:\t" #name "\n" +#define ASSERT_EQUAL_FMT(name_type, type_fmt) \ + LOCATION_FMT \ + ASSERT_NAME_FMT(assert_ ## name_type ## _equal) \ + "got: " type_fmt "\n" \ + "expected: " type_fmt "\n" + +#define ASSERT_NOT_EQUAL_FMT(type_fmt) \ + LOCATION_FMT \ + ASSERT_NAME_FMT(assert_ ## name_type ## _not_equal) \ + "got: " type_fmt "\n" \ + "unexpected: " type_fmt "\n" + +#define assert_true(cond) do { \ + if (!(cond)) { \ + test_save_diag_data(LOCATION_FMT \ + "condition_failed:\t'" #cond "'\n", \ + __FILE__, __LINE__); \ + _test_exit(TEST_LJMP_EXIT_FAILURE); \ + } \ +} while (0) + +#define assert_false(cond) assert_true(!(cond)) + +#define assert_general(cond, fmt, ...) do { \ + if (!(cond)) { \ + test_save_diag_data(fmt, __VA_ARGS__); \ + _test_exit(TEST_LJMP_EXIT_FAILURE); \ + } \ +} while (0) + +#define assert_ptr_equal(got, expected) do { \ + assert_general((got) == (expected), \ + ASSERT_EQUAL_FMT(ptr, "%p"), \ + __FILE__, __LINE__, (got), (expected) \ + ); \ +} while (0) + +#define assert_ptr_not_equal(got, unexpected) do { \ + assert_general((got) != (unexpected), \ + ASSERT_NOT_EQUAL_FMT(ptr, "%p"), \ + __FILE__, __LINE__, (got), (unexpected) \ + ); \ +} while (0) + + +#define assert_int_equal(got, expected) do { \ + assert_general((got) == (expected), \ + ASSERT_EQUAL_FMT(int, "%d"), \ + __FILE__, __LINE__, (got), (expected) \ + ); \ +} while (0) + +#define assert_int_not_equal(got, unexpected) do { \ + assert_general((got) != (unexpected), \ + ASSERT_NOT_EQUAL_FMT(int, "%d"), \ + __FILE__, __LINE__, (got), (unexpected) \ + ); \ +} while (0) + +#define assert_sizet_equal(got, expected) do { \ + assert_general((got) == (expected), \ + ASSERT_EQUAL_FMT(sizet, "%lu"), \ + __FILE__, __LINE__, (got), (expected) \ + ); \ +} while (0) + +#define assert_sizet_not_equal(got, unexpected) do { \ + assert_general((got) != (unexpected), \ + ASSERT_NOT_EQUAL_FMT(sizet, "%lu"), \ + __FILE__, __LINE__, (got), (unexpected) \ + ); \ +} while (0) + +/* Check that doubles are __exactly__ the same. */ +#define assert_double_equal(got, expected) do { \ + assert_general((got) == (expected), \ + ASSERT_EQUAL_FMT(double, "%lf"), \ + __FILE__, __LINE__, (got), (expected) \ + ); \ +} while (0) + +/* Check that doubles are not __exactly__ the same. */ +#define assert_double_not_equal(got, unexpected) do { \ + assert_general((got) != (unexpected), \ + ASSERT_NOT_EQUAL_FMT(double, "%lf"), \ + __FILE__, __LINE__, (got), (unexpected) \ + ); \ +} while (0) + +#endif /* TARANTOOL_LUAJIT_TEST_H */ diff --git a/test/tarantool-c-tests/unit-tap.test.c b/test/tarantool-c-tests/unit-tap.test.c new file mode 100644 index 00000000..27dc84ee --- /dev/null +++ b/test/tarantool-c-tests/unit-tap.test.c @@ -0,0 +1,31 @@ +#include "test.h" + +#define UNUSED(x) ((void)(x)) + +static int test_ok(void *test_state) +{ + UNUSED(test_state); + return TEST_EXIT_SUCCESS; +} + +static int test_skip(void *test_state) +{ + UNUSED(test_state); + return skip("test skip"); +} + +static int test_todo(void *test_state) +{ + UNUSED(test_state); + return todo("test todo"); +} + +int main(void) +{ + const struct test_unit tgroup[] = { + test_unit_new(test_ok), + test_unit_new(test_skip), + test_unit_new(test_todo) + }; + return test_run_group(tgroup, NULL); +} -- 2.34.1