AutoTest: Handle test tools inside project settings

This effectively enables to handle ctest project wise instead
of just globally. So far only code based test frameworks could
be enabled or disabled by project.

Change-Id: I491f91119c3500131ca51af4d55b0e68d47debf0
Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
Christian Stenger
2021-01-08 15:53:30 +01:00
parent 2b2cacfc09
commit 3de4ac3736
7 changed files with 107 additions and 26 deletions

View File

@@ -31,6 +31,7 @@
#include "testtreemodel.h"
#include <utils/algorithm.h>
#include <utils/qtcassert.h>
#include <QBoxLayout>
#include <QComboBox>
@@ -40,7 +41,10 @@
namespace Autotest {
namespace Internal {
enum ItemDataRole { FrameworkIdRole = Qt::UserRole + 1 };
enum ItemDataRole {
BaseIdRole = Qt::UserRole + 1,
BaseTypeRole
};
static QSpacerItem *createSpacer(QSizePolicy::Policy horizontal, QSizePolicy::Policy vertical)
{
@@ -90,13 +94,15 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(ProjectExplorer::Project *p
m_useGlobalSettings->setCurrentIndex(m_projectSettings->useGlobalSettings() ? 0 : 1);
generalWidget->setDisabled(m_projectSettings->useGlobalSettings());
populateFrameworks(m_projectSettings->activeFrameworks());
populateFrameworks(m_projectSettings->activeFrameworks(),
m_projectSettings->activeTestTools());
connect(m_useGlobalSettings, QOverload<int>::of(&QComboBox::currentIndexChanged),
this, [this, generalWidget](int index) {
generalWidget->setEnabled(index != 0);
m_projectSettings->setUseGlobalSettings(index == 0);
m_syncFrameworksTimer.start(3000);
m_syncTimer.start(3000);
m_syncType = ITestBase::Framework | ITestBase::Tool;
});
connect(m_activeFrameworks, &QTreeWidget::itemChanged,
this, &ProjectTestSettingsWidget::onActiveFrameworkChanged);
@@ -104,29 +110,53 @@ ProjectTestSettingsWidget::ProjectTestSettingsWidget(ProjectExplorer::Project *p
this, [this](int index) {
m_projectSettings->setRunAfterBuild(RunAfterBuildMode(index));
});
m_syncFrameworksTimer.setSingleShot(true);
connect(&m_syncFrameworksTimer, &QTimer::timeout,
TestTreeModel::instance(), &TestTreeModel::synchronizeTestFrameworks);
m_syncTimer.setSingleShot(true);
connect(&m_syncTimer, &QTimer::timeout,
[this]() {
auto testTreeModel = TestTreeModel::instance();
if (m_syncType & ITestBase::Framework)
testTreeModel->synchronizeTestFrameworks();
if (m_syncType & ITestBase::Tool)
testTreeModel->synchronizeTestTools();
m_syncType = ITestBase::None;
});
}
void ProjectTestSettingsWidget::populateFrameworks(const QHash<ITestFramework *, bool> &frameworks)
void ProjectTestSettingsWidget::populateFrameworks(const QHash<ITestFramework *, bool> &frameworks,
const QHash<ITestTool *, bool> &testTools)
{
TestFrameworks sortedFrameworks = frameworks.keys();
Utils::sort(sortedFrameworks, &ITestFramework::priority);
for (ITestFramework *framework : sortedFrameworks) {
auto item = new QTreeWidgetItem(m_activeFrameworks, QStringList(QLatin1String(framework->name())));
auto generateItem = [this](ITestBase *frameworkOrTestTool, bool checked) {
auto item = new QTreeWidgetItem(m_activeFrameworks, {QLatin1String(frameworkOrTestTool->name())});
item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable);
item->setCheckState(0, frameworks.value(framework) ? Qt::Checked : Qt::Unchecked);
item->setData(0, FrameworkIdRole, framework->id().toSetting());
}
item->setCheckState(0, checked ? Qt::Checked : Qt::Unchecked);
item->setData(0, BaseIdRole, frameworkOrTestTool->id().toSetting());
item->setData(0, BaseTypeRole, frameworkOrTestTool->type());
};
for (ITestFramework *framework : qAsConst(sortedFrameworks))
generateItem(framework, frameworks.value(framework));
// FIXME: testTools aren't sorted and we cannot use priority here
auto end = testTools.cend();
for (auto it = testTools.cbegin(); it != end; ++it)
generateItem(it.key(), it.value());
}
void ProjectTestSettingsWidget::onActiveFrameworkChanged(QTreeWidgetItem *item, int column)
{
auto id = Utils::Id::fromSetting(item->data(column, FrameworkIdRole));
m_projectSettings->activateFramework(id, item->data(0, Qt::CheckStateRole) == Qt::Checked);
m_syncFrameworksTimer.start(3000);
auto id = Utils::Id::fromSetting(item->data(column, BaseIdRole));
int type = item->data(column, BaseTypeRole).toInt();
if (type == ITestBase::Framework)
m_projectSettings->activateFramework(id, item->data(0, Qt::CheckStateRole) == Qt::Checked);
else if (type == ITestBase::Tool)
m_projectSettings->activateTestTool(id, item->data(0, Qt::CheckStateRole) == Qt::Checked);
else
QTC_ASSERT(! "unexpected test base type", return);
m_syncTimer.start(3000);
m_syncType |= type;
}
} // namespace Internal

View File

@@ -39,6 +39,7 @@ namespace ProjectExplorer { class Project; }
namespace Autotest {
class ITestFramework;
class ITestTool;
namespace Internal {
@@ -51,13 +52,15 @@ public:
explicit ProjectTestSettingsWidget(ProjectExplorer::Project *project,
QWidget *parent = nullptr);
private:
void populateFrameworks(const QHash<Autotest::ITestFramework *, bool> &frameworks);
void populateFrameworks(const QHash<Autotest::ITestFramework *, bool> &frameworks,
const QHash<Autotest::ITestTool *, bool> &testTools);
void onActiveFrameworkChanged(QTreeWidgetItem *item, int column);
TestProjectSettings *m_projectSettings;
QComboBox *m_useGlobalSettings = nullptr;
QTreeWidget *m_activeFrameworks = nullptr;
QComboBox *m_runAfterBuild = nullptr;
QTimer m_syncFrameworksTimer;
QTimer m_syncTimer;
int m_syncType = 0;
};
} // namespace Internal

View File

@@ -99,6 +99,14 @@ ITestFramework *TestFrameworkManager::frameworkForId(Id frameworkId)
});
}
ITestTool *TestFrameworkManager::testToolForId(Id testToolId)
{
return Utils::findOrDefault(s_instance->m_registeredTestTools,
[testToolId](ITestTool *testTool) {
return testTool->id() == testToolId;
});
}
ITestTool *TestFrameworkManager::testToolForBuildSystemId(Id buildSystemId)
{
if (!buildSystemId.isValid())

View File

@@ -48,6 +48,7 @@ public:
void synchronizeSettings(QSettings *s);
static ITestFramework *frameworkForId(Utils::Id frameworkId);
static ITestTool *testToolForId(Utils::Id testToolId);
static ITestTool *testToolForBuildSystemId(Utils::Id buildSystemId);
static void activateFrameworksAndToolsFromSettings(const Internal::TestSettings *settings);
static const TestFrameworks registeredFrameworks();

View File

@@ -73,26 +73,43 @@ void TestProjectSettings::activateFramework(const Utils::Id &id, bool activate)
framework->resetRootNode();
}
void TestProjectSettings::activateTestTool(const Utils::Id &id, bool activate)
{
ITestTool *testTool = TestFrameworkManager::testToolForId(id);
m_activeTestTools[testTool] = activate;
if (!activate)
testTool->resetRootNode();
}
void TestProjectSettings::load()
{
const QVariant useGlobal = m_project->namedSettings(Constants::SK_USE_GLOBAL);
m_useGlobalSettings = useGlobal.isValid() ? useGlobal.toBool() : true;
const TestFrameworks registered = TestFrameworkManager::registeredFrameworks();
qCDebug(LOG) << "Registered frameworks sorted by priority" << registered;
const TestFrameworks registeredFrameworks = TestFrameworkManager::registeredFrameworks();
qCDebug(LOG) << "Registered frameworks sorted by priority" << registeredFrameworks;
const TestTools registeredTestTools = TestFrameworkManager::registeredTestTools();
const QVariant activeFrameworks = m_project->namedSettings(SK_ACTIVE_FRAMEWORKS);
m_activeTestFrameworks.clear();
m_activeTestTools.clear();
if (activeFrameworks.isValid()) {
const QMap<QString, QVariant> frameworksMap = activeFrameworks.toMap();
for (ITestFramework *framework : registered) {
for (ITestFramework *framework : registeredFrameworks) {
const Utils::Id id = framework->id();
bool active = frameworksMap.value(id.toString(), framework->active()).toBool();
m_activeTestFrameworks.insert(framework, active);
}
for (ITestTool *testTool : registeredTestTools) {
const Utils::Id id = testTool->id();
bool active = frameworksMap.value(id.toString(), testTool->active()).toBool();
m_activeTestTools.insert(testTool, active);
}
} else {
for (ITestFramework *framework : registered)
for (ITestFramework *framework : registeredFrameworks)
m_activeTestFrameworks.insert(framework, framework->active());
for (ITestTool *testTool : registeredTestTools)
m_activeTestTools.insert(testTool, testTool->active());
}
const QVariant runAfterBuild = m_project->namedSettings(SK_RUN_AFTER_BUILD);
@@ -108,6 +125,9 @@ void TestProjectSettings::save()
auto end = m_activeTestFrameworks.cend();
for (auto it = m_activeTestFrameworks.cbegin(); it != end; ++it)
activeFrameworks.insert(it.key()->id().toString(), it.value());
auto endTools = m_activeTestTools.cend();
for (auto it = m_activeTestTools.cbegin(); it != endTools; ++it)
activeFrameworks.insert(it.key()->id().toString(), it.value());
m_project->setNamedSettings(SK_ACTIVE_FRAMEWORKS, activeFrameworks);
m_project->setNamedSettings(SK_RUN_AFTER_BUILD, int(m_runAfterBuild));
m_project->setNamedSettings(SK_CHECK_STATES, m_checkStateCache.toSettings());

View File

@@ -33,6 +33,7 @@ namespace ProjectExplorer { class Project; }
namespace Autotest {
class ITestFramework;
class ITestTool;
namespace Internal {
@@ -40,17 +41,21 @@ class TestProjectSettings : public QObject
{
Q_OBJECT
public:
TestProjectSettings(ProjectExplorer::Project *project);
explicit TestProjectSettings(ProjectExplorer::Project *project);
~TestProjectSettings();
void setUseGlobalSettings(bool useGlobal);
bool useGlobalSettings() const { return m_useGlobalSettings; }
void setRunAfterBuild(RunAfterBuildMode mode) {m_runAfterBuild = mode; }
RunAfterBuildMode runAfterBuild() const { return m_runAfterBuild; }
void setActiveFrameworks(const QHash<ITestFramework *, bool> enabledFrameworks)
void setActiveFrameworks(const QHash<ITestFramework *, bool> &enabledFrameworks)
{ m_activeTestFrameworks = enabledFrameworks; }
QHash<ITestFramework *, bool> activeFrameworks() const { return m_activeTestFrameworks; }
void activateFramework(const Utils::Id &id, bool activate);
void setActiveTestTools(const QHash<ITestTool *, bool> &enabledTestTools)
{ m_activeTestTools = enabledTestTools; }
QHash<ITestTool *, bool> activeTestTools() const { return m_activeTestTools; }
void activateTestTool(const Utils::Id &id, bool activate);
Internal::ItemDataCache<Qt::CheckState> *checkStateCache() { return &m_checkStateCache; }
private:
void load();
@@ -60,6 +65,7 @@ private:
bool m_useGlobalSettings = true;
RunAfterBuildMode m_runAfterBuild = RunAfterBuildMode::None;
QHash<ITestFramework *, bool> m_activeTestFrameworks;
QHash<ITestTool *, bool> m_activeTestTools;
Internal::ItemDataCache<Qt::CheckState> m_checkStateCache;
};

View File

@@ -353,7 +353,21 @@ void TestTreeModel::synchronizeTestFrameworks()
void TestTreeModel::synchronizeTestTools()
{
// currently no project settings...
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
TestTools tools;
if (!project || AutotestPlugin::projectSettings(project)->useGlobalSettings()) {
tools = Utils::filtered(TestFrameworkManager::registeredTestTools(),
&ITestFramework::active);
qCDebug(LOG) << "Active test tools" << tools; // FIXME tools aren't sorted
} else { // we've got custom project settings
const TestProjectSettings *settings = AutotestPlugin::projectSettings(project);
const QHash<ITestTool *, bool> active = settings->activeTestTools();
tools = Utils::filtered(TestFrameworkManager::registeredTestTools(),
[active](ITestTool *testTool) {
return active.value(testTool, false);
});
}
// pre-check to avoid further processing when test tools are unchanged
Utils::TreeItem *invisibleRoot = rootItem();
QSet<ITestTool *> newlyAdded;
@@ -367,7 +381,7 @@ void TestTreeModel::synchronizeTestTools()
for (ITestTreeItem *oldFrameworkRoot : oldFrameworkRoots)
takeItem(oldFrameworkRoot); // do NOT delete the ptr is still held by TestFrameworkManager
for (ITestTool *testTool : TestFrameworkManager::registeredTestTools()) {
for (ITestTool *testTool : tools) {
ITestTreeItem *testToolRootNode = testTool->rootNode();
if (testTool->active()) {
invisibleRoot->appendChild(testToolRootNode);
@@ -376,7 +390,6 @@ void TestTreeModel::synchronizeTestTools()
}
}
const Project *project = SessionManager::startupProject();
if (project) {
const QList<Target *> &allTargets = project->targets();
auto target = allTargets.empty() ? nullptr : allTargets.first();