diff --git a/code/logic/common.c b/code/logic/common.c index 231fa98f..86bb8d76 100644 --- a/code/logic/common.c +++ b/code/logic/common.c @@ -1205,6 +1205,10 @@ bool pizza_sys_memory_is_valid(const pizza_sys_memory_t ptr) { // output management // ***************************************************************************** +pizza_fstream_t *PIZZA_STDIN; +pizza_fstream_t *PIZZA_STDOUT; +pizza_fstream_t *PIZZA_STDERR; + int32_t PIZZA_IO_COLOR_ENABLE = 1; // Flag to enable/disable color output int32_t FOSSIL_IO_ATTR_ENABLE = 1; // Flag to enable/disable attribute output diff --git a/code/logic/fossil/pizza/common.h b/code/logic/fossil/pizza/common.h index e9a94a30..6dda45a3 100644 --- a/code/logic/fossil/pizza/common.h +++ b/code/logic/fossil/pizza/common.h @@ -19,21 +19,21 @@ #include #include #include -#include #include #include #include #include #include #include +#include // Only include once #ifdef _WIN32 #include #include #elif defined(__APPLE__) + #define _DARWIN_C_SOURCE #include - #include - #include + #include // Before sysctl.h #include #include #include @@ -43,7 +43,6 @@ #include #include #include - #include #endif #ifndef _POSIX_C_SOURCE diff --git a/code/logic/fossil/pizza/mock.h b/code/logic/fossil/pizza/mock.h index 753cb36b..583c6687 100644 --- a/code/logic/fossil/pizza/mock.h +++ b/code/logic/fossil/pizza/mock.h @@ -117,6 +117,25 @@ void fossil_mock_add_call(fossil_mock_calllist_t *list, const char *function_nam */ void fossil_mock_print(fossil_mock_calllist_t *list); +/** + * Captures the output of a function to a buffer for testing purposes. + * + * @param buffer The buffer to store the captured output. + * @param size The size of the buffer. + * @param function The function whose output is to be captured. + * @return The number of characters captured. + */ +int fossil_mock_capture_output(char *buffer, size_t size, void (*function)(void)); + +/** + * Compares the captured output with the expected output. + * + * @param captured The captured output. + * @param expected The expected output. + * @return True if the captured output matches the expected output, false otherwise. + */ +bool fossil_mock_compare_output(const char *captured, const char *expected); + #ifdef __cplusplus } #endif @@ -211,6 +230,30 @@ void fossil_mock_print(fossil_mock_calllist_t *list); typedef struct name #endif +/** + * @def _FOSSIL_MOCK_REDIRECT_STDOUT + * @brief Macro for redirecting stdout to a buffer. + * + * This macro redirects stdout to a buffer for capturing output. + * + * @param buffer The buffer to capture the output. + * @param size The size of the buffer. + */ +#define _FOSSIL_MOCK_CAPTURE_OUTPUT(buffer, size, function) \ + fossil_mock_capture_output(buffer, size, function) + +/** + * @def _FOSSIL_MOCK_REDIRECT_STDOUT + * @brief Macro for redirecting stdout to a buffer. + * + * This macro redirects stdout to a buffer for capturing output. + * + * @param buffer The buffer to capture the output. + * @param size The size of the buffer. + */ +#define _FOSSIL_MOCK_COMPARE_OUTPUT(captured, expected) \ + fossil_mock_compare_output(captured, expected) + // ***************************************************************************** // Public API Macros // ***************************************************************************** @@ -299,4 +342,30 @@ void fossil_mock_print(fossil_mock_calllist_t *list); #define FOSSIL_MOCK_STRUCT(name) \ _FOSSIL_MOCK_STRUCT(name) +/** + * @def FOSSIL_MOCK_REDIRECT_STDOUT + * @brief Macro for redirecting stdout to a buffer. + * + * This macro redirects stdout to a buffer for capturing output. + * + * @param buffer The buffer to capture the output. + * @param size The size of the buffer. + */ +#define FOSSIL_MOCK_REDIRECT_STDOUT(buffer, size, function) \ + _FOSSIL_MOCK_CAPTURE_OUTPUT(buffer, size, function) + +/** + * @def FOSSIL_MOCK_COMPARE_OUTPUT + * @brief Macro for comparing captured output with expected output. + * + * This macro compares the captured output with the expected output. + * + * @param captured The captured output. + * @param expected The expected output. + * @return True if the captured output matches the expected output, false otherwise. + */ +#define FOSSIL_MOCK_COMPARE_OUTPUT(captured, expected) \ + _FOSSIL_MOCK_COMPARE_OUTPUT(captured, expected) + #endif // FOSSIL_MOCK_FRAMEWORK_H + diff --git a/code/logic/mock.c b/code/logic/mock.c index 6eb5bc47..0d22dfa5 100644 --- a/code/logic/mock.c +++ b/code/logic/mock.c @@ -12,8 +12,8 @@ * Copyright (C) 2014-2025 Fossil Logic. All rights reserved. * ----------------------------------------------------------------------------- */ +#define _POSIX_C_SOURCE 200809L #include "fossil/pizza/mock.h" -#include "fossil/pizza/common.h" // ***************************************************************************** // Function declarations @@ -134,3 +134,46 @@ void fossil_mock_print(fossil_mock_calllist_t *list) { current = current->next; } } + +int fossil_mock_capture_output(char *buffer, size_t size, void (*function)(void)) { + if (!buffer || size == 0 || !function) { + return -1; + } + + FILE *temp_file = tmpfile(); + if (!temp_file) { + return -1; + } + + int original_stdout_fd = dup(STDOUT_FILENO); + if (original_stdout_fd == -1) { + fclose(temp_file); + return -1; + } + fflush(stdout); + if (dup2(fileno(temp_file), STDOUT_FILENO) == -1) { + fclose(temp_file); + close(original_stdout_fd); + return -1; + } + + function(); // no arguments passed + + fflush(stdout); + dup2(original_stdout_fd, STDOUT_FILENO); + close(original_stdout_fd); + + rewind(temp_file); + size_t read_size = fread(buffer, 1, size - 1, temp_file); + buffer[read_size] = '\0'; + + fclose(temp_file); + return (int)read_size; +} + +bool fossil_mock_compare_output(const char *captured, const char *expected) { + if (!captured || !expected) { + return false; + } + return strcmp(captured, expected) == 0; +} diff --git a/code/tests/cases/test_mock.c b/code/tests/cases/test_mock.c index 3a3b0bc7..5acdfb09 100644 --- a/code/tests/cases/test_mock.c +++ b/code/tests/cases/test_mock.c @@ -49,6 +49,14 @@ FOSSIL_MOCK_FUNC(int, c_mock_function, int a, int b) { return a + b; } +FOSSIL_MOCK_FUNC(void, c_mock_function_with_output, void) { + pizza_io_printf("Hello, Fossil Logic!"); +} + +FOSSIL_MOCK_FUNC(void, mock_function_redirection, void) { + pizza_io_printf("Testing macro redirection!"); +} + // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Cases // * * * * * * * * * * * * * * * * * * * * * * * * @@ -323,6 +331,53 @@ FOSSIL_TEST(c_mock_macro_destruction) { FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after destruction using macro"); } // end case +FOSSIL_TEST(c_mock_io_capture_output) { + // Buffer to capture output + char buffer[256]; + + // Capture the output of the mock function + int captured_size = fossil_mock_capture_output(buffer, sizeof(buffer), fossil_mockup_c_mock_function_with_output); + + // Test cases + FOSSIL_TEST_ASSUME(captured_size > 0, "Captured size should be greater than 0"); + FOSSIL_TEST_ASSUME(strcmp(buffer, "Hello, Fossil Logic!") == 0, "Captured output should match expected output"); +} // end case + +FOSSIL_TEST(c_mock_io_compare_output) { + // Captured and expected outputs + const char *captured = "Hello, Fossil Logic!"; + const char *expected = "Hello, Fossil Logic!"; + + // Compare the outputs + bool result = fossil_mock_compare_output(captured, expected); + + // Test cases + FOSSIL_TEST_ASSUME(result == true, "Captured output should match expected output"); +} // end case + +FOSSIL_TEST(c_mock_io_redirect_stdout_macro) { + // Buffer to capture output + char buffer[256]; + + // Use the macro to redirect stdout and capture output + FOSSIL_MOCK_REDIRECT_STDOUT(buffer, sizeof(buffer), fossil_mockup_mock_function_redirection); + + // Test cases + FOSSIL_TEST_ASSUME(strcmp(buffer, "Testing macro redirection!") == 0, "Captured output should match expected output"); +} // end case + +FOSSIL_TEST(c_mock_io_compare_output_macro) { + // Captured and expected outputs + const char *captured = "Macro comparison test!"; + const char *expected = "Macro comparison test!"; + + // Use the macro to compare outputs + bool result = FOSSIL_MOCK_COMPARE_OUTPUT(captured, expected); + + // Test cases + FOSSIL_TEST_ASSUME(result == true, "Captured output should match expected output using macro"); +} // end case + // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Pool // * * * * * * * * * * * * * * * * * * * * * * * * @@ -341,5 +396,11 @@ FOSSIL_TEST_GROUP(c_mock_test_cases) { FOSSIL_TEST_ADD(c_mock_suite, c_mock_macro_addition); FOSSIL_TEST_ADD(c_mock_suite, c_mock_macro_destruction); + FOSSIL_TEST_ADD(c_mock_suite, c_mock_io_capture_output); + FOSSIL_TEST_ADD(c_mock_suite, c_mock_io_compare_output); + FOSSIL_TEST_ADD(c_mock_suite, c_mock_io_redirect_stdout_macro); + FOSSIL_TEST_ADD(c_mock_suite, c_mock_io_compare_output_macro); + FOSSIL_TEST_ADD(c_mock_suite, c_mock_io_compare_output); + FOSSIL_TEST_REGISTER(c_mock_suite); } // end of group diff --git a/code/tests/cases/test_mock.cpp b/code/tests/cases/test_mock.cpp index 23462907..a27dc738 100644 --- a/code/tests/cases/test_mock.cpp +++ b/code/tests/cases/test_mock.cpp @@ -49,6 +49,14 @@ FOSSIL_MOCK_FUNC(int, cpp_mock_function, int a, int b) { return a + b; } +FOSSIL_MOCK_FUNC(void, cpp_mock_function_with_output, void) { + pizza_io_printf("Hello, Fossil Logic!"); +} + +FOSSIL_MOCK_FUNC(void, mock_function_redirection, void) { + pizza_io_printf("Testing macro redirection!"); +} + // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Cases // * * * * * * * * * * * * * * * * * * * * * * * * @@ -62,8 +70,8 @@ FOSSIL_TEST(cpp_mock_call_list_initialization) { fossil_mock_init(&list); // Test cases - FOSSIL_TEST_ASSUME(list.head == nullptr, "fossil_mock_calllist_t head should be nullptr after initialization"); - FOSSIL_TEST_ASSUME(list.tail == nullptr, "fossil_mock_calllist_t tail should be nullptr after initialization"); + FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after initialization"); + FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after initialization"); FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after initialization"); } // end case @@ -208,7 +216,7 @@ FOSSIL_TEST(cpp_mock_call_list_edge_cases) { fossil_mock_init(&list); // Add a call with no arguments - fossil_mock_add_call(&list, "no_args_function", nullptr, 0); + fossil_mock_add_call(&list, "no_args_function", NULL, 0); // Test cases FOSSIL_TEST_ASSUME(list.size == 1, "fossil_mock_calllist_t size should be 1 after adding a call with no arguments"); @@ -253,8 +261,8 @@ FOSSIL_TEST(cpp_mock_macro_initialization) { MOCK_INIT(list); // Test cases - FOSSIL_TEST_ASSUME(list.head == nullptr, "fossil_mock_calllist_t head should be nullptr after initialization using macro"); - FOSSIL_TEST_ASSUME(list.tail == nullptr, "fossil_mock_calllist_t tail should be nullptr after initialization using macro"); + FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after initialization using macro"); + FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after initialization using macro"); FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after initialization using macro"); } // end case @@ -318,11 +326,58 @@ FOSSIL_TEST(cpp_mock_macro_destruction) { MOCK_DESTROY(list); // Test cases - FOSSIL_TEST_ASSUME(list.head == nullptr, "fossil_mock_calllist_t head should be nullptr after destruction using macro"); - FOSSIL_TEST_ASSUME(list.tail == nullptr, "fossil_mock_calllist_t tail should be nullptr after destruction using macro"); + FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after destruction using macro"); + FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after destruction using macro"); FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after destruction using macro"); } // end case +FOSSIL_TEST(cpp_mock_io_capture_output) { + // Buffer to capture output + char buffer[256]; + + // Capture the output of the mock function + int captured_size = fossil_mock_capture_output(buffer, sizeof(buffer), fossil_mockup_cpp_mock_function_with_output); + + // Test cases + FOSSIL_TEST_ASSUME(captured_size > 0, "Captured size should be greater than 0"); + FOSSIL_TEST_ASSUME(strcmp(buffer, "Hello, Fossil Logic!") == 0, "Captured output should match expected output"); +} // end case + +FOSSIL_TEST(cpp_mock_io_compare_output) { + // Captured and expected outputs + const char *captured = "Hello, Fossil Logic!"; + const char *expected = "Hello, Fossil Logic!"; + + // Compare the outputs + bool result = fossil_mock_compare_output(captured, expected); + + // Test cases + FOSSIL_TEST_ASSUME(result == true, "Captured output should match expected output"); +} // end case + +FOSSIL_TEST(cpp_mock_io_redirect_stdout_macro) { + // Buffer to capture output + char buffer[256]; + + // Use the macro to redirect stdout and capture output + FOSSIL_MOCK_REDIRECT_STDOUT(buffer, sizeof(buffer), fossil_mockup_mock_function_redirection); + + // Test cases + FOSSIL_TEST_ASSUME(strcmp(buffer, "Testing macro redirection!") == 0, "Captured output should match expected output"); +} // end case + +FOSSIL_TEST(cpp_mock_io_compare_output_macro) { + // Captured and expected outputs + const char *captured = "Macro comparison test!"; + const char *expected = "Macro comparison test!"; + + // Use the macro to compare outputs + bool result = FOSSIL_MOCK_COMPARE_OUTPUT(captured, expected); + + // Test cases + FOSSIL_TEST_ASSUME(result == true, "Captured output should match expected output using macro"); +} // end case + // * * * * * * * * * * * * * * * * * * * * * * * * // * Fossil Logic Test Pool // * * * * * * * * * * * * * * * * * * * * * * * * @@ -341,5 +396,11 @@ FOSSIL_TEST_GROUP(cpp_mock_test_cases) { FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_macro_addition); FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_macro_destruction); + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_io_capture_output); + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_io_compare_output); + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_io_redirect_stdout_macro); + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_io_compare_output_macro); + FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_io_compare_output); + FOSSIL_TEST_REGISTER(cpp_mock_suite); } // end of group