|
|
#include "../../unity/unity.h" |
|
|
#include <stdio.h> |
|
|
#include <string.h> |
|
|
#include <stdbool.h> |
|
|
#include <unistd.h> |
|
|
#include <errno.h> |
|
|
|
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static const char* capture_writeline_output(const char* line_data, size_t line_len, |
|
|
int cls, |
|
|
bool enable_col1, bool enable_col2, bool enable_both, |
|
|
const char* sep, size_t sep_len, |
|
|
char* outbuf, size_t outcap, size_t* outlen) |
|
|
{ |
|
|
static char errbuf[256]; |
|
|
|
|
|
|
|
|
only_file_1 = enable_col1; |
|
|
only_file_2 = enable_col2; |
|
|
both = enable_both; |
|
|
col_sep = sep; |
|
|
col_sep_len = sep_len; |
|
|
|
|
|
|
|
|
FILE* tmp = tmpfile(); |
|
|
if (!tmp) { |
|
|
snprintf(errbuf, sizeof errbuf, "tmpfile failed: %s", strerror(errno)); |
|
|
return errbuf; |
|
|
} |
|
|
|
|
|
int saved_fd = dup(fileno(stdout)); |
|
|
if (saved_fd < 0) { |
|
|
snprintf(errbuf, sizeof errbuf, "dup failed: %s", strerror(errno)); |
|
|
fclose(tmp); |
|
|
return errbuf; |
|
|
} |
|
|
|
|
|
if (fflush(stdout) != 0) { |
|
|
snprintf(errbuf, sizeof errbuf, "fflush(stdout) failed: %s", strerror(errno)); |
|
|
close(saved_fd); |
|
|
fclose(tmp); |
|
|
return errbuf; |
|
|
} |
|
|
|
|
|
if (dup2(fileno(tmp), fileno(stdout)) < 0) { |
|
|
snprintf(errbuf, sizeof errbuf, "dup2 failed: %s", strerror(errno)); |
|
|
close(saved_fd); |
|
|
fclose(tmp); |
|
|
return errbuf; |
|
|
} |
|
|
|
|
|
|
|
|
struct linebuffer lb; |
|
|
lb.buffer = (char*) line_data; |
|
|
lb.length = line_len; |
|
|
|
|
|
|
|
|
writeline(&lb, cls); |
|
|
|
|
|
|
|
|
if (fflush(stdout) != 0) { |
|
|
snprintf(errbuf, sizeof errbuf, "fflush after writeline failed: %s", strerror(errno)); |
|
|
|
|
|
} |
|
|
|
|
|
long endpos = ftell(tmp); |
|
|
if (endpos < 0) { |
|
|
snprintf(errbuf, sizeof errbuf, "ftell failed: %s", strerror(errno)); |
|
|
|
|
|
} |
|
|
|
|
|
if (fseek(tmp, 0, SEEK_SET) != 0) { |
|
|
snprintf(errbuf, sizeof errbuf, "fseek failed: %s", strerror(errno)); |
|
|
|
|
|
} |
|
|
|
|
|
size_t toread = (endpos > 0) ? (size_t) endpos : 0; |
|
|
if (toread > outcap) { |
|
|
|
|
|
toread = outcap; |
|
|
snprintf(errbuf, sizeof errbuf, "captured output larger than buffer"); |
|
|
} else { |
|
|
errbuf[0] = '\0'; |
|
|
} |
|
|
|
|
|
size_t nread = (toread > 0) ? fread(outbuf, 1, toread, tmp) : 0; |
|
|
if (nread != toread) { |
|
|
snprintf(errbuf, sizeof errbuf, "fread failed or short read: %s", ferror(tmp) ? strerror(errno) : "short read"); |
|
|
} |
|
|
|
|
|
|
|
|
fflush(stdout); |
|
|
dup2(saved_fd, fileno(stdout)); |
|
|
close(saved_fd); |
|
|
fclose(tmp); |
|
|
|
|
|
*outlen = nread; |
|
|
|
|
|
if (errbuf[0] != '\0') { |
|
|
return errbuf; |
|
|
} |
|
|
return NULL; |
|
|
} |
|
|
|
|
|
|
|
|
static void assert_capture_equals(const char* line, size_t line_len, int cls, |
|
|
bool c1, bool c2, bool cboth, |
|
|
const char* sep, size_t sep_len, |
|
|
const char* expected, size_t expected_len) |
|
|
{ |
|
|
char buf[256]; |
|
|
size_t got_len = 0; |
|
|
const char* err = capture_writeline_output(line, line_len, cls, c1, c2, cboth, |
|
|
sep, sep_len, buf, sizeof buf, &got_len); |
|
|
TEST_ASSERT_NULL_MESSAGE(err, err ? err : "unexpected error capturing output"); |
|
|
TEST_ASSERT_EQUAL_UINT64(expected_len, got_len); |
|
|
TEST_ASSERT_EQUAL_MEMORY(expected, buf, expected_len); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void test_writeline_class1_suppressed_no_output(void) { |
|
|
const char line[] = "alpha\n"; |
|
|
assert_capture_equals(line, strlen(line), 1, |
|
|
false, |
|
|
false, |
|
|
false, |
|
|
"\t", 1, |
|
|
"", 0); |
|
|
} |
|
|
|
|
|
|
|
|
void test_writeline_class1_enabled_prints_line(void) { |
|
|
const char line[] = "alpha\n"; |
|
|
assert_capture_equals(line, strlen(line), 1, |
|
|
true, |
|
|
false, |
|
|
false, |
|
|
"\t", 1, |
|
|
line, strlen(line)); |
|
|
} |
|
|
|
|
|
|
|
|
void test_writeline_class2_suppressed_no_output(void) { |
|
|
const char line[] = "beta\n"; |
|
|
assert_capture_equals(line, strlen(line), 2, |
|
|
false, |
|
|
false, |
|
|
false, |
|
|
"|", 1, |
|
|
"", 0); |
|
|
} |
|
|
|
|
|
|
|
|
void test_writeline_class2_enabled_no_col1_no_prefix(void) { |
|
|
const char line[] = "beta\n"; |
|
|
assert_capture_equals(line, strlen(line), 2, |
|
|
false, |
|
|
true, |
|
|
false, |
|
|
"|", 1, |
|
|
line, strlen(line)); |
|
|
} |
|
|
|
|
|
|
|
|
void test_writeline_class2_enabled_with_col1_prefix_once(void) { |
|
|
const char line[] = "beta\n"; |
|
|
const char sep[] = "|"; |
|
|
char expected[16]; |
|
|
size_t e_len = 0; |
|
|
memcpy(expected + e_len, sep, 1); e_len += 1; |
|
|
memcpy(expected + e_len, line, strlen(line)); e_len += strlen(line); |
|
|
assert_capture_equals(line, strlen(line), 2, |
|
|
true, |
|
|
true, |
|
|
false, |
|
|
sep, strlen(sep), |
|
|
expected, e_len); |
|
|
} |
|
|
|
|
|
|
|
|
void test_writeline_class3_enabled_no_columns_no_prefix(void) { |
|
|
const char line[] = "gamma\n"; |
|
|
assert_capture_equals(line, strlen(line), 3, |
|
|
false, |
|
|
false, |
|
|
true, |
|
|
"\t", 1, |
|
|
line, strlen(line)); |
|
|
} |
|
|
|
|
|
|
|
|
void test_writeline_class3_enabled_with_col1_one_prefix(void) { |
|
|
const char line[] = "delta\n"; |
|
|
const char sep[] = "+++"; |
|
|
char expected[32]; |
|
|
size_t e_len = 0; |
|
|
memcpy(expected + e_len, sep, strlen(sep)); e_len += strlen(sep); |
|
|
memcpy(expected + e_len, line, strlen(line)); e_len += strlen(line); |
|
|
assert_capture_equals(line, strlen(line), 3, |
|
|
true, |
|
|
false, |
|
|
true, |
|
|
sep, strlen(sep), |
|
|
expected, e_len); |
|
|
} |
|
|
|
|
|
|
|
|
void test_writeline_class3_enabled_with_col2_one_prefix(void) { |
|
|
const char line[] = "epsilon\n"; |
|
|
const char sep[] = "::"; |
|
|
char expected[32]; |
|
|
size_t e_len = 0; |
|
|
memcpy(expected + e_len, sep, strlen(sep)); e_len += strlen(sep); |
|
|
memcpy(expected + e_len, line, strlen(line)); e_len += strlen(line); |
|
|
assert_capture_equals(line, strlen(line), 3, |
|
|
false, |
|
|
true, |
|
|
true, |
|
|
sep, strlen(sep), |
|
|
expected, e_len); |
|
|
} |
|
|
|
|
|
|
|
|
void test_writeline_class3_enabled_with_both_columns_two_prefixes(void) { |
|
|
const char line[] = "zeta\n"; |
|
|
const char sep[] = "::"; |
|
|
char expected[64]; |
|
|
size_t e_len = 0; |
|
|
memcpy(expected + e_len, sep, strlen(sep)); e_len += strlen(sep); |
|
|
memcpy(expected + e_len, sep, strlen(sep)); e_len += strlen(sep); |
|
|
memcpy(expected + e_len, line, strlen(line)); e_len += strlen(line); |
|
|
assert_capture_equals(line, strlen(line), 3, |
|
|
true, |
|
|
true, |
|
|
true, |
|
|
sep, strlen(sep), |
|
|
expected, e_len); |
|
|
} |
|
|
|
|
|
|
|
|
void test_writeline_separator_len_zero_emits_no_prefix(void) { |
|
|
const char line[] = "theta\n"; |
|
|
|
|
|
assert_capture_equals(line, strlen(line), 2, |
|
|
true, |
|
|
true, |
|
|
false, |
|
|
"XYZ", 0 , |
|
|
line, strlen(line)); |
|
|
} |
|
|
|
|
|
int main(void) { |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_writeline_class1_suppressed_no_output); |
|
|
RUN_TEST(test_writeline_class1_enabled_prints_line); |
|
|
RUN_TEST(test_writeline_class2_suppressed_no_output); |
|
|
RUN_TEST(test_writeline_class2_enabled_no_col1_no_prefix); |
|
|
RUN_TEST(test_writeline_class2_enabled_with_col1_prefix_once); |
|
|
RUN_TEST(test_writeline_class3_enabled_no_columns_no_prefix); |
|
|
RUN_TEST(test_writeline_class3_enabled_with_col1_one_prefix); |
|
|
RUN_TEST(test_writeline_class3_enabled_with_col2_one_prefix); |
|
|
RUN_TEST(test_writeline_class3_enabled_with_both_columns_two_prefixes); |
|
|
RUN_TEST(test_writeline_separator_len_zero_emits_no_prefix); |
|
|
return UNITY_END(); |
|
|
} |