File size: 3,763 Bytes
78d2150 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 |
#include "../../unity/unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
/* Unity requires these, even if empty. */
void setUp(void) {
/* Setup code here, or leave empty */
}
void tearDown(void) {
/* Cleanup code here, or leave empty */
}
#ifdef EVAL_TRACE
/* Helper: capture the output of trace(fxn) given a specific args[].
Returns a newly allocated NUL-terminated buffer on success, or NULL on error.
Ensures stdout is restored before returning. Does not use Unity macros. */
static char *capture_trace_output(const char *fxn, char **test_args) {
/* Save and set args for the duration of the call. */
char ***args_ptr = &args; /* args is from expr.c (static char **args) */
char **saved_args = *args_ptr;
*args_ptr = test_args;
/* Prepare a temporary FILE to redirect stdout. */
FILE *tmp = tmpfile();
if (!tmp) {
*args_ptr = saved_args;
return NULL;
}
fflush(stdout);
int stdout_fd = fileno(stdout);
int saved_fd = dup(stdout_fd);
if (saved_fd == -1) {
fclose(tmp);
*args_ptr = saved_args;
return NULL;
}
if (dup2(fileno(tmp), stdout_fd) == -1) {
close(saved_fd);
fclose(tmp);
*args_ptr = saved_args;
return NULL;
}
/* Call the function under test while stdout is redirected. */
trace((char *)fxn);
/* Flush everything to the temp file. */
fflush(stdout);
/* Determine size and read back. */
long end = ftell(tmp);
if (end < 0) {
/* Restore stdout before returning. */
dup2(saved_fd, stdout_fd);
close(saved_fd);
fclose(tmp);
*args_ptr = saved_args;
return NULL;
}
if (fseek(tmp, 0, SEEK_SET) != 0) {
dup2(saved_fd, stdout_fd);
close(saved_fd);
fclose(tmp);
*args_ptr = saved_args;
return NULL;
}
char *buf = (char *)malloc((size_t)end + 1);
if (!buf) {
dup2(saved_fd, stdout_fd);
close(saved_fd);
fclose(tmp);
*args_ptr = saved_args;
return NULL;
}
size_t nread = fread(buf, 1, (size_t)end, tmp);
buf[nread] = '\0';
/* Restore stdout and cleanup. */
fflush(stdout);
dup2(saved_fd, stdout_fd);
close(saved_fd);
fclose(tmp);
*args_ptr = saved_args;
return buf;
}
static void assert_trace_equals(const char *fxn, char **test_args, const char *expected) {
char *out = capture_trace_output(fxn, test_args);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "Failed to capture trace output");
TEST_ASSERT_EQUAL_STRING(expected, out);
free(out);
}
void test_trace_empty_args(void) {
char *a0[] = { NULL };
assert_trace_equals("eval", a0, "eval:\n");
}
void test_trace_single_arg(void) {
char *a1[] = { "foo", NULL };
assert_trace_equals("walk", a1, "walk: foo\n");
}
void test_trace_multiple_args(void) {
char *a2[] = { "arg1", "arg2", "arg3", NULL };
assert_trace_equals("parse", a2, "parse: arg1 arg2 arg3\n");
}
void test_trace_empty_string_arg(void) {
char *a3[] = { "", "X", NULL };
/* Note the double space after ':' due to empty first argument. */
assert_trace_equals("step", a3, "step: X\n");
}
void test_trace_utf8_args(void) {
/* UTF-8: "\xCE\xB1" is Greek alpha, "\xE2\x9D\xA7" is U+2767. */
char *a4[] = { "\xCE\xB1bc", "\xE2\x9D\xA7", NULL };
assert_trace_equals("mb", a4, "mb: \xCE\xB1bc \xE2\x9D\xA7\n");
}
#else /* !EVAL_TRACE */
void test_trace_unavailable(void) {
TEST_IGNORE_MESSAGE("EVAL_TRACE not defined; trace() not compiled, skipping tests.");
}
#endif /* EVAL_TRACE */
int main(void) {
UNITY_BEGIN();
#ifdef EVAL_TRACE
RUN_TEST(test_trace_empty_args);
RUN_TEST(test_trace_single_arg);
RUN_TEST(test_trace_multiple_args);
RUN_TEST(test_trace_empty_string_arg);
RUN_TEST(test_trace_utf8_args);
#else
RUN_TEST(test_trace_unavailable);
#endif
return UNITY_END();
} |