From 5a75a41d0737480127d8ed695418c4ae6b735f42 Mon Sep 17 00:00:00 2001 From: "Jon A. Cruz" Date: Thu, 2 Jul 2015 23:36:44 -0700 Subject: [PATCH] Added simple unit/integration test framework and corresponding test program. Added a simple C-based test framework and an example program that uses it to run through some simple unit tests. This is new code inspired primarily by the approaches of Google Test, Boost Test, JUnit and TestNG. Factors of others were also considered during design and implementation. Signed-off-by: Jon A. Cruz Acked-by: Pekka Paalanen --- .gitignore | 2 + Makefile.am | 54 +- tools/zunitc/doc/zunitc.dox | 145 +++ tools/zunitc/inc/zunitc/zunitc.h | 696 +++++++++++ tools/zunitc/inc/zunitc/zunitc_impl.h | 105 ++ tools/zunitc/src/main.c | 58 + tools/zunitc/src/zuc_base_logger.c | 404 +++++++ tools/zunitc/src/zuc_base_logger.h | 38 + tools/zunitc/src/zuc_collector.c | 427 +++++++ tools/zunitc/src/zuc_collector.h | 58 + tools/zunitc/src/zuc_context.h | 58 + tools/zunitc/src/zuc_event.h | 86 ++ tools/zunitc/src/zuc_event_listener.h | 174 +++ tools/zunitc/src/zuc_types.h | 80 ++ tools/zunitc/src/zunitc_impl.c | 1572 +++++++++++++++++++++++++ tools/zunitc/test/fixtures_test.c | 106 ++ tools/zunitc/test/zunitc_test.c | 464 ++++++++ 17 files changed, 4524 insertions(+), 3 deletions(-) create mode 100644 tools/zunitc/doc/zunitc.dox create mode 100644 tools/zunitc/inc/zunitc/zunitc.h create mode 100644 tools/zunitc/inc/zunitc/zunitc_impl.h create mode 100644 tools/zunitc/src/main.c create mode 100644 tools/zunitc/src/zuc_base_logger.c create mode 100644 tools/zunitc/src/zuc_base_logger.h create mode 100644 tools/zunitc/src/zuc_collector.c create mode 100644 tools/zunitc/src/zuc_collector.h create mode 100644 tools/zunitc/src/zuc_context.h create mode 100644 tools/zunitc/src/zuc_event.h create mode 100644 tools/zunitc/src/zuc_event_listener.h create mode 100644 tools/zunitc/src/zuc_types.h create mode 100644 tools/zunitc/src/zunitc_impl.c create mode 100644 tools/zunitc/test/fixtures_test.c create mode 100644 tools/zunitc/test/zunitc_test.c 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); +}