Commit 8cba5803 authored by Christian Stenger's avatar Christian Stenger

AutoTest: Introduce active state for test frameworks

Change-Id: I0fddce91a239c0a51352a25e34a221fd8880b733
Reviewed-by: default avatarDavid Schulz <david.schulz@theqtcompany.com>
Reviewed-by: Riitta-Leena Miettinen's avatarLeena Miettinen <riitta-leena.miettinen@qt.io>
parent c64fa0d3
......@@ -134,7 +134,6 @@ bool AutotestPlugin::initialize(const QStringList &arguments, QString *errorStri
m_frameworkManager->registerTestFramework(new QtTestFramework);
m_frameworkManager->registerTestFramework(new QuickTestFramework);
m_frameworkManager->registerTestFramework(new GTestFramework);
TestTreeModel::instance()->syncTestFrameworks();
m_settings->fromSettings(ICore::settings());
addAutoReleasedObject(new TestSettingsPage(m_settings));
......@@ -143,6 +142,8 @@ bool AutotestPlugin::initialize(const QStringList &arguments, QString *errorStri
if (m_settings->alwaysParse)
TestTreeModel::instance()->enableParsingFromSettings();
m_frameworkManager->activateFrameworksFromSettings(m_settings);
TestTreeModel::instance()->syncTestFrameworks();
return true;
}
......
......@@ -33,6 +33,7 @@ namespace Internal {
class GTestFramework : public ITestFramework
{
public:
GTestFramework() : ITestFramework(true) {}
const char *name() const override;
unsigned priority() const override;
......
......@@ -34,7 +34,12 @@ namespace Internal {
class ITestFramework
{
public:
virtual ~ITestFramework() { }
ITestFramework(bool activeByDefault) : m_active(activeByDefault) {}
virtual ~ITestFramework()
{
delete m_rootNode;
delete m_testParser;
}
virtual const char *name() const = 0;
virtual unsigned priority() const = 0; // should this be modifyable?
......@@ -52,6 +57,9 @@ public:
return m_testParser;
}
bool active() const { return m_active; }
void setActive(bool active) { m_active = active; }
protected:
virtual ITestParser *createTestParser() const = 0;
virtual TestTreeItem *createRootNode() const = 0;
......@@ -59,6 +67,7 @@ protected:
private:
TestTreeItem *m_rootNode = 0;
ITestParser *m_testParser = 0;
bool m_active = false;
};
} // namespace Internal
......
......@@ -33,6 +33,7 @@ namespace Internal {
class QtTestFramework : public ITestFramework
{
public:
QtTestFramework() : ITestFramework(true) {}
const char *name() const override;
unsigned priority() const override;
......
......@@ -33,6 +33,7 @@ namespace Internal {
class QuickTestFramework : public ITestFramework
{
public:
QuickTestFramework() : ITestFramework(true) {}
const char *name() const override;
unsigned priority() const override;
......
......@@ -80,7 +80,6 @@ TestCodeParser::TestCodeParser(TestTreeModel *parent)
TestCodeParser::~TestCodeParser()
{
qDeleteAll(m_testCodeParsers);
}
void TestCodeParser::setState(State state)
......@@ -114,7 +113,7 @@ void TestCodeParser::setState(State state)
}
}
void TestCodeParser::syncTestFrameworks(const QList<Core::Id> &frameworkIds)
void TestCodeParser::syncTestFrameworks(const QVector<Core::Id> &frameworkIds)
{
if (m_parserState != Disabled && m_parserState != Idle) {
// there's a running parse
......
......@@ -55,7 +55,7 @@ public:
State state() const { return m_parserState; }
bool isParsing() const { return m_parserState == PartialParse || m_parserState == FullParse; }
void setDirty() { m_dirty = true; }
void syncTestFrameworks(const QList<Core::Id> &frameworkIds);
void syncTestFrameworks(const QVector<Core::Id> &frameworkIds);
#ifdef WITH_TESTS
bool furtherParsingExpected() const
{ return m_singleShotScheduled || m_fullUpdatePostponed || m_partialUpdatePostponed; }
......@@ -95,7 +95,7 @@ private:
QSet<QString> m_postponedFiles;
State m_parserState;
QFutureWatcher<TestParseResultPtr> m_futureWatcher;
QVector<ITestParser *> m_testCodeParsers;
QVector<ITestParser *> m_testCodeParsers; // ptrs are still owned by TestFrameworkManager
};
} // namespace Internal
......
......@@ -28,6 +28,7 @@
#include "itestframework.h"
#include "itestparser.h"
#include "testrunner.h"
#include "testsettings.h"
#include "testtreeitem.h"
#include "testtreemodel.h"
......@@ -61,6 +62,8 @@ TestFrameworkManager::~TestFrameworkManager()
{
delete m_testRunner;
delete m_testTreeModel;
for (ITestFramework *framework : m_registeredFrameworks.values())
delete framework;
}
bool TestFrameworkManager::registerTestFramework(ITestFramework *framework)
......@@ -68,26 +71,61 @@ bool TestFrameworkManager::registerTestFramework(ITestFramework *framework)
QTC_ASSERT(framework, return false);
Core::Id id = Core::Id(Constants::FRAMEWORK_PREFIX).withSuffix(framework->name());
QTC_ASSERT(!m_registeredFrameworks.contains(id), return false);
// check for unique priority?
// TODO check for unique priority before registering
qCDebug(LOG) << "Registering" << id;
m_registeredFrameworks.insert(id, framework);
return true;
}
void TestFrameworkManager::activateFrameworksFromSettings(QSharedPointer<TestSettings> settings)
{
FrameworkIterator it = m_registeredFrameworks.begin();
FrameworkIterator end = m_registeredFrameworks.end();
for ( ; it != end; ++it)
it.value()->setActive(settings->frameworks.value(it.key(), false));
}
QString TestFrameworkManager::frameworkNameForId(const Core::Id &id) const
{
ITestFramework *framework = m_registeredFrameworks.value(id, 0);
return framework ? QString::fromLatin1(framework->name()) : QString();
}
QList<Core::Id> TestFrameworkManager::registeredFrameworkIds() const
{
return m_registeredFrameworks.keys();
}
QList<Core::Id> TestFrameworkManager::sortedFrameworkIds() const
QList<Core::Id> TestFrameworkManager::sortedRegisteredFrameworkIds() const
{
QList<Core::Id> registered = m_registeredFrameworks.keys();
Utils::sort(registered, [this] (const Core::Id &lhs, const Core::Id &rhs) {
return m_registeredFrameworks[lhs]->priority() < m_registeredFrameworks[rhs]->priority();
});
qCDebug(LOG) << "Registered frameworks sorted by priority" << registered;
return registered;
}
QVector<Core::Id> TestFrameworkManager::activeFrameworkIds() const
{
QList<Core::Id> sorted = registeredFrameworkIds();
qCDebug(LOG) << "Registered frameworks" << sorted;
QVector<Core::Id> active;
FrameworkIterator it = m_registeredFrameworks.begin();
FrameworkIterator end = m_registeredFrameworks.end();
for ( ; it != end; ++it) {
if (it.value()->active())
active.append(it.key());
}
return active;
}
Utils::sort(sorted, [this] (const Core::Id &lhs, const Core::Id &rhs) {
QVector<Core::Id> TestFrameworkManager::sortedActiveFrameworkIds() const
{
QVector<Core::Id> active = activeFrameworkIds();
Utils::sort(active, [this] (const Core::Id &lhs, const Core::Id &rhs) {
return m_registeredFrameworks[lhs]->priority() < m_registeredFrameworks[rhs]->priority();
});
qCDebug(LOG) << "Sorted by priority" << sorted;
return sorted;
qCDebug(LOG) << "Active frameworks sorted by priority" << active;
return active;
}
TestTreeItem *TestFrameworkManager::rootNodeForTestFramework(const Core::Id &frameworkId) const
......@@ -107,5 +145,11 @@ ITestParser *TestFrameworkManager::testParserForTestFramework(const Core::Id &fr
return testParser;
}
bool TestFrameworkManager::isActive(const Core::Id &frameworkId) const
{
ITestFramework *framework = m_registeredFrameworks.value(frameworkId);
return framework ? framework->active() : false;
}
} // namespace Internal
} // namespace Autotest
......@@ -26,6 +26,7 @@
#pragma once
#include <QHash>
#include <QSharedPointer>
namespace Core { class Id; }
......@@ -35,6 +36,7 @@ namespace Internal {
class ITestFramework;
class ITestParser;
class TestRunner;
struct TestSettings;
class TestTreeItem;
class TestTreeModel;
......@@ -44,17 +46,25 @@ public:
static TestFrameworkManager *instance();
virtual ~TestFrameworkManager();
bool registerTestFramework(ITestFramework *framework);
void activateFrameworksFromSettings(QSharedPointer<TestSettings> settings);
QString frameworkNameForId(const Core::Id &id) const;
QList<Core::Id> registeredFrameworkIds() const;
QList<Core::Id> sortedFrameworkIds() const;
QList<Core::Id> sortedRegisteredFrameworkIds() const;
QVector<Core::Id> sortedActiveFrameworkIds() const;
TestTreeItem *rootNodeForTestFramework(const Core::Id &frameworkId) const;
ITestParser *testParserForTestFramework(const Core::Id &frameworkId) const;
bool isActive(const Core::Id &frameworkId) const;
private:
QVector<Core::Id> activeFrameworkIds() const;
explicit TestFrameworkManager();
QHash<Core::Id, ITestFramework *> m_registeredFrameworks;
TestTreeModel *m_testTreeModel;
TestRunner *m_testRunner;
typedef QHash<Core::Id, ITestFramework *>::ConstIterator FrameworkIterator;
};
} // namespace Internal
......
......@@ -24,6 +24,9 @@
****************************************************************************/
#include "testsettings.h"
#include "testframeworkmanager.h"
#include <coreplugin/id.h>
#include <QSettings>
......@@ -43,6 +46,7 @@ static const char gtestRepeatKey[] = "RepeatGTests";
static const char gtestShuffleKey[] = "ShuffleGTests";
static const char gtestIterationsKey[] = "IterationsGTests";
static const char gtestSeedKey[] = "SeedGTests";
static const int defaultTimeout = 60000;
TestSettings::TestSettings()
......@@ -66,6 +70,9 @@ void TestSettings::toSettings(QSettings *s) const
s->setValue(QLatin1String(gtestShuffleKey), gtestShuffle);
s->setValue(QLatin1String(gtestIterationsKey), gtestIterations);
s->setValue(QLatin1String(gtestSeedKey), gtestSeed);
// store frameworks and their current active state
for (const Core::Id &id : frameworks.keys())
s->setValue(QLatin1String(id.name()), frameworks.value(id));
s->endGroup();
}
......@@ -102,6 +109,14 @@ void TestSettings::fromSettings(const QSettings *s)
gtestShuffle = s->value(root + QLatin1String(gtestShuffleKey), false).toBool();
gtestIterations = s->value(root + QLatin1String(gtestIterationsKey), 1).toInt();
gtestSeed = s->value(root + QLatin1String(gtestSeedKey), 0).toInt();
// try to get settings for registered frameworks
TestFrameworkManager *frameworkManager = TestFrameworkManager::instance();
const QList<Core::Id> &registered = frameworkManager->registeredFrameworkIds();
frameworks.clear();
for (const Core::Id &id : registered) {
frameworks.insert(id, s->value(root + QLatin1String(id.name()),
frameworkManager->isActive(id)).toBool());
}
}
bool TestSettings::equals(const TestSettings &rhs) const
......@@ -114,7 +129,8 @@ bool TestSettings::equals(const TestSettings &rhs) const
&& alwaysParse == rhs.alwaysParse
&& gtestRunDisabled == rhs.gtestRunDisabled
&& gtestRepeat == rhs.gtestRepeat && gtestIterations == rhs.gtestIterations
&& gtestShuffle == rhs.gtestShuffle && gtestSeed == rhs.gtestSeed;
&& gtestShuffle == rhs.gtestShuffle && gtestSeed == rhs.gtestSeed
&& frameworks == rhs.frameworks;
}
QString TestSettings::metricsTypeToOption(const MetricsType type)
......
......@@ -25,7 +25,9 @@
#pragma once
#include <QtGlobal>
#include <QHash>
namespace Core { class Id; }
QT_BEGIN_NAMESPACE
class QSettings;
......@@ -62,6 +64,7 @@ struct TestSettings
bool gtestRunDisabled;
bool gtestShuffle;
bool gtestRepeat;
QHash<Core::Id, bool> frameworks;
};
inline bool operator==(const TestSettings &s1, const TestSettings &s2) { return s1.equals(s2); }
......
......@@ -24,6 +24,7 @@
****************************************************************************/
#include "autotestconstants.h"
#include "testframeworkmanager.h"
#include "testsettingspage.h"
#include "testsettings.h"
#include "testtreemodel.h"
......@@ -79,6 +80,7 @@ void TestSettingsWidget::setSettings(const TestSettings &settings)
default:
m_ui.walltimeRB->setChecked(true);
}
populateFrameworksListWidget(settings.frameworks);
}
TestSettings TestSettingsWidget::settings() const
......@@ -107,9 +109,38 @@ TestSettings TestSettingsWidget::settings() const
else if (m_ui.perfRB->isChecked())
result.metrics = MetricsType::Perf;
result.frameworks = frameworks();
return result;
}
void TestSettingsWidget::populateFrameworksListWidget(const QHash<Core::Id, bool> &frameworks)
{
TestFrameworkManager *frameworkManager = TestFrameworkManager::instance();
const QList<Core::Id> &registered = frameworkManager->sortedRegisteredFrameworkIds();
m_ui.frameworkListWidget->clear();
for (const Core::Id &id : registered) {
QListWidgetItem *item = new QListWidgetItem(frameworkManager->frameworkNameForId(id),
m_ui.frameworkListWidget);
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
item->setCheckState(frameworks.value(id) ? Qt::Checked : Qt::Unchecked);
item->setData(Qt::UserRole, id.toSetting());
}
}
QHash<Core::Id, bool> TestSettingsWidget::frameworks() const
{
const int itemCount = m_ui.frameworkListWidget->count();
QHash<Core::Id, bool> frameworks;
for (int row = 0; row < itemCount; ++row) {
if (QListWidgetItem *item = m_ui.frameworkListWidget->item(row)) {
frameworks.insert(Core::Id::fromSetting(item->data(Qt::UserRole)),
item->checkState() == Qt::Checked);
}
}
return frameworks;
}
TestSettingsPage::TestSettingsPage(const QSharedPointer<TestSettings> &settings)
: m_settings(settings), m_widget(0)
{
......@@ -139,12 +170,16 @@ void TestSettingsPage::apply()
return;
const TestSettings newSettings = m_widget->settings();
if (newSettings != *m_settings) {
bool frameworkSyncNecessary = newSettings.frameworks != m_settings->frameworks;
*m_settings = newSettings;
m_settings->toSettings(Core::ICore::settings());
if (m_settings->alwaysParse)
TestTreeModel::instance()->enableParsingFromSettings();
else
TestTreeModel::instance()->disableParsingFromSettings();
TestFrameworkManager::instance()->activateFrameworksFromSettings(m_settings);
if (frameworkSyncNecessary)
TestTreeModel::instance()->syncTestFrameworks();
}
}
......
......@@ -46,6 +46,8 @@ public:
TestSettings settings() const;
private:
void populateFrameworksListWidget(const QHash<Core::Id, bool> &frameworks);
QHash<Core::Id, bool> frameworks() const;
Ui::TestSettingsPage m_ui;
};
......
......@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>485</width>
<width>574</width>
<height>404</height>
</rect>
</property>
......@@ -151,130 +151,18 @@
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<widget class="QGroupBox" name="groupBox_4">
<property name="title">
<string>Google Test</string>
<string>Active Test Frameworks</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<layout class="QVBoxLayout" name="verticalLayout_7">
<item>
<widget class="QCheckBox" name="runDisabledGTestsCB">
<widget class="QListWidget" name="frameworkListWidget">
<property name="toolTip">
<string>Executes disabled tests when performing a test run.</string>
</property>
<property name="text">
<string>Run disabled tests</string>
<string>Select the test frameworks to be handled by the AutoTest plugin.</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="repeatGTestsCB">
<property name="toolTip">
<string>Repeats a test run (you might be required to increase the timeout to avoid canceling the tests).</string>
</property>
<property name="text">
<string>Repeat tests</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Iterations:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="repetitionSpin">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QCheckBox" name="shuffleGTestsCB">
<property name="toolTip">
<string>Shuffle tests automatically on every iteration by the given seed.</string>
</property>
<property name="text">
<string>Shuffle tests</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Seed:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="seedSpin">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>A seed of 0 generates a seed based on the current timestamp.</string>
</property>
<property name="specialValueText">
<string/>
</property>
<property name="maximum">
<number>99999</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
......@@ -406,6 +294,134 @@
</layout>
</widget>
</item>
<item>
<widget class="QGroupBox" name="groupBox_3">
<property name="title">
<string>Google Test</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_6">
<item>
<widget class="QCheckBox" name="runDisabledGTestsCB">
<property name="toolTip">
<string>Executes disabled tests when performing a test run.</string>
</property>
<property name="text">
<string>Run disabled tests</string>
</property>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<widget class="QCheckBox" name="repeatGTestsCB">
<property name="toolTip">
<string>Repeats a test run (you might be required to increase the timeout to avoid canceling the tests).</string>
</property>
<property name="text">
<string>Repeat tests</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_3">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Iterations:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="repetitionSpin">
<property name="enabled">
<bool>false</bool>
</property>
<property name="minimum">
<number>1</number>
</property>
<property name="maximum">
<number>9999</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_3">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<widget class="QCheckBox" name="shuffleGTestsCB">
<property name="toolTip">
<string>Shuffle tests automatically on every iteration by the given seed.</string>
</property>
<property name="text">
<string>Shuffle tests</string>
</property>
</widget>
</item>
<item>
<widget class="QLabel" name="label_2">
<property name="sizePolicy">
<sizepolicy hsizetype="Preferred" vsizetype="Fixed">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="text">
<string>Seed:</string>
</property>
</widget>
</item>
<item>
<widget class="QSpinBox" name="seedSpin">
<property name="enabled">
<bool>false</bool>
</property>
<property name="toolTip">
<string>A seed of 0 generates a seed based on the current timestamp.</string>
</property>
<property name="specialValueText">
<string/>
</property>
<property name="maximum">
<number>99999</number>
</property>
</widget>
</item>
<item>
<spacer name="horizontalSpacer_4">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
</layout>
</widget>
</item>
<item>
<spacer name="verticalSpacer_2">
<property name="orientation">
......
......@@ -67,6 +67,11 @@ TestTreeModel *TestTreeModel::instance()
TestTreeModel::~TestTreeModel()
{
foreach (Utils::TreeItem *item, rootItem()->children()) {
item->removeChildren();
takeItem(item); // do NOT delete the item as it's still a ptr hold by TestFrameworkManager
}
m_instance = 0;
}
......@@ -191,10 +196,15 @@ TestConfiguration *TestTreeModel::getTestConfiguration(const TestTreeItem *item)
void TestTreeModel::syncTestFrameworks()
{
// remove all currently registered
rootItem()->removeChildren();
foreach (Utils::TreeItem *item, rootItem()->children()) {
item->removeChildren();
takeItem(item); // do NOT delete the item as it's still a ptr hold by TestFrameworkManager
}