forked from qt-creator/qt-creator
AutoTest: Take precompiled headers into account
Test frameworks might be added to the precompiled headers. This in turn would make some pre-checks whether a file has to be processed or not fail. Fixes: QTCREATORBUG-25821 Change-Id: Iff69c1a83889cb6f79a3e3f9b2e59c5383989ccd Reviewed-by: Christian Stenger <christian.stenger@qt.io> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -84,7 +84,9 @@ static bool includesBoostTest(const CPlusPlus::Document::Ptr &doc,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return CppParser::precompiledHeaderContains(snapshot,
|
||||||
|
Utils::FilePath::fromString(doc->fileName()),
|
||||||
|
boostTestHpp);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hasBoostTestMacros(const CPlusPlus::Document::Ptr &doc)
|
static bool hasBoostTestMacros(const CPlusPlus::Document::Ptr &doc)
|
||||||
|
@@ -86,6 +86,12 @@ static bool includesCatchHeader(const CPlusPlus::Document::Ptr &doc,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const QString &catchHeader : catchHeaders) {
|
||||||
|
if (CppParser::precompiledHeaderContains(snapshot,
|
||||||
|
Utils::FilePath::fromString(doc->fileName()),
|
||||||
|
catchHeader))
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -109,13 +115,24 @@ bool CatchTestParser::processDocument(QFutureInterface<TestParseResultPtr> futur
|
|||||||
const Utils::FilePath &fileName)
|
const Utils::FilePath &fileName)
|
||||||
{
|
{
|
||||||
CPlusPlus::Document::Ptr doc = document(fileName);
|
CPlusPlus::Document::Ptr doc = document(fileName);
|
||||||
if (doc.isNull() || !includesCatchHeader(doc, m_cppSnapshot) || !hasCatchNames(doc))
|
if (doc.isNull() || !includesCatchHeader(doc, m_cppSnapshot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance();
|
const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance();
|
||||||
const QString &filePath = doc->fileName();
|
const QString &filePath = doc->fileName();
|
||||||
const QByteArray &fileContent = getFileContent(fileName);
|
const QByteArray &fileContent = getFileContent(fileName);
|
||||||
|
|
||||||
|
if (!hasCatchNames(doc)) {
|
||||||
|
const QRegularExpression regex("\\b(SCENARIO|(TEMPLATE_(PRODUCT_)?)?TEST_CASE(_METHOD)?|"
|
||||||
|
"TEMPLATE_TEST_CASE(_METHOD)?_SIG|"
|
||||||
|
"TEMPLATE_PRODUCT_TEST_CASE(_METHOD)?_SIG|"
|
||||||
|
"TEMPLATE_LIST_TEST_CASE_METHOD|METHOD_AS_TEST_CASE|"
|
||||||
|
"REGISTER_TEST_CASE)");
|
||||||
|
if (!regex.match(QString::fromUtf8(fileContent)).hasMatch())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const QList<CppTools::ProjectPart::Ptr> projectParts = modelManager->projectPart(fileName);
|
const QList<CppTools::ProjectPart::Ptr> projectParts = modelManager->projectPart(fileName);
|
||||||
if (projectParts.isEmpty()) // happens if shutting down while parsing
|
if (projectParts.isEmpty()) // happens if shutting down while parsing
|
||||||
return false;
|
return false;
|
||||||
|
@@ -32,6 +32,9 @@
|
|||||||
#include <cpptools/cppmodelmanager.h>
|
#include <cpptools/cppmodelmanager.h>
|
||||||
#include <cpptools/projectpart.h>
|
#include <cpptools/projectpart.h>
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpressionMatch>
|
||||||
|
|
||||||
namespace Autotest {
|
namespace Autotest {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -69,7 +72,9 @@ static bool includesGTest(const CPlusPlus::Document::Ptr &doc,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return CppParser::precompiledHeaderContains(snapshot,
|
||||||
|
Utils::FilePath::fromString(doc->fileName()),
|
||||||
|
gtestH);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool hasGTestNames(const CPlusPlus::Document::Ptr &document)
|
static bool hasGTestNames(const CPlusPlus::Document::Ptr &document)
|
||||||
@@ -91,12 +96,18 @@ bool GTestParser::processDocument(QFutureInterface<TestParseResultPtr> futureInt
|
|||||||
const Utils::FilePath &fileName)
|
const Utils::FilePath &fileName)
|
||||||
{
|
{
|
||||||
CPlusPlus::Document::Ptr doc = document(fileName);
|
CPlusPlus::Document::Ptr doc = document(fileName);
|
||||||
if (doc.isNull() || !includesGTest(doc, m_cppSnapshot) || !hasGTestNames(doc))
|
if (doc.isNull() || !includesGTest(doc, m_cppSnapshot))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance();
|
|
||||||
const QString &filePath = doc->fileName();
|
|
||||||
const QByteArray &fileContent = getFileContent(fileName);
|
const QByteArray &fileContent = getFileContent(fileName);
|
||||||
|
if (!hasGTestNames(doc)) {
|
||||||
|
const QRegularExpression regex("\\b(TEST(_[FP])?|TYPED_TEST(_P)?|(GTEST_TEST))");
|
||||||
|
if (!regex.match(QString::fromUtf8(fileContent)).hasMatch())
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString &filePath = doc->fileName();
|
||||||
|
const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance();
|
||||||
CPlusPlus::Document::Ptr document = m_cppSnapshot.preprocessedDocument(fileContent, fileName);
|
CPlusPlus::Document::Ptr document = m_cppSnapshot.preprocessedDocument(fileContent, fileName);
|
||||||
document->check();
|
document->check();
|
||||||
CPlusPlus::AST *ast = document->translationUnit()->ast();
|
CPlusPlus::AST *ast = document->translationUnit()->ast();
|
||||||
|
@@ -28,6 +28,10 @@
|
|||||||
#include <coreplugin/editormanager/editormanager.h>
|
#include <coreplugin/editormanager/editormanager.h>
|
||||||
#include <cpptools/cppmodelmanager.h>
|
#include <cpptools/cppmodelmanager.h>
|
||||||
#include <utils/textfileformat.h>
|
#include <utils/textfileformat.h>
|
||||||
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
|
#include <QRegularExpression>
|
||||||
|
#include <QRegularExpressionMatch>
|
||||||
|
|
||||||
namespace Autotest {
|
namespace Autotest {
|
||||||
|
|
||||||
@@ -69,6 +73,39 @@ QByteArray CppParser::getFileContent(const Utils::FilePath &filePath) const
|
|||||||
return fileContent;
|
return fileContent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const Utils::FilePath &filePath,
|
||||||
|
const std::function<bool(const QString &)> &checker)
|
||||||
|
{
|
||||||
|
const CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance();
|
||||||
|
const QList<CppTools::ProjectPart::Ptr> projectParts = modelManager->projectPart(filePath);
|
||||||
|
if (projectParts.isEmpty())
|
||||||
|
return false;
|
||||||
|
const QStringList precompiledHeaders = projectParts.first()->precompiledHeaders;
|
||||||
|
auto headerContains = [&](const QString &header){
|
||||||
|
return Utils::anyOf(snapshot.allIncludesForDocument(header), checker);
|
||||||
|
};
|
||||||
|
return Utils::anyOf(precompiledHeaders, headerContains);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CppParser::precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const Utils::FilePath &filePath,
|
||||||
|
const QString &headerFilePath)
|
||||||
|
{
|
||||||
|
return Autotest::precompiledHeaderContains(snapshot, filePath, [&](const QString &include) {
|
||||||
|
return include.endsWith(headerFilePath);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CppParser::precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const Utils::FilePath &filePath,
|
||||||
|
const QRegularExpression &headerFileRegex)
|
||||||
|
{
|
||||||
|
return Autotest::precompiledHeaderContains(snapshot, filePath, [&](const QString &include) {
|
||||||
|
return headerFileRegex.match(include).hasMatch();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
void CppParser::release()
|
void CppParser::release()
|
||||||
{
|
{
|
||||||
m_cppSnapshot = CPlusPlus::Snapshot();
|
m_cppSnapshot = CPlusPlus::Snapshot();
|
||||||
|
@@ -33,6 +33,10 @@
|
|||||||
|
|
||||||
#include <QFutureInterface>
|
#include <QFutureInterface>
|
||||||
|
|
||||||
|
QT_BEGIN_NAMESPACE
|
||||||
|
class QRegularExpression;
|
||||||
|
QT_END_NAMESPACE
|
||||||
|
|
||||||
namespace Autotest {
|
namespace Autotest {
|
||||||
|
|
||||||
class ITestFramework;
|
class ITestFramework;
|
||||||
@@ -85,6 +89,12 @@ public:
|
|||||||
|
|
||||||
CPlusPlus::Document::Ptr document(const Utils::FilePath &fileName);
|
CPlusPlus::Document::Ptr document(const Utils::FilePath &fileName);
|
||||||
|
|
||||||
|
static bool precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const Utils::FilePath &filePath,
|
||||||
|
const QString &headerFilePath);
|
||||||
|
static bool precompiledHeaderContains(const CPlusPlus::Snapshot &snapshot,
|
||||||
|
const Utils::FilePath &filePath,
|
||||||
|
const QRegularExpression &headerFileRegex);
|
||||||
protected:
|
protected:
|
||||||
CPlusPlus::Snapshot m_cppSnapshot;
|
CPlusPlus::Snapshot m_cppSnapshot;
|
||||||
CppTools::WorkingCopy m_workingCopy;
|
CppTools::WorkingCopy m_workingCopy;
|
||||||
|
@@ -32,6 +32,8 @@
|
|||||||
#include <cplusplus/TypeOfExpression.h>
|
#include <cplusplus/TypeOfExpression.h>
|
||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
|
|
||||||
|
#include <QRegularExpressionMatchIterator>
|
||||||
|
|
||||||
namespace Autotest {
|
namespace Autotest {
|
||||||
namespace Internal {
|
namespace Internal {
|
||||||
|
|
||||||
@@ -78,6 +80,14 @@ static bool includesQtTest(const CPlusPlus::Document::Ptr &doc, const CPlusPlus:
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const QString &prefix : expectedHeaderPrefixes) {
|
||||||
|
if (CppParser::precompiledHeaderContains(snapshot,
|
||||||
|
Utils::FilePath::fromString(doc->fileName()),
|
||||||
|
QString("%1/qtest.h").arg(prefix))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,7 +130,19 @@ TestCases QtTestParser::testCases(const CppTools::CppModelManager *modelManager,
|
|||||||
CPlusPlus::AST *ast = document->translationUnit()->ast();
|
CPlusPlus::AST *ast = document->translationUnit()->ast();
|
||||||
TestAstVisitor astVisitor(document, m_cppSnapshot);
|
TestAstVisitor astVisitor(document, m_cppSnapshot);
|
||||||
astVisitor.accept(ast);
|
astVisitor.accept(ast);
|
||||||
return astVisitor.testCases();
|
if (!astVisitor.testCases().isEmpty())
|
||||||
|
return astVisitor.testCases();
|
||||||
|
|
||||||
|
// check pch usage - might give false positives, but we can't do better without cost
|
||||||
|
TestCases result;
|
||||||
|
const QRegularExpression regex("\\bQTEST_(APPLESS_|GUILESS_)?MAIN"
|
||||||
|
"\\s*\\(\\s*([[:alnum:]]+)\\s*\\)");
|
||||||
|
QRegularExpressionMatchIterator it = regex.globalMatch(QString::fromUtf8(fileContent));
|
||||||
|
while (it.hasNext()) {
|
||||||
|
const QRegularExpressionMatch match = it.next();
|
||||||
|
result.append({match.captured(2), false});
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
static CPlusPlus::Document::Ptr declaringDocument(CPlusPlus::Document::Ptr doc,
|
static CPlusPlus::Document::Ptr declaringDocument(CPlusPlus::Document::Ptr doc,
|
||||||
|
@@ -87,6 +87,14 @@ static bool includesQtQuickTest(const CPlusPlus::Document::Ptr &doc,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const QString &prefix : expectedHeaderPrefixes) {
|
||||||
|
if (CppParser::precompiledHeaderContains(snapshot,
|
||||||
|
Utils::FilePath::fromString(doc->fileName()),
|
||||||
|
QString("%1/quicktest.h").arg(prefix))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,6 +123,7 @@ static QString quickTestSrcDir(const CppTools::CppModelManager *cppMM,
|
|||||||
QString QuickTestParser::quickTestName(const CPlusPlus::Document::Ptr &doc) const
|
QString QuickTestParser::quickTestName(const CPlusPlus::Document::Ptr &doc) const
|
||||||
{
|
{
|
||||||
const QList<CPlusPlus::Document::MacroUse> macros = doc->macroUses();
|
const QList<CPlusPlus::Document::MacroUse> macros = doc->macroUses();
|
||||||
|
const Utils::FilePath filePath = Utils::FilePath::fromString(doc->fileName());
|
||||||
|
|
||||||
for (const CPlusPlus::Document::MacroUse ¯o : macros) {
|
for (const CPlusPlus::Document::MacroUse ¯o : macros) {
|
||||||
if (!macro.isFunctionLike())
|
if (!macro.isFunctionLike())
|
||||||
@@ -122,22 +131,45 @@ QString QuickTestParser::quickTestName(const CPlusPlus::Document::Ptr &doc) cons
|
|||||||
const QByteArray name = macro.macro().name();
|
const QByteArray name = macro.macro().name();
|
||||||
if (QuickTestUtils::isQuickTestMacro(name)) {
|
if (QuickTestUtils::isQuickTestMacro(name)) {
|
||||||
CPlusPlus::Document::Block arg = macro.arguments().at(0);
|
CPlusPlus::Document::Block arg = macro.arguments().at(0);
|
||||||
return QLatin1String(getFileContent(Utils::FilePath::fromString(doc->fileName()))
|
return QLatin1String(getFileContent(filePath)
|
||||||
.mid(int(arg.bytesBegin()), int(arg.bytesEnd() - arg.bytesBegin())));
|
.mid(int(arg.bytesBegin()), int(arg.bytesEnd() - arg.bytesBegin())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const QByteArray &fileContent = getFileContent(filePath);
|
||||||
// check for using quick_test_main() directly
|
// check for using quick_test_main() directly
|
||||||
const QString fileName = doc->fileName();
|
CPlusPlus::Document::Ptr document = m_cppSnapshot.preprocessedDocument(fileContent, filePath);
|
||||||
const QByteArray &fileContent = getFileContent(Utils::FilePath::fromString(fileName));
|
|
||||||
CPlusPlus::Document::Ptr document = m_cppSnapshot.preprocessedDocument(fileContent, fileName);
|
|
||||||
if (document.isNull())
|
if (document.isNull())
|
||||||
return QString();
|
return QString();
|
||||||
document->check();
|
document->check();
|
||||||
CPlusPlus::AST *ast = document->translationUnit()->ast();
|
CPlusPlus::AST *ast = document->translationUnit()->ast();
|
||||||
QuickTestAstVisitor astVisitor(document, m_cppSnapshot);
|
QuickTestAstVisitor astVisitor(document, m_cppSnapshot);
|
||||||
astVisitor.accept(ast);
|
astVisitor.accept(ast);
|
||||||
return astVisitor.testBaseName();
|
if (!astVisitor.testBaseName().isEmpty())
|
||||||
|
return astVisitor.testBaseName();
|
||||||
|
|
||||||
|
// check for precompiled headers
|
||||||
|
static QStringList expectedHeaderPrefixes
|
||||||
|
= Utils::HostOsInfo::isMacHost()
|
||||||
|
? QStringList({"QtQuickTest.framework/Headers", "QtQuickTest"})
|
||||||
|
: QStringList({"QtQuickTest"});
|
||||||
|
bool pchIncludes = false;
|
||||||
|
for (const QString &prefix : expectedHeaderPrefixes) {
|
||||||
|
if (CppParser::precompiledHeaderContains(m_cppSnapshot, filePath,
|
||||||
|
QString("%1/quicktest.h").arg(prefix))) {
|
||||||
|
pchIncludes = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pchIncludes) {
|
||||||
|
const QRegularExpression regex("\\bQUICK_TEST_(MAIN|OPENGL_MAIN|MAIN_WITH_SETUP)");
|
||||||
|
const QRegularExpressionMatch match = regex.match(QString::fromUtf8(fileContent));
|
||||||
|
if (match.hasMatch())
|
||||||
|
return match.captured(); // we do not care for the name, just return something non-empty
|
||||||
|
}
|
||||||
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
QList<Document::Ptr> QuickTestParser::scanDirectoryForQuickTestQmlFiles(const QString &srcDir)
|
QList<Document::Ptr> QuickTestParser::scanDirectoryForQuickTestQmlFiles(const QString &srcDir)
|
||||||
|
Reference in New Issue
Block a user