diff --git a/.gitignore b/.gitignore index 01234a2a..2e6a9e2b 100644 --- a/.gitignore +++ b/.gitignore @@ -98,3 +98,5 @@ weston.ini.5 /tests/weston-ivi.ini internal-screenshot-00.png + +/zuctest diff --git a/Makefile.am b/Makefile.am index f493d165..4f0a4501 100644 --- a/Makefile.am +++ b/Makefile.am @@ -932,7 +932,8 @@ endif # Shared utilities # -noinst_LTLIBRARIES += libshared.la libshared-cairo.la +noinst_LTLIBRARIES += libshared.la libshared-cairo.la \ + libzunitc.la libzunitcmain.la libshared_la_CFLAGS = $(AM_CFLAGS) $(COMPOSITOR_CFLAGS) @@ -971,6 +972,37 @@ libshared_cairo_la_SOURCES = \ shared/frame.c \ shared/cairo-util.h +libzunitc_la_SOURCES = \ + tools/zunitc/inc/zunitc/zunitc.h \ + tools/zunitc/inc/zunitc/zunitc_impl.h \ + tools/zunitc/src/zuc_base_logger.c \ + tools/zunitc/src/zuc_base_logger.h \ + tools/zunitc/src/zuc_collector.c \ + tools/zunitc/src/zuc_collector.h \ + tools/zunitc/src/zuc_context.h \ + tools/zunitc/src/zuc_event.h \ + tools/zunitc/src/zuc_event_listener.h \ + tools/zunitc/src/zuc_types.h \ + tools/zunitc/src/zunitc_impl.c \ + shared/helpers.h + +libzunitc_la_CFLAGS = \ + $(AM_CFLAGS) \ + -I$(top_srcdir)/tools/zunitc/inc + +libzunitc_la_LIBADD = \ + libshared.la + +libzunitcmain_la_SOURCES = \ + tools/zunitc/src/main.c + +libzunitcmain_la_CFLAGS = \ + $(AM_CFLAGS) \ + -I$(top_srcdir)/tools/zunitc/inc + +libzunitcmain_la_LIBADD = \ + libzunitc.la \ + libshared.la # # tests subdirectory @@ -983,7 +1015,8 @@ internal_tests = \ shared_tests = \ config-parser.test \ - vertex-clip.test + vertex-clip.test \ + zuctest module_tests = \ surface-test.la \ @@ -1216,6 +1249,22 @@ setbacklight_CFLAGS = $(AM_CFLAGS) $(SETBACKLIGHT_CFLAGS) setbacklight_LDADD = $(SETBACKLIGHT_LIBS) endif +all-local: zuctest$(EXEEXT) + +noinst_PROGRAMS += zuctest$(EXEEXT) + +zuctest_LDADD = \ + libzunitc.la \ + libzunitcmain.la + +zuctest_CFLAGS = \ + $(AM_CFLAGS) \ + -I$(top_srcdir)/tools/zunitc/inc + +zuctest_SOURCES = \ + tools/zunitc/test/fixtures_test.c \ + tools/zunitc/test/zunitc_test.c + EXTRA_DIST += \ tests/weston-tests-env \ tests/internal-screenshot.ini \ @@ -1285,7 +1334,6 @@ EXTRA_DIST += \ CLEANFILES += $(man_MANS) - protocol/%-protocol.c : $(top_srcdir)/protocol/%.xml $(AM_V_GEN)$(MKDIR_P) $(dir $@) && $(wayland_scanner) code < $< > $@ diff --git a/tools/zunitc/doc/zunitc.dox b/tools/zunitc/doc/zunitc.dox new file mode 100644 index 00000000..24f20fac --- /dev/null +++ b/tools/zunitc/doc/zunitc.dox @@ -0,0 +1,145 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +/** +@page zunitc zunitc + +- @ref zunitc_overview +- @ref zunitc_execution + - @ref zunitc_execution_commandline + - @ref zunitc_execution_matching + - @ref zunitc_execution_wildcards + - @ref zunitc_execution_repeat + - @ref zunitc_execution_randomize +- @ref zunitc_fixtures +- @ref zunitc_functions + +@section zunitc_overview Overview + +A simple test framework in plain C suitable for basic unit and integration testing. + +The main rationale for creating this framework was to have a simple to use testing +framework with tests implemented in C using common patterns and under a +compatible license. The structure of the test code and macro use is intended to +follow common patterns established by frameworks such as Boost Test and Google Test. + + +To get started, one or more tests should be defined via ZUC_TEST() and/or +ZUC_TEST_F(), which set up automatic test registration via gcc extensions. +To actually execute tests, ZUC_RUN_TESTS() should be called. + + +Tests can use several ZUC_ASSERT_* or ZUC_ASSERTG_* checks to validate +conditions. The ZUC_ASSERT_* ones upon failure will mark the current test +as failing and immediatly execute a return. On the other hand, the +ZUC_ASSERTG_* tests will mark the current test as failed and then execute a +'goto' targeting the specified label. + +The set of fatal test checks are + +- ZUC_ASSERT_TRUE() +- ZUC_ASSERT_FALSE() +- ZUC_ASSERT_NULL() +- ZUC_ASSERT_NOT_NULL() +- ZUC_ASSERT_EQ() +- ZUC_ASSERT_NE() +- ZUC_ASSERT_LT() +- ZUC_ASSERT_LE() +- ZUC_ASSERT_GT() +- ZUC_ASSERT_GE() +- ZUC_ASSERT_STREQ() +- ZUC_ASSERT_STRNE() + +and + +- ZUC_ASSERTG_TRUE() +- ZUC_ASSERTG_FALSE() +- ZUC_ASSERTG_NULL() +- ZUC_ASSERTG_NOT_NULL() +- ZUC_ASSERTG_EQ() +- ZUC_ASSERTG_NE() +- ZUC_ASSERTG_LT() +- ZUC_ASSERTG_LE() +- ZUC_ASSERTG_GT() +- ZUC_ASSERTG_GE() +- ZUC_ASSERTG_STREQ() +- ZUC_ASSERTG_STRNE() + +Unconditional test values for logging and termination are +- ZUC_SKIP() +- ZUC_FATAL() + +Unconditional message logging for failure cases only is +- ZUC_TRACEPOINT() + +@section zunitc_execution Controlling Execution + +To control execution, the various zuc_set_* functions can be called before invoking ZUC_RUN_TESTS(). + +@subsection zunitc_execution_commandline Commandline Parameters + +The current implementation defers processing of command-line parameters to the main application hosting the testing. It is possible that a helper to process certain parameters may be added. + +@subsection zunitc_execution_matching Matching Patterns for Tests + +The function zuc_set_filter() can be used to specify a pattern for matching or excluding tests from a run. The general form is + match1[:match2[:match3..n]][:-exclude1[:exclude2[:exclude3..n]]] + +@subsection zunitc_execution_wildcards Matching Wildcards + +Wildcards can be used in the match/exclude patterns and recognize the following two special characters: +- '*' matches any number of characters including zero. +- '?' matches any single character. + +Calling zuc_list_tests() after zuc_set_filter() can be done to show the effects of the matching without needing to actually run tests. + +@subsection zunitc_execution_repeat Repeating Tests + +Setting the repeat count higher than 1 ( via zuc_set_repeat() ) will cause the tests to be executed several times in a row. This can be useful for stress testing, checking for leaks, etc. + +@subsection zunitc_execution_randomize Randomizing Tests + +Test ordering can be randomized by setting a non-zero positive value to zuc_set_random(). Setting it to 1 will cause the framework to pick a random seed based on the time. A value greater than 1 will be taken as a random seed itself. And setting it to 0 will disable randomization and allow the test to be executed in their natural ordering. + +@section zunitc_fixtures Fixtures + +Per-suite and per-test setup and teardown fixtures can be implemented by defining an instance of struct zuc_fixture and using it as the first parameter to ZUC_TEST_F(). + +@section zunitc_functions Functions + +- ZUC_TEST() +- ZUC_TEST_F() +- ZUC_RUN_TESTS() +- zuc_cleanup() +- zuc_list_tests() +- zuc_set_filter() +- zuc_set_random() +- zuc_set_spawn() +- zuc_set_output_tap() +- zuc_set_output_junit() +- zuc_has_skip() +- zuc_has_failure() + +*/ diff --git a/tools/zunitc/inc/zunitc/zunitc.h b/tools/zunitc/inc/zunitc/zunitc.h new file mode 100644 index 00000000..fc6ee1ce --- /dev/null +++ b/tools/zunitc/inc/zunitc/zunitc.h @@ -0,0 +1,696 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef Z_UNIT_C_H +#define Z_UNIT_C_H + +#include +#include +#include +#include + +#include "zunitc/zunitc_impl.h" + +#if !__GNUC__ +#error Framework currently requires gcc or compatible compiler. +#endif + +#if INTPTR_MAX < INT_MAX +#error Odd platform requires rework of value type from intptr_t to custom. +#endif + +/** + * @file + * Simple unit test framework declarations. + */ + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @page zunitc + */ + +/** + * Structure to use when defining a test fixture. + * @note likely pending refactoring as use cases are refined. + */ +struct zuc_fixture { + /** + * Initial optional seed data to pass to setup functions and/or tests. + */ + const void *data; + + /** + * Per-suite setup called before invoking any of the tests + * contained in the suite. + * + * @return a pointer to test data, or NULL. + */ + void *(*set_up_test_case)(const void *data); + + /** + * Per-suite tear-down called after invoking all of the tests + * contained in the suite. + * + * @param data pointer returned from the setup function. + */ + void (*tear_down_test_case)(void *data); + + /** + * Setup called before running each of the tests in the suite. + * + * @param data optional data from suite setup, or NULL. + * @return a pointer to test data, or NULL. + */ + void *(*set_up)(void *data); + + /** + * Tear-down called after running each of the tests in the suite. + * + * @param data pointer returned from the setup function. + */ + void (*tear_down)(void *data); +}; + +/** + * Process exit code to mark skipped tests, consistent with + * automake tests. + */ +#define ZUC_EXIT_SKIP 77 + +/** + * Accesses the test executable program name. + * This version will include any full or partial path used to + * launch the executable. + * + * @note This depends on zuc_initialize() having been called. + * + * @return the name of the running program. + * The caller should not free nor hold this pointer. It will not stay + * valid across calls to zuc_initialize() or zuc_cleanup(). + * @see zuc_get_program_basename() + */ +const char * +zuc_get_program_name(void); + +/** + * Accesses the test executable program name in trimmed format. + * If the program is launched via a partial or full path, this + * version trims to return only the basename. + * + * @note This depends on zuc_initialize() having been called. + * + * @return the name of the running program. + * The caller should not free nor hold this pointer. It will not stay + * valid across calls to zuc_initialize() or zuc_cleanup(). + * @see zuc_get_program_name() + */ +const char * +zuc_get_program_basename(void); + +/** + * Initializes the test framework and consumes any known command-line + * parameters from the list. + * The exception is 'h/help' which will be left in place for follow-up + * processing by the hosting app if so desired. + * + * @param argc pointer to argc value to read and possibly change. + * @param argv array of parameter pointers to read and possibly change. + * @param help_flagged if non-NULL will be set to true if the user + * specifies the help flag (and framework help has been output). + * @return EXIT_SUCCESS upon success setting or help, EXIT_FAILURE otherwise. + */ +int zuc_initialize(int *argc, char *argv[], bool *help_flagged); + +/** + * Runs all tests that have been registered. + * Expected return values include EXIT_FAILURE if any errors or failures + * have occurred, ::ZUC_EXIT_SKIP if no failures have occurred but at least + * one test reported skipped, otherwise EXIT_SUCCESS if nothing of note + * was recorded. + * + * @note for consistency with other frameworks and to allow for additional + * cleanup to be added later this is implemented as a wrapper macro. + * + * @return expected exit status - normally EXIT_SUCCESS, ::ZUC_EXIT_SKIP, + * or EXIT_FAILURE. + * Normally an application can use this value directly in calling exit(), + * however there could be cases where some additional processing such as + * resource cleanup or library shutdown that a program might want to do + * first. + */ +#define ZUC_RUN_TESTS() \ + zucimpl_run_tests() + +/** + * Clears the test system in preparation for application shutdown. + */ +void +zuc_cleanup(void); + +/** + * Displays all known tests. + * The list returned is affected to any filtering in place. + * + * @see zuc_set_filter() + */ +void +zuc_list_tests(void); + +/** + * Sets the filter string to use for tests. + * The format is a series of patterns separated by a colon, with wildcards + * and an optional flag for negative matching. For wildcards, the '*' + * character will match any sequence and the '?' character will match any + * single character. + * The '-' character at the start of a pattern marks the end of the + * patterns required to match and the begining of patterns that names + * must not match. + * Defaults to use all tests. + * + * @param filter the filter string to apply to tests. + */ +void +zuc_set_filter(const char *filter); + +/** + * Trigger specific failure/signal upon test failures; useful when + * running under a debugger. + * Currently this is implemented to raise a SIGABRT signal when any + * failure is reported. + * Defaults to false. + * + * @param break_on_failure true to cause a break when tests fail, false to + * allow normal operation upon failures. + */ +void +zuc_set_break_on_failure(bool break_on_failure); + +/** + * Sets the number of times to repeat the tests. + * Any number higher than 1 will cause the tests to be repeated the + * specified number of times. + * Defaults to 1/no repeating. + * + * @param repeat number of times to repeat the tests. + */ +void +zuc_set_repeat(int repeat); + +/** + * Randomizes the order in which tests are executed. + * A value of 0 (the default) means tests are executed in their natural + * ordering. A value of 1 will pick a random seed based on the time to + * use for running tests in a pseudo-random order. A value greater than 1 + * will be used directly for the initial seed. + * + * If the tests are also repeated, the seed will be incremented for each + * subsequent run. + * Defaults to 0/not randomize. + * + * @param random 0|1|seed value. + * @see zuc_set_repeat() + */ +void +zuc_set_random(int random); + +/** + * Controls whether or not to run the tests as forked child processes. + * Defaults to true. + * + * @param spawn true to spawn each test in a forked child process, + * false to run tests directly. + */ +void +zuc_set_spawn(bool spawn); + +/** + * Enables output in the JUnit XML format. + * Defaults to false. + * + * @param enable true to generate JUnit XML output, false to disable. + */ +void +zuc_set_output_junit(bool enable); + +/** + * Defines a test case that can be registered to run. + */ +#define ZUC_TEST(tcase, test) \ + static void zuctest_##tcase##_##test(void); \ + \ + const struct zuc_registration zzz_##tcase##_##test \ + __attribute__ ((section ("zuc_tsect"))) = \ + { \ + #tcase, #test, 0, \ + zuctest_##tcase##_##test, \ + 0 \ + }; \ + \ + static void zuctest_##tcase##_##test(void) + +/** + * Defines a test case that can be registered to run along with setup/teardown + * support per-test and/or per test case. + * + * @note likely pending refactoring as use cases are refined. + */ +#define ZUC_TEST_F(tcase, test) \ + static void zuctest_##tcase##_##test(void *data); \ + \ + const struct zuc_registration zzz_##tcase##_##test \ + __attribute__ ((section ("zuc_tsect"))) = \ + { \ + #tcase, #test, &tcase, \ + 0, \ + zuctest_##tcase##_##test \ + }; \ + \ + static void zuctest_##tcase##_##test(void *data) + + +/** + * Returns true if the currently executing test has encountered any skips. + * + * @return true if there is currently a test executing and it has + * encountered any skips. + * @see zuc_has_failure + */ +bool +zuc_has_skip(void); + +/** + * Returns true if the currently executing test has encountered any failures. + * + * @return true if there is currently a test executing and it has + * encountered any failures. + * @see zuc_has_skip + */ +bool +zuc_has_failure(void); + +/** + * Terminates the current test without marking it as failed. + * + * @param message the message to log as to why the test has been skipped. + */ +#define ZUC_SKIP(message) \ + do { \ + zucimpl_terminate(__FILE__, __LINE__, false, false, #message); \ + return; \ + } \ + while (0) + +/** + * Terminates the current test and marks it as failed. + * + * @param message the message to log as to why the test has failed. + */ +#define ZUC_FATAL(message) \ + do { \ + zucimpl_terminate(__FILE__, __LINE__, true, true, #message); \ + return; \ + } \ + while (0) + +/** + * Marks the current test as failed with a fatal issue, but does not + * immediately return from the current function. ZUC_FATAL() is normally + * preferred, but when further cleanup is needed, or the current function + * needs to return a value, this macro may be required. + * + * @param message the message to log as to why the test has failed. + * @see ZUC_FATAL() + */ +#define ZUC_MARK_FATAL(message) \ + do { \ + zucimpl_terminate(__FILE__, __LINE__, true, true, #message); \ + } \ + while (0) + +/** + * Creates a message that will be processed in the case of failure. + * If the test encounters any failures (fatal or non-fatal) then these + * messages are included in output. Otherwise they are discarded at the + * end of the test run. + * + * @param message the format string style message. + */ +#define ZUC_TRACEPOINT(message, ...) \ + zucimpl_tracepoint(__FILE__, __LINE__, message, ##__VA_ARGS__); + +/** + * Internal use macro for ASSERT implementation. + * Should not be used directly in code. + */ +#define ZUCIMPL_ASSERT(opcode, valtype, lhs, rhs) \ + do { \ + if (zucimpl_expect_pred2(__FILE__, __LINE__, \ + (opcode), (valtype), true, \ + (intptr_t)(lhs), (intptr_t)(rhs), \ + #lhs, #rhs)) { \ + return; \ + } \ + } \ + while (0) + +/** + * Internal use macro for ASSERT with Goto implementation. + * Should not be used directly in code. + */ +#define ZUCIMPL_ASSERTG(label, opcode, valtype, lhs, rhs) \ + do { \ + if (zucimpl_expect_pred2(__FILE__, __LINE__, \ + (opcode), (valtype), true, \ + (intptr_t)(lhs), (intptr_t)(rhs), \ + #lhs, #rhs)) { \ + goto label; \ + } \ + } \ + while (0) + +/** + * Verifies that the specified expression is true, marks the test as failed + * and terminates the test if it is not. + * + * @param condition the expression that is expected to be true. + * @note it is far better to use a more specific check when possible + * (e.g. ZUC_ASSERT_EQ(), ZUC_ASSERT_NE(), etc.) + * @see ZUC_ASSERTG_TRUE() + */ +#define ZUC_ASSERT_TRUE(condition) \ + ZUCIMPL_ASSERT(ZUC_OP_TRUE, ZUC_VAL_INT, condition, 0) + +/** + * Verifies that the specified expression is false, marks the test as + * failed and terminates the test if it is not. + * + * @param condition the expression that is expected to be false. + * @note it is far better to use a more specific check when possible + * (e.g. ZUC_ASSERT_EQ(), ZUC_ASSERT_NE(), etc.) + * @see ZUC_ASSERTG_FALSE() + */ +#define ZUC_ASSERT_FALSE(condition) \ + ZUCIMPL_ASSERT(ZUC_OP_FALSE, ZUC_VAL_INT, condition, 0) + +/** + * Verifies that the specified expression is NULL, marks the test as failed + * and terminates the test if it is not. + * + * @param condition the expression that is expected to be a NULL pointer. + * @see ZUC_ASSERTG_NULL() + */ +#define ZUC_ASSERT_NULL(condition) \ + ZUCIMPL_ASSERT(ZUC_OP_NULL, ZUC_VAL_PTR, condition, 0) + +/** + * Verifies that the specified expression is non-NULL, marks the test as + * failed and terminates the test if it is not. + * + * @param condition the expression that is expected to be a non-NULL pointer. + * @see ZUC_ASSERTG_NOT_NULL() + */ +#define ZUC_ASSERT_NOT_NULL(condition) \ + ZUCIMPL_ASSERT(ZUC_OP_NOT_NULL, ZUC_VAL_PTR, condition, 0) + +/** + * Verifies that the values of the specified expressions match, marks the + * test as failed and terminates the test if they do not. + * + * @param expected the value the result should hold. + * @param actual the actual value seen in testing. + * @see ZUC_ASSERTG_EQ() + */ +#define ZUC_ASSERT_EQ(expected, actual) \ + ZUCIMPL_ASSERT(ZUC_OP_EQ, ZUC_VAL_INT, expected, actual) + +/** + * Verifies that the values of the specified expressions differ, marks the + * test as failed and terminates the test if they do not. + * + * @param expected the value the result should not hold. + * @param actual the actual value seen in testing. + * @see ZUC_ASSERTG_NE() + */ +#define ZUC_ASSERT_NE(expected, actual) \ + ZUCIMPL_ASSERT(ZUC_OP_NE, ZUC_VAL_INT, expected, actual) + +/** + * Verifies that the value of the first expression is less than the value + * of the second expression, marks the test as failed and terminates the + * test if it is not. + * + * @param lesser the expression whose value should be lesser than the other. + * @param greater the expression whose value should be greater than the other. + * @see ZUC_ASSERTG_LT() + */ +#define ZUC_ASSERT_LT(lesser, greater) \ + ZUCIMPL_ASSERT(ZUC_OP_LT, ZUC_VAL_INT, lesser, greater) + +/** + * Verifies that the value of the first expression is less than or equal + * to the value of the second expression, marks the test as failed and + * terminates the test if it is not. + * + * @param lesser the expression whose value should be lesser than or equal to + * the other. + * @param greater the expression whose value should be greater than or equal to + * the other. + * @see ZUC_ASSERTG_LE() + */ +#define ZUC_ASSERT_LE(lesser, greater) \ + ZUCIMPL_ASSERT(ZUC_OP_LE, ZUC_VAL_INT, lesser, greater) + +/** + * Verifies that the value of the first expression is greater than the + * value of the second expression, marks the test as failed and terminates + * the test if it is not. + * + * @param greater the expression whose value should be greater than the other. + * @param lesser the expression whose value should be lesser than the other. + * @see ZUC_ASSERTG_GT() + */ +#define ZUC_ASSERT_GT(greater, lesser) \ + ZUCIMPL_ASSERT(ZUC_OP_GT, ZUC_VAL_INT, greater, lesser) + +/** + * Verifies that the value of the first expression is greater than or equal + * to the value of the second expression, marks the test as failed and + * terminates the test if it is not. + * + * @param greater the expression whose value should be greater than or equal to + * the other. + * @param lesser the expression whose value should be lesser than or equal to + * the other. + * @see ZUC_ASSERTG_GE() + */ +#define ZUC_ASSERT_GE(greater, lesser) \ + ZUCIMPL_ASSERT(ZUC_OP_GE, ZUC_VAL_INT, greater, lesser) + +/** + * Verifies that the values of the specified expressions match when + * compared as null-terminated C-style strings, marks the test as failed + * and terminates the test if they do not. + * + * @param expected the value the result should hold. + * @param actual the actual value seen in testing. + * @see ZUC_ASSERTG_STREQ() + */ +#define ZUC_ASSERT_STREQ(expected, actual) \ + ZUCIMPL_ASSERT(ZUC_OP_EQ, ZUC_VAL_CSTR, expected, actual) + +/** + * Verifies that the values of the specified expressions differ when + * compared as null-terminated C-style strings, marks the test as failed + * and terminates the test if they do not. + * + * @param expected the value the result should not hold. + * @param actual the actual value seen in testing. + * @see ZUC_ASSERTG_STRNE() + */ +#define ZUC_ASSERT_STRNE(expected, actual) \ + ZUCIMPL_ASSERT(ZUC_OP_NE, ZUC_VAL_CSTR, expected, actual) + +/** + * Verifies that the specified expression is true, marks the test as failed + * and terminates the test via a 'goto' if it is not. + * + * @param condition the expression that is expected to be true. + * @note it is far better to use a more specific check when possible + * (e.g. ZUC_ASSERTG_EQ(), ZUC_ASSERTG_NE(), etc.) + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_TRUE() + */ +#define ZUC_ASSERTG_TRUE(condition, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_TRUE, ZUC_VAL_INT, condition, 0) + +/** + * Verifies that the specified expression is false, marks the test as + * failed and terminates the test via a 'goto' if it is not. + * + * @param condition the expression that is expected to be false. + * @note it is far better to use a more specific check when possible + * (e.g. ZUC_ASSERTG_EQ(), ZUC_ASSERTG_NE(), etc.) + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_FALSE() + */ +#define ZUC_ASSERTG_FALSE(condition, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_FALSE, ZUC_VAL_INT, condition, 0) + +/** + * Verifies that the specified expression is NULL, marks the test as failed + * and terminates the test via a 'goto' if it is not. + * + * @param condition the expression that is expected to be a NULL pointer. + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_NULL() + */ +#define ZUC_ASSERTG_NULL(condition, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_NULL, ZUC_VAL_PTR, condition, 0) + +/** + * Verifies that the specified expression is non-NULL, marks the test as + * failed and terminates the test via a 'goto' if it is not. + * + * @param condition the expression that is expected to be a non-NULL pointer. + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_NOT_NULL() + */ +#define ZUC_ASSERTG_NOT_NULL(condition, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_NOT_NULL, ZUC_VAL_PTR, condition, 0) + +/** + * Verifies that the values of the specified expressions match, marks the + * test as failed and terminates the test via a 'goto' if they do not. + * + * @param expected the value the result should hold. + * @param actual the actual value seen in testing. + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_EQ() + */ +#define ZUC_ASSERTG_EQ(expected, actual, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_EQ, ZUC_VAL_INT, expected, actual) + +/** + * Verifies that the values of the specified expressions differ, marks the + * test as failed and terminates the test via a 'goto' if they do not. + * + * @param expected the value the result should not hold. + * @param actual the actual value seen in testing. + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_NE() + */ +#define ZUC_ASSERTG_NE(expected, actual, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_NE, ZUC_VAL_INT, expected, actual) + +/** + * Verifies that the value of the first expression is less than the value + * of the second expression, marks the test as failed and terminates the + * test if it is not. + * + * @param lesser the expression whose value should be lesser than the other. + * @param greater the expression whose value should be greater than the other. + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_LT() + */ +#define ZUC_ASSERTG_LT(lesser, greater, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_LT, ZUC_VAL_INT, lesser, greater) + +/** + * Verifies that the value of the first expression is less than or equal + * to the value of the second expression, marks the test as failed and + * terminates the test via a 'goto' if it is not. + * + * @param lesser the expression whose value should be lesser than or equal to + * the other. + * @param greater the expression whose value should be greater than or equal to + * the other. + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_LE() + */ +#define ZUC_ASSERTG_LE(lesser, greater, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_LE, ZUC_VAL_INT, lesser, greater) + +/** + * Verifies that the value of the first expression is greater than the + * value of the second expression, marks the test as failed and terminates + * the test if it is not. + * + * @param greater the expression whose value should be greater than the other. + * @param lesser the expression whose value should be lesser than the other. + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_GT() + */ +#define ZUC_ASSERTG_GT(greater, lesser, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_GT, ZUC_VAL_INT, greater, lesser) + +/** + * Verifies that the value of the first expression is greater than or equal + * to the value of the second expression, marks the test as failed and + * terminates the test via a 'goto' if it is not. + * + * @param greater the expression whose value should be greater than or equal to + * the other. + * @param lesser the expression whose value should be lesser than or equal to + * the other. + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_GE() + */ +#define ZUC_ASSERTG_GE(greater, lesser, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_GE, ZUC_VAL_INT, greater, lesser) + +/** + * Verifies that the values of the specified expressions match when + * compared as null-terminated C-style strings, marks the test as failed + * and terminates the test via a 'goto' if they do not. + * + * @param expected the value the result should hold. + * @param actual the actual value seen in testing. + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_STREQ() + */ +#define ZUC_ASSERTG_STREQ(expected, actual, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_EQ, ZUC_VAL_CSTR, expected, actual) + +/** + * Verifies that the values of the specified expressions differ when + * compared as null-terminated C-style strings, marks the test as failed + * and terminates the test via a 'goto' if they do not. + * + * @param expected the value the result should not hold. + * @param actual the actual value seen in testing. + * @param label the target for 'goto' if the assertion fails. + * @see ZUC_ASSERT_STRNE() + */ +#define ZUC_ASSERTG_STRNE(expected, actual, label) \ + ZUCIMPL_ASSERTG(label, ZUC_OP_NE, ZUC_VAL_CSTR, expected, actual) + +#ifdef __cplusplus +} /* extern "C" */ +#endif + +#endif /* Z_UNIT_C_H */ diff --git a/tools/zunitc/inc/zunitc/zunitc_impl.h b/tools/zunitc/inc/zunitc/zunitc_impl.h new file mode 100644 index 00000000..f0533689 --- /dev/null +++ b/tools/zunitc/inc/zunitc/zunitc_impl.h @@ -0,0 +1,105 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef Z_UNIT_C_IMPL_H +#define Z_UNIT_C_IMPL_H + +/** + * @file + * Internal details to bridge the public API - should not be used + * directly in user code. + */ + +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +enum zuc_check_op +{ + ZUC_OP_TRUE, + ZUC_OP_FALSE, + ZUC_OP_NULL, + ZUC_OP_NOT_NULL, + ZUC_OP_EQ, + ZUC_OP_NE, + ZUC_OP_GE, + ZUC_OP_GT, + ZUC_OP_LE, + ZUC_OP_LT, + ZUC_OP_TERMINATE, + ZUC_OP_TRACEPOINT +}; + +enum zuc_check_valtype +{ + ZUC_VAL_INT, + ZUC_VAL_CSTR, + ZUC_VAL_PTR, +}; + +typedef void (*zucimpl_test_fn)(void); + +typedef void (*zucimpl_test_fn_f)(void *); + +/** + * Internal use structure for automatic test case registration. + * Should not be used directly in code. + */ +struct zuc_registration { + const char *tcase; /**< Name of the test case. */ + const char *test; /**< Name of the specific test. */ + const struct zuc_fixture* fxt; /**< Optional fixture for test/case. */ + zucimpl_test_fn fn; /**< function implementing base test. */ + zucimpl_test_fn_f fn_f; /**< function implementing test with + fixture. */ +} __attribute__ ((aligned (32))); + + +int +zucimpl_run_tests(void); + +void +zucimpl_terminate(char const *file, int line, + bool fail, bool fatal, const char *msg); + +int +zucimpl_tracepoint(char const *file, int line, const char *fmt, ...) + __attribute__ ((format (printf, 3, 4))); + +int +zucimpl_expect_pred2(char const *file, int line, + enum zuc_check_op, enum zuc_check_valtype valtype, + bool fatal, + intptr_t lhs, intptr_t rhs, + const char *lhs_str, const char* rhs_str); + +#ifdef __cplusplus +} +#endif + +#endif /* Z_UNIT_C_IMPL_H */ diff --git a/tools/zunitc/src/main.c b/tools/zunitc/src/main.c new file mode 100644 index 00000000..81798071 --- /dev/null +++ b/tools/zunitc/src/main.c @@ -0,0 +1,58 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +/* + * Common main() for test programs. + */ + +#include +#include + +#include "zunitc/zunitc.h" + +int +main(int argc, char* argv[]) +{ + bool helped = false; + int rc = zuc_initialize(&argc, argv, &helped); + + if ((rc == EXIT_SUCCESS) && !helped) { + /* Stop if any unrecognized parameters were encountered. */ + if (argc > 1) { + printf("%s: unrecognized option '%s'\n", + argv[0], argv[1]); + printf("Try '%s --help' for more information.\n", + argv[0]); + rc = EXIT_FAILURE; + } else { + rc = ZUC_RUN_TESTS(); + } + } + + zuc_cleanup(); + return rc; +} diff --git a/tools/zunitc/src/zuc_base_logger.c b/tools/zunitc/src/zuc_base_logger.c new file mode 100644 index 00000000..1beb60d2 --- /dev/null +++ b/tools/zunitc/src/zuc_base_logger.c @@ -0,0 +1,404 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include "zuc_base_logger.h" + +#include +#include +#include +#include +#include + +#include "zuc_event_listener.h" +#include "zuc_types.h" + +#include "shared/zalloc.h" + +/* a few sequences for rudimentary ANSI graphics. */ +#define CSI_GRN "\x1b[0;32m" +#define CSI_RED "\x1b[0;31m" +#define CSI_YLW "\x1b[0;33m" +#define CSI_RST "\x1b[m" + +/** + * Logical mappings of style levels. + */ +enum style_level { + STYLE_GOOD, + STYLE_WARN, + STYLE_BAD +}; + +/** + * Structure for internal context. + */ +struct base_data { + bool use_color; +}; + +/** + * Prints a formatted string with optional ANSI coloring. + * + * @param use_color true to colorize the output, false to output normally. + * @param slevel the logical type to color for. + * @param fmt the format string to print with. + */ +static void +styled_printf(bool use_color, enum style_level slevel, const char *fmt, ...); + +static void +destroy(void *data); + +static void +pre_run(void *data, int pass_count, int pass_num, int seed, const char *filter); + +static void +run_started(void *data, int live_case_count, + int live_test_count, int disabled_count); + +static void +run_ended(void *data, int case_count, struct zuc_case **cases, + int live_case_count, int live_test_count, int total_passed, + int total_failed, int total_disabled, long total_elapsed); + +static void +case_started(void *data, struct zuc_case *test_case, int live_test_count, + int disabled_count); + +static void +case_ended(void *data, struct zuc_case *test_case); + +static void +test_started(void *data, struct zuc_test *test); + +static void +test_ended(void *data, struct zuc_test *test); + +static void +check_triggered(void *data, char const *file, int line, + enum zuc_fail_state state, enum zuc_check_op op, + enum zuc_check_valtype valtype, + intptr_t val1, intptr_t val2, + const char *expr1, const char *expr2); + +struct zuc_event_listener * +zuc_base_logger_create(void) +{ + struct zuc_event_listener *listener = + zalloc(sizeof(struct zuc_event_listener)); + + listener->data = zalloc(sizeof(struct base_data)); + listener->destroy = destroy; + listener->pre_run = pre_run; + listener->run_started = run_started; + listener->run_ended = run_ended; + listener->case_started = case_started; + listener->case_ended = case_ended; + listener->test_started = test_started; + listener->test_ended = test_ended; + listener->check_triggered = check_triggered; + + return listener; +} + +void +styled_printf(bool use_color, enum style_level slevel, const char *fmt, ...) +{ + va_list argp; + + if (use_color) + switch (slevel) { + case STYLE_GOOD: + printf(CSI_GRN); + break; + case STYLE_WARN: + printf(CSI_YLW); + break; + case STYLE_BAD: + printf(CSI_RED); + break; + default: + break; + } + + va_start(argp, fmt); + vprintf(fmt, argp); + va_end(argp); + + if (use_color) + printf(CSI_RST); +} + +void +destroy(void *data) +{ + free(data); +} + +void +pre_run(void *data, int pass_count, int pass_num, int seed, const char *filter) +{ + struct base_data *bdata = data; + + bdata->use_color = isatty(fileno(stdout)) + && getenv("TERM") && strcmp(getenv("TERM"), "dumb"); + + if (pass_count > 1) + printf("\nRepeating all tests (iteration %d) . . .\n\n", + pass_num); + + if (filter && filter[0]) + styled_printf(bdata->use_color, STYLE_WARN, + "Note: test filter = %s\n", + filter); + + if (seed > 0) + styled_printf(bdata->use_color, STYLE_WARN, + "Note: Randomizing tests' orders" + " with a seed of %u .\n", + seed); +} + +void +run_started(void *data, int live_case_count, + int live_test_count, int disabled_count) +{ + struct base_data *bdata = data; + + styled_printf(bdata->use_color, STYLE_GOOD, "[==========]"); + printf(" Running %d %s from %d test %s.\n", + live_test_count, + (live_test_count == 1) ? "test" : "tests", + live_case_count, + (live_case_count == 1) ? "case" : "cases"); +} + +void +run_ended(void *data, int case_count, struct zuc_case **cases, + int live_case_count, int live_test_count, int total_passed, + int total_failed, int total_disabled, long total_elapsed) +{ + struct base_data *bdata = data; + styled_printf(bdata->use_color, STYLE_GOOD, "[==========]"); + printf(" %d %s from %d test %s ran. (%ld ms)\n", + live_test_count, + (live_test_count == 1) ? "test" : "tests", + live_case_count, + (live_case_count == 1) ? "case" : "cases", + total_elapsed); + + if (total_passed) { + styled_printf(bdata->use_color, STYLE_GOOD, "[ PASSED ]"); + printf(" %d %s.\n", total_passed, + (total_passed == 1) ? "test" : "tests"); + } + + if (total_failed) { + int case_num; + styled_printf(bdata->use_color, STYLE_BAD, "[ FAILED ]"); + printf(" %d %s, listed below:\n", + total_failed, (total_failed == 1) ? "test" : "tests"); + + for (case_num = 0; case_num < case_count; ++case_num) { + int i; + for (i = 0; i < cases[case_num]->test_count; ++i) { + struct zuc_test *curr = + cases[case_num]->tests[i]; + if (curr->failed || curr->fatal) { + styled_printf(bdata->use_color, + STYLE_BAD, + "[ FAILED ]"); + printf(" %s.%s\n", + cases[case_num]->name, + curr->name); + } + } + } + } + + if (total_failed || total_disabled) + printf("\n"); + + if (total_failed) + printf(" %d FAILED %s\n", + total_failed, + (total_failed == 1) ? "TEST" : "TESTS"); + + if (total_disabled) + styled_printf(bdata->use_color, STYLE_WARN, + " YOU HAVE %d DISABLED %s\n", + total_disabled, + (total_disabled == 1) ? "TEST" : "TESTS"); +} + +void +case_started(void *data, struct zuc_case *test_case, int live_test_count, + int disabled_count) +{ + struct base_data *bdata = data; + styled_printf(bdata->use_color, STYLE_GOOD, "[----------]"); + printf(" %d %s from %s.\n", + live_test_count, + (live_test_count == 1) ? "test" : "tests", + test_case->name); + +} + +void +case_ended(void *data, struct zuc_case *test_case) +{ + struct base_data *bdata = data; + styled_printf(bdata->use_color, STYLE_GOOD, "[----------]"); + printf(" %d %s from %s (%ld ms)\n", + test_case->test_count, + (test_case->test_count == 1) ? "test" : "tests", + test_case->name, + test_case->elapsed); + printf("\n"); +} + +void +test_started(void *data, struct zuc_test *test) +{ + struct base_data *bdata = data; + styled_printf(bdata->use_color, STYLE_GOOD, "[ RUN ]"); + printf(" %s.%s\n", test->test_case->name, test->name); +} + +void +test_ended(void *data, struct zuc_test *test) +{ + struct base_data *bdata = data; + if (test->failed || test->fatal) { + styled_printf(bdata->use_color, STYLE_BAD, "[ FAILED ]"); + printf(" %s.%s (%ld ms)\n", + test->test_case->name, test->name, test->elapsed); + } else { + styled_printf(bdata->use_color, STYLE_GOOD, "[ OK ]"); + printf(" %s.%s (%ld ms)\n", + test->test_case->name, test->name, test->elapsed); + } +} + +const char * +zuc_get_opstr(enum zuc_check_op op) +{ + switch (op) { + case ZUC_OP_EQ: + return "="; + break; + case ZUC_OP_NE: + return "!="; + break; + case ZUC_OP_GE: + return ">="; + break; + case ZUC_OP_GT: + return ">"; + break; + case ZUC_OP_LE: + return "<="; + break; + case ZUC_OP_LT: + return "<"; + break; + default: + return "???"; + } +} + +void +check_triggered(void *data, char const *file, int line, + enum zuc_fail_state state, enum zuc_check_op op, + enum zuc_check_valtype valtype, + intptr_t val1, intptr_t val2, + const char *expr1, const char *expr2) +{ + switch (op) { + case ZUC_OP_TRUE: + printf("%s:%d: error: Value of: %s\n", file, line, expr1); + printf(" Actual: false\n"); + printf("Expected: true\n"); + break; + case ZUC_OP_FALSE: + printf("%s:%d: error: Value of: %s\n", file, line, expr1); + printf(" Actual: true\n"); + printf("Expected: false\n"); + break; + case ZUC_OP_NULL: + printf("%s:%d: error: Value of: %s\n", file, line, expr1); + printf(" Actual: %p\n", (void *)val1); + printf("Expected: %p\n", NULL); + break; + case ZUC_OP_NOT_NULL: + printf("%s:%d: error: Value of: %s\n", file, line, expr1); + printf(" Actual: %p\n", (void *)val1); + printf("Expected: not %p\n", NULL); + break; + case ZUC_OP_EQ: + if (valtype == ZUC_VAL_CSTR) { + printf("%s:%d: error: Value of: %s\n", file, line, + expr2); + printf(" Actual: %s\n", (const char *)val2); + printf("Expected: %s\n", expr1); + printf("Which is: %s\n", (const char *)val1); + } else { + printf("%s:%d: error: Value of: %s\n", file, line, + expr2); + printf(" Actual: %ld\n", val2); + printf("Expected: %s\n", expr1); + printf("Which is: %ld\n", val1); + } + break; + case ZUC_OP_NE: + if (valtype == ZUC_VAL_CSTR) { + printf("%s:%d: error: ", file, line); + printf("Expected: (%s) %s (%s), actual: %s == %s\n", + expr1, zuc_get_opstr(op), expr2, + (char *)val1, (char *)val2); + } else { + printf("%s:%d: error: ", file, line); + printf("Expected: (%s) %s (%s), actual: %ld vs %ld\n", + expr1, zuc_get_opstr(op), expr2, val1, val2); + } + break; + case ZUC_OP_TERMINATE: { + char const *level = (val1 == 0) ? "error" + : (val1 == 1) ? "warning" + : "note"; + printf("%s:%d: %s: %s\n", file, line, level, expr1); + break; + } + case ZUC_OP_TRACEPOINT: + printf("%s:%d: note: %s\n", file, line, expr1); + break; + default: + printf("%s:%d: error: ", file, line); + printf("Expected: (%s) %s (%s), actual: %ld vs %ld\n", + expr1, zuc_get_opstr(op), expr2, val1, val2); + } +} diff --git a/tools/zunitc/src/zuc_base_logger.h b/tools/zunitc/src/zuc_base_logger.h new file mode 100644 index 00000000..80b108e4 --- /dev/null +++ b/tools/zunitc/src/zuc_base_logger.h @@ -0,0 +1,38 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZUC_BASE_LOGGER_H +#define ZUC_BASE_LOGGER_H + +struct zuc_event_listener; + +/** + * Creates a new logger that outputs data to console in the default + * format. + */ +struct zuc_event_listener * +zuc_base_logger_create(void); + +#endif /* ZUC_BASE_LOGGER_H */ diff --git a/tools/zunitc/src/zuc_collector.c b/tools/zunitc/src/zuc_collector.c new file mode 100644 index 00000000..e17231cd --- /dev/null +++ b/tools/zunitc/src/zuc_collector.c @@ -0,0 +1,427 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include "zuc_collector.h" + +#include +#include +#include +#include + +#include "shared/zalloc.h" +#include "zuc_event_listener.h" +#include "zunitc/zunitc_impl.h" + +#include +#include +#include + +/** + * @file + * General handling of collecting events during testing to pass back to + * main tracking of fork()'d tests. + * + * @note implementation of zuc_process_message() is included here so that + * all child->parent IPC is in a single source file for easier maintenance + * and updating. + */ + +/** + * Internal data struct for processing. + */ +struct collector_data +{ + int *fd; /**< file descriptor to output to. */ + struct zuc_test *test; /**< current test, or NULL. */ +}; + +/** + * Stores an int32_t into the given buffer. + * + * @param ptr the buffer to store to. + * @param val the value to store. + * @return a pointer to the position in the buffer after the stored value. + */ +static char * +pack_int32(char *ptr, int32_t val); + +/** + * Stores an intptr_t into the given buffer. + * + * @param ptr the buffer to store to. + * @param val the value to store. + * @return a pointer to the position in the buffer after the stored value. + */ +static char * +pack_intptr_t(char *ptr, intptr_t val); + +/** + * Extracts a int32_t from the given buffer. + * + * @param ptr the buffer to extract from. + * @param val the value to set. + * @return a pointer to the position in the buffer after the extracted + * value. + */ +static char const * +unpack_int32(char const *ptr, int32_t *val); + +/** + * Extracts a intptr_t from the given buffer. + * + * @param ptr the buffer to extract from. + * @param val the value to set. + * @return a pointer to the position in the buffer after the extracted + * value. + */ +static char const * +unpack_intptr_t(char const *ptr, intptr_t *val); + +/** + * Extracts a length-prefixed string from the given buffer. + * + * @param ptr the buffer to extract from. + * @param str the value to set. + * @return a pointer to the position in the buffer after the extracted + * value. + */ +static char const * +unpack_string(char const *ptr, char **str); + +/** + * Extracts an event from the given buffer. + * + * @param ptr the buffer to extract from. + * @param len the length of the given buffer. + * @return an event that was packed in the buffer + */ +static struct zuc_event * +unpack_event(char const *ptr, int32_t len); + +/** + * Handles an event by either attaching it directly or sending it over IPC + * as needed. + */ +static void +store_event(struct collector_data *cdata, + enum zuc_event_type event_type, char const *file, int line, + enum zuc_fail_state state, enum zuc_check_op op, + enum zuc_check_valtype valtype, + intptr_t val1, intptr_t val2, const char *expr1, const char *expr2); + +static void +destroy(void *data); + +static void +test_started(void *data, struct zuc_test *test); + +static void +test_ended(void *data, struct zuc_test *test); + +static void +check_triggered(void *data, char const *file, int line, + enum zuc_fail_state state, enum zuc_check_op op, + enum zuc_check_valtype valtype, + intptr_t val1, intptr_t val2, + const char *expr1, const char *expr2); + +static void +collect_event(void *data, char const *file, int line, const char *expr1); + +struct zuc_event_listener * +zuc_collector_create(int *pipe_fd) +{ + struct zuc_event_listener *listener = + zalloc(sizeof(struct zuc_event_listener)); + + listener->data = zalloc(sizeof(struct collector_data)); + ((struct collector_data *)listener->data)->fd = pipe_fd; + listener->destroy = destroy; + listener->test_started = test_started; + listener->test_ended = test_ended; + listener->check_triggered = check_triggered; + listener->collect_event = collect_event; + + return listener; +} + +char * +pack_int32(char *ptr, int32_t val) +{ + memcpy(ptr, &val, sizeof(val)); + return ptr + sizeof(val); +} + +char * +pack_intptr_t(char *ptr, intptr_t val) +{ + memcpy(ptr, &val, sizeof(val)); + return ptr + sizeof(val); +} + +static char * +pack_cstr(char *ptr, intptr_t val, int len) +{ + if (val == 0) { /* a null pointer */ + ptr = pack_int32(ptr, -1); + } else { + ptr = pack_int32(ptr, len); + memcpy(ptr, (const char *)val, len); + ptr += len; + } + return ptr; +} + +void +destroy(void *data) +{ + free(data); +} + +void +test_started(void *data, struct zuc_test *test) +{ + struct collector_data *cdata = data; + cdata->test = test; +} + +void +test_ended(void *data, struct zuc_test *test) +{ + struct collector_data *cdata = data; + cdata->test = NULL; +} + +void +check_triggered(void *data, char const *file, int line, + enum zuc_fail_state state, enum zuc_check_op op, + enum zuc_check_valtype valtype, + intptr_t val1, intptr_t val2, + const char *expr1, const char *expr2) +{ + struct collector_data *cdata = data; + if (op != ZUC_OP_TRACEPOINT) + store_event(cdata, ZUC_EVENT_IMMEDIATE, file, line, state, op, + valtype, + val1, val2, expr1, expr2); +} + +void +collect_event(void *data, char const *file, int line, const char *expr1) +{ + struct collector_data *cdata = data; + store_event(cdata, ZUC_EVENT_DEFERRED, file, line, ZUC_CHECK_OK, + ZUC_OP_TRACEPOINT, ZUC_VAL_INT, + 0, 0, expr1, ""); +} + +void +store_event(struct collector_data *cdata, + enum zuc_event_type event_type, char const *file, int line, + enum zuc_fail_state state, enum zuc_check_op op, + enum zuc_check_valtype valtype, + intptr_t val1, intptr_t val2, const char *expr1, const char *expr2) +{ + struct zuc_event *event = zalloc(sizeof(*event)); + event->file = strdup(file); + event->line = line; + event->state = state; + event->op = op; + event->valtype = valtype; + event->val1 = val1; + event->val2 = val2; + if (valtype == ZUC_VAL_CSTR) { + if (val1) + event->val1 = (intptr_t)strdup((const char *)val1); + if (val2) + event->val2 = (intptr_t)strdup((const char *)val2); + } + event->expr1 = strdup(expr1); + event->expr2 = strdup(expr2); + + zuc_attach_event(cdata->test, event, event_type, false); + + if (*cdata->fd == -1) { + } else { + /* Need to pass it back */ + int sent; + int count; + int expr1_len = strlen(expr1); + int expr2_len = strlen(expr2); + int val1_len = + ((valtype == ZUC_VAL_CSTR) && val1) ? + strlen((char *)val1) : 0; + int val2_len = + ((valtype == ZUC_VAL_CSTR) && val2) ? + strlen((char *)val2) : 0; + int file_len = strlen(file); + int len = (sizeof(int32_t) * 9) + file_len + + (sizeof(intptr_t) * 2) + + ((valtype == ZUC_VAL_CSTR) ? + (sizeof(int32_t) * 2) + val1_len + val2_len : 0) + + expr1_len + expr2_len; + char *buf = zalloc(len); + + char *ptr = pack_int32(buf, len - 4); + ptr = pack_int32(ptr, event_type); + ptr = pack_int32(ptr, file_len); + memcpy(ptr, file, file_len); + ptr += file_len; + ptr = pack_int32(ptr, line); + ptr = pack_int32(ptr, state); + ptr = pack_int32(ptr, op); + ptr = pack_int32(ptr, valtype); + if (valtype == ZUC_VAL_CSTR) { + ptr = pack_cstr(ptr, val1, val1_len); + ptr = pack_cstr(ptr, val2, val2_len); + } else { + ptr = pack_intptr_t(ptr, val1); + ptr = pack_intptr_t(ptr, val2); + } + + ptr = pack_int32(ptr, expr1_len); + if (expr1_len) { + memcpy(ptr, expr1, expr1_len); + ptr += expr1_len; + } + ptr = pack_int32(ptr, expr2_len); + if (expr2_len) { + memcpy(ptr, expr2, expr2_len); + ptr += expr2_len; + } + + + sent = 0; + while (sent < len) { + count = write(*cdata->fd, buf, len); + if (count == -1) + break; + sent += count; + } + + free(buf); + } +} + +char const * +unpack_int32(char const *ptr, int32_t *val) +{ + memcpy(val, ptr, sizeof(*val)); + return ptr + sizeof(*val); +} + +char const * +unpack_intptr_t(char const *ptr, intptr_t *val) +{ + memcpy(val, ptr, sizeof(*val)); + return ptr + sizeof(*val); +} + +char const * +unpack_string(char const *ptr, char **str) +{ + int32_t len = 0; + ptr = unpack_int32(ptr, &len); + *str = zalloc(len + 1); + if (len) + memcpy(*str, ptr, len); + ptr += len; + return ptr; +} + +static char const * +unpack_cstr(char const *ptr, char **str) +{ + int32_t len = 0; + ptr = unpack_int32(ptr, &len); + if (len < 0) { + *str = NULL; + } else { + *str = zalloc(len + 1); + if (len) + memcpy(*str, ptr, len); + ptr += len; + } + return ptr; +} + +struct zuc_event * +unpack_event(char const *ptr, int32_t len) +{ + int32_t val = 0; + struct zuc_event *evt = zalloc(sizeof(*evt)); + char const *tmp = unpack_string(ptr, &evt->file); + tmp = unpack_int32(tmp, &evt->line); + + tmp = unpack_int32(tmp, &val); + evt->state = val; + tmp = unpack_int32(tmp, &val); + evt->op = val; + tmp = unpack_int32(tmp, &val); + evt->valtype = val; + + if (evt->valtype == ZUC_VAL_CSTR) { + char *ptr = NULL; + tmp = unpack_cstr(tmp, &ptr); + evt->val1 = (intptr_t)ptr; + tmp = unpack_cstr(tmp, &ptr); + evt->val2 = (intptr_t)ptr; + } else { + tmp = unpack_intptr_t(tmp, &evt->val1); + tmp = unpack_intptr_t(tmp, &evt->val2); + } + + tmp = unpack_string(tmp, &evt->expr1); + tmp = unpack_string(tmp, &evt->expr2); + + return evt; +} + +int +zuc_process_message(struct zuc_test *test, int fd) +{ + char buf[4] = {}; + int got = read(fd, buf, 4); + if (got == 4) { + enum zuc_event_type event_type = ZUC_EVENT_IMMEDIATE; + int32_t val = 0; + int32_t len = 0; + const char *tmp = NULL; + char *raw = NULL; + unpack_int32(buf, &len); + raw = zalloc(len); + got = read(fd, raw, len); + + tmp = unpack_int32(raw, &val); + event_type = val; + + struct zuc_event *evt = unpack_event(tmp, len - (tmp - raw)); + zuc_attach_event(test, evt, event_type, true); + free(raw); + } + return got; +} diff --git a/tools/zunitc/src/zuc_collector.h b/tools/zunitc/src/zuc_collector.h new file mode 100644 index 00000000..f21e670c --- /dev/null +++ b/tools/zunitc/src/zuc_collector.h @@ -0,0 +1,58 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZUC_COLLECTOR_H +#define ZUC_COLLECTOR_H + +struct zuc_event_listener; +struct zuc_test; + +/** + * Creates a new instance of an even collector that will attatch events + * to the current test directly or via connection from child to parent. + * + * @param pipe_fd pointer to the file descriptor to use for communication if + * needed. If the value is -1 the events will be attached directly to the + * current test. Otherwise events will be passed back via IPC over this + * pipe with the expectation that the payload will be handled in the parent + * process via zuc_process_message(). + * @return a new collector intance. + * @see zuc_process_message() + */ +struct zuc_event_listener * +zuc_collector_create(int *pipe_fd); + +/** + * Reads events from the given pipe and processes them. + * + * @param test the currently active test to attache events for. + * @param pipe_fd the file descriptor of the pipe to read from. + * @return a positive value if a message was received, 0 if the end has + * been reached and -1 if an error has occurred. + */ +int +zuc_process_message(struct zuc_test *test, int pipe_fd); + +#endif /* ZUC_COLLECTOR_H */ diff --git a/tools/zunitc/src/zuc_context.h b/tools/zunitc/src/zuc_context.h new file mode 100644 index 00000000..609f34bd --- /dev/null +++ b/tools/zunitc/src/zuc_context.h @@ -0,0 +1,58 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZUC_CONTEXT_H +#define ZUC_CONTEXT_H + +#include "zuc_types.h" + +struct zuc_slinked; + +/** + * Internal context for processing. + * Collecting data members here minimizes use of globals. + */ +struct zuc_context { + int case_count; + struct zuc_case **cases; + + bool fatal; + int repeat; + int random; + unsigned int seed; + bool spawn; + bool break_on_failure; + bool output_tap; + bool output_junit; + int fds[2]; + char *filter; + + struct zuc_slinked *listeners; + + struct zuc_case *curr_case; + struct zuc_test *curr_test; +}; + +#endif /* ZUC_CONTEXT_H */ diff --git a/tools/zunitc/src/zuc_event.h b/tools/zunitc/src/zuc_event.h new file mode 100644 index 00000000..ccb2f7b6 --- /dev/null +++ b/tools/zunitc/src/zuc_event.h @@ -0,0 +1,86 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZUC_EVENT_H +#define ZUC_EVENT_H + +#include + +#include "zunitc/zunitc_impl.h" + +/** + * + */ +enum zuc_event_type +{ + ZUC_EVENT_IMMEDIATE, + ZUC_EVENT_DEFERRED +}; + +/** + * Status enum for posted events. + */ +enum zuc_fail_state +{ + ZUC_CHECK_OK, /**< no problem. */ + ZUC_CHECK_SKIP, /**< runtime skip directive encountered. */ + ZUC_CHECK_FAIL, /**< non-fatal check fails. */ + ZUC_CHECK_FATAL, /**< fatal assertion/check fails. */ + ZUC_CHECK_ERROR /**< internal level problem. */ +}; + +/** + * Record of an event that occurs during testing such as assert macro + * failures. + */ +struct zuc_event +{ + char *file; + int32_t line; + enum zuc_fail_state state; + enum zuc_check_op op; + enum zuc_check_valtype valtype; + intptr_t val1; + intptr_t val2; + char *expr1; + char *expr2; + + struct zuc_event *next; +}; + +/** + * Attaches an event to the specified test. + * + * @param test the test to attach to. + * @param event the event to attach. + * @param event_type of event (immediate or deferred) to attach. + * @param transferred true if the event has been processed elsewhere and + * is being transferred for storage, false otherwise. + */ +void +zuc_attach_event(struct zuc_test *test, struct zuc_event *event, + enum zuc_event_type event_type, bool transferred); + +#endif /* ZUC_EVENT_H */ diff --git a/tools/zunitc/src/zuc_event_listener.h b/tools/zunitc/src/zuc_event_listener.h new file mode 100644 index 00000000..41c5fbdf --- /dev/null +++ b/tools/zunitc/src/zuc_event_listener.h @@ -0,0 +1,174 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZUC_EVENT_HANDLER_H +#define ZUC_EVENT_HANDLER_H + +#include + +#include "zuc_context.h" +#include "zuc_event.h" + +struct zuc_test; +struct zuc_case; + +/** + * Interface to allow components to process testing events as they occur. + * + * Event listeners that will stream output as testing progresses are often + * named "*_logger" whereas those that produce their output upon test run + * completion are named "*_reporter". + */ +struct zuc_event_listener { + /** + * User data pointer. + */ + void *data; + + /** + * Destructor. + * @param data the user data associated with this instance. + */ + void (*destroy)(void *data); + + /** + * Handler for simple pre-run state. + * + * @param pass_count total number of expected test passes. + * @param pass_num current pass iteration number. + * @param seed random seed being used, or 0 for no randomization. + * @param filter filter string used for tests, or NULL/blank for none. + */ + void (*pre_run)(void *data, + int pass_count, + int pass_num, + int seed, + const char *filter); + + /** + * Handler for test runs starting. + * + * @param data the user data associated with this instance. + * @param live_case_count number of active test cases in this run. + * @param live_test_count number of active tests in this run. + * @param disabled_count number of disabled tests in this run. + */ + void (*run_started)(void *data, + int live_case_count, + int live_test_count, + int disabled_count); + + /** + * Handler for test runs ending. + * + * @param data the user data associated with this instance. + */ + void (*run_ended)(void *data, + int case_count, + struct zuc_case** cases, + int live_case_count, + int live_test_count, + int total_passed, + int total_failed, + int total_disabled, + long total_elapsed); + + /** + * Handler for test case starting. + * + * @param data the user data associated with this instance. + */ + void (*case_started)(void *data, + struct zuc_case *test_case, + int live_test_count, + int disabled_count); + + /** + * Handler for test case ending. + * + * @param data the user data associated with this instance. + */ + void (*case_ended)(void *data, + struct zuc_case *test_case); + + /** + * Handler for test starting. + * + * @param data the user data associated with this instance. + */ + void (*test_started)(void *data, + struct zuc_test *test); + + /** + * Handler for test ending. + * + * @param data the user data associated with this instance. + */ + void (*test_ended)(void *data, + struct zuc_test *test); + + /** + * Handler for disabled test notification. + * + * @param data the user data associated with this instance. + */ + void (*test_disabled)(void *data, + struct zuc_test *test); + + /** + * Handler for check/assertion fired due to failure, warning, etc. + * + * @param data the user data associated with this instance. + */ + void (*check_triggered)(void *data, + char const *file, + int line, + enum zuc_fail_state state, + enum zuc_check_op op, + enum zuc_check_valtype valtype, + intptr_t val1, + intptr_t val2, + const char *expr1, + const char *expr2); + + /** + * Handler for tracepoints and such that may be displayed later. + * + * @param data the user data associated with this instance. + */ + void (*collect_event)(void *data, + char const *file, + int line, + const char *expr1); +}; + +/** + * Registers an event listener instance to be called. + */ +void +zuc_add_event_listener(struct zuc_event_listener *event_listener); + + +#endif /* ZUC_EVENT_HANDLER_H */ diff --git a/tools/zunitc/src/zuc_types.h b/tools/zunitc/src/zuc_types.h new file mode 100644 index 00000000..4ed93427 --- /dev/null +++ b/tools/zunitc/src/zuc_types.h @@ -0,0 +1,80 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef ZUC_TYPES_H +#define ZUC_TYPES_H + +#include "zunitc/zunitc_impl.h" + +struct zuc_case; + +/** + * Represents a specific test. + */ +struct zuc_test +{ + int order; + struct zuc_case *test_case; + zucimpl_test_fn fn; + zucimpl_test_fn_f fn_f; + char *name; + int disabled; + int skipped; + int failed; + int fatal; + long elapsed; + struct zuc_event *events; + struct zuc_event *deferred; +}; + +/** + * Represents a test case that can hold a collection of tests. + */ +struct zuc_case +{ + int order; + char *name; + const struct zuc_fixture* fxt; + int disabled; + int skipped; + int failed; + int fatal; + int passed; + long elapsed; + int test_count; + struct zuc_test **tests; +}; + +/** + * Returns a human-readable version of the comparison opcode. + * + * @param op the opcode to get a string version of. + * @return a human-readable string of the opcode. + * (This value should not be freed) + */ +const char * +zuc_get_opstr(enum zuc_check_op op); + +#endif /* ZUC_TYPES_H */ diff --git a/tools/zunitc/src/zunitc_impl.c b/tools/zunitc/src/zunitc_impl.c new file mode 100644 index 00000000..b8ab0b2c --- /dev/null +++ b/tools/zunitc/src/zunitc_impl.c @@ -0,0 +1,1572 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "zunitc/zunitc_impl.h" +#include "zunitc/zunitc.h" + +#include "zuc_base_logger.h" +#include "zuc_collector.h" +#include "zuc_context.h" +#include "zuc_event_listener.h" + +#include "shared/config-parser.h" +#include "shared/helpers.h" +#include "shared/zalloc.h" + +/* + * If CLOCK_MONOTONIC is present on the system it will give us reliable + * results under certain edge conditions that normally require manual + * admin actions to trigger. If not, CLOCK_REALTIME is a reasonable + * fallback. + */ +#if _POSIX_MONOTONIC_CLOCK +static const clockid_t TARGET_TIMER = CLOCK_MONOTONIC; +#else +static const clockid_t TARGET_TIMER = CLOCK_REALTIME; +#endif + +static char const DISABLED_PREFIX[] = "DISABLED_"; + +#define MS_PER_SEC 1000L +#define NANO_PER_MS 1000000L + +/** + * Simple single-linked list structure. + */ +struct zuc_slinked { + void *data; + struct zuc_slinked *next; +}; + +static struct zuc_context g_ctx = { + .case_count = 0, + .cases = NULL, + + .fatal = false, + .repeat = 0, + .random = 0, + .spawn = true, + .break_on_failure = false, + .fds = {-1, -1}, + + .listeners = NULL, + + .curr_case = NULL, + .curr_test = NULL, +}; + +static char *g_progname = NULL; +static char *g_progbasename = NULL; + +typedef int (*comp_pred2)(intptr_t lhs, intptr_t rhs); + +static bool +test_has_skip(struct zuc_test *test) +{ + return test->skipped; +} + +static bool +test_has_failure(struct zuc_test *test) +{ + return test->fatal || test->failed; +} + +bool +zuc_has_skip(void) +{ + return g_ctx.curr_test ? + test_has_skip(g_ctx.curr_test) : false; +} + +bool +zuc_has_failure(void) +{ + return g_ctx.curr_test ? + test_has_failure(g_ctx.curr_test) : false; +} + +void +zuc_set_filter(const char *filter) +{ + g_ctx.filter = strdup(filter); +} + +void +zuc_set_repeat(int repeat) +{ + g_ctx.repeat = repeat; +} + +void +zuc_set_random(int random) +{ + g_ctx.random = random; +} + +void +zuc_set_spawn(bool spawn) +{ + g_ctx.spawn = spawn; +} + +void +zuc_set_break_on_failure(bool break_on_failure) +{ + g_ctx.break_on_failure = break_on_failure; +} + +const char * +zuc_get_program_name(void) +{ + return g_progname; +} + +const char * +zuc_get_program_basename(void) +{ + return g_progbasename; +} + +static struct zuc_test * +create_test(int order, zucimpl_test_fn fn, zucimpl_test_fn_f fn_f, + char const *case_name, char const *test_name, + struct zuc_case *parent) +{ + struct zuc_test *test = zalloc(sizeof(struct zuc_test)); + ZUC_ASSERTG_NOT_NULL(test, out); + test->order = order; + test->fn = fn; + test->fn_f = fn_f; + test->name = strdup(test_name); + if ((!fn && !fn_f) || + (strncmp(DISABLED_PREFIX, + test_name, sizeof(DISABLED_PREFIX) - 1) == 0)) + test->disabled = 1; + + test->test_case = parent; + +out: + return test; +} + +static int +compare_regs(const void *lhs, const void *rhs) +{ + int rc = strcmp((*(struct zuc_registration **)lhs)->tcase, + (*(struct zuc_registration **)rhs)->tcase); + if (rc == 0) + rc = strcmp((*(struct zuc_registration **)lhs)->test, + (*(struct zuc_registration **)rhs)->test); + + return rc; +} + +/* gcc-specific markers for auto test case registration: */ +extern const struct zuc_registration __start_zuc_tsect; +extern const struct zuc_registration __stop_zuc_tsect; + +static void +register_tests(void) +{ + size_t case_count = 0; + size_t count = &__stop_zuc_tsect - &__start_zuc_tsect; + size_t i; + int idx = 0; + const char *last_name = NULL; + void **array = zalloc(sizeof(void *) * count); + ZUC_ASSERT_NOT_NULL(array); + for (i = 0; i < count; ++i) + array[i] = (void *)(&__start_zuc_tsect + i); + + qsort(array, count, sizeof(array[0]), compare_regs); + + /* Count transitions to get number of test cases. */ + last_name = NULL; + for (i = 0; i < count; ++i) { + const struct zuc_registration *reg = + (const struct zuc_registration *)array[i]; + if (!last_name || (strcmp(last_name, reg->tcase))) { + last_name = reg->tcase; + case_count++; + } + } + + /* Create test case data items. */ + struct zuc_case **case_array = + zalloc(sizeof(struct zuc_case *) * case_count); + ZUC_ASSERT_NOT_NULL(case_array); + struct zuc_case *last_case = NULL; + size_t case_num = 0; + for (i = 0; i < count; ++i) { + const struct zuc_registration *reg = + (const struct zuc_registration *)array[i]; + if (!last_case || (strcmp(last_case->name, reg->tcase))) { + last_case = zalloc(sizeof(struct zuc_case)); + ZUC_ASSERT_NOT_NULL(last_case); + last_case->order = count; + last_case->name = strdup(reg->tcase); + last_case->fxt = reg->fxt; + last_case->test_count = i; + if (case_num > 0) { + int tcount = i + - case_array[case_num - 1]->test_count; + case_array[case_num - 1]->test_count = tcount; + } + case_array[case_num++] = last_case; + } + } + case_array[case_count - 1]->test_count = count + - case_array[case_count - 1]->test_count; + + /* Reserve space for tests per test case. */ + for (case_num = 0; case_num < case_count; ++case_num) { + case_array[case_num]->tests = + zalloc(case_array[case_num]->test_count + * sizeof(struct zuc_test *)); + ZUC_ASSERT_NOT_NULL(case_array[case_num]->tests); + } + + last_name = NULL; + case_num = -1; + for (i = 0; i < count; ++i) { + const struct zuc_registration *reg = + (const struct zuc_registration *)array[i]; + int order = count - (1 + (reg - &__start_zuc_tsect)); + + if (!last_name || (strcmp(last_name, reg->tcase))) { + last_name = reg->tcase; + case_num++; + idx = 0; + } + if (order < case_array[case_num]->order) + case_array[case_num]->order = order; + case_array[case_num]->tests[idx] = + create_test(order, reg->fn, reg->fn_f, + reg->tcase, reg->test, + case_array[case_num]); + + if (case_array[case_num]->fxt != reg->fxt) + printf("%s:%d: error: Mismatched fixtures for '%s'\n", + __FILE__, __LINE__, case_array[case_num]->name); + + idx++; + } + free(array); + + g_ctx.case_count = case_count; + g_ctx.cases = case_array; +} + +static int +compare_case_order(const void *lhs, const void *rhs) +{ + return (*(struct zuc_case **)lhs)->order + - (*(struct zuc_case **)rhs)->order; +} + +static int +compare_test_order(const void *lhs, const void *rhs) +{ + return (*(struct zuc_test **)lhs)->order + - (*(struct zuc_test **)rhs)->order; +} + +static void +order_cases(int count, struct zuc_case **cases) +{ + int i; + qsort(cases, count, sizeof(*cases), compare_case_order); + for (i = 0; i < count; ++i) { + qsort(cases[i]->tests, cases[i]->test_count, + sizeof(*cases[i]->tests), compare_test_order); + } +} + +static void +free_events(struct zuc_event **events) +{ + struct zuc_event *evt = *events; + *events = NULL; + while (evt) { + struct zuc_event *old = evt; + evt = evt->next; + free(old->file); + if (old->valtype == ZUC_VAL_CSTR) { + free((void *)old->val1); + free((void *)old->val2); + } + free(old->expr1); + free(old->expr2); + free(old); + } +} + +static void +free_test(struct zuc_test *test) +{ + free(test->name); + free_events(&test->events); + free_events(&test->deferred); + free(test); +} + +static void +free_test_case(struct zuc_case *test_case) +{ + int i; + free(test_case->name); + for (i = test_case->test_count - 1; i >= 0; --i) { + free_test(test_case->tests[i]); + test_case->tests[i] = NULL; + } + free(test_case->tests); + free(test_case); +} + +/** + * A very simple matching that is compatible with the algorithm used in + * Google Test. + * + * @param wildcard sequence of '?', '*' or normal characters to match. + * @param str string to check for matching. + * @return true if the wildcard matches the entire string, false otherwise. + */ +static bool +wildcard_matches(char const *wildcard, char const *str) +{ + switch (*wildcard) { + case '\0': + return !*str; + case '?': + return *str && wildcard_matches(wildcard + 1, str + 1); + case '*': + return (*str && wildcard_matches(wildcard, str + 1)) + || wildcard_matches(wildcard + 1, str); + default: + return (*wildcard == *str) + && wildcard_matches(wildcard + 1, str + 1); + }; +} + +static char** +segment_str(char *str) +{ + int count = 1; + char **parts = NULL; + char *saved = NULL; + char *tok = NULL; + int i = 0; + for (i = 0; str[i]; ++i) + if (str[i] == ':') + count++; + parts = zalloc(sizeof(char*) * (count + 1)); + ZUC_ASSERTG_NOT_NULL(parts, out); + tok = strtok_r(str, ":", &saved); + i = 0; + parts[i++] = tok; + while (tok) { + tok = strtok_r(NULL, ":", &saved); + parts[i++] = tok; + } +out: + return parts; +} + +static void +filter_cases(int *count, struct zuc_case **cases, char const *filter) +{ + int i = 0; + int j = 0; + int num_pos = 0; + int negative = -1; + char *buf = strdup(filter); + char **parts = segment_str(buf); + + for (i = 0; parts[i]; ++i) { + if (parts[i][0] == '-') { + parts[i]++; + negative = i; + break; + } + num_pos++; + } + + for (i = 0; i < *count; ++i) { + /* Walk backwards for easier pruning. */ + for (j = cases[i]->test_count - 1; j >= 0; --j) { + int x; + bool keep = num_pos == 0; + char *name = NULL; + struct zuc_test *test = cases[i]->tests[j]; + if (asprintf(&name, "%s.%s", cases[i]->name, + test->name) < 0) { + printf("%s:%d: error: %d\n", __FILE__, __LINE__, + errno); + exit(EXIT_FAILURE); + } + for (x = 0; (x < num_pos) && !keep; ++x) + keep = wildcard_matches(parts[x], name); + if (keep && (negative >= 0)) + for (x = negative; parts[x] && keep; ++x) + keep &= !wildcard_matches(parts[x], + name); + if (!keep) { + int w; + free_test(test); + for (w = j + 1; w < cases[i]->test_count; w++) + cases[i]->tests[w - 1] = + cases[i]->tests[w]; + cases[i]->test_count--; + } + + free(name); + } + } + free(parts); + parts = NULL; + free(buf); + buf = NULL; + + /* Prune any cases with no more tests. */ + for (i = *count - 1; i >= 0; --i) { + if (cases[i]->test_count < 1) { + free_test_case(cases[i]); + for (j = i + 1; j < *count; ++j) + cases[j - 1] = cases[j]; + cases[*count - 1] = NULL; + (*count)--; + } + } +} + +static unsigned int +get_seed_from_time(void) +{ + time_t sec = time(NULL); + unsigned int seed = (unsigned int) sec % 100000; + if (seed < 2) + seed = 2; + + return seed; +} + +static void +initialize(void) +{ + static bool init = false; + if (init) + return; + + init = true; + register_tests(); + if (g_ctx.fatal) + return; + + if (g_ctx.random > 1) { + g_ctx.seed = g_ctx.random; + } else if (g_ctx.random == 1) { + g_ctx.seed = get_seed_from_time(); + } + + if (g_ctx.case_count) { + order_cases(g_ctx.case_count, g_ctx.cases); + if (g_ctx.filter && g_ctx.filter[0]) + filter_cases(&g_ctx.case_count, g_ctx.cases, + g_ctx.filter); + } +} + +int +zuc_initialize(int *argc, char *argv[], bool *help_flagged) +{ + int rc = EXIT_FAILURE; + int opt_help = 0; + int opt_nofork = 0; + int opt_list = 0; + int opt_repeat = 0; + int opt_random = 0; + int opt_break_on_failure = 0; + char *opt_filter = NULL; + + char *help_param = NULL; + int argc_in = *argc; + + const struct weston_option options[] = { + { WESTON_OPTION_BOOLEAN, "zuc-nofork", 0, &opt_nofork }, + { WESTON_OPTION_BOOLEAN, "zuc-list-tests", 0, &opt_list }, + { WESTON_OPTION_INTEGER, "zuc-repeat", 0, &opt_repeat }, + { WESTON_OPTION_INTEGER, "zuc-random", 0, &opt_random }, + { WESTON_OPTION_BOOLEAN, "zuc-break-on-failure", 0, + &opt_break_on_failure }, + { WESTON_OPTION_STRING, "zuc-filter", 0, &opt_filter }, + }; + + /* + *If a test binary is linked to our libzunitcmain it might want + * to access the program 'name' from argv[0] + */ + free(g_progname); + g_progname = NULL; + free(g_progbasename); + g_progbasename = NULL; + if ((*argc > 0) && argv) { + char *path = NULL; + char *base = NULL; + + g_progname = strdup(argv[0]); + + /* basename() might modify the input, so needs a writeable + * string. + * It also may return a statically allocated buffer subject to + * being overwritten so needs to be dup'd. + */ + path = strdup(g_progname); + base = basename(path); + g_progbasename = strdup(base); + free(path); + } else { + g_progname = strdup(""); + printf("%s:%d: warning: No valid argv[0] for initialization\n", + __FILE__, __LINE__); + } + + + initialize(); + if (g_ctx.fatal) + return EXIT_FAILURE; + + if (help_flagged) + *help_flagged = false; + + { + /* Help param will be a special case and need restoring. */ + int i = 0; + char **argv_in = NULL; + const struct weston_option help_options[] = { + { WESTON_OPTION_BOOLEAN, "help", 'h', &opt_help }, + }; + argv_in = zalloc(sizeof(char *) * argc_in); + if (!argv_in) { + printf("%s:%d: error: alloc failed.\n", + __FILE__, __LINE__); + return EXIT_FAILURE; + } + for (i = 0; i < argc_in; ++i) + argv_in[i] = argv[i]; + + parse_options(help_options, ARRAY_LENGTH(help_options), + argc, argv); + if (*argc < argc_in) { + for (i = 1; (i < argc_in) && !help_param; ++i) { + bool found = false; + int j = 0; + for (j = 0; (j < *argc) && !found; ++j) + found = (argv[j] == argv_in[i]); + + if (!found) + help_param = argv_in[i]; + } + } + free(argv_in); + } + + parse_options(options, ARRAY_LENGTH(options), argc, argv); + + if (help_param && (*argc < argc_in)) + argv[(*argc)++] = help_param; + + if (opt_filter) { + zuc_set_filter(opt_filter); + free(opt_filter); + } + + if (opt_help) { + printf("Usage: %s [OPTIONS]\n" + " --zuc-break-on-failure\n" + " --zuc-filter=FILTER\n" + " --zuc-list-tests\n" + " --zuc-nofork\n" + " --zuc-random=N [0|1|]\n" + " --zuc-repeat=N\n" + " --help\n", + argv[0]); + if (help_flagged) + *help_flagged = true; + rc = EXIT_SUCCESS; + } else if (opt_list) { + zuc_list_tests(); + rc = EXIT_FAILURE; + } else { + zuc_set_repeat(opt_repeat); + zuc_set_random(opt_random); + zuc_set_spawn(!opt_nofork); + zuc_set_break_on_failure(opt_break_on_failure); + rc = EXIT_SUCCESS; + } + + return rc; +} + +static void +dispatch_pre_run(struct zuc_context *ctx, int pass_count, int pass_num, + int seed, const char *filter) +{ + struct zuc_slinked *curr; + for (curr = ctx->listeners; curr; curr = curr->next) { + struct zuc_event_listener *listener = curr->data; + if (listener->pre_run) + listener->pre_run(listener->data, + pass_count, + pass_num, + seed, + filter); + } +} + +static void +dispatch_run_started(struct zuc_context *ctx, int live_case_count, + int live_test_count, int disabled_count) +{ + struct zuc_slinked *curr; + for (curr = ctx->listeners; curr; curr = curr->next) { + struct zuc_event_listener *listener = curr->data; + if (listener->run_started) + listener->run_started(listener->data, + live_case_count, + live_test_count, + disabled_count); + } +} + +static void +dispatch_run_ended(struct zuc_context *ctx, + int live_case_count, int live_test_count, int total_passed, + int total_failed, int total_disabled, long total_elapsed) +{ + struct zuc_slinked *curr; + for (curr = ctx->listeners; curr; curr = curr->next) { + struct zuc_event_listener *listener = curr->data; + if (listener->run_ended) + listener->run_ended(listener->data, + ctx->case_count, + ctx->cases, + live_case_count, + live_test_count, + total_passed, + total_failed, + total_disabled, + total_elapsed); + } +} + +static void +dispatch_case_started(struct zuc_context *ctx,struct zuc_case *test_case, + int live_test_count, int disabled_count) +{ + struct zuc_slinked *curr; + for (curr = ctx->listeners; curr; curr = curr->next) { + struct zuc_event_listener *listener = curr->data; + if (listener->case_started) + listener->case_started(listener->data, + test_case, + live_test_count, + disabled_count); + } +} + +static void +dispatch_case_ended(struct zuc_context *ctx, struct zuc_case *test_case) +{ + struct zuc_slinked *curr; + for (curr = ctx->listeners; curr; curr = curr->next) { + struct zuc_event_listener *listener = curr->data; + if (listener->case_ended) + listener->case_ended(listener->data, test_case); + } +} + +static void +dispatch_test_started(struct zuc_context *ctx, struct zuc_test *test) +{ + struct zuc_slinked *curr; + for (curr = ctx->listeners; curr; curr = curr->next) { + struct zuc_event_listener *listener = curr->data; + if (listener->test_started) + listener->test_started(listener->data, test); + } +} + +static void +dispatch_test_ended(struct zuc_context *ctx, struct zuc_test *test) +{ + struct zuc_slinked *curr; + for (curr = ctx->listeners; curr; curr = curr->next) { + struct zuc_event_listener *listener = curr->data; + if (listener->test_ended) + listener->test_ended(listener->data, test); + } +} + +static void +dispatch_test_disabled(struct zuc_context *ctx, struct zuc_test *test) +{ + struct zuc_slinked *curr; + for (curr = ctx->listeners; curr; curr = curr->next) { + struct zuc_event_listener *listener = curr->data; + if (listener->test_disabled) + listener->test_disabled(listener->data, test); + } +} + +static void +dispatch_check_triggered(struct zuc_context *ctx, char const *file, int line, + enum zuc_fail_state state, enum zuc_check_op op, + enum zuc_check_valtype valtype, + intptr_t val1, intptr_t val2, + const char *expr1, const char *expr2) +{ + struct zuc_slinked *curr; + for (curr = ctx->listeners; curr; curr = curr->next) { + struct zuc_event_listener *listener = curr->data; + if (listener->check_triggered) + listener->check_triggered(listener->data, + file, line, + state, op, valtype, + val1, val2, + expr1, expr2); + } +} + +static void +dispatch_collect_event(struct zuc_context *ctx, char const *file, int line, + const char *expr1) +{ + struct zuc_slinked *curr; + for (curr = ctx->listeners; curr; curr = curr->next) { + struct zuc_event_listener *listener = curr->data; + if (listener->collect_event) + listener->collect_event(listener->data, + file, line, expr1); + } +} + +static void +migrate_deferred_events(struct zuc_test *test, bool transferred) +{ + struct zuc_event *evt = test->deferred; + if (!evt) + return; + + test->deferred = NULL; + if (test->events) { + struct zuc_event *old = test->events; + while (old->next) + old = old->next; + old->next = evt; + } else { + test->events = evt; + } + while (evt && !transferred) { + dispatch_check_triggered(&g_ctx, + evt->file, evt->line, + evt->state, evt->op, + evt->valtype, + evt->val1, evt->val2, + evt->expr1, evt->expr2); + evt = evt->next; + } +} + +static void +mark_single_failed(struct zuc_test *test, enum zuc_fail_state state) +{ + switch (state) { + case ZUC_CHECK_OK: + /* no internal state to change */ + break; + case ZUC_CHECK_SKIP: + if (test) + test->skipped = 1; + break; + case ZUC_CHECK_FAIL: + if (test) + test->failed = 1; + break; + case ZUC_CHECK_ERROR: + case ZUC_CHECK_FATAL: + if (test) + test->fatal = 1; + break; + } + + if (g_ctx.break_on_failure) + raise(SIGABRT); + +} + +static void +mark_failed(struct zuc_test *test, enum zuc_fail_state state) +{ + if (!test && g_ctx.curr_test) + test = g_ctx.curr_test; + + if (test) { + mark_single_failed(test, state); + } else if (g_ctx.curr_case) { + /* In setup or tear-down of test suite */ + int i; + for (i = 0; i < g_ctx.curr_case->test_count; ++i) + mark_single_failed(g_ctx.curr_case->tests[i], state); + } + if ((state == ZUC_CHECK_FATAL) || (state == ZUC_CHECK_ERROR)) + g_ctx.fatal = true; +} + +void +zuc_attach_event(struct zuc_test *test, struct zuc_event *event, + enum zuc_event_type event_type, bool transferred) +{ + if (!test) { + /* + * consider adding events directly to the case. + * would be for use during per-suite setup and teardown. + */ + printf("%s:%d: error: No current test.\n", __FILE__, __LINE__); + } else if (event_type == ZUC_EVENT_DEFERRED) { + if (test->deferred) { + struct zuc_event *curr = test->deferred; + while (curr->next) + curr = curr->next; + curr->next = event; + } else { + test->deferred = event; + } + } else { + if (test) + migrate_deferred_events(test, transferred); + + if (test->events) { + struct zuc_event *curr = test->events; + while (curr->next) + curr = curr->next; + curr->next = event; + } else { + test->events = event; + } + mark_failed(test, event->state); + } +} + +void +zuc_add_event_listener(struct zuc_event_listener *event_listener) +{ + if (!event_listener) /* ensure null entries are not added */ + return; + + if (!g_ctx.listeners) { + g_ctx.listeners = zalloc(sizeof(struct zuc_slinked)); + ZUC_ASSERT_NOT_NULL(g_ctx.listeners); + g_ctx.listeners->data = event_listener; + } else { + struct zuc_slinked *curr = g_ctx.listeners; + while (curr->next) + curr = curr->next; + curr->next = zalloc(sizeof(struct zuc_slinked)); + ZUC_ASSERT_NOT_NULL(curr->next); + curr->next->data = event_listener; + } +} + + +void +zuc_cleanup(void) +{ + int i; + + free(g_ctx.filter); + g_ctx.filter = 0; + for (i = 0; i < 2; ++i) + if (g_ctx.fds[i] != -1) { + close(g_ctx.fds[i]); + g_ctx.fds[i] = -1; + } + + if (g_ctx.listeners) { + struct zuc_slinked *curr = g_ctx.listeners; + while (curr) { + struct zuc_slinked *old = curr; + struct zuc_event_listener *listener = curr->data; + if (listener->destroy) + listener->destroy(listener->data); + free(listener); + curr = curr->next; + free(old); + } + g_ctx.listeners = NULL; + } + + for (i = g_ctx.case_count - 1; i >= 0; --i) { + free_test_case(g_ctx.cases[i]); + g_ctx.cases[i] = NULL; + } + free(g_ctx.cases); + g_ctx.cases = NULL; + + free(g_progname); + g_progname = NULL; + free(g_progbasename); + g_progbasename = NULL; +} + +static void +shuffle_cases(int count, struct zuc_case **cases, + unsigned int seed) +{ + int i; + unsigned int rseed = seed; + for (i = 0; i < count; ++i) { + int j; + for (j = cases[i]->test_count - 1; j > 0 ; --j) { + int val = rand_r(&rseed); + int b = ((val / (double)RAND_MAX) * j + 0.5); + if (j != b) { + struct zuc_test *tmp = cases[i]->tests[j]; + cases[i]->tests[j] = cases[i]->tests[b]; + cases[i]->tests[b] = tmp; + } + } + } + + for (i = count - 1; i > 0; --i) { + int val = rand_r(&rseed); + int j = ((val / (double)RAND_MAX) * i + 0.5); + + if (i != j) { + struct zuc_case *tmp = cases[i]; + cases[i] = cases[j]; + cases[j] = tmp; + } + } +} + +void +zuc_list_tests(void) +{ + int i; + int j; + initialize(); + if (g_ctx.fatal) + return; + for (i = 0; i < g_ctx.case_count; ++i) { + printf("%s.\n", g_ctx.cases[i]->name); + for (j = 0; j < g_ctx.cases[i]->test_count; ++j) { + printf(" %s\n", g_ctx.cases[i]->tests[j]->name); + } + } +} + +static void +spawn_test(struct zuc_test *test, void *test_data, + void (*cleanup_fn)(void *data), void *cleanup_data) +{ + pid_t pid = -1; + + if (!test || (!test->fn && !test->fn_f)) + return; + + if (pipe2(g_ctx.fds, O_CLOEXEC)) { + printf("%s:%d: error: Unable to create pipe: %d\n", + __FILE__, __LINE__, errno); + mark_failed(test, ZUC_CHECK_ERROR); + return; + } + + fflush(NULL); /* important. avoid duplication of output */ + pid = fork(); + switch (pid) { + case -1: /* Error forking */ + printf("%s:%d: error: Problem with fork: %d\n", + __FILE__, __LINE__, errno); + mark_failed(test, ZUC_CHECK_ERROR); + close(g_ctx.fds[0]); + g_ctx.fds[0] = -1; + close(g_ctx.fds[1]); + g_ctx.fds[1] = -1; + break; + case 0: { /* child */ + int rc = EXIT_SUCCESS; + close(g_ctx.fds[0]); + g_ctx.fds[0] = -1; + + if (test->fn_f) + test->fn_f(test_data); + else + test->fn(); + + if (test_has_failure(test)) + rc = EXIT_FAILURE; + else if (test_has_skip(test)) + rc = ZUC_EXIT_SKIP; + + /* Avoid confusing memory tools like valgrind */ + if (cleanup_fn) + cleanup_fn(cleanup_data); + + zuc_cleanup(); + exit(rc); + break; + } + default: { /* parent */ + ssize_t rc = 0; + siginfo_t info = {}; + + close(g_ctx.fds[1]); + g_ctx.fds[1] = -1; + + do { + rc = zuc_process_message(g_ctx.curr_test, + g_ctx.fds[0]); + } while (rc > 0); + close(g_ctx.fds[0]); + g_ctx.fds[0] = -1; + + if (waitid(P_ALL, 0, &info, WEXITED)) { + printf("%s:%d: error: waitid failed. (%d)\n", + __FILE__, __LINE__, errno); + mark_failed(test, ZUC_CHECK_ERROR); + } else { + switch (info.si_code) { + case CLD_EXITED: { + int exit_code = info.si_status; + switch(exit_code) { + case EXIT_SUCCESS: + break; + case ZUC_EXIT_SKIP: + if (!test_has_skip(g_ctx.curr_test) && + !test_has_failure(g_ctx.curr_test)) + ZUC_SKIP("Child exited SKIP"); + break; + default: + /* unexpected failure */ + if (!test_has_failure(g_ctx.curr_test)) + ZUC_ASSERT_EQ(0, exit_code); + } + break; + } + case CLD_KILLED: + case CLD_DUMPED: + printf("%s:%d: error: signaled: %d\n", + __FILE__, __LINE__, info.si_status); + mark_failed(test, ZUC_CHECK_ERROR); + break; + } + } + } + } +} + +static void +run_single_test(struct zuc_test *test,const struct zuc_fixture *fxt, + void *case_data, bool spawn) +{ + long elapsed = 0; + struct timespec begin; + struct timespec end; + void *test_data = NULL; + void *cleanup_data = NULL; + void (*cleanup_fn)(void *data) = NULL; + memset(&begin, 0, sizeof(begin)); + memset(&end, 0, sizeof(end)); + + g_ctx.curr_test = test; + dispatch_test_started(&g_ctx, test); + + cleanup_fn = fxt ? fxt->tear_down : NULL; + cleanup_data = NULL; + + if (fxt && fxt->set_up) { + test_data = fxt->set_up(case_data); + cleanup_data = test_data; + } else { + test_data = case_data; + } + + clock_gettime(TARGET_TIMER, &begin); + + /* Need to re-check these, as fixtures might have changed test state. */ + if (!test->fatal && !test->skipped) { + if (spawn) { + spawn_test(test, test_data, + cleanup_fn, cleanup_data); + } else { + if (test->fn_f) + test->fn_f(test_data); + else + test->fn(); + } + } + + clock_gettime(TARGET_TIMER, &end); + + elapsed = (end.tv_sec - begin.tv_sec) * MS_PER_SEC; + if (end.tv_sec != begin.tv_sec) { + elapsed -= (begin.tv_nsec) / NANO_PER_MS; + elapsed += (end.tv_nsec) / NANO_PER_MS; + } else { + elapsed += (end.tv_nsec - begin.tv_nsec) / NANO_PER_MS; + } + test->elapsed = elapsed; + + if (cleanup_fn) + cleanup_fn(cleanup_data); + + if (test->deferred) { + if (test_has_failure(test)) + migrate_deferred_events(test, false); + else + free_events(&test->deferred); + } + + dispatch_test_ended(&g_ctx, test); + + g_ctx.curr_test = NULL; +} + +static void +run_single_case(struct zuc_case *test_case) +{ + int count_live = test_case->test_count - test_case->disabled; + g_ctx.curr_case = test_case; + if (count_live) { + int i = 0; + const struct zuc_fixture *fxt = test_case->fxt; + void *case_data = fxt ? (void *)fxt->data : NULL; + + dispatch_case_started(&g_ctx, test_case, + count_live, test_case->disabled); + + if (fxt && fxt->set_up_test_case) + case_data = fxt->set_up_test_case(fxt->data); + + for (i = 0; i < test_case->test_count; ++i) { + struct zuc_test *curr = test_case->tests[i]; + if (curr->disabled) { + dispatch_test_disabled(&g_ctx, curr); + } else { + run_single_test(curr, fxt, case_data, + g_ctx.spawn); + if (curr->skipped) + test_case->skipped++; + if (curr->failed) + test_case->failed++; + if (curr->fatal) + test_case->fatal++; + if (!curr->failed && !curr->fatal) + test_case->passed++; + test_case->elapsed += curr->elapsed; + } + } + + if (fxt && fxt->tear_down_test_case) + fxt->tear_down_test_case(case_data); + + dispatch_case_ended(&g_ctx, test_case); + } + g_ctx.curr_case = NULL; +} + +static void +reset_test_values(struct zuc_case **cases, int case_count) +{ + int i; + for (i = 0; i < case_count; ++i) { + int j; + cases[i]->disabled = 0; + cases[i]->skipped = 0; + cases[i]->failed = 0; + cases[i]->fatal = 0; + cases[i]->passed = 0; + cases[i]->elapsed = 0; + for (j = 0; j < cases[i]->test_count; ++j) { + struct zuc_test *test = cases[i]->tests[j]; + if (test->disabled) + cases[i]->disabled++; + test->skipped = 0; + test->failed = 0; + test->fatal = 0; + test->elapsed = 0; + + free_events(&test->events); + free_events(&test->deferred); + } + } +} + +static int +run_single_pass(void) +{ + long total_elapsed = 0; + int total_passed = 0; + int total_failed = 0; + int total_skipped = 0; + int live_case_count = 0; + int live_test_count = 0; + int disabled_test_count = 0; + int i; + + reset_test_values(g_ctx.cases, g_ctx.case_count); + for (i = 0; i < g_ctx.case_count; ++i) { + int live = g_ctx.cases[i]->test_count + - g_ctx.cases[i]->disabled; + if (live) { + live_test_count += live; + live_case_count++; + } + if (g_ctx.cases[i]->disabled) + disabled_test_count++; + } + + dispatch_run_started(&g_ctx, live_case_count, live_test_count, + disabled_test_count); + + for (i = 0; i < g_ctx.case_count; ++i) { + run_single_case(g_ctx.cases[i]); + total_failed += g_ctx.cases[i]->test_count + - (g_ctx.cases[i]->passed + g_ctx.cases[i]->disabled); + total_passed += g_ctx.cases[i]->passed; + total_elapsed += g_ctx.cases[i]->elapsed; + total_skipped += g_ctx.cases[i]->skipped; + } + + dispatch_run_ended(&g_ctx, live_case_count, live_test_count, + total_passed, total_failed, disabled_test_count, + total_elapsed); + + if (total_failed) + return EXIT_FAILURE; + else if (total_skipped) + return ZUC_EXIT_SKIP; + else + return EXIT_SUCCESS; +} + +int +zucimpl_run_tests(void) +{ + int rc = EXIT_SUCCESS; + int i; + int limit = g_ctx.repeat > 0 ? g_ctx.repeat : 1; + + initialize(); + if (g_ctx.fatal) + return EXIT_FAILURE; + + if (g_ctx.listeners == NULL) { + zuc_add_event_listener(zuc_collector_create(&(g_ctx.fds[1]))); + zuc_add_event_listener(zuc_base_logger_create()); + } + + if (g_ctx.case_count < 1) { + printf("%s:%d: error: Setup error: test tree is empty\n", + __FILE__, __LINE__); + rc = EXIT_FAILURE; + } + + for (i = 0; (i < limit) && (g_ctx.case_count > 0); ++i) { + int pass_code = EXIT_SUCCESS; + dispatch_pre_run(&g_ctx, limit, i + 1, + (g_ctx.random > 0) ? g_ctx.seed : 0, + g_ctx.filter); + + order_cases(g_ctx.case_count, g_ctx.cases); + if (g_ctx.random > 0) + shuffle_cases(g_ctx.case_count, g_ctx.cases, + g_ctx.seed); + + pass_code = run_single_pass(); + if (pass_code == EXIT_FAILURE) + rc = EXIT_FAILURE; + else if ((pass_code == ZUC_EXIT_SKIP) && (rc == EXIT_SUCCESS)) + rc = ZUC_EXIT_SKIP; + + g_ctx.seed++; + } + + return rc; +} + +int +zucimpl_tracepoint(char const *file, int line, char const *fmt, ...) +{ + int rc = -1; + va_list argp; + char *msg = NULL; + + + va_start(argp, fmt); + rc = vasprintf(&msg, fmt, argp); + if (rc == -1) { + msg = NULL; + } + va_end(argp); + + dispatch_collect_event(&g_ctx, + file, line, + msg); + + free(msg); + + return rc; +} + +void +zucimpl_terminate(char const *file, int line, + bool fail, bool fatal, const char *msg) +{ + enum zuc_fail_state state = ZUC_CHECK_SKIP; + int level = 2; + if (fail && fatal) { + state = ZUC_CHECK_FATAL; + level = 0; + } else if (fail && !fatal) { + state = ZUC_CHECK_FAIL; + level = 0; + } + + mark_failed(g_ctx.curr_test, state); + + if ((state != ZUC_CHECK_OK) && g_ctx.curr_test) + migrate_deferred_events(g_ctx.curr_test, false); + + dispatch_check_triggered(&g_ctx, + file, line, + state, + ZUC_OP_TERMINATE, ZUC_VAL_INT, + level, 0, + msg, ""); +} + +static void +validate_types(enum zuc_check_op op, enum zuc_check_valtype valtype) +{ + bool is_valid = true; + + switch (op) { + case ZUC_OP_NULL: + case ZUC_OP_NOT_NULL: + is_valid = is_valid && (valtype == ZUC_VAL_PTR); + break; + default: + ; /* all rest OK */ + } + + switch (valtype) { + case ZUC_VAL_CSTR: + is_valid = is_valid && ((op == ZUC_OP_EQ) || (op == ZUC_OP_NE)); + break; + default: + ; /* all rest OK */ + } + + if (!is_valid) + printf("%s:%d: warning: Unexpected op+type %d/%d.\n", + __FILE__, __LINE__, op, valtype); +} + +static int +pred2_unknown(intptr_t lhs, intptr_t rhs) +{ + return 0; +} + +static int +pred2_true(intptr_t lhs, intptr_t rhs) +{ + return lhs; +} + +static int +pred2_false(intptr_t lhs, intptr_t rhs) +{ + return !lhs; +} + +static int +pred2_eq(intptr_t lhs, intptr_t rhs) +{ + return lhs == rhs; +} + +static int +pred2_streq(intptr_t lhs, intptr_t rhs) +{ + int status = 0; + const char *lhptr = (const char *)lhs; + const char *rhptr = (const char *)rhs; + + if (!lhptr && !rhptr) + status = 1; + else if (lhptr && rhptr) + status = strcmp(lhptr, rhptr) == 0; + + return status; +} + +static int +pred2_ne(intptr_t lhs, intptr_t rhs) +{ + return lhs != rhs; +} + +static int +pred2_strne(intptr_t lhs, intptr_t rhs) +{ + int status = 0; + const char *lhptr = (const char *)lhs; + const char *rhptr = (const char *)rhs; + + if (lhptr != rhptr) { + if (!lhptr || !rhptr) + status = 1; + else + status = strcmp(lhptr, rhptr) != 0; + } + + return status; +} + +static int +pred2_ge(intptr_t lhs, intptr_t rhs) +{ + return lhs >= rhs; +} + +static int +pred2_gt(intptr_t lhs, intptr_t rhs) +{ + return lhs > rhs; +} + +static int +pred2_le(intptr_t lhs, intptr_t rhs) +{ + return lhs <= rhs; +} + +static int +pred2_lt(intptr_t lhs, intptr_t rhs) +{ + return lhs < rhs; +} + +static comp_pred2 +get_pred2(enum zuc_check_op op, enum zuc_check_valtype valtype) +{ + switch (op) { + case ZUC_OP_TRUE: + return pred2_true; + break; + case ZUC_OP_FALSE: + return pred2_false; + break; + case ZUC_OP_NULL: + return pred2_false; + break; + case ZUC_OP_NOT_NULL: + return pred2_true; + break; + case ZUC_OP_EQ: + if (valtype == ZUC_VAL_CSTR) + return pred2_streq; + else + return pred2_eq; + break; + case ZUC_OP_NE: + if (valtype == ZUC_VAL_CSTR) + return pred2_strne; + else + return pred2_ne; + break; + case ZUC_OP_GE: + return pred2_ge; + break; + case ZUC_OP_GT: + return pred2_gt; + break; + case ZUC_OP_LE: + return pred2_le; + break; + case ZUC_OP_LT: + return pred2_lt; + break; + default: + return pred2_unknown; + } +} + +int +zucimpl_expect_pred2(char const *file, int line, + enum zuc_check_op op, enum zuc_check_valtype valtype, + bool fatal, + intptr_t lhs, intptr_t rhs, + const char *lhs_str, const char* rhs_str) +{ + enum zuc_fail_state state = fatal ? ZUC_CHECK_FATAL : ZUC_CHECK_FAIL; + comp_pred2 pred = get_pred2(op, valtype); + int failed = !pred(lhs, rhs); + + validate_types(op, valtype); + + if (failed) { + mark_failed(g_ctx.curr_test, state); + + if (g_ctx.curr_test) + migrate_deferred_events(g_ctx.curr_test, false); + + dispatch_check_triggered(&g_ctx, + file, line, + fatal ? ZUC_CHECK_FATAL + : ZUC_CHECK_FAIL, + op, valtype, + lhs, rhs, + lhs_str, rhs_str); + } + return failed; +} diff --git a/tools/zunitc/test/fixtures_test.c b/tools/zunitc/test/fixtures_test.c new file mode 100644 index 00000000..89c1e3e1 --- /dev/null +++ b/tools/zunitc/test/fixtures_test.c @@ -0,0 +1,106 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +/** + * Tests of fixtures. + */ + +#include +#include +#include +#include + +#include "zunitc/zunitc.h" + + + +/* Use a simple string for a simplistic fixture */ +static struct zuc_fixture fixture_minimal = { + .data = "for all good men to", +}; + +ZUC_TEST_F(fixture_minimal, just_as_is) +{ + const char *str = data; + ZUC_ASSERT_NOT_NULL(str); + + ZUC_ASSERT_EQ(0, strcmp("for all good men to", str)); +} + +/* + * Not important what or how data is manipulated, just that this function + * does something non-transparent to it. + */ +static void * +setup_test_config(void *data) +{ + int i; + const char *str = data; + char *upper = NULL; + ZUC_ASSERTG_NOT_NULL(data, out); + + upper = strdup(str); + ZUC_ASSERTG_NOT_NULL(upper, out); + + for (i = 0; upper[i]; ++i) + upper[i] = (char)toupper(upper[i]); + +out: + return upper; +} + +static void +teardown_test_config(void *data) +{ + ZUC_ASSERT_NOT_NULL(data); + free(data); +} + +static struct zuc_fixture fixture_data0 = { + .data = "Now is the time", + .set_up = setup_test_config, + .tear_down = teardown_test_config +}; + +ZUC_TEST_F(fixture_data0, base) +{ + const char *str = data; + ZUC_ASSERT_NOT_NULL(str); + + ZUC_ASSERT_EQ(0, strcmp("NOW IS THE TIME", str)); +} + +/* Use the same fixture for a second test. */ +ZUC_TEST_F(fixture_data0, no_lower) +{ + int i; + const char *str = data; + ZUC_ASSERT_NOT_NULL(str); + + for (i = 0; str[i]; ++i) + ZUC_ASSERT_EQ(0, islower(str[i])); +} diff --git a/tools/zunitc/test/zunitc_test.c b/tools/zunitc/test/zunitc_test.c new file mode 100644 index 00000000..177c6bc8 --- /dev/null +++ b/tools/zunitc/test/zunitc_test.c @@ -0,0 +1,464 @@ +/* + * Copyright © 2015 Samsung Electronics Co., Ltd + * + * Permission is hereby granted, free of charge, to any person obtaining + * a copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, sublicense, and/or sell copies of the Software, and to + * permit persons to whom the Software is furnished to do so, subject to + * the following conditions: + * + * The above copyright notice and this permission notice (including the + * next paragraph) shall be included in all copies or substantial + * portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "config.h" + +/* + * A simple file to show tests being setup and run. + */ + +#include +#include +#include +#include + +#include "zunitc/zunitc.h" + +/* + * The SKIP and FAIL sets of tests are those that will cause 'make check' + * to fail so are disabled by default. They can be re-enabled when working + * on the test framework itself. + */ + +/* #define ENABLE_FAIL_TESTS */ +/* #define ENABLE_SKIP_TESTS */ + +ZUC_TEST(base_test, math_is_sane) +{ + ZUC_ASSERT_EQ(4, 2 + 2); +} + +ZUC_TEST(base_test, math_is_hard) +{ + ZUC_TRACEPOINT("Tracepoint here."); + + ZUC_TRACEPOINT("Checking %d", 4); + +#ifdef ENABLE_FAIL_TESTS + + ZUC_ASSERT_EQ(5, 2 + 2); + ZUC_TRACEPOINT("flip %1.3f", 3.1415927); /* not seen */ +#endif +} + +#ifdef ENABLE_FAIL_TESTS +ZUC_TEST(base_test, tracepoint_after_assert) +{ + ZUC_TRACEPOINT("Should be seen in output"); + + ZUC_ASSERT_EQ(5, 2 + 2); + + ZUC_TRACEPOINT("Should NOT be seen in output"); +} +#endif + +#ifdef ENABLE_FAIL_TESTS +ZUC_TEST(base_test, math_is_more_hard) +{ + ZUC_ASSERT_EQ(5, 2 + 2); +} + +ZUC_TEST(base_test, math_is_more_hard2) +{ + ZUC_ASSERT_EQ(7, 9); +} +#endif + +ZUC_TEST(base_test, time_counted) +{ + ZUC_TRACEPOINT("Never seen"); + + ZUC_TRACEPOINT("Sleepy Time %d", 10000 * 5); + ZUC_ASSERT_EQ(0, usleep(10000 * 5)); /* 50ms to show up in reporting */ +} + +ZUC_TEST(other_test, math_monkey) +{ + ZUC_ASSERT_TRUE(1); + ZUC_ASSERT_TRUE(3); + ZUC_ASSERT_FALSE(0); + + ZUC_ASSERT_TRUE(1); + ZUC_ASSERT_TRUE(3); + ZUC_ASSERT_FALSE(0); + + ZUC_ASSERT_EQ(5, 2 + 3); + ZUC_ASSERT_EQ(5, 2 + 3); + + int b = 9; + ZUC_ASSERT_NE(1, 2); + ZUC_ASSERT_NE(b, b + 2); + + ZUC_ASSERT_NE(1, 2); + ZUC_ASSERT_NE(b, b + 1); + + ZUC_ASSERT_LT(1, 2); + ZUC_ASSERT_LT(1, 3); + + ZUC_ASSERT_LE(1, 2); + ZUC_ASSERT_LE(1, 3); + + ZUC_ASSERT_LE(1, 1); + ZUC_ASSERT_LE(1, 1); + + ZUC_ASSERT_GT(2, 1); + ZUC_ASSERT_GT(3, 1); + + ZUC_ASSERT_GE(1, 1); + ZUC_ASSERT_GE(1, 1); + + ZUC_ASSERT_GE(2, 1); + ZUC_ASSERT_GE(3, 1); +} + +static void +force_fatal_failure(void) +{ +#ifdef ENABLE_FAIL_TESTS + bool expected_to_fail_here = true; + ZUC_ASSERT_FALSE(expected_to_fail_here); + + ZUC_FATAL("Should never reach here"); + ZUC_ASSERT_NE(1, 1); +#endif +} + +#ifdef ENABLE_FAIL_TESTS +ZUC_TEST(infrastructure, fail_keeps_testing) +{ + ZUC_FATAL("Should always reach here"); + ZUC_ASSERT_NE(1, 1); /* in case ZUC_FATAL doesn't work. */ +} +#endif + +#ifdef ENABLE_FAIL_TESTS +ZUC_TEST(infrastructure, fatal_stops_test) +{ + ZUC_FATAL("Time to kill testing"); + + ZUC_FATAL("Should never reach here"); + ZUC_ASSERT_NE(1, 1); /* in case ZUC_FATAL doesn't work. */ +} +#endif + +#ifdef ENABLE_SKIP_TESTS +ZUC_TEST(infrastructure, skip_stops_test) +{ + ZUC_SKIP("Time to skip testing"); + + ZUC_FATAL("Should never reach here"); + ZUC_ASSERT_NE(1, 1); /* in case ZUC_FATAL doesn't work. */ +} +#endif + +struct fixture_data { + int case_counter; + int test_counter; +}; + +static struct fixture_data fixture_info = {0, 0}; + +static void * +complex_test_set_up_case(const void *data) +{ + fixture_info.case_counter++; + return &fixture_info; +} + +static void +complex_test_tear_down_case(void *data) +{ + ZUC_ASSERT_TRUE(&fixture_info == data); + fixture_info.case_counter--; +} + +static void * +complex_test_set_up(void *data) +{ + fixture_info.test_counter = fixture_info.case_counter; + return &fixture_info; +} + +static void +complex_test_tear_down(void *data) +{ + ZUC_ASSERT_EQ(1, fixture_info.case_counter); +} + +struct zuc_fixture complex_test = { + .set_up = complex_test_set_up, + .tear_down = complex_test_tear_down, + .set_up_test_case = complex_test_set_up_case, + .tear_down_test_case = complex_test_tear_down_case +}; + +/* + * Note that these next cases all try to modify the test_counter member, + * but the fixture should reset that. +*/ + +ZUC_TEST_F(complex_test, bases_cenario) +{ + struct fixture_data *fdata = data; + ZUC_ASSERT_NOT_NULL(fdata); + + ZUC_ASSERT_EQ(4, 3 + 1); + ZUC_ASSERT_EQ(1, fdata->case_counter); + ZUC_ASSERT_EQ(1, fdata->test_counter); + fdata->test_counter++; + ZUC_ASSERT_EQ(2, fdata->test_counter); +} + +ZUC_TEST_F(complex_test, something) +{ + struct fixture_data *fdata = data; + ZUC_ASSERT_NOT_NULL(fdata); + + ZUC_ASSERT_EQ(4, 3 + 1); + ZUC_ASSERT_EQ(1, fdata->case_counter); + ZUC_ASSERT_EQ(1, fdata->test_counter); + fdata->test_counter++; + ZUC_ASSERT_EQ(2, fdata->test_counter); +} + +ZUC_TEST_F(complex_test, else_here) +{ + struct fixture_data *fdata = data; + ZUC_ASSERT_NOT_NULL(fdata); + + ZUC_ASSERT_EQ(4, 3 + 1); + ZUC_ASSERT_EQ(1, fdata->case_counter); + ZUC_ASSERT_EQ(1, fdata->test_counter); + fdata->test_counter++; + ZUC_ASSERT_EQ(2, fdata->test_counter); +} + +ZUC_TEST(more, DISABLED_not_run) +{ + ZUC_ASSERT_EQ(1, 2); +} + +ZUC_TEST(more, failure_states) +{ +#ifdef ENABLE_FAIL_TESTS + bool expected_to_fail_here = true; +#endif + + ZUC_ASSERT_FALSE(zuc_has_failure()); + +#ifdef ENABLE_FAIL_TESTS + ZUC_ASSERT_FALSE(expected_to_fail_here); /* should fail */ + + ZUC_ASSERT_TRUE(zuc_has_failure()); +#endif +} + +ZUC_TEST(more, failure_sub_fatal) +{ + ZUC_ASSERT_FALSE(zuc_has_failure()); + + force_fatal_failure(); + +#ifdef ENABLE_FAIL_TESTS + ZUC_ASSERT_TRUE(zuc_has_failure()); +#endif +} + +ZUC_TEST(pointers, null) +{ + const char *a = NULL; + + ZUC_ASSERT_NULL(NULL); + ZUC_ASSERT_NULL(0); + ZUC_ASSERT_NULL(a); +} + +#ifdef ENABLE_FAIL_TESTS +ZUC_TEST(pointers, null_fail) +{ + const char *a = "a"; + + ZUC_ASSERT_NULL(!NULL); + ZUC_ASSERT_NULL(!0); + ZUC_ASSERT_NULL(a); +} +#endif + +ZUC_TEST(pointers, not_null) +{ + const char *a = "a"; + + ZUC_ASSERT_NOT_NULL(!NULL); + ZUC_ASSERT_NOT_NULL(!0); + ZUC_ASSERT_NOT_NULL(a); +} + +#ifdef ENABLE_FAIL_TESTS +ZUC_TEST(pointers, not_null_fail) +{ + const char *a = NULL; + + ZUC_ASSERT_NOT_NULL(NULL); + ZUC_ASSERT_NOT_NULL(0); + ZUC_ASSERT_NOT_NULL(a); +} +#endif + +ZUC_TEST(strings, eq) +{ + /* Note that we use strdup() to ensure different addresses. */ + char *str_a = strdup("a"); + const char *str_nil = NULL; + + ZUC_ASSERT_STREQ(str_a, str_a); + ZUC_ASSERT_STREQ("a", str_a); + ZUC_ASSERT_STREQ(str_a, "a"); + + ZUC_ASSERT_STREQ(str_nil, str_nil); + ZUC_ASSERT_STREQ(NULL, str_nil); + ZUC_ASSERT_STREQ(str_nil, NULL); + + free(str_a); +} + +#ifdef ENABLE_FAIL_TESTS +ZUC_TEST(strings, eq_fail) +{ + /* Note that we use strdup() to ensure different addresses. */ + char *str_a = strdup("a"); + char *str_b = strdup("b"); + const char *str_nil = NULL; + + ZUC_ASSERT_STREQ(str_a, str_b); + ZUC_ASSERT_STREQ("b", str_a); + ZUC_ASSERT_STREQ(str_a, "b"); + + ZUC_ASSERT_STREQ(str_nil, str_a); + ZUC_ASSERT_STREQ(str_nil, str_b); + ZUC_ASSERT_STREQ(str_a, str_nil); + ZUC_ASSERT_STREQ(str_b, str_nil); + + ZUC_ASSERT_STREQ(NULL, str_a); + ZUC_ASSERT_STREQ(NULL, str_b); + ZUC_ASSERT_STREQ(str_a, NULL); + ZUC_ASSERT_STREQ(str_b, NULL); + + free(str_a); + free(str_b); +} +#endif + +ZUC_TEST(strings, ne) +{ + /* Note that we use strdup() to ensure different addresses. */ + char *str_a = strdup("a"); + char *str_b = strdup("b"); + const char *str_nil = NULL; + + ZUC_ASSERT_STRNE(str_a, str_b); + ZUC_ASSERT_STRNE("b", str_a); + ZUC_ASSERT_STRNE(str_a, "b"); + + ZUC_ASSERT_STRNE(str_nil, str_a); + ZUC_ASSERT_STRNE(str_nil, str_b); + ZUC_ASSERT_STRNE(str_a, str_nil); + ZUC_ASSERT_STRNE(str_b, str_nil); + + ZUC_ASSERT_STRNE(NULL, str_a); + ZUC_ASSERT_STRNE(NULL, str_b); + ZUC_ASSERT_STRNE(str_a, NULL); + ZUC_ASSERT_STRNE(str_b, NULL); + + free(str_a); + free(str_b); +} + +#ifdef ENABLE_FAIL_TESTS +ZUC_TEST(strings, ne_fail01) +{ + /* Note that we use strdup() to ensure different addresses. */ + char *str_a = strdup("a"); + + ZUC_ASSERTG_STRNE(str_a, str_a, err); + +err: + free(str_a); +} + +ZUC_TEST(strings, ne_fail02) +{ + /* Note that we use strdup() to ensure different addresses. */ + char *str_a = strdup("a"); + + ZUC_ASSERTG_STRNE("a", str_a, err); + +err: + free(str_a); +} + +ZUC_TEST(strings, ne_fail03) +{ + /* Note that we use strdup() to ensure different addresses. */ + char *str_a = strdup("a"); + + ZUC_ASSERTG_STRNE(str_a, "a", err); + +err: + free(str_a); +} + +ZUC_TEST(strings, ne_fail04) +{ + const char *str_nil = NULL; + + ZUC_ASSERT_STRNE(str_nil, str_nil); +} + +ZUC_TEST(strings, ne_fail05) +{ + const char *str_nil = NULL; + + ZUC_ASSERT_STRNE(NULL, str_nil); +} + +ZUC_TEST(strings, ne_fail06) +{ + const char *str_nil = NULL; + + ZUC_ASSERT_STRNE(str_nil, NULL); +} +#endif + +ZUC_TEST(base_test, later) +{ + /* an additional test for the same case but later in source */ + ZUC_ASSERT_EQ(3, 5 - 2); +} + +ZUC_TEST(base_test, zed) +{ + /* an additional test for the same case but later in source */ + ZUC_ASSERT_EQ(3, 5 - 2); +}