102 lines
5.1 KiB
C++
102 lines
5.1 KiB
C++
|
|
#include <gtest/gtest.h>
|
|||
|
|
#include "test_errors.h"
|
|||
|
|
#include <cstdlib>
|
|||
|
|
#include <iostream>
|
|||
|
|
#include <fstream>
|
|||
|
|
#include <string>
|
|||
|
|
#include <cstdio>
|
|||
|
|
|
|||
|
|
// 测试主函数 main
|
|||
|
|
TEST(MainTest, MainExecutesAllErrorFunctions) {
|
|||
|
|
// 由于 main 函数调用了一系列会触发错误或副作用的函数,
|
|||
|
|
// 直接测试其返回值可能不够。
|
|||
|
|
// 策略:通过检查标准输出/错误或程序退出码来验证函数被调用。
|
|||
|
|
// 注意:这依赖于 test_errors.h 中函数的实现(例如,它们可能打印信息)。
|
|||
|
|
// 假设这些函数在出错时会打印特定信息到 stderr 或导致非零退出码(如果被包装)。
|
|||
|
|
// 更健壮的方法是使用死亡测试(death test)来验证程序是否因特定错误而终止。
|
|||
|
|
// 这里我们演示两种方法:
|
|||
|
|
|
|||
|
|
// 方法1:直接调用 main 并检查其返回值(如果函数设计为返回0)。
|
|||
|
|
// 由于 main 函数总是返回 0,这个测试价值有限,但可以验证链接和基本执行。
|
|||
|
|
// 为了隔离测试,我们通常不直接测试 main,而是测试其调用的函数。
|
|||
|
|
// 因此,这个测试用例主要作为集成测试的占位符。
|
|||
|
|
// 在实际项目中,main 函数的逻辑应尽可能简单,将测试重点放在其调用的函数上。
|
|||
|
|
|
|||
|
|
// 方法2:使用死亡测试验证某些错误函数会导致程序终止(例如,空指针解引用)。
|
|||
|
|
// 注意:这需要 test_errors.h 中的函数确实会导致程序崩溃(如 test_null_pointer)。
|
|||
|
|
// 假设 test_null_pointer() 会解引用空指针导致段错误。
|
|||
|
|
// 使用 ASSERT_DEATH 来验证。
|
|||
|
|
// 注意:死亡测试在某些环境(如 Windows 或某些 sanitizers)下可能不可靠。
|
|||
|
|
// 这里我们注释掉,因为 test_errors.h 的实现未知,可能被修改为不崩溃。
|
|||
|
|
/*
|
|||
|
|
ASSERT_DEATH({
|
|||
|
|
test_null_pointer();
|
|||
|
|
}, ".*"); // 匹配任何错误输出
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// 由于上述不确定性,我们采用一个更安全的测试:
|
|||
|
|
// 验证 main 函数可以正常编译、链接并执行到返回,没有抛出异常。
|
|||
|
|
// 这通过调用 main 函数并检查其返回值(应为0)来实现。
|
|||
|
|
// 注意:直接调用 main 函数在 C++ 标准中通常是允许的,但有些编译器可能警告。
|
|||
|
|
// 我们使用 extern "C" 来避免名称修饰问题(如果 main 是 C++函数)。
|
|||
|
|
// 实际上,main 函数是全局函数,可以直接调用。
|
|||
|
|
testing::internal::CaptureStdout(); // 捕获 stdout 以避免测试输出干扰
|
|||
|
|
testing::internal::CaptureStderr(); // 捕获 stderr
|
|||
|
|
int result = main(); // 调用 main 函数
|
|||
|
|
std::string output = testing::internal::GetCapturedStdout();
|
|||
|
|
std::string errors = testing::internal::GetCapturedStderr();
|
|||
|
|
|
|||
|
|
// 断言 main 返回 0
|
|||
|
|
EXPECT_EQ(result, 0);
|
|||
|
|
|
|||
|
|
// 可选:断言没有标准错误输出(如果错误函数被设计为静默)。
|
|||
|
|
// 或者断言有错误输出(如果错误函数打印了信息)。
|
|||
|
|
// 这里我们不做具体断言,因为 test_errors.h 的实现未知。
|
|||
|
|
// 可以输出以供调试
|
|||
|
|
// std::cout << "stdout: " << output << std::endl;
|
|||
|
|
// std::cout << "stderr: " << errors << std::endl;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 边界条件测试:无参数 main 函数(本身就是无参数)
|
|||
|
|
// 此测试用例强调 main 函数不接受参数,但我们的 main 函数确实如此。
|
|||
|
|
// 没有实际的边界值需要测试,因为 main 函数签名固定。
|
|||
|
|
|
|||
|
|
// 异常输入测试:不适用,因为 main 函数无参数。
|
|||
|
|
|
|||
|
|
// 特殊场景测试:验证 main 函数在多次调用下的行为(如果需要)。
|
|||
|
|
// 注意:多次调用 main 通常不是预期用法,但我们可以测试其幂等性(如果函数设计如此)。
|
|||
|
|
TEST(MainTest, MainCanBeCalledMultipleTimes) {
|
|||
|
|
// 第一次调用
|
|||
|
|
testing::internal::CaptureStdout();
|
|||
|
|
testing::internal::CaptureStderr();
|
|||
|
|
int result1 = main();
|
|||
|
|
std::string output1 = testing::internal::GetCapturedStdout();
|
|||
|
|
std::string errors1 = testing::internal::GetCapturedStderr();
|
|||
|
|
EXPECT_EQ(result1, 0);
|
|||
|
|
|
|||
|
|
// 第二次调用
|
|||
|
|
testing::internal::CaptureStdout();
|
|||
|
|
testing::internal::CaptureStderr();
|
|||
|
|
int result2 = main();
|
|||
|
|
std::string output2 = testing::internal::GetCapturedStdout();
|
|||
|
|
std::string errors2 = testing::internal::GetCapturedStderr();
|
|||
|
|
EXPECT_EQ(result2, 0);
|
|||
|
|
|
|||
|
|
// 可选:比较两次调用的输出是否一致(如果函数有确定输出)。
|
|||
|
|
// 由于 test_errors.h 中的函数可能有副作用(如内存泄漏),输出可能不同。
|
|||
|
|
// 这里我们不做断言。
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 集成测试:验证 main 函数调用的所有函数都被执行。
|
|||
|
|
// 这可以通过 mock 或 spy 来实现,但这里我们假设 test_errors.h 中的函数
|
|||
|
|
// 有可观察的副作用(如写入文件、全局变量)。
|
|||
|
|
// 由于 test_errors.h 的实现未知,我们无法编写具体断言。
|
|||
|
|
// 作为示例,我们假设 test_file_leak() 会创建一个临时文件。
|
|||
|
|
// 我们可以检查文件是否被创建(如果知道文件名)。
|
|||
|
|
// 这里我们跳过,因为需要知道实现细节。
|
|||
|
|
|
|||
|
|
int main(int argc, char **argv) {
|
|||
|
|
::testing::InitGoogleTest(&argc, argv);
|
|||
|
|
return RUN_ALL_TESTS();
|
|||
|
|
}
|