coreutils / tests /od /tests_for_read_block.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 <stdint.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
/* Unity hooks */
void setUp(void) {
/* Reset relevant globals to safe defaults between tests */
in_stream = NULL;
have_read_stdin = false;
/* Preserve other program globals at their defaults; set per-test as needed */
}
void tearDown(void) {
/* Ensure any open stream is closed to not leak across tests */
if (in_stream) {
check_and_close(0);
}
}
/* Helper: create a temporary file with provided content; returns heap-allocated path */
static char* create_temp_file_with_content(const char* content) {
const char* tmpdir = getenv("TMPDIR");
if (!tmpdir || !*tmpdir) tmpdir = "/tmp";
size_t need = strlen(tmpdir) + 1 + strlen("od_utXXXXXX") + 1;
char* tmpl = (char*)malloc(need);
TEST_ASSERT_NOT_NULL(tmpl);
snprintf(tmpl, need, "%s/%s", tmpdir, "od_utXXXXXX");
int fd = mkstemp(tmpl);
TEST_ASSERT_MESSAGE(fd >= 0, "mkstemp failed creating temporary file");
ssize_t len = (ssize_t)strlen(content);
if (len > 0) {
ssize_t w = write(fd, content, (size_t)len);
TEST_ASSERT_EQUAL_INT64((int64_t)len, (int64_t)w);
}
close(fd);
return tmpl; /* caller must unlink() and free() */
}
/* Helper: initialize reading from first file 'path_cur', with remaining files list 'rest' */
static void init_stream_and_file_list(const char* path_cur, const char* const* rest) {
/* Open current file and position file_list to the next (so file_list[-1] is current) */
FILE* f = fopen(path_cur, (O_BINARY ? "rb" : "r"));
TEST_ASSERT_NOT_NULL_MESSAGE(f, "Failed to open initial file");
in_stream = f;
input_filename = path_cur;
/* Build a contiguous array: arr[0] = current, arr[1..k] = rest..., arr[k+1] = NULL.
Then set file_list = &arr[1], so file_list[-1] == current. */
/* Count rest length */
size_t k = 0;
if (rest) {
while (rest[k] != NULL) k++;
}
const char** arr = (const char**)malloc((k + 2) * sizeof(const char*));
TEST_ASSERT_NOT_NULL(arr);
arr[0] = path_cur;
for (size_t i = 0; i < k; i++) arr[i + 1] = rest[i];
arr[k + 1] = NULL;
file_list = arr + 1;
/* Note: we malloc'd the array. We'll free it in the test after we're done using file_list. */
}
/* Helper: free the malloc'd file_list array created by init_stream_and_file_list */
static void free_file_list_array(void) {
/* file_list points into arr + 1; free the base pointer (file_list - 1) */
if (file_list) {
const char** base = file_list - 1;
free((void*)base);
file_list = NULL;
}
}
/* Test 1: read exact N bytes from a single file */
static void test_read_block_single_file_exact(void) {
bytes_per_block = 16; /* ensure n <= bytes_per_block */
char* p1 = create_temp_file_with_content("ABCDEFGH");
const char* rest[] = { NULL };
init_stream_and_file_list(p1, rest);
char buf[16];
memset(buf, 0, sizeof(buf));
idx_t got = 0;
bool ok = read_block(4, buf, &got);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_EQUAL_UINT64((uint64_t)4, (uint64_t)got);
TEST_ASSERT_EQUAL_INT(0, memcmp(buf, "ABCD", 4));
/* Cleanup */
check_and_close(0);
free_file_list_array();
unlink(p1);
free(p1);
}
/* Test 2: read across file boundary into the next file */
static void test_read_block_cross_file_boundary(void) {
bytes_per_block = 16;
char* p1 = create_temp_file_with_content("ABC"); /* 3 bytes */
char* p2 = create_temp_file_with_content("DEFG"); /* 4 bytes */
const char* rest[] = { p2, NULL };
init_stream_and_file_list(p1, rest);
char buf[16];
memset(buf, 0, sizeof(buf));
idx_t got = 0;
/* Request 5 bytes: should read 3 from p1 and 2 from p2 */
bool ok = read_block(5, buf, &got);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_EQUAL_UINT64((uint64_t)5, (uint64_t)got);
TEST_ASSERT_EQUAL_INT(0, memcmp(buf, "ABCDE", 5));
/* Subsequent call should continue from p2 (offset 2), reading next 2 bytes "FG" */
char buf2[8];
memset(buf2, 0, sizeof(buf2));
idx_t got2 = 0;
ok = read_block(2, buf2, &got2);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_EQUAL_UINT64((uint64_t)2, (uint64_t)got2);
TEST_ASSERT_EQUAL_INT(0, memcmp(buf2, "FG", 2));
/* Cleanup */
check_and_close(0);
free_file_list_array();
unlink(p1);
unlink(p2);
free(p1);
free(p2);
}
/* Test 3: partial read at end-of-input and subsequent call returns zero bytes */
static void test_read_block_partial_then_eof_then_zero_on_next_call(void) {
bytes_per_block = 16;
char* p1 = create_temp_file_with_content("HI"); /* 2 bytes */
const char* rest[] = { NULL };
init_stream_and_file_list(p1, rest);
char buf[16];
memset(buf, 0, sizeof(buf));
idx_t got = 0;
/* Request 5 bytes: only 2 available in total */
bool ok = read_block(5, buf, &got);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_EQUAL_UINT64((uint64_t)2, (uint64_t)got);
TEST_ASSERT_EQUAL_INT(0, memcmp(buf, "HI", 2));
/* Next call: in_stream should be NULL; expect 0 bytes returned, ok==true */
char buf2[8];
memset(buf2, 0xAA, sizeof(buf2)); /* fill to detect modification */
idx_t got2 = 0;
ok = read_block(3, buf2, &got2);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_EQUAL_UINT64((uint64_t)0, (uint64_t)got2);
/* Do not assert on buf2 contents, only that it wasn't written (contract says don't modify block) */
/* Cleanup */
/* in_stream is already NULL after previous call, but call to close is safe */
check_and_close(0);
free_file_list_array();
unlink(p1);
free(p1);
}
/* Test 4: failure to open the next file causes read_block to return false,
while still returning the bytes already read */
static void test_read_block_next_file_open_failure(void) {
bytes_per_block = 16;
char* p1 = create_temp_file_with_content("A"); /* 1 byte */
/* Nonexistent path; choose a path unlikely to exist */
const char* bad_path = "/no_such_od_test_file_path_hopefully_123456789";
const char* rest[] = { bad_path, NULL };
init_stream_and_file_list(p1, rest);
char buf[8];
memset(buf, 0, sizeof(buf));
idx_t got = 0;
/* Request 2 bytes: will read 1 from p1; next file fails to open => ok == false */
bool ok = read_block(2, buf, &got);
TEST_ASSERT_FALSE(ok);
TEST_ASSERT_EQUAL_UINT64((uint64_t)1, (uint64_t)got);
TEST_ASSERT_EQUAL_INT(0, memcmp(buf, "A", 1));
/* Cleanup (in_stream should be NULL by now) */
check_and_close(0);
free_file_list_array();
unlink(p1);
free(p1);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_read_block_single_file_exact);
RUN_TEST(test_read_block_cross_file_boundary);
RUN_TEST(test_read_block_partial_then_eof_then_zero_on_next_call);
RUN_TEST(test_read_block_next_file_open_failure);
return UNITY_END();
}