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