为什么link顺序会影响使用宏的测试的测试结果?

Why does link order affect the test result of a test that uses macros?

我正在使用 Google 测试和 Google 模拟来使用测试驱动开发编写代码。我正在为 Google 测试编写一个小的 "plugin"。为了确保测试会在预期的正确消息时失败,我创建了一个简单的 FailureReporter class ,我将子 class 并在插件中注入模拟版本以捕获结果并与预期值进行比较。

本着 TDD 极端主义的精神,我还编写了一个测试来确保 FailureReporter 有效。为此,我 "replaced" FAIL() 宏是为了不导致失败,而是为了捕获生成的错误消息。然而,这就是事情变得奇怪的地方。它在一台机器上工作,但在另一台机器上不工作。在寻找原因时,我发现可以通过更改 link 顺序来修复它。这看起来很奇怪,因为 "fix" 是使用宏完成的,我认为它会在编译时进行硬编码,并且 link 不会有什么不同。

我发现:link测试本身没有问题。将它与使用 FailureReporter 的模拟版本的测试链接只有在另一个测试在 FailureReporterTest. 之后 linked 时才有效 为什么会发生这种情况?

FailureReporter.hpp:

#ifndef FAILURE_REPORTER_H
#define FAILURE_REPORTER_H

#include "gtest/gtest.h"

class FailureReporter {
public:
    virtual void fail(const char* errorMessage) {
        FAIL() << errorMessage;
    }
};

#endif

FailureReporterTest.cpp:

#include <sstream>

#include "gtest/gtest.h"

static std::stringstream* failStream;

#ifdef FAIL
#undef FAIL
#endif

#define FAIL() *failStream << ""

#include "FailureReporter.hpp"

TEST(FailureReporterTest, failMethod) {
    const char* errorMessage = "Test error message";
    FailureReporter *reporter;
    std::stringstream stream;

    failStream = &stream;

    reporter = new FailureReporter();

    reporter->fail(errorMessage);

    EXPECT_STREQ(errorMessage, stream.str().c_str());

    delete reporter;
}

MockFailureReporter.cpp

#ifndef MOCK_FAILURE_REPORTER_HPP
#define MOCK_FAILURE_REPORTER_HPP

#include "gmock/gmock.h"

#include "FailureReporter.hpp"

class MockFailureReporter : public FailureReporter {
public:
    MOCK_METHOD1(fail, void(const char*));
};

#endif

DummyTest.cpp

#include "gtest/gtest.h"

#include "MockFailureReporter.hpp"

TEST(DummyTest, dummy) {
    new MockFailureReporter();
    SUCCEED();
}

使用

编译源代码
g++ -c DummyTest.cpp
g++ -c FailureReporterTest.cpp

和link将它们与

g++ DummyTest.o FailureReporterTest.o -pthread -lgtest -lgmock -lgmock_main

生成未通过 failMethod 测试的 a.out 可执行文件,同时 link 使用

对它们进行测试
g++ FailureReporterTest.o DummyTest.o -pthread -lgtest -lgmock -lgmock_main

生成成功通过两个测试的 a.out 可执行文件。为什么?

class FailureReporter {
public:
  virtual void fail(const char* errorMessage) {
    /* code */

创建 FailureReporter::fail 的隐式 inline 实现。

inline 函数被导出。如果 linker 看到两个同名同类型的 inline 函数,它会默默地丢弃一个。如果它们实际上不相同,那么您的程序格式不正确,不需要诊断。

当您仅在一个编译单元中重新定义 FAIL() 并在其他编译单元中重新定义 link 时,您最终会得到两个 FailureReporter::fail 的定义。他们 link 和未定义的行为结果。在你的情况下,你在两种情况下都会得到其中之一 运行,确定了 linker 选择丢弃内联冲突的任意规则。看起来像 'keep first one I see'.