File size: 6,219 Bytes
78d2150 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
#include "../../unity/unity.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
/* The test file is included into the od source, so we can access
the internal globals and functions directly. We declare externs only
for clarity; they are defined above in the included program. */
/* Globals from od.c (visible since this test is included into the TU) */
extern FILE *in_stream;
extern char const *const *file_list;
extern char const *input_filename;
extern bool have_read_stdin;
extern intmax_t end_offset;
extern bool flag_dump_strings;
/* Functions from od.c */
extern bool open_next_file (void);
extern bool check_and_close (int in_errno);
extern bool skip (intmax_t n_skip);
/* Helpers for tests */
static char *create_temp_file_with_size(size_t size) {
char tmpl[] = "/tmp/od_skip_testXXXXXX";
int fd = mkstemp(tmpl);
if (fd < 0) {
return NULL;
}
/* Write 'size' bytes of predictable data */
size_t remaining = size;
char buf[4096];
memset(buf, 'A', sizeof(buf));
while (remaining > 0) {
size_t chunk = remaining < sizeof(buf) ? remaining : sizeof(buf);
ssize_t w = write(fd, buf, chunk);
if (w < 0) {
int e = errno;
close(fd);
unlink(tmpl);
errno = e;
return NULL;
}
remaining -= (size_t)w;
}
if (fsync(fd) != 0) {
/* Not fatal for tests, continue */
}
if (close(fd) != 0) {
unlink(tmpl);
return NULL;
}
/* Return a heap-allocated copy of the path so caller can free */
char *path = strdup(tmpl);
return path;
}
static long current_offset(FILE *f) {
off_t off = ftello(f);
if (off == (off_t)-1) return -1;
return (long)off;
}
void setUp(void) {
/* Reset core globals to a safe baseline for each test. */
in_stream = NULL;
have_read_stdin = false;
end_offset = -1; /* No end limit */
flag_dump_strings = false;
}
void tearDown(void) {
/* If a stream is still open, close it safely. Note: in tests, we
prefer to close via check_and_close within each test before
local arrays (for file_list) go out of scope. */
if (in_stream) {
check_and_close(0);
in_stream = NULL;
}
}
/* Test: n_skip == 0 is a no-op and returns true, without requiring an open stream. */
static void test_skip_zero_no_stream(void) {
in_stream = NULL;
bool ok = skip(0);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NULL(in_stream);
}
/* Test: Skip within a single large file using seek optimization. */
static void test_skip_within_single_file_seek(void) {
char *p1 = create_temp_file_with_size(100000); /* large to trigger seek path */
TEST_ASSERT_NOT_NULL_MESSAGE(p1, "failed to create temp file");
/* Set up file list and open first file */
const char *flist[] = { p1, NULL };
file_list = flist;
bool ok = open_next_file();
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(in_stream);
TEST_ASSERT_EQUAL_INT(0, current_offset(in_stream));
ok = skip(12345);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(in_stream);
TEST_ASSERT_EQUAL_INT(12345, current_offset(in_stream));
/* Cleanup while file_list is still valid */
check_and_close(0);
unlink(p1);
free(p1);
}
/* Test: Skip across two files, requiring skipping past the entire first file
and partially into the second file. */
static void test_skip_across_two_files_boundary_seek_on_first(void) {
char *p1 = create_temp_file_with_size(5000);
char *p2 = create_temp_file_with_size(2000);
TEST_ASSERT_NOT_NULL(p1);
TEST_ASSERT_NOT_NULL(p2);
const char *flist[] = { p1, p2, NULL };
file_list = flist;
bool ok = open_next_file();
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(in_stream);
TEST_ASSERT_EQUAL_INT(0, current_offset(in_stream));
/* Skip more than file1, but less than total */
ok = skip(5200);
TEST_ASSERT_TRUE(ok);
/* Expect to be in the second file at offset 200. */
TEST_ASSERT_NOT_NULL(in_stream);
long off = current_offset(in_stream);
TEST_ASSERT_EQUAL_INT(200, off);
/* Cleanup */
check_and_close(0);
unlink(p1); unlink(p2);
free(p1); free(p2);
}
/* Test: Read-based skipping on a small regular file (size <= block size),
exercising the fread path. */
static void test_skip_read_based_small_file(void) {
char *p1 = create_temp_file_with_size(10);
TEST_ASSERT_NOT_NULL(p1);
const char *flist[] = { p1, NULL };
file_list = flist;
bool ok = open_next_file();
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(in_stream);
TEST_ASSERT_EQUAL_INT(0, current_offset(in_stream));
ok = skip(6);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_EQUAL_INT(6, current_offset(in_stream));
/* Cleanup */
check_and_close(0);
unlink(p1);
free(p1);
}
/* Test: Skipping exactly the size of the first file remains at EOF of first file.
The code seeks within the first file and does not immediately open the next. */
static void test_skip_exact_file_size_stays_on_first_at_eof(void) {
char *p1 = create_temp_file_with_size(1000);
char *p2 = create_temp_file_with_size(100);
TEST_ASSERT_NOT_NULL(p1);
TEST_ASSERT_NOT_NULL(p2);
const char *flist[] = { p1, p2, NULL };
file_list = flist;
bool ok = open_next_file();
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(in_stream);
ok = skip(1000);
TEST_ASSERT_TRUE(ok);
/* Expect still on first file at offset 1000 (EOF), not yet transitioned */
TEST_ASSERT_NOT_NULL(in_stream);
TEST_ASSERT_EQUAL_INT(1000, current_offset(in_stream));
/* Cleanup */
check_and_close(0);
unlink(p1); unlink(p2);
free(p1); free(p2);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_skip_zero_no_stream);
RUN_TEST(test_skip_within_single_file_seek);
RUN_TEST(test_skip_across_two_files_boundary_seek_on_first);
RUN_TEST(test_skip_read_based_small_file);
RUN_TEST(test_skip_exact_file_size_stays_on_first_at_eof);
return UNITY_END();
} |