|
|
#include "../../unity/unity.h" |
|
|
|
|
|
#include <stdio.h> |
|
|
#include <stdlib.h> |
|
|
#include <string.h> |
|
|
#include <unistd.h> |
|
|
#include <fcntl.h> |
|
|
#include <sys/stat.h> |
|
|
#include <errno.h> |
|
|
#include <limits.h> |
|
|
|
|
|
|
|
|
extern intmax_t skip (int fdesc, char const *file, intmax_t records, idx_t blocksize, idx_t *bytes); |
|
|
|
|
|
|
|
|
extern ssize_t iread (int fd, char *buf, idx_t size); |
|
|
extern int ifd_reopen (int desired_fd, char const *file, int flag, mode_t mode); |
|
|
|
|
|
|
|
|
extern char const *input_file; |
|
|
extern char const *output_file; |
|
|
extern idx_t page_size; |
|
|
extern idx_t input_blocksize; |
|
|
extern idx_t output_blocksize; |
|
|
extern char *ibuf; |
|
|
extern char *obuf; |
|
|
extern off_t input_offset; |
|
|
extern ssize_t (*iread_fnc) (int fd, char *buf, idx_t size); |
|
|
|
|
|
|
|
|
static long get_pagesize_fallback(void) |
|
|
{ |
|
|
long ps = sysconf(_SC_PAGESIZE); |
|
|
if (ps <= 0) ps = 4096; |
|
|
return ps; |
|
|
} |
|
|
|
|
|
static int save_fd(int fd) |
|
|
{ |
|
|
int dupfd = dup(fd); |
|
|
return dupfd; |
|
|
} |
|
|
|
|
|
static void restore_fd(int dstfd, int saved) |
|
|
{ |
|
|
if (saved >= 0) { |
|
|
(void)dup2(saved, dstfd); |
|
|
close(saved); |
|
|
} |
|
|
} |
|
|
|
|
|
static void make_temp_file(char *tmpl_buf, size_t bufsz) |
|
|
{ |
|
|
|
|
|
int fd = mkstemp(tmpl_buf); |
|
|
TEST_ASSERT_MESSAGE(fd >= 0, "mkstemp failed"); |
|
|
close(fd); |
|
|
} |
|
|
|
|
|
static void set_file_size(const char *path, off_t size) |
|
|
{ |
|
|
int fd = open(path, O_RDWR); |
|
|
TEST_ASSERT_MESSAGE(fd >= 0, "open temp file failed"); |
|
|
int r = ftruncate(fd, size); |
|
|
TEST_ASSERT_MESSAGE(r == 0, "ftruncate failed"); |
|
|
close(fd); |
|
|
} |
|
|
|
|
|
static void reopen_stdin_to(const char *path) |
|
|
{ |
|
|
int r = ifd_reopen(STDIN_FILENO, path, O_RDONLY, 0); |
|
|
TEST_ASSERT_MESSAGE(r == 0, "ifd_reopen(stdin) failed"); |
|
|
} |
|
|
|
|
|
static void reopen_stdout_to(const char *path) |
|
|
{ |
|
|
int r = ifd_reopen(STDOUT_FILENO, path, O_WRONLY | O_CREAT | O_TRUNC, 0600); |
|
|
TEST_ASSERT_MESSAGE(r == 0, "ifd_reopen(stdout) failed"); |
|
|
} |
|
|
|
|
|
void setUp(void) { |
|
|
|
|
|
page_size = (idx_t)get_pagesize_fallback(); |
|
|
|
|
|
|
|
|
input_blocksize = 4; |
|
|
output_blocksize = 4; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
iread_fnc = iread; |
|
|
|
|
|
input_offset = 0; |
|
|
input_file = NULL; |
|
|
output_file = NULL; |
|
|
} |
|
|
|
|
|
void tearDown(void) { |
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
void test_skip_stdin_seek_within_file(void) |
|
|
{ |
|
|
int saved_stdin = save_fd(STDIN_FILENO); |
|
|
|
|
|
char path[64] = "/tmp/ddtest1-XXXXXX"; |
|
|
make_temp_file(path, sizeof(path)); |
|
|
set_file_size(path, 100); |
|
|
|
|
|
input_file = path; |
|
|
input_offset = 0; |
|
|
|
|
|
reopen_stdin_to(path); |
|
|
|
|
|
|
|
|
idx_t bytes = 5; |
|
|
intmax_t rem = skip(STDIN_FILENO, input_file, 3, (idx_t)10, &bytes); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(0, rem); |
|
|
|
|
|
TEST_ASSERT_EQUAL_UINT64(5, (unsigned long long)bytes); |
|
|
|
|
|
off_t pos = lseek(STDIN_FILENO, 0, SEEK_CUR); |
|
|
TEST_ASSERT_EQUAL_INT64(35, (long long)pos); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(35, (long long)input_offset); |
|
|
|
|
|
restore_fd(STDIN_FILENO, saved_stdin); |
|
|
unlink(path); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void test_skip_stdin_seek_past_eof(void) |
|
|
{ |
|
|
int saved_stdin = save_fd(STDIN_FILENO); |
|
|
|
|
|
char path[64] = "/tmp/ddtest2-XXXXXX"; |
|
|
make_temp_file(path, sizeof(path)); |
|
|
set_file_size(path, 10); |
|
|
|
|
|
input_file = path; |
|
|
input_offset = 0; |
|
|
|
|
|
reopen_stdin_to(path); |
|
|
|
|
|
|
|
|
idx_t bytes = 0; |
|
|
intmax_t rem = skip(STDIN_FILENO, input_file, 5, (idx_t)4, &bytes); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(2, rem); |
|
|
|
|
|
|
|
|
off_t pos = lseek(STDIN_FILENO, 0, SEEK_CUR); |
|
|
TEST_ASSERT_EQUAL_INT64(20, (long long)pos); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(10, (long long)input_offset); |
|
|
|
|
|
restore_fd(STDIN_FILENO, saved_stdin); |
|
|
unlink(path); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
void test_skip_stdin_fallback_pipe_partial_blocks_and_bytes(void) |
|
|
{ |
|
|
int saved_stdin = save_fd(STDIN_FILENO); |
|
|
|
|
|
int p[2]; |
|
|
TEST_ASSERT_MESSAGE(pipe(p) == 0, "pipe failed"); |
|
|
|
|
|
|
|
|
const char payload[10] = {0}; |
|
|
ssize_t wr = write(p[1], payload, sizeof(payload)); |
|
|
TEST_ASSERT_EQUAL_INT64(10, wr); |
|
|
close(p[1]); |
|
|
|
|
|
|
|
|
(void)dup2(p[0], STDIN_FILENO); |
|
|
close(p[0]); |
|
|
|
|
|
input_file = "pipe"; |
|
|
input_offset = 0; |
|
|
|
|
|
|
|
|
idx_t bytes = 2; |
|
|
intmax_t rem = skip(STDIN_FILENO, input_file, 3, (idx_t)4, &bytes); |
|
|
|
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(0, rem); |
|
|
TEST_ASSERT_EQUAL_UINT64(2, (unsigned long long)bytes); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(10, (long long)input_offset); |
|
|
|
|
|
restore_fd(STDIN_FILENO, saved_stdin); |
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
void test_skip_stdin_fallback_pipe_early_eof_records_remaining(void) |
|
|
{ |
|
|
int saved_stdin = save_fd(STDIN_FILENO); |
|
|
|
|
|
int p[2]; |
|
|
TEST_ASSERT_MESSAGE(pipe(p) == 0, "pipe failed"); |
|
|
|
|
|
|
|
|
const char payload[5] = {0}; |
|
|
ssize_t wr = write(p[1], payload, sizeof(payload)); |
|
|
TEST_ASSERT_EQUAL_INT64(5, wr); |
|
|
close(p[1]); |
|
|
|
|
|
(void)dup2(p[0], STDIN_FILENO); |
|
|
close(p[0]); |
|
|
|
|
|
input_file = "pipe"; |
|
|
input_offset = 0; |
|
|
|
|
|
idx_t bytes = 0; |
|
|
intmax_t rem = skip(STDIN_FILENO, input_file, 3, (idx_t)4, &bytes); |
|
|
|
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(1, rem); |
|
|
TEST_ASSERT_EQUAL_INT64(5, (long long)input_offset); |
|
|
|
|
|
restore_fd(STDIN_FILENO, saved_stdin); |
|
|
} |
|
|
|
|
|
|
|
|
void test_skip_stdout_seekable_with_bytes_zeroed(void) |
|
|
{ |
|
|
int saved_stdout = save_fd(STDOUT_FILENO); |
|
|
|
|
|
char path[64] = "/tmp/ddtest5-XXXXXX"; |
|
|
make_temp_file(path, sizeof(path)); |
|
|
set_file_size(path, 0); |
|
|
|
|
|
output_file = path; |
|
|
reopen_stdout_to(path); |
|
|
|
|
|
|
|
|
idx_t bytes = 1; |
|
|
intmax_t rem = skip(STDOUT_FILENO, output_file, 2, (idx_t)4, &bytes); |
|
|
|
|
|
TEST_ASSERT_EQUAL_INT64(0, rem); |
|
|
TEST_ASSERT_EQUAL_UINT64(0, (unsigned long long)bytes); |
|
|
|
|
|
off_t pos = lseek(STDOUT_FILENO, 0, SEEK_CUR); |
|
|
TEST_ASSERT_EQUAL_INT64(9, (long long)pos); |
|
|
|
|
|
restore_fd(STDOUT_FILENO, saved_stdout); |
|
|
unlink(path); |
|
|
} |
|
|
|
|
|
int main(void) |
|
|
{ |
|
|
UNITY_BEGIN(); |
|
|
|
|
|
RUN_TEST(test_skip_stdin_seek_within_file); |
|
|
RUN_TEST(test_skip_stdin_seek_past_eof); |
|
|
RUN_TEST(test_skip_stdin_fallback_pipe_partial_blocks_and_bytes); |
|
|
RUN_TEST(test_skip_stdin_fallback_pipe_early_eof_records_remaining); |
|
|
RUN_TEST(test_skip_stdout_seekable_with_bytes_zeroed); |
|
|
|
|
|
return UNITY_END(); |
|
|
} |