diff options
author | Heather Lee Wilson <hwilson@google.com> | 2013-12-28 15:12:39 -0800 |
---|---|---|
committer | Heather Lee Wilson <hwilson@google.com> | 2013-12-28 15:18:58 -0800 |
commit | bdd62c531bbdea115a3a7e71bba91c19dd319cc4 (patch) | |
tree | ca6dbdc52599a865901d0ab1121563ae1ca5f47a /cmockery_0_1_2/src | |
parent | 78931d3e5b88cec04dac31c95ca67ff6378bed76 (diff) | |
download | cmockery-android-cts-5.1_r1.tar.gz |
Uploading cmockery 0.1.2 to external/cmockeryandroid-wear-5.0.0_r1android-cts-5.1_r9android-cts-5.1_r8android-cts-5.1_r7android-cts-5.1_r6android-cts-5.1_r5android-cts-5.1_r4android-cts-5.1_r3android-cts-5.1_r28android-cts-5.1_r27android-cts-5.1_r26android-cts-5.1_r25android-cts-5.1_r24android-cts-5.1_r23android-cts-5.1_r22android-cts-5.1_r21android-cts-5.1_r20android-cts-5.1_r2android-cts-5.1_r19android-cts-5.1_r18android-cts-5.1_r17android-cts-5.1_r16android-cts-5.1_r15android-cts-5.1_r14android-cts-5.1_r13android-cts-5.1_r10android-cts-5.1_r1android-cts-5.0_r9android-cts-5.0_r8android-cts-5.0_r7android-cts-5.0_r6android-cts-5.0_r5android-cts-5.0_r4android-cts-5.0_r3android-5.1.1_r9android-5.1.1_r8android-5.1.1_r7android-5.1.1_r6android-5.1.1_r5android-5.1.1_r4android-5.1.1_r38android-5.1.1_r37android-5.1.1_r36android-5.1.1_r35android-5.1.1_r34android-5.1.1_r33android-5.1.1_r30android-5.1.1_r3android-5.1.1_r29android-5.1.1_r28android-5.1.1_r26android-5.1.1_r25android-5.1.1_r24android-5.1.1_r23android-5.1.1_r22android-5.1.1_r20android-5.1.1_r2android-5.1.1_r19android-5.1.1_r18android-5.1.1_r17android-5.1.1_r16android-5.1.1_r15android-5.1.1_r14android-5.1.1_r13android-5.1.1_r12android-5.1.1_r10android-5.1.1_r1android-5.1.0_r5android-5.1.0_r4android-5.1.0_r3android-5.1.0_r1android-5.0.2_r3android-5.0.2_r1android-5.0.1_r1android-5.0.0_r7android-5.0.0_r6android-5.0.0_r5.1android-5.0.0_r5android-5.0.0_r4android-5.0.0_r3android-5.0.0_r2android-5.0.0_r1lollipop-wear-releaselollipop-releaselollipop-mr1-wfc-releaselollipop-mr1-releaselollipop-mr1-fi-releaselollipop-mr1-devlollipop-mr1-cts-releaselollipop-devlollipop-cts-release
A lightweight library to simplify and generalize the process of writing unit
tests for C applications.
Change-Id: I460a9b6740f10593e35ae988df753a43491c6456
Diffstat (limited to 'cmockery_0_1_2/src')
-rw-r--r-- | cmockery_0_1_2/src/cmockery.c | 1680 | ||||
-rw-r--r-- | cmockery_0_1_2/src/config.h | 137 | ||||
-rw-r--r-- | cmockery_0_1_2/src/config.h.in | 136 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/allocate_module.c | 44 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/allocate_module_test.c | 47 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/assert_macro.c | 37 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/assert_macro_test.c | 44 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/calculator.c | 266 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/calculator_test.c | 425 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/customer_database.c | 44 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/customer_database_test.c | 69 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/database.h | 37 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/key_value.c | 54 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/key_value_test.c | 80 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/product_database.c | 22 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/product_database_test.c | 66 | ||||
-rw-r--r-- | cmockery_0_1_2/src/example/run_tests.c | 30 | ||||
-rw-r--r-- | cmockery_0_1_2/src/google/cmockery.h | 435 |
18 files changed, 3653 insertions, 0 deletions
diff --git a/cmockery_0_1_2/src/cmockery.c b/cmockery_0_1_2/src/cmockery.c new file mode 100644 index 0000000..e66304e --- /dev/null +++ b/cmockery_0_1_2/src/cmockery.c @@ -0,0 +1,1680 @@ +/* + * 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 <setjmp.h> +#ifndef _WIN32 +#include <signal.h> +#endif // !_WIN32 +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#ifdef _WIN32 +#include <windows.h> +#endif // _WIN32 +#include <cmockery.h> + +#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 (int)total_failed; +} diff --git a/cmockery_0_1_2/src/config.h b/cmockery_0_1_2/src/config.h new file mode 100644 index 0000000..bcc87fa --- /dev/null +++ b/cmockery_0_1_2/src/config.h @@ -0,0 +1,137 @@ +/* src/config.h. Generated by configure. */ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Namespace for Google classes */ +#define GOOGLE_NAMESPACE ::google + +/* Define to 1 if you have the <assert.h> header file. */ +#define HAVE_ASSERT_H 1 + +/* Define to 1 if you have the `calloc' function. */ +#define HAVE_CALLOC 1 + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#define HAVE_DLFCN_H 1 + +/* Define to 1 if you have the `exit' function. */ +#define HAVE_EXIT 1 + +/* Define to 1 if you have the `fprintf' function. */ +#define HAVE_FPRINTF 1 + +/* Define to 1 if you have the `free' function. */ +#define HAVE_FREE 1 + +/* Define to 1 if you have the <inttypes.h> header file. */ +#define HAVE_INTTYPES_H 1 + +/* Define to 1 if you have the `longjmp' function. */ +#define HAVE_LONGJMP 1 + +/* Define to 1 if you have the `malloc' function. */ +#define HAVE_MALLOC 1 + +/* Define to 1 if you have the <malloc.h> header file. */ +#define HAVE_MALLOC_H 1 + +/* Define to 1 if you have the `memcpy' function. */ +#define HAVE_MEMCPY 1 + +/* Define to 1 if you have the <memory.h> header file. */ +#define HAVE_MEMORY_H 1 + +/* Define to 1 if you have the `memset' function. */ +#define HAVE_MEMSET 1 + +/* define if the compiler implements namespaces */ +#define HAVE_NAMESPACES 1 + +/* Define to 1 if you have the `printf' function. */ +#define HAVE_PRINTF 1 + +/* Define to 1 if you have the `setjmp' function. */ +#define HAVE_SETJMP 1 + +/* Define to 1 if you have the <setjmp.h> header file. */ +#define HAVE_SETJMP_H 1 + +/* Define to 1 if you have the `signal' function. */ +#define HAVE_SIGNAL 1 + +/* Define to 1 if you have the <signal.h> header file. */ +#define HAVE_SIGNAL_H 1 + +/* Define to 1 if you have the `snprintf' function. */ +#define HAVE_SNPRINTF 1 + +/* Define to 1 if you have the <stdarg.h> header file. */ +#define HAVE_STDARG_H 1 + +/* Define to 1 if you have the <stddef.h> header file. */ +#define HAVE_STDDEF_H 1 + +/* Define to 1 if you have the <stdint.h> header file. */ +#define HAVE_STDINT_H 1 + +/* Define to 1 if you have the <stdio.h> header file. */ +#define HAVE_STDIO_H 1 + +/* Define to 1 if you have the <stdlib.h> header file. */ +#define HAVE_STDLIB_H 1 + +/* Define to 1 if you have the `strcmp' function. */ +#define HAVE_STRCMP 1 + +/* Define to 1 if you have the `strcpy' function. */ +#define HAVE_STRCPY 1 + +/* Define to 1 if you have the <strings.h> header file. */ +#define HAVE_STRINGS_H 1 + +/* Define to 1 if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#define HAVE_SYS_STAT_H 1 + +/* Define to 1 if you have the <sys/types.h> header file. */ +#define HAVE_SYS_TYPES_H 1 + +/* Define to 1 if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define to 1 if you have the `vsnprintf' function. */ +#define HAVE_VSNPRINTF 1 + +/* Name of package */ +#define PACKAGE "cmockery" + +/* Define to the address where bug reports for this package should be sent. */ +#define PACKAGE_BUGREPORT "opensource@google.com" + +/* Define to the full name of this package. */ +#define PACKAGE_NAME "cmockery" + +/* Define to the full name and version of this package. */ +#define PACKAGE_STRING "cmockery 0.1.2" + +/* Define to the one symbol short name of this package. */ +#define PACKAGE_TARNAME "cmockery" + +/* Define to the version of this package. */ +#define PACKAGE_VERSION "0.1.2" + +/* Define to 1 if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* the namespace where STL code like vector<> is defined */ +#define STL_NAMESPACE std + +/* Version number of package */ +#define VERSION "0.1.2" + +/* Stops putting the code inside the Google namespace */ +#define _END_GOOGLE_NAMESPACE_ } + +/* Puts following code inside the Google namespace */ +#define _START_GOOGLE_NAMESPACE_ namespace google { diff --git a/cmockery_0_1_2/src/config.h.in b/cmockery_0_1_2/src/config.h.in new file mode 100644 index 0000000..802d225 --- /dev/null +++ b/cmockery_0_1_2/src/config.h.in @@ -0,0 +1,136 @@ +/* src/config.h.in. Generated from configure.ac by autoheader. */ + +/* Namespace for Google classes */ +#undef GOOGLE_NAMESPACE + +/* Define to 1 if you have the <assert.h> header file. */ +#undef HAVE_ASSERT_H + +/* Define to 1 if you have the `calloc' function. */ +#undef HAVE_CALLOC + +/* Define to 1 if you have the <dlfcn.h> header file. */ +#undef HAVE_DLFCN_H + +/* Define to 1 if you have the `exit' function. */ +#undef HAVE_EXIT + +/* Define to 1 if you have the `fprintf' function. */ +#undef HAVE_FPRINTF + +/* Define to 1 if you have the `free' function. */ +#undef HAVE_FREE + +/* Define to 1 if you have the <inttypes.h> header file. */ +#undef HAVE_INTTYPES_H + +/* Define to 1 if you have the `longjmp' function. */ +#undef HAVE_LONGJMP + +/* Define to 1 if you have the `malloc' function. */ +#undef HAVE_MALLOC + +/* Define to 1 if you have the <malloc.h> header file. */ +#undef HAVE_MALLOC_H + +/* Define to 1 if you have the `memcpy' function. */ +#undef HAVE_MEMCPY + +/* Define to 1 if you have the <memory.h> header file. */ +#undef HAVE_MEMORY_H + +/* Define to 1 if you have the `memset' function. */ +#undef HAVE_MEMSET + +/* define if the compiler implements namespaces */ +#undef HAVE_NAMESPACES + +/* Define to 1 if you have the `printf' function. */ +#undef HAVE_PRINTF + +/* Define to 1 if you have the `setjmp' function. */ +#undef HAVE_SETJMP + +/* Define to 1 if you have the <setjmp.h> header file. */ +#undef HAVE_SETJMP_H + +/* Define to 1 if you have the `signal' function. */ +#undef HAVE_SIGNAL + +/* Define to 1 if you have the <signal.h> header file. */ +#undef HAVE_SIGNAL_H + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the <stdarg.h> header file. */ +#undef HAVE_STDARG_H + +/* Define to 1 if you have the <stddef.h> header file. */ +#undef HAVE_STDDEF_H + +/* Define to 1 if you have the <stdint.h> header file. */ +#undef HAVE_STDINT_H + +/* Define to 1 if you have the <stdio.h> header file. */ +#undef HAVE_STDIO_H + +/* Define to 1 if you have the <stdlib.h> header file. */ +#undef HAVE_STDLIB_H + +/* Define to 1 if you have the `strcmp' function. */ +#undef HAVE_STRCMP + +/* Define to 1 if you have the `strcpy' function. */ +#undef HAVE_STRCPY + +/* Define to 1 if you have the <strings.h> header file. */ +#undef HAVE_STRINGS_H + +/* Define to 1 if you have the <string.h> header file. */ +#undef HAVE_STRING_H + +/* Define to 1 if you have the <sys/stat.h> header file. */ +#undef HAVE_SYS_STAT_H + +/* Define to 1 if you have the <sys/types.h> header file. */ +#undef HAVE_SYS_TYPES_H + +/* Define to 1 if you have the <unistd.h> header file. */ +#undef HAVE_UNISTD_H + +/* Define to 1 if you have the `vsnprintf' function. */ +#undef HAVE_VSNPRINTF + +/* Name of package */ +#undef PACKAGE + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* Define to 1 if you have the ANSI C header files. */ +#undef STDC_HEADERS + +/* the namespace where STL code like vector<> is defined */ +#undef STL_NAMESPACE + +/* Version number of package */ +#undef VERSION + +/* Stops putting the code inside the Google namespace */ +#undef _END_GOOGLE_NAMESPACE_ + +/* Puts following code inside the Google namespace */ +#undef _START_GOOGLE_NAMESPACE_ diff --git a/cmockery_0_1_2/src/example/allocate_module.c b/cmockery_0_1_2/src/example/allocate_module.c new file mode 100644 index 0000000..dda7260 --- /dev/null +++ b/cmockery_0_1_2/src/example/allocate_module.c @@ -0,0 +1,44 @@ +/* + * 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 <stdlib.h> + +#if UNIT_TESTING +extern void* _test_malloc(const size_t size, const char* file, const int line); +extern void* _test_calloc(const size_t number_of_elements, const size_t size, + const char* file, const int line); +extern void _test_free(void* const ptr, const char* file, const int line); + +#define malloc(size) _test_malloc(size, __FILE__, __LINE__) +#define calloc(num, size) _test_calloc(num, size, __FILE__, __LINE__) +#define free(ptr) _test_free(ptr, __FILE__, __LINE__) +#endif // UNIT_TESTING + +void leak_memory() { + int * const temporary = (int*)malloc(sizeof(int)); + *temporary = 0; +} + +void buffer_overflow() { + char * const memory = (char*)malloc(sizeof(int)); + memory[sizeof(int)] = '!'; + free(memory); +} + +void buffer_underflow() { + char * const memory = (char*)malloc(sizeof(int)); + memory[-1] = '!'; + free(memory); +} diff --git a/cmockery_0_1_2/src/example/allocate_module_test.c b/cmockery_0_1_2/src/example/allocate_module_test.c new file mode 100644 index 0000000..f7d6079 --- /dev/null +++ b/cmockery_0_1_2/src/example/allocate_module_test.c @@ -0,0 +1,47 @@ +/* + * 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmockery.h> + +extern void leak_memory(); +extern void buffer_overflow(); +extern void buffer_underflow(); + +// Test case that fails as leak_memory() leaks a dynamically allocated block. +void leak_memory_test(void **state) { + leak_memory(); +} + +// Test case that fails as buffer_overflow() corrupts an allocated block. +void buffer_overflow_test(void **state) { + buffer_overflow(); +} + +// Test case that fails as buffer_underflow() corrupts an allocated block. +void buffer_underflow_test(void **state) { + buffer_underflow(); +} + +int main(int argc, char* argv[]) { + const UnitTest tests[] = { + unit_test(leak_memory_test), + unit_test(buffer_overflow_test), + unit_test(buffer_underflow_test), + }; + return run_tests(tests); +} diff --git a/cmockery_0_1_2/src/example/assert_macro.c b/cmockery_0_1_2/src/example/assert_macro.c new file mode 100644 index 0000000..aadcac3 --- /dev/null +++ b/cmockery_0_1_2/src/example/assert_macro.c @@ -0,0 +1,37 @@ +/* + * 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 <string.h> + +static const char* status_code_strings[] = { + "Address not found", + "Connection dropped", + "Connection timed out", +}; + +const char* get_status_code_string(const unsigned int status_code) { + return status_code_strings[status_code]; +}; + +unsigned int string_to_status_code(const char* const status_code_string) { + unsigned int i; + for (i = 0; i < sizeof(status_code_string) / sizeof(status_code_string[0]); + i++) { + if (strcmp(status_code_strings[i], status_code_string) == 0) { + return i; + } + } + return ~0U; +} diff --git a/cmockery_0_1_2/src/example/assert_macro_test.c b/cmockery_0_1_2/src/example/assert_macro_test.c new file mode 100644 index 0000000..0df8f96 --- /dev/null +++ b/cmockery_0_1_2/src/example/assert_macro_test.c @@ -0,0 +1,44 @@ +/* + * 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmockery.h> + +extern const char* get_status_code_string(const unsigned int status_code); +extern unsigned int string_to_status_code( + const char* const status_code_string); + +/* This test will fail since the string returned by get_status_code_string(0) + * doesn't match "Connection timed out". */ +void get_status_code_string_test(void **state) { + assert_string_equal(get_status_code_string(0), "Address not found"); + assert_string_equal(get_status_code_string(1), "Connection timed out"); +} + +// This test will fail since the status code of "Connection timed out" isn't 1 +void string_to_status_code_test(void **state) { + assert_int_equal(string_to_status_code("Address not found"), 0); + assert_int_equal(string_to_status_code("Connection timed out"), 1); +} + +int main(int argc, char *argv[]) { + const UnitTest tests[] = { + unit_test(get_status_code_string_test), + unit_test(string_to_status_code_test), + }; + return run_tests(tests); +} diff --git a/cmockery_0_1_2/src/example/calculator.c b/cmockery_0_1_2/src/example/calculator.c new file mode 100644 index 0000000..da3775b --- /dev/null +++ b/cmockery_0_1_2/src/example/calculator.c @@ -0,0 +1,266 @@ +/* + * 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. + */ + +// A calculator example used to demonstrate the cmockery testing library. + +#include <assert.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +// If this is being built for a unit test. +#if UNIT_TESTING + +/* Redirect printf to a function in the test application so it's possible to + * test the standard output. */ +#ifdef printf +#undef printf +#endif // printf +#define printf example_test_printf + +extern void print_message(const char *format, ...); + +/* Redirect fprintf to a function in the test application so it's possible to + * test error messages. */ +#ifdef fprintf +#undef fprintf +#endif // fprintf +#define fprintf example_test_fprintf + +extern int example_test_fprintf(FILE * const file, const char *format, ...); + +// Redirect assert to mock_assert() so assertions can be caught by cmockery. +#ifdef assert +#undef assert +#endif // assert +#define assert(expression) \ + mock_assert((int)(expression), #expression, __FILE__, __LINE__) +void mock_assert(const int result, const char* expression, const char *file, + const int line); + +/* Redirect calloc and free to test_calloc() and test_free() so cmockery can + * check for memory leaks. */ +#ifdef calloc +#undef calloc +#endif // calloc +#define calloc(num, size) _test_calloc(num, size, __FILE__, __LINE__) +#ifdef free +#undef free +#endif // free +#define free(ptr) _test_free(ptr, __FILE__, __LINE__) +void* _test_calloc(const size_t number_of_elements, const size_t size, + const char* file, const int line); +void _test_free(void* const ptr, const char* file, const int line); + +/* main is defined in the unit test so redefine name of the the main function + * here. */ +#define main example_main + +/* All functions in this object need to be exposed to the test application, + * so redefine static to nothing. */ +#define static + +#endif // UNIT_TESTING + + +// A binary arithmetic integer operation (add, subtract etc.) +typedef int (*BinaryOperator)(int a, int b); + +// Structure which maps operator strings to functions. +typedef struct OperatorFunction { + const char* operator; + BinaryOperator function; +} OperatorFunction; + + +static int add(int a, int b); +static int subtract(int a, int b); +static int multiply(int a, int b); +static int divide(int a, int b); + +// Associate operator strings to functions. +static OperatorFunction operator_function_map[] = { + {"+", add}, + {"-", subtract}, + {"*", multiply}, + {"/", divide}, +}; + +static int add(int a, int b) { + return a + b; +} + +static int subtract(int a, int b) { + return a - b; +} + +static int multiply(int a, int b) { + return a * b; +} + +static int divide(int a, int b) { + assert(b); // Check for divde by zero. + return a / b; +} + +/* Searches the specified array of operator_functions for the function + * associated with the specified operator_string. This function returns the + * function associated with operator_string if successful, NULL otherwise. + */ +static BinaryOperator find_operator_function_by_string( + const size_t number_of_operator_functions, + const OperatorFunction * const operator_functions, + const char* const operator_string) { + size_t i; + assert(!number_of_operator_functions || operator_functions); + assert(operator_string); + + for (i = 0; i < number_of_operator_functions; i++) { + const OperatorFunction *const operator_function = + &operator_functions[i]; + if (strcmp(operator_function->operator, operator_string) == 0) { + return operator_function->function; + } + } + return NULL; +} + +/* Perform a series of binary arithmetic integer operations with no operator + * precedence. + * + * The input expression is specified by arguments which is an array of + * containing number_of_arguments strings. Operators invoked by the expression + * are specified by the array operator_functions containing + * number_of_operator_functions, OperatorFunction structures. The value of + * each binary operation is stored in a pointer returned to intermediate_values + * which is allocated by malloc(). + * + * If successful, this function returns the integer result of the operations. + * If an error occurs while performing the operation error_occurred is set to + * 1, the operation is aborted and 0 is returned. + */ +static int perform_operation( + int number_of_arguments, char *arguments[], + const size_t number_of_operator_functions, + const OperatorFunction * const operator_functions, + int * const number_of_intermediate_values, + int ** const intermediate_values, int * const error_occurred) { + char *end_of_integer; + int value; + unsigned int i; + assert(!number_of_arguments || arguments); + assert(!number_of_operator_functions || operator_functions); + assert(error_occurred); + assert(number_of_intermediate_values); + assert(intermediate_values); + + *error_occurred = 0; + *number_of_intermediate_values = 0; + *intermediate_values = NULL; + if (!number_of_arguments) + return 0; + + // Parse the first value. + value = (int)strtol(arguments[0], &end_of_integer, 10); + if (end_of_integer == arguments[0]) { + // If an error occurred while parsing the integer. + fprintf(stderr, "Unable to parse integer from argument %s\n", + arguments[0]); + *error_occurred = 1; + return 0; + } + + // Allocate an array for the output values. + *intermediate_values = calloc(((number_of_arguments - 1) / 2), + sizeof(**intermediate_values)); + + i = 1; + while (i < number_of_arguments) { + int other_value; + const char* const operator_string = arguments[i]; + const BinaryOperator function = find_operator_function_by_string( + number_of_operator_functions, operator_functions, operator_string); + int * const intermediate_value = + &((*intermediate_values)[*number_of_intermediate_values]); + (*number_of_intermediate_values) ++; + + if (!function) { + fprintf(stderr, "Unknown operator %s, argument %d\n", + operator_string, i); + *error_occurred = 1; + break; + } + i ++; + + if (i == number_of_arguments) { + fprintf(stderr, "Binary operator %s missing argument\n", + operator_string); + *error_occurred = 1; + break; + } + + other_value = (int)strtol(arguments[i], &end_of_integer, 10); + if (end_of_integer == arguments[i]) { + // If an error occurred while parsing the integer. + fprintf(stderr, "Unable to parse integer %s of argument %d\n", + arguments[i], i); + *error_occurred = 1; + break; + } + i ++; + + // Perform the operation and store the intermediate value. + *intermediate_value = function(value, other_value); + value = *intermediate_value; + } + if (*error_occurred) { + free(*intermediate_values); + *intermediate_values = NULL; + *number_of_intermediate_values = 0; + return 0; + } + return value; +} + +int main(int argc, char *argv[]) { + int return_value; + int number_of_intermediate_values; + int *intermediate_values; + // Peform the operation. + const int result = perform_operation( + argc - 1, &argv[1], + sizeof(operator_function_map) / sizeof(operator_function_map[0]), + operator_function_map, &number_of_intermediate_values, + &intermediate_values, &return_value); + + // If no errors occurred display the result. + if (!return_value && argc > 1) { + unsigned int i; + unsigned int intermediate_value_index = 0; + printf("%s\n", argv[1]); + for (i = 2; i < argc; i += 2) { + assert(intermediate_value_index < number_of_intermediate_values); + printf(" %s %s = %d\n", argv[i], argv[i + 1], + intermediate_values[intermediate_value_index++]); + } + printf("= %d\n", result); + } + if (intermediate_values) { + free(intermediate_values); + } + + return return_value; +} diff --git a/cmockery_0_1_2/src/example/calculator_test.c b/cmockery_0_1_2/src/example/calculator_test.c new file mode 100644 index 0000000..6feaf41 --- /dev/null +++ b/cmockery_0_1_2/src/example/calculator_test.c @@ -0,0 +1,425 @@ +/* + * 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include "cmockery.h" +#include <stdio.h> + +#ifdef _WIN32 +// Compatibility with the Windows standard C library. +#define vsnprintf _vsnprintf +#endif // _WIN32 + +#define array_length(x) (sizeof(x) / sizeof((x)[0])) + +/* To simplify this code, these functions and data structures could have been + * separated out from the application example.c into a header shared with + * test application. However, this example illustrates how it's possible to + * test existing code with little modification. */ + +typedef int (*BinaryOperator)(int a, int b); + +typedef struct OperatorFunction { + const char* operator; + BinaryOperator function; +} OperatorFunction; + +extern int add(int a, int b); +extern int subtract(int a, int b); +extern int multiply(int a, int b); +extern int divide(int a, int b); +extern BinaryOperator find_operator_function_by_string( + const size_t number_of_operator_functions, + const OperatorFunction * const operator_functions, + const char* const operator_string); +extern int perform_operation( + int number_of_arguments, char *arguments[], + const size_t number_of_operator_functions, + const OperatorFunction * const operator_functions, + int * const number_of_intermediate_values, + int ** const intermediate_values, int * const error_occurred); +extern int example_main(int argc, char *argv[]); + +/* A mock fprintf function that checks the value of strings printed to the + * standard error stream. */ +int example_test_fprintf(FILE* const file, const char *format, ...) { + int return_value; + va_list args; + char temporary_buffer[256]; + assert_true(file == stderr); + va_start(args, format); + return_value = vsnprintf(temporary_buffer, sizeof(temporary_buffer), + format, args); + check_expected(temporary_buffer); + va_end(args); + return return_value; +} + +/* A mock printf function that checks the value of strings printed to the + * standard output stream. */ +int example_test_printf(const char *format, ...) { + int return_value; + va_list args; + char temporary_buffer[256]; + va_start(args, format); + return_value = vsnprintf(temporary_buffer, sizeof(temporary_buffer), + format, args); + check_expected(temporary_buffer); + va_end(args); + return return_value; +} + +// A mock binary operator function. +int binary_operator(int a, int b) { + check_expected(a); + check_expected(b); + return (int)mock(); +} + + +// Ensure add() adds two integers correctly. +void test_add(void **state) { + assert_int_equal(add(3, 3), 6); + assert_int_equal(add(3, -3), 0); +} + +// Ensure subtract() subtracts two integers correctly. +void test_subtract(void **state) { + assert_int_equal(subtract(3, 3), 0); + assert_int_equal(subtract(3, -3), 6); +} + +// Ensure multiple() mulitplies two integers correctly. +void test_multiply(void **state) { + assert_int_equal(multiply(3, 3), 9); + assert_int_equal(multiply(3, 0), 0); +} + +// Ensure divide() divides one integer by another correctly. +void test_divide(void **state) { + assert_int_equal(divide(10, 2), 5); + assert_int_equal(divide(2, 10), 0); +} + +// Ensure divide() asserts when trying to divide by zero. +void test_divide_by_zero(void **state) { + expect_assert_failure(divide(100, 0)); +} + +/* Ensure find_operator_function_by_string() asserts when a NULL pointer is + * specified as the table to search. */ +void test_find_operator_function_by_string_null_functions(void **state) { + expect_assert_failure(find_operator_function_by_string(1, NULL, "test")); +} + +/* Ensure find_operator_function_by_string() asserts when a NULL pointer is + * specified as the string to search for. */ +void test_find_operator_function_by_string_null_string(void **state) { + const OperatorFunction operator_functions[] = { + {"+", binary_operator}, + }; + expect_assert_failure(find_operator_function_by_string( + array_length(operator_functions), operator_functions, NULL)); +} + +/* Ensure find_operator_function_by_string() returns NULL when a NULL pointer + * is specified as the table to search when the table size is 0. */ +void test_find_operator_function_by_string_valid_null_functions(void **state) { + assert_int_equal((int)find_operator_function_by_string(0, NULL, "test"), + (int)NULL); +} + +/* Ensure find_operator_function_by_string() returns NULL when searching for + * an operator string that isn't in the specified table. */ +void test_find_operator_function_by_string_not_found(void **state) { + const OperatorFunction operator_functions[] = { + {"+", binary_operator}, + {"-", binary_operator}, + {"/", binary_operator}, + }; + assert_int_equal((int)find_operator_function_by_string( + array_length(operator_functions), operator_functions, "test"), + (int)NULL); +} + +/* Ensure find_operator_function_by_string() returns the correct function when + * searching for an operator string that is in the specified table. */ +void test_find_operator_function_by_string_found(void **state) { + const OperatorFunction operator_functions[] = { + {"+", (BinaryOperator)0x12345678}, + {"-", (BinaryOperator)0xDEADBEEF}, + {"/", (BinaryOperator)0xABADCAFE}, + }; + assert_int_equal((int)find_operator_function_by_string( + array_length(operator_functions), operator_functions, "-"), + 0xDEADBEEF); +} + +// Ensure perform_operation() asserts when a NULL arguments array is specified. +void test_perform_operation_null_args(void **state) { + const OperatorFunction operator_functions[] = { + {"+", binary_operator}, + }; + int number_of_intermediate_values; + int *intermediate_values; + int error_occurred; + expect_assert_failure(perform_operation( + 1, NULL, array_length(operator_functions), operator_functions, + &number_of_intermediate_values, &intermediate_values, + &error_occurred)); +} + +/* Ensure perform_operation() asserts when a NULL operator_functions array is + * specified. */ +void test_perform_operation_null_operator_functions(void **state) { + char *args[] = { + "1", "+", "2", "*", "4" + }; + int number_of_intermediate_values; + int *intermediate_values; + int error_occurred; + expect_assert_failure(perform_operation( + array_length(args), args, 1, NULL, &number_of_intermediate_values, + &intermediate_values, &error_occurred)); +} + +/* Ensure perform_operation() asserts when a NULL pointer is specified for + * number_of_intermediate_values. */ +void test_perform_operation_null_number_of_intermediate_values(void **state) { + const OperatorFunction operator_functions[] = { + {"+", binary_operator}, + }; + char *args[] = { + "1", "+", "2", "*", "4" + }; + int *intermediate_values; + int error_occurred; + expect_assert_failure(perform_operation( + array_length(args), args, 1, operator_functions, NULL, + &intermediate_values, &error_occurred)); +} + +/* Ensure perform_operation() asserts when a NULL pointer is specified for + * intermediate_values. */ +void test_perform_operation_null_intermediate_values(void **state) { + const OperatorFunction operator_functions[] = { + {"+", binary_operator}, + }; + char *args[] = { + "1", "+", "2", "*", "4" + }; + int number_of_intermediate_values; + int error_occurred; + expect_assert_failure(perform_operation( + array_length(args), args, array_length(operator_functions), + operator_functions, &number_of_intermediate_values, NULL, + &error_occurred)); +} + +// Ensure perform_operation() returns 0 when no arguments are specified. +void test_perform_operation_no_arguments(void **state) { + int number_of_intermediate_values; + int *intermediate_values; + int error_occurred; + assert_int_equal(perform_operation( + 0, NULL, 0, NULL, &number_of_intermediate_values, &intermediate_values, + &error_occurred), 0); + assert_int_equal(error_occurred, 0); +} + +/* Ensure perform_operation() returns an error if the first argument isn't + * an integer string. */ +void test_perform_operation_first_arg_not_integer(void **state) { + const OperatorFunction operator_functions[] = { + {"+", binary_operator}, + }; + char *args[] = { + "test", "+", "2", "*", "4" + }; + int number_of_intermediate_values; + int *intermediate_values; + int error_occurred; + + expect_string(example_test_fprintf, temporary_buffer, + "Unable to parse integer from argument test\n"); + + assert_int_equal(perform_operation( + array_length(args), args, array_length(operator_functions), + operator_functions, &number_of_intermediate_values, + &intermediate_values, &error_occurred), 0); + assert_int_equal(error_occurred, 1); +} + +/* Ensure perform_operation() returns an error when parsing an unknown + * operator. */ +void test_perform_operation_unknown_operator(void **state) { + const OperatorFunction operator_functions[] = { + {"+", binary_operator}, + }; + char *args[] = { + "1", "*", "2", "*", "4" + }; + int number_of_intermediate_values; + int *intermediate_values; + int error_occurred; + + expect_string(example_test_fprintf, temporary_buffer, + "Unknown operator *, argument 1\n"); + + assert_int_equal(perform_operation( + array_length(args), args, array_length(operator_functions), + operator_functions, &number_of_intermediate_values, + &intermediate_values, &error_occurred), 0); + assert_int_equal(error_occurred, 1); +} + +/* Ensure perform_operation() returns an error when nothing follows an + * operator. */ +void test_perform_operation_missing_argument(void **state) { + const OperatorFunction operator_functions[] = { + {"+", binary_operator}, + }; + char *args[] = { + "1", "+", + }; + int number_of_intermediate_values; + int *intermediate_values; + int error_occurred; + + expect_string(example_test_fprintf, temporary_buffer, + "Binary operator + missing argument\n"); + + assert_int_equal(perform_operation( + array_length(args), args, array_length(operator_functions), + operator_functions, &number_of_intermediate_values, + &intermediate_values, &error_occurred), 0); + assert_int_equal(error_occurred, 1); +} + +/* Ensure perform_operation() returns an error when an integer doesn't follow + * an operator. */ +void test_perform_operation_no_integer_after_operator(void **state) { + const OperatorFunction operator_functions[] = { + {"+", binary_operator}, + }; + char *args[] = { + "1", "+", "test", + }; + int number_of_intermediate_values; + int *intermediate_values; + int error_occurred; + + expect_string(example_test_fprintf, temporary_buffer, + "Unable to parse integer test of argument 2\n"); + + assert_int_equal(perform_operation( + array_length(args), args, array_length(operator_functions), + operator_functions, &number_of_intermediate_values, + &intermediate_values, &error_occurred), 0); + assert_int_equal(error_occurred, 1); +} + + +// Ensure perform_operation() succeeds given valid input parameters. +void test_perform_operation(void **state) { + const OperatorFunction operator_functions[] = { + {"+", binary_operator}, + {"*", binary_operator}, + }; + char *args[] = { + "1", "+", "3", "*", "10", + }; + int number_of_intermediate_values; + int *intermediate_values; + int error_occurred; + + // Setup return values of mock operator functions. + // Addition. + expect_value(binary_operator, a, 1); + expect_value(binary_operator, b, 3); + will_return(binary_operator, 4); + + // Multiplication. + expect_value(binary_operator, a, 4); + expect_value(binary_operator, b, 10); + will_return(binary_operator, 40); + + assert_int_equal(perform_operation( + array_length(args), args, array_length(operator_functions), + operator_functions, &number_of_intermediate_values, + &intermediate_values, &error_occurred), 40); + assert_int_equal(error_occurred, 0); + + assert_true(intermediate_values); + assert_int_equal(intermediate_values[0], 4); + assert_int_equal(intermediate_values[1], 40); + test_free(intermediate_values); +} + + +// Ensure main() in example.c succeeds given no arguments. +void test_example_main_no_args(void **state) { + char *args[] = { + "example", + }; + assert_int_equal(example_main(array_length(args), args), 0); +} + + + +// Ensure main() in example.c succeeds given valid input arguments. +void test_example_main(void **state) { + char *args[] = { + "example", "1", "+", "3", "*", "10", + }; + + expect_string(example_test_printf, temporary_buffer, "1\n"); + expect_string(example_test_printf, temporary_buffer, " + 3 = 4\n"); + expect_string(example_test_printf, temporary_buffer, " * 10 = 40\n"); + expect_string(example_test_printf, temporary_buffer, "= 40\n"); + + assert_int_equal(example_main(array_length(args), args), 0); +} + + +int main(int argc, char* argv[]) { + UnitTest tests[] = { + unit_test(test_add), + unit_test(test_subtract), + unit_test(test_multiply), + unit_test(test_divide), + unit_test(test_divide_by_zero), + unit_test(test_find_operator_function_by_string_null_functions), + unit_test(test_find_operator_function_by_string_null_string), + unit_test(test_find_operator_function_by_string_valid_null_functions), + unit_test(test_find_operator_function_by_string_not_found), + unit_test(test_find_operator_function_by_string_found), + unit_test(test_perform_operation_null_args), + unit_test(test_perform_operation_null_operator_functions), + unit_test(test_perform_operation_null_number_of_intermediate_values), + unit_test(test_perform_operation_null_intermediate_values), + unit_test(test_perform_operation_no_arguments), + unit_test(test_perform_operation_first_arg_not_integer), + unit_test(test_perform_operation_unknown_operator), + unit_test(test_perform_operation_missing_argument), + unit_test(test_perform_operation_no_integer_after_operator), + unit_test(test_perform_operation), + unit_test(test_example_main_no_args), + unit_test(test_example_main), + }; + return run_tests(tests); +} diff --git a/cmockery_0_1_2/src/example/customer_database.c b/cmockery_0_1_2/src/example/customer_database.c new file mode 100644 index 0000000..1cfce7e --- /dev/null +++ b/cmockery_0_1_2/src/example/customer_database.c @@ -0,0 +1,44 @@ +/* + * 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 <stddef.h> +#include <stdio.h> +#include <database.h> +#ifdef _WIN32 +#define snprintf _snprintf +#endif // _WIN32 + +// Connect to the database containing customer information. +DatabaseConnection* connect_to_customer_database() { + return connect_to_database("customers.abcd.org", 321); +} + +/* Find the ID of a customer by his/her name returning a value > 0 if + * successful, 0 otherwise. */ +unsigned int get_customer_id_by_name( + DatabaseConnection * const connection, + const char * const customer_name) { + char query_string[256]; + int number_of_results; + void **results; + snprintf(query_string, sizeof(query_string), + "SELECT ID FROM CUSTOMERS WHERE NAME = %s", customer_name); + number_of_results = connection->query_database(connection, query_string, + &results); + if (number_of_results != 1) { + return -1; + } + return (unsigned int)results[0]; +} diff --git a/cmockery_0_1_2/src/example/customer_database_test.c b/cmockery_0_1_2/src/example/customer_database_test.c new file mode 100644 index 0000000..b059007 --- /dev/null +++ b/cmockery_0_1_2/src/example/customer_database_test.c @@ -0,0 +1,69 @@ +/* + * 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmockery.h> +#include <database.h> + +extern DatabaseConnection* connect_to_customer_database(); +extern unsigned int get_customer_id_by_name( + DatabaseConnection * const connection, const char * const customer_name); + +// Mock query database function. +unsigned int mock_query_database( + DatabaseConnection* const connection, const char * const query_string, + void *** const results) { + *results = (void**)mock(); + return (unsigned int)mock(); +} + +// Mock of the connect to database function. +DatabaseConnection* connect_to_database(const char * const database_url, + const unsigned int port) { + return (DatabaseConnection*)mock(); +} + +void test_connect_to_customer_database(void **state) { + will_return(connect_to_database, 0x0DA7ABA53); + assert_int_equal((int)connect_to_customer_database(), 0x0DA7ABA53); +} + +/* This test fails as the mock function connect_to_database() will have no + * value to return. */ +void fail_connect_to_customer_database(void **state) { + assert_true(connect_to_customer_database() == + (DatabaseConnection*)0x0DA7ABA53); +} + +void test_get_customer_id_by_name(void **state) { + DatabaseConnection connection = { + "somedatabase.somewhere.com", 12345678, mock_query_database + }; + // Return a single customer ID when mock_query_database() is called. + int customer_ids = 543; + will_return(mock_query_database, &customer_ids); + will_return(mock_query_database, 1); + assert_int_equal(get_customer_id_by_name(&connection, "john doe"), 543); +} + +int main(int argc, char* argv[]) { + const UnitTest tests[] = { + unit_test(test_connect_to_customer_database), + unit_test(test_get_customer_id_by_name), + }; + return run_tests(tests); +} diff --git a/cmockery_0_1_2/src/example/database.h b/cmockery_0_1_2/src/example/database.h new file mode 100644 index 0000000..f6f6a43 --- /dev/null +++ b/cmockery_0_1_2/src/example/database.h @@ -0,0 +1,37 @@ +/* + * 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. + */ +typedef struct DatabaseConnection DatabaseConnection; + +/* Function that takes an SQL query string and sets results to an array of + * pointers with the result of the query. The value returned specifies the + * number of items in the returned array of results. The returned array of + * results are statically allocated and should not be deallocated using free() + */ +typedef unsigned int (*QueryDatabase)( + DatabaseConnection* const connection, const char * const query_string, + void *** const results); + +// Connection to a database. +struct DatabaseConnection { + const char *url; + unsigned int port; + QueryDatabase query_database; +}; + +// Connect to a database. +DatabaseConnection* connect_to_database(const char * const url, + const unsigned int port); + diff --git a/cmockery_0_1_2/src/example/key_value.c b/cmockery_0_1_2/src/example/key_value.c new file mode 100644 index 0000000..00e1bc4 --- /dev/null +++ b/cmockery_0_1_2/src/example/key_value.c @@ -0,0 +1,54 @@ +/* + * 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 <stddef.h> +#include <stdlib.h> +#include <string.h> + +typedef struct KeyValue { + unsigned int key; + const char* value; +} KeyValue; + +static KeyValue *key_values = NULL; +static unsigned int number_of_key_values = 0; + +void set_key_values(KeyValue * const new_key_values, + const unsigned int new_number_of_key_values) { + key_values = new_key_values; + number_of_key_values = new_number_of_key_values; +} + +// Compare two key members of KeyValue structures. +int key_value_compare_keys(const void *a, const void *b) { + return (int)((KeyValue*)a)->key - (int)((KeyValue*)b)->key; +} + +// Search an array of key value pairs for the item with the specified value. +KeyValue* find_item_by_value(const char * const value) { + unsigned int i; + for (i = 0; i < number_of_key_values; i++) { + if (strcmp(key_values[i].value, value) == 0) { + return &key_values[i]; + } + } + return NULL; +} + +// Sort an array of key value pairs by key. +void sort_items_by_key() { + qsort(key_values, number_of_key_values, sizeof(*key_values), + key_value_compare_keys); +} diff --git a/cmockery_0_1_2/src/example/key_value_test.c b/cmockery_0_1_2/src/example/key_value_test.c new file mode 100644 index 0000000..4f58e57 --- /dev/null +++ b/cmockery_0_1_2/src/example/key_value_test.c @@ -0,0 +1,80 @@ +/* + * 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <string.h> +#include <cmockery.h> + +/* This is duplicated here from the module setup_teardown.c to reduce the + * number of files used in this test. */ +typedef struct KeyValue { + unsigned int key; + const char* value; +} KeyValue; + +void set_key_values(KeyValue * const new_key_values, + const unsigned int new_number_of_key_values); +extern KeyValue* find_item_by_value(const char * const value); +extern void sort_items_by_key(); + +static KeyValue key_values[] = { + { 10, "this" }, + { 52, "test" }, + { 20, "a" }, + { 13, "is" }, +}; + +void create_key_values(void **state) { + KeyValue * const items = (KeyValue*)test_malloc(sizeof(key_values)); + memcpy(items, key_values, sizeof(key_values)); + *state = (void*)items; + set_key_values(items, sizeof(key_values) / sizeof(key_values[0])); +} + +void destroy_key_values(void **state) { + test_free(*state); + set_key_values(NULL, 0); +} + +void test_find_item_by_value(void **state) { + unsigned int i; + for (i = 0; i < sizeof(key_values) / sizeof(key_values[0]); i++) { + KeyValue * const found = find_item_by_value(key_values[i].value); + assert_true(found); + assert_int_equal(found->key, key_values[i].key); + assert_string_equal(found->value, key_values[i].value); + } +} + +void test_sort_items_by_key(void **state) { + unsigned int i; + KeyValue * const kv = *state; + sort_items_by_key(); + for (i = 1; i < sizeof(key_values) / sizeof(key_values[0]); i++) { + assert_true(kv[i - 1].key < kv[i].key); + } +} + +int main(int argc, char* argv[]) { + const UnitTest tests[] = { + unit_test_setup_teardown(test_find_item_by_value, create_key_values, + destroy_key_values), + unit_test_setup_teardown(test_sort_items_by_key, create_key_values, + destroy_key_values), + }; + return run_tests(tests); +} diff --git a/cmockery_0_1_2/src/example/product_database.c b/cmockery_0_1_2/src/example/product_database.c new file mode 100644 index 0000000..6c6ecf2 --- /dev/null +++ b/cmockery_0_1_2/src/example/product_database.c @@ -0,0 +1,22 @@ +/* + * 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 <database.h> + +// Connect to the database containing customer information. +DatabaseConnection* connect_to_product_database() { + return connect_to_database("products.abcd.org", 322); +} + diff --git a/cmockery_0_1_2/src/example/product_database_test.c b/cmockery_0_1_2/src/example/product_database_test.c new file mode 100644 index 0000000..d3c5109 --- /dev/null +++ b/cmockery_0_1_2/src/example/product_database_test.c @@ -0,0 +1,66 @@ +/* + * 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmockery.h> +#include <database.h> + +extern DatabaseConnection* connect_to_product_database(); + +/* Mock connect to database function. + * NOTE: This mock function is very general could be shared between tests + * that use the imaginary database.h module. */ +DatabaseConnection* connect_to_database(const char * const url, + const unsigned int port) { + check_expected(url); + check_expected(port); + return (DatabaseConnection*)mock(); +} + +void test_connect_to_product_database(void **state) { + expect_string(connect_to_database, url, "products.abcd.org"); + expect_value(connect_to_database, port, 322); + will_return(connect_to_database, 0xDA7ABA53); + assert_int_equal((int)connect_to_product_database(), 0xDA7ABA53); +} + +/* This test will fail since the expected URL is different to the URL that is + * passed to connect_to_database() by connect_to_product_database(). */ +void test_connect_to_product_database_bad_url(void **state) { + expect_string(connect_to_database, url, "products.abcd.com"); + expect_value(connect_to_database, port, 322); + will_return(connect_to_database, 0xDA7ABA53); + assert_int_equal((int)connect_to_product_database(), 0xDA7ABA53); +} + +/* This test will fail since the mock connect_to_database() will attempt to + * retrieve a value for the parameter port which isn't specified by this + * test function. */ +void test_connect_to_product_database_missing_parameter(void **state) { + expect_string(connect_to_database, url, "products.abcd.org"); + will_return(connect_to_database, 0xDA7ABA53); + assert_int_equal((int)connect_to_product_database(), 0xDA7ABA53); +} + +int main(int argc, char* argv[]) { + const UnitTest tests[] = { + unit_test(test_connect_to_product_database), + unit_test(test_connect_to_product_database_bad_url), + unit_test(test_connect_to_product_database_missing_parameter), + }; + return run_tests(tests); +} diff --git a/cmockery_0_1_2/src/example/run_tests.c b/cmockery_0_1_2/src/example/run_tests.c new file mode 100644 index 0000000..f6d7620 --- /dev/null +++ b/cmockery_0_1_2/src/example/run_tests.c @@ -0,0 +1,30 @@ +/* + * 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 <stdarg.h> +#include <stddef.h> +#include <setjmp.h> +#include <cmockery.h> + +// A test case that does nothing and succeeds. +void null_test_success(void **state) { +} + +int main(int argc, char* argv[]) { + const UnitTest tests[] = { + unit_test(null_test_success), + }; + return run_tests(tests); +} diff --git a/cmockery_0_1_2/src/google/cmockery.h b/cmockery_0_1_2/src/google/cmockery.h new file mode 100644 index 0000000..827d3c4 --- /dev/null +++ b/cmockery_0_1_2/src/google/cmockery.h @@ -0,0 +1,435 @@ +/* + * 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. + */ +#ifndef CMOCKERY_H_ +#define CMOCKERY_H_ +/* + * These headers or their equivalents should be included prior to including + * this header file. + * + * #include <stdarg.h> + * #include <stddef.h> + * #include <setjmp.h> + * + * This allows test applications to use custom definitions of C standard + * library functions and types. + */ + +// For those who are used to __func__ from gcc. +#ifndef __func__ +#define __func__ __FUNCTION__ +#endif + +// Retrieves a return value for the current function. +#define mock() _mock(__func__, __FILE__, __LINE__) + +/* Stores a value to be returned by the specified function later. + * The count parameter returns the number of times the value should be returned + * by mock(). If count is set to -1 the value will always be returned. + */ +#define will_return(function, value) \ + _will_return(#function, __FILE__, __LINE__, (void*)value, 1) +#define will_return_count(function, value, count) \ + _will_return(#function, __FILE__, __LINE__, (void*)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. + */ +#define expect_check(function, parameter, check_function, check_data) \ + _expect_check(#function, #parameter, __FILE__, __LINE__, check_function, \ + check_data) + +/* Add an event to check a parameter, using check_expected(), against a set of + * values. See will_return() for a description of the count parameter. + */ +#define expect_in_set(function, parameter, value_array) \ + expect_in_set_count(function, parameter, value_array, 1) +#define expect_in_set_count(function, parameter, value_array, count) \ + _expect_in_set(#function, #parameter, __FILE__, __LINE__, value_array, \ + sizeof(value_array) / sizeof(value_array[0]), count) +#define expect_not_in_set(function, parameter, value_array) \ + expect_not_in_set_count(function, parameter, value_array, 1) +#define expect_not_in_set_count(function, parameter, value_array, count) \ + _expect_not_in_set( \ + #function, #parameter, __FILE__, __LINE__, value_array, \ + sizeof(value_array) / sizeof(value_array[0]), count) + + +/* Add an event to check a parameter, using check_expected(), against a + * signed range. Where range is minimum <= value <= maximum. + * See will_return() for a description of the count parameter. + */ +#define expect_in_range(function, parameter, minimum, maximum) \ + expect_in_range_count(function, parameter, minimum, maximum, 1) +#define expect_in_range_count(function, parameter, minimum, maximum, count) \ + _expect_in_range(#function, #parameter, __FILE__, __LINE__, minimum, \ + maximum, count) + +/* Add an event to check a parameter, using check_expected(), against a + * signed range. Where range is value < minimum or value > maximum. + * See will_return() for a description of the count parameter. + */ +#define expect_not_in_range(function, parameter, minimum, maximum) \ + expect_not_in_range_count(function, parameter, minimum, maximum, 1) +#define expect_not_in_range_count(function, parameter, minimum, maximum, \ + count) \ + _expect_not_in_range(#function, #parameter, __FILE__, __LINE__, \ + minimum, maximum, count) + +/* Add an event to check whether a parameter, using check_expected(), is or + * isn't a value. See will_return() for a description of the count parameter. + */ +#define expect_value(function, parameter, value) \ + expect_value_count(function, parameter, value, 1) +#define expect_value_count(function, parameter, value, count) \ + _expect_value(#function, #parameter, __FILE__, __LINE__, (void*)value, \ + count) +#define expect_not_value(function, parameter, value) \ + expect_not_value_count(function, parameter, value, 1) +#define expect_not_value_count(function, parameter, value, count) \ + _expect_not_value(#function, #parameter, __FILE__, __LINE__, \ + (void*)value, count) + +/* Add an event to check whether a parameter, using check_expected(), + * is or isn't a string. See will_return() for a description of the count + * parameter. + */ +#define expect_string(function, parameter, string) \ + expect_string_count(function, parameter, string, 1) +#define expect_string_count(function, parameter, string, count) \ + _expect_string(#function, #parameter, __FILE__, __LINE__, (void*)string, \ + count) +#define expect_not_string(function, parameter, string) \ + expect_not_string_count(function, parameter, string, 1) +#define expect_not_string_count(function, parameter, string, count) \ + _expect_not_string(#function, #parameter, __FILE__, __LINE__, \ + (void*)string, count) + +/* Add an event to check whether a parameter, using check_expected() does or + * doesn't match an area of memory. See will_return() for a description of + * the count parameter. + */ +#define expect_memory(function, parameter, memory, size) \ + expect_memory_count(function, parameter, memory, size, 1) +#define expect_memory_count(function, parameter, memory, size, count) \ + _expect_memory(#function, #parameter, __FILE__, __LINE__, (void*)memory, \ + size, count) +#define expect_not_memory(function, parameter, memory, size) \ + expect_not_memory_count(function, parameter, memory, size, 1) +#define expect_not_memory_count(function, parameter, memory, size, count) \ + _expect_not_memory(#function, #parameter, __FILE__, __LINE__, \ + (void*)memory, size, count) + + +/* Add an event to allow any value for a parameter checked using + * check_expected(). See will_return() for a description of the count + * parameter. + */ +#define expect_any(function, parameter) \ + expect_any_count(function, parameter, 1) +#define expect_any_count(function, parameter, count) \ + _expect_any(#function, #parameter, __FILE__, __LINE__, count) + +/* Determine whether a function parameter is correct. This ensures the next + * value queued by one of the expect_*() macros matches the specified variable. + */ +#define check_expected(parameter) \ + _check_expected(__func__, #parameter, __FILE__, __LINE__, (void*)parameter) + +// Assert that the given expression is true. +#define assert_true(c) _assert_true((int)(c), #c, __FILE__, __LINE__) +// Assert that the given expression is false. +#define assert_false(c) _assert_true(!((int)(c)), #c, __FILE__, __LINE__) + +// Assert that the two given integers are equal, otherwise fail. +#define assert_int_equal(a, b) _assert_int_equal(a, b, __FILE__, __LINE__) +// Assert that the two given integers are not equal, otherwise fail. +#define assert_int_not_equal(a, b) \ + _assert_int_not_equal(a, b, __FILE__, __LINE__) + +// Assert that the two given strings are equal, otherwise fail. +#define assert_string_equal(a, b) \ + _assert_string_equal((const char*)a, (const char*)b, __FILE__, __LINE__) +// Assert that the two given strings are not equal, otherwise fail. +#define assert_string_not_equal(a, b) \ + _assert_string_not_equal((const char*)a, (const char*)b, __FILE__, \ + __LINE__) + +// Assert that the two given areas of memory are equal, otherwise fail. +#define assert_memory_equal(a, b, size) \ + _assert_memory_equal((const char*)a, (const char*)b, size, __FILE__, \ + __LINE__) +// Assert that the two given areas of memory are not equal, otherwise fail. +#define assert_memory_not_equal(a, b, size) \ + _assert_memory_not_equal((const char*)a, (const char*)b, size, __FILE__, \ + __LINE__) + +// Assert that the specified value is >= minimum and <= maximum. +#define assert_in_range(value, minimum, maximum) \ + _assert_in_range((int)value, (int)minimum, (int)maximum, __FILE__, \ + __LINE__) +// Assert that the specified value is < minumum or > maximum +#define assert_not_in_range(value, minimum, maximum) \ + _assert_not_in_range((int)value, (int)minimum, (int)maximum, __FILE__, \ + __LINE__) + +// Assert that the specified value is within a set. +#define assert_in_set(value, values, number_of_values) \ + _assert_in_set(value, values, number_of_values, __FILE__, __LINE__) +// Assert that the specified value is not within a set. +#define assert_not_in_set(value, values, number_of_values) \ + _assert_not_in_set(value, values, number_of_values, __FILE__, __LINE__) + + +// Forces the test to fail immediately and quit. +#define fail() _fail(__FILE__, __LINE__) + +// Generic method to kick off testing +#define run_test(f) _run_test(#f, f, NULL, UNIT_TEST_FUNCTION_TYPE_TEST, NULL) + +// Initializes a UnitTest structure. +#define unit_test(f) { #f, f, UNIT_TEST_FUNCTION_TYPE_TEST } +#define unit_test_setup(test, setup) \ + { #test "_" #setup, setup, UNIT_TEST_FUNCTION_TYPE_SETUP } +#define unit_test_teardown(test, teardown) \ + { #test "_" #teardown, teardown, UNIT_TEST_FUNCTION_TYPE_TEARDOWN } + +/* Initialize an array of UnitTest structures with a setup function for a test + * and a teardown function. Either setup or teardown can be NULL. + */ +#define unit_test_setup_teardown(test, setup, teardown) \ + unit_test_setup(test, setup), \ + unit_test(test), \ + unit_test_teardown(test, teardown) + +/* + * Run tests specified by an array of UnitTest structures. The following + * example illustrates this macro's use with the unit_test macro. + * + * void Test0(); + * void Test1(); + * + * int main(int argc, char* argv[]) { + * const UnitTest tests[] = { + * unit_test(Test0); + * unit_test(Test1); + * }; + * return run_tests(tests); + * } + */ +#define run_tests(tests) _run_tests(tests, sizeof(tests) / sizeof(tests)[0]) + +// Dynamic allocators +#define test_malloc(size) _test_malloc(size, __FILE__, __LINE__) +#define test_calloc(num, size) _test_calloc(num, size, __FILE__, __LINE__) +#define test_free(ptr) _test_free(ptr, __FILE__, __LINE__) + +// Redirect malloc, calloc and free to the unit test allocators. +#if UNIT_TESTING +#define malloc test_malloc +#define calloc test_calloc +#define free test_free +#endif // UNIT_TESTING + +/* + * Ensure mock_assert() is called. If mock_assert() is called the assert + * expression string is returned. + * For example: + * + * #define assert mock_assert + * + * void showmessage(const char *message) { + * assert(message); + * } + * + * int main(int argc, const char* argv[]) { + * expect_assert_failure(show_message(NULL)); + * printf("succeeded\n"); + * return 0; + * } + */ +#define expect_assert_failure(function_call) \ + { \ + const char* expression = (const char*)setjmp(global_expect_assert_env); \ + global_expecting_assert = 1; \ + if (expression) { \ + print_message("Expected assertion %s occurred\n", expression); \ + global_expecting_assert = 0; \ + } else { \ + function_call ; \ + global_expecting_assert = 0; \ + print_error("Expected assert in %s\n", #function_call); \ + _fail(__FILE__, __LINE__); \ + } \ + } + +// Function prototype for setup, test and teardown functions. +typedef void (*UnitTestFunction)(void **state); + +// Function that determines whether a function parameter value is correct. +typedef int (*CheckParameterValue)(const void *value, void *check_value_data); + +// Type of the unit test function. +typedef enum UnitTestFunctionType { + UNIT_TEST_FUNCTION_TYPE_TEST = 0, + UNIT_TEST_FUNCTION_TYPE_SETUP, + UNIT_TEST_FUNCTION_TYPE_TEARDOWN, +} UnitTestFunctionType; + +/* Stores a unit test function with its name and type. + * NOTE: Every setup function must be paired with a teardown function. It's + * possible to specify NULL function pointers. + */ +typedef struct UnitTest { + const char* name; + UnitTestFunction function; + UnitTestFunctionType function_type; +} UnitTest; + + +// Location within some source code. +typedef struct SourceLocation { + const char* file; + int line; +} SourceLocation; + +// Event that's called to check a parameter value. +typedef struct CheckParameterEvent { + SourceLocation location; + const char *parameter_name; + CheckParameterValue check_value; + void *check_value_data; +} CheckParameterEvent; + +// Used by expect_assert_failure() and mock_assert(). +extern int global_expecting_assert; +extern jmp_buf global_expect_assert_env; + +// Retrieves a value for the given function, as set by "will_return". +void* _mock(const char * const function, const char* const file, + const int line); + +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); + +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); +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); + +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); +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); +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); +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); +void _expect_string( + const char* const function, const char* const parameter, + const char* const file, const int line, const char* string, + const int count); +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); +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); +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); +void _expect_any( + const char* const function, const char* const parameter, + const char* const file, const int line, const int count); + +void _check_expected( + const char * const function_name, const char * const parameter_name, + const char* file, const int line, const void* value); + +// Can be used to replace assert in tested code so that in conjuction with +// check_assert() it's possible to determine whether an assert condition has +// failed without stopping a test. +void mock_assert(const int result, const char* const expression, + const char * const file, const int line); + +void _will_return(const char * const function_name, const char * const file, + const int line, const void* const value, const int count); +void _assert_true(const int result, const char* const expression, + const char * const file, const int line); +void _assert_int_equal(const int a, const int b, const char * const file, + const int line); +void _assert_int_not_equal(const int a, const int b, const char * const file, + const int line); +void _assert_string_equal(const char * const a, const char * const b, + const char * const file, const int line); +void _assert_string_not_equal(const char * const a, const char * const b, + const char *file, const int line); +void _assert_memory_equal(const void * const a, const void * const b, + const size_t size, const char* const file, + const int 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); +void _assert_in_range(const int value, const int minimum, const int maximum, + const char* const file, const int line); +void _assert_not_in_range(const int value, const int minimum, + const int maximum, const char* const file, + const int 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); +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); + +void* _test_malloc(const size_t size, const char* file, const int line); +void* _test_calloc(const size_t number_of_elements, const size_t size, + const char* file, const int line); +void _test_free(void* const ptr, const char* file, const int line); + +void _fail(const char * const file, const int line); +int _run_test( + const char * const function_name, const UnitTestFunction Function, + void ** const state, const UnitTestFunctionType function_type, + const void* const heap_check_point); +int _run_tests(const UnitTest * const tests, const size_t number_of_tests); + +// Standard output and error print methods. +void print_message(const char* const format, ...); +void print_error(const char* const format, ...); +void vprint_message(const char* const format, va_list args); +void vprint_error(const char* const format, va_list args); + +#endif // CMOCKERY_H_ |