File size: 8,373 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 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
#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>
/* Access to dd.c internals since this test file is included into dd.c */
extern intmax_t skip (int fdesc, char const *file, intmax_t records, idx_t blocksize, idx_t *bytes);
/* The following variables/functions are defined in dd.c and are accessible here */
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);
/* Globals from dd.c (file-scope static there, but visible here via inclusion) */
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);
/* Helpers */
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)
{
/* Caller should provide a buffer like "/tmp/ddtest-XXXXXX" */
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) {
/* Ensure environment */
page_size = (idx_t)get_pagesize_fallback();
/* Set a consistent input/output blocksize before any allocation occurs. */
input_blocksize = 4;
output_blocksize = 4;
/* Ensure buffers are not yet allocated by our tests unless needed. */
/* ibuf/obuf are static in dd.c; if already allocated by previous tests,
we'll keep using the same blocksize (4) to avoid mismatch. */
/* Set default function pointer for reading. */
iread_fnc = iread;
input_offset = 0;
input_file = NULL;
output_file = NULL;
}
void tearDown(void) {
/* Nothing special to clean here; each test restores fds and unlinks files. */
}
/* Test 1: seekable STDIN, lseek succeeds within file (no EOF overrun). */
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);
/* Target: records=3, blocksize=10, bytes=5 => offset 35 (within 100). */
idx_t bytes = 5;
intmax_t rem = skip(STDIN_FILENO, input_file, 3, (idx_t)10, &bytes);
TEST_ASSERT_EQUAL_INT64(0, rem);
/* bytes is not specified to change for STDIN lseek path; ensure it's unchanged */
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);
/* input_offset should have advanced by 35 */
TEST_ASSERT_EQUAL_INT64(35, (long long)input_offset);
restore_fd(STDIN_FILENO, saved_stdin);
unlink(path);
}
/* Test 2: seekable STDIN, lseek succeeds but skipping past EOF.
Expect returned remaining full records equal to overrun/blocksize,
and input_offset advanced only up to EOF. */
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); /* 10 bytes */
input_file = path;
input_offset = 0;
reopen_stdin_to(path);
/* records=5, blocksize=4 => 20 bytes; past EOF by 10; so remaining records = (20-10)/4 = 2 */
idx_t bytes = 0;
intmax_t rem = skip(STDIN_FILENO, input_file, 5, (idx_t)4, &bytes);
TEST_ASSERT_EQUAL_INT64(2, rem);
/* File position was lseek'd to requested offset (20) */
off_t pos = lseek(STDIN_FILENO, 0, SEEK_CUR);
TEST_ASSERT_EQUAL_INT64(20, (long long)pos);
/* input_offset advanced only by available data (10) */
TEST_ASSERT_EQUAL_INT64(10, (long long)input_offset);
restore_fd(STDIN_FILENO, saved_stdin);
unlink(path);
}
/* Test 3: non-seekable STDIN (pipe), fallback reading.
Provide less data than requested so that some extra bytes remain unskipped.
Expect returned remaining records 0, but bytes remain nonzero. */
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");
/* Write 10 bytes total to the pipe */
const char payload[10] = {0};
ssize_t wr = write(p[1], payload, sizeof(payload));
TEST_ASSERT_EQUAL_INT64(10, wr);
close(p[1]); /* EOF after 10 bytes */
/* Make read end be STDIN */
(void)dup2(p[0], STDIN_FILENO);
close(p[0]);
input_file = "pipe";
input_offset = 0;
/* Request: records=3, blocksize=4 (12 bytes), then bytes=2 => total 14; only 10 available */
idx_t bytes = 2;
intmax_t rem = skip(STDIN_FILENO, input_file, 3, (idx_t)4, &bytes);
/* With the implementation, records becomes 0 even though the last block was partial,
and since EOF occurred before skipping the extra bytes, bytes remains nonzero (2). */
TEST_ASSERT_EQUAL_INT64(0, rem);
TEST_ASSERT_EQUAL_UINT64(2, (unsigned long long)bytes);
/* input_offset advanced by exactly the bytes read */
TEST_ASSERT_EQUAL_INT64(10, (long long)input_offset);
restore_fd(STDIN_FILENO, saved_stdin);
}
/* Test 4: non-seekable STDIN (pipe), EOF before finishing all records.
Expect returned remaining records to be >0. */
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");
/* Write only 5 bytes; request 3 records of 4 bytes (12 total) */
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);
/* We read 4 (records->2), then 1 (records->1), then EOF => remaining records should be 1 */
TEST_ASSERT_EQUAL_INT64(1, rem);
TEST_ASSERT_EQUAL_INT64(5, (long long)input_offset);
restore_fd(STDIN_FILENO, saved_stdin);
}
/* Test 5: seekable STDOUT, lseek succeeds; expect bytes cleared and 0 remaining records. */
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);
/* Seek 2 records of 4 plus 1 byte; on success for output, bytes is set to 0 */
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();
} |