diff --git a/tests/test_errors.cpp b/tests/test_errors.cpp new file mode 100644 index 0000000..6fc1de8 --- /dev/null +++ b/tests/test_errors.cpp @@ -0,0 +1,133 @@ +#include "gtest/gtest.h" +#include "src/errors.cpp" +#include +#include +#include + +// 信号处理跳转缓冲区,用于捕获崩溃 +static jmp_buf jump_buffer; + +// 信号处理函数,用于捕获段错误等信号 +void signal_handler(int signum) { + longjmp(jump_buffer, 1); +} + +// 测试夹具类 +class ErrorsTest : public ::testing::Test { +protected: + // 保存原始信号处理器 + void (*original_signal_handler)(int); + + void SetUp() override { + // 设置自定义信号处理器来捕获段错误 + original_signal_handler = signal(SIGSEGV, signal_handler); + } + + void TearDown() override { + // 恢复原始信号处理器 + signal(SIGSEGV, original_signal_handler); + } +}; + +// 测试 test_null_pointer 函数 +TEST_F(ErrorsTest, test_null_pointer_should_crash) { + // 使用 setjmp/longjmp 来捕获预期的崩溃 + if (setjmp(jump_buffer) == 0) { + // 这应该触发段错误 + test_null_pointer(); + // 如果执行到这里,说明没有崩溃,测试失败 + FAIL() << "Expected test_null_pointer to crash due to null pointer dereference"; + } else { + // 成功捕获到信号,测试通过 + SUCCEED(); + } +} + +// 测试 test_array_out_of_bounds 函数 +TEST_F(ErrorsTest, test_array_out_of_bounds_should_crash) { + // 使用 setjmp/longjmp 来捕获预期的崩溃 + if (setjmp(jump_buffer) == 0) { + // 这应该触发段错误或未定义行为 + test_array_out_of_bounds(); + // 如果执行到这里,说明没有崩溃,测试失败 + FAIL() << "Expected test_array_out_of_bounds to crash due to array out of bounds"; + } else { + // 成功捕获到信号,测试通过 + SUCCEED(); + } +} + +// 测试 test_uninitialized_var 函数 +TEST_F(ErrorsTest, test_uninitialized_var_has_undefined_behavior) { + // 未初始化变量的行为是未定义的,可能不会崩溃 + // 我们只能验证函数可以执行而不崩溃(或者可能崩溃) + // 由于行为未定义,我们主要测试函数能够被调用 + + // 注意:未初始化变量的行为是未定义的,可能不会立即导致崩溃 + // 但可能导致不可预测的结果 + + // 我们可以多次调用该函数,观察是否有时会崩溃 + bool crashed_at_least_once = false; + + for (int i = 0; i < 10; i++) { + if (setjmp(jump_buffer) == 0) { + test_uninitialized_var(); + // 没有崩溃,继续下一次迭代 + } else { + // 发生了崩溃 + crashed_at_least_once = true; + // 重置跳转缓冲区以进行下一次迭代 + signal(SIGSEGV, signal_handler); + } + } + + // 由于未初始化变量的行为是未定义的,我们无法做出确定的断言 + // 但我们可以记录观察到的行为 + if (crashed_at_least_once) { + std::cout << "Note: test_uninitialized_var caused a crash in at least one iteration" << std::endl; + } else { + std::cout << "Note: test_uninitialized_var did not cause a crash in any iteration" << std::endl; + } + + // 对于未定义行为,我们无法做出确定的断言 + // 但测试至少验证了函数可以被调用 + SUCCEED(); +} + +// 边界条件测试:验证这些错误函数确实会导致问题 +TEST_F(ErrorsTest, verify_error_functions_are_dangerous) { + // 这个测试验证所有三个函数都存在潜在的危险行为 + // 通过尝试执行它们并观察是否发生崩溃 + + int dangerous_functions = 3; + int detected_danger = 0; + + // 测试 test_null_pointer + if (setjmp(jump_buffer) == 0) { + test_null_pointer(); + } else { + detected_danger++; + signal(SIGSEGV, signal_handler); + } + + // 测试 test_array_out_of_bounds + if (setjmp(jump_buffer) == 0) { + test_array_out_of_bounds(); + } else { + detected_danger++; + signal(SIGSEGV, signal_handler); + } + + // 测试 test_uninitialized_var (可能不会崩溃,但行为未定义) + // 我们调用它但不检查崩溃,因为未初始化变量的行为是未定义的 + test_uninitialized_var(); + + // 至少前两个函数应该被检测为危险的 + EXPECT_GE(detected_danger, 2) << "At least two of the error functions should be detected as dangerous"; +} + +// 主函数 +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/tests/test_memory.cpp b/tests/test_memory.cpp new file mode 100644 index 0000000..e7149c1 --- /dev/null +++ b/tests/test_memory.cpp @@ -0,0 +1,78 @@ +#include "gtest/gtest.h" +#include "src/memory.cpp" +#include +#include +#include + +// 注意:由于源文件中的函数旨在演示错误,直接调用它们会导致未定义行为(如崩溃)。 +// 因此,测试用例将主要验证这些函数的存在性,并说明在正常使用中应避免的错误模式。 +// 对于内存泄漏和文件泄漏,我们无法在测试中断言其发生,但可以验证函数执行后没有立即崩溃。 +// 对于重复释放,我们可以预期程序会崩溃,但这在单元测试中难以安全捕获。 +// 以下测试用例旨在作为教学示例,展示如何为这类函数构建测试框架。 + +// 测试 test_memory_leak 函数的存在性和基本执行(不验证泄漏本身) +TEST(MemoryTest, MemoryLeakFunctionExists) { + // 该函数应该可以调用而不立即导致程序崩溃(尽管会导致内存泄漏) + // 我们仅验证函数可以被调用。在实际项目中,应使用内存检测工具(如Valgrind)来验证泄漏。 + EXPECT_NO_FATAL_FAILURE(test_memory_leak()); + // 注意:EXPECT_NO_FATAL_FAILURE 仅保证测试进程本身不因致命错误而终止。 + // 它无法检测到内存泄漏。 +} + +// 测试 test_double_free 函数的存在性 +// 警告:直接调用此函数会导致未定义行为(通常是崩溃)。 +// 在单元测试环境中,我们无法安全地测试重复释放,因为它会使整个测试进程不稳定。 +// 因此,此测试被标记为 DISABLED,不会在常规测试中运行。 +// 它仅作为示例,说明如何标记危险的测试。 +TEST(MemoryTest, DISABLED_DoubleFreeFunctionExists) { + // 此测试被禁用,因为 test_double_free() 会导致崩溃。 + // 在实际场景中,应通过代码审查或静态分析来避免此类错误。 + // test_double_free(); // 这行代码被注释掉,因为调用它会崩溃。 + SUCCEED(); // 仅表示测试框架存在 +} + +// 测试 test_file_leak 函数的存在性和基本执行 +TEST(MemoryTest, FileLeakFunctionExists) { + // 调用函数,验证其可以执行而不立即崩溃。 + // 文件 "test.txt" 可能会被创建并保持打开状态。 + EXPECT_NO_FATAL_FAILURE(test_file_leak()); + // 可选:检查文件是否被创建(但请注意,文件句柄仍处于泄漏状态)。 + // 由于文件可能被其他进程锁定,此处不进行断言。 + // 清理:尝试删除可能创建的文件(如果存在且未被锁定)。 + std::remove("test.txt"); +} + +// 边界和异常测试:由于这些函数没有参数,无法进行传统的边界值测试。 +// 但我们可以创建辅助测试来演示正确的内存和文件管理实践,作为对比。 +TEST(MemoryTest, CorrectMemoryManagement) { + // 正确分配和释放内存 + int* data = new int[100]; + ASSERT_NE(data, nullptr); + data[0] = 42; + EXPECT_EQ(data[0], 42); + delete[] data; // 正确释放 + // 注意:释放后不应再访问 data。 +} + +TEST(MemoryTest, CorrectFileManagement) { + // 正确打开和关闭文件(使用C++风格,但原理相同) + const char* filename = "correct_test.txt"; + FILE* fp = fopen(filename, "w"); + ASSERT_NE(fp, nullptr); + fprintf(fp, "Hello, world!"); + int closeResult = fclose(fp); + EXPECT_EQ(closeResult, 0); // fclose 成功应返回 0 + // 验证文件内容(可选) + std::ifstream inFile(filename); + std::string content; + std::getline(inFile, content); + EXPECT_EQ(content, "Hello, world!"); + inFile.close(); + std::remove(filename); // 清理测试文件 +} + +// 主函数,通常由 gtest 框架提供,但在此文件中明确写出以确保可编译。 +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +}