@@ -1,11 +1,26 @@ | |||
#ifndef 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/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 |
@@ -7,11 +7,7 @@ void IZ_LogError(const char* fmt, ...) { | |||
va_start(args, fmt); | |||
vsnprintf(buffer, sizeof(buffer), fmt, args); | |||
va_end(args); | |||
#if IZ_LOG_DATE_FUNCTION == IZ_TimerElapsed | |||
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 | |||
} | |||
@@ -22,7 +18,7 @@ void IZ_LogInfo(IZ_LogCategory category, const char* fmt, ...) { | |||
va_start(args, fmt); | |||
vsnprintf(buffer, sizeof(buffer), fmt, args); | |||
va_end(args); | |||
#if IZ_LOG_DATE_FUNCTION == IZ_TimerElapsed | |||
switch (category) { | |||
default: | |||
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); | |||
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 | |||
} | |||
@@ -60,20 +41,13 @@ void IZ_LogWarn(bool is_critical, const char* fmt, ...) { | |||
va_start(args, fmt); | |||
vsnprintf(buffer, sizeof(buffer), fmt, 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) { | |||
fprintf(stdout, WHT "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | |||
} else { | |||
fprintf(stdout, YEL "%24s" RESET " %s\n", IZ_LOG_DATE_FUNCTION(), buffer); | |||
} | |||
#endif | |||
#endif | |||
} | |||
void IZ_Log(const char* fmt, ...) { | |||
@@ -83,10 +57,7 @@ void IZ_Log(const char* fmt, ...) { | |||
va_start(args, fmt); | |||
vsnprintf(buffer, sizeof(buffer), fmt, args); | |||
va_end(args); | |||
#if IZ_LOG_DATE_FUNCTION == IZ_TimerElapsed | |||
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 | |||
} |
@@ -5,6 +5,14 @@ | |||
#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_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."); | |||
} | |||
} | |||
} | |||
``` |