I'm using Google Test with parameterized tests and want to see the correct line number in the test output so I can quickly find the specific test case that failed.
When a test fails, the output shows the line number of the closing bracket of INSTANTIATE_TEST_SUITE_P
rather than the line where the specific test case is declared.
#include <gtest/gtest.h>
#include <string>
class TracedName {
public:
TracedName(std::string name, std::string filename, int line):
m_name(std::move(name)), m_filename(std::move(filename)), m_lineNumber(line)
{
}
std::string_view operator()() const { return m_name; }
::testing::ScopedTrace trace() const
{
return {m_filename.c_str(), m_lineNumber, m_name.c_str()};
}
private:
std::string m_name;
std::string m_filename;
int m_lineNumber;
};
#define TRACED_NAME(name) TracedName(name, __FILE__, __LINE__)
struct TestInput {
std::string value;
};
struct TestCase {
TracedName name;
TestInput input;
bool expected;
};
class ParameterizedTestSuite : public ::testing::TestWithParam<TestCase> {};
TEST_P(ParameterizedTestSuite, Test) {
const auto& param = GetParam();
const auto trace = param.name.trace(); // Trying to get the trace to point to the test case
EXPECT_EQ(param.input.value == "valid", param.expected);
}
INSTANTIATE_TEST_SUITE_P(
MyTests,
ParameterizedTestSuite,
::testing::Values(
TestCase{
.name = TRACED_NAME("ValidInput"),
.input = {.value = "valid"},
.expected = true,
},
TestCase{
.name = TRACED_NAME("InvalidInput"), // I want error message to point to THIS line
.input = {.value = "invalid"},
.expected = true, // This will fail
}
)
);
When the test fails, I get an output with line numbers pointing to where INSTANTIATE_TEST_SUITE_P
ends, not where the failing test case is declared.
I want the output to show the line number where TRACED_NAME("InvalidInput")
is defined, so I can quickly go to the failing test case when looking at the terminal output.
I suspect the problem is caused by nested macro calls. Is this even possible?
https://godbolt.org/z/8Tfqesh7e with GCC the problem is not reproduced. The actual project uses 14.42.34433 MSVC Build Tools, there the printed line is not correct
This is not an issue with Google Test, but rather how __LINE__
is interpreted by the compiler in a macro expansion (in your case INSTANTIATE_TEST_SUITE_P
). gcc and clang see __LINE__
at the expected location, while MSVC puts it on the closing parentheses of the macro.
std::source_location::line()
gives yet again other results, and there gcc and clang do not agree anymore.
Example (godbolt):
1. #include <iostream>
2. #include <source_location>
3.
4. using namespace std;
5.
6. #define PRINT_LINE cout << "__LINE__ macro = " << __LINE__ << endl;
7. #define PRINT_SRC_LOC cout << "source_location macro = " << 8. source_location::current().line() << endl;
8.
9. #define DO_STUFF(x) x
10.
11. int main() {
12. DO_STUFF(
13.
14. PRINT_LINE PRINT_SRC_LOC cout << "__LINE__ direct = " << __LINE__ << endl; cout << "source_location direct = " << source_location::current().line() << endl;
15.
16. )
17. }
gcc 14.2:
__LINE__ macro = 14
source_location macro = 12
__LINE__ direct = 14
source_location direct = 12
clang 20.1.0:
__LINE__ macro = 14
source_location macro = 16
__LINE__ direct = 14
source_location direct = 16
MSVC 19.40:
__LINE__ macro = 16
source_location macro = 12
__LINE__ direct = 16
source_location direct = 12
So: __LINE__
works as expected for gcc/clang, while MSVC sees it at the closing parenthesis. Moreover, std::source_location::line()
is seen by gcc/MSVC at the opening parenthesis, while clang sees it at the closing parenthesis.
To me the C++ standard sounds as if std::source_location::line()
should give the same result as __LINE__
, but it doesn't.
Moreover, the standard doesn't seem to define the interaction between macro expansion and __LINE__
, compare this answer. As an additional note, C (rather than C++) also does not really define the behavior, see e.g. this gcc discussion and the references therein (especially N2115).
The simplest solution would be to store the ::testing::Values(...)
in a global variable (with internal linkage, i.e. marked as static
or in an anonymous namespace, so that it cannot clash with identical names in other translation units). As a result, the TRACED_NAME
is not within an argument of the macro INSTANTIATE_TEST_SUITE_P
, and the line numbers will be correct:
static const auto MyTestValues = ::testing::Values(
TestCase{
.name = TRACED_NAME("ValidInput"),
.input = {.value = "valid"},
.expected = true,
},
TestCase{
.name = TRACED_NAME("InvalidInput"),
.input = {.value = "invalid"},
.expected = true,
}
);
INSTANTIATE_TEST_SUITE_P(
MyTests,
ParameterizedTestSuite,
MyTestValues
);