coreutils / tests /od /tests_for_print_ascii.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 <errno.h>
#include <ctype.h>
#include <locale.h>
/* The function under test is defined in the including TU (od.c):
static void print_ascii(idx_t fields, idx_t blank, void const *block,
MAYBE_UNUSED char const *unused_fmt_string,
int width, idx_t pad);
We also use pad_at from od.c to mirror padding calculations for expected output. */
static char *capture_print_ascii(const unsigned char *bytes,
idx_t fields, idx_t blank,
int width, idx_t pad)
{
/* Redirect stdout to a temporary file, call print_ascii, then restore. */
FILE *tmp = tmpfile();
if (!tmp) return NULL;
if (fflush(stdout) != 0) { fclose(tmp); return NULL; }
int out_fd = fileno(stdout);
int saved_fd = dup(out_fd);
if (saved_fd < 0) { fclose(tmp); return NULL; }
if (dup2(fileno(tmp), out_fd) < 0) {
int e = errno; (void)e;
close(saved_fd);
fclose(tmp);
return NULL;
}
/* Call function under test. Avoid any Unity asserts while stdout is redirected. */
print_ascii(fields, blank, bytes, NULL, width, pad);
/* Flush redirected stdout to ensure all data in tmp. */
fflush(stdout);
/* Restore stdout. */
if (dup2(saved_fd, out_fd) < 0) {
/* If restore fails, still try to proceed, but indicate failure. */
close(saved_fd);
fclose(tmp);
return NULL;
}
close(saved_fd);
/* Read back captured output. */
if (fflush(tmp) != 0) { fclose(tmp); return NULL; }
if (fseek(tmp, 0, SEEK_END) != 0) { fclose(tmp); return NULL; }
long len = ftell(tmp);
if (len < 0) { fclose(tmp); return NULL; }
if (fseek(tmp, 0, SEEK_SET) != 0) { fclose(tmp); return NULL; }
char *buf = (char *)malloc((size_t)len + 1);
if (!buf) { fclose(tmp); return NULL; }
size_t nread = fread(buf, 1, (size_t)len, tmp);
buf[nread] = '\0';
fclose(tmp);
return buf;
}
static void append_str(char **dst, size_t *cap, size_t *len, const char *s)
{
size_t sl = strlen(s);
if (*len + sl + 1 > *cap) {
size_t ncap = (*cap ? *cap : 64);
while (*len + sl + 1 > ncap) ncap *= 2;
char *nbuf = (char *)realloc(*dst, ncap);
if (!nbuf) {
free(*dst);
*dst = NULL;
*cap = *len = 0;
return;
}
*dst = nbuf;
*cap = ncap;
}
memcpy(*dst + *len, s, sl);
*len += sl;
(*dst)[*len] = '\0';
}
static void append_spaces(char **dst, size_t *cap, size_t *len, int n)
{
if (n <= 0) return;
if (*len + (size_t)n + 1 > *cap) {
size_t ncap = (*cap ? *cap : 64);
while (*len + (size_t)n + 1 > ncap) ncap *= 2;
char *nbuf = (char *)realloc(*dst, ncap);
if (!nbuf) {
free(*dst);
*dst = NULL;
*cap = *len = 0;
return;
}
*dst = nbuf;
*cap = ncap;
}
memset(*dst + *len, ' ', (size_t)n);
*len += (size_t)n;
(*dst)[*len] = '\0';
}
static const char *ascii_repr(unsigned char c, char tmp[4])
{
switch (c) {
case '\0': return "\\0";
case '\a': return "\\a";
case '\b': return "\\b";
case '\f': return "\\f";
case '\n': return "\\n";
case '\r': return "\\r";
case '\t': return "\\t";
case '\v': return "\\v";
default:
if (isprint(c)) {
tmp[0] = (char)c;
tmp[1] = '\0';
} else {
/* 3-digit octal */
sprintf(tmp, "%03o", c);
}
return tmp;
}
}
static char *build_expected_for_bytes(const unsigned char *bytes,
idx_t fields, idx_t blank,
int width, idx_t pad)
{
size_t cap = 0, len = 0;
char *out = NULL;
idx_t pad_remaining = pad;
idx_t j = 0; /* index into bytes */
for (idx_t i = fields; blank < i; i--) {
char tmp[4];
const char *s = ascii_repr(bytes[j++], tmp);
size_t slen = strlen(s);
idx_t next_pad = pad_at(fields, i - 1, pad);
int adjusted_width = (int)(pad_remaining - next_pad) + width;
int spaces = adjusted_width - (int)slen;
if (spaces < 0) spaces = 0;
append_spaces(&out, &cap, &len, spaces);
if (!out) return NULL;
append_str(&out, &cap, &len, s);
if (!out) return NULL;
pad_remaining = next_pad;
}
if (!out) {
out = (char *)malloc(1);
if (out) out[0] = '\0';
}
return out;
}
void setUp(void)
{
/* Ensure predictable ctype behavior for isprint */
setlocale(LC_ALL, "C");
}
void tearDown(void)
{
}
void test_print_ascii_single_printable(void)
{
unsigned char bytes[] = { 'A' };
char *got = capture_print_ascii(bytes, 1, 0, 3, 0);
TEST_ASSERT_NOT_NULL(got);
/* width=3, printable 'A' => two spaces then 'A' */
TEST_ASSERT_EQUAL_STRING(" A", got);
free(got);
}
void test_print_ascii_octal_nonprintable(void)
{
unsigned char bytes[] = { 1u, 31u, 127u };
char *got = capture_print_ascii(bytes, 3, 0, 3, 0);
TEST_ASSERT_NOT_NULL(got);
/* Expect 001 037 177 with no extra spaces since width==len==3 */
TEST_ASSERT_EQUAL_STRING("001037177", got);
free(got);
}
void test_print_ascii_escapes(void)
{
unsigned char bytes[] = { '\0', '\a', '\b', '\f', '\n', '\r', '\t', '\v' };
idx_t fields = 8;
int width = 3;
idx_t pad = 0;
char *got = capture_print_ascii(bytes, fields, 0, width, pad);
TEST_ASSERT_NOT_NULL(got);
char *expected = build_expected_for_bytes(bytes, fields, 0, width, pad);
TEST_ASSERT_NOT_NULL(expected);
TEST_ASSERT_EQUAL_STRING(expected, got);
free(expected);
free(got);
}
void test_print_ascii_blank_fields(void)
{
unsigned char bytes[] = { 'A', 'B', 'C', 'D' };
/* fields=4, blank=2 means only first 2 bytes are printed. */
char *got = capture_print_ascii(bytes, 4, 2, 3, 0);
TEST_ASSERT_NOT_NULL(got);
TEST_ASSERT_EQUAL_STRING(" A B", got);
free(got);
}
void test_print_ascii_padding_distribution(void)
{
unsigned char bytes[] = { 'A', 'B', 'C', 'D' };
idx_t fields = 4;
idx_t blank = 0;
int width = 3;
idx_t pad = 10; /* Non-evenly divisible by fields to test distribution */
char *got = capture_print_ascii(bytes, fields, blank, width, pad);
TEST_ASSERT_NOT_NULL(got);
char *expected = build_expected_for_bytes(bytes, fields, blank, width, pad);
TEST_ASSERT_NOT_NULL(expected);
TEST_ASSERT_EQUAL_STRING(expected, got);
/* Additionally verify the specific spacing counts for each field. */
/* Compute expected widths per field to ensure distribution 6,5,6,5 (for pad=10, width=3). */
idx_t pad_remaining = pad;
int widths[4];
for (idx_t i = fields, k = 0; i > blank; i--, k++) {
idx_t next_pad = pad_at(fields, i - 1, pad);
int adjusted_width = (int)(pad_remaining - next_pad) + width;
widths[k] = adjusted_width;
pad_remaining = next_pad;
}
/* Extract spaces before each letter in got. */
int spaces_before[4] = {0,0,0,0};
const char *p = got;
for (int idx = 0; idx < 4; idx++) {
int count = 0;
while (*p == ' ') { count++; p++; }
/* Should now be the letter */
/* Guard in case of malformed string */
if (*p == '\0') break;
p++; /* skip the letter */
spaces_before[idx] = count;
}
/* For width W and 1-char letter, spaces = W - 1 */
TEST_ASSERT_EQUAL_INT(widths[0] - 1, spaces_before[0]);
TEST_ASSERT_EQUAL_INT(widths[1] - 1, spaces_before[1]);
TEST_ASSERT_EQUAL_INT(widths[2] - 1, spaces_before[2]);
TEST_ASSERT_EQUAL_INT(widths[3] - 1, spaces_before[3]);
free(expected);
free(got);
}
void test_print_ascii_width_less_than_repr(void)
{
unsigned char bytes[] = { '\n' }; /* maps to "\\n" (length 2) */
/* width=1 < len("\\n") => no truncation, exact string "\\n" printed. */
char *got = capture_print_ascii(bytes, 1, 0, 1, 0);
TEST_ASSERT_NOT_NULL(got);
TEST_ASSERT_EQUAL_STRING("\\n", got);
free(got);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_print_ascii_single_printable);
RUN_TEST(test_print_ascii_octal_nonprintable);
RUN_TEST(test_print_ascii_escapes);
RUN_TEST(test_print_ascii_blank_fields);
RUN_TEST(test_print_ascii_padding_distribution);
RUN_TEST(test_print_ascii_width_less_than_repr);
return UNITY_END();
}