|
|
#include "../../unity/unity.h" |
|
|
#include <unistd.h> |
|
|
#include <fcntl.h> |
|
|
#include <string.h> |
|
|
#include <stdlib.h> |
|
|
#include <stdio.h> |
|
|
#include <errno.h> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
extern bool have_read_eof; |
|
|
extern struct buffer_record *head; |
|
|
extern char *hold_area; |
|
|
extern idx_t hold_count; |
|
|
extern intmax_t last_line_number; |
|
|
extern intmax_t current_line; |
|
|
|
|
|
|
|
|
static void free_buffer (struct buffer_record *buf); |
|
|
|
|
|
static int saved_stdin = -1; |
|
|
|
|
|
|
|
|
static int write_all(int fd, const void *buf, size_t len) { |
|
|
const char *p = (const char*)buf; |
|
|
size_t off = 0; |
|
|
while (off < len) { |
|
|
ssize_t w = write(fd, p + off, len - off); |
|
|
if (w < 0) { |
|
|
if (errno == EINTR) continue; |
|
|
return -1; |
|
|
} |
|
|
off += (size_t)w; |
|
|
} |
|
|
return 0; |
|
|
} |
|
|
|
|
|
|
|
|
static void set_stdin_from_data(const char *data, size_t len) { |
|
|
int fds[2]; |
|
|
TEST_ASSERT_EQUAL_INT(0, pipe(fds)); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, write_all(fds[1], data, len)); |
|
|
close(fds[1]); |
|
|
|
|
|
TEST_ASSERT_TRUE(fds[0] >= 0); |
|
|
TEST_ASSERT_TRUE(saved_stdin >= 0); |
|
|
TEST_ASSERT_NOT_EQUAL(-1, dup2(fds[0], STDIN_FILENO)); |
|
|
close(fds[0]); |
|
|
} |
|
|
|
|
|
|
|
|
static void free_all_buffers(void) { |
|
|
while (head != NULL) { |
|
|
struct buffer_record *b = head; |
|
|
head = head->next; |
|
|
free_buffer(b); |
|
|
} |
|
|
head = NULL; |
|
|
} |
|
|
|
|
|
|
|
|
static void reset_internal_state(void) { |
|
|
free_all_buffers(); |
|
|
if (hold_area) { |
|
|
free(hold_area); |
|
|
hold_area = NULL; |
|
|
} |
|
|
hold_count = 0; |
|
|
last_line_number = 0; |
|
|
current_line = 0; |
|
|
have_read_eof = false; |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
if (saved_stdin == -1) { |
|
|
saved_stdin = dup(STDIN_FILENO); |
|
|
TEST_ASSERT_TRUE(saved_stdin >= 0); |
|
|
} |
|
|
reset_internal_state(); |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
if (saved_stdin >= 0) { |
|
|
dup2(saved_stdin, STDIN_FILENO); |
|
|
} |
|
|
reset_internal_state(); |
|
|
} |
|
|
|
|
|
|
|
|
void test_load_buffer_empty_input_returns_false(void) { |
|
|
set_stdin_from_data("", 0); |
|
|
|
|
|
bool got = load_buffer(); |
|
|
TEST_ASSERT_FALSE(got); |
|
|
TEST_ASSERT_EQUAL_PTR(NULL, head); |
|
|
TEST_ASSERT_TRUE(have_read_eof); |
|
|
TEST_ASSERT_EQUAL_INT64(0, last_line_number); |
|
|
} |
|
|
|
|
|
|
|
|
void test_load_buffer_reads_multiple_short_lines(void) { |
|
|
const char *data = "a\nb\nc\n"; |
|
|
set_stdin_from_data(data, strlen(data)); |
|
|
|
|
|
bool got = load_buffer(); |
|
|
TEST_ASSERT_TRUE(got); |
|
|
TEST_ASSERT_NOT_NULL(head); |
|
|
TEST_ASSERT_EQUAL_INT(3, head->num_lines); |
|
|
TEST_ASSERT_EQUAL_INT64(1, head->start_line); |
|
|
TEST_ASSERT_EQUAL_INT64(1, head->first_available); |
|
|
|
|
|
|
|
|
struct line *l = head->line_start; |
|
|
TEST_ASSERT_NOT_NULL(l); |
|
|
TEST_ASSERT_TRUE(l->used >= 3); |
|
|
|
|
|
struct cstring line0 = l->starts[0]; |
|
|
struct cstring line1 = l->starts[1]; |
|
|
struct cstring line2 = l->starts[2]; |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(2, (int)line0.len); |
|
|
TEST_ASSERT_EQUAL_INT(2, (int)line1.len); |
|
|
TEST_ASSERT_EQUAL_INT(2, (int)line2.len); |
|
|
TEST_ASSERT_TRUE(memcmp(line0.str, "a\n", 2) == 0); |
|
|
TEST_ASSERT_TRUE(memcmp(line1.str, "b\n", 2) == 0); |
|
|
TEST_ASSERT_TRUE(memcmp(line2.str, "c\n", 2) == 0); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, (int)hold_count); |
|
|
} |
|
|
|
|
|
|
|
|
void test_load_buffer_handles_long_line_larger_than_start_size(void) { |
|
|
size_t long_len = (size_t)START_SIZE * 2 + 10; |
|
|
char *buf = (char*)malloc(long_len + 1); |
|
|
TEST_ASSERT_NOT_NULL(buf); |
|
|
memset(buf, 'x', long_len); |
|
|
buf[long_len - 1] = 'y'; |
|
|
buf[long_len] = '\n'; |
|
|
|
|
|
set_stdin_from_data(buf, long_len + 1); |
|
|
|
|
|
bool got = load_buffer(); |
|
|
TEST_ASSERT_TRUE(got); |
|
|
TEST_ASSERT_NOT_NULL(head); |
|
|
TEST_ASSERT_EQUAL_INT(1, head->num_lines); |
|
|
|
|
|
struct line *l = head->line_start; |
|
|
TEST_ASSERT_NOT_NULL(l); |
|
|
TEST_ASSERT_TRUE(l->used >= 1); |
|
|
struct cstring ln = l->starts[0]; |
|
|
TEST_ASSERT_EQUAL_INT((int)(long_len + 1), (int)ln.len); |
|
|
TEST_ASSERT_EQUAL_CHAR('x', ln.str[0]); |
|
|
TEST_ASSERT_EQUAL_CHAR('y', ln.str[long_len - 1]); |
|
|
TEST_ASSERT_EQUAL_CHAR('\n', ln.str[long_len]); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, (int)hold_count); |
|
|
|
|
|
free(buf); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_load_buffer_incomplete_last_line_requires_second_call(void) { |
|
|
const char *data = "foo\nbar"; |
|
|
set_stdin_from_data(data, strlen(data)); |
|
|
|
|
|
bool got1 = load_buffer(); |
|
|
TEST_ASSERT_TRUE(got1); |
|
|
TEST_ASSERT_NOT_NULL(head); |
|
|
TEST_ASSERT_EQUAL_INT(1, head->num_lines); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(3, (int)hold_count); |
|
|
TEST_ASSERT_NOT_NULL(hold_area); |
|
|
TEST_ASSERT_TRUE(memcmp(hold_area, "bar", 3) == 0); |
|
|
TEST_ASSERT_FALSE(have_read_eof); |
|
|
|
|
|
|
|
|
bool got2 = load_buffer(); |
|
|
TEST_ASSERT_TRUE(got2); |
|
|
|
|
|
|
|
|
TEST_ASSERT_NOT_NULL(head->next); |
|
|
struct buffer_record *second = head->next; |
|
|
TEST_ASSERT_EQUAL_INT(1, second->num_lines); |
|
|
|
|
|
struct line *l2 = second->line_start; |
|
|
TEST_ASSERT_NOT_NULL(l2); |
|
|
struct cstring last = l2->starts[0]; |
|
|
TEST_ASSERT_EQUAL_INT(3, (int)last.len); |
|
|
TEST_ASSERT_TRUE(memcmp(last.str, "bar", 3) == 0); |
|
|
|
|
|
TEST_ASSERT_TRUE(have_read_eof); |
|
|
TEST_ASSERT_EQUAL_INT(0, (int)hold_count); |
|
|
} |
|
|
|
|
|
|
|
|
void test_load_buffer_single_incomplete_line_only(void) { |
|
|
const char *data = "xyz"; |
|
|
set_stdin_from_data(data, strlen(data)); |
|
|
|
|
|
bool got = load_buffer(); |
|
|
TEST_ASSERT_TRUE(got); |
|
|
TEST_ASSERT_NOT_NULL(head); |
|
|
TEST_ASSERT_EQUAL_INT(1, head->num_lines); |
|
|
|
|
|
struct line *l = head->line_start; |
|
|
TEST_ASSERT_NOT_NULL(l); |
|
|
struct cstring ln = l->starts[0]; |
|
|
TEST_ASSERT_EQUAL_INT(3, (int)ln.len); |
|
|
TEST_ASSERT_TRUE(memcmp(ln.str, "xyz", 3) == 0); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT(0, (int)hold_count); |
|
|
} |
|
|
|
|
|
|
|
|
int main(void) { |
|
|
UNITY_BEGIN(); |
|
|
RUN_TEST(test_load_buffer_empty_input_returns_false); |
|
|
RUN_TEST(test_load_buffer_reads_multiple_short_lines); |
|
|
RUN_TEST(test_load_buffer_handles_long_line_larger_than_start_size); |
|
|
RUN_TEST(test_load_buffer_incomplete_last_line_requires_second_call); |
|
|
RUN_TEST(test_load_buffer_single_incomplete_line_only); |
|
|
return UNITY_END(); |
|
|
} |