89 lines
3.9 KiB
C++
89 lines
3.9 KiB
C++
|
|
#include <gtest/gtest.h>
|
|||
|
|
#include <gmock/gmock.h>
|
|||
|
|
#include <iostream>
|
|||
|
|
#include <sstream>
|
|||
|
|
#include <csignal>
|
|||
|
|
|
|||
|
|
// 由于被测函数包含未定义行为(空指针解引用、数组越界、未初始化变量),
|
|||
|
|
// 这些函数在正常执行时会导致程序崩溃或不可预测的结果。
|
|||
|
|
// 因此,测试用例的设计侧重于验证这些函数在特定条件下的行为,
|
|||
|
|
// 例如通过信号处理或模拟环境来捕获崩溃,或者验证函数不会在非崩溃路径上产生意外输出。
|
|||
|
|
// 注意:直接调用这些函数会导致测试程序崩溃,因此测试用例使用子进程或信号处理来隔离。
|
|||
|
|
|
|||
|
|
// 辅助函数:捕获信号并验证
|
|||
|
|
void signal_handler(int sig) {
|
|||
|
|
// 空实现,仅用于捕获信号
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试 test_null_pointer 函数
|
|||
|
|
// 由于该函数会解引用空指针,预期会导致 SIGSEGV 信号
|
|||
|
|
TEST(ErrorTest, NullPointerCausesSegfault) {
|
|||
|
|
// 设置信号处理函数
|
|||
|
|
signal(SIGSEGV, signal_handler);
|
|||
|
|
|
|||
|
|
// 使用 EXPECT_EXIT 宏来验证程序因 SIGSEGV 而退出
|
|||
|
|
// 注意:由于函数直接崩溃,无法正常返回,因此使用 EXPECT_EXIT
|
|||
|
|
EXPECT_EXIT({
|
|||
|
|
test_null_pointer();
|
|||
|
|
}, ::testing::KilledBySignal(SIGSEGV), "");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试 test_array_out_of_bounds 函数
|
|||
|
|
// 数组越界访问是未定义行为,可能不会立即崩溃,但通常会导致访问非法内存
|
|||
|
|
// 这里我们使用 EXPECT_EXIT 来捕获可能的 SIGSEGV,但并非所有实现都会崩溃
|
|||
|
|
// 因此,我们也可以验证函数不会产生意外输出(如果未崩溃)
|
|||
|
|
TEST(ErrorTest, ArrayOutOfBoundsUndefinedBehavior) {
|
|||
|
|
// 由于未定义行为,测试可能不稳定。这里我们尝试捕获 SIGSEGV
|
|||
|
|
// 如果程序崩溃,测试通过;如果不崩溃,我们至少验证没有异常输出
|
|||
|
|
// 但更合理的做法是使用 EXPECT_EXIT 并允许 SIGSEGV
|
|||
|
|
EXPECT_EXIT({
|
|||
|
|
test_array_out_of_bounds();
|
|||
|
|
}, ::testing::KilledBySignal(SIGSEGV), "");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 测试 test_uninitialized_var 函数
|
|||
|
|
// 未初始化变量的使用也是未定义行为,可能不会立即崩溃
|
|||
|
|
// 我们使用 EXPECT_EXIT 来捕获可能的 SIGSEGV,但通常不会崩溃
|
|||
|
|
// 因此,我们也可以验证函数不会产生意外输出
|
|||
|
|
TEST(ErrorTest, UninitializedVarUndefinedBehavior) {
|
|||
|
|
// 未初始化变量通常不会导致崩溃,但行为不可预测
|
|||
|
|
// 这里我们尝试捕获 SIGSEGV,但预期不会发生
|
|||
|
|
// 更合理的测试是验证函数不会产生异常输出
|
|||
|
|
testing::internal::CaptureStdout();
|
|||
|
|
test_uninitialized_var();
|
|||
|
|
std::string output = testing::internal::GetCapturedStdout();
|
|||
|
|
// 由于 val 未初始化,条件可能为真或假,输出可能为空或包含 "val > 10"
|
|||
|
|
// 我们只验证没有异常发生(即函数正常返回)
|
|||
|
|
EXPECT_TRUE(true); // 函数正常返回即视为通过
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 额外的测试:验证函数在非崩溃路径下的行为
|
|||
|
|
// 由于这些函数设计为演示错误,我们也可以测试它们是否按预期产生输出
|
|||
|
|
// 但注意:直接调用会导致崩溃,因此需要特殊处理
|
|||
|
|
|
|||
|
|
// 使用子进程测试 test_null_pointer
|
|||
|
|
TEST(ErrorTest, NullPointerSubprocess) {
|
|||
|
|
// 使用 EXPECT_EXIT 验证子进程因 SIGSEGV 退出
|
|||
|
|
EXPECT_EXIT({
|
|||
|
|
test_null_pointer();
|
|||
|
|
}, ::testing::KilledBySignal(SIGSEGV), "");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用子进程测试 test_array_out_of_bounds
|
|||
|
|
TEST(ErrorTest, ArrayOutOfBoundsSubprocess) {
|
|||
|
|
// 使用 EXPECT_EXIT 验证子进程可能因 SIGSEGV 退出
|
|||
|
|
EXPECT_EXIT({
|
|||
|
|
test_array_out_of_bounds();
|
|||
|
|
}, ::testing::KilledBySignal(SIGSEGV), "");
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 使用子进程测试 test_uninitialized_var
|
|||
|
|
TEST(ErrorTest, UninitializedVarSubprocess) {
|
|||
|
|
// 未初始化变量通常不会导致崩溃,但行为不可预测
|
|||
|
|
// 我们只验证子进程正常退出(返回0)
|
|||
|
|
EXPECT_EXIT({
|
|||
|
|
test_uninitialized_var();
|
|||
|
|
exit(0);
|
|||
|
|
}, ::testing::ExitedWithCode(0), "");
|
|||
|
|
}
|