summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--plugins/autotest/autotest.qbs6
-rw-r--r--plugins/autotest/autotest.qrc1
-rw-r--r--plugins/autotest/autotest_dependencies.pri8
-rw-r--r--plugins/autotest/autotestunittests.cpp60
-rw-r--r--plugins/autotest/autotestunittests.h2
-rw-r--r--plugins/autotest/autotestunittests.qrc11
-rw-r--r--plugins/autotest/images/data.pngbin0 -> 646 bytes
-rw-r--r--plugins/autotest/testcodeparser.cpp293
-rw-r--r--plugins/autotest/testcodeparser.h8
-rw-r--r--plugins/autotest/testnavigationwidget.cpp2
-rw-r--r--plugins/autotest/testresultdelegate.cpp5
-rw-r--r--plugins/autotest/testresultdelegate.h4
-rw-r--r--plugins/autotest/testtreeitem.h5
-rw-r--r--plugins/autotest/testtreemodel.cpp31
-rw-r--r--plugins/autotest/testvisitor.cpp149
-rw-r--r--plugins/autotest/testvisitor.h30
-rw-r--r--plugins/autotest/unit_test/mixed_atp/mixed_atp.qbs8
-rw-r--r--plugins/autotest/unit_test/mixed_atp/src/src.qbs11
-rw-r--r--plugins/autotest/unit_test/mixed_atp/tests/auto/auto.qbs13
-rw-r--r--plugins/autotest/unit_test/mixed_atp/tests/auto/bench/bench.qbs14
-rw-r--r--plugins/autotest/unit_test/mixed_atp/tests/auto/dummy/dummy.qbs12
-rw-r--r--plugins/autotest/unit_test/mixed_atp/tests/auto/gui/gui.qbs13
-rw-r--r--plugins/autotest/unit_test/mixed_atp/tests/auto/quickauto/quickauto.qbs33
-rw-r--r--plugins/autotest/unit_test/mixed_atp/tests/auto/quickauto2/quickauto2.qbs29
-rw-r--r--plugins/autotest/unit_test/mixed_atp/tests/tests.qbs7
-rw-r--r--plugins/autotest/unit_test/plain/plain.qbs7
-rw-r--r--plugins/autotest/unit_test/plain/test_plain/test_plain.qbs10
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
new file mode 100644
index 0000000000..1f510c4042
--- /dev/null
+++ b/plugins/autotest/images/data.png
Binary files differ
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 &current, 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 &current, 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" ]
+}