|
|
#include "../../unity/unity.h" |
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <stdarg.h> |
|
|
#include <unistd.h> |
|
|
#include <fcntl.h> |
|
|
#include <errno.h> |
|
|
#include <stdbool.h> |
|
|
|
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
} |
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
static char *read_all_into_string(int fd) { |
|
|
size_t cap = 1024; |
|
|
size_t len = 0; |
|
|
char *buf = (char *)malloc(cap); |
|
|
if (!buf) return NULL; |
|
|
for (;;) { |
|
|
if (len + 512 > cap) { |
|
|
size_t ncap = cap * 2; |
|
|
char *nbuf = (char *)realloc(buf, ncap); |
|
|
if (!nbuf) { free(buf); return NULL; } |
|
|
cap = ncap; buf = nbuf; |
|
|
} |
|
|
ssize_t n = read(fd, buf + len, 512); |
|
|
if (n < 0) { |
|
|
if (errno == EINTR) continue; |
|
|
break; |
|
|
} |
|
|
if (n == 0) break; |
|
|
len += (size_t)n; |
|
|
} |
|
|
buf[len] = '\0'; |
|
|
return buf; |
|
|
} |
|
|
|
|
|
|
|
|
static char *capture_stderr_and_call(void (*cb)(void)) { |
|
|
int pipefd[2]; |
|
|
if (pipe(pipefd) != 0) { |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
int saved_stderr = dup(STDERR_FILENO); |
|
|
if (saved_stderr < 0) { |
|
|
close(pipefd[0]); close(pipefd[1]); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
|
|
|
if (dup2(pipefd[1], STDERR_FILENO) < 0) { |
|
|
close(pipefd[0]); close(pipefd[1]); close(saved_stderr); |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
close(pipefd[1]); |
|
|
|
|
|
|
|
|
cb(); |
|
|
|
|
|
|
|
|
fflush(stderr); |
|
|
|
|
|
|
|
|
dup2(saved_stderr, STDERR_FILENO); |
|
|
close(saved_stderr); |
|
|
|
|
|
|
|
|
char *out = read_all_into_string(pipefd[0]); |
|
|
close(pipefd[0]); |
|
|
return out; |
|
|
} |
|
|
|
|
|
|
|
|
static bool str_contains(const char *haystack, const char *needle) { |
|
|
if (!haystack || !needle) return false; |
|
|
return strstr(haystack, needle) != NULL; |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
extern int progress_len; |
|
|
|
|
|
|
|
|
|
|
|
static int g_errnum; |
|
|
static const char *g_fmt; |
|
|
static const char *g_sarg; |
|
|
static int g_iarg; |
|
|
static void call_diagnose_s_i(void) { |
|
|
diagnose(g_errnum, g_fmt, g_sarg, g_iarg); |
|
|
} |
|
|
static void call_diagnose_s(void) { |
|
|
diagnose(g_errnum, g_fmt, g_sarg); |
|
|
} |
|
|
static void call_diagnose_literal(void) { |
|
|
diagnose(g_errnum, "Literal message"); |
|
|
} |
|
|
|
|
|
void test_diagnose_inserts_newline_when_progress_pending(void) { |
|
|
progress_len = 5; |
|
|
g_errnum = 0; g_fmt = "Test %s"; g_sarg = "NL"; |
|
|
char *out = capture_stderr_and_call(call_diagnose_s); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE(out[0] == '\n', "diagnose should prepend a newline when progress_len>0"); |
|
|
|
|
|
TEST_ASSERT_TRUE(str_contains(out, "Test NL")); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, progress_len); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
void test_diagnose_no_leading_newline_without_progress(void) { |
|
|
progress_len = 0; |
|
|
g_errnum = 0; g_fmt = "Hello %s"; g_sarg = "World"; |
|
|
char *out = capture_stderr_and_call(call_diagnose_s); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE(out[0] != '\n', "diagnose should not prepend newline when progress_len==0"); |
|
|
TEST_ASSERT_TRUE(str_contains(out, "Hello World")); |
|
|
TEST_ASSERT_EQUAL_INT(0, progress_len); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
void test_diagnose_includes_errno_message_when_errnum_nonzero(void) { |
|
|
progress_len = 0; |
|
|
g_errnum = EINVAL; g_fmt = "Bad option %s"; g_sarg = "--foo"; |
|
|
const char *errtxt = strerror(g_errnum); |
|
|
char *out = capture_stderr_and_call(call_diagnose_s); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
|
|
|
TEST_ASSERT_TRUE(str_contains(out, "Bad option --foo")); |
|
|
|
|
|
TEST_ASSERT_TRUE_MESSAGE(str_contains(out, errtxt), "diagnose should include strerror text when errnum != 0"); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
void test_diagnose_varargs_formatting_with_int_and_string(void) { |
|
|
progress_len = 0; |
|
|
g_errnum = 0; g_fmt = "Value:%d;Name:%s"; g_sarg = "Z"; g_iarg = 123; |
|
|
char *out = capture_stderr_and_call(call_diagnose_s_i); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_TRUE(str_contains(out, "Value:123;Name:Z")); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
void test_diagnose_resets_progress_len_even_on_simple_message(void) { |
|
|
progress_len = 2; |
|
|
g_errnum = 0; |
|
|
char *out = capture_stderr_and_call(call_diagnose_literal); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_INT(0, progress_len); |
|
|
TEST_ASSERT_TRUE(str_contains(out, "Literal message")); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
int main(void) { |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_diagnose_inserts_newline_when_progress_pending); |
|
|
RUN_TEST(test_diagnose_no_leading_newline_without_progress); |
|
|
RUN_TEST(test_diagnose_includes_errno_message_when_errnum_nonzero); |
|
|
RUN_TEST(test_diagnose_varargs_formatting_with_int_and_string); |
|
|
RUN_TEST(test_diagnose_resets_progress_len_even_on_simple_message); |
|
|
return UNITY_END(); |
|
|
} |