From a411781b00fa1ce1f8d7c0d48ad2765f9aa4268a Mon Sep 17 00:00:00 2001 From: stewartamiles Date: Fri, 29 Aug 2008 17:58:06 +0000 Subject: [PATCH] Fri Aug 29 10:50:46 2008 Google Inc. * cmockery: version 0.11 * Made it possible to specify executable, library and object output directories. git-svn-id: http://cmockery.googlecode.com/svn/trunk@5 40f4469a-5155-0410-be90-2de3f0bae501 --- ChangeLog | 6 + configure | 20 +- configure.ac | 2 +- src/cmockery.c | 3362 +++++++++++++++++++++++----------------------- windows/makefile | 350 +++-- 5 files changed, 1909 insertions(+), 1831 deletions(-) diff --git a/ChangeLog b/ChangeLog index 37b1921..2a3b607 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +Fri Aug 29 10:50:46 2008 Google Inc. + + * cmockery: version 0.11 + * Made it possible to specify executable, library and object output + directories. + Tue Aug 26 10:18:02 2008 Google Inc. * cmockery: initial release: diff --git a/configure b/configure index 9a05aa2..934d9d2 100755 --- a/configure +++ b/configure @@ -1,6 +1,6 @@ #! /bin/sh # Guess values for system-dependent variables and create Makefiles. -# Generated by GNU Autoconf 2.59 for cmockery 0.1. +# Generated by GNU Autoconf 2.59 for cmockery 0.11. # # Report bugs to . # @@ -423,8 +423,8 @@ SHELL=${CONFIG_SHELL-/bin/sh} # Identity of this package. PACKAGE_NAME='cmockery' PACKAGE_TARNAME='cmockery' -PACKAGE_VERSION='0.1' -PACKAGE_STRING='cmockery 0.1' +PACKAGE_VERSION='0.11' +PACKAGE_STRING='cmockery 0.11' PACKAGE_BUGREPORT='opensource@google.com' ac_unique_file="README" @@ -954,7 +954,7 @@ if test "$ac_init_help" = "long"; then # Omit some internal or obsolete options to make the list less imposing. # This message is too long to be a string in the A/UX 3.1 sh. cat <<_ACEOF -\`configure' configures cmockery 0.1 to adapt to many kinds of systems. +\`configure' configures cmockery 0.11 to adapt to many kinds of systems. Usage: $0 [OPTION]... [VAR=VALUE]... @@ -1020,7 +1020,7 @@ fi if test -n "$ac_init_help"; then case $ac_init_help in - short | recursive ) echo "Configuration of cmockery 0.1:";; + short | recursive ) echo "Configuration of cmockery 0.11:";; esac cat <<\_ACEOF @@ -1163,7 +1163,7 @@ fi test -n "$ac_init_help" && exit 0 if $ac_init_version; then cat <<\_ACEOF -cmockery configure 0.1 +cmockery configure 0.11 generated by GNU Autoconf 2.59 Copyright (C) 2003 Free Software Foundation, Inc. @@ -1177,7 +1177,7 @@ cat >&5 <<_ACEOF This file contains any messages produced by compilers while running configure, to aid debugging if configure makes a mistake. -It was created by cmockery $as_me 0.1, which was +It was created by cmockery $as_me 0.11, which was generated by GNU Autoconf 2.59. Invocation command line was $ $0 $@ @@ -1823,7 +1823,7 @@ fi # Define the identity of the package. PACKAGE='cmockery' - VERSION='0.1' + VERSION='0.11' cat >>confdefs.h <<_ACEOF @@ -22247,7 +22247,7 @@ _ASBOX } >&5 cat >&5 <<_CSEOF -This file was extended by cmockery $as_me 0.1, which was +This file was extended by cmockery $as_me 0.11, which was generated by GNU Autoconf 2.59. Invocation command line was CONFIG_FILES = $CONFIG_FILES @@ -22310,7 +22310,7 @@ _ACEOF cat >>$CONFIG_STATUS <<_ACEOF ac_cs_version="\\ -cmockery config.status 0.1 +cmockery config.status 0.11 configured by $0, generated by GNU Autoconf 2.59, with options \\"`echo "$ac_configure_args" | sed 's/[\\""\`\$]/\\\\&/g'`\\" diff --git a/configure.ac b/configure.ac index 32667f4..b5fee28 100644 --- a/configure.ac +++ b/configure.ac @@ -9,7 +9,7 @@ # make sure we're interpreted by some minimal autoconf AC_PREREQ(2.57) -AC_INIT(cmockery, 0.1, opensource@google.com) +AC_INIT(cmockery, 0.11, opensource@google.com) # The argument here is just something that should be in the current directory # (for sanity checking) AC_CONFIG_SRCDIR(README) diff --git a/src/cmockery.c b/src/cmockery.c index 9d25a15..0287a46 100755 --- a/src/cmockery.c +++ b/src/cmockery.c @@ -1,1681 +1,1681 @@ -/* - * Copyright 2008 Google Inc. - * - * 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. - */ -#include -#include -#ifndef _WIN32 -#include -#endif // !_WIN32 -#include -#include -#include -#include -#include -#ifdef _WIN32 -#include -#endif // _WIN32 -#include - -#ifdef _WIN32 -#define vsnprintf _vsnprintf -#endif // _WIN32 - -// 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:%d" - -// Calculates the number of elements in an array. -#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) - -// Doubly linked list node. -typedef struct ListNode { - const void *value; - int refcount; - struct ListNode *next; - struct ListNode *prev; -} ListNode; - -// Debug information for malloc(). -typedef struct MallocBlockInfo { - 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. -} 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; - const void* 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; - -// 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. -typedef struct CheckIntegerRange { - CheckParameterEvent event; - int minimum; - int maximum; -} CheckIntegerRange; - -// Structure used to check whether an integer value is in a set. -typedef struct CheckIntegerSet { - CheckParameterEvent event; - const void **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 int check_for_leftover_values( - const ListNode * const map_head, const char * const error_message, - const size_t number_of_symbol_names); -// 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); - - -// Keeps track of the calling context returned by setenv() so that the fail() -// method can jump out of a test. -static jmp_buf global_run_test_env; -static 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; - -// Keeps a map of the values that functions will have to return to provide -// mocked interfaces. -static ListNode global_function_result_map_head; -// Location of the last mock value returned was declared. -static SourceLocation global_last_mock_value_location; - -/* Keeps a map of the values that functions expect as parameters to their - * mocked interfaces. */ -static ListNode global_function_parameter_map_head; -// Location of last parameter value checked was declared. -static SourceLocation global_last_parameter_location; - -// List of all currently allocated blocks. -static ListNode global_allocated_blocks; - -#ifndef _WIN32 -// Signals caught by exception_handler(). -static const int exception_signals[] = { - SIGFPE, - SIGILL, - SIGSEGV, - SIGBUS, - SIGSYS, -}; - -// Default signal functions that should be restored after a test is complete. -typedef void (*SignalFunction)(int signal); -static SignalFunction default_signal_functions[ - ARRAY_LENGTH(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 - - -// Exit the currently executing test. -static void exit_test(const int quit_application) { - if (global_running_test) { - longjmp(global_run_test_env, 1); - } else if (quit_application) { - exit(-1); - } -} - - -// Initialize a SourceLocation structure. -static void initialize_source_location(SourceLocation * const location) { - assert_true(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_true(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_true(location); - location->file = file; - location->line = line; -} - - -// Create function results and expected parameter lists. -void initialize_testing(const char *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); -} - - -void fail_if_leftover_values(const char *test_name) { - int error_occurred = 0; - 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; - } - if (error_occurred) { - exit_test(1); - } -} - - -void teardown_testing(const char *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); -} - -// 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_true(head); - assert_true(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_true(head); - assert_true(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_true(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_true(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_true(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_true(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_true(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; - assert_true(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) { - assert_true(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 unsigned int children = (unsigned int)cleanup_value_data; - assert_true(value); - list_free(&map_value->symbol_values_list_head, - children ? free_symbol_map_value : free_value, - (void*)(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_true(symbol_map_head); - assert_true(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 = - 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; - ListNode *target_node; - assert_true(head); - assert_true(symbol_names); - assert_true(number_of_symbol_names); - assert_true(output); - symbol_name = symbol_names[0]; - - if (list_find(head, symbol_name, symbol_names_match, &target_node)) { - SymbolMapValue *map_value; - ListNode *child_list; - int return_value = 0; - assert_true(target_node); - assert_true(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); - *output = (void*) value_node->value; - return_value = value_node->refcount; - if (--value_node->refcount == 0) { - list_remove_free(value_node, NULL, NULL); - } - } 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; - } else { - print_error("No entries for symbol %s.\n", symbol_name); - } - return 0; -} - - -/* 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_true(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_true(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; - } -} - -/* 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 int check_for_leftover_values( - const ListNode * const map_head, const char * const error_message, - const size_t number_of_symbol_names) { - const ListNode *current; - int symbols_with_leftover_values = 0; - assert_true(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_true(value); - child_list = &value->symbol_values_list_head; - - if (!list_empty(child_list)) { - if (number_of_symbol_names == 1) { - const ListNode *child_node; - print_error(error_message, value->symbol_name); - print_error(" Remaining item(s) declared at...\n"); - - for (child_node = child_list->next; child_node != child_list; - child_node = child_node->next) { - const SourceLocation * const location = child_node->value; - print_error(" " SOURCE_LOCATION_FORMAT "\n", - location->file, location->line); - } - } else { - 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. -void* _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 = result; - void * const value = (void*)symbol->value; - global_last_mock_value_location = symbol->location; - if (rc == 1) { - free(symbol); - } - return value; - } else { - print_error("ERROR: " SOURCE_LOCATION_FORMAT " - Could not get value " - "to mock function %s\n", file, line, function); - if (source_location_is_set(&global_last_mock_value_location)) { - print_error("Previously returned mock value was declared at " - SOURCE_LOCATION_FORMAT "\n", - global_last_mock_value_location.file, - global_last_mock_value_location.line); - } else { - print_error("There were no previously returned mock values for " - "this test.\n"); - } - exit_test(1); - } - return NULL; -} - - -// 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 void* const value, const int count) { - SymbolValue * const return_value = 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, void * const check_data, - CheckParameterEvent * const event, const int count) { - CheckParameterEvent * const check = - event ? event : 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); -} - - -/* 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 void* const left, - const void* const right) { - const int equal = left == right; - if (!equal) { - print_error("0x%x != 0x%x\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 void* const left, - const void* const right) { - const int not_equal = left != right; - if (!not_equal) { - print_error("0x%x == 0x%x\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 void *value, const CheckIntegerSet * const check_integer_set, - const int invert) { - int succeeded = invert; - assert_true(check_integer_set); - { - const void ** 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) { - succeeded = 0; - } - break; - } - } - if (succeeded) { - return 1; - } - print_error("%d is %sin the set (", value, invert ? "" : "not "); - for (i = 0; i < size_of_set; i++) { - print_error("%d, ", set[i]); - } - 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 int value, const int range_min, const int range_max) { - if (value >= range_min && value <= range_max) { - return 1; - } - print_error("%d is not within the range %d-%d\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 int value, const int range_min, const int range_max) { - if (value < range_min || value > range_max) { - return 1; - } - print_error("%d is within the range %d-%d\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; - } - 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; - } - 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* a, const char* b, - const size_t size) { - int differences = 0; - size_t i; - for (i = 0; i < size; i++) { - const char l = a[i]; - const char r = b[i]; - if (l != r) { - print_error("difference at offset %d 0x%02x 0x%02x\n", i, l, r); - differences ++; - } - } - if (differences) { - print_error("%d bytes of 0x%08x and 0x%08x differ\n", differences, - a, 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* a, const char* b, - const size_t size) { - int same = 0; - size_t i; - for (i = 0; i < size; i++) { - const char l = a[i]; - const char r = b[i]; - if (l == r) { - print_error("equal at offset %d 0x%02x 0x%02x\n", i, l, r); - same ++; - } - } - if (same) { - print_error("%d bytes of 0x%08x and 0x%08x the same\n", same, - a, b); - return 0; - } - return 1; -} - - -// CheckParameterValue callback to check whether a value is within a set. -static int check_in_set(const void *value, void *check_value_data) { - return value_in_set_display_error(value, - (CheckIntegerSet*)check_value_data, 0); -} - - -// CheckParameterValue callback to check whether a value isn't within a set. -static int check_not_in_set(const void *value, void *check_value_data) { - return value_in_set_display_error(value, - (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 void *values[], - const size_t number_of_values, - const CheckParameterValue check_function, const int count) { - CheckIntegerSet * const check_integer_set = - malloc(sizeof(*check_integer_set) + - (sizeof(values[0]) * number_of_values)); - void ** const set = (void**)(check_integer_set + 1); - assert_true(values); - assert_true(number_of_values); - memcpy(set, values, number_of_values * sizeof(values[0])); - check_integer_set->set = (const void**)set; - _expect_check(function, parameter, file, line, check_function, - check_integer_set, &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 void *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 void *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 void *value, void *check_value_data) { - CheckIntegerRange * const check_integer_range = check_value_data; - assert_true(check_value_data); - return integer_in_range_display_error( - (int)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 void *value, void *check_value_data) { - CheckIntegerRange * const check_integer_range = check_value_data; - assert_true(check_value_data); - return integer_not_in_range_display_error( - (int)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 int minimum, const int maximum, - const CheckParameterValue check_function, const int count) { - CheckIntegerRange * const check_integer_range = - malloc(sizeof(*check_integer_range)); - check_integer_range->minimum = minimum; - check_integer_range->maximum = maximum; - _expect_check(function, parameter, file, line, check_function, - check_integer_range, &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 int minimum, const int 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 int minimum, const int 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 void *value, void *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 void* const value, - const int count) { - _expect_check(function, parameter, file, line, check_value, - (void*)value, NULL, count); -} - - -/* CheckParameterValue callback to check whether a value is not equal to an - * expected value. */ -static int check_not_value(const void *value, void *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 void* const value, - const int count) { - _expect_check(function, parameter, file, line, check_not_value, - (void*)value, NULL, count); -} - - -// CheckParameterValue callback to check whether a parameter equals a string. -static int check_string(const void * value, void *check_value_data) { - return string_equal_display_error(value, 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) { - _expect_check(function, parameter, file, line, check_string, (void*)string, - NULL, count); -} - - -/* CheckParameterValue callback to check whether a parameter is not equals to - * a string. */ -static int check_not_string(const void * value, void *check_value_data) { - return string_not_equal_display_error(value, 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) { - _expect_check(function, parameter, file, line, check_not_string, - (void*)string, NULL, count); -} - -/* CheckParameterValue callback to check whether a parameter equals an area of - * memory. */ -static int check_memory(const void* value, void *check_value_data) { - CheckMemoryData * const check = (CheckMemoryData*)check_value_data; - assert_true(check); - return memory_equal_display_error(value, 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 = malloc(sizeof(*check_data) + size); - void * const mem = (void*)(check_data + 1); - assert_true(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, &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 void* value, void *check_value_data) { - CheckMemoryData * const check = (CheckMemoryData*)check_value_data; - assert_true(check); - return memory_not_equal_display_error(value, 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 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, NULL, NULL, - count); -} - - -void _check_expected( - const char * const function_name, const char * const parameter_name, - const char* file, const int line, const void* value) { - void *result; - 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) { - print_error("ERROR: Check of parameter %s, function %s failed\n" - "Expected parameter declared at " - SOURCE_LOCATION_FORMAT "\n", - parameter_name, function_name, - global_last_parameter_location.file, - global_last_parameter_location.line); - _fail(file, line); - } - } else { - print_error("ERROR: " SOURCE_LOCATION_FORMAT " - 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)) { - print_error("Previously declared parameter value was declared at " - SOURCE_LOCATION_FORMAT "\n", - global_last_parameter_location.file, - global_last_parameter_location.line); - } else { - 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) { - longjmp(global_expect_assert_env, (int)expression); - } else { - print_error("ASSERT: %s\n", expression); - _fail(file, line); - } - } -} - - -void _assert_true(const int result, const char * const expression, - const char * const file, const int line) { - if (!result) { - print_error("%s\n", expression); - _fail(file, line); - } -} - -void _assert_int_equal(const int a, const int b, const char * const file, - const int line) { - if (!values_equal_display_error((void*)a, (void*)b)) { - _fail(file, line); - } -} - - -void _assert_int_not_equal(const int a, const int b, const char * const file, - const int line) { - if (!values_not_equal_display_error((void*)a, (void*)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 int value, const int minimum, const int 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 int value, const int minimum, - const int 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 void* const value, const void *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 void* const value, const void *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() { - // 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; -} - -// Use the real malloc in this function. -#undef malloc -void* _test_malloc(const size_t size, const char* file, const int line) { - char* ptr; - MallocBlockInfo *block_info; - ListNode * const block_list = get_allocated_blocks_list(); - const size_t allocate_size = size + (MALLOC_GUARD_SIZE * 2) + - sizeof(*block_info) + MALLOC_ALIGNMENT; - char* const block = (char*)malloc(allocate_size); - assert_true(block); - - // Calculate the returned address. - ptr = (char*)(((size_t)block + MALLOC_GUARD_SIZE + sizeof(*block_info) + - 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 = (MallocBlockInfo*)(ptr - (MALLOC_GUARD_SIZE + - sizeof(*block_info))); - set_source_location(&block_info->location, file, line); - block_info->allocated_size = allocate_size; - block_info->size = size; - block_info->block = block; - block_info->node.value = block_info; - list_add(block_list, &block_info->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 = (char*)ptr; - MallocBlockInfo *block_info; - _assert_true((int)ptr, "ptr", file, line); - block_info = (MallocBlockInfo*)(block - (MALLOC_GUARD_SIZE + - sizeof(*block_info))); - // Check the guard blocks. - { - char *guards[2] = {block - MALLOC_GUARD_SIZE, - block + block_info->size}; - for (i = 0; i < ARRAY_LENGTH(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) { - print_error( - "Guard block of 0x%08x size=%d allocated by " - SOURCE_LOCATION_FORMAT " at 0x%08x is corrupt\n", - (size_t)ptr, block_info->size, - block_info->location.file, block_info->location.line, - (size_t)&guard[j]); - _fail(file, line); - } - } - } - } - list_remove(&block_info->node, NULL, NULL); - - block = block_info->block; - memset(block, MALLOC_FREE_PATTERN, block_info->allocated_size); - free(block); -} -#define free test_free - - -// Crudely checkpoint the current heap state. -static const ListNode* check_point_allocated_blocks() { - return get_allocated_blocks_list()->prev; -} - - -/* Display the blocks allocated after the specified check point. This - * function returns the number of blocks displayed. */ -static int display_allocated_blocks(const ListNode * const check_point) { - const ListNode * const head = get_allocated_blocks_list(); - const ListNode *node; - int allocated_blocks = 0; - assert_true(check_point); - assert_true(check_point->next); - - for (node = check_point->next; node != head; node = node->next) { - const MallocBlockInfo * const block_info = node->value; - assert_true(block_info); - - if (!allocated_blocks) { - print_error("Blocks allocated...\n"); - } - print_error(" 0x%08x : " SOURCE_LOCATION_FORMAT "\n", - block_info->block, block_info->location.file, - block_info->location.line); - 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_true(check_point); - - node = check_point->next; - assert_true(node); - - while (node != head) { - MallocBlockInfo * const block_info = (MallocBlockInfo*)node->value; - node = node->next; - free((char*)block_info + sizeof(*block_info) + 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 int allocated_blocks = display_allocated_blocks(check_point); - if (allocated_blocks) { - free_allocated_blocks(check_point); - print_error("ERROR: %s leaked %d block(s)\n", test_name, - allocated_blocks); - exit_test(1); - } -} - - -void _fail(const char * const file, const int line) { - print_error("ERROR: " SOURCE_LOCATION_FORMAT " Failure!\n", file, line); - exit_test(1); -} - - -#ifndef _WIN32 -static void exception_handler(int sig) { - print_error("%s\n", strsignal(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_LENGTH(exception_codes); i++) { - const ExceptionCodeInfo * const code_info = &exception_codes[i]; - if (code == code_info->code) { - static int shown_debug_message = 0; - fflush(stdout); - print_error("%s occurred at 0x%08x.\n", code_info->description, - exception_record->ExceptionAddress); - if (!shown_debug_message) { - 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 - - -// 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(buffer); -#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, buffer); -#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); -} - - -int _run_test( - const char * const function_name, const UnitTestFunction Function, - void ** const state, const UnitTestFunctionType function_type, - const void* const heap_check_point) { - const ListNode * const check_point = heap_check_point ? - heap_check_point : check_point_allocated_blocks(); - void *current_state = NULL; - int rc = 1; - int handle_exceptions = 1; -#ifdef _WIN32 - handle_exceptions = !IsDebuggerPresent(); -#endif // _WIN32 -#if UNIT_TESTING_DEBUG - handle_exceptions = 0; -#endif // UNIT_TESTING_DEBUG - - if (handle_exceptions) { -#ifndef _WIN32 - unsigned int i; - for (i = 0; i < ARRAY_LENGTH(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("%s: Starting test\n", function_name); - } - initialize_testing(function_name); - global_running_test = 1; - if (setjmp(global_run_test_env) == 0) { - Function(state ? state : ¤t_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("%s: Test completed successfully.\n", function_name); - } - rc = 0; - } else { - global_running_test = 0; - print_message("%s: Test failed.\n", function_name); - } - teardown_testing(function_name); - - if (handle_exceptions) { -#ifndef _WIN32 - unsigned int i; - for (i = 0; i < ARRAY_LENGTH(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; - // 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; - /* 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 = malloc(number_of_tests * sizeof(*test_states)); - size_t number_of_test_states = 0; - // Names of the tests that failed. - const char** failed_names = malloc(number_of_tests * - sizeof(*failed_names)); - void **current_state = NULL; - - 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: - 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 = ¤t_TestState->state; - *current_state = NULL; - run_next_test = 1; - setups ++; - 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 = ¤t_TestState->state; - teardowns ++; - 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_test_failed = 0; - break; - - case UNIT_TEST_FUNCTION_TYPE_TEARDOWN: - // If this test failed. - if (failed && !previous_test_failed) { - total_failed ++; - } - break; - default: - assert_false("BUG: shouldn't be here!"); - break; - } - } - } - - if (total_failed) { - size_t i; - print_error("%d out of %d tests failed!\n", total_failed, - tests_executed); - for (i = 0; i < total_failed; i++) { - print_error(" %s\n", failed_names[i]); - } - } else { - print_message("All %d tests passed\n", tests_executed); - } - - if (number_of_test_states) { - print_error("Mismatched number of setup %d and teardown %d " - "functions\n", setups, teardowns); - total_failed = -1; - } - - free(test_states); - free((void*)failed_names); - - fail_if_blocks_allocated(check_point, "run_tests"); - return total_failed; -} +/* + * Copyright 2008 Google Inc. + * + * 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. + */ +#include +#include +#ifndef _WIN32 +#include +#endif // !_WIN32 +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#endif // _WIN32 +#include + +#ifdef _WIN32 +#define vsnprintf _vsnprintf +#endif // _WIN32 + +// 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:%d" + +// Calculates the number of elements in an array. +#define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) + +// Doubly linked list node. +typedef struct ListNode { + const void *value; + int refcount; + struct ListNode *next; + struct ListNode *prev; +} ListNode; + +// Debug information for malloc(). +typedef struct MallocBlockInfo { + 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. +} 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; + const void* 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; + +// 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. +typedef struct CheckIntegerRange { + CheckParameterEvent event; + int minimum; + int maximum; +} CheckIntegerRange; + +// Structure used to check whether an integer value is in a set. +typedef struct CheckIntegerSet { + CheckParameterEvent event; + const void **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 int check_for_leftover_values( + const ListNode * const map_head, const char * const error_message, + const size_t number_of_symbol_names); +// 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); + + +// Keeps track of the calling context returned by setenv() so that the fail() +// method can jump out of a test. +static jmp_buf global_run_test_env; +static 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; + +// Keeps a map of the values that functions will have to return to provide +// mocked interfaces. +static ListNode global_function_result_map_head; +// Location of the last mock value returned was declared. +static SourceLocation global_last_mock_value_location; + +/* Keeps a map of the values that functions expect as parameters to their + * mocked interfaces. */ +static ListNode global_function_parameter_map_head; +// Location of last parameter value checked was declared. +static SourceLocation global_last_parameter_location; + +// List of all currently allocated blocks. +static ListNode global_allocated_blocks; + +#ifndef _WIN32 +// Signals caught by exception_handler(). +static const int exception_signals[] = { + SIGFPE, + SIGILL, + SIGSEGV, + SIGBUS, + SIGSYS, +}; + +// Default signal functions that should be restored after a test is complete. +typedef void (*SignalFunction)(int signal); +static SignalFunction default_signal_functions[ + ARRAY_LENGTH(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 + + +// Exit the currently executing test. +static void exit_test(const int quit_application) { + if (global_running_test) { + longjmp(global_run_test_env, 1); + } else if (quit_application) { + exit(-1); + } +} + + +// Initialize a SourceLocation structure. +static void initialize_source_location(SourceLocation * const location) { + assert_true(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_true(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_true(location); + location->file = file; + location->line = line; +} + + +// Create function results and expected parameter lists. +void initialize_testing(const char *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); +} + + +void fail_if_leftover_values(const char *test_name) { + int error_occurred = 0; + 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; + } + if (error_occurred) { + exit_test(1); + } +} + + +void teardown_testing(const char *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); +} + +// 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_true(head); + assert_true(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_true(head); + assert_true(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_true(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_true(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_true(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_true(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_true(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; + assert_true(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) { + assert_true(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 unsigned int children = (unsigned int)cleanup_value_data; + assert_true(value); + list_free(&map_value->symbol_values_list_head, + children ? free_symbol_map_value : free_value, + (void*)(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_true(symbol_map_head); + assert_true(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 = + 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; + ListNode *target_node; + assert_true(head); + assert_true(symbol_names); + assert_true(number_of_symbol_names); + assert_true(output); + symbol_name = symbol_names[0]; + + if (list_find(head, symbol_name, symbol_names_match, &target_node)) { + SymbolMapValue *map_value; + ListNode *child_list; + int return_value = 0; + assert_true(target_node); + assert_true(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); + *output = (void*) value_node->value; + return_value = value_node->refcount; + if (--value_node->refcount == 0) { + list_remove_free(value_node, NULL, NULL); + } + } 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; + } else { + print_error("No entries for symbol %s.\n", symbol_name); + } + return 0; +} + + +/* 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_true(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_true(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; + } +} + +/* 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 int check_for_leftover_values( + const ListNode * const map_head, const char * const error_message, + const size_t number_of_symbol_names) { + const ListNode *current; + int symbols_with_leftover_values = 0; + assert_true(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_true(value); + child_list = &value->symbol_values_list_head; + + if (!list_empty(child_list)) { + if (number_of_symbol_names == 1) { + const ListNode *child_node; + print_error(error_message, value->symbol_name); + print_error(" Remaining item(s) declared at...\n"); + + for (child_node = child_list->next; child_node != child_list; + child_node = child_node->next) { + const SourceLocation * const location = child_node->value; + print_error(" " SOURCE_LOCATION_FORMAT "\n", + location->file, location->line); + } + } else { + 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. +void* _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 = result; + void * const value = (void*)symbol->value; + global_last_mock_value_location = symbol->location; + if (rc == 1) { + free(symbol); + } + return value; + } else { + print_error("ERROR: " SOURCE_LOCATION_FORMAT " - Could not get value " + "to mock function %s\n", file, line, function); + if (source_location_is_set(&global_last_mock_value_location)) { + print_error("Previously returned mock value was declared at " + SOURCE_LOCATION_FORMAT "\n", + global_last_mock_value_location.file, + global_last_mock_value_location.line); + } else { + print_error("There were no previously returned mock values for " + "this test.\n"); + } + exit_test(1); + } + return NULL; +} + + +// 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 void* const value, const int count) { + SymbolValue * const return_value = 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, void * const check_data, + CheckParameterEvent * const event, const int count) { + CheckParameterEvent * const check = + event ? event : 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); +} + + +/* 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 void* const left, + const void* const right) { + const int equal = left == right; + if (!equal) { + print_error("0x%x != 0x%x\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 void* const left, + const void* const right) { + const int not_equal = left != right; + if (!not_equal) { + print_error("0x%x == 0x%x\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 void *value, const CheckIntegerSet * const check_integer_set, + const int invert) { + int succeeded = invert; + assert_true(check_integer_set); + { + const void ** 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) { + succeeded = 0; + } + break; + } + } + if (succeeded) { + return 1; + } + print_error("%d is %sin the set (", value, invert ? "" : "not "); + for (i = 0; i < size_of_set; i++) { + print_error("%d, ", set[i]); + } + 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 int value, const int range_min, const int range_max) { + if (value >= range_min && value <= range_max) { + return 1; + } + print_error("%d is not within the range %d-%d\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 int value, const int range_min, const int range_max) { + if (value < range_min || value > range_max) { + return 1; + } + print_error("%d is within the range %d-%d\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; + } + 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; + } + 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* a, const char* b, + const size_t size) { + int differences = 0; + size_t i; + for (i = 0; i < size; i++) { + const char l = a[i]; + const char r = b[i]; + if (l != r) { + print_error("difference at offset %d 0x%02x 0x%02x\n", i, l, r); + differences ++; + } + } + if (differences) { + print_error("%d bytes of 0x%08x and 0x%08x differ\n", differences, + a, 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* a, const char* b, + const size_t size) { + int same = 0; + size_t i; + for (i = 0; i < size; i++) { + const char l = a[i]; + const char r = b[i]; + if (l == r) { + print_error("equal at offset %d 0x%02x 0x%02x\n", i, l, r); + same ++; + } + } + if (same) { + print_error("%d bytes of 0x%08x and 0x%08x the same\n", same, + a, b); + return 0; + } + return 1; +} + + +// CheckParameterValue callback to check whether a value is within a set. +static int check_in_set(const void *value, void *check_value_data) { + return value_in_set_display_error(value, + (CheckIntegerSet*)check_value_data, 0); +} + + +// CheckParameterValue callback to check whether a value isn't within a set. +static int check_not_in_set(const void *value, void *check_value_data) { + return value_in_set_display_error(value, + (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 void *values[], + const size_t number_of_values, + const CheckParameterValue check_function, const int count) { + CheckIntegerSet * const check_integer_set = + malloc(sizeof(*check_integer_set) + + (sizeof(values[0]) * number_of_values)); + void ** const set = (void**)(check_integer_set + 1); + assert_true(values); + assert_true(number_of_values); + memcpy(set, values, number_of_values * sizeof(values[0])); + check_integer_set->set = (const void**)set; + _expect_check(function, parameter, file, line, check_function, + check_integer_set, &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 void *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 void *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 void *value, void *check_value_data) { + CheckIntegerRange * const check_integer_range = check_value_data; + assert_true(check_value_data); + return integer_in_range_display_error( + (int)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 void *value, void *check_value_data) { + CheckIntegerRange * const check_integer_range = check_value_data; + assert_true(check_value_data); + return integer_not_in_range_display_error( + (int)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 int minimum, const int maximum, + const CheckParameterValue check_function, const int count) { + CheckIntegerRange * const check_integer_range = + malloc(sizeof(*check_integer_range)); + check_integer_range->minimum = minimum; + check_integer_range->maximum = maximum; + _expect_check(function, parameter, file, line, check_function, + check_integer_range, &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 int minimum, const int 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 int minimum, const int 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 void *value, void *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 void* const value, + const int count) { + _expect_check(function, parameter, file, line, check_value, + (void*)value, NULL, count); +} + + +/* CheckParameterValue callback to check whether a value is not equal to an + * expected value. */ +static int check_not_value(const void *value, void *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 void* const value, + const int count) { + _expect_check(function, parameter, file, line, check_not_value, + (void*)value, NULL, count); +} + + +// CheckParameterValue callback to check whether a parameter equals a string. +static int check_string(const void * value, void *check_value_data) { + return string_equal_display_error(value, 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) { + _expect_check(function, parameter, file, line, check_string, (void*)string, + NULL, count); +} + + +/* CheckParameterValue callback to check whether a parameter is not equals to + * a string. */ +static int check_not_string(const void * value, void *check_value_data) { + return string_not_equal_display_error(value, 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) { + _expect_check(function, parameter, file, line, check_not_string, + (void*)string, NULL, count); +} + +/* CheckParameterValue callback to check whether a parameter equals an area of + * memory. */ +static int check_memory(const void* value, void *check_value_data) { + CheckMemoryData * const check = (CheckMemoryData*)check_value_data; + assert_true(check); + return memory_equal_display_error(value, 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 = malloc(sizeof(*check_data) + size); + void * const mem = (void*)(check_data + 1); + assert_true(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, &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 void* value, void *check_value_data) { + CheckMemoryData * const check = (CheckMemoryData*)check_value_data; + assert_true(check); + return memory_not_equal_display_error(value, 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 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, NULL, NULL, + count); +} + + +void _check_expected( + const char * const function_name, const char * const parameter_name, + const char* file, const int line, const void* value) { + void *result; + 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) { + print_error("ERROR: Check of parameter %s, function %s failed\n" + "Expected parameter declared at " + SOURCE_LOCATION_FORMAT "\n", + parameter_name, function_name, + global_last_parameter_location.file, + global_last_parameter_location.line); + _fail(file, line); + } + } else { + print_error("ERROR: " SOURCE_LOCATION_FORMAT " - 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)) { + print_error("Previously declared parameter value was declared at " + SOURCE_LOCATION_FORMAT "\n", + global_last_parameter_location.file, + global_last_parameter_location.line); + } else { + 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) { + longjmp(global_expect_assert_env, (int)expression); + } else { + print_error("ASSERT: %s\n", expression); + _fail(file, line); + } + } +} + + +void _assert_true(const int result, const char * const expression, + const char * const file, const int line) { + if (!result) { + print_error("%s\n", expression); + _fail(file, line); + } +} + +void _assert_int_equal(const int a, const int b, const char * const file, + const int line) { + if (!values_equal_display_error((void*)a, (void*)b)) { + _fail(file, line); + } +} + + +void _assert_int_not_equal(const int a, const int b, const char * const file, + const int line) { + if (!values_not_equal_display_error((void*)a, (void*)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 int value, const int minimum, const int 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 int value, const int minimum, + const int 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 void* const value, const void *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 void* const value, const void *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() { + // 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; +} + +// Use the real malloc in this function. +#undef malloc +void* _test_malloc(const size_t size, const char* file, const int line) { + char* ptr; + MallocBlockInfo *block_info; + ListNode * const block_list = get_allocated_blocks_list(); + const size_t allocate_size = size + (MALLOC_GUARD_SIZE * 2) + + sizeof(*block_info) + MALLOC_ALIGNMENT; + char* const block = (char*)malloc(allocate_size); + assert_true(block); + + // Calculate the returned address. + ptr = (char*)(((size_t)block + MALLOC_GUARD_SIZE + sizeof(*block_info) + + 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 = (MallocBlockInfo*)(ptr - (MALLOC_GUARD_SIZE + + sizeof(*block_info))); + set_source_location(&block_info->location, file, line); + block_info->allocated_size = allocate_size; + block_info->size = size; + block_info->block = block; + block_info->node.value = block_info; + list_add(block_list, &block_info->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 = (char*)ptr; + MallocBlockInfo *block_info; + _assert_true((int)ptr, "ptr", file, line); + block_info = (MallocBlockInfo*)(block - (MALLOC_GUARD_SIZE + + sizeof(*block_info))); + // Check the guard blocks. + { + char *guards[2] = {block - MALLOC_GUARD_SIZE, + block + block_info->size}; + for (i = 0; i < ARRAY_LENGTH(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) { + print_error( + "Guard block of 0x%08x size=%d allocated by " + SOURCE_LOCATION_FORMAT " at 0x%08x is corrupt\n", + (size_t)ptr, block_info->size, + block_info->location.file, block_info->location.line, + (size_t)&guard[j]); + _fail(file, line); + } + } + } + } + list_remove(&block_info->node, NULL, NULL); + + block = block_info->block; + memset(block, MALLOC_FREE_PATTERN, block_info->allocated_size); + free(block); +} +#define free test_free + + +// Crudely checkpoint the current heap state. +static const ListNode* check_point_allocated_blocks() { + return get_allocated_blocks_list()->prev; +} + + +/* Display the blocks allocated after the specified check point. This + * function returns the number of blocks displayed. */ +static int display_allocated_blocks(const ListNode * const check_point) { + const ListNode * const head = get_allocated_blocks_list(); + const ListNode *node; + int allocated_blocks = 0; + assert_true(check_point); + assert_true(check_point->next); + + for (node = check_point->next; node != head; node = node->next) { + const MallocBlockInfo * const block_info = node->value; + assert_true(block_info); + + if (!allocated_blocks) { + print_error("Blocks allocated...\n"); + } + print_error(" 0x%08x : " SOURCE_LOCATION_FORMAT "\n", + block_info->block, block_info->location.file, + block_info->location.line); + 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_true(check_point); + + node = check_point->next; + assert_true(node); + + while (node != head) { + MallocBlockInfo * const block_info = (MallocBlockInfo*)node->value; + node = node->next; + free((char*)block_info + sizeof(*block_info) + 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 int allocated_blocks = display_allocated_blocks(check_point); + if (allocated_blocks) { + free_allocated_blocks(check_point); + print_error("ERROR: %s leaked %d block(s)\n", test_name, + allocated_blocks); + exit_test(1); + } +} + + +void _fail(const char * const file, const int line) { + print_error("ERROR: " SOURCE_LOCATION_FORMAT " Failure!\n", file, line); + exit_test(1); +} + + +#ifndef _WIN32 +static void exception_handler(int sig) { + print_error("%s\n", strsignal(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_LENGTH(exception_codes); i++) { + const ExceptionCodeInfo * const code_info = &exception_codes[i]; + if (code == code_info->code) { + static int shown_debug_message = 0; + fflush(stdout); + print_error("%s occurred at 0x%08x.\n", code_info->description, + exception_record->ExceptionAddress); + if (!shown_debug_message) { + 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 + + +// 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(buffer); +#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, buffer); +#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); +} + + +int _run_test( + const char * const function_name, const UnitTestFunction Function, + void ** const state, const UnitTestFunctionType function_type, + const void* const heap_check_point) { + const ListNode * const check_point = heap_check_point ? + heap_check_point : check_point_allocated_blocks(); + void *current_state = NULL; + int rc = 1; + int handle_exceptions = 1; +#ifdef _WIN32 + handle_exceptions = !IsDebuggerPresent(); +#endif // _WIN32 +#if UNIT_TESTING_DEBUG + handle_exceptions = 0; +#endif // UNIT_TESTING_DEBUG + + if (handle_exceptions) { +#ifndef _WIN32 + unsigned int i; + for (i = 0; i < ARRAY_LENGTH(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("%s: Starting test\n", function_name); + } + initialize_testing(function_name); + global_running_test = 1; + if (setjmp(global_run_test_env) == 0) { + Function(state ? state : ¤t_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("%s: Test completed successfully.\n", function_name); + } + rc = 0; + } else { + global_running_test = 0; + print_message("%s: Test failed.\n", function_name); + } + teardown_testing(function_name); + + if (handle_exceptions) { +#ifndef _WIN32 + unsigned int i; + for (i = 0; i < ARRAY_LENGTH(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; + // 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; + /* 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 = malloc(number_of_tests * sizeof(*test_states)); + size_t number_of_test_states = 0; + // Names of the tests that failed. + const char** failed_names = malloc(number_of_tests * + sizeof(*failed_names)); + void **current_state = NULL; + + 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: + 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 = ¤t_TestState->state; + *current_state = NULL; + run_next_test = 1; + setups ++; + 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 = ¤t_TestState->state; + teardowns ++; + 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_test_failed = 0; + break; + + case UNIT_TEST_FUNCTION_TYPE_TEARDOWN: + // If this test failed. + if (failed && !previous_test_failed) { + total_failed ++; + } + break; + default: + assert_false("BUG: shouldn't be here!"); + break; + } + } + } + + if (total_failed) { + size_t i; + print_error("%d out of %d tests failed!\n", total_failed, + tests_executed); + for (i = 0; i < total_failed; i++) { + print_error(" %s\n", failed_names[i]); + } + } else { + print_message("All %d tests passed\n", tests_executed); + } + + if (number_of_test_states) { + print_error("Mismatched number of setup %d and teardown %d " + "functions\n", setups, teardowns); + total_failed = -1; + } + + free(test_states); + free((void*)failed_names); + + fail_if_blocks_allocated(check_point, "run_tests"); + return total_failed; +} diff --git a/windows/makefile b/windows/makefile index 0395bba..8cb7bb2 100644 --- a/windows/makefile +++ b/windows/makefile @@ -1,139 +1,211 @@ -# -# Copyright 2008 Google Inc. -# -# 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. -# -# Microsoft Windows namke file which builds the cmockery library and example -# applications. -# -# To use this makefile... -# Select Start->Run and run "cmd.exe" to open the command line. -# Run "vsvars.bat" located in Microsoft Visual Studio's directory. -# Run "nmake" to build the cmockery library and example applications. -# Run "nmake clean" to delete all files built by this makefile. - -LIBRARY_SOURCE_DIRECTORY=..\src -EXAMPLE_SOURCE_DIRECTORY=..\src\example - -CFLAGS=/nologo /c /D_WIN32_WINNT=0x501 /I$(LIBRARY_SOURCE_DIRECTORY)\google \ - /I$(EXAMPLE_SOURCE_DIRECTORY) -CC_COMMAND=$(CC) $(CFLAGS) $(**) /Fo$(@) -CC_COMMAND_UNIT_TEST=$(CC_COMMAND) /DUNIT_TESTING=1 - -LIBLINKFLAGS=/NOLOGO /MACHINE:x86 /SUBSYSTEM:console - -LIBRARY_ARCHIVER=lib.exe -LIBFLAGS=$(LIBLINKFLAGS) -LIB_COMMAND=$(LIBRARY_ARCHIVER) $(LIBFLAGS) $(**) /OUT:$(@) - -LINK=link.exe -LFLAGS=$(LIBLINKFLAGS) libcmt.lib kernel32.lib /NODEFAULTLIB:libc.lib -LINK_COMMAND=$(LINK) $(LFLAGS) $(**) /OUT:$(@) - -.SUFFIXES: .exe .lib .obj .c - -all: cmockery.lib calculator.exe calculator_test.exe allocate_module_test.exe \ - assert_macro_test.exe customer_database_test.exe key_value_test.exe \ - product_database_test.exe run_tests.exe - -clean: - -cmd /c "@for %A in (\ - cmockery.lib cmockery.obj \ - calculator.exe calculator.obj \ - calculator_test.exe calculator_test.obj \ - calculator_test-calculator.obj \ - allocate_module_test.exe allocate_module_test.obj \ - allocate_module.obj \ - assert_macro_test.exe assert_macro_test.obj \ - assert_macro.obj \ - customer_database_test.exe customer_database_test.obj \ - customer_database.obj \ - key_value_test.exe key_value_test.obj key_value.obj \ - product_database_test.exe product_database_test.obj \ - product_database.obj \ - run_tests.exe run_tests.obj) do @del %A 2>NUL" - -# Rules for the cmockery library. -cmockery.lib: cmockery.obj -cmockery.obj: $(LIBRARY_SOURCE_DIRECTORY)\cmockery.c - -# Rules for the calculator application. -calculator.exe: calculator.obj - -calculator.obj: $(EXAMPLE_SOURCE_DIRECTORY)\calculator.c - $(CC_COMMAND) - -# Rules for the calculator test application. -calculator_test.exe: calculator_test.obj calculator_test-calculator.obj \ - cmockery.lib - $(LINK_COMMAND) - -calculator_test.obj: $(EXAMPLE_SOURCE_DIRECTORY)\calculator_test.c - -calculator_test-calculator.obj: $(EXAMPLE_SOURCE_DIRECTORY)\calculator.c - $(CC_COMMAND_UNIT_TEST) - -# Sample code applications. -allocate_module_test.exe: allocate_module_test.obj allocate_module.obj \ - cmockery.lib - $(LINK_COMMAND) - -allocate_module_test.obj: $(EXAMPLE_SOURCE_DIRECTORY)\allocate_module_test.c -allocate_module.obj: $(EXAMPLE_SOURCE_DIRECTORY)\allocate_module.c - -assert_macro_test.exe: assert_macro_test.obj assert_macro.obj \ - cmockery.lib - $(LINK_COMMAND) - -assert_macro_test.obj: $(EXAMPLE_SOURCE_DIRECTORY)\assert_macro_test.c -assert_macro.obj: $(EXAMPLE_SOURCE_DIRECTORY)\assert_macro.c - -customer_database_test.exe: customer_database_test.obj customer_database.obj \ - cmockery.lib - $(LINK_COMMAND) - -customer_database_test.obj: \ - $(EXAMPLE_SOURCE_DIRECTORY)\customer_database_test.c -customer_database.obj: $(EXAMPLE_SOURCE_DIRECTORY)\customer_database.c - -key_value_test.exe: key_value_test.obj key_value.obj \ - cmockery.lib - $(LINK_COMMAND) - -key_value_test.obj: $(EXAMPLE_SOURCE_DIRECTORY)\key_value_test.c -key_value.obj: $(EXAMPLE_SOURCE_DIRECTORY)\key_value.c - -product_database_test.exe: product_database_test.obj product_database.obj \ - cmockery.lib - $(LINK_COMMAND) - -product_database_test.obj: $(EXAMPLE_SOURCE_DIRECTORY)\product_database_test.c -product_database.obj: $(EXAMPLE_SOURCE_DIRECTORY)\product_database.c - -run_tests.exe: run_tests.obj cmockery.lib - $(LINK_COMMAND) - -run_tests.obj: $(EXAMPLE_SOURCE_DIRECTORY)\run_tests.c - -# Inference rules. -.obj.exe: - $(LINK_COMMAND) - -.obj.lib: - $(LIB_COMMAND) - -{$(LIBRARY_SOURCE_DIRECTORY)\}.c{}.obj: - $(CC_COMMAND) - -{$(EXAMPLE_SOURCE_DIRECTORY)\}.c{}.obj: - $(CC_COMMAND_UNIT_TEST) +# +# Copyright 2008 Google Inc. +# +# 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. +# +# Microsoft Windows namke file which builds the cmockery library and example +# applications. +# +# To use this makefile... +# Select Start->Run and run "cmd.exe" to open the command line. +# Run "vsvars.bat" located in Microsoft Visual Studio's directory. +# Run "nmake" to build the cmockery library and example applications. +# Run "nmake clean" to delete all files built by this makefile. + +# Target directories. +!IFNDEF EXECUTABLE_DIR +EXECUTABLE_DIR=. +!ENDIF # EXECUTABLE_DIR + +!IFNDEF LIBRARY_DIR +LIBRARY_DIR=. +!ENDIF # LIBRARY_DIR + +!IFNDEF OBJECT_DIR +OBJECT_DIR=. +!ENDIF # OBJECT_DIR + +# Source directories. +LIBRARY_SOURCE_DIRECTORY=..\src +EXAMPLE_SOURCE_DIRECTORY=..\src\example + +# Compiler flags and commands. +CFLAGS=/nologo /c /D_WIN32_WINNT=0x501 /I$(LIBRARY_SOURCE_DIRECTORY)\google \ + /I$(EXAMPLE_SOURCE_DIRECTORY) +CC_COMMAND=$(CC) $(CFLAGS) $(**) /Fo$(@) +CC_COMMAND_UNIT_TEST=$(CC_COMMAND) /DUNIT_TESTING=1 + +LIBLINKFLAGS=/NOLOGO /MACHINE:x86 /SUBSYSTEM:console + +# Library archiver flags and command. +LIBRARY_ARCHIVER=lib.exe +LIBFLAGS=$(LIBLINKFLAGS) +LIB_COMMAND=$(LIBRARY_ARCHIVER) $(LIBFLAGS) $(**) /OUT:$(@) + +# Linker flags and command. +LINK=link.exe +LFLAGS=$(LIBLINKFLAGS) libcmt.lib kernel32.lib /NODEFAULTLIB:libc.lib +LINK_COMMAND=$(LINK) $(LFLAGS) $(**) /OUT:$(@) + +.SUFFIXES: .exe .lib .obj .c + +all: $(EXECUTABLE_DIR) $(LIBRARY_DIR) $(OBJECT_DIR) \ + $(LIBRARY_DIR)\cmockery.lib examples + +$(EXECUTABLE_DIR): + mkdir $@ + +!IF "$(LIBRARY_DIR)" != "$(EXECUTABLE_DIR)" +$(LIBRARY_DIR): + mkdir $* +!ENDIF + +!IF "$(OBJECT_DIR)" != "$(LIBRARY_DIR)" && \ + "$(OBJECT_DIR)" != "$(EXECUTABLE_DIR)" +$(OBJECT_DIR): + mkdir $@ +!ENDIF + +examples: \ + $(EXECUTABLE_DIR)\calculator.exe \ + $(EXECUTABLE_DIR)\calculator_test.exe \ + $(EXECUTABLE_DIR)\allocate_module_test.exe \ + $(EXECUTABLE_DIR)\assert_macro_test.exe \ + $(EXECUTABLE_DIR)\customer_database_test.exe \ + $(EXECUTABLE_DIR)\key_value_test.exe \ + $(EXECUTABLE_DIR)\product_database_test.exe \ + $(EXECUTABLE_DIR)\run_tests.exe + +clean: + -cmd /c "@for %A in (\ + $(LIBRARY_DIR)\cmockery.lib \ + $(OBJECT_DIR)\cmockery.obj \ + $(EXECUTABLE_DIR)\calculator.exe \ + $(OBJECT_DIR)\calculator.obj \ + $(EXECUTABLE_DIR)\calculator_test.exe \ + $(OBJECT_DIR)\calculator_test.obj \ + $(OBJECT_DIR)\calculator_test-calculator.obj \ + $(EXECUTABLE_DIR)\allocate_module_test.exe \ + $(OBJECT_DIR)\allocate_module_test.obj \ + $(OBJECT_DIR)\allocate_module.obj \ + $(EXECUTABLE_DIR)\assert_macro_test.exe \ + $(OBJECT_DIR)\assert_macro_test.obj \ + $(OBJECT_DIR)\assert_macro.obj \ + $(EXECUTABLE_DIR)\customer_database_test.exe \ + $(OBJECT_DIR)\customer_database_test.obj \ + $(OBJECT_DIR)\customer_database.obj \ + $(EXECUTABLE_DIR)\key_value_test.exe \ + $(OBJECT_DIR)\key_value_test.obj \ + $(OBJECT_DIR)\key_value.obj \ + $(EXECUTABLE_DIR)\product_database_test.exe \ + $(OBJECT_DIR)\product_database_test.obj \ + $(OBJECT_DIR)\product_database.obj \ + $(EXECUTABLE_DIR)\run_tests.exe \ + $(OBJECT_DIR)\run_tests.obj) do @del %A 2>NUL" + -rmdir $(EXECUTABLE_DIR) $(OBJECT_DIR) $(LIBRARY_DIR) 2>NUL + +# Rules for the cmockery library. +$(LIBRARY_DIR)\cmockery.lib: $(OBJECT_DIR)\cmockery.obj +$(OBJECT_DIR)\cmockery.obj: $(LIBRARY_SOURCE_DIRECTORY)\cmockery.c + +# Rules for the calculator application. +$(EXECUTABLE_DIR)\calculator.exe: $(OBJECT_DIR)\calculator.obj + +$(OBJECT_DIR)\calculator.obj: $(EXAMPLE_SOURCE_DIRECTORY)\calculator.c + $(CC_COMMAND) + +# Rules for the calculator test application. +$(EXECUTABLE_DIR)\calculator_test.exe: \ + $(OBJECT_DIR)\calculator_test.obj \ + $(OBJECT_DIR)\calculator_test-calculator.obj \ + $(LIBRARY_DIR)\cmockery.lib + $(LINK_COMMAND) + +$(OBJECT_DIR)\calculator_test.obj: \ + $(EXAMPLE_SOURCE_DIRECTORY)\calculator_test.c + +$(OBJECT_DIR)\calculator_test-calculator.obj: \ + $(EXAMPLE_SOURCE_DIRECTORY)\calculator.c + $(CC_COMMAND_UNIT_TEST) + +# Sample code applications. +$(EXECUTABLE_DIR)\allocate_module_test.exe: \ + $(OBJECT_DIR)\allocate_module_test.obj \ + $(OBJECT_DIR)\allocate_module.obj \ + $(LIBRARY_DIR)\cmockery.lib + $(LINK_COMMAND) + +$(OBJECT_DIR)\allocate_module_test.obj: \ + $(EXAMPLE_SOURCE_DIRECTORY)\allocate_module_test.c +$(OBJECT_DIR)\allocate_module.obj: \ + $(EXAMPLE_SOURCE_DIRECTORY)\allocate_module.c + +$(EXECUTABLE_DIR)\assert_macro_test.exe: \ + $(OBJECT_DIR)\assert_macro_test.obj \ + $(OBJECT_DIR)\assert_macro.obj \ + $(LIBRARY_DIR)\cmockery.lib + $(LINK_COMMAND) + +$(OBJECT_DIR)\assert_macro_test.obj: \ + $(EXAMPLE_SOURCE_DIRECTORY)\assert_macro_test.c +$(OBJECT_DIR)\assert_macro.obj: $(EXAMPLE_SOURCE_DIRECTORY)\assert_macro.c + +$(EXECUTABLE_DIR)\customer_database_test.exe: \ + $(OBJECT_DIR)\customer_database_test.obj \ + $(OBJECT_DIR)\customer_database.obj \ + $(LIBRARY_DIR)\cmockery.lib + $(LINK_COMMAND) + +$(OBJECT_DIR)\customer_database_test.obj: \ + $(EXAMPLE_SOURCE_DIRECTORY)\customer_database_test.c +$(OBJECT_DIR)\customer_database.obj: \ + $(EXAMPLE_SOURCE_DIRECTORY)\customer_database.c + +$(EXECUTABLE_DIR)\key_value_test.exe: \ + $(OBJECT_DIR)\key_value_test.obj \ + $(OBJECT_DIR)\key_value.obj \ + $(LIBRARY_DIR)\cmockery.lib + $(LINK_COMMAND) + +$(OBJECT_DIR)\key_value_test.obj: $(EXAMPLE_SOURCE_DIRECTORY)\key_value_test.c +$(OBJECT_DIR)\key_value.obj: $(EXAMPLE_SOURCE_DIRECTORY)\key_value.c + +$(EXECUTABLE_DIR)\product_database_test.exe: \ + $(OBJECT_DIR)\product_database_test.obj \ + $(OBJECT_DIR)\product_database.obj \ + $(LIBRARY_DIR)\cmockery.lib + $(LINK_COMMAND) + +$(OBJECT_DIR)\product_database_test.obj: \ + $(EXAMPLE_SOURCE_DIRECTORY)\product_database_test.c +$(OBJECT_DIR)\product_database.obj: \ + $(EXAMPLE_SOURCE_DIRECTORY)\product_database.c + +$(EXECUTABLE_DIR)\run_tests.exe: \ + $(OBJECT_DIR)\run_tests.obj $(LIBRARY_DIR)\cmockery.lib + $(LINK_COMMAND) + +$(OBJECT_DIR)\run_tests.obj: $(EXAMPLE_SOURCE_DIRECTORY)\run_tests.c + +# Inference rules. +{$(OBJECT_DIR)\}.obj{$(EXECUTABLE_DIR)\}.exe: + $(LINK_COMMAND) + +{$(OBJECT_DIR)\}.obj{$(LIBRARY_DIR)\}.lib: + $(LIB_COMMAND) + +{$(LIBRARY_SOURCE_DIRECTORY)\}.c{$(OBJECT_DIR)\}.obj: + $(CC_COMMAND) + +{$(EXAMPLE_SOURCE_DIRECTORY)\}.c{$(OBJECT_DIR)\}.obj: + $(CC_COMMAND_UNIT_TEST) -- 2.34.1