#include "c_File.h" #include #include #include /* ------------------------------------------------------------------------------------------------------------------ */ /* */ 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; }