coreutils / tests /od /tests_for_dump_strings.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 <stdbool.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
/* We rely on inclusion into the od translation unit so we can access
internal globals and functions directly. */
/* Prototypes for internal helpers used from od source (visible here due to inclusion): */
static bool dump_strings(void);
static bool open_next_file(void);
/* Internal globals from od we will set up in tests. */
extern int address_base; /* default 8 */
extern int address_pad_len; /* default 7 */
extern bool flag_dump_strings; /* controls buffering path in open_next_file */
extern intmax_t n_bytes_to_skip; /* starting address */
extern intmax_t end_offset; /* termination offset or -1 */
extern idx_t string_min; /* minimum printable run length */
extern FILE *in_stream; /* current input stream */
extern char const *const *file_list; /* list of file names */
extern void (*format_address)(intmax_t, char);
static void format_address_std (intmax_t, char); /* from od */
/* ---------- Test utilities ---------- */
/* Create a temporary file with the given bytes. Returns malloc'd path string. */
static char *testutil_make_temp_file(const unsigned char *data, size_t len)
{
char tmpl[] = "od_test_XXXXXX";
int fd = mkstemp(tmpl);
TEST_ASSERT_TRUE_MESSAGE(fd >= 0, "mkstemp failed");
ssize_t written = 0;
while ((size_t)written < len) {
ssize_t w = write(fd, data + written, len - written);
TEST_ASSERT_TRUE_MESSAGE(w >= 0, "write failed");
written += w;
}
int rc = close(fd);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, rc, "close temp file failed");
/* Return a copy so caller can free after unlink */
char *ret = (char *)malloc(strlen(tmpl) + 1);
TEST_ASSERT_NOT_NULL(ret);
strcpy(ret, tmpl);
return ret;
}
/* Build a heap-allocated, NULL-terminated file list for od's globals. */
static const char **testutil_build_file_list(char **paths, size_t n)
{
const char **list = (const char **)malloc((n + 1) * sizeof(*list));
TEST_ASSERT_NOT_NULL(list);
for (size_t i = 0; i < n; i++) list[i] = paths[i];
list[n] = NULL;
return list;
}
/* Capture stdout while running dump_strings. Return malloc'd buffer with output. */
static char *testutil_capture_dump_strings(bool *ok_out)
{
/* Save current stdout */
int saved_fd = dup(fileno(stdout));
TEST_ASSERT_TRUE_MESSAGE(saved_fd >= 0, "dup(stdout) failed");
FILE *cap = tmpfile();
TEST_ASSERT_NOT_NULL_MESSAGE(cap, "tmpfile failed");
int cap_fd = fileno(cap);
TEST_ASSERT_TRUE_MESSAGE(cap_fd >= 0, "fileno(tmpfile) failed");
fflush(stdout);
int rc = dup2(cap_fd, fileno(stdout));
TEST_ASSERT_TRUE_MESSAGE(rc >= 0, "dup2 to stdout failed");
/* Call function under test without using Unity assertions while redirected */
bool ok = dump_strings();
fflush(stdout);
/* Restore stdout before doing any TEST_ASSERTs */
rc = dup2(saved_fd, fileno(stdout));
TEST_ASSERT_TRUE_MESSAGE(rc >= 0, "restore stdout failed");
close(saved_fd);
/* Read captured output */
long sz;
int fseek_rc = fseek(cap, 0, SEEK_END);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, fseek_rc, "fseek end failed");
sz = ftell(cap);
TEST_ASSERT_TRUE_MESSAGE(sz >= 0, "ftell failed");
fseek_rc = fseek(cap, 0, SEEK_SET);
TEST_ASSERT_EQUAL_INT_MESSAGE(0, fseek_rc, "fseek set failed");
char *buf = (char *)malloc((size_t)sz + 1);
TEST_ASSERT_NOT_NULL(buf);
size_t rd = fread(buf, 1, (size_t)sz, cap);
TEST_ASSERT_EQUAL_UINT_MESSAGE((size_t)sz, rd, "fread captured output failed");
buf[sz] = '\0';
fclose(cap);
if (ok_out) *ok_out = ok;
return buf;
}
/* Common setup before each test */
void setUp(void)
{
/* Reset key globals to known defaults for each test */
in_stream = NULL;
n_bytes_to_skip = 0;
end_offset = -1;
string_min = 0;
flag_dump_strings = true; /* ensure open_next_file doesn't force unbuffered */
address_base = 8;
address_pad_len = 7;
format_address = format_address_std;
}
void tearDown(void)
{
/* Nothing to clean globally here */
}
/* ---------- Tests ---------- */
/* Basic: single NUL-terminated string, length >= string_min */
void test_dump_strings_basic_nul_terminated(void)
{
const unsigned char data[] = { 'a','b','c','\0' };
char *path = testutil_make_temp_file(data, sizeof data);
char *paths_arr[] = { path };
const char **list = testutil_build_file_list(paths_arr, 1);
file_list = list;
/* Configure parameters */
string_min = 3;
n_bytes_to_skip = 0;
end_offset = -1;
/* Open first file */
bool open_ok = open_next_file();
TEST_ASSERT_TRUE(open_ok);
bool ok = false;
char *out = testutil_capture_dump_strings(&ok);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("0000000 abc\n", out);
/* Cleanup */
free(out);
unlink(path);
free(path);
free((void*)list);
}
/* Strings shorter than string_min are ignored */
void test_dump_strings_minlen_not_met(void)
{
const unsigned char data[] = { 'a','b','\0' }; /* length 2 before NUL */
char *path = testutil_make_temp_file(data, sizeof data);
char *paths_arr[] = { path };
const char **list = testutil_build_file_list(paths_arr, 1);
file_list = list;
string_min = 3;
n_bytes_to_skip = 0;
end_offset = -1;
bool open_ok = open_next_file();
TEST_ASSERT_TRUE(open_ok);
bool ok = false;
char *out = testutil_capture_dump_strings(&ok);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("", out);
free(out);
unlink(path);
free(path);
free((void*)list);
}
/* End offset termination without NUL, string should print if >= string_min */
void test_dump_strings_end_offset_termination(void)
{
const unsigned char data[] = { 'a','b','c','d','e','f' }; /* 6 bytes */
char *path = testutil_make_temp_file(data, sizeof data);
char *paths_arr[] = { path };
const char **list = testutil_build_file_list(paths_arr, 1);
file_list = list;
string_min = 3;
n_bytes_to_skip = 0;
end_offset = 6; /* terminate exactly after 6 bytes */
bool open_ok = open_next_file();
TEST_ASSERT_TRUE(open_ok);
bool ok = false;
char *out = testutil_capture_dump_strings(&ok);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("0000000 abcdef\n", out);
free(out);
unlink(path);
free(path);
free((void*)list);
}
/* Non-zero starting address via n_bytes_to_skip affects printed address */
void test_dump_strings_start_offset_address(void)
{
const unsigned char data[] = { 'a','b','c','\0' };
char *path = testutil_make_temp_file(data, sizeof data);
char *paths_arr[] = { path };
const char **list = testutil_build_file_list(paths_arr, 1);
file_list = list;
string_min = 3;
n_bytes_to_skip = 2; /* start address offset */
end_offset = -1;
bool open_ok = open_next_file();
TEST_ASSERT_TRUE(open_ok);
bool ok = false;
char *out = testutil_capture_dump_strings(&ok);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("0000002 abc\n", out);
free(out);
unlink(path);
free(path);
free((void*)list);
}
/* String spanning across two files: "hello" + "\0" */
void test_dump_strings_across_files(void)
{
const unsigned char data1[] = { 'h','e','l','l','o' };
const unsigned char data2[] = { '\0' };
char *path1 = testutil_make_temp_file(data1, sizeof data1);
char *path2 = testutil_make_temp_file(data2, sizeof data2);
char *paths_arr[] = { path1, path2 };
const char **list = testutil_build_file_list(paths_arr, 2);
file_list = list;
string_min = 3;
n_bytes_to_skip = 0;
end_offset = -1;
bool open_ok = open_next_file();
TEST_ASSERT_TRUE(open_ok);
bool ok = false;
char *out = testutil_capture_dump_strings(&ok);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("0000000 hello\n", out);
free(out);
unlink(path1);
unlink(path2);
free(path1);
free(path2);
free((void*)list);
}
/* Multiple strings within a single file */
void test_dump_strings_multiple_strings(void)
{
const unsigned char data[] = { 'a','b','c','\0', 'x','y','z','\0' };
char *path = testutil_make_temp_file(data, sizeof data);
char *paths_arr[] = { path };
const char **list = testutil_build_file_list(paths_arr, 1);
file_list = list;
string_min = 3;
n_bytes_to_skip = 0;
end_offset = -1;
bool open_ok = open_next_file();
TEST_ASSERT_TRUE(open_ok);
bool ok = false;
char *out = testutil_capture_dump_strings(&ok);
TEST_ASSERT_TRUE(ok);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_STRING("0000000 abc\n0000004 xyz\n", out);
free(out);
unlink(path);
free(path);
free((void*)list);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_dump_strings_basic_nul_terminated);
RUN_TEST(test_dump_strings_minlen_not_met);
RUN_TEST(test_dump_strings_end_offset_termination);
RUN_TEST(test_dump_strings_start_offset_address);
RUN_TEST(test_dump_strings_across_files);
RUN_TEST(test_dump_strings_multiple_strings);
return UNITY_END();
}