|
|
#include "../../unity/unity.h" |
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <unistd.h> |
|
|
#include <locale.h> |
|
|
#include <errno.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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'; |
|
|
|
|
|
|
|
|
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; |
|
|
} |
|
|
|
|
|
|
|
|
static int count_newlines(const char *s) |
|
|
{ |
|
|
int c = 0; |
|
|
for (; *s; ++s) if (*s == '\n') ++c; |
|
|
return c; |
|
|
} |
|
|
|
|
|
|
|
|
static void reset_print_stats_state(void) |
|
|
{ |
|
|
|
|
|
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) { |
|
|
|
|
|
if (cap_file) { |
|
|
char *discard = finish_capture_stderr(); |
|
|
free(discard); |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
void test_print_stats_status_none_no_output_no_progress_reset(void) |
|
|
{ |
|
|
status_level = STATUS_NONE; |
|
|
progress_len = 5; |
|
|
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); |
|
|
|
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
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); |
|
|
} |
|
|
|
|
|
|
|
|
void test_print_stats_progress_len_newline_and_reset(void) |
|
|
{ |
|
|
status_level = STATUS_NOXFER; |
|
|
progress_len = 10; |
|
|
|
|
|
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); |
|
|
|
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
void test_print_stats_calls_print_xfer_stats_and_updates_reported(void) |
|
|
{ |
|
|
status_level = STATUS_DEFAULT; |
|
|
progress_len = 0; |
|
|
r_truncate = 0; |
|
|
w_bytes = 12345; |
|
|
reported_w_bytes = -1; |
|
|
|
|
|
|
|
|
|
|
|
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); |
|
|
|
|
|
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(); |
|
|
} |