Files
cKit/Foundation/c_File.t.c
T
2026-08-29 11:39:28 +08:00

366 lines
15 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "c_File.h"
#include <stdlib.h>
#include <stdio.h>
#include <c_Test.h>
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
static const char* TEST_DIR = "./test_sandbox";
static const char* TEST_FILE = "./test_sandbox/test_data.txt";
static const char* NESTED_DIR_ROOT = "./test_sandbox_deep";
static const char* NESTED_DIR_SUB1 = "./test_sandbox_deep/level1";
static const char* NESTED_DIR_SUB2 = "./test_sandbox_deep/level1/level2";
static const char* NESTED_DEEP_FILE = "./test_sandbox_deep/level1/level2/target.txt";
static const char* TEST_DEL_FILE = "./test_sandbox/delete_target.txt";
static const char* FILE_SRC = "./test_sandbox/copy_src.txt";
static const char* FILE_COPY = "./test_sandbox/copy_dest.txt";
static const char* FILE_MOVE = "./test_sandbox/move_dest.txt";
static c_File_t g_file;
// 每个文件测试开始前:创建目录确保沙盒环境存在,并重置文件结构体
void setup_file_env() {
c_File_MkDirs(TEST_DIR);
g_file.fp = NULL;
}
// 每个文件测试结束后:强制关闭可能遗留的文件流,并清理生成的临时测试文件
void teardown_file_env() {
if (g_file.fp != NULL) {
c_File_Close(&g_file);
}
// 移除沙盒内的文件(如果存在)
remove(TEST_FILE);
// 移除沙盒目录(Linux/macOS 下使用 rmdir,为保持跨平台通用这里主要移除文件)
remove(TEST_DIR);
}
void setup_deep_dir_env() {
// 1. 建立一个多级深层嵌套的目录
c_File_MkDirs(NESTED_DIR_SUB2);
}
void teardown_deep_dir_env() {
// 兜底清理:如果测试挂了,防止残留污染本地磁盘
// 在这里直接调用它自己完成强制扫尾
c_File_Rmdirs(NESTED_DIR_ROOT);
}
void setup_delete_env() {
// 确保测试沙盒目录存在
c_File_MkDirs("./test_sandbox");
}
void teardown_delete_env() {
// 扫尾清理,防止测试中断导致文件残留
remove(TEST_DEL_FILE);
remove("./test_sandbox");
}
void setup_move_copy_env() {
c_File_MkDirs("./test_sandbox");
}
void teardown_move_copy_env() {
// 强制清理,防止测试中断产生磁盘残留
remove(FILE_SRC);
remove(FILE_COPY);
remove(FILE_MOVE);
remove("./test_sandbox");
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
void test_file_write_and_size() {
// 1. 以只写/新建模式打开文件
c_err_t err = c_File_Open(&g_file, TEST_FILE, "wb");
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Failed to open file for writing");
ASSERT_MSG(g_file.fp != NULL, "File pointer should not be NULL after open");
// 2. 检查刚创建的文件是否存在
ASSERT_MSG(c_File_IsExist(TEST_FILE) == C_TRUE, "File should exist on disk");
// 3. 写入数据
const char* content = "Hello TinyTest Framework!";
c_size_t bytes_to_write = strlen(content);
c_size_t bytes_written = 0;
err = c_File_Write(&g_file, (void*)content, bytes_to_write, &bytes_written);
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "File write error");
ASSERT_INT_EQ_MSG((int)bytes_to_write, (int)bytes_written, "Written size mismatched");
// 4. 刷新缓冲区
err = c_File_Flush(&g_file);
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "File flush error");
// 5. 校验文件大小是否和写入的字节数严格对齐
long long f_size = c_File_Size(&g_file);
ASSERT_INT_EQ_MSG((int)bytes_to_write, (int)f_size, "File size reported incorrectly");
// 6. 正常关闭文件
c_File_Close(&g_file);
}
// 用例 2:测试文件的读取(Read)以及指针重定位(Seek)
void test_file_read_and_seek() {
// 预备工作:先写入一串已知文本用于后续测试
c_File_Open(&g_file, TEST_FILE, "wb");
const char* dummy_data = "abcdefghij"; // 10 字节
c_size_t written = 0;
c_File_Write(&g_file, (void*)dummy_data, 10, &written);
c_File_Close(&g_file);
// 1. 以只读模式重新打开文件
c_err_t err = c_File_Open(&g_file, TEST_FILE, "rb");
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Failed to open file for reading");
// 2. 测试基础顺序读取(先读取 4 字节,应该读到 "abcd"
char buffer[16] = {0};
c_size_t bytes_read = 0;
err = c_File_Read(&g_file, buffer, 4, &bytes_read);
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "File read error");
ASSERT_INT_EQ_MSG(4, (int)bytes_read, "Should read exactly 4 bytes");
ASSERT_INT_EQ_MSG(0, memcmp(buffer, "abcd", 4), "Buffer content mismatches on initial read");
// 3. 测试文件指针重定位:移到绝对位置索引 5 处(对应字符 'f'
err = c_File_Seek(&g_file, 5);
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "File seek error");
// 4. 定位后再次读取 3 字节(应该读到 "fgh"
memset(buffer, 0, sizeof(buffer));
err = c_File_Read(&g_file, buffer, 3, &bytes_read);
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "File read error after seeking");
ASSERT_INT_EQ_MSG(3, (int)bytes_read, "Should read 3 bytes after seek");
ASSERT_INT_EQ_MSG(0, memcmp(buffer, "fgh", 3), "Buffer content mismatches after seek");
c_File_Close(&g_file);
}
// 用例 3:测试按行读取(Readline)文本的边界与断行识别
void test_file_read_line() {
// 预备工作:写入包含多行换行符的文本
c_File_Open(&g_file, TEST_FILE, "wb");
const char* lines = "Line1\nLine2\r\nLine3";
c_size_t written = 0;
c_File_Write(&g_file, (void*)lines, strlen(lines), &written);
c_File_Close(&g_file);
// 1. 打开文件开始按行验证
c_File_Open(&g_file, TEST_FILE, "rb");
char line_buf[32];
c_size_t read_len = 0;
// 读取第一行(预期为 "Line1\n" 或处理掉换行符的 "Line1" 视你内部实现而定,通常 fgets 保留换行)
c_err_t err = c_File_Readline(&g_file, line_buf, sizeof(line_buf), &read_len);
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Readline 1 failed");
ASSERT_MSG(strstr(line_buf, "Line1") != NULL, "Line 1 content error");
// 读取第二行
err = c_File_Readline(&g_file, line_buf, sizeof(line_buf), &read_len);
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Readline 2 failed");
ASSERT_MSG(strstr(line_buf, "Line2") != NULL, "Line 2 content error");
c_File_Close(&g_file);
}
// 用例 4:测试文件不存在、无效路径时的防御性报错
void test_file_invalid_operations() {
// 1. 检查一个绝对不存在的文件
c_bool_t exist = c_File_IsExist("./this_file_does_not_exist_12345.xyz");
ASSERT_INT_EQ_MSG(C_FALSE, exist, "IsExist should return FALSE for phantom files");
// 2. 尝试打开一个不存在的文件用于只读,应当安全返回错误码,且内部指针保持为 NULL
c_File_t bad_file = {NULL};
c_err_t err = c_File_Open(&bad_file, "./phantom_file.txt", "rb");
ASSERT_MSG(err != C_ERR_OK, "Opening non-existent file for read must fail");
ASSERT_MSG(bad_file.fp == NULL, "Failed open must keep fp as NULL");
}
void test_file_rmdirs_force_delete_nested() {
// 1. 验证前置多级目录环境已经被 SetUp 成功拉起
ASSERT_MSG(c_File_IsExist(NESTED_DIR_SUB2) == C_TRUE, "Setup failed to prepare nested dir");
// 2. 在最深层目录 level2 里写入一个真实的文本文件,夯实其“非空”属性
c_File_t file;
c_err_t err = c_File_Open(&file, NESTED_DEEP_FILE, "wb");
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Failed to create deep file inside test sandbox");
const char* dummy = "deep data";
c_size_t written = 0;
c_File_Write(&file, (void*)dummy, strlen(dummy), &written);
c_File_Close(&file);
// 再次确认文件已在磁盘落地
ASSERT_MSG(c_File_IsExist(NESTED_DEEP_FILE) == C_TRUE, "Deep text file should exist before wipe");
// 3. 一剑封喉:直接调用 Rmdirs 强删最外层的根目录 NESTED_DIR_ROOT
c_err_t rmdir_status = c_File_Rmdirs(NESTED_DIR_ROOT);
ASSERT_INT_EQ_MSG(C_ERR_OK, rmdir_status, "c_File_Rmdirs failed to clear nested architecture");
// 4. 断言验证:整棵目录树必须从盘面上被完全抹除
ASSERT_MSG(c_File_IsExist(NESTED_DEEP_FILE) == C_FALSE, "Deep file should have been blasted");
ASSERT_MSG(c_File_IsExist(NESTED_DIR_SUB2) == C_FALSE, "Level 2 directory should be wiped");
ASSERT_MSG(c_File_IsExist(NESTED_DIR_ROOT) == C_FALSE, "Root sandbox directory must be cleared");
}
/* ------------------------------------------------------------------------------------------------------------------ */
/* */
// 用例 1:测试常规文件的正常创建与成功删除
void test_file_delete_success() {
// 1. 先创建一个真实的文件
c_File_t file;
c_err_t err = c_File_Open(&file, TEST_DEL_FILE, "wb");
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Failed to create delete-target file");
const char* dummy = "delete me";
c_size_t written = 0;
c_File_Write(&file, (void*)dummy, strlen(dummy), &written);
c_File_Close(&file);
// 2. 确认文件确实落地并在磁盘中存在
ASSERT_MSG(c_File_IsExist(TEST_DEL_FILE) == C_TRUE, "Target file must exist before deletion");
// 3. 执行删除操作
c_err_t del_err = c_File_Delete(TEST_DEL_FILE);
ASSERT_INT_EQ_MSG(C_ERR_OK, del_err, "c_File_Delete failed on a standard closed file");
// 4. 再次验证文件是否已经从文件系统中消失
ASSERT_MSG(c_File_IsExist(TEST_DEL_FILE) == C_FALSE, "Target file should be gone after c_File_Delete");
}
// 用例 2:测试删除一个完全不存在的文件时的防御表现
void test_file_delete_non_existent() {
const char* phantom_file = "./test_sandbox/ghost_file_999.xyz";
// 确保该路径当前确实不存在
ASSERT_MSG(c_File_IsExist(phantom_file) == C_FALSE, "Phantom file should not exist");
// 尝试执行删除
c_err_t err = c_File_Delete(phantom_file);
// 应当安全返回非 OK 的错误状态,且程序绝不能发生崩溃
ASSERT_MSG(err != C_ERR_OK, "c_File_Delete must report an error when trying to delete a non-existent file");
}
// 用例 3:测试删除一个正处于“打开/占用状态”的文件(进阶边界测试)
void test_file_delete_while_open() {
// 1. 创建并保持打开该文件,故意不执行 Close
c_File_t file;
c_err_t err = c_File_Open(&file, TEST_DEL_FILE, "wb");
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "Failed to open file for lock-test");
// 2. 尝试在文件流未关闭的情况下,强行调用接口删除它
c_err_t del_err = c_File_Delete(TEST_DEL_FILE);
/*
* 注意:这里的断言取决于你对框架跨平台容忍度的设计。
* - Windows 底层会因为文件处于 Share Violation 锁死状态而直接拒绝删除,返回错误码(del_err != C_ERR_OK)。
* - Linux / POSIX 则允许执行 unlink 删除,表现为返回 C_ERR_OK,但文件直到 Close 后才会真正释放空间。
* 为了让你的测试能在多平台安全兼容,我们主要确保程序不会挂死崩溃,并打印当前表现。
*/
printf(" [INFO] c_File_Delete while open returned: %d (Platform dependent behaviour)\n", del_err);
// 无论刚才删除成功与否,为了测试框架的安全,我们都要显式地进行资源关闭和文件清理
c_File_Close(&file);
remove(TEST_DEL_FILE);
}
// 用例 1:验证文件流式复制 c_File_Copy
void test_file_copy_integrity() {
// 1. 准备源文件并写入特定文本
c_File_t src_file;
c_File_Open(&src_file, FILE_SRC, "wb");
const char* pattern = "Copy & Move Structural Verification Data.";
c_size_t written = 0;
c_File_Write(&src_file, (void*)pattern, strlen(pattern), &written);
c_File_Close(&src_file);
// 2. 调用复制函数
c_err_t err = c_File_Copy(FILE_SRC, FILE_COPY);
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "c_File_Copy execution failed");
// 3. 断言验证:目标文件必须存在,且大小必须完全一致
ASSERT_MSG(c_File_IsExist(FILE_COPY) == C_TRUE, "Copied target file does not exist");
c_File_t dest_file;
c_File_Open(&dest_file, FILE_COPY, "rb");
long long copy_size = c_File_Size(&dest_file);
char read_buf[128] = {0};
c_size_t read_bytes = 0;
c_File_Read(&dest_file, read_buf, sizeof(read_buf) - 1, &read_bytes);
c_File_Close(&dest_file);
ASSERT_INT_EQ_MSG((int)strlen(pattern), (int)copy_size, "Copied file size mismatches");
ASSERT_INT_EQ_MSG(0, strcmp(pattern, read_buf), "Copied data content corruption detected");
}
// 用例 2:验证文件移动与重命名 c_File_Move
void test_file_move_behavior() {
// 1. 再次在旧路径上创建一个基准文件
c_File_t src_file;
c_File_Open(&src_file, FILE_SRC, "wb");
const char* payload = "MovePayload";
c_size_t written = 0;
c_File_Write(&src_file, (void*)payload, strlen(payload), &written);
c_File_Close(&src_file);
// 2. 调用移动函数
c_err_t err = c_File_Move(FILE_SRC, FILE_MOVE);
ASSERT_INT_EQ_MSG(C_ERR_OK, err, "c_File_Move execution failed");
// 3. 断言验证:【关键点】旧文件必须在文件系统中消失,新路径下必须出现该文件
ASSERT_MSG(c_File_IsExist(FILE_SRC) == C_FALSE, "Source file should be gone after move");
ASSERT_MSG(c_File_IsExist(FILE_MOVE) == C_TRUE, "Moved destination file should exist");
// 4. 读取内容验证原子性与准确性
c_File_t moved_file;
c_File_Open(&moved_file, FILE_MOVE, "rb");
char read_buf[32] = {0};
c_size_t read_bytes = 0;
c_File_Read(&moved_file, read_buf, sizeof(read_buf) - 1, &read_bytes);
c_File_Close(&moved_file);
ASSERT_INT_EQ_MSG(0, strcmp(payload, read_buf), "Moved file content mismatches");
}
// 用例 3:验证对非正常状态路径(不存在的源文件)调用 Copy 的防御机制
void test_file_copy_non_existent() {
c_err_t err = c_File_Copy("./test_sandbox/non_exist_source_xyz.dat", FILE_COPY);
// 应该优雅报错,不能返回 C_ERR_OK
ASSERT_MSG(err != C_ERR_OK, "Copying a non-existent file must return error status");
}
int main(int argc, char** argv){
TEST_START(Starting Unit Tests);
RUN_TEST_FIXTURE(test_file_write_and_size, setup_file_env, teardown_file_env);
RUN_TEST_FIXTURE(test_file_read_and_seek, setup_file_env, teardown_file_env);
RUN_TEST_FIXTURE(test_file_read_line, setup_file_env, teardown_file_env);
RUN_TEST_FIXTURE(test_file_invalid_operations, setup_file_env, teardown_file_env);
RUN_TEST_FIXTURE(test_file_rmdirs_force_delete_nested, setup_deep_dir_env, teardown_deep_dir_env);
RUN_TEST_FIXTURE(test_file_delete_success, setup_delete_env, teardown_delete_env);
RUN_TEST_FIXTURE(test_file_delete_non_existent, setup_delete_env, teardown_delete_env);
RUN_TEST_FIXTURE(test_file_delete_while_open, setup_delete_env, teardown_delete_env);
RUN_TEST_FIXTURE(test_file_copy_integrity, setup_move_copy_env, teardown_move_copy_env);
RUN_TEST_FIXTURE(test_file_move_behavior, setup_move_copy_env, teardown_move_copy_env);
RUN_TEST_FIXTURE(test_file_copy_non_existent, setup_move_copy_env, teardown_move_copy_env);
// 打印最终统计报告
TEST_REPORT();
RETURN_TEST_STATUS;
return 0;
}