coreutils / tests /chmod /tests_for_describe_change.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <locale.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include "quote.h"
/* Unity fixtures */
void setUp(void) {
/* Ensure deterministic messages and quoting */
setlocale(LC_ALL, "C");
set_quoting_style(NULL, literal_quoting_style);
}
void tearDown(void) {
/* no-op */
}
/* Helper: capture stdout produced by describe_change.
On success, returns malloc'd NUL-terminated string (caller frees).
On failure, returns NULL (no Unity assertions while stdout is redirected). */
static char* capture_describe_change_output(const char *file, const struct change_status *ch) {
int pipefd[2];
if (pipe(pipefd) == -1) {
return NULL;
}
int saved_stdout = dup(STDOUT_FILENO);
if (saved_stdout == -1) {
close(pipefd[0]);
close(pipefd[1]);
return NULL;
}
fflush(stdout);
if (dup2(pipefd[1], STDOUT_FILENO) == -1) {
close(saved_stdout);
close(pipefd[0]);
close(pipefd[1]);
return NULL;
}
/* We can close the original write end now; stdout holds a dup of it. */
close(pipefd[1]);
/* Call the target function; avoid Unity asserts during redirection */
describe_change(file, ch);
/* Flush and restore stdout */
fflush(stdout);
if (dup2(saved_stdout, STDOUT_FILENO) == -1) {
/* Even if restore fails, attempt to continue to read what we can */
}
close(saved_stdout);
/* Read all data from the pipe */
char *buf = NULL;
size_t cap = 0;
size_t len = 0;
char tmp[4096];
ssize_t n;
while ((n = read(pipefd[0], tmp, sizeof tmp)) > 0) {
if (len + (size_t)n + 1 > cap) {
size_t new_cap = (cap == 0 ? 4096 : cap * 2);
while (new_cap < len + (size_t)n + 1) new_cap *= 2;
char *new_buf = (char*)realloc(buf, new_cap);
if (!new_buf) {
free(buf);
close(pipefd[0]);
return NULL;
}
buf = new_buf;
cap = new_cap;
}
memcpy(buf + len, tmp, (size_t)n);
len += (size_t)n;
}
close(pipefd[0]);
if (!buf) {
buf = (char*)malloc(1);
if (!buf) return NULL;
len = 0;
}
buf[len] = '\0';
return buf;
}
/* Test: CH_NOT_APPLIED message */
void test_describe_change_not_applied(void) {
struct change_status ch;
memset(&ch, 0, sizeof ch);
ch.status = CH_NOT_APPLIED;
char *out = capture_describe_change_output("file", &ch);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "Failed to capture output");
TEST_ASSERT_EQUAL_STRING("neither symbolic link file nor referent has been changed\n", out);
free(out);
}
/* Test: CH_NO_STAT message */
void test_describe_change_no_stat(void) {
struct change_status ch;
memset(&ch, 0, sizeof ch);
ch.status = CH_NO_STAT;
char *out = capture_describe_change_output("missing", &ch);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "Failed to capture output");
TEST_ASSERT_EQUAL_STRING("missing could not be accessed\n", out);
free(out);
}
/* Test: CH_SUCCEEDED message with regular file modes */
void test_describe_change_succeeded_basic(void) {
struct change_status ch;
memset(&ch, 0, sizeof ch);
ch.status = CH_SUCCEEDED;
ch.old_mode = S_IFREG | 0600;
ch.new_mode = S_IFREG | 0640;
char *out = capture_describe_change_output("file", &ch);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "Failed to capture output");
TEST_ASSERT_EQUAL_STRING(
"mode of file changed from 0600 (rw-------) to 0640 (rw-r-----)\n",
out);
free(out);
}
/* Test: CH_FAILED message with regular file modes */
void test_describe_change_failed_basic(void) {
struct change_status ch;
memset(&ch, 0, sizeof ch);
ch.status = CH_FAILED;
ch.old_mode = S_IFREG | 0755;
ch.new_mode = S_IFREG | 0700;
char *out = capture_describe_change_output("exec", &ch);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "Failed to capture output");
TEST_ASSERT_EQUAL_STRING(
"failed to change mode of exec from 0755 (rwxr-xr-x) to 0700 (rwx------)\n",
out);
free(out);
}
/* Test: CH_NO_CHANGE_REQUESTED prints only new mode and perms */
void test_describe_change_no_change_requested_with_setuid(void) {
struct change_status ch;
memset(&ch, 0, sizeof ch);
ch.status = CH_NO_CHANGE_REQUESTED;
ch.old_mode = S_IFREG | 0600; /* old_mode is ignored in this branch */
ch.new_mode = S_IFREG | S_ISUID | 0755; /* 04755 -> rwsr-xr-x */
char *out = capture_describe_change_output("suidbin", &ch);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "Failed to capture output");
TEST_ASSERT_EQUAL_STRING(
"mode of suidbin retained as 4755 (rwsr-xr-x)\n",
out);
free(out);
}
/* Test: CH_SUCCEEDED with directory mode; ensure leading file-type char removed */
void test_describe_change_succeeded_directory(void) {
struct change_status ch;
memset(&ch, 0, sizeof ch);
ch.status = CH_SUCCEEDED;
ch.old_mode = S_IFDIR | 0700;
ch.new_mode = S_IFDIR | 0777;
char *out = capture_describe_change_output("dir", &ch);
TEST_ASSERT_NOT_NULL_MESSAGE(out, "Failed to capture output");
TEST_ASSERT_EQUAL_STRING(
"mode of dir changed from 0700 (rwx------) to 0777 (rwxrwxrwx)\n",
out);
free(out);
}
int main(void) {
UNITY_BEGIN();
RUN_TEST(test_describe_change_not_applied);
RUN_TEST(test_describe_change_no_stat);
RUN_TEST(test_describe_change_succeeded_basic);
RUN_TEST(test_describe_change_failed_basic);
RUN_TEST(test_describe_change_no_change_requested_with_setuid);
RUN_TEST(test_describe_change_succeeded_directory);
return UNITY_END();
}