coreutils / tests /dd /tests_for_skip.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#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();
}