AutoTest: Fix gtest detection

If a test name starts with a number the codemodel handles
the function-like macro differently and does not generate
the arguments as it would do when using normal literals.
Work around by explicitly handling the expression tokens.

Fixes: QTCREATORBUG-25498
Change-Id: Ibf381af912403cb7b87c1c30b5a8fc7043c16056
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Christian Stenger
2021-03-19 13:21:30 +01:00
parent 0c01ef9998
commit 5d5ff57495
3 changed files with 69 additions and 26 deletions

View File

@@ -213,7 +213,7 @@ void AutoTestUnitTests::testCodeParserGTest()
QVERIFY(parserSpy.wait(20000));
QVERIFY(modelUpdateSpy.wait());
QCOMPARE(m_model->gtestNamesCount(), 7);
QCOMPARE(m_model->gtestNamesCount(), 8);
QMultiMap<QString, int> expectedNamesAndSets;
expectedNamesAndSets.insert(QStringLiteral("FactorialTest"), 3);
@@ -222,6 +222,7 @@ void AutoTestUnitTests::testCodeParserGTest()
expectedNamesAndSets.insert(QStringLiteral("QueueTest"), 2);
expectedNamesAndSets.insert(QStringLiteral("DummyTest"), 1); // used as parameterized test
expectedNamesAndSets.insert(QStringLiteral("DummyTest"), 1); // used as 'normal' test
expectedNamesAndSets.insert(QStringLiteral("NumberAsNameStart"), 1);
expectedNamesAndSets.insert(QStringLiteral("NamespaceTest"), 1);
QMultiMap<QString, int> foundNamesAndSets = m_model->gtestNamesAndSets();

View File

@@ -29,6 +29,8 @@
#include <cplusplus/LookupContext.h>
#include <utils/qtcassert.h>
#include <QRegularExpression>
namespace Autotest {
namespace Internal {
@@ -45,7 +47,7 @@ bool GTestVisitor::visit(CPlusPlus::FunctionDefinitionAST *ast)
return false;
CPlusPlus::DeclaratorIdAST *id = ast->declarator->core_declarator->asDeclaratorId();
if (!id || !ast->symbol || ast->symbol->argumentCount() != 2)
if (!id || !ast->symbol)
return false;
QString prettyName =
@@ -61,33 +63,68 @@ bool GTestVisitor::visit(CPlusPlus::FunctionDefinitionAST *ast)
if (!GTestUtils::isGTestMacro(prettyName))
return false;
CPlusPlus::Argument *testCaseNameArg = ast->symbol->argumentAt(0)->asArgument();
CPlusPlus::Argument *testNameArg = ast->symbol->argumentAt(1)->asArgument();
if (testCaseNameArg && testNameArg) {
const QString &testCaseName = m_overview.prettyType(testCaseNameArg->type());
const QString &testName = m_overview.prettyType(testNameArg->type());
QString testSuiteName;
QString testCaseName;
if (ast->symbol->argumentCount() != 2 && ast->declarator->initializer) {
// we might have a special case when second parameter is a literal starting with a number
if (auto expressionListParenAST = ast->declarator->initializer->asExpressionListParen()) {
// only try if we have 3 tokens between left and right paren (2 parameters and a comma)
if (expressionListParenAST->rparen_token - expressionListParenAST->lparen_token != 4)
return false;
const bool disabled = testName.startsWith(disabledPrefix);
const bool disabledCase = testCaseName.startsWith(disabledPrefix);
int line = 0;
int column = 0;
unsigned token = id->firstToken();
m_document->translationUnit()->getTokenStartPosition(token, &line, &column);
const CPlusPlus::Token parameter1
= translationUnit()->tokenAt(expressionListParenAST->lparen_token + 1);
const CPlusPlus::Token parameter2
= translationUnit()->tokenAt(expressionListParenAST->rparen_token - 1);
const CPlusPlus::Token comma
= translationUnit()->tokenAt(expressionListParenAST->lparen_token + 2);
if (!comma.is(CPlusPlus::T_COMMA))
return false;
GTestCodeLocationAndType locationAndType;
locationAndType.m_name = testName;
locationAndType.m_line = line;
locationAndType.m_column = column - 1;
locationAndType.m_type = TestTreeItem::TestCase;
locationAndType.m_state = disabled ? GTestTreeItem::Disabled
: GTestTreeItem::Enabled;
GTestCaseSpec spec;
spec.testCaseName = testCaseName;
spec.parameterized = GTestUtils::isGTestParameterized(prettyName);
spec.typed = GTestUtils::isGTestTyped(prettyName);
spec.disabled = disabledCase;
m_gtestFunctions[spec].append(locationAndType);
testSuiteName = QString::fromUtf8(parameter1.spell());
testCaseName = QString::fromUtf8(parameter2.spell());
// test (suite) name needs to be a alpha numerical literal ( _ and $ allowed)
const QRegularExpression alnum("^[[:alnum:]_$]+$");
// test suite must not start with a number, test case may
if (!alnum.match(testSuiteName).hasMatch()
|| (!testSuiteName.isEmpty() && testSuiteName.at(0).isNumber())) {
testSuiteName.clear();
}
if (!alnum.match(testCaseName).hasMatch())
testCaseName.clear();
}
} else {
const CPlusPlus::Argument *testSuiteNameArg = ast->symbol->argumentAt(0)->asArgument();
const CPlusPlus::Argument *testCaseNameArg = ast->symbol->argumentAt(1)->asArgument();
if (testSuiteNameArg && testCaseNameArg) {
testSuiteName = m_overview.prettyType(testSuiteNameArg->type());
testCaseName = m_overview.prettyType(testCaseNameArg->type());
}
}
if (testSuiteName.isEmpty() || testCaseName.isEmpty())
return false;
const bool disabled = testCaseName.startsWith(disabledPrefix);
const bool disabledCase = testSuiteName.startsWith(disabledPrefix);
int line = 0;
int column = 0;
unsigned token = id->firstToken();
m_document->translationUnit()->getTokenStartPosition(token, &line, &column);
GTestCodeLocationAndType locationAndType;
locationAndType.m_name = testCaseName;
locationAndType.m_line = line;
locationAndType.m_column = column - 1;
locationAndType.m_type = TestTreeItem::TestCase;
locationAndType.m_state = disabled ? GTestTreeItem::Disabled
: GTestTreeItem::Enabled;
GTestCaseSpec spec;
// FIXME GTestCaseSpec structure wrong nowadays (suite vs case / case vs function)
spec.testCaseName = testSuiteName;
spec.parameterized = GTestUtils::isGTestParameterized(prettyName);
spec.typed = GTestUtils::isGTestTyped(prettyName);
spec.disabled = disabledCase;
m_gtestFunctions[spec].append(locationAndType);
return false;
}

View File

@@ -70,6 +70,11 @@ TEST(FactorialTest_Iterative, DISABLED_HandlesPositiveInput)
ASSERT_EQ(40320, factorial_it(8));
}
TEST(NumberAsNameStart, 1IsEnough)
{
EXPECT_FALSE(false);
}
int main(int argc, char *argv[])
{
::testing::InitGoogleTest(&argc, argv);