diff options
27 files changed, 609 insertions, 163 deletions
diff --git a/plugins/autotest/autotest.qbs b/plugins/autotest/autotest.qbs index 4a9ec9e4c8..f746d7fae1 100644 --- a/plugins/autotest/autotest.qbs +++ b/plugins/autotest/autotest.qbs @@ -7,11 +7,15 @@ QtcCommercialPlugin { Depends { name: "CppTools" } Depends { name: "CPlusPlus" } Depends { name: "ProjectExplorer" } - Depends { name: "QmakeProjectManager" } Depends { name: "QmlJS" } Depends { name: "QmlJSTools" } Depends { name: "Utils" } + pluginTestDepends: [ + "QbsProjectManager", + "QmakeProjectManager" + ] + Depends { name: "QtSupport" condition: project.testsEnabled diff --git a/plugins/autotest/autotest.qrc b/plugins/autotest/autotest.qrc index 2770aff5fe..8c70e3ae3f 100644 --- a/plugins/autotest/autotest.qrc +++ b/plugins/autotest/autotest.qrc @@ -21,5 +21,6 @@ <file>images/run.png</file> <file>images/runselected.png</file> <file>images/stop.png</file> + <file>images/data.png</file> </qresource> </RCC> diff --git a/plugins/autotest/autotest_dependencies.pri b/plugins/autotest/autotest_dependencies.pri index 74dc51e15b..9410d77950 100644 --- a/plugins/autotest/autotest_dependencies.pri +++ b/plugins/autotest/autotest_dependencies.pri @@ -4,8 +4,7 @@ QTC_PLUGIN_DEPENDS += \ coreplugin \ projectexplorer \ cpptools \ - qmljstools \ - qmakeprojectmanager + qmljstools CONFIG(licensechecker): QTC_PLUGIN_DEPENDS += licensechecker @@ -14,4 +13,9 @@ QTC_LIB_DEPENDS += \ qmljs \ utils +QTC_TEST_DEPENDS += \ + qbsprojectmanager \ + qmakeprojectmanager \ + qtsupport + #QTC_PLUGIN_RECOMMENDS += \ diff --git a/plugins/autotest/autotestunittests.cpp b/plugins/autotest/autotestunittests.cpp index 15f32db669..8878f75418 100644 --- a/plugins/autotest/autotestunittests.cpp +++ b/plugins/autotest/autotestunittests.cpp @@ -117,6 +117,66 @@ void AutoTestUnitTests::testCodeParser_data() QTest::newRow("mixedAutoTestAndQuickTests") << QString(m_tmpDir->path() + QLatin1String("/mixed_atp/mixed_atp.pro")) << 3 << 5 << 3; + QTest::newRow("plainAutoTestQbs") + << QString(m_tmpDir->path() + QLatin1String("/plain/plain.qbs")) + << 1 << 0 << 0; + QTest::newRow("mixedAuotTestAndQuickTestsQbs") + << QString(m_tmpDir->path() + QLatin1String("/mixed_atp/mixed_atp.qbs")) + << 3 << 5 << 3; +} + +void AutoTestUnitTests::testCodeParserSwitchStartup() +{ + QFETCH(QStringList, projectFilePaths); + QFETCH(QList<int>, expectedAutoTestsCount); + QFETCH(QList<int>, expectedNamedQuickTestsCount); + QFETCH(QList<int>, expectedUnnamedQuickTestsCount); + + NavigationWidget *navigation = NavigationWidget::instance(); + navigation->activateSubWidget(Constants::AUTOTEST_ID); + + CppTools::Tests::ProjectOpenerAndCloser projectManager; + for (int i = 0; i < projectFilePaths.size(); ++i) { + qDebug() << "Opening project" << projectFilePaths.at(i); + CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePaths.at(i), true); + QVERIFY(projectInfo.isValid()); + + QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished())); + QVERIFY(parserSpy.wait(20000)); + + QCOMPARE(m_model->autoTestsCount(), expectedAutoTestsCount.at(i)); + QCOMPARE(m_model->namedQuickTestsCount(), + m_isQt4 ? 0 : expectedNamedQuickTestsCount.at(i)); + QCOMPARE(m_model->unnamedQuickTestsCount(), + m_isQt4 ? 0 : expectedUnnamedQuickTestsCount.at(i)); + + QCOMPARE(m_model->parser()->autoTestsCount(), expectedAutoTestsCount.at(i)); + QCOMPARE(m_model->parser()->namedQuickTestsCount(), + m_isQt4 ? 0 : expectedNamedQuickTestsCount.at(i)); + QCOMPARE(m_model->parser()->unnamedQuickTestsCount(), + m_isQt4 ? 0 : expectedUnnamedQuickTestsCount.at(i)); + } +} + +void AutoTestUnitTests::testCodeParserSwitchStartup_data() +{ + QTest::addColumn<QStringList>("projectFilePaths"); + QTest::addColumn<QList<int> >("expectedAutoTestsCount"); + QTest::addColumn<QList<int> >("expectedNamedQuickTestsCount"); + QTest::addColumn<QList<int> >("expectedUnnamedQuickTestsCount"); + + QStringList projects = QStringList() + << QString(m_tmpDir->path() + QLatin1String("/plain/plain.pro")) + << QString(m_tmpDir->path() + QLatin1String("/mixed_atp/mixed_atp.pro")) + << QString(m_tmpDir->path() + QLatin1String("/plain/plain.qbs")) + << QString(m_tmpDir->path() + QLatin1String("/mixed_atp/mixed_atp.qbs")); + + QList<int> expectedAutoTests = QList<int>() << 1 << 3 << 1 << 3; + QList<int> expectedNamedQuickTests = QList<int>() << 0 << 5 << 0 << 5; + QList<int> expectedUnnamedQuickTests = QList<int>() << 0 << 3 << 0 << 3; + + QTest::newRow("loadMultipleProjects") + << projects << expectedAutoTests << expectedNamedQuickTests << expectedUnnamedQuickTests; } } // namespace Internal diff --git a/plugins/autotest/autotestunittests.h b/plugins/autotest/autotestunittests.h index ab2b7c1b9b..e359e4175d 100644 --- a/plugins/autotest/autotestunittests.h +++ b/plugins/autotest/autotestunittests.h @@ -43,6 +43,8 @@ private slots: void cleanupTestCase(); void testCodeParser(); void testCodeParser_data(); + void testCodeParserSwitchStartup(); + void testCodeParserSwitchStartup_data(); private: TestTreeModel *m_model; diff --git a/plugins/autotest/autotestunittests.qrc b/plugins/autotest/autotestunittests.qrc index 6fb5348e54..25ce4044fe 100644 --- a/plugins/autotest/autotestunittests.qrc +++ b/plugins/autotest/autotestunittests.qrc @@ -28,5 +28,16 @@ <file>unit_test/mixed_atp/tests/auto/quickauto/quickauto.pro</file> <file>unit_test/mixed_atp/tests/auto/quickauto2/quickauto2.pro</file> <file>unit_test/mixed_atp/tests/auto/auto.pro</file> + <file>unit_test/plain/plain.qbs</file> + <file>unit_test/plain/test_plain/test_plain.qbs</file> + <file>unit_test/mixed_atp/mixed_atp.qbs</file> + <file>unit_test/mixed_atp/src/src.qbs</file> + <file>unit_test/mixed_atp/tests/tests.qbs</file> + <file>unit_test/mixed_atp/tests/auto/auto.qbs</file> + <file>unit_test/mixed_atp/tests/auto/bench/bench.qbs</file> + <file>unit_test/mixed_atp/tests/auto/dummy/dummy.qbs</file> + <file>unit_test/mixed_atp/tests/auto/gui/gui.qbs</file> + <file>unit_test/mixed_atp/tests/auto/quickauto/quickauto.qbs</file> + <file>unit_test/mixed_atp/tests/auto/quickauto2/quickauto2.qbs</file> </qresource> </RCC> diff --git a/plugins/autotest/images/data.png b/plugins/autotest/images/data.png Binary files differnew file mode 100644 index 0000000000..1f510c4042 --- /dev/null +++ b/plugins/autotest/images/data.png diff --git a/plugins/autotest/testcodeparser.cpp b/plugins/autotest/testcodeparser.cpp index 004f733fbd..b7a13b4ee8 100644 --- a/plugins/autotest/testcodeparser.cpp +++ b/plugins/autotest/testcodeparser.cpp @@ -33,11 +33,9 @@ #include <cpptools/cppmodelmanager.h> #include <cpptools/cppworkingcopy.h> +#include <projectexplorer/project.h> #include <projectexplorer/session.h> -#include <qmakeprojectmanager/qmakeproject.h> -#include <qmakeprojectmanager/qmakeprojectmanagerconstants.h> - #include <qmljs/parser/qmljsast_p.h> #include <qmljs/qmljsdialect.h> #include <qmljstools/qmljsmodelmanager.h> @@ -46,8 +44,13 @@ #include <utils/qtcassert.h> #include <utils/textfileformat.h> +#include <QDirIterator> #include <QFuture> #include <QFutureInterface> +#include <QLoggingCategory> +#include <QTimer> + +static Q_LOGGING_CATEGORY(LOG, "qtc.autotest.testcodeparser") namespace Autotest { namespace Internal { @@ -55,10 +58,11 @@ namespace Internal { TestCodeParser::TestCodeParser(TestTreeModel *parent) : QObject(parent), m_model(parent), - m_parserEnabled(true), + m_codeModelParsing(false), m_fullUpdatePostponed(false), m_partialUpdatePostponed(false), - m_dirty(true), + m_dirty(false), + m_singleShotScheduled(false), m_parserState(Disabled) { // connect to ProgressManager to postpone test parsing when CppModelManager is parsing @@ -76,61 +80,66 @@ TestCodeParser::~TestCodeParser() clearCache(); } -ProjectExplorer::Project *currentProject() -{ - const ProjectExplorer::SessionManager *session = ProjectExplorer::SessionManager::instance(); - if (!session || !session->hasProjects()) - return 0; - return session->startupProject(); -} - void TestCodeParser::setState(State state) { - // avoid triggering parse before code model parsing has finished - if (!m_parserEnabled) + qCDebug(LOG) << "setState(" << state << "), currentState:" << m_parserState; + // avoid triggering parse before code model parsing has finished, but mark as dirty + if (m_codeModelParsing) { + m_dirty = true; + qCDebug(LOG) << "Not setting new state - code model parsing is running, just marking dirty"; return; + } if ((state == Disabled || state == Idle) - && (m_parserState == PartialParse || m_parserState == FullParse)) + && (m_parserState == PartialParse || m_parserState == FullParse)) { + qCDebug(LOG) << "Not setting state, parse is running"; return; + } m_parserState = state; if (m_parserState == Disabled) { m_fullUpdatePostponed = m_partialUpdatePostponed = false; m_postponedFiles.clear(); - } else if (m_parserState == Idle && m_dirty && currentProject()) { - scanForTests(m_postponedFiles.toList()); + } else if (m_parserState == Idle && ProjectExplorer::SessionManager::startupProject()) { + if (m_fullUpdatePostponed || m_dirty) { + emitUpdateTestTree(); + } else if (m_partialUpdatePostponed) { + m_partialUpdatePostponed = false; + qCDebug(LOG) << "calling scanForTests with postponed files (setState)"; + scanForTests(m_postponedFiles.toList()); + } } } void TestCodeParser::emitUpdateTestTree() { + if (m_singleShotScheduled) { + qCDebug(LOG) << "not scheduling another updateTestTree"; + return; + } + + qCDebug(LOG) << "adding singleShot"; + m_singleShotScheduled = true; QTimer::singleShot(1000, this, SLOT(updateTestTree())); } void TestCodeParser::updateTestTree() { - if (!m_parserEnabled) { + m_singleShotScheduled = false; + if (m_codeModelParsing) { m_fullUpdatePostponed = true; + m_partialUpdatePostponed = false; + m_postponedFiles.clear(); return; } - if (ProjectExplorer::Project *project = currentProject()) { - if (auto qmakeProject = qobject_cast<QmakeProjectManager::QmakeProject *>(project)) { - if (qmakeProject->asyncUpdateState() != QmakeProjectManager::QmakeProject::Base) { - m_fullUpdatePostponed = true; - return; - } - connect(qmakeProject, &QmakeProjectManager::QmakeProject::proFilesEvaluated, - this, &TestCodeParser::onProFileEvaluated, Qt::UniqueConnection); - } - } else + if (!ProjectExplorer::SessionManager::startupProject()) return; m_fullUpdatePostponed = false; clearCache(); - emit cacheCleared(); + qCDebug(LOG) << "calling scanForTests (updateTestTree)"; scanForTests(); } @@ -346,11 +355,40 @@ static CPlusPlus::Document::Ptr declaringDocument(CPlusPlus::Document::Ptr doc, return declaringDoc; } +static bool hasFunctionWithDataTagUsage(const QMap<QString, TestCodeLocationAndType> &testFunctions) +{ + foreach (const QString &functionName, testFunctions.keys()) { + if (functionName.endsWith(QLatin1String("_data")) && + testFunctions.contains(functionName.left(functionName.size() - 5))) { + return true; + } + } + return false; +} + +static QMap<QString, TestCodeLocationList> checkForDataTags(const QString &fileName, + const QMap<QString, TestCodeLocationAndType> &testFunctions) +{ + if (hasFunctionWithDataTagUsage(testFunctions)) { + const CPlusPlus::Snapshot snapshot = CPlusPlus::CppModelManagerBase::instance()->snapshot(); + const QByteArray fileContent = getFileContent(fileName); + CPlusPlus::Document::Ptr document = snapshot.preprocessedDocument(fileContent, fileName); + document->check(); + CPlusPlus::AST *ast = document->translationUnit()->ast(); + TestDataFunctionVisitor visitor(document); + visitor.accept(ast); + return visitor.dataTags(); + } + return QMap<QString, TestCodeLocationList>(); +} + + static TestTreeItem constructTestTreeItem(const QString &fileName, const QString &mainFile, // used for Quick Tests only const QString &testCaseName, int line, int column, - const QMap<QString, TestCodeLocationAndType> functions) + const QMap<QString, TestCodeLocationAndType> functions, + const QMap<QString, TestCodeLocationList> dataTags = QMap<QString, TestCodeLocationList>()) { TestTreeItem treeItem(testCaseName, fileName, TestTreeItem::TEST_CLASS); treeItem.setMainFile(mainFile); // used for Quick Tests only @@ -359,10 +397,24 @@ static TestTreeItem constructTestTreeItem(const QString &fileName, foreach (const QString &functionName, functions.keys()) { const TestCodeLocationAndType locationAndType = functions.value(functionName); - TestTreeItem *treeItemChild = new TestTreeItem(functionName, locationAndType.m_fileName, + TestTreeItem *treeItemChild = new TestTreeItem(functionName, locationAndType.m_name, locationAndType.m_type, &treeItem); treeItemChild->setLine(locationAndType.m_line); treeItemChild->setColumn(locationAndType.m_column); + // check for data tags and if there are any for this function add them + const QString qualifiedFunctionName = testCaseName + QLatin1String("::") + functionName; + if (dataTags.contains(qualifiedFunctionName)) { + const TestCodeLocationList &tags = dataTags.value(qualifiedFunctionName); + foreach (const TestCodeLocationAndType &tagLocation, tags) { + TestTreeItem *tagTreeItem = new TestTreeItem(tagLocation.m_name, + locationAndType.m_name, + tagLocation.m_type, treeItemChild); + tagTreeItem->setLine(tagLocation.m_line); + tagTreeItem->setColumn(tagLocation.m_column); + treeItemChild->appendChild(tagTreeItem); + } + } + treeItem.appendChild(treeItemChild); } return treeItem; @@ -370,6 +422,10 @@ static TestTreeItem constructTestTreeItem(const QString &fileName, /****** end of helpers ******/ +// used internally to indicate a parse that failed due to having triggered a parse for a file that +// is not (yet) part of the CppModelManager's snapshot +static bool parsingHasFailed; + void performParse(QFutureInterface<void> &futureInterface, QStringList list, TestCodeParser *testCodeParser) { @@ -384,6 +440,9 @@ void performParse(QFutureInterface<void> &futureInterface, QStringList list, CPlusPlus::Document::Ptr doc = snapshot.find(file).value(); futureInterface.setProgressValue(++progressValue); testCodeParser->checkDocumentForTestCode(doc); + } else { + parsingHasFailed |= (CppTools::ProjectFile::classify(file) + != CppTools::ProjectFile::Unclassified); } } futureInterface.setProgressValue(list.size()); @@ -421,8 +480,11 @@ void TestCodeParser::checkDocumentForTestCode(CPlusPlus::Document::Ptr document) visitor.accept(declaringDoc->globalNamespace()); const QMap<QString, TestCodeLocationAndType> testFunctions = visitor.privateSlots(); + const QMap<QString, TestCodeLocationList> dataTags = + checkForDataTags(declaringDoc->fileName(), testFunctions); TestTreeItem item = constructTestTreeItem(declaringDoc->fileName(), QString(), - testCaseName, line, column, testFunctions); + testCaseName, line, column, testFunctions, + dataTags); updateModelAndCppDocMap(document, declaringDoc->fileName(), item); return; } @@ -471,7 +533,7 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr document) // construct new/modified TestTreeItem TestTreeItem testTreeItem - = constructTestTreeItem(tcLocationAndType.m_fileName, cppFileName, testCaseName, + = constructTestTreeItem(tcLocationAndType.m_name, cppFileName, testCaseName, tcLocationAndType.m_line, tcLocationAndType.m_column, testFunctions); @@ -482,7 +544,7 @@ void TestCodeParser::handleQtQuickTest(CPlusPlus::Document::Ptr document) void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &document) { - if (!m_parserEnabled) { + if (m_codeModelParsing) { if (!m_fullUpdatePostponed) { m_partialUpdatePostponed = true; m_postponedFiles.insert(document->fileName()); @@ -490,7 +552,7 @@ void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &docume return; } - ProjectExplorer::Project *project = currentProject(); + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); if (!project) return; const QString fileName = document->fileName(); @@ -502,12 +564,13 @@ void TestCodeParser::onCppDocumentUpdated(const CPlusPlus::Document::Ptr &docume } else if (!project->files(ProjectExplorer::Project::AllFiles).contains(fileName)) { return; } + qCDebug(LOG) << "calling scanForTests (onCppDocumentUpdated)"; scanForTests(QStringList(fileName)); } void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &document) { - if (!m_parserEnabled) { + if (m_codeModelParsing) { if (!m_fullUpdatePostponed) { m_partialUpdatePostponed = true; m_postponedFiles.insert(document->fileName()); @@ -515,7 +578,7 @@ void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &document) return; } - ProjectExplorer::Project *project = currentProject(); + ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject(); if (!project) return; const QString fileName = document->fileName(); @@ -530,8 +593,11 @@ void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &document) const CPlusPlus::Snapshot snapshot = CppTools::CppModelManager::instance()->snapshot(); if (m_quickDocMap.contains(fileName) && snapshot.contains(m_quickDocMap[fileName].referencingFile())) { - if (!m_quickDocMap[fileName].referencingFile().isEmpty()) + if (!m_quickDocMap[fileName].referencingFile().isEmpty()) { + qCDebug(LOG) << "calling scanForTests with cached referencing files" + << "(onQmlDocumentUpdated)"; scanForTests(QStringList(m_quickDocMap[fileName].referencingFile())); + } } if (m_unnamedQuickDocList.size() == 0) return; @@ -539,15 +605,26 @@ void TestCodeParser::onQmlDocumentUpdated(const QmlJS::Document::Ptr &document) // special case of having unnamed TestCases const QString &mainFile = m_model->getMainFileForUnnamedQuickTest(fileName); if (!mainFile.isEmpty() && snapshot.contains(mainFile)) { + qCDebug(LOG) << "calling scanForTests with mainfile (onQmlDocumentUpdated)"; scanForTests(QStringList(mainFile)); } } +void TestCodeParser::onStartupProjectChanged(ProjectExplorer::Project *) +{ + if (m_parserState == FullParse || m_parserState == PartialParse) { + Core::ProgressManager::instance()->cancelTasks(Constants::TASK_PARSE); + } else { + clearCache(); + emitUpdateTestTree(); + } +} + void TestCodeParser::onProjectPartsUpdated(ProjectExplorer::Project *project) { - if (project != currentProject()) + if (project != ProjectExplorer::SessionManager::startupProject()) return; - if (!m_parserEnabled || m_parserState == Disabled) + if (m_codeModelParsing || m_parserState == Disabled) m_fullUpdatePostponed = true; else emitUpdateTestTree(); @@ -565,7 +642,8 @@ bool TestCodeParser::postponed(const QStringList &fileList) case Idle: return false; case PartialParse: - // partial is running, postponing a full parse + case FullParse: + // parse is running, postponing a full parse if (fileList.isEmpty()) { m_partialUpdatePostponed = false; m_postponedFiles.clear(); @@ -576,23 +654,6 @@ bool TestCodeParser::postponed(const QStringList &fileList) return true; // partial parse triggered, postpone or add current files to already postponed partial foreach (const QString &file, fileList) - m_postponedFiles.insert(file); - m_partialUpdatePostponed = true; - } - return true; - case FullParse: - // full parse is running, postponing another full parse - if (fileList.isEmpty()) { - m_partialUpdatePostponed = false; - m_postponedFiles.clear(); - m_fullUpdatePostponed = true; - } else { - // full parse already postponed, ignoring triggering a partial parse - if (m_fullUpdatePostponed) { - return true; - } - // partial parse triggered, postpone or add current files to already postponed partial - foreach (const QString &file, fileList) m_postponedFiles.insert(file); m_partialUpdatePostponed = true; } @@ -624,19 +685,23 @@ void TestCodeParser::scanForTests(const QStringList &fileList) if (postponed(fileList)) return; + m_postponedFiles.clear(); bool isFullParse = fileList.isEmpty(); bool isSmallChange = !isFullParse && fileList.size() < 6; QStringList list; if (isFullParse) { - list = currentProject()->files(ProjectExplorer::Project::AllFiles); + list = ProjectExplorer::SessionManager::startupProject()->files(ProjectExplorer::Project::AllFiles); if (list.isEmpty()) return; + qCDebug(LOG) << "setting state to FullParse (scanForTests)"; m_parserState = FullParse; } else { list << fileList; + qCDebug(LOG) << "setting state to PartialParse (scanForTests)"; m_parserState = PartialParse; } + parsingHasFailed = false; if (isSmallChange) { // no need to do this async or should we do this always async? CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance(); CPlusPlus::Snapshot snapshot = cppMM->snapshot(); @@ -644,9 +709,12 @@ void TestCodeParser::scanForTests(const QStringList &fileList) if (snapshot.contains(file)) { CPlusPlus::Document::Ptr doc = snapshot.find(file).value(); checkDocumentForTestCode(doc); + } else { + parsingHasFailed |= (CppTools::ProjectFile::classify(file) + != CppTools::ProjectFile::Unclassified); } } - emit onFinished(); + onFinished(); return; } @@ -666,6 +734,7 @@ void TestCodeParser::clearCache() m_cppDocMap.clear(); m_quickDocMap.clear(); m_unnamedQuickDocList.clear(); + emit cacheCleared(); } void TestCodeParser::removeTestsIfNecessary(const QString &fileName) @@ -704,39 +773,10 @@ void TestCodeParser::removeTestsIfNecessary(const QString &fileName) } } -void TestCodeParser::removeTestsIfNecessaryByProFile(const QString &proFile) -{ - QList<QString> fList; - foreach (const QString &fileName, m_cppDocMap.keys()) { - if (m_cppDocMap[fileName].proFile() == proFile) - fList.append(fileName); - } - foreach (const QString &fileName, fList) { - m_cppDocMap.remove(fileName); - emit testItemsRemoved(fileName, TestTreeModel::AutoTest); - } - fList.clear(); - foreach (const QString &fileName, m_quickDocMap.keys()) { - if (m_quickDocMap[fileName].proFile() == proFile) - fList.append(fileName); - } - foreach (const QString &fileName, fList) { - m_quickDocMap.remove(fileName); - emit testItemsRemoved(fileName, TestTreeModel::QuickTest); - } - // handle unnamed Quick Tests - const QSet<QString> &filePaths = m_model->qmlFilesForProFile(proFile); - foreach (const QString &fileName, filePaths) { - removeUnnamedQuickTestsByName(fileName); - emit unnamedQuickTestsRemoved(fileName); - } -} - void TestCodeParser::onTaskStarted(Core::Id type) { - if (type != CppTools::Constants::TASK_INDEX) - return; - m_parserEnabled = false; + if (type == CppTools::Constants::TASK_INDEX) + m_codeModelParsing = true; } void TestCodeParser::onAllTasksFinished(Core::Id type) @@ -744,35 +784,35 @@ void TestCodeParser::onAllTasksFinished(Core::Id type) // only CPP parsing is relevant as we trigger Qml parsing internally anyway if (type != CppTools::Constants::TASK_INDEX) return; - m_parserEnabled = true; + m_codeModelParsing = false; + // avoid illegal parser state if respective widgets became hidden while parsing setState(Idle); - - if (m_fullUpdatePostponed) - updateTestTree(); - else if (m_partialUpdatePostponed) { - m_partialUpdatePostponed = false; - QStringList tmp; - foreach (const QString &file, m_postponedFiles) - tmp << file; - m_postponedFiles.clear(); - scanForTests(tmp); - } } void TestCodeParser::onFinished() { switch (m_parserState) { case PartialParse: + qCDebug(LOG) << "setting state to Idle (onFinished, PartialParse)"; m_parserState = Idle; emit partialParsingFinished(); break; case FullParse: + qCDebug(LOG) << "setting state to Idle (onFinished, FullParse)"; m_parserState = Idle; - emit parsingFinished(); + m_dirty = parsingHasFailed; + if (m_partialUpdatePostponed || m_fullUpdatePostponed || parsingHasFailed) { + emit partialParsingFinished(); + } else { + qCDebug(LOG) << "emitting parsingFinished" + << "(onFinished, FullParse, nothing postponed, parsing succeeded)"; + emit parsingFinished(); + } m_dirty = false; break; case Disabled: // can happen if all Test related widgets become hidden while parsing + qCDebug(LOG) << "emitting parsingFinished (onFinished, Disabled)"; emit parsingFinished(); break; default: @@ -788,17 +828,24 @@ void TestCodeParser::onPartialParsingFinished() m_partialUpdatePostponed = false;m_postponedFiles.clear();); if (m_fullUpdatePostponed) { m_fullUpdatePostponed = false; + qCDebug(LOG) << "calling updateTestTree (onPartialParsingFinished)"; updateTestTree(); } else if (m_partialUpdatePostponed) { m_partialUpdatePostponed = false; - QStringList tmp; - foreach (const QString &file, m_postponedFiles) - tmp << file; - m_postponedFiles.clear(); - scanForTests(tmp); + qCDebug(LOG) << "calling scanForTests with postponed files (onPartialParsingFinished)"; + scanForTests(m_postponedFiles.toList()); } else { - m_dirty = false; - emit parsingFinished(); + m_dirty |= m_codeModelParsing; + if (m_dirty) { + emit parsingFailed(); + } else if (!m_singleShotScheduled) { + qCDebug(LOG) << "emitting parsingFinished" + << "(onPartialParsingFinished, nothing postponed, not dirty)"; + emit parsingFinished(); + } else { + qCDebug(LOG) << "not emitting parsingFinished" + << "(on PartialParsingFinished, singleshot scheduled)"; + } } } @@ -894,28 +941,6 @@ void TestCodeParser::removeUnnamedQuickTestsByName(const QString &fileName) } } -void TestCodeParser::onProFileEvaluated() -{ - ProjectExplorer::Project *project = currentProject(); - if (!project) - return; - - CppTools::CppModelManager *modelManager = CppTools::CppModelManager::instance(); - const QList<CppTools::ProjectPart::Ptr> pp = modelManager->projectInfo(project).projectParts(); - foreach (const CppTools::ProjectPart::Ptr &p, pp) { - if (!p->selectedForBuilding) - removeTestsIfNecessaryByProFile(p->projectFile); - else { - QStringList files; - foreach (auto projectFile, p->files) - files.append(projectFile.path); - // avoid illegal parser state when respective widgets became hidden while evaluating - setState(Idle); - scanForTests(files); - } - } -} - #ifdef WITH_TESTS int TestCodeParser::autoTestsCount() const { diff --git a/plugins/autotest/testcodeparser.h b/plugins/autotest/testcodeparser.h index 1b8215fe50..5bb54d9320 100644 --- a/plugins/autotest/testcodeparser.h +++ b/plugins/autotest/testcodeparser.h @@ -56,6 +56,7 @@ public: virtual ~TestCodeParser(); void setState(State state); State state() const { return m_parserState; } + void setDirty() { m_dirty = true; } #ifdef WITH_TESTS int autoTestsCount() const; @@ -74,6 +75,7 @@ signals: void unnamedQuickTestsRemoved(const QString &filePath); void parsingStarted(); void parsingFinished(); + void parsingFailed(); void partialParsingFinished(); public slots: @@ -84,16 +86,15 @@ public slots: void onCppDocumentUpdated(const CPlusPlus::Document::Ptr &document); void onQmlDocumentUpdated(const QmlJS::Document::Ptr &document); + void onStartupProjectChanged(ProjectExplorer::Project *); void onProjectPartsUpdated(ProjectExplorer::Project *project); void removeFiles(const QStringList &files); - void onProFileEvaluated(); private: bool postponed(const QStringList &fileList); void scanForTests(const QStringList &fileList = QStringList()); void clearCache(); void removeTestsIfNecessary(const QString &fileName); - void removeTestsIfNecessaryByProFile(const QString &proFile); void onTaskStarted(Core::Id type); void onAllTasksFinished(Core::Id type); @@ -111,10 +112,11 @@ private: QMap<QString, TestInfo> m_cppDocMap; QMap<QString, TestInfo> m_quickDocMap; QList<UnnamedQuickTestInfo> m_unnamedQuickDocList; - bool m_parserEnabled; + bool m_codeModelParsing; bool m_fullUpdatePostponed; bool m_partialUpdatePostponed; bool m_dirty; + bool m_singleShotScheduled; QSet<QString> m_postponedFiles; State m_parserState; }; diff --git a/plugins/autotest/testnavigationwidget.cpp b/plugins/autotest/testnavigationwidget.cpp index 9745e1a19b..ad1a496b5f 100644 --- a/plugins/autotest/testnavigationwidget.cpp +++ b/plugins/autotest/testnavigationwidget.cpp @@ -75,6 +75,8 @@ TestNavigationWidget::TestNavigationWidget(QWidget *parent) : this, &TestNavigationWidget::onParsingStarted); connect(m_model->parser(), &TestCodeParser::parsingFinished, this, &TestNavigationWidget::onParsingFinished); + connect(m_model->parser(), &TestCodeParser::parsingFailed, + this, &TestNavigationWidget::onParsingFinished); connect(m_progressTimer, &QTimer::timeout, m_progressIndicator, &Utils::ProgressIndicator::show); } diff --git a/plugins/autotest/testresultdelegate.cpp b/plugins/autotest/testresultdelegate.cpp index 5e37c64955..57dc2de847 100644 --- a/plugins/autotest/testresultdelegate.cpp +++ b/plugins/autotest/testresultdelegate.cpp @@ -269,11 +269,6 @@ QSize TestResultDelegate::sizeHint(const QStyleOptionViewItem &option, const QMo return s; } -void TestResultDelegate::emitSizeHintChanged(const QModelIndex &index) -{ - emit sizeHintChanged(index); -} - void TestResultDelegate::currentChanged(const QModelIndex ¤t, const QModelIndex &previous) { emit sizeHintChanged(current); diff --git a/plugins/autotest/testresultdelegate.h b/plugins/autotest/testresultdelegate.h index 8bc5d58ad8..0b837ed2b2 100644 --- a/plugins/autotest/testresultdelegate.h +++ b/plugins/autotest/testresultdelegate.h @@ -37,10 +37,6 @@ public: void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const; QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const; - void emitSizeHintChanged(const QModelIndex &index); - -signals: - public slots: void currentChanged(const QModelIndex ¤t, const QModelIndex &previous); diff --git a/plugins/autotest/testtreeitem.h b/plugins/autotest/testtreeitem.h index 0cfa1aead1..8c7bf14307 100644 --- a/plugins/autotest/testtreeitem.h +++ b/plugins/autotest/testtreeitem.h @@ -35,6 +35,7 @@ public: ROOT, TEST_CLASS, TEST_FUNCTION, + TEST_DATATAG, TEST_DATAFUNCTION, TEST_SPECIALFUNCTION }; @@ -84,12 +85,14 @@ private: }; struct TestCodeLocationAndType { - QString m_fileName; + QString m_name; // tag name for m_type == TEST_DATATAG, file name for other values unsigned m_line; unsigned m_column; TestTreeItem::Type m_type; }; +typedef QVector<TestCodeLocationAndType> TestCodeLocationList; + } // namespace Internal } // namespace Autotest diff --git a/plugins/autotest/testtreemodel.cpp b/plugins/autotest/testtreemodel.cpp index 605167c249..0e3b09b439 100644 --- a/plugins/autotest/testtreemodel.cpp +++ b/plugins/autotest/testtreemodel.cpp @@ -96,13 +96,17 @@ TestTreeModel::~TestTreeModel() void TestTreeModel::enableParsing() { m_refCounter.ref(); + + if (!m_connectionsInitialized) + m_parser->setDirty(); + m_parser->setState(TestCodeParser::Idle); if (m_connectionsInitialized) return; ProjectExplorer::SessionManager *sm = ProjectExplorer::SessionManager::instance(); connect(sm, &ProjectExplorer::SessionManager::startupProjectChanged, - m_parser, &TestCodeParser::emitUpdateTestTree); + m_parser, &TestCodeParser::onStartupProjectChanged); CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance(); connect(cppMM, &CppTools::CppModelManager::documentUpdated, @@ -176,12 +180,13 @@ int TestTreeModel::columnCount(const QModelIndex &) const static QIcon testTreeIcon(TestTreeItem::Type type) { - static QIcon icons[3] = { + static QIcon icons[] = { QIcon(), QIcon(QLatin1String(":/images/class.png")), - QIcon(QLatin1String(":/images/func.png")) + QIcon(QLatin1String(":/images/func.png")), + QIcon(QLatin1String(":/images/data.png")) }; - if (type >= 3) + if (type >= sizeof(icons) / sizeof(icons[0])) return icons[2]; return icons[type]; } @@ -200,7 +205,7 @@ QVariant TestTreeModel::data(const QModelIndex &index, int role) const || (item == m_quickTestRootItem && m_quickTestRootItem->childCount() == 0)) { return QString(item->name() + tr(" (none)")); } else { - if (item->name().isEmpty()) + if (item->name().isEmpty() && item->type() == TestTreeItem::TEST_CLASS) return tr(Constants::UNNAMED_QUICKTESTS); return item->name(); } @@ -218,6 +223,7 @@ QVariant TestTreeModel::data(const QModelIndex &index, int role) const case Qt::CheckStateRole: switch (item->type()) { case TestTreeItem::ROOT: + case TestTreeItem::TEST_DATATAG: case TestTreeItem::TEST_DATAFUNCTION: case TestTreeItem::TEST_SPECIALFUNCTION: return QVariant(); @@ -680,7 +686,7 @@ void TestTreeModel::updateUnnamedQuickTest(const QString &fileName, const QStrin foreach (const QString &functionName, functions.keys()) { const TestCodeLocationAndType locationAndType = functions.value(functionName); - TestTreeItem *testFunction = new TestTreeItem(functionName, locationAndType.m_fileName, + TestTreeItem *testFunction = new TestTreeItem(functionName, locationAndType.m_name, locationAndType.m_type, &unnamed); testFunction->setLine(locationAndType.m_line); testFunction->setColumn(locationAndType.m_column); @@ -817,6 +823,19 @@ void TestTreeModel::processChildren(QModelIndex &parentIndex, const TestTreeItem TestTreeItem *modifiedChild = newItem.child(row); if (toBeModifiedChild->modifyContent(modifiedChild)) emit dataChanged(child, child, modificationRoles); + + // handle data tags - just remove old and add them + if (modifiedChild->childCount() || toBeModifiedChild->childCount()) { + beginRemoveRows(child, 0, toBeModifiedChild->childCount()); + toBeModifiedChild->removeChildren(); + endRemoveRows(); + const int count = modifiedChild->childCount(); + beginInsertRows(child, 0, count); + for (int childRow = 0; childRow < count; ++childRow) + toBeModifiedChild->appendChild(new TestTreeItem(*modifiedChild->child(childRow))); + endInsertRows(); + } + if (checkStates.contains(toBeModifiedChild->name())) { Qt::CheckState state = checkStates.value(toBeModifiedChild->name()); if (state != toBeModifiedChild->checked()) { diff --git a/plugins/autotest/testvisitor.cpp b/plugins/autotest/testvisitor.cpp index 2d38cb56b4..3fe84cb077 100644 --- a/plugins/autotest/testvisitor.cpp +++ b/plugins/autotest/testvisitor.cpp @@ -21,7 +21,6 @@ #include <cplusplus/FullySpecifiedType.h> #include <cplusplus/LookupContext.h> -#include <cplusplus/Overview.h> #include <cplusplus/Symbols.h> #include <cplusplus/TypeOfExpression.h> @@ -29,6 +28,8 @@ #include <qmljs/parser/qmljsast_p.h> +#include <utils/qtcassert.h> + #include <QList> namespace Autotest { @@ -68,11 +69,19 @@ bool TestVisitor::visit(CPlusPlus::Class *symbol) if (const auto func = type->asFunctionType()) { if (func->isSlot() && member->isPrivate()) { const QString name = o.prettyName(func->name()); - // TODO use definition of function instead of declaration! TestCodeLocationAndType locationAndType; - locationAndType.m_fileName = QLatin1String(member->fileName()); - locationAndType.m_line = member->line(); - locationAndType.m_column = member->column() - 1; + + CPlusPlus::Function *functionDefinition = m_symbolFinder.findMatchingDefinition( + func, CppTools::CppModelManager::instance()->snapshot(), true); + if (functionDefinition) { + locationAndType.m_name = QString::fromUtf8(functionDefinition->fileName()); + locationAndType.m_line = functionDefinition->line(); + locationAndType.m_column = functionDefinition->column() - 1; + } else { // if we cannot find the definition use declaration as fallback + locationAndType.m_name = QString::fromUtf8(member->fileName()); + locationAndType.m_line = member->line(); + locationAndType.m_column = member->column() - 1; + } if (specialFunctions.contains(name)) locationAndType.m_type = TestTreeItem::TEST_SPECIALFUNCTION; else if (name.endsWith(QLatin1String("_data"))) @@ -137,6 +146,132 @@ bool TestAstVisitor::visit(CPlusPlus::CompoundStatementAST *ast) return true; } +/********************** Test Data Function AST Visitor ************************/ + +TestDataFunctionVisitor::TestDataFunctionVisitor(CPlusPlus::Document::Ptr doc) + : CPlusPlus::ASTVisitor(doc->translationUnit()), + m_currentDoc(doc), + m_currentAstDepth(0), + m_insideUsingQTestDepth(0), + m_insideUsingQTest(false) +{ +} + +TestDataFunctionVisitor::~TestDataFunctionVisitor() +{ +} + +bool TestDataFunctionVisitor::visit(CPlusPlus::UsingDirectiveAST *ast) +{ + if (auto nameAST = ast->name) { + if (m_overview.prettyName(nameAST->name) == QLatin1String("QTest")) { + m_insideUsingQTest = true; + // we need the surrounding AST depth as using directive is an AST itself + m_insideUsingQTestDepth = m_currentAstDepth - 1; + } + } + return true; +} + +bool TestDataFunctionVisitor::visit(CPlusPlus::FunctionDefinitionAST *ast) +{ + if (ast->declarator) { + CPlusPlus::DeclaratorIdAST *id = ast->declarator->core_declarator->asDeclaratorId(); + if (!id) + return false; + + const QString prettyName = m_overview.prettyName(id->name->name); + // do not handle functions that aren't real test data functions + if (!prettyName.endsWith(QLatin1String("_data")) || !ast->symbol + || ast->symbol->argumentCount() != 0) { + return false; + } + + m_currentFunction = prettyName.left(prettyName.size() - 5); + m_currentTags.clear(); + return true; + } + + return false; +} + +bool TestDataFunctionVisitor::visit(CPlusPlus::CallAST *ast) +{ + if (m_currentFunction.isEmpty()) + return true; + + unsigned firstToken; + if (newRowCallFound(ast, &firstToken)) { + if (const auto expressionListAST = ast->expression_list) { + // first argument is the one we need + if (const auto argumentExpressionAST = expressionListAST->value) { + if (const auto stringLiteral = argumentExpressionAST->asStringLiteral()) { + auto token = m_currentDoc->translationUnit()->tokenAt( + stringLiteral->literal_token); + if (token.isStringLiteral()) { + unsigned line = 0; + unsigned column = 0; + m_currentDoc->translationUnit()->getTokenStartPosition( + firstToken, &line, &column); + TestCodeLocationAndType locationAndType; + locationAndType.m_name = QString::fromUtf8(token.spell()); + locationAndType.m_column = column - 1; + locationAndType.m_line = line; + locationAndType.m_type = TestTreeItem::TEST_DATATAG; + m_currentTags.append(locationAndType); + } + } + } + } + } + return true; +} + +bool TestDataFunctionVisitor::preVisit(CPlusPlus::AST *) +{ + ++m_currentAstDepth; + return true; +} + +void TestDataFunctionVisitor::postVisit(CPlusPlus::AST *ast) +{ + --m_currentAstDepth; + m_insideUsingQTest &= m_currentAstDepth >= m_insideUsingQTestDepth; + + if (!ast->asFunctionDefinition()) + return; + + if (!m_currentFunction.isEmpty() && !m_currentTags.isEmpty()) + m_dataTags.insert(m_currentFunction, m_currentTags); + + m_currentFunction.clear(); + m_currentTags.clear(); +} + +bool TestDataFunctionVisitor::newRowCallFound(CPlusPlus::CallAST *ast, unsigned *firstToken) const +{ + QTC_ASSERT(firstToken, return false); + + if (!ast->base_expression) + return false; + + bool found = false; + + if (const CPlusPlus::IdExpressionAST *exp = ast->base_expression->asIdExpression()) { + if (!exp->name) + return false; + + if (const auto qualifiedNameAST = exp->name->asQualifiedName()) { + found = m_overview.prettyName(qualifiedNameAST->name) == QLatin1String("QTest::newRow"); + *firstToken = qualifiedNameAST->firstToken(); + } else if (m_insideUsingQTest) { + found = m_overview.prettyName(exp->name->name) == QLatin1String("newRow"); + *firstToken = exp->name->firstToken(); + } + } + return found; +} + /*************************** Quick Test AST Visitor ***************************/ TestQmlVisitor::TestQmlVisitor(QmlJS::Document::Ptr doc) @@ -156,7 +291,7 @@ bool TestQmlVisitor::visit(QmlJS::AST::UiObjectDefinition *ast) m_currentTestCaseName.clear(); const auto sourceLocation = ast->firstSourceLocation(); - m_testCaseLocation.m_fileName = m_currentDoc->fileName(); + m_testCaseLocation.m_name = m_currentDoc->fileName(); m_testCaseLocation.m_line = sourceLocation.startLine; m_testCaseLocation.m_column = sourceLocation.startColumn - 1; m_testCaseLocation.m_type = TestTreeItem::TEST_CLASS; @@ -184,7 +319,7 @@ bool TestQmlVisitor::visit(QmlJS::AST::FunctionDeclaration *ast) || specialFunctions.contains(name.toString())) { const auto sourceLocation = ast->firstSourceLocation(); TestCodeLocationAndType locationAndType; - locationAndType.m_fileName = m_currentDoc->fileName(); + locationAndType.m_name = m_currentDoc->fileName(); locationAndType.m_line = sourceLocation.startLine; locationAndType.m_column = sourceLocation.startColumn - 1; if (specialFunctions.contains(name.toString())) diff --git a/plugins/autotest/testvisitor.h b/plugins/autotest/testvisitor.h index 279aca6cbe..4441eb26f8 100644 --- a/plugins/autotest/testvisitor.h +++ b/plugins/autotest/testvisitor.h @@ -24,9 +24,12 @@ #include <cplusplus/ASTVisitor.h> #include <cplusplus/CppDocument.h> +#include <cplusplus/Overview.h> #include <cplusplus/Scope.h> #include <cplusplus/SymbolVisitor.h> +#include <cpptools/symbolfinder.h> + #include <qmljs/parser/qmljsastvisitor_p.h> #include <qmljs/qmljsdocument.h> @@ -47,6 +50,7 @@ public: bool visit(CPlusPlus::Class *symbol); private: + CppTools::SymbolFinder m_symbolFinder; QString m_className; QMap<QString, TestCodeLocationAndType> m_privSlots; }; @@ -69,6 +73,32 @@ private: }; +class TestDataFunctionVisitor : public CPlusPlus::ASTVisitor +{ +public: + TestDataFunctionVisitor(CPlusPlus::Document::Ptr doc); + virtual ~TestDataFunctionVisitor(); + + bool visit(CPlusPlus::UsingDirectiveAST *ast); + bool visit(CPlusPlus::FunctionDefinitionAST *ast); + bool visit(CPlusPlus::CallAST *ast); + bool preVisit(CPlusPlus::AST *ast); + void postVisit(CPlusPlus::AST *ast); + QMap<QString, TestCodeLocationList> dataTags() const { return m_dataTags; } + +private: + bool newRowCallFound(CPlusPlus::CallAST *ast, unsigned *firstToken) const; + + CPlusPlus::Document::Ptr m_currentDoc; + CPlusPlus::Overview m_overview; + QString m_currentFunction; + QMap<QString, TestCodeLocationList> m_dataTags; + TestCodeLocationList m_currentTags; + unsigned m_currentAstDepth; + unsigned m_insideUsingQTestDepth; + bool m_insideUsingQTest; +}; + class TestQmlVisitor : public QmlJS::AST::Visitor { public: diff --git a/plugins/autotest/unit_test/mixed_atp/mixed_atp.qbs b/plugins/autotest/unit_test/mixed_atp/mixed_atp.qbs new file mode 100644 index 0000000000..dfc99c15df --- /dev/null +++ b/plugins/autotest/unit_test/mixed_atp/mixed_atp.qbs @@ -0,0 +1,8 @@ +import qbs + +Project { + references: [ + "src/src.qbs", + "tests/tests.qbs" + ] +} diff --git a/plugins/autotest/unit_test/mixed_atp/src/src.qbs b/plugins/autotest/unit_test/mixed_atp/src/src.qbs new file mode 100644 index 0000000000..5ed4dd4a36 --- /dev/null +++ b/plugins/autotest/unit_test/mixed_atp/src/src.qbs @@ -0,0 +1,11 @@ +import qbs + +CppApplication { + type: "application" + name: "Dummy Application" + + Depends { name: "Qt.gui" } + Depends { name: "Qt.widgets" } + + files: [ "main.cpp" ] +} diff --git a/plugins/autotest/unit_test/mixed_atp/tests/auto/auto.qbs b/plugins/autotest/unit_test/mixed_atp/tests/auto/auto.qbs new file mode 100644 index 0000000000..a27b392244 --- /dev/null +++ b/plugins/autotest/unit_test/mixed_atp/tests/auto/auto.qbs @@ -0,0 +1,13 @@ +import qbs + +Project { + name: "Auto tests" + + references: [ + "bench/bench.qbs", + "dummy/dummy.qbs", + "gui/gui.qbs", + "quickauto/quickauto.qbs", + "quickauto2/quickauto2.qbs" + ] +} diff --git a/plugins/autotest/unit_test/mixed_atp/tests/auto/bench/bench.qbs b/plugins/autotest/unit_test/mixed_atp/tests/auto/bench/bench.qbs new file mode 100644 index 0000000000..d10891316a --- /dev/null +++ b/plugins/autotest/unit_test/mixed_atp/tests/auto/bench/bench.qbs @@ -0,0 +1,14 @@ +import qbs + +CppApplication { + type: "application" + name: "Benchmark Auto Test" + targetName: "tst_benchtest" + + Depends { name: "cpp" } + Depends { name: "Qt.test" } + + files: [ "tst_benchtest.cpp" ] + + cpp.defines: base.concat("SRCDIR=" + path) +} diff --git a/plugins/autotest/unit_test/mixed_atp/tests/auto/dummy/dummy.qbs b/plugins/autotest/unit_test/mixed_atp/tests/auto/dummy/dummy.qbs new file mode 100644 index 0000000000..012ba11a67 --- /dev/null +++ b/plugins/autotest/unit_test/mixed_atp/tests/auto/dummy/dummy.qbs @@ -0,0 +1,12 @@ +import qbs + +CppApplication { + type: "application" + name: "Dummy auto test" + targetName: "tst_FooBar" + + Depends { name: "Qt.test" } + Depends { name: "Qt.gui" } + + files: [ "tst_foo.cpp", "tst_foo.h" ] +} diff --git a/plugins/autotest/unit_test/mixed_atp/tests/auto/gui/gui.qbs b/plugins/autotest/unit_test/mixed_atp/tests/auto/gui/gui.qbs new file mode 100644 index 0000000000..05359cf10f --- /dev/null +++ b/plugins/autotest/unit_test/mixed_atp/tests/auto/gui/gui.qbs @@ -0,0 +1,13 @@ +import qbs + +CppApplication { + name: "Gui auto test" + targetName: "tst_gui" + + Depends { name: "Qt"; submodules: [ "gui", "widgets", "test" ] } + Depends { name: "cpp" } + + files: [ "tst_guitest.cpp" ] + + cpp.defines: base.concat("SRCDIR=" + path) +} diff --git a/plugins/autotest/unit_test/mixed_atp/tests/auto/quickauto/quickauto.qbs b/plugins/autotest/unit_test/mixed_atp/tests/auto/quickauto/quickauto.qbs new file mode 100644 index 0000000000..1697cd51a2 --- /dev/null +++ b/plugins/autotest/unit_test/mixed_atp/tests/auto/quickauto/quickauto.qbs @@ -0,0 +1,33 @@ +import qbs + +CppApplication { + name: "Qt Quick auto test" + targetName: "test_mal_qtquick" + + Depends { name: "cpp" } + Depends { name: "Qt.core" } + Depends { + condition: Qt.core.versionMajor > 4 + name: "Qt.qmltest" + } + + Group { + name: "main application" + condition: Qt.core.versionMajor > 4 + + files: [ "main.cpp" ] + } + + Group { + name: "qml test files" + qbs.install: true + + files: [ + "tst_test1.qml", "tst_test2.qml", "TestDummy.qml", + "bar/tst_foo.qml", "tst_test3.qml" + ] + } + + // this should be set automatically, but it seems as if this does not happen + cpp.defines: base.concat("QUICK_TEST_SOURCE_DIR=\"" + path + "\"") +} diff --git a/plugins/autotest/unit_test/mixed_atp/tests/auto/quickauto2/quickauto2.qbs b/plugins/autotest/unit_test/mixed_atp/tests/auto/quickauto2/quickauto2.qbs new file mode 100644 index 0000000000..a5fe65fbd6 --- /dev/null +++ b/plugins/autotest/unit_test/mixed_atp/tests/auto/quickauto2/quickauto2.qbs @@ -0,0 +1,29 @@ +import qbs + +CppApplication { + name: "Qt Quick auto test 2" + targetName: "test_mal_qtquick" + + Depends { name: "cpp" } + Depends { name: "Qt.core" } + Depends { + condition: Qt.core.versionMajor > 4 + name: "Qt.qmltest" + } + + Group { + condition: Qt.core.versionMajor > 4 + name: "main application" + files: [ "main.cpp" ] + } + + Group { + name: "qml test files" + qbs.install: true + + files: [ "tst_test1.qml", "tst_test2.qml" ] + } + + // this should be set automatically, but it seems as if this does not happen + cpp.defines: base.concat("QUICK_TEST_SOURCE_DIR=\"" + path + "\"") +} diff --git a/plugins/autotest/unit_test/mixed_atp/tests/tests.qbs b/plugins/autotest/unit_test/mixed_atp/tests/tests.qbs new file mode 100644 index 0000000000..e62815789f --- /dev/null +++ b/plugins/autotest/unit_test/mixed_atp/tests/tests.qbs @@ -0,0 +1,7 @@ +import qbs + +Project { + name: "Tests" + + references: [ "auto/auto.qbs" ] +} diff --git a/plugins/autotest/unit_test/plain/plain.qbs b/plugins/autotest/unit_test/plain/plain.qbs new file mode 100644 index 0000000000..c2fe33618a --- /dev/null +++ b/plugins/autotest/unit_test/plain/plain.qbs @@ -0,0 +1,7 @@ +import qbs + +Project { + name: "Plain test project" + + references: [ "test_plain/test_plain.qbs" ] +} diff --git a/plugins/autotest/unit_test/plain/test_plain/test_plain.qbs b/plugins/autotest/unit_test/plain/test_plain/test_plain.qbs new file mode 100644 index 0000000000..1b7937ce6a --- /dev/null +++ b/plugins/autotest/unit_test/plain/test_plain/test_plain.qbs @@ -0,0 +1,10 @@ +import qbs + +CppApplication { + type: "application" // suppress bundle generation on OSX + + Depends { name: "Qt.gui" } + Depends { name: "Qt.test" } + + files: [ "tst_simple.cpp", "tst_simple.h" ] +} |