Skip to content

Commit 44e8736

Browse files
Merge pull request #103 from dreamer-coding/add_io_mock
Adding io mock capture
2 parents 0e8cbce + 2d76755 commit 44e8736

File tree

6 files changed

+249
-12
lines changed

6 files changed

+249
-12
lines changed

code/logic/common.c

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1205,6 +1205,10 @@ bool pizza_sys_memory_is_valid(const pizza_sys_memory_t ptr) {
12051205
// output management
12061206
// *****************************************************************************
12071207

1208+
pizza_fstream_t *PIZZA_STDIN;
1209+
pizza_fstream_t *PIZZA_STDOUT;
1210+
pizza_fstream_t *PIZZA_STDERR;
1211+
12081212
int32_t PIZZA_IO_COLOR_ENABLE = 1; // Flag to enable/disable color output
12091213
int32_t FOSSIL_IO_ATTR_ENABLE = 1; // Flag to enable/disable attribute output
12101214

code/logic/fossil/pizza/common.h

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,21 +19,21 @@
1919
#include <stdlib.h>
2020
#include <stdint.h>
2121
#include <stdarg.h>
22-
#include <unistd.h>
2322
#include <string.h>
2423
#include <time.h>
2524
#include <stdio.h>
2625
#include <float.h>
2726
#include <ctype.h>
2827
#include <math.h>
28+
#include <unistd.h> // Only include once
2929

3030
#ifdef _WIN32
3131
#include <windows.h>
3232
#include <sys/stat.h>
3333
#elif defined(__APPLE__)
34+
#define _DARWIN_C_SOURCE
3435
#include <sys/utsname.h>
35-
#include <sys/types.h>
36-
#include <unistd.h>
36+
#include <sys/types.h> // Before sysctl.h
3737
#include <sys/sysctl.h>
3838
#include <sys/stat.h>
3939
#include <mach/mach_time.h>
@@ -43,7 +43,6 @@
4343
#include <sys/types.h>
4444
#include <sys/stat.h>
4545
#include <sys/time.h>
46-
#include <unistd.h>
4746
#endif
4847

4948
#ifndef _POSIX_C_SOURCE

code/logic/fossil/pizza/mock.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,25 @@ void fossil_mock_add_call(fossil_mock_calllist_t *list, const char *function_nam
117117
*/
118118
void fossil_mock_print(fossil_mock_calllist_t *list);
119119

120+
/**
121+
* Captures the output of a function to a buffer for testing purposes.
122+
*
123+
* @param buffer The buffer to store the captured output.
124+
* @param size The size of the buffer.
125+
* @param function The function whose output is to be captured.
126+
* @return The number of characters captured.
127+
*/
128+
int fossil_mock_capture_output(char *buffer, size_t size, void (*function)(void));
129+
130+
/**
131+
* Compares the captured output with the expected output.
132+
*
133+
* @param captured The captured output.
134+
* @param expected The expected output.
135+
* @return True if the captured output matches the expected output, false otherwise.
136+
*/
137+
bool fossil_mock_compare_output(const char *captured, const char *expected);
138+
120139
#ifdef __cplusplus
121140
}
122141
#endif
@@ -211,6 +230,30 @@ void fossil_mock_print(fossil_mock_calllist_t *list);
211230
typedef struct name
212231
#endif
213232

233+
/**
234+
* @def _FOSSIL_MOCK_REDIRECT_STDOUT
235+
* @brief Macro for redirecting stdout to a buffer.
236+
*
237+
* This macro redirects stdout to a buffer for capturing output.
238+
*
239+
* @param buffer The buffer to capture the output.
240+
* @param size The size of the buffer.
241+
*/
242+
#define _FOSSIL_MOCK_CAPTURE_OUTPUT(buffer, size, function) \
243+
fossil_mock_capture_output(buffer, size, function)
244+
245+
/**
246+
* @def _FOSSIL_MOCK_REDIRECT_STDOUT
247+
* @brief Macro for redirecting stdout to a buffer.
248+
*
249+
* This macro redirects stdout to a buffer for capturing output.
250+
*
251+
* @param buffer The buffer to capture the output.
252+
* @param size The size of the buffer.
253+
*/
254+
#define _FOSSIL_MOCK_COMPARE_OUTPUT(captured, expected) \
255+
fossil_mock_compare_output(captured, expected)
256+
214257
// *****************************************************************************
215258
// Public API Macros
216259
// *****************************************************************************
@@ -299,4 +342,30 @@ void fossil_mock_print(fossil_mock_calllist_t *list);
299342
#define FOSSIL_MOCK_STRUCT(name) \
300343
_FOSSIL_MOCK_STRUCT(name)
301344

345+
/**
346+
* @def FOSSIL_MOCK_REDIRECT_STDOUT
347+
* @brief Macro for redirecting stdout to a buffer.
348+
*
349+
* This macro redirects stdout to a buffer for capturing output.
350+
*
351+
* @param buffer The buffer to capture the output.
352+
* @param size The size of the buffer.
353+
*/
354+
#define FOSSIL_MOCK_REDIRECT_STDOUT(buffer, size, function) \
355+
_FOSSIL_MOCK_CAPTURE_OUTPUT(buffer, size, function)
356+
357+
/**
358+
* @def FOSSIL_MOCK_COMPARE_OUTPUT
359+
* @brief Macro for comparing captured output with expected output.
360+
*
361+
* This macro compares the captured output with the expected output.
362+
*
363+
* @param captured The captured output.
364+
* @param expected The expected output.
365+
* @return True if the captured output matches the expected output, false otherwise.
366+
*/
367+
#define FOSSIL_MOCK_COMPARE_OUTPUT(captured, expected) \
368+
_FOSSIL_MOCK_COMPARE_OUTPUT(captured, expected)
369+
302370
#endif // FOSSIL_MOCK_FRAMEWORK_H
371+

code/logic/mock.c

Lines changed: 44 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,8 @@
1212
* Copyright (C) 2014-2025 Fossil Logic. All rights reserved.
1313
* -----------------------------------------------------------------------------
1414
*/
15+
#define _POSIX_C_SOURCE 200809L
1516
#include "fossil/pizza/mock.h"
16-
#include "fossil/pizza/common.h"
1717

1818
// *****************************************************************************
1919
// Function declarations
@@ -134,3 +134,46 @@ void fossil_mock_print(fossil_mock_calllist_t *list) {
134134
current = current->next;
135135
}
136136
}
137+
138+
int fossil_mock_capture_output(char *buffer, size_t size, void (*function)(void)) {
139+
if (!buffer || size == 0 || !function) {
140+
return -1;
141+
}
142+
143+
FILE *temp_file = tmpfile();
144+
if (!temp_file) {
145+
return -1;
146+
}
147+
148+
int original_stdout_fd = dup(STDOUT_FILENO);
149+
if (original_stdout_fd == -1) {
150+
fclose(temp_file);
151+
return -1;
152+
}
153+
fflush(stdout);
154+
if (dup2(fileno(temp_file), STDOUT_FILENO) == -1) {
155+
fclose(temp_file);
156+
close(original_stdout_fd);
157+
return -1;
158+
}
159+
160+
function(); // no arguments passed
161+
162+
fflush(stdout);
163+
dup2(original_stdout_fd, STDOUT_FILENO);
164+
close(original_stdout_fd);
165+
166+
rewind(temp_file);
167+
size_t read_size = fread(buffer, 1, size - 1, temp_file);
168+
buffer[read_size] = '\0';
169+
170+
fclose(temp_file);
171+
return (int)read_size;
172+
}
173+
174+
bool fossil_mock_compare_output(const char *captured, const char *expected) {
175+
if (!captured || !expected) {
176+
return false;
177+
}
178+
return strcmp(captured, expected) == 0;
179+
}

code/tests/cases/test_mock.c

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ FOSSIL_MOCK_FUNC(int, c_mock_function, int a, int b) {
4949
return a + b;
5050
}
5151

52+
FOSSIL_MOCK_FUNC(void, c_mock_function_with_output, void) {
53+
pizza_io_printf("Hello, Fossil Logic!");
54+
}
55+
56+
FOSSIL_MOCK_FUNC(void, mock_function_redirection, void) {
57+
pizza_io_printf("Testing macro redirection!");
58+
}
59+
5260
// * * * * * * * * * * * * * * * * * * * * * * * *
5361
// * Fossil Logic Test Cases
5462
// * * * * * * * * * * * * * * * * * * * * * * * *
@@ -323,6 +331,53 @@ FOSSIL_TEST(c_mock_macro_destruction) {
323331
FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after destruction using macro");
324332
} // end case
325333

334+
FOSSIL_TEST(c_mock_io_capture_output) {
335+
// Buffer to capture output
336+
char buffer[256];
337+
338+
// Capture the output of the mock function
339+
int captured_size = fossil_mock_capture_output(buffer, sizeof(buffer), fossil_mockup_c_mock_function_with_output);
340+
341+
// Test cases
342+
FOSSIL_TEST_ASSUME(captured_size > 0, "Captured size should be greater than 0");
343+
FOSSIL_TEST_ASSUME(strcmp(buffer, "Hello, Fossil Logic!") == 0, "Captured output should match expected output");
344+
} // end case
345+
346+
FOSSIL_TEST(c_mock_io_compare_output) {
347+
// Captured and expected outputs
348+
const char *captured = "Hello, Fossil Logic!";
349+
const char *expected = "Hello, Fossil Logic!";
350+
351+
// Compare the outputs
352+
bool result = fossil_mock_compare_output(captured, expected);
353+
354+
// Test cases
355+
FOSSIL_TEST_ASSUME(result == true, "Captured output should match expected output");
356+
} // end case
357+
358+
FOSSIL_TEST(c_mock_io_redirect_stdout_macro) {
359+
// Buffer to capture output
360+
char buffer[256];
361+
362+
// Use the macro to redirect stdout and capture output
363+
FOSSIL_MOCK_REDIRECT_STDOUT(buffer, sizeof(buffer), fossil_mockup_mock_function_redirection);
364+
365+
// Test cases
366+
FOSSIL_TEST_ASSUME(strcmp(buffer, "Testing macro redirection!") == 0, "Captured output should match expected output");
367+
} // end case
368+
369+
FOSSIL_TEST(c_mock_io_compare_output_macro) {
370+
// Captured and expected outputs
371+
const char *captured = "Macro comparison test!";
372+
const char *expected = "Macro comparison test!";
373+
374+
// Use the macro to compare outputs
375+
bool result = FOSSIL_MOCK_COMPARE_OUTPUT(captured, expected);
376+
377+
// Test cases
378+
FOSSIL_TEST_ASSUME(result == true, "Captured output should match expected output using macro");
379+
} // end case
380+
326381
// * * * * * * * * * * * * * * * * * * * * * * * *
327382
// * Fossil Logic Test Pool
328383
// * * * * * * * * * * * * * * * * * * * * * * * *
@@ -341,5 +396,11 @@ FOSSIL_TEST_GROUP(c_mock_test_cases) {
341396
FOSSIL_TEST_ADD(c_mock_suite, c_mock_macro_addition);
342397
FOSSIL_TEST_ADD(c_mock_suite, c_mock_macro_destruction);
343398

399+
FOSSIL_TEST_ADD(c_mock_suite, c_mock_io_capture_output);
400+
FOSSIL_TEST_ADD(c_mock_suite, c_mock_io_compare_output);
401+
FOSSIL_TEST_ADD(c_mock_suite, c_mock_io_redirect_stdout_macro);
402+
FOSSIL_TEST_ADD(c_mock_suite, c_mock_io_compare_output_macro);
403+
FOSSIL_TEST_ADD(c_mock_suite, c_mock_io_compare_output);
404+
344405
FOSSIL_TEST_REGISTER(c_mock_suite);
345406
} // end of group

code/tests/cases/test_mock.cpp

Lines changed: 68 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,14 @@ FOSSIL_MOCK_FUNC(int, cpp_mock_function, int a, int b) {
4949
return a + b;
5050
}
5151

52+
FOSSIL_MOCK_FUNC(void, cpp_mock_function_with_output, void) {
53+
pizza_io_printf("Hello, Fossil Logic!");
54+
}
55+
56+
FOSSIL_MOCK_FUNC(void, mock_function_redirection, void) {
57+
pizza_io_printf("Testing macro redirection!");
58+
}
59+
5260
// * * * * * * * * * * * * * * * * * * * * * * * *
5361
// * Fossil Logic Test Cases
5462
// * * * * * * * * * * * * * * * * * * * * * * * *
@@ -62,8 +70,8 @@ FOSSIL_TEST(cpp_mock_call_list_initialization) {
6270
fossil_mock_init(&list);
6371

6472
// Test cases
65-
FOSSIL_TEST_ASSUME(list.head == nullptr, "fossil_mock_calllist_t head should be nullptr after initialization");
66-
FOSSIL_TEST_ASSUME(list.tail == nullptr, "fossil_mock_calllist_t tail should be nullptr after initialization");
73+
FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after initialization");
74+
FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after initialization");
6775
FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after initialization");
6876
} // end case
6977

@@ -208,7 +216,7 @@ FOSSIL_TEST(cpp_mock_call_list_edge_cases) {
208216
fossil_mock_init(&list);
209217

210218
// Add a call with no arguments
211-
fossil_mock_add_call(&list, "no_args_function", nullptr, 0);
219+
fossil_mock_add_call(&list, "no_args_function", NULL, 0);
212220

213221
// Test cases
214222
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) {
253261
MOCK_INIT(list);
254262

255263
// Test cases
256-
FOSSIL_TEST_ASSUME(list.head == nullptr, "fossil_mock_calllist_t head should be nullptr after initialization using macro");
257-
FOSSIL_TEST_ASSUME(list.tail == nullptr, "fossil_mock_calllist_t tail should be nullptr after initialization using macro");
264+
FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after initialization using macro");
265+
FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after initialization using macro");
258266
FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after initialization using macro");
259267
} // end case
260268

@@ -318,11 +326,58 @@ FOSSIL_TEST(cpp_mock_macro_destruction) {
318326
MOCK_DESTROY(list);
319327

320328
// Test cases
321-
FOSSIL_TEST_ASSUME(list.head == nullptr, "fossil_mock_calllist_t head should be nullptr after destruction using macro");
322-
FOSSIL_TEST_ASSUME(list.tail == nullptr, "fossil_mock_calllist_t tail should be nullptr after destruction using macro");
329+
FOSSIL_TEST_ASSUME(list.head == NULL, "fossil_mock_calllist_t head should be NULL after destruction using macro");
330+
FOSSIL_TEST_ASSUME(list.tail == NULL, "fossil_mock_calllist_t tail should be NULL after destruction using macro");
323331
FOSSIL_TEST_ASSUME(list.size == 0, "fossil_mock_calllist_t size should be 0 after destruction using macro");
324332
} // end case
325333

334+
FOSSIL_TEST(cpp_mock_io_capture_output) {
335+
// Buffer to capture output
336+
char buffer[256];
337+
338+
// Capture the output of the mock function
339+
int captured_size = fossil_mock_capture_output(buffer, sizeof(buffer), fossil_mockup_cpp_mock_function_with_output);
340+
341+
// Test cases
342+
FOSSIL_TEST_ASSUME(captured_size > 0, "Captured size should be greater than 0");
343+
FOSSIL_TEST_ASSUME(strcmp(buffer, "Hello, Fossil Logic!") == 0, "Captured output should match expected output");
344+
} // end case
345+
346+
FOSSIL_TEST(cpp_mock_io_compare_output) {
347+
// Captured and expected outputs
348+
const char *captured = "Hello, Fossil Logic!";
349+
const char *expected = "Hello, Fossil Logic!";
350+
351+
// Compare the outputs
352+
bool result = fossil_mock_compare_output(captured, expected);
353+
354+
// Test cases
355+
FOSSIL_TEST_ASSUME(result == true, "Captured output should match expected output");
356+
} // end case
357+
358+
FOSSIL_TEST(cpp_mock_io_redirect_stdout_macro) {
359+
// Buffer to capture output
360+
char buffer[256];
361+
362+
// Use the macro to redirect stdout and capture output
363+
FOSSIL_MOCK_REDIRECT_STDOUT(buffer, sizeof(buffer), fossil_mockup_mock_function_redirection);
364+
365+
// Test cases
366+
FOSSIL_TEST_ASSUME(strcmp(buffer, "Testing macro redirection!") == 0, "Captured output should match expected output");
367+
} // end case
368+
369+
FOSSIL_TEST(cpp_mock_io_compare_output_macro) {
370+
// Captured and expected outputs
371+
const char *captured = "Macro comparison test!";
372+
const char *expected = "Macro comparison test!";
373+
374+
// Use the macro to compare outputs
375+
bool result = FOSSIL_MOCK_COMPARE_OUTPUT(captured, expected);
376+
377+
// Test cases
378+
FOSSIL_TEST_ASSUME(result == true, "Captured output should match expected output using macro");
379+
} // end case
380+
326381
// * * * * * * * * * * * * * * * * * * * * * * * *
327382
// * Fossil Logic Test Pool
328383
// * * * * * * * * * * * * * * * * * * * * * * * *
@@ -341,5 +396,11 @@ FOSSIL_TEST_GROUP(cpp_mock_test_cases) {
341396
FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_macro_addition);
342397
FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_macro_destruction);
343398

399+
FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_io_capture_output);
400+
FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_io_compare_output);
401+
FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_io_redirect_stdout_macro);
402+
FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_io_compare_output_macro);
403+
FOSSIL_TEST_ADD(cpp_mock_suite, cpp_mock_io_compare_output);
404+
344405
FOSSIL_TEST_REGISTER(cpp_mock_suite);
345406
} // end of group

0 commit comments

Comments
 (0)