@@ -1,11 +1,26 @@ | |||||
#ifndef IZ_LOG_MOCK_H | #ifndef IZ_LOG_MOCK_H | ||||
#define IZ_LOG_MOCK_H | #define IZ_LOG_MOCK_H | ||||
#include "../../../src/packages/log/IZ_log.h" | |||||
#include "../../../src/packages/test/IZ_mock.h" | #include "../../../src/packages/test/IZ_mock.h" | ||||
#include "../../../src/packages/log/IZ_log.h" | |||||
mock_modes(IZ_LogInfo) { | |||||
IZ_LOG_INFO_SUPPRESS = 0, | |||||
IZ_LOG_INFO_LOG, | |||||
}; | |||||
mock(IZ_LogInfo) void IZ_LogInfo(IZ_LogCategory category, const char* format, ...) { | |||||
mock_return(IZ_LogInfo); | |||||
mock(IZ_LogInfo) void IZ_LogInfo(IZ_LogCategory category, const char* fmt, ...) { | |||||
mock_mode_if(IZ_LogInfo, IZ_LOG_INFO_SUPPRESS) { | |||||
mock_return(IZ_LogInfo); | |||||
} else mock_mode_if(IZ_LogInfo, IZ_LOG_INFO_LOG) { | |||||
char buffer[4096]; | |||||
va_list args; | |||||
va_start(args, fmt); | |||||
vsnprintf(buffer, sizeof(buffer), fmt, args); | |||||
va_end(args); | |||||
fprintf(stdout, CYN "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | |||||
mock_return(IZ_LogInfo); | |||||
} | |||||
} | } | ||||
#endif | #endif |
@@ -7,11 +7,7 @@ void IZ_LogError(const char* fmt, ...) { | |||||
va_start(args, fmt); | va_start(args, fmt); | ||||
vsnprintf(buffer, sizeof(buffer), fmt, args); | vsnprintf(buffer, sizeof(buffer), fmt, args); | ||||
va_end(args); | va_end(args); | ||||
#if IZ_LOG_DATE_FUNCTION == IZ_TimerElapsed | |||||
fprintf(stderr, RED "%12s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | fprintf(stderr, RED "%12s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | ||||
#elif IZ_LOG_DATE_FUNCTION == IZ_TimerNow | |||||
fprintf(stderr, RED "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | |||||
#endif | |||||
#endif | #endif | ||||
} | } | ||||
@@ -22,7 +18,7 @@ void IZ_LogInfo(IZ_LogCategory category, const char* fmt, ...) { | |||||
va_start(args, fmt); | va_start(args, fmt); | ||||
vsnprintf(buffer, sizeof(buffer), fmt, args); | vsnprintf(buffer, sizeof(buffer), fmt, args); | ||||
va_end(args); | va_end(args); | ||||
#if IZ_LOG_DATE_FUNCTION == IZ_TimerElapsed | |||||
switch (category) { | switch (category) { | ||||
default: | default: | ||||
case IZ_LOG_CATEGORY_GENERIC: | case IZ_LOG_CATEGORY_GENERIC: | ||||
@@ -35,21 +31,6 @@ void IZ_LogInfo(IZ_LogCategory category, const char* fmt, ...) { | |||||
fprintf(stdout, GRN "%12s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | fprintf(stdout, GRN "%12s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | ||||
break; | break; | ||||
} | } | ||||
#elif IZ_LOG_DATE_FUNCTION == IZ_TimerNow | |||||
switch (category) { | |||||
default: | |||||
case IZ_LOG_CATEGORY_GENERIC: | |||||
fprintf(stdout, CYN "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | |||||
break; | |||||
case IZ_LOG_CATEGORY_GLOBAL: | |||||
fprintf(stdout, MAG "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | |||||
break; | |||||
case IZ_LOG_CATEGORY_INPUT: | |||||
fprintf(stdout, GRN "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | |||||
break; | |||||
} | |||||
#endif | |||||
#endif | #endif | ||||
} | } | ||||
@@ -60,20 +41,13 @@ void IZ_LogWarn(bool is_critical, const char* fmt, ...) { | |||||
va_start(args, fmt); | va_start(args, fmt); | ||||
vsnprintf(buffer, sizeof(buffer), fmt, args); | vsnprintf(buffer, sizeof(buffer), fmt, args); | ||||
va_end(args); | va_end(args); | ||||
#if IZ_LOG_DATE_FUNCTION == IZ_TimerElapsed | |||||
if (is_critical) { | |||||
fprintf(stdout, WHT "%12s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | |||||
} else { | |||||
fprintf(stdout, YEL "%12s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | |||||
} | |||||
#elif IZ_LOG_DATE_FUNCTION == IZ_TimerNow | |||||
if (is_critical) { | if (is_critical) { | ||||
fprintf(stdout, WHT "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | fprintf(stdout, WHT "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | ||||
} else { | } else { | ||||
fprintf(stdout, YEL "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | fprintf(stdout, YEL "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | ||||
} | } | ||||
#endif | #endif | ||||
#endif | |||||
} | } | ||||
void IZ_Log(const char* fmt, ...) { | void IZ_Log(const char* fmt, ...) { | ||||
@@ -83,10 +57,7 @@ void IZ_Log(const char* fmt, ...) { | |||||
va_start(args, fmt); | va_start(args, fmt); | ||||
vsnprintf(buffer, sizeof(buffer), fmt, args); | vsnprintf(buffer, sizeof(buffer), fmt, args); | ||||
va_end(args); | va_end(args); | ||||
#if IZ_LOG_DATE_FUNCTION == IZ_TimerElapsed | |||||
fprintf(stdout, BLU "%12s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | fprintf(stdout, BLU "%12s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | ||||
#elif IZ_LOG_DATE_FUNCTION == IZ_TimerNow | |||||
fprintf(stdout, BLU "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | |||||
#endif | |||||
#endif | #endif | ||||
} | } |
@@ -5,6 +5,14 @@ | |||||
#define mock(X) static mock_call_count_t actual_##X = 0; | #define mock(X) static mock_call_count_t actual_##X = 0; | ||||
#define mock_mode_t unsigned char | |||||
#define mock_modes(X) static mock_mode_t current_mock_mode_##X = 0; enum mock_modes_##X | |||||
#define mock_mode(X, Y) current_mock_mode_##X = Y | |||||
#define mock_mode_if(X, Y) if (current_mock_mode_##X == Y) | |||||
#define mock_return(X) actual_##X += 1; return | #define mock_return(X) actual_##X += 1; return | ||||
#define mock_reset(X) actual_##X = 0 | #define mock_reset(X) actual_##X = 0 | ||||
@@ -0,0 +1,126 @@ | |||||
# bdd-for-c-mocks | |||||
## Usage | |||||
Suppose you have a library `lib` and an application `app` that uses `lib`. You want to test `app` and you want to mock | |||||
`lib`'s functions. You can do this by creating a mock header file for `lib` and including it in `app`'s test file. | |||||
### `lib.h` | |||||
This is a reference header file for `lib`. The implementation should be abstracted from us, and we want to be able to | |||||
mock its behavior. | |||||
```c | |||||
#ifndef LIB_H | |||||
#define LIB_H | |||||
int add(int a, int b); | |||||
void alert(); | |||||
#endif | |||||
``` | |||||
### `lib.mock.h` | |||||
We define mocked behavior for `lib`. `bdd-for-c-mocks` offers a capability to force mock behavior through modes. Note | |||||
that all mode behaviors should be implemented already. | |||||
```c | |||||
#ifndef LIB_MOCK_H | |||||
#define LIB_MOCK_H | |||||
#include <bdd-for-c-mocks.h> | |||||
mock_modes(add) { | |||||
ADD_RETURNS_SUM = 0, | |||||
ADD_RETURNS_CONSTANT_VALUE, | |||||
}; | |||||
mock(add) int add(int a, int b) { | |||||
mock_mode_if(add, ADD_RETURNS_SUM) { | |||||
// suppose we want to test normal behavior | |||||
mock_return(add) a + b; | |||||
} else mock_mode_if(add, ADD_RETURNS_CONSTANT_VALUE) { | |||||
// maybe an edge case? | |||||
mock_return(add) 5; | |||||
} | |||||
} | |||||
mock(alert) void alert() { | |||||
mock_return(alert); | |||||
} | |||||
#endif | |||||
``` | |||||
### `app.h` | |||||
```c | |||||
#ifndef APP_H | |||||
#define APP_H | |||||
void math(); | |||||
#endif | |||||
``` | |||||
### `app.c` | |||||
This is a reference implementation of `app`. We want to test the invocations done inside the `math()` function. | |||||
```c | |||||
#include "lib.h" | |||||
#include "app.h" | |||||
void math() { | |||||
int result_a = add(1, 2); | |||||
int result_b = add(3, 4); | |||||
if (result_a == result_b) { | |||||
// this is the edge case! | |||||
// alert() should be called if the add values are the same | |||||
alert(); | |||||
} | |||||
} | |||||
``` | |||||
### `app.test.c` | |||||
This is the test file for `app`. It includes `lib.mock.h` instead of `lib.c`. `lib.h` should still be linked because we | |||||
are sharing the original function declarations. | |||||
```c | |||||
#include <bdd-for-c.h> | |||||
#include "app.h" | |||||
spec("app") { | |||||
describe("math") { | |||||
after_each() { | |||||
mock_reset(add); | |||||
} | |||||
after_each() { | |||||
mock_reset(alert); | |||||
} | |||||
it("calls add") { | |||||
mock_mode(add, ADD_RETURNS_SUM); | |||||
mock_set_excepted_calls(math, 2); | |||||
math(); | |||||
check( | |||||
mock_get_expected_calls(math) == mock_get_actual_calls(math), | |||||
"add was not called." | |||||
); | |||||
} | |||||
it("calls alert when calls from add return the same values") { | |||||
mock_mode(add, ADD_RETURNS_CONSTANT_VALUE); | |||||
math(); | |||||
check(mock_is_called(alert), "alert was not called."); | |||||
} | |||||
} | |||||
} | |||||
``` |