如何解决Catch2 GENERATE 宏在内部如何工作?
最近我了解了 Catch2 中的 GENERATE
宏(来自 this video)。现在我很好奇它在内部是如何工作的。
天真的人会认为对于带有 k
个生成器的测试用例(生成器我指的是一个 GENERATE
调用站点),Catch2 只运行每个测试用例 n1 * n2 * ... * nk
次,其中 { {1}} 是第 ni
个生成器中的元素数,每次指定来自这些 i
生成器的不同值组合。事实上,这个幼稚的规范似乎适用于一个简单的测试用例:
k
正如预期的那样,输出是:
TEST_CASE("Naive") {
auto x = GENERATE(0,1);
auto y = GENERATE(2,3);
std::cout << "x = " << x << ",y = " << y << std::endl;
}
表示测试用例运行了 x = 0,y = 2
x = 0,y = 3
x = 1,y = 2
x = 1,y = 3
次。
然而,似乎catch并没有天真地实现它,如下例所示:
2 * 2 == 4
在上述情况下,TEST_CASE("Depends on if") {
auto choice = GENERATE(0,1);
int x = -1,y = -1;
if (choice == 0) {
x = GENERATE(2,3);
} else {
y = GENERATE(4,5);
}
std::cout << "choice = " << choice << ",x = " << x << ",y = " << y << std::endl;
}
的实际调用(不是调用站点)取决于 GENERATE
。如果逻辑被天真地实现,人们会期望有 8 行输出(从 choice
起):
2 * 2 * 2 == 8
注意重复的行:即使没有实际调用生成器,朴素的排列仍然会排列生成器的值。例如,choice = 0,x = 2,y = -1
choice = 0,x = 3,y = -1
choice = 1,x = -1,y = 4
choice = 1,y = 5
choice = 1,y = 5
仅在 y = GENERATE(4,5)
时被调用,然而,即使在 choice == 1
时,实现仍会置换值 4 和 5,即使它们没有被使用。
实际输出是:
choice != 1
没有重复的行。这让我怀疑 Catch 在内部使用堆栈来跟踪调用的生成器及其最近调用的顺序。每次测试用例完成一次迭代时,它以相反的顺序遍历调用的生成器,并推进每个生成器的值。如果这种推进失败(即生成器内的值序列完成),则该生成器将重置为其初始状态(即准备好按顺序发出第一个值);否则(推进成功),遍历结束。
在伪代码中它看起来像:
choice = 0,y = 5
这完美地解释了前面的情况。但这并不能解释这个(相当晦涩的):
for each generator that is invoked in reverse order of latest invocation:
bool success = generator.moveNext();
if success: break;
generator.reset();
人们会期望这会运行 TEST_CASE("Non structured generators") {
int x = -1,y = -1;
for (int i = 0; i <= 1; ++i) {
x = GENERATE(0,1);
if (i == 1) break;
y = GENERATE(2,3);
}
std::cout << x << "," << y << std::endl;
}
次,并且输出是:
4 == 2 * 2
(x = 0,y = 3
在 x
之前发生变化,因为 y
是最后调用的生成器)
然而,这不是 catch 实际上所做的,而是现实中发生的事情:
x = GENERATE(0,1)
8 行输出,即前四行重复两次。
所以我的问题是,Catch2 中的 x = 0,y = 3
x = 0,y = 3
究竟是如何实现的?我不是在寻找详细的代码,而是在寻找可以解释我在前面的示例中看到的内容的高级描述。
解决方法
也许您可以尝试使用 GCC 中的 -E 选项查看预处理器后生成的代码。
交流:
GENERATE(0,1)
gcc -E -CC a.c
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。