summaryrefslogtreecommitdiffstats
path: root/third_party/cmocka/cmocka.c
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/cmocka/cmocka.c')
-rw-r--r--third_party/cmocka/cmocka.c3431
1 files changed, 3431 insertions, 0 deletions
diff --git a/third_party/cmocka/cmocka.c b/third_party/cmocka/cmocka.c
new file mode 100644
index 0000000..b21fe15
--- /dev/null
+++ b/third_party/cmocka/cmocka.c
@@ -0,0 +1,3431 @@
+/*
+ * Copyright 2008 Google Inc.
+ * Copyright 2014-2018 Andreas Schneider <asn@cryptomilk.org>
+ * Copyright 2015 Jakub Hrozek <jakub.hrozek@posteo.se>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#ifdef HAVE_MALLOC_H
+#include <malloc.h>
+#endif
+
+#ifdef HAVE_INTTYPES_H
+#include <inttypes.h>
+#endif
+
+#ifdef HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+
+#include <stdint.h>
+#include <setjmp.h>
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+/*
+ * This allows to add a platform specific header file. Some embedded platforms
+ * sometimes miss certain types and definitions.
+ *
+ * Example:
+ *
+ * typedef unsigned long int uintptr_t
+ * #define _UINTPTR_T 1
+ * #define _UINTPTR_T_DEFINED 1
+ */
+#ifdef CMOCKA_PLATFORM_INCLUDE
+# include "cmocka_platform.h"
+#endif /* CMOCKA_PLATFORM_INCLUDE */
+
+#include <cmocka.h>
+#include <cmocka_private.h>
+
+/* Size of guard bytes around dynamically allocated blocks. */
+#define MALLOC_GUARD_SIZE 16
+/* Pattern used to initialize guard blocks. */
+#define MALLOC_GUARD_PATTERN 0xEF
+/* Pattern used to initialize memory allocated with test_malloc(). */
+#define MALLOC_ALLOC_PATTERN 0xBA
+#define MALLOC_FREE_PATTERN 0xCD
+/* Alignment of allocated blocks. NOTE: This must be base2. */
+#define MALLOC_ALIGNMENT sizeof(size_t)
+
+/* Printf formatting for source code locations. */
+#define SOURCE_LOCATION_FORMAT "%s:%u"
+
+#if defined(HAVE_GCC_THREAD_LOCAL_STORAGE)
+# define CMOCKA_THREAD __thread
+#elif defined(HAVE_MSVC_THREAD_LOCAL_STORAGE)
+# define CMOCKA_THREAD __declspec(thread)
+#else
+# define CMOCKA_THREAD
+#endif
+
+#ifdef HAVE_CLOCK_REALTIME
+#define CMOCKA_CLOCK_GETTIME(clock_id, ts) clock_gettime((clock_id), (ts))
+#else
+#define CMOCKA_CLOCK_GETTIME(clock_id, ts)
+#endif
+
+#ifndef MAX
+#define MAX(a,b) ((a) < (b) ? (b) : (a))
+#endif
+
+/**
+ * POSIX has sigsetjmp/siglongjmp, while Windows only has setjmp/longjmp.
+ */
+#ifdef HAVE_SIGLONGJMP
+# define cm_jmp_buf sigjmp_buf
+# define cm_setjmp(env) sigsetjmp(env, 1)
+# define cm_longjmp(env, val) siglongjmp(env, val)
+#else
+# define cm_jmp_buf jmp_buf
+# define cm_setjmp(env) setjmp(env)
+# define cm_longjmp(env, val) longjmp(env, val)
+#endif
+
+
+/*
+ * Declare and initialize the pointer member of ValuePointer variable name
+ * with ptr.
+ */
+#define declare_initialize_value_pointer_pointer(name, ptr) \
+ ValuePointer name ; \
+ name.value = 0; \
+ name.x.pointer = (void*)(ptr)
+
+/*
+ * Declare and initialize the value member of ValuePointer variable name
+ * with val.
+ */
+#define declare_initialize_value_pointer_value(name, val) \
+ ValuePointer name ; \
+ name.value = val
+
+/* Cast a LargestIntegralType to pointer_type via a ValuePointer. */
+#define cast_largest_integral_type_to_pointer( \
+ pointer_type, largest_integral_type) \
+ ((pointer_type)((ValuePointer*)&(largest_integral_type))->x.pointer)
+
+/* Used to cast LargetIntegralType to void* and vice versa. */
+typedef union ValuePointer {
+ LargestIntegralType value;
+ struct {
+#if defined(WORDS_BIGENDIAN) && (WORDS_SIZEOF_VOID_P == 4)
+ unsigned int padding;
+#endif
+ void *pointer;
+ } x;
+} ValuePointer;
+
+/* Doubly linked list node. */
+typedef struct ListNode {
+ const void *value;
+ int refcount;
+ struct ListNode *next;
+ struct ListNode *prev;
+} ListNode;
+
+/* Debug information for malloc(). */
+struct MallocBlockInfoData {
+ void* block; /* Address of the block returned by malloc(). */
+ size_t allocated_size; /* Total size of the allocated block. */
+ size_t size; /* Request block size. */
+ SourceLocation location; /* Where the block was allocated. */
+ ListNode node; /* Node within list of all allocated blocks. */
+};
+
+typedef union {
+ struct MallocBlockInfoData *data;
+ char *ptr;
+} MallocBlockInfo;
+
+/* State of each test. */
+typedef struct TestState {
+ const ListNode *check_point; /* Check point of the test if there's a */
+ /* setup function. */
+ void *state; /* State associated with the test. */
+} TestState;
+
+/* Determines whether two values are the same. */
+typedef int (*EqualityFunction)(const void *left, const void *right);
+
+/* Value of a symbol and the place it was declared. */
+typedef struct SymbolValue {
+ SourceLocation location;
+ LargestIntegralType value;
+} SymbolValue;
+
+/*
+ * Contains a list of values for a symbol.
+ * NOTE: Each structure referenced by symbol_values_list_head must have a
+ * SourceLocation as its' first member.
+ */
+typedef struct SymbolMapValue {
+ const char *symbol_name;
+ ListNode symbol_values_list_head;
+} SymbolMapValue;
+
+/* Where a particular ordering was located and its symbol name */
+typedef struct FuncOrderingValue {
+ SourceLocation location;
+ const char * function;
+} FuncOrderingValue;
+
+/* Used by list_free() to deallocate values referenced by list nodes. */
+typedef void (*CleanupListValue)(const void *value, void *cleanup_value_data);
+
+/* Structure used to check the range of integer types.a */
+typedef struct CheckIntegerRange {
+ CheckParameterEvent event;
+ LargestIntegralType minimum;
+ LargestIntegralType maximum;
+} CheckIntegerRange;
+
+/* Structure used to check whether an integer value is in a set. */
+typedef struct CheckIntegerSet {
+ CheckParameterEvent event;
+ const LargestIntegralType *set;
+ size_t size_of_set;
+} CheckIntegerSet;
+
+/* Used to check whether a parameter matches the area of memory referenced by
+ * this structure. */
+typedef struct CheckMemoryData {
+ CheckParameterEvent event;
+ const void *memory;
+ size_t size;
+} CheckMemoryData;
+
+static ListNode* list_initialize(ListNode * const node);
+static ListNode* list_add(ListNode * const head, ListNode *new_node);
+static ListNode* list_add_value(ListNode * const head, const void *value,
+ const int count);
+static ListNode* list_remove(
+ ListNode * const node, const CleanupListValue cleanup_value,
+ void * const cleanup_value_data);
+static void list_remove_free(
+ ListNode * const node, const CleanupListValue cleanup_value,
+ void * const cleanup_value_data);
+static int list_empty(const ListNode * const head);
+static int list_find(
+ ListNode * const head, const void *value,
+ const EqualityFunction equal_func, ListNode **output);
+static int list_first(ListNode * const head, ListNode **output);
+static ListNode* list_free(
+ ListNode * const head, const CleanupListValue cleanup_value,
+ void * const cleanup_value_data);
+
+static void add_symbol_value(
+ ListNode * const symbol_map_head, const char * const symbol_names[],
+ const size_t number_of_symbol_names, const void* value, const int count);
+static int get_symbol_value(
+ ListNode * const symbol_map_head, const char * const symbol_names[],
+ const size_t number_of_symbol_names, void **output);
+static void free_value(const void *value, void *cleanup_value_data);
+static void free_symbol_map_value(
+ const void *value, void *cleanup_value_data);
+static void remove_always_return_values(ListNode * const map_head,
+ const size_t number_of_symbol_names);
+
+static size_t check_for_leftover_values_list(const ListNode * head,
+ const char * const error_message);
+
+static size_t check_for_leftover_values(
+ const ListNode * const map_head, const char * const error_message,
+ const size_t number_of_symbol_names);
+
+static void remove_always_return_values_from_list(ListNode * const map_head);
+
+/*
+ * This must be called at the beginning of a test to initialize some data
+ * structures.
+ */
+static void initialize_testing(const char *test_name);
+
+/* This must be called at the end of a test to free() allocated structures. */
+static void teardown_testing(const char *test_name);
+
+static enum cm_message_output cm_get_output(void);
+
+static int cm_error_message_enabled = 1;
+static CMOCKA_THREAD char *cm_error_message;
+
+void cm_print_error(const char * const format, ...) CMOCKA_PRINTF_ATTRIBUTE(1, 2);
+
+/*
+ * Keeps track of the calling context returned by setenv() so that the fail()
+ * method can jump out of a test.
+ */
+static CMOCKA_THREAD cm_jmp_buf global_run_test_env;
+static CMOCKA_THREAD int global_running_test = 0;
+
+/* Keeps track of the calling context returned by setenv() so that */
+/* mock_assert() can optionally jump back to expect_assert_failure(). */
+jmp_buf global_expect_assert_env;
+int global_expecting_assert = 0;
+const char *global_last_failed_assert = NULL;
+static int global_skip_test;
+
+/* Keeps a map of the values that functions will have to return to provide */
+/* mocked interfaces. */
+static CMOCKA_THREAD ListNode global_function_result_map_head;
+/* Location of the last mock value returned was declared. */
+static CMOCKA_THREAD SourceLocation global_last_mock_value_location;
+
+/* Keeps a map of the values that functions expect as parameters to their
+ * mocked interfaces. */
+static CMOCKA_THREAD ListNode global_function_parameter_map_head;
+/* Location of last parameter value checked was declared. */
+static CMOCKA_THREAD SourceLocation global_last_parameter_location;
+
+/* List (acting as FIFO) of call ordering. */
+static CMOCKA_THREAD ListNode global_call_ordering_head;
+/* Location of last call ordering that was declared. */
+static CMOCKA_THREAD SourceLocation global_last_call_ordering_location;
+
+/* List of all currently allocated blocks. */
+static CMOCKA_THREAD ListNode global_allocated_blocks;
+
+static enum cm_message_output global_msg_output = CM_OUTPUT_STDOUT;
+
+static const char *global_test_filter_pattern;
+
+#ifndef _WIN32
+/* Signals caught by exception_handler(). */
+static const int exception_signals[] = {
+ SIGFPE,
+ SIGILL,
+ SIGSEGV,
+#ifdef SIGBUS
+ SIGBUS,
+#endif
+#ifdef SIGSYS
+ SIGSYS,
+#endif
+};
+
+/* Default signal functions that should be restored after a test is complete. */
+typedef void (*SignalFunction)(int signal);
+static SignalFunction default_signal_functions[
+ ARRAY_SIZE(exception_signals)];
+
+#else /* _WIN32 */
+
+/* The default exception filter. */
+static LPTOP_LEVEL_EXCEPTION_FILTER previous_exception_filter;
+
+/* Fatal exceptions. */
+typedef struct ExceptionCodeInfo {
+ DWORD code;
+ const char* description;
+} ExceptionCodeInfo;
+
+#define EXCEPTION_CODE_INFO(exception_code) {exception_code, #exception_code}
+
+static const ExceptionCodeInfo exception_codes[] = {
+ EXCEPTION_CODE_INFO(EXCEPTION_ACCESS_VIOLATION),
+ EXCEPTION_CODE_INFO(EXCEPTION_ARRAY_BOUNDS_EXCEEDED),
+ EXCEPTION_CODE_INFO(EXCEPTION_DATATYPE_MISALIGNMENT),
+ EXCEPTION_CODE_INFO(EXCEPTION_FLT_DENORMAL_OPERAND),
+ EXCEPTION_CODE_INFO(EXCEPTION_FLT_DIVIDE_BY_ZERO),
+ EXCEPTION_CODE_INFO(EXCEPTION_FLT_INEXACT_RESULT),
+ EXCEPTION_CODE_INFO(EXCEPTION_FLT_INVALID_OPERATION),
+ EXCEPTION_CODE_INFO(EXCEPTION_FLT_OVERFLOW),
+ EXCEPTION_CODE_INFO(EXCEPTION_FLT_STACK_CHECK),
+ EXCEPTION_CODE_INFO(EXCEPTION_FLT_UNDERFLOW),
+ EXCEPTION_CODE_INFO(EXCEPTION_GUARD_PAGE),
+ EXCEPTION_CODE_INFO(EXCEPTION_ILLEGAL_INSTRUCTION),
+ EXCEPTION_CODE_INFO(EXCEPTION_INT_DIVIDE_BY_ZERO),
+ EXCEPTION_CODE_INFO(EXCEPTION_INT_OVERFLOW),
+ EXCEPTION_CODE_INFO(EXCEPTION_INVALID_DISPOSITION),
+ EXCEPTION_CODE_INFO(EXCEPTION_INVALID_HANDLE),
+ EXCEPTION_CODE_INFO(EXCEPTION_IN_PAGE_ERROR),
+ EXCEPTION_CODE_INFO(EXCEPTION_NONCONTINUABLE_EXCEPTION),
+ EXCEPTION_CODE_INFO(EXCEPTION_PRIV_INSTRUCTION),
+ EXCEPTION_CODE_INFO(EXCEPTION_STACK_OVERFLOW),
+};
+#endif /* !_WIN32 */
+
+enum CMUnitTestStatus {
+ CM_TEST_NOT_STARTED,
+ CM_TEST_PASSED,
+ CM_TEST_FAILED,
+ CM_TEST_ERROR,
+ CM_TEST_SKIPPED,
+};
+
+struct CMUnitTestState {
+ const ListNode *check_point; /* Check point of the test if there's a setup function. */
+ const struct CMUnitTest *test; /* Point to array element in the tests we get passed */
+ void *state; /* State associated with the test */
+ const char *error_message; /* The error messages by the test */
+ enum CMUnitTestStatus status; /* PASSED, FAILED, ABORT ... */
+ double runtime; /* Time calculations */
+};
+
+/* Exit the currently executing test. */
+static void exit_test(const int quit_application)
+{
+ const char *env = getenv("CMOCKA_TEST_ABORT");
+ int abort_test = 0;
+
+ if (env != NULL && strlen(env) == 1) {
+ abort_test = (env[0] == '1');
+ }
+
+ if (global_skip_test == 0 &&
+ abort_test == 1) {
+ print_error("%s", cm_error_message);
+ abort();
+ } else if (global_running_test) {
+ cm_longjmp(global_run_test_env, 1);
+ } else if (quit_application) {
+ exit(-1);
+ }
+}
+
+void _skip(const char * const file, const int line)
+{
+ cm_print_error(SOURCE_LOCATION_FORMAT ": Skipped!\n", file, line);
+ global_skip_test = 1;
+ exit_test(1);
+}
+
+/* Initialize a SourceLocation structure. */
+static void initialize_source_location(SourceLocation * const location) {
+ assert_non_null(location);
+ location->file = NULL;
+ location->line = 0;
+}
+
+
+/* Determine whether a source location is currently set. */
+static int source_location_is_set(const SourceLocation * const location) {
+ assert_non_null(location);
+ return location->file && location->line;
+}
+
+
+/* Set a source location. */
+static void set_source_location(
+ SourceLocation * const location, const char * const file,
+ const int line) {
+ assert_non_null(location);
+ location->file = file;
+ location->line = line;
+}
+
+
+static int c_strreplace(char *src,
+ size_t src_len,
+ const char *pattern,
+ const char *repl,
+ int *str_replaced)
+{
+ char *p = NULL;
+
+ p = strstr(src, pattern);
+ if (p == NULL) {
+ return -1;
+ }
+
+ do {
+ size_t of = p - src;
+ size_t l = strlen(src);
+ size_t pl = strlen(pattern);
+ size_t rl = strlen(repl);
+
+ /* overflow check */
+ if (src_len <= l + MAX(pl, rl) + 1) {
+ return -1;
+ }
+
+ if (rl != pl) {
+ memmove(src + of + rl, src + of + pl, l - of - pl + 1);
+ }
+
+ memcpy(src + of, repl, rl);
+
+ if (str_replaced != NULL) {
+ *str_replaced = 1;
+ }
+ p = strstr(src, pattern);
+ } while (p != NULL);
+
+ return 0;
+}
+
+static int c_strmatch(const char *str, const char *pattern)
+{
+ int ok;
+
+ if (str == NULL || pattern == NULL) {
+ return 0;
+ }
+
+ for (;;) {
+ /* Check if pattern is done */
+ if (*pattern == '\0') {
+ /* If string is at the end, we're good */
+ if (*str == '\0') {
+ return 1;
+ }
+
+ return 0;
+ }
+
+ if (*pattern == '*') {
+ /* Move on */
+ pattern++;
+
+ /* If we are at the end, everything is fine */
+ if (*pattern == '\0') {
+ return 1;
+ }
+
+ /* Try to match each position */
+ for (; *str != '\0'; str++) {
+ ok = c_strmatch(str, pattern);
+ if (ok) {
+ return 1;
+ }
+ }
+
+ /* No match */
+ return 0;
+ }
+
+ /* If we are at the end, leave */
+ if (*str == '\0') {
+ return 0;
+ }
+
+ /* Check if we have a single wildcard or matching char */
+ if (*pattern != '?' && *str != *pattern) {
+ return 0;
+ }
+
+ /* Move string and pattern */
+ str++;
+ pattern++;
+ }
+
+ return 0;
+}
+
+/* Create function results and expected parameter lists. */
+void initialize_testing(const char *test_name) {
+ (void)test_name;
+ list_initialize(&global_function_result_map_head);
+ initialize_source_location(&global_last_mock_value_location);
+ list_initialize(&global_function_parameter_map_head);
+ initialize_source_location(&global_last_parameter_location);
+ list_initialize(&global_call_ordering_head);
+ initialize_source_location(&global_last_parameter_location);
+}
+
+
+static void fail_if_leftover_values(const char *test_name) {
+ int error_occurred = 0;
+ (void)test_name;
+ remove_always_return_values(&global_function_result_map_head, 1);
+ if (check_for_leftover_values(
+ &global_function_result_map_head,
+ "%s() has remaining non-returned values.\n", 1)) {
+ error_occurred = 1;
+ }
+
+ remove_always_return_values(&global_function_parameter_map_head, 2);
+ if (check_for_leftover_values(
+ &global_function_parameter_map_head,
+ "'%s' parameter still has values that haven't been checked.\n",
+ 2)) {
+ error_occurred = 1;
+ }
+
+ remove_always_return_values_from_list(&global_call_ordering_head);
+ if (check_for_leftover_values_list(&global_call_ordering_head,
+ "%s function was expected to be called but was not not.\n")) {
+ error_occurred = 1;
+ }
+ if (error_occurred) {
+ exit_test(1);
+ }
+}
+
+
+static void teardown_testing(const char *test_name) {
+ (void)test_name;
+ list_free(&global_function_result_map_head, free_symbol_map_value,
+ (void*)0);
+ initialize_source_location(&global_last_mock_value_location);
+ list_free(&global_function_parameter_map_head, free_symbol_map_value,
+ (void*)1);
+ initialize_source_location(&global_last_parameter_location);
+ list_free(&global_call_ordering_head, free_value,
+ (void*)0);
+ initialize_source_location(&global_last_call_ordering_location);
+}
+
+/* Initialize a list node. */
+static ListNode* list_initialize(ListNode * const node) {
+ node->value = NULL;
+ node->next = node;
+ node->prev = node;
+ node->refcount = 1;
+ return node;
+}
+
+
+/*
+ * Adds a value at the tail of a given list.
+ * The node referencing the value is allocated from the heap.
+ */
+static ListNode* list_add_value(ListNode * const head, const void *value,
+ const int refcount) {
+ ListNode * const new_node = (ListNode*)malloc(sizeof(ListNode));
+ assert_non_null(head);
+ assert_non_null(value);
+ new_node->value = value;
+ new_node->refcount = refcount;
+ return list_add(head, new_node);
+}
+
+
+/* Add new_node to the end of the list. */
+static ListNode* list_add(ListNode * const head, ListNode *new_node) {
+ assert_non_null(head);
+ assert_non_null(new_node);
+ new_node->next = head;
+ new_node->prev = head->prev;
+ head->prev->next = new_node;
+ head->prev = new_node;
+ return new_node;
+}
+
+
+/* Remove a node from a list. */
+static ListNode* list_remove(
+ ListNode * const node, const CleanupListValue cleanup_value,
+ void * const cleanup_value_data) {
+ assert_non_null(node);
+ node->prev->next = node->next;
+ node->next->prev = node->prev;
+ if (cleanup_value) {
+ cleanup_value(node->value, cleanup_value_data);
+ }
+ return node;
+}
+
+
+/* Remove a list node from a list and free the node. */
+static void list_remove_free(
+ ListNode * const node, const CleanupListValue cleanup_value,
+ void * const cleanup_value_data) {
+ assert_non_null(node);
+ free(list_remove(node, cleanup_value, cleanup_value_data));
+}
+
+
+/*
+ * Frees memory kept by a linked list The cleanup_value function is called for
+ * every "value" field of nodes in the list, except for the head. In addition
+ * to each list value, cleanup_value_data is passed to each call to
+ * cleanup_value. The head of the list is not deallocated.
+ */
+static ListNode* list_free(
+ ListNode * const head, const CleanupListValue cleanup_value,
+ void * const cleanup_value_data) {
+ assert_non_null(head);
+ while (!list_empty(head)) {
+ list_remove_free(head->next, cleanup_value, cleanup_value_data);
+ }
+ return head;
+}
+
+
+/* Determine whether a list is empty. */
+static int list_empty(const ListNode * const head) {
+ assert_non_null(head);
+ return head->next == head;
+}
+
+
+/*
+ * Find a value in the list using the equal_func to compare each node with the
+ * value.
+ */
+static int list_find(ListNode * const head, const void *value,
+ const EqualityFunction equal_func, ListNode **output) {
+ ListNode *current;
+ assert_non_null(head);
+ for (current = head->next; current != head; current = current->next) {
+ if (equal_func(current->value, value)) {
+ *output = current;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/* Returns the first node of a list */
+static int list_first(ListNode * const head, ListNode **output) {
+ ListNode *target_node = NULL;
+ assert_non_null(head);
+ if (list_empty(head)) {
+ return 0;
+ }
+ target_node = head->next;
+ *output = target_node;
+ return 1;
+}
+
+
+/* Deallocate a value referenced by a list. */
+static void free_value(const void *value, void *cleanup_value_data) {
+ (void)cleanup_value_data;
+ assert_non_null(value);
+ free((void*)value);
+}
+
+
+/* Releases memory associated to a symbol_map_value. */
+static void free_symbol_map_value(const void *value,
+ void *cleanup_value_data) {
+ SymbolMapValue * const map_value = (SymbolMapValue*)value;
+ const LargestIntegralType children = cast_ptr_to_largest_integral_type(cleanup_value_data);
+ assert_non_null(value);
+ list_free(&map_value->symbol_values_list_head,
+ children ? free_symbol_map_value : free_value,
+ (void *) ((uintptr_t)children - 1));
+ free(map_value);
+}
+
+
+/*
+ * Determine whether a symbol name referenced by a symbol_map_value matches the
+ * specified function name.
+ */
+static int symbol_names_match(const void *map_value, const void *symbol) {
+ return !strcmp(((SymbolMapValue*)map_value)->symbol_name,
+ (const char*)symbol);
+}
+
+/*
+ * Adds a value to the queue of values associated with the given hierarchy of
+ * symbols. It's assumed value is allocated from the heap.
+ */
+static void add_symbol_value(ListNode * const symbol_map_head,
+ const char * const symbol_names[],
+ const size_t number_of_symbol_names,
+ const void* value, const int refcount) {
+ const char* symbol_name;
+ ListNode *target_node;
+ SymbolMapValue *target_map_value;
+ assert_non_null(symbol_map_head);
+ assert_non_null(symbol_names);
+ assert_true(number_of_symbol_names);
+ symbol_name = symbol_names[0];
+
+ if (!list_find(symbol_map_head, symbol_name, symbol_names_match,
+ &target_node)) {
+ SymbolMapValue * const new_symbol_map_value =
+ (SymbolMapValue*)malloc(sizeof(*new_symbol_map_value));
+ new_symbol_map_value->symbol_name = symbol_name;
+ list_initialize(&new_symbol_map_value->symbol_values_list_head);
+ target_node = list_add_value(symbol_map_head, new_symbol_map_value,
+ 1);
+ }
+
+ target_map_value = (SymbolMapValue*)target_node->value;
+ if (number_of_symbol_names == 1) {
+ list_add_value(&target_map_value->symbol_values_list_head,
+ value, refcount);
+ } else {
+ add_symbol_value(&target_map_value->symbol_values_list_head,
+ &symbol_names[1], number_of_symbol_names - 1, value,
+ refcount);
+ }
+}
+
+
+/*
+ * Gets the next value associated with the given hierarchy of symbols.
+ * The value is returned as an output parameter with the function returning the
+ * node's old refcount value if a value is found, 0 otherwise. This means that
+ * a return value of 1 indicates the node was just removed from the list.
+ */
+static int get_symbol_value(
+ ListNode * const head, const char * const symbol_names[],
+ const size_t number_of_symbol_names, void **output) {
+ const char* symbol_name = NULL;
+ ListNode *target_node = NULL;
+ assert_non_null(head);
+ assert_non_null(symbol_names);
+ assert_true(number_of_symbol_names);
+ assert_non_null(output);
+ symbol_name = symbol_names[0];
+
+ if (list_find(head, symbol_name, symbol_names_match, &target_node)) {
+ SymbolMapValue *map_value = NULL;
+ ListNode *child_list = NULL;
+ int return_value = 0;
+ assert_non_null(target_node);
+ assert_non_null(target_node->value);
+
+ map_value = (SymbolMapValue*)target_node->value;
+ child_list = &map_value->symbol_values_list_head;
+
+ if (number_of_symbol_names == 1) {
+ ListNode *value_node = NULL;
+ return_value = list_first(child_list, &value_node);
+ assert_true(return_value);
+ /* Add a check to silence clang analyzer */
+ if (return_value == 0) {
+ goto out;
+ }
+ *output = (void*) value_node->value;
+ return_value = value_node->refcount;
+ if (value_node->refcount - 1 == 0) {
+ list_remove_free(value_node, NULL, NULL);
+ } else if (value_node->refcount > WILL_RETURN_ONCE) {
+ --value_node->refcount;
+ }
+ } else {
+ return_value = get_symbol_value(
+ child_list, &symbol_names[1], number_of_symbol_names - 1,
+ output);
+ }
+ if (list_empty(child_list)) {
+ list_remove_free(target_node, free_symbol_map_value, (void*)0);
+ }
+ return return_value;
+ }
+out:
+ cm_print_error("No entries for symbol %s.\n", symbol_name);
+ return 0;
+}
+
+/**
+ * Taverse a list of nodes and remove first symbol value in list that has a
+ * refcount < -1 (i.e. should always be returned and has been returned at
+ * least once).
+ */
+
+static void remove_always_return_values_from_list(ListNode * const map_head)
+{
+ ListNode * current = NULL;
+ ListNode * next = NULL;
+ assert_non_null(map_head);
+
+ for (current = map_head->next, next = current->next;
+ current != map_head;
+ current = next, next = current->next) {
+ if (current->refcount < -1) {
+ list_remove_free(current, free_value, NULL);
+ }
+ }
+}
+
+/*
+ * Traverse down a tree of symbol values and remove the first symbol value
+ * in each branch that has a refcount < -1 (i.e should always be returned
+ * and has been returned at least once).
+ */
+static void remove_always_return_values(ListNode * const map_head,
+ const size_t number_of_symbol_names) {
+ ListNode *current;
+ assert_non_null(map_head);
+ assert_true(number_of_symbol_names);
+ current = map_head->next;
+ while (current != map_head) {
+ SymbolMapValue * const value = (SymbolMapValue*)current->value;
+ ListNode * const next = current->next;
+ ListNode *child_list;
+ assert_non_null(value);
+ child_list = &value->symbol_values_list_head;
+
+ if (!list_empty(child_list)) {
+ if (number_of_symbol_names == 1) {
+ ListNode * const child_node = child_list->next;
+ /* If this item has been returned more than once, free it. */
+ if (child_node->refcount < -1) {
+ list_remove_free(child_node, free_value, NULL);
+ }
+ } else {
+ remove_always_return_values(child_list,
+ number_of_symbol_names - 1);
+ }
+ }
+
+ if (list_empty(child_list)) {
+ list_remove_free(current, free_value, NULL);
+ }
+ current = next;
+ }
+}
+
+static size_t check_for_leftover_values_list(const ListNode * head,
+ const char * const error_message)
+{
+ ListNode *child_node;
+ size_t leftover_count = 0;
+ if (!list_empty(head))
+ {
+ for (child_node = head->next; child_node != head;
+ child_node = child_node->next, ++leftover_count) {
+ const FuncOrderingValue *const o =
+ (const FuncOrderingValue*) child_node->value;
+ cm_print_error(error_message, o->function);
+ cm_print_error(SOURCE_LOCATION_FORMAT
+ ": note: remaining item was declared here\n",
+ o->location.file, o->location.line);
+ }
+ }
+ return leftover_count;
+}
+
+/*
+ * Checks if there are any leftover values set up by the test that were never
+ * retrieved through execution, and fail the test if that is the case.
+ */
+static size_t check_for_leftover_values(
+ const ListNode * const map_head, const char * const error_message,
+ const size_t number_of_symbol_names) {
+ const ListNode *current;
+ size_t symbols_with_leftover_values = 0;
+ assert_non_null(map_head);
+ assert_true(number_of_symbol_names);
+
+ for (current = map_head->next; current != map_head;
+ current = current->next) {
+ const SymbolMapValue * const value =
+ (SymbolMapValue*)current->value;
+ const ListNode *child_list;
+ assert_non_null(value);
+ child_list = &value->symbol_values_list_head;
+
+ if (!list_empty(child_list)) {
+ if (number_of_symbol_names == 1) {
+ const ListNode *child_node;
+ cm_print_error(error_message, value->symbol_name);
+
+ for (child_node = child_list->next; child_node != child_list;
+ child_node = child_node->next) {
+ const SourceLocation * const location =
+ (const SourceLocation*)child_node->value;
+ cm_print_error(SOURCE_LOCATION_FORMAT
+ ": note: remaining item was declared here\n",
+ location->file, location->line);
+ }
+ } else {
+ cm_print_error("%s: ", value->symbol_name);
+ check_for_leftover_values(child_list, error_message,
+ number_of_symbol_names - 1);
+ }
+ symbols_with_leftover_values ++;
+ }
+ }
+ return symbols_with_leftover_values;
+}
+
+
+/* Get the next return value for the specified mock function. */
+LargestIntegralType _mock(const char * const function, const char* const file,
+ const int line) {
+ void *result;
+ const int rc = get_symbol_value(&global_function_result_map_head,
+ &function, 1, &result);
+ if (rc) {
+ SymbolValue * const symbol = (SymbolValue*)result;
+ const LargestIntegralType value = symbol->value;
+ global_last_mock_value_location = symbol->location;
+ if (rc == 1) {
+ free(symbol);
+ }
+ return value;
+ } else {
+ cm_print_error(SOURCE_LOCATION_FORMAT ": error: Could not get value "
+ "to mock function %s\n", file, line, function);
+ if (source_location_is_set(&global_last_mock_value_location)) {
+ cm_print_error(SOURCE_LOCATION_FORMAT
+ ": note: Previously returned mock value was declared here\n",
+ global_last_mock_value_location.file,
+ global_last_mock_value_location.line);
+ } else {
+ cm_print_error("There were no previously returned mock values for "
+ "this test.\n");
+ }
+ exit_test(1);
+ }
+ return 0;
+}
+
+/* Ensure that function is being called in proper order */
+void _function_called(const char *const function,
+ const char *const file,
+ const int line)
+{
+ ListNode *first_value_node = NULL;
+ ListNode *value_node = NULL;
+ int rc;
+
+ rc = list_first(&global_call_ordering_head, &value_node);
+ first_value_node = value_node;
+ if (rc) {
+ FuncOrderingValue *expected_call;
+ int cmp;
+
+ expected_call = (FuncOrderingValue *)value_node->value;
+
+ cmp = strcmp(expected_call->function, function);
+ if (value_node->refcount < -1) {
+ /*
+ * Search through value nodes until either function is found or
+ * encounter a non-zero refcount greater than -2
+ */
+ if (cmp != 0) {
+ value_node = value_node->next;
+ expected_call = (FuncOrderingValue *)value_node->value;
+
+ cmp = strcmp(expected_call->function, function);
+ while (value_node->refcount < -1 &&
+ cmp != 0 &&
+ value_node != first_value_node->prev) {
+ value_node = value_node->next;
+ if (value_node == NULL) {
+ break;
+ }
+ expected_call = (FuncOrderingValue *)value_node->value;
+ if (expected_call == NULL) {
+ continue;
+ }
+ cmp = strcmp(expected_call->function, function);
+ }
+
+ if (expected_call == NULL || value_node == first_value_node->prev) {
+ cm_print_error(SOURCE_LOCATION_FORMAT
+ ": error: No expected mock calls matching "
+ "called() invocation in %s",
+ file, line,
+ function);
+ exit_test(1);
+ }
+ }
+ }
+
+ if (cmp == 0) {
+ if (value_node->refcount > -2 && --value_node->refcount == 0) {
+ list_remove_free(value_node, free_value, NULL);
+ }
+ } else {
+ cm_print_error(SOURCE_LOCATION_FORMAT
+ ": error: Expected call to %s but received called() "
+ "in %s\n",
+ file, line,
+ expected_call->function,
+ function);
+ exit_test(1);
+ }
+ } else {
+ cm_print_error(SOURCE_LOCATION_FORMAT
+ ": error: No mock calls expected but called() was "
+ "invoked in %s\n",
+ file, line,
+ function);
+ exit_test(1);
+ }
+}
+
+/* Add a return value for the specified mock function name. */
+void _will_return(const char * const function_name, const char * const file,
+ const int line, const LargestIntegralType value,
+ const int count) {
+ SymbolValue * const return_value =
+ (SymbolValue*)malloc(sizeof(*return_value));
+ assert_true(count != 0);
+ return_value->value = value;
+ set_source_location(&return_value->location, file, line);
+ add_symbol_value(&global_function_result_map_head, &function_name, 1,
+ return_value, count);
+}
+
+
+/*
+ * Add a custom parameter checking function. If the event parameter is NULL
+ * the event structure is allocated internally by this function. If event
+ * parameter is provided it must be allocated on the heap and doesn't need to
+ * be deallocated by the caller.
+ */
+void _expect_check(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line,
+ const CheckParameterValue check_function,
+ const LargestIntegralType check_data,
+ CheckParameterEvent * const event, const int count) {
+ CheckParameterEvent * const check =
+ event ? event : (CheckParameterEvent*)malloc(sizeof(*check));
+ const char* symbols[] = {function, parameter};
+ check->parameter_name = parameter;
+ check->check_value = check_function;
+ check->check_value_data = check_data;
+ set_source_location(&check->location, file, line);
+ add_symbol_value(&global_function_parameter_map_head, symbols, 2, check,
+ count);
+}
+
+/*
+ * Add an call expectations that a particular function is called correctly.
+ * This is used for code under test that makes calls to several functions
+ * in depended upon components (mocks).
+ */
+
+void _expect_function_call(
+ const char * const function_name,
+ const char * const file,
+ const int line,
+ const int count)
+{
+ FuncOrderingValue *ordering;
+
+ assert_non_null(function_name);
+ assert_non_null(file);
+ assert_true(count != 0);
+
+ ordering = (FuncOrderingValue *)malloc(sizeof(*ordering));
+
+ set_source_location(&ordering->location, file, line);
+ ordering->function = function_name;
+
+ list_add_value(&global_call_ordering_head, ordering, count);
+}
+
+/* Returns 1 if the specified values are equal. If the values are not equal
+ * an error is displayed and 0 is returned. */
+static int values_equal_display_error(const LargestIntegralType left,
+ const LargestIntegralType right) {
+ const int equal = left == right;
+ if (!equal) {
+ cm_print_error(LargestIntegralTypePrintfFormat " != "
+ LargestIntegralTypePrintfFormat "\n", left, right);
+ }
+ return equal;
+}
+
+/*
+ * Returns 1 if the specified values are not equal. If the values are equal
+ * an error is displayed and 0 is returned. */
+static int values_not_equal_display_error(const LargestIntegralType left,
+ const LargestIntegralType right) {
+ const int not_equal = left != right;
+ if (!not_equal) {
+ cm_print_error(LargestIntegralTypePrintfFormat " == "
+ LargestIntegralTypePrintfFormat "\n", left, right);
+ }
+ return not_equal;
+}
+
+
+/*
+ * Determine whether value is contained within check_integer_set.
+ * If invert is 0 and the value is in the set 1 is returned, otherwise 0 is
+ * returned and an error is displayed. If invert is 1 and the value is not
+ * in the set 1 is returned, otherwise 0 is returned and an error is
+ * displayed.
+ */
+static int value_in_set_display_error(
+ const LargestIntegralType value,
+ const CheckIntegerSet * const check_integer_set, const int invert) {
+ int succeeded = invert;
+ assert_non_null(check_integer_set);
+ {
+ const LargestIntegralType * const set = check_integer_set->set;
+ const size_t size_of_set = check_integer_set->size_of_set;
+ size_t i;
+ for (i = 0; i < size_of_set; i++) {
+ if (set[i] == value) {
+ /* If invert = 0 and item is found, succeeded = 1. */
+ /* If invert = 1 and item is found, succeeded = 0. */
+ succeeded = !succeeded;
+ break;
+ }
+ }
+ if (succeeded) {
+ return 1;
+ }
+ cm_print_error(LargestIntegralTypePrintfFormatDecimal
+ " is %sin the set (",
+ value, invert ? "" : "not ");
+ for (i = 0; i < size_of_set; i++) {
+ cm_print_error(LargestIntegralTypePrintfFormat ", ", set[i]);
+ }
+ cm_print_error(")\n");
+ }
+ return 0;
+}
+
+
+/*
+ * Determine whether a value is within the specified range. If the value is
+ * within the specified range 1 is returned. If the value isn't within the
+ * specified range an error is displayed and 0 is returned.
+ */
+static int integer_in_range_display_error(
+ const LargestIntegralType value, const LargestIntegralType range_min,
+ const LargestIntegralType range_max) {
+ if (value >= range_min && value <= range_max) {
+ return 1;
+ }
+ cm_print_error(LargestIntegralTypePrintfFormatDecimal
+ " is not within the range "
+ LargestIntegralTypePrintfFormatDecimal "-"
+ LargestIntegralTypePrintfFormatDecimal "\n",
+ value, range_min, range_max);
+ return 0;
+}
+
+
+/*
+ * Determine whether a value is within the specified range. If the value
+ * is not within the range 1 is returned. If the value is within the
+ * specified range an error is displayed and zero is returned.
+ */
+static int integer_not_in_range_display_error(
+ const LargestIntegralType value, const LargestIntegralType range_min,
+ const LargestIntegralType range_max) {
+ if (value < range_min || value > range_max) {
+ return 1;
+ }
+ cm_print_error(LargestIntegralTypePrintfFormatDecimal
+ " is within the range "
+ LargestIntegralTypePrintfFormatDecimal "-"
+ LargestIntegralTypePrintfFormatDecimal "\n",
+ value, range_min, range_max);
+ return 0;
+}
+
+
+/*
+ * Determine whether the specified strings are equal. If the strings are equal
+ * 1 is returned. If they're not equal an error is displayed and 0 is
+ * returned.
+ */
+static int string_equal_display_error(
+ const char * const left, const char * const right) {
+ if (strcmp(left, right) == 0) {
+ return 1;
+ }
+ cm_print_error("\"%s\" != \"%s\"\n", left, right);
+ return 0;
+}
+
+
+/*
+ * Determine whether the specified strings are equal. If the strings are not
+ * equal 1 is returned. If they're not equal an error is displayed and 0 is
+ * returned
+ */
+static int string_not_equal_display_error(
+ const char * const left, const char * const right) {
+ if (strcmp(left, right) != 0) {
+ return 1;
+ }
+ cm_print_error("\"%s\" == \"%s\"\n", left, right);
+ return 0;
+}
+
+
+/*
+ * Determine whether the specified areas of memory are equal. If they're equal
+ * 1 is returned otherwise an error is displayed and 0 is returned.
+ */
+static int memory_equal_display_error(const char* const a, const char* const b,
+ const size_t size) {
+ size_t differences = 0;
+ size_t i;
+ for (i = 0; i < size; i++) {
+ const char l = a[i];
+ const char r = b[i];
+ if (l != r) {
+ if (differences < 16) {
+ cm_print_error("difference at offset %" PRIdS " 0x%02x 0x%02x\n",
+ i, l, r);
+ }
+ differences ++;
+ }
+ }
+ if (differences > 0) {
+ if (differences >= 16) {
+ cm_print_error("...\n");
+ }
+ cm_print_error("%"PRIdS " bytes of %p and %p differ\n",
+ differences, (void *)a, (void *)b);
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * Determine whether the specified areas of memory are not equal. If they're
+ * not equal 1 is returned otherwise an error is displayed and 0 is
+ * returned.
+ */
+static int memory_not_equal_display_error(
+ const char* const a, const char* const b, const size_t size) {
+ size_t same = 0;
+ size_t i;
+ for (i = 0; i < size; i++) {
+ const char l = a[i];
+ const char r = b[i];
+ if (l == r) {
+ same ++;
+ }
+ }
+ if (same == size) {
+ cm_print_error("%"PRIdS "bytes of %p and %p the same\n",
+ same, (void *)a, (void *)b);
+ return 0;
+ }
+ return 1;
+}
+
+
+/* CheckParameterValue callback to check whether a value is within a set. */
+static int check_in_set(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ return value_in_set_display_error(value,
+ cast_largest_integral_type_to_pointer(CheckIntegerSet*,
+ check_value_data), 0);
+}
+
+
+/* CheckParameterValue callback to check whether a value isn't within a set. */
+static int check_not_in_set(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ return value_in_set_display_error(value,
+ cast_largest_integral_type_to_pointer(CheckIntegerSet*,
+ check_value_data), 1);
+}
+
+
+/* Create the callback data for check_in_set() or check_not_in_set() and
+ * register a check event. */
+static void expect_set(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line,
+ const LargestIntegralType values[], const size_t number_of_values,
+ const CheckParameterValue check_function, const int count) {
+ CheckIntegerSet * const check_integer_set =
+ (CheckIntegerSet*)malloc(sizeof(*check_integer_set) +
+ (sizeof(values[0]) * number_of_values));
+ LargestIntegralType * const set = (LargestIntegralType*)(
+ check_integer_set + 1);
+ declare_initialize_value_pointer_pointer(check_data, check_integer_set);
+ assert_non_null(values);
+ assert_true(number_of_values);
+ memcpy(set, values, number_of_values * sizeof(values[0]));
+ check_integer_set->set = set;
+ check_integer_set->size_of_set = number_of_values;
+ _expect_check(
+ function, parameter, file, line, check_function,
+ check_data.value, &check_integer_set->event, count);
+}
+
+
+/* Add an event to check whether a value is in a set. */
+void _expect_in_set(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line,
+ const LargestIntegralType values[], const size_t number_of_values,
+ const int count) {
+ expect_set(function, parameter, file, line, values, number_of_values,
+ check_in_set, count);
+}
+
+
+/* Add an event to check whether a value isn't in a set. */
+void _expect_not_in_set(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line,
+ const LargestIntegralType values[], const size_t number_of_values,
+ const int count) {
+ expect_set(function, parameter, file, line, values, number_of_values,
+ check_not_in_set, count);
+}
+
+
+/* CheckParameterValue callback to check whether a value is within a range. */
+static int check_in_range(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ CheckIntegerRange * const check_integer_range =
+ cast_largest_integral_type_to_pointer(CheckIntegerRange*,
+ check_value_data);
+ assert_non_null(check_integer_range);
+ return integer_in_range_display_error(value, check_integer_range->minimum,
+ check_integer_range->maximum);
+}
+
+
+/* CheckParameterValue callback to check whether a value is not within a range. */
+static int check_not_in_range(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ CheckIntegerRange * const check_integer_range =
+ cast_largest_integral_type_to_pointer(CheckIntegerRange*,
+ check_value_data);
+ assert_non_null(check_integer_range);
+ return integer_not_in_range_display_error(
+ value, check_integer_range->minimum, check_integer_range->maximum);
+}
+
+
+/* Create the callback data for check_in_range() or check_not_in_range() and
+ * register a check event. */
+static void expect_range(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line,
+ const LargestIntegralType minimum, const LargestIntegralType maximum,
+ const CheckParameterValue check_function, const int count) {
+ CheckIntegerRange * const check_integer_range =
+ (CheckIntegerRange*)malloc(sizeof(*check_integer_range));
+ declare_initialize_value_pointer_pointer(check_data, check_integer_range);
+ check_integer_range->minimum = minimum;
+ check_integer_range->maximum = maximum;
+ _expect_check(function, parameter, file, line, check_function,
+ check_data.value, &check_integer_range->event, count);
+}
+
+
+/* Add an event to determine whether a parameter is within a range. */
+void _expect_in_range(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line,
+ const LargestIntegralType minimum, const LargestIntegralType maximum,
+ const int count) {
+ expect_range(function, parameter, file, line, minimum, maximum,
+ check_in_range, count);
+}
+
+
+/* Add an event to determine whether a parameter is not within a range. */
+void _expect_not_in_range(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line,
+ const LargestIntegralType minimum, const LargestIntegralType maximum,
+ const int count) {
+ expect_range(function, parameter, file, line, minimum, maximum,
+ check_not_in_range, count);
+}
+
+
+/* CheckParameterValue callback to check whether a value is equal to an
+ * expected value. */
+static int check_value(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ return values_equal_display_error(value, check_value_data);
+}
+
+
+/* Add an event to check a parameter equals an expected value. */
+void _expect_value(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line,
+ const LargestIntegralType value, const int count) {
+ _expect_check(function, parameter, file, line, check_value, value, NULL,
+ count);
+}
+
+
+/* CheckParameterValue callback to check whether a value is not equal to an
+ * expected value. */
+static int check_not_value(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ return values_not_equal_display_error(value, check_value_data);
+}
+
+
+/* Add an event to check a parameter is not equal to an expected value. */
+void _expect_not_value(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line,
+ const LargestIntegralType value, const int count) {
+ _expect_check(function, parameter, file, line, check_not_value, value,
+ NULL, count);
+}
+
+
+/* CheckParameterValue callback to check whether a parameter equals a string. */
+static int check_string(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ return string_equal_display_error(
+ cast_largest_integral_type_to_pointer(char*, value),
+ cast_largest_integral_type_to_pointer(char*, check_value_data));
+}
+
+
+/* Add an event to check whether a parameter is equal to a string. */
+void _expect_string(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line, const char* string,
+ const int count) {
+ declare_initialize_value_pointer_pointer(string_pointer,
+ discard_const(string));
+ _expect_check(function, parameter, file, line, check_string,
+ string_pointer.value, NULL, count);
+}
+
+
+/* CheckParameterValue callback to check whether a parameter is not equals to
+ * a string. */
+static int check_not_string(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ return string_not_equal_display_error(
+ cast_largest_integral_type_to_pointer(char*, value),
+ cast_largest_integral_type_to_pointer(char*, check_value_data));
+}
+
+
+/* Add an event to check whether a parameter is not equal to a string. */
+void _expect_not_string(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line, const char* string,
+ const int count) {
+ declare_initialize_value_pointer_pointer(string_pointer,
+ discard_const(string));
+ _expect_check(function, parameter, file, line, check_not_string,
+ string_pointer.value, NULL, count);
+}
+
+/* CheckParameterValue callback to check whether a parameter equals an area of
+ * memory. */
+static int check_memory(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ CheckMemoryData * const check = cast_largest_integral_type_to_pointer(
+ CheckMemoryData*, check_value_data);
+ assert_non_null(check);
+ return memory_equal_display_error(
+ cast_largest_integral_type_to_pointer(const char*, value),
+ (const char*)check->memory, check->size);
+}
+
+
+/* Create the callback data for check_memory() or check_not_memory() and
+ * register a check event. */
+static void expect_memory_setup(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line,
+ const void * const memory, const size_t size,
+ const CheckParameterValue check_function, const int count) {
+ CheckMemoryData * const check_data =
+ (CheckMemoryData*)malloc(sizeof(*check_data) + size);
+ void * const mem = (void*)(check_data + 1);
+ declare_initialize_value_pointer_pointer(check_data_pointer, check_data);
+ assert_non_null(memory);
+ assert_true(size);
+ memcpy(mem, memory, size);
+ check_data->memory = mem;
+ check_data->size = size;
+ _expect_check(function, parameter, file, line, check_function,
+ check_data_pointer.value, &check_data->event, count);
+}
+
+
+/* Add an event to check whether a parameter matches an area of memory. */
+void _expect_memory(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line, const void* const memory,
+ const size_t size, const int count) {
+ expect_memory_setup(function, parameter, file, line, memory, size,
+ check_memory, count);
+}
+
+
+/* CheckParameterValue callback to check whether a parameter is not equal to
+ * an area of memory. */
+static int check_not_memory(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ CheckMemoryData * const check = cast_largest_integral_type_to_pointer(
+ CheckMemoryData*, check_value_data);
+ assert_non_null(check);
+ return memory_not_equal_display_error(
+ cast_largest_integral_type_to_pointer(const char*, value),
+ (const char*)check->memory,
+ check->size);
+}
+
+
+/* Add an event to check whether a parameter doesn't match an area of memory. */
+void _expect_not_memory(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line, const void* const memory,
+ const size_t size, const int count) {
+ expect_memory_setup(function, parameter, file, line, memory, size,
+ check_not_memory, count);
+}
+
+
+/* CheckParameterValue callback that always returns 1. */
+static int check_any(const LargestIntegralType value,
+ const LargestIntegralType check_value_data) {
+ (void)value;
+ (void)check_value_data;
+ return 1;
+}
+
+
+/* Add an event to allow any value for a parameter. */
+void _expect_any(
+ const char* const function, const char* const parameter,
+ const char* const file, const int line, const int count) {
+ _expect_check(function, parameter, file, line, check_any, 0, NULL,
+ count);
+}
+
+
+void _check_expected(
+ const char * const function_name, const char * const parameter_name,
+ const char* file, const int line, const LargestIntegralType value) {
+ void *result = NULL;
+ const char* symbols[] = {function_name, parameter_name};
+ const int rc = get_symbol_value(&global_function_parameter_map_head,
+ symbols, 2, &result);
+ if (rc) {
+ CheckParameterEvent * const check = (CheckParameterEvent*)result;
+ int check_succeeded;
+ global_last_parameter_location = check->location;
+ check_succeeded = check->check_value(value, check->check_value_data);
+ if (rc == 1) {
+ free(check);
+ }
+ if (!check_succeeded) {
+ cm_print_error(SOURCE_LOCATION_FORMAT
+ ": error: Check of parameter %s, function %s failed\n"
+ SOURCE_LOCATION_FORMAT
+ ": note: Expected parameter declared here\n",
+ file, line,
+ parameter_name, function_name,
+ global_last_parameter_location.file,
+ global_last_parameter_location.line);
+ _fail(file, line);
+ }
+ } else {
+ cm_print_error(SOURCE_LOCATION_FORMAT ": error: Could not get value "
+ "to check parameter %s of function %s\n", file, line,
+ parameter_name, function_name);
+ if (source_location_is_set(&global_last_parameter_location)) {
+ cm_print_error(SOURCE_LOCATION_FORMAT
+ ": note: Previously declared parameter value was declared here\n",
+ global_last_parameter_location.file,
+ global_last_parameter_location.line);
+ } else {
+ cm_print_error("There were no previously declared parameter values "
+ "for this test.\n");
+ }
+ exit_test(1);
+ }
+}
+
+
+/* Replacement for assert. */
+void mock_assert(const int result, const char* const expression,
+ const char* const file, const int line) {
+ if (!result) {
+ if (global_expecting_assert) {
+ global_last_failed_assert = expression;
+ longjmp(global_expect_assert_env, result);
+ } else {
+ cm_print_error("ASSERT: %s\n", expression);
+ _fail(file, line);
+ }
+ }
+}
+
+
+void _assert_true(const LargestIntegralType result,
+ const char * const expression,
+ const char * const file, const int line) {
+ if (!result) {
+ cm_print_error("%s\n", expression);
+ _fail(file, line);
+ }
+}
+
+void _assert_return_code(const LargestIntegralType result,
+ size_t rlen,
+ const LargestIntegralType error,
+ const char * const expression,
+ const char * const file,
+ const int line)
+{
+ LargestIntegralType valmax;
+
+
+ switch (rlen) {
+ case 1:
+ valmax = 255;
+ break;
+ case 2:
+ valmax = 32767;
+ break;
+ case 4:
+ valmax = 2147483647;
+ break;
+ case 8:
+ default:
+ if (rlen > sizeof(valmax)) {
+ valmax = 2147483647;
+ } else {
+ valmax = 9223372036854775807L;
+ }
+ break;
+ }
+
+ if (result > valmax - 1) {
+ if (error > 0) {
+ cm_print_error("%s < 0, errno("
+ LargestIntegralTypePrintfFormatDecimal "): %s\n",
+ expression, error, strerror((int)error));
+ } else {
+ cm_print_error("%s < 0\n", expression);
+ }
+ _fail(file, line);
+ }
+}
+
+void _assert_int_equal(
+ const LargestIntegralType a, const LargestIntegralType b,
+ const char * const file, const int line) {
+ if (!values_equal_display_error(a, b)) {
+ _fail(file, line);
+ }
+}
+
+
+void _assert_int_not_equal(
+ const LargestIntegralType a, const LargestIntegralType b,
+ const char * const file, const int line) {
+ if (!values_not_equal_display_error(a, b)) {
+ _fail(file, line);
+ }
+}
+
+
+void _assert_string_equal(const char * const a, const char * const b,
+ const char * const file, const int line) {
+ if (!string_equal_display_error(a, b)) {
+ _fail(file, line);
+ }
+}
+
+
+void _assert_string_not_equal(const char * const a, const char * const b,
+ const char *file, const int line) {
+ if (!string_not_equal_display_error(a, b)) {
+ _fail(file, line);
+ }
+}
+
+
+void _assert_memory_equal(const void * const a, const void * const b,
+ const size_t size, const char* const file,
+ const int line) {
+ if (!memory_equal_display_error((const char*)a, (const char*)b, size)) {
+ _fail(file, line);
+ }
+}
+
+
+void _assert_memory_not_equal(const void * const a, const void * const b,
+ const size_t size, const char* const file,
+ const int line) {
+ if (!memory_not_equal_display_error((const char*)a, (const char*)b,
+ size)) {
+ _fail(file, line);
+ }
+}
+
+
+void _assert_in_range(
+ const LargestIntegralType value, const LargestIntegralType minimum,
+ const LargestIntegralType maximum, const char* const file,
+ const int line) {
+ if (!integer_in_range_display_error(value, minimum, maximum)) {
+ _fail(file, line);
+ }
+}
+
+void _assert_not_in_range(
+ const LargestIntegralType value, const LargestIntegralType minimum,
+ const LargestIntegralType maximum, const char* const file,
+ const int line) {
+ if (!integer_not_in_range_display_error(value, minimum, maximum)) {
+ _fail(file, line);
+ }
+}
+
+void _assert_in_set(const LargestIntegralType value,
+ const LargestIntegralType values[],
+ const size_t number_of_values, const char* const file,
+ const int line) {
+ CheckIntegerSet check_integer_set;
+ check_integer_set.set = values;
+ check_integer_set.size_of_set = number_of_values;
+ if (!value_in_set_display_error(value, &check_integer_set, 0)) {
+ _fail(file, line);
+ }
+}
+
+void _assert_not_in_set(const LargestIntegralType value,
+ const LargestIntegralType values[],
+ const size_t number_of_values, const char* const file,
+ const int line) {
+ CheckIntegerSet check_integer_set;
+ check_integer_set.set = values;
+ check_integer_set.size_of_set = number_of_values;
+ if (!value_in_set_display_error(value, &check_integer_set, 1)) {
+ _fail(file, line);
+ }
+}
+
+
+/* Get the list of allocated blocks. */
+static ListNode* get_allocated_blocks_list(void) {
+ /* If it initialized, initialize the list of allocated blocks. */
+ if (!global_allocated_blocks.value) {
+ list_initialize(&global_allocated_blocks);
+ global_allocated_blocks.value = (void*)1;
+ }
+ return &global_allocated_blocks;
+}
+
+static void *libc_malloc(size_t size)
+{
+#undef malloc
+ return malloc(size);
+#define malloc test_malloc
+}
+
+static void libc_free(void *ptr)
+{
+#undef free
+ free(ptr);
+#define free test_free
+}
+
+static void *libc_realloc(void *ptr, size_t size)
+{
+#undef realloc
+ return realloc(ptr, size);
+#define realloc test_realloc
+}
+
+static void vcm_print_error(const char* const format,
+ va_list args) CMOCKA_PRINTF_ATTRIBUTE(1, 0);
+
+/* It's important to use the libc malloc and free here otherwise
+ * the automatic free of leaked blocks can reap the error messages
+ */
+static void vcm_print_error(const char* const format, va_list args)
+{
+ char buffer[1024];
+ size_t msg_len = 0;
+ va_list ap;
+ int len;
+ va_copy(ap, args);
+
+ len = vsnprintf(buffer, sizeof(buffer), format, args);
+ if (len < 0) {
+ /* TODO */
+ goto end;
+ }
+
+ if (cm_error_message == NULL) {
+ /* CREATE MESSAGE */
+
+ cm_error_message = libc_malloc(len + 1);
+ if (cm_error_message == NULL) {
+ /* TODO */
+ goto end;
+ }
+ } else {
+ /* APPEND MESSAGE */
+ char *tmp;
+
+ msg_len = strlen(cm_error_message);
+ tmp = libc_realloc(cm_error_message, msg_len + len + 1);
+ if (tmp == NULL) {
+ goto end;
+ }
+ cm_error_message = tmp;
+ }
+
+ if (((size_t)len) < sizeof(buffer)) {
+ /* Use len + 1 to also copy '\0' */
+ memcpy(cm_error_message + msg_len, buffer, len + 1);
+ } else {
+ vsnprintf(cm_error_message + msg_len, len, format, ap);
+ }
+end:
+ va_end(ap);
+
+}
+
+static void vcm_free_error(char *err_msg)
+{
+ libc_free(err_msg);
+}
+
+/* Use the real malloc in this function. */
+#undef malloc
+void* _test_malloc(const size_t size, const char* file, const int line) {
+ char *ptr = NULL;
+ MallocBlockInfo block_info;
+ ListNode * const block_list = get_allocated_blocks_list();
+ size_t allocate_size;
+ char *block = NULL;
+
+ allocate_size = size + (MALLOC_GUARD_SIZE * 2) +
+ sizeof(struct MallocBlockInfoData) + MALLOC_ALIGNMENT;
+ assert_true(allocate_size > size);
+
+ block = (char *)malloc(allocate_size);
+ assert_non_null(block);
+
+ /* Calculate the returned address. */
+ ptr = (char*)(((size_t)block + MALLOC_GUARD_SIZE +
+ sizeof(struct MallocBlockInfoData) +
+ MALLOC_ALIGNMENT) & ~(MALLOC_ALIGNMENT - 1));
+
+ /* Initialize the guard blocks. */
+ memset(ptr - MALLOC_GUARD_SIZE, MALLOC_GUARD_PATTERN, MALLOC_GUARD_SIZE);
+ memset(ptr + size, MALLOC_GUARD_PATTERN, MALLOC_GUARD_SIZE);
+ memset(ptr, MALLOC_ALLOC_PATTERN, size);
+
+ block_info.ptr = ptr - (MALLOC_GUARD_SIZE +
+ sizeof(struct MallocBlockInfoData));
+ set_source_location(&block_info.data->location, file, line);
+ block_info.data->allocated_size = allocate_size;
+ block_info.data->size = size;
+ block_info.data->block = block;
+ block_info.data->node.value = block_info.ptr;
+ list_add(block_list, &block_info.data->node);
+ return ptr;
+}
+#define malloc test_malloc
+
+
+void* _test_calloc(const size_t number_of_elements, const size_t size,
+ const char* file, const int line) {
+ void* const ptr = _test_malloc(number_of_elements * size, file, line);
+ if (ptr) {
+ memset(ptr, 0, number_of_elements * size);
+ }
+ return ptr;
+}
+
+
+/* Use the real free in this function. */
+#undef free
+void _test_free(void* const ptr, const char* file, const int line) {
+ unsigned int i;
+ char *block = discard_const_p(char, ptr);
+ MallocBlockInfo block_info;
+
+ if (ptr == NULL) {
+ return;
+ }
+
+ _assert_true(cast_ptr_to_largest_integral_type(ptr), "ptr", file, line);
+ block_info.ptr = block - (MALLOC_GUARD_SIZE +
+ sizeof(struct MallocBlockInfoData));
+ /* Check the guard blocks. */
+ {
+ char *guards[2] = {block - MALLOC_GUARD_SIZE,
+ block + block_info.data->size};
+ for (i = 0; i < ARRAY_SIZE(guards); i++) {
+ unsigned int j;
+ char * const guard = guards[i];
+ for (j = 0; j < MALLOC_GUARD_SIZE; j++) {
+ const char diff = guard[j] - MALLOC_GUARD_PATTERN;
+ if (diff) {
+ cm_print_error(SOURCE_LOCATION_FORMAT
+ ": error: Guard block of %p size=%lu is corrupt\n"
+ SOURCE_LOCATION_FORMAT ": note: allocated here at %p\n",
+ file,
+ line,
+ ptr,
+ (unsigned long)block_info.data->size,
+ block_info.data->location.file,
+ block_info.data->location.line,
+ (void *)&guard[j]);
+ _fail(file, line);
+ }
+ }
+ }
+ }
+ list_remove(&block_info.data->node, NULL, NULL);
+
+ block = discard_const_p(char, block_info.data->block);
+ memset(block, MALLOC_FREE_PATTERN, block_info.data->allocated_size);
+ free(block);
+}
+#define free test_free
+
+#undef realloc
+void *_test_realloc(void *ptr,
+ const size_t size,
+ const char *file,
+ const int line)
+{
+ MallocBlockInfo block_info;
+ char *block = ptr;
+ size_t block_size = size;
+ void *new_block;
+
+ if (ptr == NULL) {
+ return _test_malloc(size, file, line);
+ }
+
+ if (size == 0) {
+ _test_free(ptr, file, line);
+ return NULL;
+ }
+
+ block_info.ptr = block - (MALLOC_GUARD_SIZE +
+ sizeof(struct MallocBlockInfoData));
+
+ new_block = _test_malloc(size, file, line);
+ if (new_block == NULL) {
+ return NULL;
+ }
+
+ if (block_info.data->size < size) {
+ block_size = block_info.data->size;
+ }
+
+ memcpy(new_block, ptr, block_size);
+
+ /* Free previous memory */
+ _test_free(ptr, file, line);
+
+ return new_block;
+}
+#define realloc test_realloc
+
+/* Crudely checkpoint the current heap state. */
+static const ListNode* check_point_allocated_blocks(void) {
+ return get_allocated_blocks_list()->prev;
+}
+
+
+/* Display the blocks allocated after the specified check point. This
+ * function returns the number of blocks displayed. */
+static size_t display_allocated_blocks(const ListNode * const check_point) {
+ const ListNode * const head = get_allocated_blocks_list();
+ const ListNode *node;
+ size_t allocated_blocks = 0;
+ assert_non_null(check_point);
+ assert_non_null(check_point->next);
+
+ for (node = check_point->next; node != head; node = node->next) {
+ const MallocBlockInfo block_info = {
+ .ptr = discard_const(node->value),
+ };
+ assert_non_null(block_info.ptr);
+
+ if (allocated_blocks == 0) {
+ cm_print_error("Blocks allocated...\n");
+ }
+ cm_print_error(SOURCE_LOCATION_FORMAT ": note: block %p allocated here\n",
+ block_info.data->location.file,
+ block_info.data->location.line,
+ block_info.data->block);
+ allocated_blocks++;
+ }
+ return allocated_blocks;
+}
+
+
+/* Free all blocks allocated after the specified check point. */
+static void free_allocated_blocks(const ListNode * const check_point) {
+ const ListNode * const head = get_allocated_blocks_list();
+ const ListNode *node;
+ assert_non_null(check_point);
+
+ node = check_point->next;
+ assert_non_null(node);
+
+ while (node != head) {
+ const MallocBlockInfo block_info = {
+ .ptr = discard_const(node->value),
+ };
+ node = node->next;
+ free(discard_const_p(char, block_info.data) +
+ sizeof(struct MallocBlockInfoData) +
+ MALLOC_GUARD_SIZE);
+ }
+}
+
+
+/* Fail if any any blocks are allocated after the specified check point. */
+static void fail_if_blocks_allocated(const ListNode * const check_point,
+ const char * const test_name) {
+ const size_t allocated_blocks = display_allocated_blocks(check_point);
+ if (allocated_blocks > 0) {
+ free_allocated_blocks(check_point);
+ cm_print_error("ERROR: %s leaked %zu block(s)\n", test_name,
+ allocated_blocks);
+ exit_test(1);
+ }
+}
+
+
+void _fail(const char * const file, const int line) {
+ enum cm_message_output output = cm_get_output();
+
+ switch(output) {
+ case CM_OUTPUT_STDOUT:
+ cm_print_error("[ LINE ] --- " SOURCE_LOCATION_FORMAT ": error: Failure!", file, line);
+ break;
+ default:
+ cm_print_error(SOURCE_LOCATION_FORMAT ": error: Failure!", file, line);
+ break;
+ }
+ exit_test(1);
+}
+
+
+#ifndef _WIN32
+static void exception_handler(int sig) {
+ const char *sig_strerror = "";
+
+#ifdef HAVE_STRSIGNAL
+ sig_strerror = strsignal(sig);
+#endif
+
+ cm_print_error("Test failed with exception: %s(%d)",
+ sig_strerror, sig);
+ exit_test(1);
+}
+
+#else /* _WIN32 */
+
+static LONG WINAPI exception_filter(EXCEPTION_POINTERS *exception_pointers) {
+ EXCEPTION_RECORD * const exception_record =
+ exception_pointers->ExceptionRecord;
+ const DWORD code = exception_record->ExceptionCode;
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(exception_codes); i++) {
+ const ExceptionCodeInfo * const code_info = &exception_codes[i];
+ if (code == code_info->code) {
+ static int shown_debug_message = 0;
+ fflush(stdout);
+ cm_print_error("%s occurred at %p.\n", code_info->description,
+ exception_record->ExceptionAddress);
+ if (!shown_debug_message) {
+ cm_print_error(
+ "\n"
+ "To debug in Visual Studio...\n"
+ "1. Select menu item File->Open Project\n"
+ "2. Change 'Files of type' to 'Executable Files'\n"
+ "3. Open this executable.\n"
+ "4. Select menu item Debug->Start\n"
+ "\n"
+ "Alternatively, set the environment variable \n"
+ "UNIT_TESTING_DEBUG to 1 and rebuild this executable, \n"
+ "then click 'Debug' in the popup dialog box.\n"
+ "\n");
+ shown_debug_message = 1;
+ }
+ exit_test(0);
+ return EXCEPTION_EXECUTE_HANDLER;
+ }
+ }
+ return EXCEPTION_CONTINUE_SEARCH;
+}
+#endif /* !_WIN32 */
+
+void cm_print_error(const char * const format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ if (cm_error_message_enabled) {
+ vcm_print_error(format, args);
+ } else {
+ vprint_error(format, args);
+ }
+ va_end(args);
+}
+
+/* Standard output and error print methods. */
+void vprint_message(const char* const format, va_list args) {
+ char buffer[1024];
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ printf("%s", buffer);
+ fflush(stdout);
+#ifdef _WIN32
+ OutputDebugString(buffer);
+#endif /* _WIN32 */
+}
+
+
+void vprint_error(const char* const format, va_list args) {
+ char buffer[1024];
+ vsnprintf(buffer, sizeof(buffer), format, args);
+ fprintf(stderr, "%s", buffer);
+ fflush(stderr);
+#ifdef _WIN32
+ OutputDebugString(buffer);
+#endif /* _WIN32 */
+}
+
+
+void print_message(const char* const format, ...) {
+ va_list args;
+ va_start(args, format);
+ vprint_message(format, args);
+ va_end(args);
+}
+
+
+void print_error(const char* const format, ...) {
+ va_list args;
+ va_start(args, format);
+ vprint_error(format, args);
+ va_end(args);
+}
+
+/* New formatter */
+static enum cm_message_output cm_get_output(void)
+{
+ enum cm_message_output output = global_msg_output;
+ char *env;
+
+ env = getenv("CMOCKA_MESSAGE_OUTPUT");
+ if (env != NULL) {
+ if (strcasecmp(env, "STDOUT") == 0) {
+ output = CM_OUTPUT_STDOUT;
+ } else if (strcasecmp(env, "SUBUNIT") == 0) {
+ output = CM_OUTPUT_SUBUNIT;
+ } else if (strcasecmp(env, "TAP") == 0) {
+ output = CM_OUTPUT_TAP;
+ } else if (strcasecmp(env, "XML") == 0) {
+ output = CM_OUTPUT_XML;
+ }
+ }
+
+ return output;
+}
+
+enum cm_printf_type {
+ PRINTF_TEST_START,
+ PRINTF_TEST_SUCCESS,
+ PRINTF_TEST_FAILURE,
+ PRINTF_TEST_ERROR,
+ PRINTF_TEST_SKIPPED,
+};
+
+static int xml_printed;
+static int file_append;
+
+static void cmprintf_group_finish_xml(const char *group_name,
+ size_t total_executed,
+ size_t total_failed,
+ size_t total_errors,
+ size_t total_skipped,
+ double total_runtime,
+ struct CMUnitTestState *cm_tests)
+{
+ FILE *fp = stdout;
+ int file_opened = 0;
+ int multiple_files = 0;
+ char *env;
+ size_t i;
+
+ env = getenv("CMOCKA_XML_FILE");
+ if (env != NULL) {
+ char buf[1024];
+ int rc;
+
+ snprintf(buf, sizeof(buf), "%s", env);
+
+ rc = c_strreplace(buf, sizeof(buf), "%g", group_name, &multiple_files);
+ if (rc < 0) {
+ snprintf(buf, sizeof(buf), "%s", env);
+ }
+
+ fp = fopen(buf, "r");
+ if (fp == NULL) {
+ fp = fopen(buf, "w");
+ if (fp != NULL) {
+ file_append = 1;
+ file_opened = 1;
+ } else {
+ fp = stderr;
+ }
+ } else {
+ fclose(fp);
+ if (file_append) {
+ fp = fopen(buf, "a");
+ if (fp != NULL) {
+ file_opened = 1;
+ xml_printed = 1;
+ } else {
+ fp = stderr;
+ }
+ } else {
+ fp = stderr;
+ }
+ }
+ }
+
+ if (!xml_printed || (file_opened && !file_append)) {
+ fprintf(fp, "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n");
+ if (!file_opened) {
+ xml_printed = 1;
+ }
+ }
+
+ fprintf(fp, "<testsuites>\n");
+ fprintf(fp, " <testsuite name=\"%s\" time=\"%.3f\" "
+ "tests=\"%u\" failures=\"%u\" errors=\"%u\" skipped=\"%u\" >\n",
+ group_name,
+ total_runtime, /* seconds */
+ (unsigned)total_executed,
+ (unsigned)total_failed,
+ (unsigned)total_errors,
+ (unsigned)total_skipped);
+
+ for (i = 0; i < total_executed; i++) {
+ struct CMUnitTestState *cmtest = &cm_tests[i];
+
+ fprintf(fp, " <testcase name=\"%s\" time=\"%.3f\" >\n",
+ cmtest->test->name, cmtest->runtime);
+
+ switch (cmtest->status) {
+ case CM_TEST_ERROR:
+ case CM_TEST_FAILED:
+ if (cmtest->error_message != NULL) {
+ fprintf(fp, " <failure><![CDATA[%s]]></failure>\n",
+ cmtest->error_message);
+ } else {
+ fprintf(fp, " <failure message=\"Unknown error\" />\n");
+ }
+ break;
+ case CM_TEST_SKIPPED:
+ fprintf(fp, " <skipped/>\n");
+ break;
+
+ case CM_TEST_PASSED:
+ case CM_TEST_NOT_STARTED:
+ break;
+ }
+
+ fprintf(fp, " </testcase>\n");
+ }
+
+ fprintf(fp, " </testsuite>\n");
+ fprintf(fp, "</testsuites>\n");
+
+ if (file_opened) {
+ fclose(fp);
+ }
+}
+
+static void cmprintf_group_start_standard(const size_t num_tests)
+{
+ print_message("[==========] Running %u test(s).\n",
+ (unsigned)num_tests);
+}
+
+static void cmprintf_group_finish_standard(size_t total_executed,
+ size_t total_passed,
+ size_t total_failed,
+ size_t total_errors,
+ size_t total_skipped,
+ struct CMUnitTestState *cm_tests)
+{
+ size_t i;
+
+ print_message("[==========] %u test(s) run.\n", (unsigned)total_executed);
+ print_error("[ PASSED ] %u test(s).\n",
+ (unsigned)(total_passed));
+
+ if (total_skipped) {
+ print_error("[ SKIPPED ] %"PRIdS " test(s), listed below:\n", total_skipped);
+ for (i = 0; i < total_executed; i++) {
+ struct CMUnitTestState *cmtest = &cm_tests[i];
+
+ if (cmtest->status == CM_TEST_SKIPPED) {
+ print_error("[ SKIPPED ] %s\n", cmtest->test->name);
+ }
+ }
+ print_error("\n %u SKIPPED TEST(S)\n", (unsigned)(total_skipped));
+ }
+
+ if (total_failed) {
+ print_error("[ FAILED ] %"PRIdS " test(s), listed below:\n", total_failed);
+ for (i = 0; i < total_executed; i++) {
+ struct CMUnitTestState *cmtest = &cm_tests[i];
+
+ if (cmtest->status == CM_TEST_FAILED) {
+ print_error("[ FAILED ] %s\n", cmtest->test->name);
+ }
+ }
+ print_error("\n %u FAILED TEST(S)\n",
+ (unsigned)(total_failed + total_errors));
+ }
+}
+
+static void cmprintf_standard(enum cm_printf_type type,
+ const char *test_name,
+ const char *error_message)
+{
+ switch (type) {
+ case PRINTF_TEST_START:
+ print_message("[ RUN ] %s\n", test_name);
+ break;
+ case PRINTF_TEST_SUCCESS:
+ print_message("[ OK ] %s\n", test_name);
+ break;
+ case PRINTF_TEST_FAILURE:
+ if (error_message != NULL) {
+ print_error("[ ERROR ] --- %s\n", error_message);
+ }
+ print_message("[ FAILED ] %s\n", test_name);
+ break;
+ case PRINTF_TEST_SKIPPED:
+ print_message("[ SKIPPED ] %s\n", test_name);
+ break;
+ case PRINTF_TEST_ERROR:
+ if (error_message != NULL) {
+ print_error("%s\n", error_message);
+ }
+ print_error("[ ERROR ] %s\n", test_name);
+ break;
+ }
+}
+
+static void cmprintf_group_start_tap(const size_t num_tests)
+{
+ print_message("1..%u\n", (unsigned)num_tests);
+}
+
+static void cmprintf_group_finish_tap(const char *group_name,
+ size_t total_executed,
+ size_t total_passed,
+ size_t total_skipped)
+{
+ const char *status = "not ok";
+ if (total_passed + total_skipped == total_executed) {
+ status = "ok";
+ }
+ print_message("# %s - %s\n", status, group_name);
+}
+
+static void cmprintf_tap(enum cm_printf_type type,
+ uint32_t test_number,
+ const char *test_name,
+ const char *error_message)
+{
+ switch (type) {
+ case PRINTF_TEST_START:
+ break;
+ case PRINTF_TEST_SUCCESS:
+ print_message("ok %u - %s\n", (unsigned)test_number, test_name);
+ break;
+ case PRINTF_TEST_FAILURE:
+ print_message("not ok %u - %s\n", (unsigned)test_number, test_name);
+ if (error_message != NULL) {
+ char *msg;
+ char *p;
+
+ msg = strdup(error_message);
+ if (msg == NULL) {
+ return;
+ }
+ p = msg;
+
+ while (p[0] != '\0') {
+ char *q = p;
+
+ p = strchr(q, '\n');
+ if (p != NULL) {
+ p[0] = '\0';
+ }
+
+ print_message("# %s\n", q);
+
+ if (p == NULL) {
+ break;
+ }
+ p++;
+ }
+ libc_free(msg);
+ }
+ break;
+ case PRINTF_TEST_SKIPPED:
+ print_message("not ok %u # SKIP %s\n", (unsigned)test_number, test_name);
+ break;
+ case PRINTF_TEST_ERROR:
+ print_message("not ok %u - %s %s\n",
+ (unsigned)test_number, test_name, error_message);
+ break;
+ }
+}
+
+static void cmprintf_subunit(enum cm_printf_type type,
+ const char *test_name,
+ const char *error_message)
+{
+ switch (type) {
+ case PRINTF_TEST_START:
+ print_message("test: %s\n", test_name);
+ break;
+ case PRINTF_TEST_SUCCESS:
+ print_message("success: %s\n", test_name);
+ break;
+ case PRINTF_TEST_FAILURE:
+ print_message("failure: %s", test_name);
+ if (error_message != NULL) {
+ print_message(" [\n%s\n]\n", error_message);
+ }
+ break;
+ case PRINTF_TEST_SKIPPED:
+ print_message("skip: %s\n", test_name);
+ break;
+ case PRINTF_TEST_ERROR:
+ print_message("error: %s [ %s ]\n", test_name, error_message);
+ break;
+ }
+}
+
+static void cmprintf_group_start(const size_t num_tests)
+{
+ enum cm_message_output output;
+
+ output = cm_get_output();
+
+ switch (output) {
+ case CM_OUTPUT_STDOUT:
+ cmprintf_group_start_standard(num_tests);
+ break;
+ case CM_OUTPUT_SUBUNIT:
+ break;
+ case CM_OUTPUT_TAP:
+ cmprintf_group_start_tap(num_tests);
+ break;
+ case CM_OUTPUT_XML:
+ break;
+ }
+}
+
+static void cmprintf_group_finish(const char *group_name,
+ size_t total_executed,
+ size_t total_passed,
+ size_t total_failed,
+ size_t total_errors,
+ size_t total_skipped,
+ double total_runtime,
+ struct CMUnitTestState *cm_tests)
+{
+ enum cm_message_output output;
+
+ output = cm_get_output();
+
+ switch (output) {
+ case CM_OUTPUT_STDOUT:
+ cmprintf_group_finish_standard(total_executed,
+ total_passed,
+ total_failed,
+ total_errors,
+ total_skipped,
+ cm_tests);
+ break;
+ case CM_OUTPUT_SUBUNIT:
+ break;
+ case CM_OUTPUT_TAP:
+ cmprintf_group_finish_tap(group_name, total_executed, total_passed, total_skipped);
+ break;
+ case CM_OUTPUT_XML:
+ cmprintf_group_finish_xml(group_name,
+ total_executed,
+ total_failed,
+ total_errors,
+ total_skipped,
+ total_runtime,
+ cm_tests);
+ break;
+ }
+}
+
+static void cmprintf(enum cm_printf_type type,
+ size_t test_number,
+ const char *test_name,
+ const char *error_message)
+{
+ enum cm_message_output output;
+
+ output = cm_get_output();
+
+ switch (output) {
+ case CM_OUTPUT_STDOUT:
+ cmprintf_standard(type, test_name, error_message);
+ break;
+ case CM_OUTPUT_SUBUNIT:
+ cmprintf_subunit(type, test_name, error_message);
+ break;
+ case CM_OUTPUT_TAP:
+ cmprintf_tap(type, test_number, test_name, error_message);
+ break;
+ case CM_OUTPUT_XML:
+ break;
+ }
+}
+
+void cmocka_set_message_output(enum cm_message_output output)
+{
+ global_msg_output = output;
+}
+
+void cmocka_set_test_filter(const char *pattern)
+{
+ global_test_filter_pattern = pattern;
+}
+
+/****************************************************************************
+ * TIME CALCULATIONS
+ ****************************************************************************/
+
+#ifdef HAVE_STRUCT_TIMESPEC
+static struct timespec cm_tspecdiff(struct timespec time1,
+ struct timespec time0)
+{
+ struct timespec ret;
+ int xsec = 0;
+ int sign = 1;
+
+ if (time0.tv_nsec > time1.tv_nsec) {
+ xsec = (int) ((time0.tv_nsec - time1.tv_nsec) / (1E9 + 1));
+ time0.tv_nsec -= (long int) (1E9 * xsec);
+ time0.tv_sec += xsec;
+ }
+
+ if ((time1.tv_nsec - time0.tv_nsec) > 1E9) {
+ xsec = (int) ((time1.tv_nsec - time0.tv_nsec) / 1E9);
+ time0.tv_nsec += (long int) (1E9 * xsec);
+ time0.tv_sec -= xsec;
+ }
+
+ ret.tv_sec = time1.tv_sec - time0.tv_sec;
+ ret.tv_nsec = time1.tv_nsec - time0.tv_nsec;
+
+ if (time1.tv_sec < time0.tv_sec) {
+ sign = -1;
+ }
+
+ ret.tv_sec = ret.tv_sec * sign;
+
+ return ret;
+}
+
+static double cm_secdiff(struct timespec clock1, struct timespec clock0)
+{
+ double ret;
+ struct timespec diff;
+
+ diff = cm_tspecdiff(clock1, clock0);
+
+ ret = diff.tv_sec;
+ ret += (double) diff.tv_nsec / (double) 1E9;
+
+ return ret;
+}
+#endif /* HAVE_STRUCT_TIMESPEC */
+
+/****************************************************************************
+ * CMOCKA TEST RUNNER
+ ****************************************************************************/
+static int cmocka_run_one_test_or_fixture(const char *function_name,
+ CMUnitTestFunction test_func,
+ CMFixtureFunction setup_func,
+ CMFixtureFunction teardown_func,
+ void ** const volatile state,
+ const void *const heap_check_point)
+{
+ const ListNode * const volatile check_point = (const ListNode*)
+ (heap_check_point != NULL ?
+ heap_check_point : check_point_allocated_blocks());
+ int handle_exceptions = 1;
+ void *current_state = NULL;
+ int rc = 0;
+
+ /* FIXME check only one test or fixture is set */
+
+ /* Detect if we should handle exceptions */
+#ifdef _WIN32
+ handle_exceptions = !IsDebuggerPresent();
+#endif /* _WIN32 */
+#ifdef UNIT_TESTING_DEBUG
+ handle_exceptions = 0;
+#endif /* UNIT_TESTING_DEBUG */
+
+
+ if (handle_exceptions) {
+#ifndef _WIN32
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(exception_signals); i++) {
+ default_signal_functions[i] = signal(
+ exception_signals[i], exception_handler);
+ }
+#else /* _WIN32 */
+ previous_exception_filter = SetUnhandledExceptionFilter(
+ exception_filter);
+#endif /* !_WIN32 */
+ }
+
+ /* Init the test structure */
+ initialize_testing(function_name);
+
+ global_running_test = 1;
+
+ if (cm_setjmp(global_run_test_env) == 0) {
+ if (test_func != NULL) {
+ test_func(state != NULL ? state : &current_state);
+
+ fail_if_blocks_allocated(check_point, function_name);
+ rc = 0;
+ } else if (setup_func != NULL) {
+ rc = setup_func(state != NULL ? state : &current_state);
+
+ /*
+ * For setup we can ignore any allocated blocks. We just need to
+ * ensure they're deallocated on tear down.
+ */
+ } else if (teardown_func != NULL) {
+ rc = teardown_func(state != NULL ? state : &current_state);
+
+ fail_if_blocks_allocated(check_point, function_name);
+ } else {
+ /* ERROR */
+ }
+ fail_if_leftover_values(function_name);
+ global_running_test = 0;
+ } else {
+ /* TEST FAILED */
+ global_running_test = 0;
+ rc = -1;
+ }
+ teardown_testing(function_name);
+
+ if (handle_exceptions) {
+#ifndef _WIN32
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(exception_signals); i++) {
+ signal(exception_signals[i], default_signal_functions[i]);
+ }
+#else /* _WIN32 */
+ if (previous_exception_filter) {
+ SetUnhandledExceptionFilter(previous_exception_filter);
+ previous_exception_filter = NULL;
+ }
+#endif /* !_WIN32 */
+ }
+
+ return rc;
+}
+
+static int cmocka_run_group_fixture(const char *function_name,
+ CMFixtureFunction setup_func,
+ CMFixtureFunction teardown_func,
+ void **state,
+ const void *const heap_check_point)
+{
+ int rc;
+
+ if (setup_func != NULL) {
+ rc = cmocka_run_one_test_or_fixture(function_name,
+ NULL,
+ setup_func,
+ NULL,
+ state,
+ heap_check_point);
+ } else {
+ rc = cmocka_run_one_test_or_fixture(function_name,
+ NULL,
+ NULL,
+ teardown_func,
+ state,
+ heap_check_point);
+ }
+
+ return rc;
+}
+
+static int cmocka_run_one_tests(struct CMUnitTestState *test_state)
+{
+#ifdef HAVE_STRUCT_TIMESPEC
+ struct timespec start = {
+ .tv_sec = 0,
+ .tv_nsec = 0,
+ };
+ struct timespec finish = {
+ .tv_sec = 0,
+ .tv_nsec = 0,
+ };
+#endif
+ int rc = 0;
+
+ /* Run setup */
+ if (test_state->test->setup_func != NULL) {
+ /* Setup the memory check point, it will be evaluated on teardown */
+ test_state->check_point = check_point_allocated_blocks();
+
+ rc = cmocka_run_one_test_or_fixture(test_state->test->name,
+ NULL,
+ test_state->test->setup_func,
+ NULL,
+ &test_state->state,
+ test_state->check_point);
+ if (rc != 0) {
+ test_state->status = CM_TEST_ERROR;
+ cm_print_error("Test setup failed");
+ }
+ }
+
+ /* Run test */
+#ifdef HAVE_STRUCT_TIMESPEC
+ CMOCKA_CLOCK_GETTIME(CLOCK_REALTIME, &start);
+#endif
+
+ if (rc == 0) {
+ rc = cmocka_run_one_test_or_fixture(test_state->test->name,
+ test_state->test->test_func,
+ NULL,
+ NULL,
+ &test_state->state,
+ NULL);
+ if (rc == 0) {
+ test_state->status = CM_TEST_PASSED;
+ } else {
+ if (global_skip_test) {
+ test_state->status = CM_TEST_SKIPPED;
+ global_skip_test = 0; /* Do not skip the next test */
+ } else {
+ test_state->status = CM_TEST_FAILED;
+ }
+ }
+ rc = 0;
+ }
+
+ test_state->runtime = 0.0;
+
+#ifdef HAVE_STRUCT_TIMESPEC
+ CMOCKA_CLOCK_GETTIME(CLOCK_REALTIME, &finish);
+ test_state->runtime = cm_secdiff(finish, start);
+#endif
+
+ /* Run teardown */
+ if (rc == 0 && test_state->test->teardown_func != NULL) {
+ rc = cmocka_run_one_test_or_fixture(test_state->test->name,
+ NULL,
+ NULL,
+ test_state->test->teardown_func,
+ &test_state->state,
+ test_state->check_point);
+ if (rc != 0) {
+ test_state->status = CM_TEST_ERROR;
+ cm_print_error("Test teardown failed");
+ }
+ }
+
+ test_state->error_message = cm_error_message;
+ cm_error_message = NULL;
+
+ return rc;
+}
+
+int _cmocka_run_group_tests(const char *group_name,
+ const struct CMUnitTest * const tests,
+ const size_t num_tests,
+ CMFixtureFunction group_setup,
+ CMFixtureFunction group_teardown)
+{
+ struct CMUnitTestState *cm_tests;
+ const ListNode *group_check_point = check_point_allocated_blocks();
+ void *group_state = NULL;
+ size_t total_tests = 0;
+ size_t total_failed = 0;
+ size_t total_passed = 0;
+ size_t total_executed = 0;
+ size_t total_errors = 0;
+ size_t total_skipped = 0;
+ double total_runtime = 0;
+ size_t i;
+ int rc;
+
+ /* Make sure LargestIntegralType is at least the size of a pointer. */
+ assert_true(sizeof(LargestIntegralType) >= sizeof(void*));
+
+ cm_tests = (struct CMUnitTestState *)libc_malloc(sizeof(struct CMUnitTestState) * num_tests);
+ if (cm_tests == NULL) {
+ return -1;
+ }
+
+ /* Setup cmocka test array */
+ for (i = 0; i < num_tests; i++) {
+ if (tests[i].name != NULL &&
+ (tests[i].test_func != NULL
+ || tests[i].setup_func != NULL
+ || tests[i].teardown_func != NULL)) {
+ if (global_test_filter_pattern != NULL) {
+ int ok;
+
+ ok = c_strmatch(tests[i].name, global_test_filter_pattern);
+ if (!ok) {
+ continue;
+ }
+ }
+ cm_tests[total_tests] = (struct CMUnitTestState) {
+ .test = &tests[i],
+ .status = CM_TEST_NOT_STARTED,
+ .state = NULL,
+ };
+ total_tests++;
+ }
+ }
+
+ cmprintf_group_start(total_tests);
+
+ rc = 0;
+
+ /* Run group setup */
+ if (group_setup != NULL) {
+ rc = cmocka_run_group_fixture("cmocka_group_setup",
+ group_setup,
+ NULL,
+ &group_state,
+ group_check_point);
+ }
+
+ if (rc == 0) {
+ /* Execute tests */
+ for (i = 0; i < total_tests; i++) {
+ struct CMUnitTestState *cmtest = &cm_tests[i];
+ size_t test_number = i + 1;
+
+ cmprintf(PRINTF_TEST_START, test_number, cmtest->test->name, NULL);
+
+ if (group_state != NULL) {
+ cmtest->state = group_state;
+ } else if (cmtest->test->initial_state != NULL) {
+ cmtest->state = cmtest->test->initial_state;
+ }
+
+ rc = cmocka_run_one_tests(cmtest);
+ total_executed++;
+ total_runtime += cmtest->runtime;
+ if (rc == 0) {
+ switch (cmtest->status) {
+ case CM_TEST_PASSED:
+ cmprintf(PRINTF_TEST_SUCCESS,
+ test_number,
+ cmtest->test->name,
+ cmtest->error_message);
+ total_passed++;
+ break;
+ case CM_TEST_SKIPPED:
+ cmprintf(PRINTF_TEST_SKIPPED,
+ test_number,
+ cmtest->test->name,
+ cmtest->error_message);
+ total_skipped++;
+ break;
+ case CM_TEST_FAILED:
+ cmprintf(PRINTF_TEST_FAILURE,
+ test_number,
+ cmtest->test->name,
+ cmtest->error_message);
+ total_failed++;
+ break;
+ default:
+ cmprintf(PRINTF_TEST_ERROR,
+ test_number,
+ cmtest->test->name,
+ "Internal cmocka error");
+ total_errors++;
+ break;
+ }
+ } else {
+ char err_msg[2048] = {0};
+
+ snprintf(err_msg, sizeof(err_msg),
+ "Could not run test: %s",
+ cmtest->error_message);
+
+ cmprintf(PRINTF_TEST_ERROR,
+ test_number,
+ cmtest->test->name,
+ err_msg);
+ total_errors++;
+ }
+ }
+ } else {
+ if (cm_error_message != NULL) {
+ print_error("[ ERROR ] --- %s\n", cm_error_message);
+ vcm_free_error(cm_error_message);
+ cm_error_message = NULL;
+ }
+ cmprintf(PRINTF_TEST_ERROR, 0,
+ group_name, "[ FAILED ] GROUP SETUP");
+ total_errors++;
+ }
+
+ /* Run group teardown */
+ if (group_teardown != NULL) {
+ rc = cmocka_run_group_fixture("cmocka_group_teardown",
+ NULL,
+ group_teardown,
+ &group_state,
+ group_check_point);
+ if (rc != 0) {
+ if (cm_error_message != NULL) {
+ print_error("[ ERROR ] --- %s\n", cm_error_message);
+ vcm_free_error(cm_error_message);
+ cm_error_message = NULL;
+ }
+ cmprintf(PRINTF_TEST_ERROR, 0,
+ group_name, "[ FAILED ] GROUP TEARDOWN");
+ }
+ }
+
+ cmprintf_group_finish(group_name,
+ total_executed,
+ total_passed,
+ total_failed,
+ total_errors,
+ total_skipped,
+ total_runtime,
+ cm_tests);
+
+ for (i = 0; i < total_tests; i++) {
+ vcm_free_error(discard_const_p(char, cm_tests[i].error_message));
+ }
+ libc_free(cm_tests);
+ fail_if_blocks_allocated(group_check_point, "cmocka_group_tests");
+
+ return total_failed + total_errors;
+}
+
+/****************************************************************************
+ * DEPRECATED TEST RUNNER
+ ****************************************************************************/
+
+int _run_test(
+ const char * const function_name, const UnitTestFunction Function,
+ void ** const volatile state, const UnitTestFunctionType function_type,
+ const void* const heap_check_point) {
+ const ListNode * const volatile check_point = (const ListNode*)
+ (heap_check_point ?
+ heap_check_point : check_point_allocated_blocks());
+ void *current_state = NULL;
+ volatile int rc = 1;
+ int handle_exceptions = 1;
+#ifdef _WIN32
+ handle_exceptions = !IsDebuggerPresent();
+#endif /* _WIN32 */
+#ifdef UNIT_TESTING_DEBUG
+ handle_exceptions = 0;
+#endif /* UNIT_TESTING_DEBUG */
+
+ cm_error_message_enabled = 0;
+
+ if (handle_exceptions) {
+#ifndef _WIN32
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(exception_signals); i++) {
+ default_signal_functions[i] = signal(
+ exception_signals[i], exception_handler);
+ }
+#else /* _WIN32 */
+ previous_exception_filter = SetUnhandledExceptionFilter(
+ exception_filter);
+#endif /* !_WIN32 */
+ }
+
+ if (function_type == UNIT_TEST_FUNCTION_TYPE_TEST) {
+ print_message("[ RUN ] %s\n", function_name);
+ }
+ initialize_testing(function_name);
+ global_running_test = 1;
+ if (cm_setjmp(global_run_test_env) == 0) {
+ Function(state ? state : &current_state);
+ fail_if_leftover_values(function_name);
+
+ /* If this is a setup function then ignore any allocated blocks
+ * only ensure they're deallocated on tear down. */
+ if (function_type != UNIT_TEST_FUNCTION_TYPE_SETUP) {
+ fail_if_blocks_allocated(check_point, function_name);
+ }
+
+ global_running_test = 0;
+
+ if (function_type == UNIT_TEST_FUNCTION_TYPE_TEST) {
+ print_message("[ OK ] %s\n", function_name);
+ }
+ rc = 0;
+ } else {
+ global_running_test = 0;
+ print_message("[ FAILED ] %s\n", function_name);
+ }
+ teardown_testing(function_name);
+
+ if (handle_exceptions) {
+#ifndef _WIN32
+ unsigned int i;
+ for (i = 0; i < ARRAY_SIZE(exception_signals); i++) {
+ signal(exception_signals[i], default_signal_functions[i]);
+ }
+#else /* _WIN32 */
+ if (previous_exception_filter) {
+ SetUnhandledExceptionFilter(previous_exception_filter);
+ previous_exception_filter = NULL;
+ }
+#endif /* !_WIN32 */
+ }
+
+ return rc;
+}
+
+
+int _run_tests(const UnitTest * const tests, const size_t number_of_tests) {
+ /* Whether to execute the next test. */
+ int run_next_test = 1;
+ /* Whether the previous test failed. */
+ int previous_test_failed = 0;
+ /* Whether the previous setup failed. */
+ int previous_setup_failed = 0;
+ /* Check point of the heap state. */
+ const ListNode * const check_point = check_point_allocated_blocks();
+ /* Current test being executed. */
+ size_t current_test = 0;
+ /* Number of tests executed. */
+ size_t tests_executed = 0;
+ /* Number of failed tests. */
+ size_t total_failed = 0;
+ /* Number of setup functions. */
+ size_t setups = 0;
+ /* Number of teardown functions. */
+ size_t teardowns = 0;
+ size_t i;
+ /*
+ * A stack of test states. A state is pushed on the stack
+ * when a test setup occurs and popped on tear down.
+ */
+ TestState* test_states =
+ (TestState*)malloc(number_of_tests * sizeof(*test_states));
+ /* The number of test states which should be 0 at the end */
+ long number_of_test_states = 0;
+ /* Names of the tests that failed. */
+ const char** failed_names = (const char**)malloc(number_of_tests *
+ sizeof(*failed_names));
+ void **current_state = NULL;
+
+ /* Count setup and teardown functions */
+ for (i = 0; i < number_of_tests; i++) {
+ const UnitTest * const test = &tests[i];
+
+ if (test->function_type == UNIT_TEST_FUNCTION_TYPE_SETUP) {
+ setups++;
+ }
+
+ if (test->function_type == UNIT_TEST_FUNCTION_TYPE_TEARDOWN) {
+ teardowns++;
+ }
+ }
+
+ print_message("[==========] Running %"PRIdS " test(s).\n",
+ number_of_tests - setups - teardowns);
+
+ /* Make sure LargestIntegralType is at least the size of a pointer. */
+ assert_true(sizeof(LargestIntegralType) >= sizeof(void*));
+
+ while (current_test < number_of_tests) {
+ const ListNode *test_check_point = NULL;
+ TestState *current_TestState;
+ const UnitTest * const test = &tests[current_test++];
+ if (!test->function) {
+ continue;
+ }
+
+ switch (test->function_type) {
+ case UNIT_TEST_FUNCTION_TYPE_TEST:
+ if (! previous_setup_failed) {
+ run_next_test = 1;
+ }
+ break;
+ case UNIT_TEST_FUNCTION_TYPE_SETUP: {
+ /* Checkpoint the heap before the setup. */
+ current_TestState = &test_states[number_of_test_states++];
+ current_TestState->check_point = check_point_allocated_blocks();
+ test_check_point = current_TestState->check_point;
+ current_state = &current_TestState->state;
+ *current_state = NULL;
+ run_next_test = 1;
+ break;
+ }
+ case UNIT_TEST_FUNCTION_TYPE_TEARDOWN:
+ /* Check the heap based on the last setup checkpoint. */
+ assert_true(number_of_test_states);
+ current_TestState = &test_states[--number_of_test_states];
+ test_check_point = current_TestState->check_point;
+ current_state = &current_TestState->state;
+ break;
+ default:
+ print_error("Invalid unit test function type %d\n",
+ test->function_type);
+ exit_test(1);
+ break;
+ }
+
+ if (run_next_test) {
+ int failed = _run_test(test->name, test->function, current_state,
+ test->function_type, test_check_point);
+ if (failed) {
+ failed_names[total_failed] = test->name;
+ }
+
+ switch (test->function_type) {
+ case UNIT_TEST_FUNCTION_TYPE_TEST:
+ previous_test_failed = failed;
+ total_failed += failed;
+ tests_executed ++;
+ break;
+
+ case UNIT_TEST_FUNCTION_TYPE_SETUP:
+ if (failed) {
+ total_failed ++;
+ tests_executed ++;
+ /* Skip forward until the next test or setup function. */
+ run_next_test = 0;
+ previous_setup_failed = 1;
+ }
+ previous_test_failed = 0;
+ break;
+
+ case UNIT_TEST_FUNCTION_TYPE_TEARDOWN:
+ /* If this test failed. */
+ if (failed && !previous_test_failed) {
+ total_failed ++;
+ }
+ break;
+ default:
+#ifndef _HPUX
+ assert_null("BUG: shouldn't be here!");
+#endif
+ break;
+ }
+ }
+ }
+
+ print_message("[==========] %"PRIdS " test(s) run.\n", tests_executed);
+ print_error("[ PASSED ] %"PRIdS " test(s).\n", tests_executed - total_failed);
+
+ if (total_failed > 0) {
+ print_error("[ FAILED ] %"PRIdS " test(s), listed below:\n", total_failed);
+ for (i = 0; i < total_failed; i++) {
+ print_error("[ FAILED ] %s\n", failed_names[i]);
+ }
+ } else {
+ print_error("\n %"PRIdS " FAILED TEST(S)\n", total_failed);
+ }
+
+ if (number_of_test_states != 0) {
+ print_error("[ ERROR ] Mismatched number of setup %"PRIdS " and "
+ "teardown %"PRIdS " functions\n", setups, teardowns);
+ total_failed = (size_t)-1;
+ }
+
+ free(test_states);
+ free((void*)failed_names);
+
+ fail_if_blocks_allocated(check_point, "run_tests");
+ return (int)total_failed;
+}
+
+int _run_group_tests(const UnitTest * const tests, const size_t number_of_tests)
+{
+ UnitTestFunction setup = NULL;
+ const char *setup_name;
+ size_t num_setups = 0;
+ UnitTestFunction teardown = NULL;
+ const char *teardown_name = NULL;
+ size_t num_teardowns = 0;
+ size_t current_test = 0;
+ size_t i;
+
+ /* Number of tests executed. */
+ size_t tests_executed = 0;
+ /* Number of failed tests. */
+ size_t total_failed = 0;
+ /* Check point of the heap state. */
+ const ListNode * const check_point = check_point_allocated_blocks();
+ const char **failed_names = NULL;
+ void **current_state = NULL;
+ TestState group_state = {
+ .check_point = NULL,
+ };
+
+ if (number_of_tests == 0) {
+ return -1;
+ }
+
+ failed_names = (const char **)malloc(number_of_tests *
+ sizeof(*failed_names));
+ if (failed_names == NULL) {
+ return -2;
+ }
+
+ /* Find setup and teardown function */
+ for (i = 0; i < number_of_tests; i++) {
+ const UnitTest * const test = &tests[i];
+
+ if (test->function_type == UNIT_TEST_FUNCTION_TYPE_GROUP_SETUP) {
+ if (setup == NULL) {
+ setup = test->function;
+ setup_name = test->name;
+ num_setups = 1;
+ } else {
+ print_error("[ ERROR ] More than one group setup function detected\n");
+ exit_test(1);
+ }
+ }
+
+ if (test->function_type == UNIT_TEST_FUNCTION_TYPE_GROUP_TEARDOWN) {
+ if (teardown == NULL) {
+ teardown = test->function;
+ teardown_name = test->name;
+ num_teardowns = 1;
+ } else {
+ print_error("[ ERROR ] More than one group teardown function detected\n");
+ exit_test(1);
+ }
+ }
+ }
+
+ print_message("[==========] Running %"PRIdS " test(s).\n",
+ number_of_tests - num_setups - num_teardowns);
+
+ if (setup != NULL) {
+ int failed;
+
+ group_state.check_point = check_point_allocated_blocks();
+ current_state = &group_state.state;
+ *current_state = NULL;
+ failed = _run_test(setup_name,
+ setup,
+ current_state,
+ UNIT_TEST_FUNCTION_TYPE_SETUP,
+ group_state.check_point);
+ if (failed) {
+ failed_names[total_failed] = setup_name;
+ }
+
+ total_failed += failed;
+ tests_executed++;
+ }
+
+ while (current_test < number_of_tests) {
+ int run_test = 0;
+ const UnitTest * const test = &tests[current_test++];
+ if (test->function == NULL) {
+ continue;
+ }
+
+ switch (test->function_type) {
+ case UNIT_TEST_FUNCTION_TYPE_TEST:
+ run_test = 1;
+ break;
+ case UNIT_TEST_FUNCTION_TYPE_SETUP:
+ case UNIT_TEST_FUNCTION_TYPE_TEARDOWN:
+ case UNIT_TEST_FUNCTION_TYPE_GROUP_SETUP:
+ case UNIT_TEST_FUNCTION_TYPE_GROUP_TEARDOWN:
+ break;
+ default:
+ print_error("Invalid unit test function type %d\n",
+ test->function_type);
+ break;
+ }
+
+ if (run_test) {
+ int failed;
+
+ failed = _run_test(test->name,
+ test->function,
+ current_state,
+ test->function_type,
+ NULL);
+ if (failed) {
+ failed_names[total_failed] = test->name;
+ }
+
+ total_failed += failed;
+ tests_executed++;
+ }
+ }
+
+ if (teardown != NULL) {
+ int failed;
+
+ failed = _run_test(teardown_name,
+ teardown,
+ current_state,
+ UNIT_TEST_FUNCTION_TYPE_GROUP_TEARDOWN,
+ group_state.check_point);
+ if (failed) {
+ failed_names[total_failed] = teardown_name;
+ }
+
+ total_failed += failed;
+ tests_executed++;
+ }
+
+ print_message("[==========] %"PRIdS " test(s) run.\n", tests_executed);
+ print_error("[ PASSED ] %"PRIdS " test(s).\n", tests_executed - total_failed);
+
+ if (total_failed) {
+ print_error("[ FAILED ] %"PRIdS " test(s), listed below:\n", total_failed);
+ for (i = 0; i < total_failed; i++) {
+ print_error("[ FAILED ] %s\n", failed_names[i]);
+ }
+ } else {
+ print_error("\n %"PRIdS " FAILED TEST(S)\n", total_failed);
+ }
+
+ free((void*)failed_names);
+ fail_if_blocks_allocated(check_point, "run_group_tests");
+
+ return (int)total_failed;
+}