coreutils / tests /dd /tests_for_ifstat.c
AryaWu's picture
Upload folder using huggingface_hub
78d2150 verified
#include "../../unity/unity.h"
#include <errno.h>
#include <sys/stat.h>
#include <stddef.h>
#include <stdint.h>
/*
Stub machinery to override fstat used by ifstat().
Since this test file is included into the same translation unit as dd.c,
defining fstat here will override the libc symbol for references in this TU.
*/
typedef struct {
int ret; /* return value for fstat */
int err; /* errno to set before returning */
mode_t mode; /* st_mode to set on success */
} fstat_plan_t;
static fstat_plan_t g_plan[16];
static int g_plan_len = 0;
static int g_plan_idx = 0;
static int g_fstat_call_count = 0;
static void fstat_stub_reset(void)
{
g_plan_len = 0;
g_plan_idx = 0;
g_fstat_call_count = 0;
}
static void fstat_stub_add(int ret, int err, mode_t mode)
{
if (g_plan_len < (int)(sizeof g_plan / sizeof g_plan[0])) {
g_plan[g_plan_len].ret = ret;
g_plan[g_plan_len].err = err;
g_plan[g_plan_len].mode = mode;
g_plan_len++;
}
}
/* Override fstat/rpl_fstat depending on any macro mapping from system.h.
If fstat is defined as a macro (e.g., to rpl_fstat), the function
definition name token below will be macro-expanded accordingly, which is
what we want so that ifstat() calls land here. */
int fstat(int fd, struct stat *st)
{
(void)fd; /* Unused in stub */
g_fstat_call_count++;
/* Default behavior if plan exhausted: succeed with a generic mode. */
int ret = 0;
int err = 0;
mode_t mode = (mode_t) (S_IFREG | 0644);
if (g_plan_idx < g_plan_len) {
ret = g_plan[g_plan_idx].ret;
err = g_plan[g_plan_idx].err;
mode = g_plan[g_plan_idx].mode;
g_plan_idx++;
}
errno = err;
if (ret == 0 && st) {
/* Set only minimal field we care about. */
st->st_mode = mode;
}
return ret;
}
/* Unity setUp/tearDown */
void setUp(void)
{
fstat_stub_reset();
errno = 0;
}
void tearDown(void)
{
/* Nothing to do */
}
/* Tests */
void test_ifstat_success_immediate(void)
{
struct stat st;
st.st_mode = 0;
mode_t expect_mode = (mode_t)(S_IFREG | 0600);
fstat_stub_add(0, 0, expect_mode);
int rc = ifstat(123, &st);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(expect_mode, st.st_mode);
TEST_ASSERT_EQUAL_INT(1, g_fstat_call_count);
}
void test_ifstat_retries_once_on_EINTR_then_success(void)
{
struct stat st;
st.st_mode = 0;
mode_t expect_mode = (mode_t)(S_IFDIR | 0755);
fstat_stub_add(-1, EINTR, 0); /* first call interrupted */
fstat_stub_add(0, 0, expect_mode); /* second call succeeds */
int rc = ifstat(5, &st);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(expect_mode, st.st_mode);
TEST_ASSERT_EQUAL_INT(2, g_fstat_call_count);
}
void test_ifstat_retries_multiple_EINTR_then_success(void)
{
struct stat st;
st.st_mode = 0;
mode_t expect_mode = (mode_t)(S_IFIFO | 0640);
fstat_stub_add(-1, EINTR, 0);
fstat_stub_add(-1, EINTR, 0);
fstat_stub_add(-1, EINTR, 0);
fstat_stub_add(0, 0, expect_mode);
int rc = ifstat(7, &st);
TEST_ASSERT_EQUAL_INT(0, rc);
TEST_ASSERT_EQUAL_INT(expect_mode, st.st_mode);
TEST_ASSERT_EQUAL_INT(4, g_fstat_call_count);
}
void test_ifstat_non_EINTR_error_propagates_no_retry(void)
{
struct stat st;
st.st_mode = 0xDEAD; /* sentinel to ensure untouched on error */
fstat_stub_add(-1, ENOENT, 0);
errno = 0;
int rc = ifstat(9, &st);
TEST_ASSERT_EQUAL_INT(-1, rc);
TEST_ASSERT_EQUAL_INT(ENOENT, errno);
TEST_ASSERT_EQUAL_INT(1, g_fstat_call_count);
TEST_ASSERT_EQUAL_INT(0xDEAD, st.st_mode);
}
int main(void)
{
UNITY_BEGIN();
RUN_TEST(test_ifstat_success_immediate);
RUN_TEST(test_ifstat_retries_once_on_EINTR_then_success);
RUN_TEST(test_ifstat_retries_multiple_EINTR_then_success);
RUN_TEST(test_ifstat_non_EINTR_error_propagates_no_retry);
return UNITY_END();
}