|
|
#include "../../unity/unity.h" |
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <unistd.h> |
|
|
#include <errno.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern void write_block (intmax_t current_offset, idx_t n_bytes, |
|
|
char const *prev_block, char const *curr_block); |
|
|
extern void format_address_none (intmax_t, char); |
|
|
extern void print_double (idx_t fields, idx_t blank, void const *data, |
|
|
const char *fmt, int width, idx_t pad); |
|
|
|
|
|
|
|
|
extern struct tspec *spec; |
|
|
extern idx_t n_specs; |
|
|
extern idx_t n_specs_allocated; |
|
|
extern idx_t bytes_per_block; |
|
|
extern bool abbreviate_duplicate_blocks; |
|
|
extern int address_pad_len; |
|
|
extern void (*format_address) (intmax_t, char); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static void configure_spec_zero_fields(int count) |
|
|
{ |
|
|
|
|
|
if (spec) { |
|
|
free(spec); |
|
|
spec = NULL; |
|
|
} |
|
|
n_specs_allocated = 0; |
|
|
|
|
|
spec = (struct tspec *)calloc((size_t)count, sizeof(*spec)); |
|
|
TEST_ASSERT_NOT_NULL_MESSAGE(spec, "Failed to allocate spec array"); |
|
|
n_specs = (idx_t)count; |
|
|
|
|
|
for (int i = 0; i < count; i++) { |
|
|
spec[i].fmt = FLOATING_POINT; |
|
|
spec[i].size = FLOAT_DOUBLE; |
|
|
spec[i].print_function = print_double; |
|
|
spec[i].field_width = 0; |
|
|
spec[i].pad_width = 0; |
|
|
spec[i].hexl_mode_trailer = false; |
|
|
spec[i].fmt_string[0] = '\0'; |
|
|
} |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
typedef struct { |
|
|
FILE *cap_file; |
|
|
int saved_fd; |
|
|
int cap_fd; |
|
|
int err; |
|
|
} Capture; |
|
|
|
|
|
static void capture_begin(Capture *c) |
|
|
{ |
|
|
memset(c, 0, sizeof(*c)); |
|
|
fflush(stdout); |
|
|
c->cap_file = tmpfile(); |
|
|
if (!c->cap_file) { |
|
|
c->err = errno ? errno : -1; |
|
|
return; |
|
|
} |
|
|
c->cap_fd = fileno(c->cap_file); |
|
|
if (c->cap_fd < 0) { |
|
|
c->err = errno ? errno : -2; |
|
|
return; |
|
|
} |
|
|
c->saved_fd = dup(fileno(stdout)); |
|
|
if (c->saved_fd < 0) { |
|
|
c->err = errno ? errno : -3; |
|
|
return; |
|
|
} |
|
|
if (dup2(c->cap_fd, fileno(stdout)) < 0) { |
|
|
c->err = errno ? errno : -4; |
|
|
|
|
|
(void)dup2(c->saved_fd, fileno(stdout)); |
|
|
return; |
|
|
} |
|
|
} |
|
|
|
|
|
static char *capture_end(Capture *c) |
|
|
{ |
|
|
fflush(stdout); |
|
|
|
|
|
if (c->saved_fd >= 0) |
|
|
(void)dup2(c->saved_fd, fileno(stdout)); |
|
|
if (c->saved_fd >= 0) { |
|
|
close(c->saved_fd); |
|
|
c->saved_fd = -1; |
|
|
} |
|
|
|
|
|
if (!c->cap_file) { |
|
|
return strdup(""); |
|
|
} |
|
|
|
|
|
|
|
|
fseek(c->cap_file, 0, SEEK_END); |
|
|
long len = ftell(c->cap_file); |
|
|
if (len < 0) len = 0; |
|
|
fseek(c->cap_file, 0, SEEK_SET); |
|
|
|
|
|
char *buf = (char *)malloc((size_t)len + 1); |
|
|
if (!buf) { |
|
|
fclose(c->cap_file); |
|
|
return strdup(""); |
|
|
} |
|
|
size_t n = fread(buf, 1, (size_t)len, c->cap_file); |
|
|
buf[n] = '\0'; |
|
|
fclose(c->cap_file); |
|
|
c->cap_file = NULL; |
|
|
return buf; |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
|
|
|
format_address = format_address_none; |
|
|
address_pad_len = 0; |
|
|
abbreviate_duplicate_blocks = true; |
|
|
bytes_per_block = 4; |
|
|
|
|
|
|
|
|
|
|
|
configure_spec_zero_fields(1); |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
if (spec) { |
|
|
free(spec); |
|
|
spec = NULL; |
|
|
} |
|
|
n_specs = 0; |
|
|
n_specs_allocated = 0; |
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_write_block_duplicate_elision_sequence(void) |
|
|
{ |
|
|
|
|
|
format_address = format_address_none; |
|
|
address_pad_len = 0; |
|
|
abbreviate_duplicate_blocks = true; |
|
|
bytes_per_block = 4; |
|
|
configure_spec_zero_fields(1); |
|
|
|
|
|
char A[4] = {1,2,3,4}; |
|
|
char B[4] = {9,8,7,6}; |
|
|
|
|
|
Capture cap; capture_begin(&cap); |
|
|
|
|
|
write_block(0, 4, A, A); |
|
|
write_block(4, 4, A, A); |
|
|
write_block(8, 4, A, A); |
|
|
write_block(12, 4, A, B); |
|
|
char *out = capture_end(&cap); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, cap.err, "capture_begin failed"); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_STRING("\n*\n\n", out); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_write_block_no_elision_when_disabled(void) |
|
|
{ |
|
|
abbreviate_duplicate_blocks = false; |
|
|
format_address = format_address_none; |
|
|
address_pad_len = 0; |
|
|
bytes_per_block = 4; |
|
|
configure_spec_zero_fields(1); |
|
|
|
|
|
char A[4] = {1,1,1,1}; |
|
|
|
|
|
Capture cap; capture_begin(&cap); |
|
|
write_block(0, 4, A, A); |
|
|
write_block(4, 4, A, A); |
|
|
char *out = capture_end(&cap); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, cap.err, "capture_begin failed"); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_STRING("\n\n", out); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_write_block_no_elision_when_partial_block(void) |
|
|
{ |
|
|
abbreviate_duplicate_blocks = true; |
|
|
format_address = format_address_none; |
|
|
address_pad_len = 0; |
|
|
bytes_per_block = 4; |
|
|
configure_spec_zero_fields(1); |
|
|
|
|
|
char A[4] = {5,5,5,5}; |
|
|
|
|
|
Capture cap; capture_begin(&cap); |
|
|
write_block(0, 3, A, A); |
|
|
write_block(3, 3, A, A); |
|
|
char *out = capture_end(&cap); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, cap.err, "capture_begin failed"); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_STRING("\n\n", out); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void test_write_block_multiple_specs_two_lines(void) |
|
|
{ |
|
|
abbreviate_duplicate_blocks = true; |
|
|
format_address = format_address_none; |
|
|
address_pad_len = 0; |
|
|
bytes_per_block = 4; |
|
|
configure_spec_zero_fields(2); |
|
|
|
|
|
char A[4] = {1,2,3,4}; |
|
|
char B[4] = {4,3,2,1}; |
|
|
|
|
|
Capture cap; capture_begin(&cap); |
|
|
write_block(0, 4, A, B); |
|
|
char *out = capture_end(&cap); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, cap.err, "capture_begin failed"); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
TEST_ASSERT_EQUAL_STRING("\n\n", out); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void test_write_block_address_pad_for_subsequent_specs(void) |
|
|
{ |
|
|
abbreviate_duplicate_blocks = true; |
|
|
format_address = format_address_none; |
|
|
address_pad_len = 3; |
|
|
bytes_per_block = 4; |
|
|
configure_spec_zero_fields(2); |
|
|
|
|
|
char P[4] = {7,7,7,7}; |
|
|
char Q[4] = {8,8,8,8}; |
|
|
|
|
|
Capture cap; capture_begin(&cap); |
|
|
write_block(0, 4, P, Q); |
|
|
char *out = capture_end(&cap); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT_MESSAGE(0, cap.err, "capture_begin failed"); |
|
|
TEST_ASSERT_NOT_NULL(out); |
|
|
|
|
|
TEST_ASSERT_EQUAL_STRING("\n \n", out); |
|
|
free(out); |
|
|
} |
|
|
|
|
|
int main(void) |
|
|
{ |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_write_block_duplicate_elision_sequence); |
|
|
RUN_TEST(test_write_block_no_elision_when_disabled); |
|
|
RUN_TEST(test_write_block_no_elision_when_partial_block); |
|
|
RUN_TEST(test_write_block_multiple_specs_two_lines); |
|
|
RUN_TEST(test_write_block_address_pad_for_subsequent_specs); |
|
|
return UNITY_END(); |
|
|
} |