forked from qt-creator/qt-creator
Squish: Implement create new test case
Change-Id: I8eeef2d024d6c8b71e2c2482f7da05b9ff221ed9 Reviewed-by: Christian Stenger <christian.stenger@qt.io> Reviewed-by: David Schulz <david.schulz@qt.io>
This commit is contained in:
@@ -193,13 +193,17 @@ void SquishFileHandler::openTestSuites()
|
||||
emit suitesOpened();
|
||||
}
|
||||
|
||||
void SquishFileHandler::openTestSuite(const Utils::FilePath &suitePath)
|
||||
void SquishFileHandler::openTestSuite(const Utils::FilePath &suitePath, bool isReopen)
|
||||
{
|
||||
const QString suiteName = suitePath.parentDir().fileName();
|
||||
const QString suitePathStr = suitePath.toString();
|
||||
const QStringList cases = SuiteConf::validTestCases(suitePath.parentDir().toString());
|
||||
|
||||
if (m_suites.contains(suiteName)) {
|
||||
if (isReopen) {
|
||||
modifySuiteItem(suiteName, suitePathStr, cases);
|
||||
return;
|
||||
}
|
||||
QMessageBox::Button replaceSuite
|
||||
= QMessageBox::question(Core::ICore::dialogParent(),
|
||||
Tr::tr("Suite Already Open"),
|
||||
|
||||
@@ -22,7 +22,7 @@ public:
|
||||
~SquishFileHandler() override = default;
|
||||
static SquishFileHandler *instance();
|
||||
void openTestSuites();
|
||||
void openTestSuite(const Utils::FilePath &suitePath);
|
||||
void openTestSuite(const Utils::FilePath &suitePath, bool isReopen = false);
|
||||
void closeTestSuite(const QString &suiteName);
|
||||
void closeAllTestSuites();
|
||||
void runTestCase(const QString &suiteName, const QString &testCaseName);
|
||||
|
||||
@@ -5,6 +5,8 @@
|
||||
|
||||
#include "squishconstants.h"
|
||||
#include "squishfilehandler.h"
|
||||
#include "squishplugin.h"
|
||||
#include "squishsettings.h"
|
||||
#include "squishtesttreemodel.h"
|
||||
#include "squishtesttreeview.h"
|
||||
#include "squishtr.h"
|
||||
@@ -50,6 +52,7 @@ SquishNavigationWidget::SquishNavigationWidget(QWidget *parent)
|
||||
header->setSectionResizeMode(2, QHeaderView::Fixed);
|
||||
m_view->setHeader(header);
|
||||
m_view->setHeaderHidden(true);
|
||||
m_view->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->setContentsMargins(0, 0, 0, 0);
|
||||
@@ -132,6 +135,10 @@ void SquishNavigationWidget::contextMenuEvent(QContextMenuEvent *event)
|
||||
connect(runThisTestSuite, &QAction::triggered, [suiteName]() {
|
||||
SquishFileHandler::instance()->runTestSuite(suiteName);
|
||||
});
|
||||
connect(addNewTestCase, &QAction::triggered, [this, idx]() {
|
||||
onNewTestCaseTriggered(idx);
|
||||
});
|
||||
|
||||
connect(closeTestSuite, &QAction::triggered, [suiteName]() {
|
||||
SquishFileHandler::instance()->closeTestSuite(suiteName);
|
||||
});
|
||||
@@ -320,6 +327,33 @@ void SquishNavigationWidget::onRecordTestCase(const QString &suiteName, const QS
|
||||
SquishFileHandler::instance()->recordTestCase(suiteName, testCase);
|
||||
}
|
||||
|
||||
void SquishNavigationWidget::onNewTestCaseTriggered(const QModelIndex &index)
|
||||
{
|
||||
auto settings = SquishPlugin::squishSettings();
|
||||
QTC_ASSERT(settings, return);
|
||||
|
||||
if (!settings->squishPath.filePath().pathAppended("scriptmodules").exists()) {
|
||||
QMessageBox::critical(Core::ICore::dialogParent(),
|
||||
Tr::tr("Error"),
|
||||
Tr::tr("Set up a valid Squish path to be able to create "
|
||||
"a new test case.\n(Edit > Preferences > Squish)"));
|
||||
return;
|
||||
}
|
||||
|
||||
SquishTestTreeItem *suiteItem = m_model->itemForIndex(m_sortModel->mapToSource(index));
|
||||
QTC_ASSERT(suiteItem, return);
|
||||
|
||||
const QString name = suiteItem->generateTestCaseName();
|
||||
SquishTestTreeItem *item = new SquishTestTreeItem(name, SquishTestTreeItem::SquishTestCase);
|
||||
item->setParentName(suiteItem->displayName());
|
||||
|
||||
m_model->addTreeItem(item);
|
||||
m_view->expand(index);
|
||||
QModelIndex added = m_model->indexForItem(item);
|
||||
QTC_ASSERT(added.isValid(), return);
|
||||
m_view->edit(m_sortModel->mapFromSource(added));
|
||||
}
|
||||
|
||||
SquishNavigationWidgetFactory::SquishNavigationWidgetFactory()
|
||||
{
|
||||
setDisplayName(Tr::tr("Squish"));
|
||||
|
||||
@@ -36,6 +36,8 @@ private:
|
||||
void onRemoveSharedFolderTriggered(int row, const QModelIndex &parent);
|
||||
void onRemoveAllSharedFolderTriggered();
|
||||
void onRecordTestCase(const QString &suiteName, const QString &testCase);
|
||||
void onNewTestCaseTriggered(const QModelIndex &index);
|
||||
|
||||
SquishTestTreeView *m_view;
|
||||
SquishTestTreeModel *m_model; // not owned
|
||||
SquishTestTreeSortModel *m_sortModel;
|
||||
|
||||
@@ -5,8 +5,10 @@
|
||||
|
||||
#include "squishfilehandler.h"
|
||||
#include "squishtr.h"
|
||||
#include "suiteconf.h"
|
||||
|
||||
#include <debugger/debuggericons.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/utilsicons.h>
|
||||
|
||||
@@ -23,23 +25,23 @@ SquishTestTreeItem::SquishTestTreeItem(const QString &displayName, Type type)
|
||||
, m_type(type)
|
||||
, m_checked(Qt::Checked)
|
||||
{
|
||||
const Qt::ItemFlags common = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
switch (type) {
|
||||
case Root:
|
||||
m_flags = Qt::NoItemFlags;
|
||||
break;
|
||||
case SquishSuite:
|
||||
m_flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserTristate
|
||||
| Qt::ItemIsUserCheckable;
|
||||
m_flags = common | Qt::ItemIsUserTristate | Qt::ItemIsUserCheckable;
|
||||
break;
|
||||
case SquishTestCase:
|
||||
m_flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
|
||||
m_flags = common | Qt::ItemIsEditable | Qt::ItemIsUserCheckable;
|
||||
break;
|
||||
case SquishSharedData:
|
||||
case SquishSharedDataFolder:
|
||||
case SquishSharedFile:
|
||||
case SquishSharedFolder:
|
||||
case SquishSharedRoot:
|
||||
m_flags = Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
m_flags = common;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -108,9 +110,41 @@ bool SquishTestTreeItem::modifyContent(const SquishTestTreeItem &other)
|
||||
m_displayName = other.m_displayName;
|
||||
m_filePath = other.m_filePath;
|
||||
m_parentName = other.m_parentName;
|
||||
|
||||
removeChildren();
|
||||
if (other.hasChildren()) {
|
||||
for (int i = 0; i < other.childCount(); ++i) {
|
||||
const auto modifiedChild = static_cast<SquishTestTreeItem *>(other.childAt(i));
|
||||
auto child = new SquishTestTreeItem(modifiedChild->displayName(),
|
||||
modifiedChild->type());
|
||||
child->modifyContent(*modifiedChild);
|
||||
appendChild(child);
|
||||
}
|
||||
}
|
||||
return modified;
|
||||
}
|
||||
|
||||
QString SquishTestTreeItem::generateTestCaseName() const
|
||||
{
|
||||
QTC_ASSERT(m_type == SquishSuite, return {});
|
||||
|
||||
const auto suiteConfFilePath = Utils::FilePath::fromString(m_filePath);
|
||||
const SuiteConf suiteConf = SuiteConf::readSuiteConf(suiteConfFilePath);
|
||||
const QStringList used = suiteConf.usedTestCases();
|
||||
const auto suiteDir = suiteConfFilePath.parentDir();
|
||||
|
||||
const QString tmpl("tst_case");
|
||||
for (int i = 1; i < 9999; ++i) {
|
||||
const QString current = tmpl + QString::number(i);
|
||||
if (used.contains(current))
|
||||
continue;
|
||||
const Utils::FilePath testCaseFolder = suiteDir.pathAppended(current);
|
||||
if (!testCaseFolder.exists())
|
||||
return current;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
void SquishTestTreeItem::revalidateCheckState()
|
||||
{
|
||||
if (childCount() == 0)
|
||||
@@ -363,7 +397,7 @@ void SquishTestTreeModel::modifyTreeItem(int row,
|
||||
|
||||
QModelIndex childIndex = index(row, 0, parent);
|
||||
|
||||
SquishTestTreeItem *toBeModified = static_cast<SquishTestTreeItem *>(itemForIndex(childIndex));
|
||||
SquishTestTreeItem *toBeModified = itemForIndex(childIndex);
|
||||
|
||||
if (toBeModified->modifyContent(modified))
|
||||
emit dataChanged(childIndex, childIndex);
|
||||
|
||||
@@ -44,7 +44,9 @@ public:
|
||||
Qt::CheckState checkState() const { return m_checked; }
|
||||
|
||||
bool modifyContent(const SquishTestTreeItem &other);
|
||||
QString generateTestCaseName() const;
|
||||
|
||||
void reloadSuite();
|
||||
private:
|
||||
void revalidateCheckState();
|
||||
|
||||
|
||||
@@ -2,12 +2,24 @@
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0+ OR GPL-3.0 WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include "squishtesttreeview.h"
|
||||
#include "squishconstants.h"
|
||||
#include "squishtesttreemodel.h"
|
||||
|
||||
#include "squishconstants.h"
|
||||
#include "squishfilehandler.h"
|
||||
#include "squishplugin.h"
|
||||
#include "squishsettings.h"
|
||||
#include "squishtesttreemodel.h"
|
||||
#include "suiteconf.h"
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/icontext.h>
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/fancylineedit.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
|
||||
namespace Squish {
|
||||
namespace Internal {
|
||||
|
||||
@@ -106,5 +118,140 @@ QSize SquishTestTreeItemDelegate::sizeHint(const QStyleOptionViewItem &option,
|
||||
return QStyledItemDelegate::sizeHint(opt, idx);
|
||||
}
|
||||
|
||||
QWidget *SquishTestTreeItemDelegate::createEditor(QWidget *parent,
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
Q_UNUSED(option)
|
||||
QTC_ASSERT(parent, return nullptr);
|
||||
QTC_ASSERT(index.isValid(), return nullptr);
|
||||
auto model = static_cast<const SquishTestTreeSortModel *>(index.model());
|
||||
QTC_ASSERT(model, return nullptr);
|
||||
auto srcModel = static_cast<SquishTestTreeModel *>(model->sourceModel());
|
||||
|
||||
const SquishTestTreeItem *suite = srcModel->itemForIndex(model->mapToSource(index.parent()));
|
||||
SquishTestTreeItem *testCaseItem = srcModel->itemForIndex(model->mapToSource(index));
|
||||
const SuiteConf suiteConf = SuiteConf::readSuiteConf(Utils::FilePath::fromString(suite->filePath()));
|
||||
const QStringList inUse = suiteConf.usedTestCases();
|
||||
Utils::FancyLineEdit *editor = new Utils::FancyLineEdit(parent);
|
||||
editor->setValidationFunction([inUse](Utils::FancyLineEdit *edit, QString *) {
|
||||
static const QRegularExpression validFileName("^[-a-zA-Z0-9_$. ]+$");
|
||||
QString testName = edit->text();
|
||||
if (!testName.startsWith("tst_"))
|
||||
testName.prepend("tst_");
|
||||
return validFileName.match(testName).hasMatch() && !inUse.contains(testName);
|
||||
});
|
||||
|
||||
connect(this, &QStyledItemDelegate::closeEditor,
|
||||
editor, [srcModel, testCaseItem](QWidget *, EndEditHint hint) {
|
||||
QTC_ASSERT(srcModel, return);
|
||||
QTC_ASSERT(testCaseItem, return);
|
||||
if (hint != QAbstractItemDelegate::RevertModelCache)
|
||||
return;
|
||||
srcModel->destroyItem(testCaseItem);
|
||||
});
|
||||
return editor;
|
||||
}
|
||||
|
||||
void SquishTestTreeItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
|
||||
{
|
||||
QTC_ASSERT(editor, return);
|
||||
QTC_ASSERT(index.isValid(), return);
|
||||
|
||||
static_cast<Utils::FancyLineEdit *>(editor)->setText(index.data().toString());
|
||||
}
|
||||
|
||||
static Utils::FilePath scriptsPath(const Utils::FilePath &squishPath, Language language)
|
||||
{
|
||||
Utils::FilePath scripts = squishPath.pathAppended("scriptmodules");
|
||||
switch (language) {
|
||||
case Language::Python: scripts = scripts.pathAppended("python"); break;
|
||||
case Language::Perl: scripts = scripts.pathAppended("perl"); break;
|
||||
case Language::JavaScript: scripts = scripts.pathAppended("javascript"); break;
|
||||
case Language::Ruby: scripts = scripts.pathAppended("ruby"); break;
|
||||
case Language::Tcl: scripts = scripts.pathAppended("tcl"); break;
|
||||
}
|
||||
|
||||
return scripts;
|
||||
}
|
||||
|
||||
static bool copyScriptTemplates(const SuiteConf &suiteConf,
|
||||
const Utils::FilePath &destination)
|
||||
{
|
||||
const SquishSettings *s = SquishPlugin::squishSettings();
|
||||
QTC_ASSERT(s, return false);
|
||||
// copy template files
|
||||
Utils::FilePath squishPath = s->squishPath.filePath();
|
||||
Utils::FilePath scripts = scriptsPath(squishPath, suiteConf.language());
|
||||
|
||||
bool ok = destination.ensureWritableDir();
|
||||
QTC_ASSERT(ok, return false);
|
||||
|
||||
const bool scripted = suiteConf.objectMapStyle() == "script";
|
||||
const QString extension = suiteConf.scriptExtension();
|
||||
const QString testStr = scripted ? QString("script_som_template") : QString("script_template");
|
||||
|
||||
const Utils::FilePath test = scripts.pathAppended(testStr + extension);
|
||||
const Utils::FilePath testFile = destination.pathAppended("test" + extension);
|
||||
ok = test.copyFile(testFile);
|
||||
QTC_ASSERT(ok, return false);
|
||||
|
||||
if (scripted) {
|
||||
const Utils::FilePath destinationObjectMap = destination.parentDir()
|
||||
.pathAppended("shared/scripts/names" + extension);
|
||||
if (destinationObjectMap.exists())
|
||||
return true;
|
||||
|
||||
const Utils::FilePath objectMap = scripts.pathAppended("objectmap_template" + extension);
|
||||
ok = destinationObjectMap.parentDir().ensureWritableDir();
|
||||
QTC_ASSERT(ok, return false);
|
||||
ok = objectMap.copyFile(destinationObjectMap);
|
||||
QTC_ASSERT(ok, return false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SquishTestTreeItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||
const QModelIndex &index) const
|
||||
{
|
||||
QTC_ASSERT(editor, return);
|
||||
QTC_ASSERT(model, return);
|
||||
QTC_ASSERT(index.isValid(), return);
|
||||
|
||||
auto sortModel = static_cast<SquishTestTreeSortModel *>(model);
|
||||
auto sourceModel = static_cast<SquishTestTreeModel *>(sortModel->sourceModel());
|
||||
auto lineEdit = static_cast<Utils::FancyLineEdit *>(editor);
|
||||
auto removeFormerlyAdded = [sortModel, sourceModel, &index](){
|
||||
auto item = sourceModel->itemForIndex(sortModel->mapToSource(index));
|
||||
QTC_ASSERT(item, return);
|
||||
sourceModel->destroyItem(item);
|
||||
};
|
||||
|
||||
if (!lineEdit->isValid()) {
|
||||
// remove the formerly added again
|
||||
removeFormerlyAdded();
|
||||
return;
|
||||
}
|
||||
|
||||
QString chosenName = lineEdit->text();
|
||||
if (!chosenName.startsWith("tst_"))
|
||||
chosenName.prepend("tst_");
|
||||
|
||||
const QModelIndex parent = index.parent();
|
||||
SquishTestTreeItem *suiteItem = sourceModel->itemForIndex(sortModel->mapToSource(parent));
|
||||
const auto suiteConfPath = Utils::FilePath::fromString(suiteItem->filePath());
|
||||
SuiteConf suiteConf = SuiteConf::readSuiteConf(suiteConfPath);
|
||||
const Utils::FilePath destination = suiteConfPath.parentDir().pathAppended(chosenName);
|
||||
bool ok = copyScriptTemplates(suiteConf, destination);
|
||||
QTC_ASSERT(ok, removeFormerlyAdded(); return);
|
||||
|
||||
suiteConf.addTestCase(chosenName);
|
||||
ok = suiteConf.write();
|
||||
QTC_ASSERT(ok, removeFormerlyAdded(); return);
|
||||
SquishFileHandler::instance()->openTestSuite(suiteConfPath, true);
|
||||
|
||||
Core::EditorManager::openEditor(destination.pathAppended("test" + suiteConf.scriptExtension()));
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Squish
|
||||
|
||||
@@ -43,6 +43,12 @@ public:
|
||||
const QStyleOptionViewItem &option,
|
||||
const QModelIndex &idx) const override;
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override;
|
||||
|
||||
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
|
||||
const QModelIndex &index) const override;
|
||||
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
|
||||
void setModelData(QWidget *editor, QAbstractItemModel *model,
|
||||
const QModelIndex &index) const override;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
|
||||
@@ -3,6 +3,9 @@
|
||||
|
||||
#include "suiteconf.h"
|
||||
|
||||
#include <coreplugin/documentmanager.h>
|
||||
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QRegularExpression>
|
||||
@@ -33,6 +36,32 @@ bool SuiteConf::read()
|
||||
return true;
|
||||
}
|
||||
|
||||
static QString languageEntry(Language language)
|
||||
{
|
||||
switch (language) {
|
||||
case Language::Python: return "Python";
|
||||
case Language::Perl: return "Perl";
|
||||
case Language::JavaScript: return "JavaScript";
|
||||
case Language::Ruby: return "Ruby";
|
||||
case Language::Tcl: return "Tcl";
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
bool SuiteConf::write()
|
||||
{
|
||||
Core::DocumentManager::expectFileChange(m_filePath);
|
||||
QSettings suiteConf(m_filePath.toString(), QSettings::IniFormat);
|
||||
suiteConf.setValue(squishAutKey, m_aut);
|
||||
suiteConf.setValue(squishLanguageKey, languageEntry(m_language));
|
||||
suiteConf.setValue(objectsMapKey, m_objectMap);
|
||||
if (!m_objectMap.isEmpty())
|
||||
suiteConf.setValue(objectMapStyleKey, m_objectMapStyle);
|
||||
suiteConf.setValue(squishTestCasesKey, m_testcases);
|
||||
suiteConf.sync();
|
||||
return suiteConf.status() == QSettings::NoError;
|
||||
}
|
||||
|
||||
QString SuiteConf::langParameter() const
|
||||
{
|
||||
switch (m_language) {
|
||||
@@ -64,6 +93,37 @@ QStringList SuiteConf::testCases() const
|
||||
return m_testcases.split(QRegularExpression("\\s+"));
|
||||
}
|
||||
|
||||
QStringList SuiteConf::usedTestCases() const
|
||||
{
|
||||
QStringList result = testCases();
|
||||
|
||||
auto suiteDir = m_filePath.parentDir();
|
||||
const Utils::FilePaths entries = Utils::filtered(
|
||||
suiteDir.dirEntries(QDir::Dirs | QDir::NoDotAndDotDot),
|
||||
[](const Utils::FilePath &fp) {
|
||||
return fp.fileName().startsWith("tst_");
|
||||
});
|
||||
const QStringList testCaseNames = Utils::transform(entries, &Utils::FilePath::fileName);
|
||||
for (const QString &testCaseName : testCaseNames) {
|
||||
if (result.contains(testCaseName))
|
||||
continue;
|
||||
result.append(testCaseName); // should this check for test.*?
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void SuiteConf::addTestCase(const QString &name)
|
||||
{
|
||||
QStringList current = testCases();
|
||||
int insertAt = 0;
|
||||
for (int count = current.count(); insertAt < count; ++insertAt) {
|
||||
if (current.at(insertAt) > name)
|
||||
break;
|
||||
}
|
||||
current.insert(insertAt, name);
|
||||
m_testcases = current.join(' ');
|
||||
}
|
||||
|
||||
void SuiteConf::setLanguage(const QString &language)
|
||||
{
|
||||
if (language == "Python")
|
||||
|
||||
@@ -21,6 +21,7 @@ public:
|
||||
static QStringList validTestCases(const QString &baseDirectory);
|
||||
|
||||
bool read();
|
||||
bool write();
|
||||
|
||||
QString aut() const { return m_aut; }
|
||||
void setAut(const QString &aut) { m_aut = aut; }
|
||||
@@ -32,7 +33,9 @@ public:
|
||||
QString objectMapStyle() const { return m_objectMapStyle; }
|
||||
QString scriptExtension() const;
|
||||
QStringList testCases() const;
|
||||
void addTestCase(const QString &testCase);
|
||||
|
||||
QStringList usedTestCases() const;
|
||||
private:
|
||||
void setLanguage(const QString &language);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user