coreutils / tests /expr /tests_for_trace.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#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();
}