| | #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(); |
| | } |