91 lines
3.4 KiB
C++
91 lines
3.4 KiB
C++
|
|
#include "gtest/gtest.h"
|
|||
|
|
#include "src/errors.cpp"
|
|||
|
|
#include <iostream>
|
|||
|
|
#include <csetjmp>
|
|||
|
|
#include <csignal>
|
|||
|
|
|
|||
|
|
// 全局跳转点,用于处理信号
|
|||
|
|
static std::jmp_buf jump_buffer;
|
|||
|
|
|
|||
|
|
// 信号处理函数
|
|||
|
|
static void signal_handler(int sig) {
|
|||
|
|
std::longjmp(jump_buffer, 1);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试夹具类
|
|||
|
|
class ErrorsTest : public ::testing::Test {
|
|||
|
|
protected:
|
|||
|
|
// 保存旧的信号处理器
|
|||
|
|
struct sigaction old_action{};
|
|||
|
|
struct sigaction new_action{};
|
|||
|
|
|
|||
|
|
void SetUp() override {
|
|||
|
|
// 设置 SIGSEGV 和 SIGABRT 的信号处理器
|
|||
|
|
new_action.sa_handler = signal_handler;
|
|||
|
|
sigemptyset(&new_action.sa_mask);
|
|||
|
|
new_action.sa_flags = 0;
|
|||
|
|
|
|||
|
|
sigaction(SIGSEGV, &new_action, &old_action);
|
|||
|
|
sigaction(SIGABRT, &new_action, nullptr);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
void TearDown() override {
|
|||
|
|
// 恢复旧的信号处理器
|
|||
|
|
sigaction(SIGSEGV, &old_action, nullptr);
|
|||
|
|
}
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// 测试 test_null_pointer 函数
|
|||
|
|
// 该函数会解引用空指针,预期会触发段错误 (SIGSEGV) 或程序终止
|
|||
|
|
TEST_F(ErrorsTest, test_null_pointer_TriggersCrash) {
|
|||
|
|
// 设置跳转点,如果发生信号则跳转回来
|
|||
|
|
if (setjmp(jump_buffer) == 0) {
|
|||
|
|
// 尝试调用会崩溃的函数
|
|||
|
|
test_null_pointer();
|
|||
|
|
// 如果执行到这里,说明没有崩溃,测试失败
|
|||
|
|
FAIL() << "Expected test_null_pointer to cause a crash (SIGSEGV), but it didn't.";
|
|||
|
|
} else {
|
|||
|
|
// 成功捕获到信号,测试通过
|
|||
|
|
SUCCEED();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试 test_array_out_of_bounds 函数
|
|||
|
|
// 该函数会访问数组越界,预期会触发段错误 (SIGSEGV) 或程序终止
|
|||
|
|
TEST_F(ErrorsTest, test_array_out_of_bounds_TriggersCrash) {
|
|||
|
|
if (setjmp(jump_buffer) == 0) {
|
|||
|
|
test_array_out_of_bounds();
|
|||
|
|
FAIL() << "Expected test_array_out_of_bounds to cause a crash (SIGSEGV), but it didn't.";
|
|||
|
|
} else {
|
|||
|
|
SUCCEED();
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试 test_uninitialized_var 函数
|
|||
|
|
// 该函数使用未初始化的变量,行为是未定义的。
|
|||
|
|
// 我们无法可靠地测试其输出,但可以验证函数能运行而不崩溃(尽管结果不可预测)。
|
|||
|
|
// 注意:这是一个“正常”路径测试,因为函数定义本身没有语法错误,可以执行。
|
|||
|
|
TEST_F(ErrorsTest, test_uninitialized_var_RunsWithoutCrash) {
|
|||
|
|
// 由于使用未初始化变量是未定义行为,它可能崩溃也可能不崩溃。
|
|||
|
|
// 我们只测试函数可以被调用而不导致必然的、可捕获的崩溃(如SIGSEGV)。
|
|||
|
|
// 我们使用一个 try-catch 块,但C++标准异常通常捕获不了内存错误。
|
|||
|
|
// 因此,我们设置一个信号处理器,如果它因未初始化变量而意外崩溃,我们也能捕获。
|
|||
|
|
if (setjmp(jump_buffer) == 0) {
|
|||
|
|
test_uninitialized_var();
|
|||
|
|
// 如果执行到这里,说明没有触发我们设置的信号处理器(SIGSEGV/SIGABRT)。
|
|||
|
|
// 这被认为是测试通过,因为函数执行完成了。
|
|||
|
|
// 我们无法断言它的输出,因为 val 是未定义的。
|
|||
|
|
SUCCEED();
|
|||
|
|
} else {
|
|||
|
|
// 如果它意外地因为未初始化变量而崩溃了,我们也记录下来。
|
|||
|
|
// 这在某些平台/编译器上是有可能的。
|
|||
|
|
ADD_FAILURE() << "test_uninitialized_var caused a signal. This is possible UB.";
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 主函数
|
|||
|
|
int main(int argc, char **argv) {
|
|||
|
|
::testing::InitGoogleTest(&argc, argv);
|
|||
|
|
return RUN_ALL_TESTS();
|
|||
|
|
}
|