AutoTest: Split off TestParseResult...

...to allow different approaches for different test frameworks.

Change-Id: I16f101fb3f702a0db00cffe33e0d83cd4ea28c99
Reviewed-by: David Schulz <david.schulz@theqtcompany.com>
This commit is contained in:
Christian Stenger
2016-04-11 14:50:04 +02:00
committed by Christian Stenger
parent db71490b1e
commit b58a10dfff
6 changed files with 151 additions and 103 deletions

View File

@@ -456,7 +456,7 @@ static bool checkQmlDocumentForTestCode(QFutureInterface<TestParseResultPtr> fut
const TestCodeLocationAndType tcLocationAndType = qmlVisitor.testCaseLocation(); const TestCodeLocationAndType tcLocationAndType = qmlVisitor.testCaseLocation();
const QMap<QString, TestCodeLocationAndType> testFunctions = qmlVisitor.testFunctions(); const QMap<QString, TestCodeLocationAndType> testFunctions = qmlVisitor.testFunctions();
TestParseResultPtr parseResult(new TestParseResult(TestTreeModel::QuickTest)); QuickTestParseResult *parseResult = new QuickTestParseResult(TestTreeModel::QuickTest);
parseResult->proFile = proFile; parseResult->proFile = proFile;
parseResult->functions = testFunctions; parseResult->functions = testFunctions;
if (!testCaseName.isEmpty()) { if (!testCaseName.isEmpty()) {
@@ -465,7 +465,7 @@ static bool checkQmlDocumentForTestCode(QFutureInterface<TestParseResultPtr> fut
parseResult->line = tcLocationAndType.m_line; parseResult->line = tcLocationAndType.m_line;
parseResult->column = tcLocationAndType.m_column; parseResult->column = tcLocationAndType.m_column;
} }
futureInterface.reportResult(parseResult); futureInterface.reportResult(TestParseResultPtr(parseResult));
return true; return true;
} }
@@ -498,16 +498,16 @@ static bool handleQtTest(QFutureInterface<TestParseResultPtr> futureInterface,
foreach (const QString &file, files) foreach (const QString &file, files)
dataTags.unite(checkForDataTags(file)); dataTags.unite(checkForDataTags(file));
TestParseResultPtr parseResult(new TestParseResult(TestTreeModel::AutoTest)); QtTestParseResult *parseResult = new QtTestParseResult(TestTreeModel::AutoTest);
parseResult->fileName = declaringDoc->fileName(); parseResult->fileName = declaringDoc->fileName();
parseResult->testCaseName = testCaseName; parseResult->testCaseName = testCaseName;
parseResult->line = line; parseResult->line = line;
parseResult->column = column; parseResult->column = column;
parseResult->functions = testFunctions; parseResult->functions = testFunctions;
parseResult->dataTagsOrTestSets = dataTags; parseResult->dataTags = dataTags;
parseResult->proFile = modelManager->projectPart(fileName).first()->projectFile; parseResult->proFile = modelManager->projectPart(fileName).first()->projectFile;
futureInterface.reportResult(parseResult); futureInterface.reportResult(TestParseResultPtr(parseResult));
return true; return true;
} }
return false; return false;
@@ -555,15 +555,15 @@ static bool handleGTest(QFutureInterface<TestParseResultPtr> futureInterface, co
proFile = ppList.first()->projectFile; proFile = ppList.first()->projectFile;
foreach (const GTestCaseSpec &testSpec, result.keys()) { foreach (const GTestCaseSpec &testSpec, result.keys()) {
TestParseResultPtr parseResult(new TestParseResult(TestTreeModel::GoogleTest)); GoogleTestParseResult *parseResult = new GoogleTestParseResult(TestTreeModel::GoogleTest);
parseResult->fileName = filePath; parseResult->fileName = filePath;
parseResult->testCaseName = testSpec.testCaseName; parseResult->testCaseName = testSpec.testCaseName;
parseResult->parameterized = testSpec.parameterized; parseResult->parameterized = testSpec.parameterized;
parseResult->typed = testSpec.typed; parseResult->typed = testSpec.typed;
parseResult->disabled = testSpec.disabled; parseResult->disabled = testSpec.disabled;
parseResult->proFile = proFile; parseResult->proFile = proFile;
parseResult->dataTagsOrTestSets.insert(QString(), result.value(testSpec)); parseResult->testSets = result.value(testSpec);
futureInterface.reportResult(parseResult); futureInterface.reportResult(TestParseResultPtr(parseResult));
} }
return !result.keys().isEmpty(); return !result.keys().isEmpty();
} }

View File

@@ -43,8 +43,46 @@ class Id;
namespace Autotest { namespace Autotest {
namespace Internal { namespace Internal {
struct TestCodeLocationAndType; class TestParseResult
struct GTestCaseSpec; {
public:
explicit TestParseResult(TestTreeModel::Type t = TestTreeModel::Invalid) : type(t) {}
virtual ~TestParseResult() {}
TestTreeModel::Type type;
QString fileName;
QString proFile;
QString testCaseName;
unsigned line = 0;
unsigned column = 0;
};
class QtTestParseResult : public TestParseResult
{
public:
QtTestParseResult(TestTreeModel::Type t = TestTreeModel::Invalid) : TestParseResult(t) {}
QMap<QString, TestCodeLocationAndType> functions;
QMap<QString, TestCodeLocationList> dataTags;
};
class QuickTestParseResult : public TestParseResult
{
public:
QuickTestParseResult(TestTreeModel::Type t = TestTreeModel::Invalid) : TestParseResult(t) {}
QMap<QString, TestCodeLocationAndType> functions;
};
class GoogleTestParseResult : public TestParseResult
{
public:
GoogleTestParseResult(TestTreeModel::Type t = TestTreeModel::Invalid) : TestParseResult(t) {}
bool parameterized = false;
bool typed = false;
bool disabled = false;
TestCodeLocationList testSets;
};
using TestParseResultPtr = QSharedPointer<TestParseResult>;
class TestCodeParser : public QObject class TestCodeParser : public QObject
{ {
@@ -106,3 +144,5 @@ private:
} // namespace Internal } // namespace Internal
} // Autotest } // Autotest
Q_DECLARE_METATYPE(Autotest::Internal::TestParseResultPtr)

View File

@@ -25,6 +25,7 @@
#include "autotestconstants.h" #include "autotestconstants.h"
#include "autotest_utils.h" #include "autotest_utils.h"
#include "testcodeparser.h"
#include "testconfiguration.h" #include "testconfiguration.h"
#include "testtreeitem.h" #include "testtreeitem.h"
#include "testtreemodel.h" #include "testtreemodel.h"
@@ -282,16 +283,17 @@ TestTreeItem *TestTreeItem::findChildBy(CompareFunction compare)
AutoTestTreeItem *AutoTestTreeItem::createTestItem(const TestParseResult &result) AutoTestTreeItem *AutoTestTreeItem::createTestItem(const TestParseResult &result)
{ {
const QtTestParseResult &parseResult = static_cast<const QtTestParseResult &>(result);
AutoTestTreeItem *item = new AutoTestTreeItem(result.testCaseName, result.fileName, TestCase); AutoTestTreeItem *item = new AutoTestTreeItem(result.testCaseName, result.fileName, TestCase);
item->setProFile(result.proFile); item->setProFile(parseResult.proFile);
item->setLine(result.line); item->setLine(parseResult.line);
item->setColumn(result.column); item->setColumn(parseResult.column);
foreach (const QString &functionName, result.functions.keys()) { foreach (const QString &functionName, parseResult.functions.keys()) {
const TestCodeLocationAndType &locationAndType = result.functions.value(functionName); const TestCodeLocationAndType &locationAndType = parseResult.functions.value(functionName);
const QString qualifiedName = result.testCaseName + QLatin1String("::") + functionName; const QString qualifiedName = result.testCaseName + QLatin1String("::") + functionName;
item->appendChild(createFunctionItem(functionName, locationAndType, item->appendChild(createFunctionItem(functionName, locationAndType,
result.dataTagsOrTestSets.value(qualifiedName))); parseResult.dataTags.value(qualifiedName)));
} }
return item; return item;
} }
@@ -467,12 +469,14 @@ QList<TestConfiguration *> AutoTestTreeItem::getSelectedTestConfigurations() con
QuickTestTreeItem *QuickTestTreeItem::createTestItem(const TestParseResult &result) QuickTestTreeItem *QuickTestTreeItem::createTestItem(const TestParseResult &result)
{ {
QuickTestTreeItem *item = new QuickTestTreeItem(result.testCaseName, result.fileName, TestCase); const QuickTestParseResult &parseResult = static_cast<const QuickTestParseResult &>(result);
QuickTestTreeItem *item = new QuickTestTreeItem(parseResult.testCaseName, parseResult.fileName,
TestCase);
item->setProFile(result.proFile); item->setProFile(result.proFile);
item->setLine(result.line); item->setLine(result.line);
item->setColumn(result.column); item->setColumn(result.column);
foreach (const QString &functionName, result.functions.keys()) foreach (const QString &functionName, parseResult.functions.keys())
item->appendChild(createFunctionItem(functionName, result.functions.value(functionName))); item->appendChild(createFunctionItem(functionName, parseResult.functions.value(functionName)));
return item; return item;
} }
@@ -487,20 +491,22 @@ QuickTestTreeItem *QuickTestTreeItem::createFunctionItem(const QString &function
QuickTestTreeItem *QuickTestTreeItem::createUnnamedQuickTestItem(const TestParseResult &result) QuickTestTreeItem *QuickTestTreeItem::createUnnamedQuickTestItem(const TestParseResult &result)
{ {
const QuickTestParseResult &parseResult = static_cast<const QuickTestParseResult &>(result);
QuickTestTreeItem *item = new QuickTestTreeItem(QString(), QString(), TestCase); QuickTestTreeItem *item = new QuickTestTreeItem(QString(), QString(), TestCase);
foreach (const QString &functionName, result.functions.keys()) foreach (const QString &functionName, parseResult.functions.keys())
item->appendChild(createUnnamedQuickFunctionItem(functionName, result)); item->appendChild(createUnnamedQuickFunctionItem(functionName, parseResult));
return item; return item;
} }
QuickTestTreeItem *QuickTestTreeItem::createUnnamedQuickFunctionItem(const QString &functionName, QuickTestTreeItem *QuickTestTreeItem::createUnnamedQuickFunctionItem(const QString &functionName,
const TestParseResult &result) const TestParseResult &result)
{ {
const TestCodeLocationAndType &location = result.functions.value(functionName); const QuickTestParseResult &parseResult = static_cast<const QuickTestParseResult &>(result);
const TestCodeLocationAndType &location = parseResult.functions.value(functionName);
QuickTestTreeItem *item = new QuickTestTreeItem(functionName, location.m_name, location.m_type); QuickTestTreeItem *item = new QuickTestTreeItem(functionName, location.m_name, location.m_type);
item->setLine(location.m_line); item->setLine(location.m_line);
item->setColumn(location.m_column); item->setColumn(location.m_column);
item->setProFile(result.proFile); item->setProFile(parseResult.proFile);
return item; return item;
} }
@@ -738,15 +744,16 @@ static QString gtestFilter(GoogleTestTreeItem::TestStates states)
GoogleTestTreeItem *GoogleTestTreeItem::createTestItem(const TestParseResult &result) GoogleTestTreeItem *GoogleTestTreeItem::createTestItem(const TestParseResult &result)
{ {
GoogleTestTreeItem *item = new GoogleTestTreeItem(result.testCaseName, QString(), TestCase); const GoogleTestParseResult &parseResult = static_cast<const GoogleTestParseResult &>(result);
item->setProFile(result.proFile); GoogleTestTreeItem *item = new GoogleTestTreeItem(parseResult.testCaseName, QString(), TestCase);
if (result.parameterized) item->setProFile(parseResult.proFile);
if (parseResult.parameterized)
item->setState(Parameterized); item->setState(Parameterized);
if (result.typed) if (parseResult.typed)
item->setState(Typed); item->setState(Typed);
if (result.disabled) if (parseResult.disabled)
item->setState(Disabled); item->setState(Disabled);
foreach (const TestCodeLocationAndType &location, result.dataTagsOrTestSets.first()) foreach (const TestCodeLocationAndType &location, parseResult.testSets)
item->appendChild(createTestSetItem(result, location)); item->appendChild(createTestSetItem(result, location));
return item; return item;
} }

View File

@@ -47,7 +47,7 @@ struct TestCodeLocationAndType;
class AutoTestTreeItem; class AutoTestTreeItem;
class QuickTestTreeItem; class QuickTestTreeItem;
class GoogleTestTreeItem; class GoogleTestTreeItem;
struct TestParseResult; class TestParseResult;
class TestConfiguration; class TestConfiguration;
class TestTreeItem : public Utils::TreeItem class TestTreeItem : public Utils::TreeItem

View File

@@ -341,17 +341,12 @@ void TestTreeModel::onParseResultReady(const TestParseResultPtr result)
{ {
switch (result->type) { switch (result->type) {
case AutoTest: case AutoTest:
handleParseResult(result); handleQtParseResult(result);
break; break;
case QuickTest: case QuickTest:
if (result->testCaseName.isEmpty()) { handleQuickParseResult(result);
handleUnnamedQuickParseResult(result);
break;
}
handleParseResult(result);
break; break;
case GoogleTest: case GoogleTest:
QTC_ASSERT(result->dataTagsOrTestSets.size() == 1, return);
handleGTestParseResult(result); handleGTestParseResult(result);
break; break;
case Invalid: case Invalid:
@@ -360,27 +355,12 @@ void TestTreeModel::onParseResultReady(const TestParseResultPtr result)
} }
} }
void TestTreeModel::handleParseResult(const TestParseResultPtr result) void TestTreeModel::handleQtParseResult(const TestParseResultPtr result)
{ {
TestTreeItem *root; TestTreeItem *toBeModified = m_autoTestRootItem->findChildByFile(result->fileName);
switch (result->type) {
case AutoTest:
root = m_autoTestRootItem;
break;
case QuickTest:
root = m_quickTestRootItem;
break;
default:
QTC_ASSERT(false, return); // should never happen, just to avoid warning
}
TestTreeItem *toBeModified = root->findChildByFile(result->fileName);
// if there's no matching item, add the new one // if there's no matching item, add the new one
if (!toBeModified) { if (!toBeModified) {
if (result->type == AutoTest) m_autoTestRootItem->appendChild(AutoTestTreeItem::createTestItem(*result));
root->appendChild(AutoTestTreeItem::createTestItem(*result));
else
root->appendChild(QuickTestTreeItem::createTestItem(*result));
return; return;
} }
// else we have to check level by level.. first the current level... // else we have to check level by level.. first the current level...
@@ -390,30 +370,26 @@ void TestTreeModel::handleParseResult(const TestParseResultPtr result)
if (changed) if (changed)
emit dataChanged(indexForItem(toBeModified), indexForItem(toBeModified)); emit dataChanged(indexForItem(toBeModified), indexForItem(toBeModified));
// ...now the functions // ...now the functions
foreach (const QString &func, result->functions.keys()) { const QtTestParseResult &parseResult = static_cast<const QtTestParseResult &>(*result);
foreach (const QString &func, parseResult.functions.keys()) {
TestTreeItem *functionItem = toBeModified->findChildByName(func); TestTreeItem *functionItem = toBeModified->findChildByName(func);
// if there's no function matching, add the new one
if (!functionItem) { if (!functionItem) {
const QString qualifiedName = result->testCaseName + QLatin1String("::") + func; // if there's no function matching, add the new one
if (result->type == AutoTest) { const QString qualifiedName = parseResult.testCaseName + QLatin1String("::") + func;
toBeModified->appendChild(AutoTestTreeItem::createFunctionItem( toBeModified->appendChild(AutoTestTreeItem::createFunctionItem(
func, result->functions.value(func), func, parseResult.functions.value(func),
result->dataTagsOrTestSets.value(qualifiedName))); parseResult.dataTags.value(qualifiedName)));
} else {
toBeModified->appendChild(QuickTestTreeItem::createFunctionItem(
func, result->functions.value(func)));
}
continue; continue;
} }
// else we have to check level by level.. first the current level... // else we have to check level by level.. first the current level...
changed = functionItem->modifyTestFunctionContent(result->functions.value(func)); changed = functionItem->modifyTestFunctionContent(parseResult.functions.value(func));
functionItem->markForRemoval(false); functionItem->markForRemoval(false);
if (changed) if (changed)
emit dataChanged(indexForItem(functionItem), indexForItem(functionItem)); emit dataChanged(indexForItem(functionItem), indexForItem(functionItem));
// ...now the data tags - actually these are supported only for AutoTestTreeItem // ...now the data tags
const QString &funcFileName = result->functions.value(func).m_name; const QString &funcFileName = parseResult.functions.value(func).m_name;
const QString qualifiedFunctionName = result->testCaseName + QLatin1String("::") + func; const QString qualifiedFunctionName = parseResult.testCaseName + QLatin1String("::") + func;
foreach (const TestCodeLocationAndType &location, result->dataTagsOrTestSets.value(qualifiedFunctionName)) { foreach (const TestCodeLocationAndType &location, parseResult.dataTags.value(qualifiedFunctionName)) {
TestTreeItem *dataTagItem = functionItem->findChildByName(location.m_name); TestTreeItem *dataTagItem = functionItem->findChildByName(location.m_name);
if (!dataTagItem) { if (!dataTagItem) {
functionItem->appendChild(AutoTestTreeItem::createDataTagItem(funcFileName, location)); functionItem->appendChild(AutoTestTreeItem::createDataTagItem(funcFileName, location));
@@ -427,20 +403,59 @@ void TestTreeModel::handleParseResult(const TestParseResultPtr result)
} }
} }
void TestTreeModel::handleQuickParseResult(const TestParseResultPtr result)
{
if (result->testCaseName.isEmpty()) {
handleUnnamedQuickParseResult(result);
return;
}
TestTreeItem *toBeModified = m_quickTestRootItem->findChildByFile(result->fileName);
// if there's no matching item, add the new one
if (!toBeModified) {
m_quickTestRootItem->appendChild(QuickTestTreeItem::createTestItem(*result));
return;
}
// else we have to check level by level.. first the current level...
bool changed = toBeModified->modifyTestCaseContent(result->testCaseName, result->line,
result->column);
toBeModified->markForRemoval(false);
if (changed)
emit dataChanged(indexForItem(toBeModified), indexForItem(toBeModified));
// ...now the functions
const QuickTestParseResult &parseResult = static_cast<const QuickTestParseResult &>(*result);
foreach (const QString &func, parseResult.functions.keys()) {
TestTreeItem *functionItem = toBeModified->findChildByName(func);
if (!functionItem) {
// if there's no matching, add the new one
toBeModified->appendChild(QuickTestTreeItem::createFunctionItem(
func, parseResult.functions.value(func)));
continue;
}
// else we have to modify..
changed = functionItem->modifyTestFunctionContent(parseResult.functions.value(func));
functionItem->markForRemoval(false);
if (changed)
emit dataChanged(indexForItem(functionItem), indexForItem(functionItem));
}
}
void TestTreeModel::handleUnnamedQuickParseResult(const TestParseResultPtr result) void TestTreeModel::handleUnnamedQuickParseResult(const TestParseResultPtr result)
{ {
const QuickTestParseResult &parseResult = static_cast<const QuickTestParseResult &>(*result);
TestTreeItem *toBeModified = unnamedQuickTests(); TestTreeItem *toBeModified = unnamedQuickTests();
if (!toBeModified) { if (!toBeModified) {
m_quickTestRootItem->appendChild(QuickTestTreeItem::createUnnamedQuickTestItem(*result)); m_quickTestRootItem->appendChild(QuickTestTreeItem::createUnnamedQuickTestItem(parseResult));
return; return;
} }
// if we have already Unnamed Quick tests we might update them.. // if we have already Unnamed Quick tests we might update them..
foreach (const QString &func, result->functions.keys()) { foreach (const QString &func, parseResult.functions.keys()) {
const TestCodeLocationAndType &location = result->functions.value(func); const TestCodeLocationAndType &location = parseResult.functions.value(func);
TestTreeItem *functionItem = toBeModified->findChildByNameAndFile(func, location.m_name); TestTreeItem *functionItem = toBeModified->findChildByNameAndFile(func, location.m_name);
if (!functionItem) { if (!functionItem) {
toBeModified->appendChild(QuickTestTreeItem::createUnnamedQuickFunctionItem( toBeModified->appendChild(QuickTestTreeItem::createUnnamedQuickFunctionItem(
func, *result)); func, parseResult));
continue; continue;
} }
functionItem->modifyLineAndColumn(location); functionItem->modifyLineAndColumn(location);
@@ -450,27 +465,30 @@ void TestTreeModel::handleUnnamedQuickParseResult(const TestParseResultPtr resul
void TestTreeModel::handleGTestParseResult(const TestParseResultPtr result) void TestTreeModel::handleGTestParseResult(const TestParseResultPtr result)
{ {
const GoogleTestParseResult &parseResult = static_cast<const GoogleTestParseResult &>(*result);
QTC_ASSERT(!parseResult.testSets.isEmpty(), return);
GoogleTestTreeItem::TestStates states = GoogleTestTreeItem::Enabled; GoogleTestTreeItem::TestStates states = GoogleTestTreeItem::Enabled;
if (result->parameterized) if (parseResult.parameterized)
states |= GoogleTestTreeItem::Parameterized; states |= GoogleTestTreeItem::Parameterized;
if (result->typed) if (parseResult.typed)
states |= GoogleTestTreeItem::Typed; states |= GoogleTestTreeItem::Typed;
TestTreeItem *toBeModified = m_googleTestRootItem->findChildByNameStateAndFile( TestTreeItem *toBeModified = m_googleTestRootItem->findChildByNameStateAndFile(
result->testCaseName, states, result->proFile); parseResult.testCaseName, states, parseResult.proFile);
if (!toBeModified) { if (!toBeModified) {
m_googleTestRootItem->appendChild(GoogleTestTreeItem::createTestItem(*result)); m_googleTestRootItem->appendChild(GoogleTestTreeItem::createTestItem(parseResult));
return; return;
} }
// if found nothing has to be updated as all relevant members are used to find the item // if found nothing has to be updated as all relevant members are used to find the item
foreach (const TestCodeLocationAndType &location , result->dataTagsOrTestSets.first()) { foreach (const TestCodeLocationAndType &location , parseResult.testSets) {
TestTreeItem *testSetItem = toBeModified->findChildByNameAndFile(location.m_name, TestTreeItem *testSetItem = toBeModified->findChildByNameAndFile(location.m_name,
result->fileName); parseResult.fileName);
if (!testSetItem) { if (!testSetItem) {
toBeModified->appendChild(GoogleTestTreeItem::createTestSetItem(*result, location)); toBeModified->appendChild(GoogleTestTreeItem::createTestSetItem(parseResult, location));
continue; continue;
} }
bool changed = static_cast<GoogleTestTreeItem *>(testSetItem)->modifyTestSetContent( bool changed = static_cast<GoogleTestTreeItem *>(testSetItem)->modifyTestSetContent(
result->fileName, location); parseResult.fileName, location);
testSetItem->markForRemoval(false); testSetItem->markForRemoval(false);
if (changed) if (changed)
emit dataChanged(indexForItem(testSetItem), indexForItem(testSetItem)); emit dataChanged(indexForItem(testSetItem), indexForItem(testSetItem));

View File

@@ -38,7 +38,7 @@ namespace Autotest {
namespace Internal { namespace Internal {
class TestCodeParser; class TestCodeParser;
struct TestParseResult; class TestParseResult;
using TestParseResultPtr = QSharedPointer<TestParseResult>; using TestParseResultPtr = QSharedPointer<TestParseResult>;
@@ -94,7 +94,8 @@ public slots:
private: private:
void onParseResultReady(const TestParseResultPtr result); void onParseResultReady(const TestParseResultPtr result);
void handleParseResult(const TestParseResultPtr result); void handleQtParseResult(const TestParseResultPtr result);
void handleQuickParseResult(const TestParseResultPtr result);
void handleUnnamedQuickParseResult(const TestParseResultPtr result); void handleUnnamedQuickParseResult(const TestParseResultPtr result);
void handleGTestParseResult(const TestParseResultPtr result); void handleGTestParseResult(const TestParseResultPtr result);
void removeAllTestItems(); void removeAllTestItems();
@@ -149,25 +150,7 @@ private:
}; };
struct TestParseResult
{
TestParseResult(TestTreeModel::Type t = TestTreeModel::Invalid) : type(t) {}
TestTreeModel::Type type;
QString fileName;
QString proFile;
QString testCaseName;
unsigned line = 0;
unsigned column = 0;
bool parameterized = false;
bool typed = false;
bool disabled = false;
QMap<QString, TestCodeLocationAndType> functions;
QMap<QString, TestCodeLocationList> dataTagsOrTestSets;
};
} // namespace Internal } // namespace Internal
} // namespace Autotest } // namespace Autotest
Q_DECLARE_METATYPE(Autotest::Internal::TestTreeModel::Type) Q_DECLARE_METATYPE(Autotest::Internal::TestTreeModel::Type)
Q_DECLARE_METATYPE(Autotest::Internal::TestParseResultPtr)