diff --git a/__mocks__/src/packages/log/IZ_log.mock.h b/__mocks__/src/packages/log/IZ_log.mock.h index f6b3d60..2a71bb3 100644 --- a/__mocks__/src/packages/log/IZ_log.mock.h +++ b/__mocks__/src/packages/log/IZ_log.mock.h @@ -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 diff --git a/src/packages/log/IZ_log.c b/src/packages/log/IZ_log.c index 2071d1e..6fa3711 100644 --- a/src/packages/log/IZ_log.c +++ b/src/packages/log/IZ_log.c @@ -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 } diff --git a/src/packages/test/IZ_mock.h b/src/packages/test/IZ_mock.h index c8a7bd4..4d45815 100644 --- a/src/packages/test/IZ_mock.h +++ b/src/packages/test/IZ_mock.h @@ -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 diff --git a/src/packages/test/README.md b/src/packages/test/README.md new file mode 100644 index 0000000..0e5e6d7 --- /dev/null +++ b/src/packages/test/README.md @@ -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 + +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 +#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."); + } + } +} + +```