| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| */ |
| |
| /* |
| * 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. |
| */ |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| #ifdef HAVE_MALLOC_H |
| #include <malloc.h> |
| #endif |
| #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 |
| |
| /* Backwards compatibility with headers shipped with Visual Studio 2005 and |
| * earlier. */ |
| #ifdef _WIN32 |
| WINBASEAPI BOOL WINAPI IsDebuggerPresent(VOID); |
| #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" |
| #define OUTPUT_PADDING " " |
| |
| |
| #define COLOR_RESET 0 |
| |
| #define COLOR_TERM_RED "\e[0;31m" |
| #define COLOR_TERM_GREEN "\e[0;32m" |
| #define COLOR_TERM_RESET "\e[0m" |
| #define COLOR_NOTERM_RED "" |
| #define COLOR_NOTERM_GREEN "" |
| #define COLOR_NOTERM_RESET "" |
| |
| // Calculates the number of elements in an array. |
| #define ARRAY_LENGTH(x) (sizeof(x) / sizeof((x)[0])) |
| |
| // Declare and initialize the pointer member of ValuePointer variable name |
| // with ptr. |
| #define declare_initialize_value_pointer_pointer(name, ptr) \ |
| ValuePointer name ; \ |
| name.value = 0; \ |
| name.pointer = (void*)(ptr) |
| |
| // Declare and initialize the value member of ValuePointer variable name |
| // with val. |
| #define declare_initialize_value_pointer_value(name, val) \ |
| ValuePointer name ; \ |
| name.value = val |
| |
| // Cast a LargestIntegralType to pointer_type via a ValuePointer. |
| #define cast_largest_integral_type_to_pointer( \ |
| pointer_type, largest_integral_type) \ |
| ((pointer_type)((ValuePointer*)&(largest_integral_type))->pointer) |
| |
| // Used to cast LargetIntegralType to void* and vice versa. |
| typedef union ValuePointer { |
| LargestIntegralType value; |
| void *pointer; |
| } ValuePointer; |
| |
| // 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; |
| LargestIntegralType value; |
| |
| SideeffectCallbackValue sideffect; |
| void* sideeffect_data; |
| } 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; |
| LargestIntegralType minimum; |
| LargestIntegralType maximum; |
| } CheckIntegerRange; |
| |
| // Structure used to check whether an integer value is in a set. |
| typedef struct CheckIntegerSet { |
| CheckParameterEvent event; |
| const LargestIntegralType *set; |
| size_t size_of_set; |
| } CheckIntegerSet; |
| |
| /* Used to check whether a parameter matches the area of memory referenced by |
| * this structure. */ |
| typedef struct CheckMemoryData { |
| CheckParameterEvent event; |
| const void *memory; |
| size_t size; |
| } CheckMemoryData; |
| |
| typedef struct AssignMemoryData |
| { |
| AssignParameterEvent event; |
| const void* memory; |
| size_t value_size; |
| } AssignMemoryData; |
| |
| 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, int log_error, 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); |
| static const char* get_color(int color_code); |
| static void check_color_env(void); |
| static int _assign_memory_for_outbuffer(const LargestIntegralType value, |
| const LargestIntegralType assign_value_data); |
| |
| // 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; |
| |
| /* Keeps a map of the values that are assigned to parameters in their |
| * mocked interfaces. |
| */ |
| static ListNode global_function_parameter_assignment_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; |
| |
| static int abort_for_missing_check = 0; |
| static int generate_suppression = 0; |
| static int run_disabled_tests = 0; |
| static int use_color = 0; |
| |
| #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); |
| list_initialize(&global_function_parameter_assignment_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, |
| OUTPUT_PADDING "%s() has remaining non-returned values.\n", 1)) { |
| error_occurred = 1; |
| } |
| |
| remove_always_return_values(&global_function_parameter_assignment_map_head, 1); |
| if (check_for_leftover_values( |
| &global_function_parameter_assignment_map_head, |
| OUTPUT_PADDING "%s() has remaining non-assigned out-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, |
| OUTPUT_PADDING "%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, int log_error, 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, |
| log_error, output); |
| } |
| if (list_empty(child_list)) { |
| list_remove_free(target_node, free_symbol_map_value, (void*)0); |
| } |
| return return_value; |
| } else if (log_error) { |
| print_error(OUTPUT_PADDING "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(OUTPUT_PADDING " 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(OUTPUT_PADDING " " 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. |
| LargestIntegralType _mock(const char * const function, const char* const file, |
| const int line) { |
| void *result; |
| const int rc = get_symbol_value(&global_function_result_map_head, |
| &function, 1, 1, &result); |
| if (rc) { |
| SymbolValue * const symbol = (SymbolValue*)result; |
| const LargestIntegralType value = symbol->value; |
| global_last_mock_value_location = symbol->location; |
| |
| |
| if (symbol->sideffect) { |
| symbol->sideffect(symbol->sideeffect_data); |
| } |
| |
| if (rc == 1) { |
| free(symbol); |
| } |
| return value; |
| } else if (generate_suppression) { |
| /* We could generate a better line here, if we read the JSON information. But that is a different "story" */ |
| print_message(COLOR_DEFAULT, OUTPUT_PADDING "Suppression for " SOURCE_LOCATION_FORMAT "\n", file, line); |
| print_message(COLOR_DEFAULT, OUTPUT_PADDING "will_return(%s, /*insert value */);\n" OUTPUT_PADDING " will_be_called(%s);\n\n", function, function); |
| } else { |
| print_error(OUTPUT_PADDING "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(OUTPUT_PADDING "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(OUTPUT_PADDING "There were no previously returned mock values for " |
| "this test.\n"); |
| } |
| if (abort_for_missing_check) { |
| abort(); |
| } else { |
| exit_test(1); |
| } |
| } |
| return 0; |
| } |
| |
| |
| // Add a return value for the specified mock function name. |
| void _will_return(const char * const function_name, const char * const file, |
| const int line, const LargestIntegralType value, |
| SideeffectCallbackValue sideeffect, void* sideeffect_data, |
| const int count) { |
| SymbolValue * const return_value = malloc(sizeof(*return_value)); |
| assert_true(count > 0 || count == -1); |
| return_value->value = value; |
| return_value->sideffect = sideeffect; |
| return_value->sideeffect_data = sideeffect_data; |
| set_source_location(&return_value->location, file, line); |
| add_symbol_value(&global_function_result_map_head, &function_name, 1, |
| return_value, count); |
| } |
| |
| void _will_be_called(const char * const function_name, const char * const file, |
| const int line, |
| SideeffectCallbackValue sideeffect, void* sideeffect_data, |
| const int count) { |
| SymbolValue * const return_value = malloc(sizeof(*return_value)); |
| assert_true(count > 0 || count == -1); |
| return_value->value = 0; |
| return_value->sideffect = sideeffect; |
| return_value->sideeffect_data = sideeffect_data; |
| set_source_location(&return_value->location, file, line); |
| add_symbol_value(&global_function_result_map_head, &function_name, 1, |
| return_value, count); |
| } |
| |
| /* Add a custom parameter checking function. If the event parameter is NULL |
| * the event structure is allocated internally by this function. If event |
| * parameter is provided it must be allocated on the heap and doesn't need to |
| * be deallocated by the caller. |
| */ |
| void _expect_check( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const CheckParameterValue check_function, |
| const LargestIntegralType check_data, |
| CheckParameterEvent * const event, const int count) { |
| CheckParameterEvent * const check = |
| event ? event : 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 LargestIntegralType left, |
| const LargestIntegralType right) { |
| const int equal = left == right; |
| if (!equal) { |
| print_error(OUTPUT_PADDING LargestIntegralTypePrintfFormat " != " |
| LargestIntegralTypePrintfFormat "\n", left, right); |
| } |
| return equal; |
| } |
| |
| /* Returns 1 if the specified values are not equal. If the values are equal |
| * an error is displayed and 0 is returned. */ |
| static int values_not_equal_display_error(const LargestIntegralType left, |
| const LargestIntegralType right) { |
| const int not_equal = left != right; |
| if (!not_equal) { |
| print_error(OUTPUT_PADDING LargestIntegralTypePrintfFormat " == " |
| LargestIntegralTypePrintfFormat "\n", left, right); |
| } |
| return not_equal; |
| } |
| |
| |
| /* Determine whether value is contained within check_integer_set. |
| * If invert is 0 and the value is in the set 1 is returned, otherwise 0 is |
| * returned and an error is displayed. If invert is 1 and the value is not |
| * in the set 1 is returned, otherwise 0 is returned and an error is |
| * displayed. */ |
| static int value_in_set_display_error( |
| const LargestIntegralType value, |
| const CheckIntegerSet * const check_integer_set, const int invert) { |
| int succeeded = invert; |
| assert_true(check_integer_set); |
| { |
| const LargestIntegralType * const set = check_integer_set->set; |
| const size_t size_of_set = check_integer_set->size_of_set; |
| size_t i; |
| for (i = 0; i < size_of_set; i++) { |
| if (set[i] == value) { |
| // If invert = 0 and item is found, succeeded = 1. |
| // If invert = 1 and item is found, succeeded = 0. |
| succeeded = !succeeded; |
| break; |
| } |
| } |
| if (succeeded) { |
| return 1; |
| } |
| print_error(OUTPUT_PADDING "%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 LargestIntegralType value, const LargestIntegralType range_min, |
| const LargestIntegralType range_max) { |
| if (value >= range_min && value <= range_max) { |
| return 1; |
| } |
| print_error(OUTPUT_PADDING "%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 LargestIntegralType value, const LargestIntegralType range_min, |
| const LargestIntegralType range_max) { |
| if (value < range_min || value > range_max) { |
| return 1; |
| } |
| print_error(OUTPUT_PADDING "%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(OUTPUT_PADDING "\"%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(OUTPUT_PADDING "\"%s\" == \"%s\"\n", left, right); |
| return 0; |
| } |
| |
| |
| /* Determine whether the specified areas of memory are equal. If they're equal |
| * 1 is returned otherwise an error is displayed and 0 is returned. */ |
| static int memory_equal_display_error(const char* const a, const char* const b, |
| const size_t size) { |
| 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(OUTPUT_PADDING "difference at offset %d 0x%02x 0x%02x\n", i, l, r); |
| differences ++; |
| } |
| } |
| if (differences) { |
| print_error(OUTPUT_PADDING "%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* const a, const char* const 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) { |
| same ++; |
| } |
| } |
| if (same == size) { |
| print_error(OUTPUT_PADDING "%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 LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| return value_in_set_display_error(value, |
| cast_largest_integral_type_to_pointer(CheckIntegerSet*, |
| check_value_data), 0); |
| } |
| |
| |
| // CheckParameterValue callback to check whether a value isn't within a set. |
| static int check_not_in_set(const LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| return value_in_set_display_error(value, |
| cast_largest_integral_type_to_pointer(CheckIntegerSet*, |
| check_value_data), 1); |
| } |
| |
| |
| /* Create the callback data for check_in_set() or check_not_in_set() and |
| * register a check event. */ |
| static void expect_set( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const LargestIntegralType values[], const size_t number_of_values, |
| const CheckParameterValue check_function, const int count) { |
| CheckIntegerSet * const check_integer_set = |
| malloc(sizeof(*check_integer_set) + |
| (sizeof(values[0]) * number_of_values)); |
| LargestIntegralType * const set = (LargestIntegralType*)( |
| check_integer_set + 1); |
| declare_initialize_value_pointer_pointer(check_data, check_integer_set); |
| assert_true(values); |
| assert_true(number_of_values); |
| memcpy(set, values, number_of_values * sizeof(values[0])); |
| check_integer_set->set = set; |
| _expect_check( |
| function, parameter, file, line, check_function, |
| check_data.value, &check_integer_set->event, count); |
| } |
| |
| |
| // Add an event to check whether a value is in a set. |
| void _expect_in_set( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const LargestIntegralType values[], const size_t number_of_values, |
| const int count) { |
| expect_set(function, parameter, file, line, values, number_of_values, |
| check_in_set, count); |
| } |
| |
| |
| // Add an event to check whether a value isn't in a set. |
| void _expect_not_in_set( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const LargestIntegralType values[], const size_t number_of_values, |
| const int count) { |
| expect_set(function, parameter, file, line, values, number_of_values, |
| check_not_in_set, count); |
| } |
| |
| |
| // CheckParameterValue callback to check whether a value is within a range. |
| static int check_in_range(const LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| CheckIntegerRange * const check_integer_range = |
| cast_largest_integral_type_to_pointer(CheckIntegerRange*, |
| check_value_data); |
| assert_true(check_integer_range); |
| return integer_in_range_display_error(value, check_integer_range->minimum, |
| check_integer_range->maximum); |
| } |
| |
| |
| // CheckParameterValue callback to check whether a value is not within a range. |
| static int check_not_in_range(const LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| CheckIntegerRange * const check_integer_range = |
| cast_largest_integral_type_to_pointer(CheckIntegerRange*, |
| check_value_data); |
| assert_true(check_integer_range); |
| return integer_not_in_range_display_error( |
| value, check_integer_range->minimum, check_integer_range->maximum); |
| } |
| |
| |
| /* Create the callback data for check_in_range() or check_not_in_range() and |
| * register a check event. */ |
| static void expect_range( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const LargestIntegralType minimum, const LargestIntegralType maximum, |
| const CheckParameterValue check_function, const int count) { |
| CheckIntegerRange * const check_integer_range = |
| malloc(sizeof(*check_integer_range)); |
| declare_initialize_value_pointer_pointer(check_data, check_integer_range); |
| check_integer_range->minimum = minimum; |
| check_integer_range->maximum = maximum; |
| _expect_check(function, parameter, file, line, check_function, |
| check_data.value, &check_integer_range->event, count); |
| } |
| |
| |
| // Add an event to determine whether a parameter is within a range. |
| void _expect_in_range( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const LargestIntegralType minimum, const LargestIntegralType maximum, |
| const int count) { |
| expect_range(function, parameter, file, line, minimum, maximum, |
| check_in_range, count); |
| } |
| |
| |
| // Add an event to determine whether a parameter is not within a range. |
| void _expect_not_in_range( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const LargestIntegralType minimum, const LargestIntegralType maximum, |
| const int count) { |
| expect_range(function, parameter, file, line, minimum, maximum, |
| check_not_in_range, count); |
| } |
| |
| |
| /* CheckParameterValue callback to check whether a value is equal to an |
| * expected value. */ |
| static int check_value(const LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| return values_equal_display_error(value, check_value_data); |
| } |
| |
| |
| // Add an event to check a parameter equals an expected value. |
| void _expect_value( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const LargestIntegralType value, const int count) { |
| _expect_check(function, parameter, file, line, check_value, value, NULL, |
| count); |
| } |
| |
| /* CheckParameterValue callback to check whether a value is not equal to an |
| * expected value. */ |
| static int check_not_value(const LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| return values_not_equal_display_error(value, check_value_data); |
| } |
| |
| |
| // Add an event to check a parameter is not equal to an expected value. |
| void _expect_not_value( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const LargestIntegralType value, const int count) { |
| _expect_check(function, parameter, file, line, check_not_value, value, |
| NULL, count); |
| } |
| |
| |
| // CheckParameterValue callback to check whether a parameter equals a string. |
| static int check_string(const LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| return string_equal_display_error( |
| cast_largest_integral_type_to_pointer(char*, value), |
| cast_largest_integral_type_to_pointer(char*, check_value_data)); |
| } |
| |
| |
| // Add an event to check whether a parameter is equal to a string. |
| void _expect_string( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, const char* string, |
| const int count) { |
| declare_initialize_value_pointer_pointer(string_pointer, (char*)string); |
| _expect_check(function, parameter, file, line, check_string, |
| string_pointer.value, NULL, count); |
| } |
| |
| |
| /* CheckParameterValue callback to check whether a parameter is not equals to |
| * a string. */ |
| static int check_not_string(const LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| return string_not_equal_display_error( |
| cast_largest_integral_type_to_pointer(char*, value), |
| cast_largest_integral_type_to_pointer(char*, check_value_data)); |
| } |
| |
| |
| // Add an event to check whether a parameter is not equal to a string. |
| void _expect_not_string( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, const char* string, |
| const int count) { |
| declare_initialize_value_pointer_pointer(string_pointer, (char*)string); |
| _expect_check(function, parameter, file, line, check_not_string, |
| string_pointer.value, NULL, count); |
| } |
| |
| /* CheckParameterValue callback to check whether a parameter equals an area of |
| * memory. */ |
| static int check_memory(const LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| CheckMemoryData * const check = cast_largest_integral_type_to_pointer( |
| CheckMemoryData*, check_value_data); |
| assert_true(check); |
| return memory_equal_display_error( |
| cast_largest_integral_type_to_pointer(void*, 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); |
| declare_initialize_value_pointer_pointer(check_data_pointer, check_data); |
| 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_pointer.value, &check_data->event, count); |
| } |
| |
| |
| // Add an event to check whether a parameter matches an area of memory. |
| void _expect_memory( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, const void* const memory, |
| const size_t size, const int count) { |
| expect_memory_setup(function, parameter, file, line, memory, size, |
| check_memory, count); |
| } |
| |
| |
| /* CheckParameterValue callback to check whether a parameter is not equal to |
| * an area of memory. */ |
| static int check_not_memory(const LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| CheckMemoryData * const check = cast_largest_integral_type_to_pointer( |
| CheckMemoryData*, check_value_data); |
| assert_true(check); |
| return memory_not_equal_display_error( |
| cast_largest_integral_type_to_pointer(void*, 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 LargestIntegralType value, |
| const LargestIntegralType check_value_data) { |
| return 1; |
| } |
| |
| |
| // Add an event to allow any value for a parameter. |
| void _expect_any( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, const int count) { |
| _expect_check(function, parameter, file, line, check_any, 0, NULL, |
| count); |
| } |
| |
| void _will_assign( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const AssignParameterValue assign_function, |
| const LargestIntegralType assign_data, |
| AssignParameterEvent * const event, const int count) { |
| AssignParameterEvent * const assign = |
| event ? event : malloc(sizeof(*assign)); |
| const char* symbols[] = {function, parameter}; |
| assign->parameter_name = parameter; |
| assign->assign_value = assign_function; |
| assign->assign_value_data = assign_data; |
| set_source_location(&assign->location, file, line); |
| add_symbol_value(&global_function_parameter_assignment_map_head, |
| symbols, 2, assign, count); |
| } |
| |
| static int _assign_memory(const LargestIntegralType value, |
| const LargestIntegralType assign_value_data) { |
| AssignMemoryData* memory_value = (AssignMemoryData*)assign_value_data; |
| |
| void* v = (void*)value; |
| memcpy(v, memory_value->memory, memory_value->value_size); |
| return 1; |
| } |
| |
| static int _assign_ignore(const LargestIntegralType value, |
| const LargestIntegralType assign_value_data) { |
| return 1; |
| } |
| |
| void _will_not_assign( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, int count) |
| { |
| _will_assign(function, parameter, file, line, _assign_ignore, |
| 0, NULL, count); |
| } |
| |
| void _will_assign_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) |
| { |
| struct AssignMemoryData * assign_data = malloc(sizeof(*assign_data) + size); |
| void * const mem = (void*)(assign_data + 1); |
| declare_initialize_value_pointer_pointer(assign_data_pointer, assign_data); |
| assert_true(memory); |
| assert_true(size); |
| memcpy(mem, memory, size); |
| assign_data->memory = mem; |
| assign_data->value_size = size; |
| |
| _will_assign(function, parameter, file, line, _assign_memory, |
| assign_data_pointer.value, &assign_data->event, count); |
| } |
| |
| /* |
| * A callback used for the case where the mock function needs to set |
| * an output function parameter which is a pointer to a buffer - |
| * for example: char** |
| * compare it to assign_memory() to see the difference. |
| * For assign_memory(), the function output parameter is just a buffer |
| */ |
| static int _assign_memory_for_outbuffer(const LargestIntegralType value, |
| const LargestIntegralType assign_value_data) { |
| AssignMemoryData* memory_value = (AssignMemoryData*)assign_value_data; |
| |
| void* v = (void*)value; |
| void *buf = malloc(memory_value->value_size); |
| memcpy(buf, memory_value->memory, memory_value->value_size); |
| *((char**)v) = buf; |
| |
| return 1; |
| } |
| |
| void _will_assign_memory_for_outbuffer( |
| 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) |
| { |
| struct AssignMemoryData * assign_data = malloc(sizeof(*assign_data) + size); |
| void * const mem = (void*)(assign_data + 1); |
| declare_initialize_value_pointer_pointer(assign_data_pointer, assign_data); |
| assert_true(memory); |
| assert_true(size); |
| memcpy(mem, memory, size); |
| assign_data->memory = mem; |
| assign_data->value_size = size; |
| |
| _will_assign(function, parameter, file, line, _assign_memory_for_outbuffer, |
| assign_data_pointer.value, &assign_data->event, count); |
| } |
| |
| void _will_assign_string( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const char* const str, const int count) { |
| _will_assign_memory_for_outbuffer(function, parameter, file, line, str, strlen(str)+1, count); |
| } |
| |
| void _will_assign_value( |
| const char* const function, const char* const parameter, |
| const char* const file, const int line, |
| const LargestIntegralType value, size_t size, const int count) |
| { |
| _will_assign_memory(function, parameter, file, line, &value, size, count); |
| } |
| |
| void _check_expected( |
| const char * const function_name, const char * const parameter_name, |
| const char* file, const int line, const LargestIntegralType value) { |
| void *result; |
| const char* symbols[] = {function_name, parameter_name}; |
| const int rc = get_symbol_value(&global_function_parameter_map_head, |
| symbols, 2, 1, &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(OUTPUT_PADDING "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 if (generate_suppression) { |
| print_message(COLOR_DEFAULT, OUTPUT_PADDING "Suppression for " SOURCE_LOCATION_FORMAT "\n", file, line); |
| print_message(COLOR_DEFAULT, OUTPUT_PADDING "expect_any(%s, %s);\n", function_name, parameter_name); |
| print_message(COLOR_DEFAULT, OUTPUT_PADDING "expect_value(%s, %s, %d);\n\n", function_name, parameter_name, value); |
| } else { |
| print_error(OUTPUT_PADDING "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(OUTPUT_PADDING "Previously declared parameter value was declared at " |
| SOURCE_LOCATION_FORMAT "\n", |
| global_last_parameter_location.file, |
| global_last_parameter_location.line); |
| } else { |
| print_error(OUTPUT_PADDING "There were no previously declared parameter values " |
| "for this test.\n"); |
| } |
| if (abort_for_missing_check) { |
| abort(); |
| } else { |
| exit_test(1); |
| } |
| } |
| } |
| |
| void _optional_assignment( |
| const char * const function_name, const char * const parameter_name, |
| const char* file, const int line, const LargestIntegralType value) { |
| void *result; |
| const char* symbols[] = {function_name, parameter_name}; |
| const int rc = get_symbol_value(&global_function_parameter_assignment_map_head, |
| symbols, 2, 0, &result); |
| if (rc) { |
| AssignParameterEvent * const assign = (AssignParameterEvent*)result; |
| int assign_succeeded; |
| global_last_parameter_location = assign->location; |
| |
| assign_succeeded = assign->assign_value(value, assign->assign_value_data); |
| if (rc == 1) { |
| free(assign); |
| } |
| if (!assign_succeeded) { |
| print_error(OUTPUT_PADDING "ERROR: Assignment 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); |
| } |
| } |
| } |
| |
| // 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(OUTPUT_PADDING "ASSERT: %s\n", expression); |
| _fail(file, line); |
| } |
| } |
| } |
| |
| |
| void _assert_true(const LargestIntegralType result, |
| const char * const expression, |
| const char * const file, const int line) { |
| if (!result) { |
| print_error(OUTPUT_PADDING "%s\n", expression); |
| _fail(file, line); |
| } |
| } |
| |
| void _assert_int_equal( |
| const LargestIntegralType a, const LargestIntegralType b, |
| const char * const file, const int line) { |
| if (!values_equal_display_error(a, b)) { |
| _fail(file, line); |
| } |
| } |
| |
| |
| void _assert_int_not_equal( |
| const LargestIntegralType a, const LargestIntegralType b, |
| const char * const file, const int line) { |
| if (!values_not_equal_display_error(a, b)) { |
| _fail(file, line); |
| } |
| } |
| |
| |
| void _assert_string_equal(const char * const a, const char * const b, |
| const char * const file, const int line) { |
| if (!string_equal_display_error(a, b)) { |
| _fail(file, line); |
| } |
| } |
| |
| |
| void _assert_string_not_equal(const char * const a, const char * const b, |
| const char *file, const int line) { |
| if (!string_not_equal_display_error(a, b)) { |
| _fail(file, line); |
| } |
| } |
| |
| |
| void _assert_memory_equal(const void * const a, const void * const b, |
| const size_t size, const char* const file, |
| const int line) { |
| if (!memory_equal_display_error((const char*)a, (const char*)b, size)) { |
| _fail(file, line); |
| } |
| } |
| |
| |
| void _assert_memory_not_equal(const void * const a, const void * const b, |
| const size_t size, const char* const file, |
| const int line) { |
| if (!memory_not_equal_display_error((const char*)a, (const char*)b, |
| size)) { |
| _fail(file, line); |
| } |
| } |
| |
| |
| void _assert_in_range( |
| const LargestIntegralType value, const LargestIntegralType minimum, |
| const LargestIntegralType maximum, const char* const file, |
| const int line) { |
| if (!integer_in_range_display_error(value, minimum, maximum)) { |
| _fail(file, line); |
| } |
| } |
| |
| void _assert_not_in_range( |
| const LargestIntegralType value, const LargestIntegralType minimum, |
| const LargestIntegralType maximum, const char* const file, |
| const int line) { |
| if (!integer_not_in_range_display_error(value, minimum, maximum)) { |
| _fail(file, line); |
| } |
| } |
| |
| void _assert_in_set(const LargestIntegralType value, |
| const LargestIntegralType values[], |
| const size_t number_of_values, const char* const file, |
| const int line) { |
| CheckIntegerSet check_integer_set; |
| check_integer_set.set = values; |
| check_integer_set.size_of_set = number_of_values; |
| if (!value_in_set_display_error(value, &check_integer_set, 0)) { |
| _fail(file, line); |
| } |
| } |
| |
| void _assert_not_in_set(const LargestIntegralType value, |
| const LargestIntegralType values[], |
| const size_t number_of_values, const char* const file, |
| const int line) { |
| CheckIntegerSet check_integer_set; |
| check_integer_set.set = values; |
| check_integer_set.size_of_set = number_of_values; |
| if (!value_in_set_display_error(value, &check_integer_set, 1)) { |
| _fail(file, line); |
| } |
| } |
| |
| |
| // Get the list of allocated blocks. |
| static ListNode* get_allocated_blocks_list() { |
| // 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( |
| OUTPUT_PADDING "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(OUTPUT_PADDING "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(OUTPUT_PADDING "ERROR: " SOURCE_LOCATION_FORMAT " Failure!\n", file, line); |
| exit_test(1); |
| } |
| |
| |
| #ifndef _WIN32 |
| static void exception_handler(int sig) { |
| print_error(OUTPUT_PADDING "%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(OUTPUT_PADDING "%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(int color_code, const char* const format, va_list args) { |
| char buffer[1024]; |
| vsnprintf(buffer, sizeof(buffer), format, args); |
| printf("%s%s%s", get_color(color_code), buffer, get_color(COLOR_RESET)); |
| #ifdef _WIN32 |
| OutputDebugString(buffer); |
| #endif // _WIN32 |
| } |
| |
| |
| void vprint_error(const char* const format, va_list args) { |
| char buffer[1024]; |
| vsnprintf(buffer, sizeof(buffer), format, args); |
| fprintf(stderr, "%s%s%s", get_color(COLOR_RED), buffer, get_color(COLOR_RESET)); |
| #ifdef _WIN32 |
| OutputDebugString(buffer); |
| #endif // _WIN32 |
| } |
| |
| |
| void print_message(int color_code, const char* const format, ...) { |
| va_list args; |
| va_start(args, format); |
| vprint_message(color_code, 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(COLOR_DEFAULT, "[ RUN ] %s\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(COLOR_GREEN, "[ OK ] %s\n", function_name); |
| } |
| rc = 0; |
| } else { |
| global_running_test = 0; |
| print_message(COLOR_RED, "[ FAILED ] %s\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) { |
| |
| check_color_env(); |
| |
| // 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; |
| // Make sure LargestIntegralType is at least the size of a pointer. |
| assert_true(sizeof(LargestIntegralType) >= sizeof(void*)); |
| |
| while (current_test < number_of_tests) { |
| const ListNode *test_check_point = NULL; |
| TestState *current_TestState; |
| const UnitTest * const test = &tests[current_test++]; |
| if (!test->function) { |
| continue; |
| } |
| |
| switch (test->function_type) { |
| case UNIT_TEST_FUNCTION_TYPE_TEST: |
| 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; |
| } |
| } |
| } |
| |
| print_message(COLOR_GREEN, "[=============] %d tests ran\n", tests_executed); |
| print_message(COLOR_GREEN, "[ PASSED ] %d tests\n", tests_executed - total_failed); |
| |
| if (total_failed) { |
| size_t i; |
| print_message(COLOR_RED, "[ FAILED ] %d tests, listed below\n", total_failed); |
| for (i = 0; i < total_failed; i++) { |
| print_message(COLOR_RED, "[ FAILED ] %s \n", failed_names[i]); |
| } |
| } |
| |
| 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; |
| } |
| |
| void cmockery_parse_arguments(int argc, char** argv) |
| { |
| int i; |
| for (i = 0; i < argc; i++) { |
| if (strcmp(argv[i], "--cmockery_abort_at_missing_check") == 0) { |
| cmockery_abort_at_missing_check(); |
| } else if (strcmp(argv[i], "--cmockery_generate_suppression") == 0) { |
| cmockery_enable_generate_suppression(); |
| } else if (strcmp(argv[i], "--cmockery_run_disabled_tests") == 0) { |
| cmockery_run_disabled_tests(); |
| } |
| } |
| } |
| |
| void cmockery_run_disabled_tests(void) |
| { |
| print_message(COLOR_DEFAULT, "Run cmockery with \"disabled tests\" enabled\n"); |
| run_disabled_tests = 1; |
| } |
| |
| void cmockery_abort_at_missing_check(void) |
| { |
| print_message(COLOR_DEFAULT, "Run cmockery with \"abort at missing check\" enabled\n"); |
| abort_for_missing_check = 1; |
| } |
| |
| void cmockery_enable_generate_suppression(void) |
| { |
| print_message(COLOR_DEFAULT, "Run cmockery with \"generate suppression\" enabled\n"); |
| |
| generate_suppression = 1; |
| } |
| |
| int cmockery_test_deactivated_(const char * const function, const char* file, const int line) |
| { |
| if (run_disabled_tests) { |
| return 0; |
| } |
| print_message(COLOR_DEFAULT, OUTPUT_PADDING "%s is currently DISABLED.\n", function); |
| return 1; |
| } |
| |
| void check_color_env(void) |
| { |
| char *term = getenv ("TERM"); |
| if (isatty(fileno(stdout)) && term && (strcmp(term, "xterm-color") == 0 || strcmp(term, "xterm") == 0 || strcmp(term, "xterm-256color") == 0)) |
| { |
| use_color = 1; |
| } |
| } |
| |
| const char* get_color(int color_code) |
| { |
| if (use_color) { |
| switch (color_code) { |
| case COLOR_GREEN: |
| return COLOR_TERM_GREEN; |
| case COLOR_RED: |
| return COLOR_TERM_RED; |
| case COLOR_RESET: |
| return COLOR_TERM_RESET; |
| default: |
| return ""; |
| } |
| } else { |
| switch (color_code) { |
| case COLOR_GREEN: |
| return COLOR_NOTERM_GREEN; |
| case COLOR_RED: |
| return COLOR_NOTERM_RED; |
| case COLOR_RESET: |
| return COLOR_NOTERM_RESET; |
| default: |
| return ""; |
| } |
| } |
| } |