coreutils / tests /dd /tests_for_print_stats.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>
#include <locale.h>
#include <errno.h>
/* The test file is included into the same translation unit as dd.c,
so we can access file-scope static variables and functions directly. */
/* Helper to capture and restore stderr output. */
static FILE *cap_file = NULL;
static int saved_stderr_fd = -1;
static void start_capture_stderr(void)
{
fflush(stderr);
saved_stderr_fd = dup(STDERR_FILENO);
TEST_ASSERT_TRUE_MESSAGE(saved_stderr_fd >= 0, "dup(STDERR_FILENO) failed");
cap_file = tmpfile();
TEST_ASSERT_NOT_NULL_MESSAGE(cap_file, "tmpfile() failed");
int cap_fd = fileno(cap_file);
TEST_ASSERT_TRUE_MESSAGE(cap_fd >= 0, "fileno(tmpfile) failed");
int rc = dup2(cap_fd, STDERR_FILENO);
TEST_ASSERT_TRUE_MESSAGE(rc >= 0, "dup2 to redirect stderr failed");
}
static char *finish_capture_stderr(void)
{
fflush(stderr);
TEST_ASSERT_NOT_NULL_MESSAGE(cap_file, "No capture active");
long size;
if (fseek(cap_file, 0, SEEK_END) != 0)
TEST_FAIL_MESSAGE("fseek end failed");
size = ftell(cap_file);
if (size < 0)
TEST_FAIL_MESSAGE("ftell failed");
if (fseek(cap_file, 0, SEEK_SET) != 0)
TEST_FAIL_MESSAGE("fseek set failed");
char *buf = (char *)malloc((size_t)size + 1);
TEST_ASSERT_NOT_NULL_MESSAGE(buf, "malloc failed");
size_t n = fread(buf, 1, (size_t)size, cap_file);
buf[n] = '\0';
/* Restore stderr */
int rc = dup2(saved_stderr_fd, STDERR_FILENO);
TEST_ASSERT_TRUE_MESSAGE(rc >= 0, "dup2 restore stderr failed");
close(saved_stderr_fd);
saved_stderr_fd = -1;
fclose(cap_file);
cap_file = NULL;
return buf;
}
/* Count newline characters in a C string. */
static int count_newlines(const char *s)
{
int c = 0;
for (; *s; ++s) if (*s == '\n') ++c;
return c;
}
/* Reset the global state relevant to print_stats. */
static void reset_print_stats_state(void)
{
/* status_level enum values are visible from included dd.c */
status_level = STATUS_DEFAULT;
progress_len = 0;
r_full = 0;
r_partial = 0;
w_full = 0;
w_partial = 0;
r_truncate = 0;
w_bytes = 0;
reported_w_bytes = -1;
}
void setUp(void) {
setlocale(LC_ALL, "C");
reset_print_stats_state();
}
void tearDown(void) {
/* Ensure capture is not left active between tests */
if (cap_file) {
char *discard = finish_capture_stderr();
free(discard);
}
}
/* Test: STATUS_NONE -> no output and progress_len unchanged */
void test_print_stats_status_none_no_output_no_progress_reset(void)
{
status_level = STATUS_NONE;
progress_len = 5; /* simulate prior progress output */
r_full = 1; r_partial = 2; w_full = 3; w_partial = 4; r_truncate = 1;
start_capture_stderr();
print_stats();
char *out = finish_capture_stderr();
TEST_ASSERT_EQUAL_INT(0, (int)strlen(out));
TEST_ASSERT_EQUAL_INT(5, progress_len); /* unchanged */
free(out);
}
/* Test: basic records lines with STATUS_NOXFER and no truncate */
void test_print_stats_basic_records_output_no_truncate(void)
{
status_level = STATUS_NOXFER;
progress_len = 0;
r_full = 2; r_partial = 3; w_full = 4; w_partial = 5; r_truncate = 0;
start_capture_stderr();
print_stats();
char *out = finish_capture_stderr();
const char *expected = "2+3 records in\n4+5 records out\n";
TEST_ASSERT_EQUAL_STRING(expected, out);
free(out);
}
/* Test: truncate singular message */
void test_print_stats_truncate_singular(void)
{
status_level = STATUS_NOXFER;
r_truncate = 1;
start_capture_stderr();
print_stats();
char *out = finish_capture_stderr();
const char *expected = "0+0 records in\n0+0 records out\n1 truncated record\n";
TEST_ASSERT_EQUAL_STRING(expected, out);
free(out);
}
/* Test: truncate plural message */
void test_print_stats_truncate_plural(void)
{
status_level = STATUS_NOXFER;
r_truncate = 2;
start_capture_stderr();
print_stats();
char *out = finish_capture_stderr();
const char *expected = "0+0 records in\n0+0 records out\n2 truncated records\n";
TEST_ASSERT_EQUAL_STRING(expected, out);
free(out);
}
/* Test: when progress_len > 0 and not STATUS_NONE, prepend newline and reset progress_len */
void test_print_stats_progress_len_newline_and_reset(void)
{
status_level = STATUS_NOXFER; /* avoid print_xfer_stats */
progress_len = 10; /* pending progress line */
start_capture_stderr();
print_stats();
char *out = finish_capture_stderr();
TEST_ASSERT_TRUE_MESSAGE(out[0] == '\n', "Expected leading newline when progress_len > 0");
const char *rest = out + 1;
const char *expected_rest = "0+0 records in\n0+0 records out\n";
TEST_ASSERT_EQUAL_STRING(expected_rest, rest);
TEST_ASSERT_EQUAL_INT(0, progress_len); /* reset */
free(out);
}
/* Test: non-NOXFER status invokes print_xfer_stats (observed via reported_w_bytes) and adds a third line */
void test_print_stats_calls_print_xfer_stats_and_updates_reported(void)
{
status_level = STATUS_DEFAULT; /* triggers print_xfer_stats */
progress_len = 0;
r_truncate = 0;
w_bytes = 12345;
reported_w_bytes = -1;
/* Make start_time close to now to keep print_xfer_stats deterministic enough.
We don't assert the exact text, only newline count and side effect. */
start_time = gethrxtime();
start_capture_stderr();
print_stats();
char *out = finish_capture_stderr();
TEST_ASSERT_EQUAL_INT64((int64_t)w_bytes, (int64_t)reported_w_bytes);
int nl = count_newlines(out);
TEST_ASSERT_GREATER_OR_EQUAL_INT(3, nl); /* at least records-in, records-out, and xfer stats */
free(out);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_print_stats_status_none_no_output_no_progress_reset);
RUN_TEST(test_print_stats_basic_records_output_no_truncate);
RUN_TEST(test_print_stats_truncate_singular);
RUN_TEST(test_print_stats_truncate_plural);
RUN_TEST(test_print_stats_progress_len_newline_and_reset);
RUN_TEST(test_print_stats_calls_print_xfer_stats_and_updates_reported);
return UNITY_END();
}