coreutils / tests /basenc /tests_for_do_decode.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <errno.h>
/* The tests are included into the same translation unit as the program,
so we can access the internal static function pointers and wrappers.
We must select a valid mode (base64) for basenc (BASE_TYPE == 42). */
static void select_base64_mode(void)
{
#if BASE_TYPE == 42
/* Select base64 mode by wiring the function pointers. */
base_length = base64_length_wrapper;
required_padding = base64_required_padding;
isubase = isubase64;
base_encode = base64_encode;
base_decode_ctx_init = base64_decode_ctx_init_wrapper;
base_decode_ctx = base64_decode_ctx_wrapper;
base_decode_ctx_finalize = decode_ctx_finalize;
has_padding = base64_ctx_has_padding;
get_pending_length = base64_ctx_get_pending_length;
#else
/* For other BASE_TYPEs, these are compile-time bound, nothing to do. */
#endif
}
/* Helper to run do_decode in a child process, capturing output and exit code. */
static int run_do_decode_case(const char *input, size_t input_len,
bool ignore_garbage,
char **out_buf, size_t *out_len)
{
select_base64_mode();
/* Create a temporary input file. */
char tmpl[] = "/tmp/basenc_test_XXXXXX";
int fd = mkstemp(tmpl);
if (fd < 0) {
perror("mkstemp");
return -1;
}
ssize_t written = 0;
while ((size_t)written < input_len) {
ssize_t w = write(fd, input + written, input_len - written);
if (w < 0) {
if (errno == EINTR) continue;
perror("write");
close(fd);
unlink(tmpl);
return -1;
}
written += w;
}
if (lseek(fd, 0, SEEK_SET) < 0) {
perror("lseek");
close(fd);
unlink(tmpl);
return -1;
}
FILE *in_fp = fdopen(fd, "rb");
if (!in_fp) {
perror("fdopen");
close(fd);
unlink(tmpl);
return -1;
}
/* Pipe for capturing output. */
int pipefd[2];
if (pipe(pipefd) != 0) {
perror("pipe");
fclose(in_fp);
unlink(tmpl);
return -1;
}
fflush(stdout);
fflush(stderr);
pid_t pid = fork();
if (pid < 0) {
perror("fork");
fclose(in_fp);
close(pipefd[0]);
close(pipefd[1]);
unlink(tmpl);
return -1;
}
if (pid == 0) {
/* Child: call do_decode, writing output to pipe write-end. */
close(pipefd[0]);
FILE *out_fp = fdopen(pipefd[1], "wb");
if (!out_fp) {
_exit(127);
}
/* Call the target function; it will exit the process. */
do_decode(in_fp, tmpl, out_fp, ignore_garbage);
/* Not reached */
_exit(126);
}
/* Parent: clean up and collect output. */
close(pipefd[1]);
/* We can unlink the temp file now; child has the file handle open. */
unlink(tmpl);
fclose(in_fp); /* Close parent's handle; child still has its own. */
/* Read all data from the pipe into a dynamically sized buffer. */
size_t cap = 1024;
size_t used = 0;
char *buf = (char *)malloc(cap);
if (!buf) {
close(pipefd[0]);
/* Reap child to avoid zombie. */
int st; waitpid(pid, &st, 0);
return -1;
}
for (;;) {
if (used == cap) {
size_t new_cap = cap * 2;
char *new_buf = (char *)realloc(buf, new_cap);
if (!new_buf) {
free(buf);
close(pipefd[0]);
int st; waitpid(pid, &st, 0);
return -1;
}
buf = new_buf;
cap = new_cap;
}
ssize_t r = read(pipefd[0], buf + used, cap - used);
if (r > 0) {
used += (size_t)r;
continue;
} else if (r == 0) {
break; /* EOF */
} else {
if (errno == EINTR) continue;
/* On read error, still proceed to collect exit status. */
break;
}
}
close(pipefd[0]);
int status = 0;
if (waitpid(pid, &status, 0) < 0) {
free(buf);
return -1;
}
*out_buf = buf;
*out_len = used;
if (WIFEXITED(status))
return WEXITSTATUS(status);
else
return 128; /* Abnormal termination. */
}
void setUp(void) {
/* No global state to init beyond selecting base64 mode on each run. */
}
void tearDown(void) {
/* Nothing to cleanup here. */
}
static void test_do_decode_base64_simple_no_padding(void)
{
const char *enc = "TWFu"; /* "Man" */
char *out = NULL;
size_t out_len = 0;
int ec = run_do_decode_case(enc, strlen(enc), false, &out, &out_len);
TEST_ASSERT_EQUAL_INT(0, ec);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_size_t(3, out_len);
TEST_ASSERT_EQUAL_MEMORY("Man", out, 3);
free(out);
}
static void test_do_decode_base64_with_padding(void)
{
const char *enc = "TWE="; /* "Ma" */
char *out = NULL;
size_t out_len = 0;
int ec = run_do_decode_case(enc, strlen(enc), false, &out, &out_len);
TEST_ASSERT_EQUAL_INT(0, ec);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_size_t(2, out_len);
TEST_ASSERT_EQUAL_MEMORY("Ma", out, 2);
free(out);
}
static void test_do_decode_base64_missing_padding_autopad(void)
{
const char *enc = "TWE"; /* "Ma" without '='; finalize should auto-pad */
char *out = NULL;
size_t out_len = 0;
int ec = run_do_decode_case(enc, strlen(enc), false, &out, &out_len);
TEST_ASSERT_EQUAL_INT(0, ec);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_size_t(2, out_len);
TEST_ASSERT_EQUAL_MEMORY("Ma", out, 2);
free(out);
}
static void test_do_decode_base64_ignore_garbage(void)
{
/* Insert various garbage and newlines; with ignore_garbage it should decode. */
const char *enc = "T?W!E= \n"; /* Should reduce to "TWE=" -> "Ma" */
char *out = NULL;
size_t out_len = 0;
int ec = run_do_decode_case(enc, strlen(enc), true, &out, &out_len);
TEST_ASSERT_EQUAL_INT(0, ec);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_size_t(2, out_len);
TEST_ASSERT_EQUAL_MEMORY("Ma", out, 2);
free(out);
}
static void test_do_decode_invalid_char_fails(void)
{
const char *enc = "TW?="; /* Invalid '?' should cause failure without ignore-garbage */
char *out = NULL;
size_t out_len = 0;
int ec = run_do_decode_case(enc, strlen(enc), false, &out, &out_len);
TEST_ASSERT_NOT_EQUAL(0, ec);
/* Output should be empty (no complete block decoded). */
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_size_t(0, out_len);
free(out);
}
static void test_do_decode_incomplete_length_error(void)
{
/* A single base64 character cannot be salvaged even with auto-padding. */
const char *enc = "T";
char *out = NULL;
size_t out_len = 0;
int ec = run_do_decode_case(enc, strlen(enc), false, &out, &out_len);
TEST_ASSERT_NOT_EQUAL(0, ec);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_size_t(0, out_len);
free(out);
}
static void test_do_decode_large_multiblock(void)
{
/* Build a large input exceeding a single DEC_BLOCKSIZE cycle.
"TWFu" -> "Man". Repeat enough times. */
const int repeats = 2000; /* 4*2000 = 8000 encoded bytes, > BASE_LENGTH(4200) */
size_t enc_len = 4u * repeats;
char *enc = (char *)malloc(enc_len);
TEST_ASSERT_NOT_NULL(enc);
for (int i = 0; i < repeats; i++) {
memcpy(enc + 4u * i, "TWFu", 4);
}
char *out = NULL;
size_t out_len = 0;
int ec = run_do_decode_case(enc, enc_len, false, &out, &out_len);
free(enc);
TEST_ASSERT_EQUAL_INT(0, ec);
TEST_ASSERT_NOT_NULL(out);
TEST_ASSERT_EQUAL_size_t(3u * repeats, out_len);
/* Spot check beginning, middle, and end segments for "Man". */
TEST_ASSERT_EQUAL_MEMORY("Man", out, 3);
TEST_ASSERT_EQUAL_MEMORY("Man", out + (3u * (repeats/2)), 3);
TEST_ASSERT_EQUAL_MEMORY("Man", out + out_len - 3, 3);
free(out);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_do_decode_base64_simple_no_padding);
RUN_TEST(test_do_decode_base64_with_padding);
RUN_TEST(test_do_decode_base64_missing_padding_autopad);
RUN_TEST(test_do_decode_base64_ignore_garbage);
RUN_TEST(test_do_decode_invalid_char_fails);
RUN_TEST(test_do_decode_incomplete_length_error);
RUN_TEST(test_do_decode_large_multiblock);
return UNITY_END();
}