Merge "Add autotest plugin"
3
share/qtcreator/templates/wizards/autotest/auto.pro
Normal file
@@ -0,0 +1,3 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += %TestCaseName:l%
|
BIN
share/qtcreator/templates/wizards/autotest/autotest_24.png
Normal file
After Width: | Height: | Size: 683 B |
18
share/qtcreator/templates/wizards/autotest/main.cpp
Normal file
@@ -0,0 +1,18 @@
|
||||
@if "%RequireGUI%" == "true"
|
||||
#include <QApplication>
|
||||
@else
|
||||
#include <QCoreApplication>
|
||||
@endif
|
||||
|
||||
// add necessary includes here
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
@if "%RequireGUI%" == "true"
|
||||
QApplication a(argc, argv);
|
||||
@else
|
||||
QCoreApplication a(argc, argv);
|
||||
@endif
|
||||
|
||||
return a.exec();
|
||||
}
|
13
share/qtcreator/templates/wizards/autotest/src.pro
Normal file
@@ -0,0 +1,13 @@
|
||||
@if "%RequireGUI%" == "true"
|
||||
QT += core gui
|
||||
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
|
||||
@else
|
||||
QT -= gui
|
||||
CONFIG += console
|
||||
CONFIG -= app_bundle
|
||||
@endif
|
||||
|
||||
TEMPLATE = app
|
||||
TARGET = %ProjectName%
|
||||
|
||||
SOURCES += main.%CppSourceSuffix%
|
3
share/qtcreator/templates/wizards/autotest/tests.pro
Normal file
@@ -0,0 +1,3 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += auto
|
12
share/qtcreator/templates/wizards/autotest/tmp.pro
Normal file
@@ -0,0 +1,12 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
@if "%BuildTests%" == "always"
|
||||
SUBDIRS += src \
|
||||
tests
|
||||
@else
|
||||
SUBDIRS += src
|
||||
|
||||
CONFIG(debug, debug|release) {
|
||||
SUBDIRS += tests
|
||||
}
|
||||
@endif
|
14
share/qtcreator/templates/wizards/autotest/tst.pro
Normal file
@@ -0,0 +1,14 @@
|
||||
QT += testlib
|
||||
@if "%RequireGUI%" == "false"
|
||||
QT -= gui
|
||||
|
||||
CONFIG += qt console warn_on depend_includepath testcase
|
||||
CONFIG -= app_bundle
|
||||
@else
|
||||
QT += gui
|
||||
CONFIG += qt warn_on depend_includepath testcase
|
||||
@endif
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += tst_%TestCaseName:l%.%CppSourceSuffix%
|
58
share/qtcreator/templates/wizards/autotest/tst_src.cpp
Normal file
@@ -0,0 +1,58 @@
|
||||
#include <QtTest>
|
||||
@if "%RequireApplication%" == "true"
|
||||
#include <QCoreApplication>
|
||||
@endif
|
||||
|
||||
// add necessary includes here
|
||||
|
||||
class %TestCaseName% : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
%TestCaseName%();
|
||||
~%TestCaseName%();
|
||||
|
||||
private slots:
|
||||
@if "%GenerateInitAndCleanup%" == "true"
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
@endif
|
||||
void test_case1();
|
||||
|
||||
};
|
||||
|
||||
%TestCaseName%::%TestCaseName%()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
%TestCaseName%::~%TestCaseName%()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@if "%GenerateInitAndCleanup%" == "true"
|
||||
void %TestCaseName%::initTestCase()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void %TestCaseName%::cleanupTestCase()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@endif
|
||||
void %TestCaseName%::test_case1()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@if "%RequireApplication%" == "true"
|
||||
QTEST_MAIN(%TestCaseName%)
|
||||
@else
|
||||
QTEST_APPLESS_MAIN(%TestCaseName%)
|
||||
@endif
|
||||
|
||||
#include "tst_%TestCaseName:l%.moc"
|
83
share/qtcreator/templates/wizards/autotest/wizard.xml
Normal file
@@ -0,0 +1,83 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!--
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
-->
|
||||
<wizard version="1" kind="project"
|
||||
class="qmakeproject" firstpage="10"
|
||||
id="R.AutoTest" category="H.Project"
|
||||
featuresRequired="QtSupport.Wizards.FeatureQt,QtSupport.Wizards.FeatureDesktop">
|
||||
<!-- "Plugin.AutotestGenerator" as required feature would disable the template if the
|
||||
plugin is disabled, but it fails on the kit selection page for having no valid kit -->
|
||||
<icon>autotest_24.png</icon>
|
||||
<description>Creates a new project including auto test skeleton.</description>
|
||||
<displayname>Auto Test</displayname>;
|
||||
<displaycategory>Other Project</displaycategory>
|
||||
<files>
|
||||
<file source="src.pro" target="src/src.pro" />
|
||||
<file source="tests.pro" target="tests/tests.pro" />
|
||||
<file source="auto.pro" target="tests/auto/auto.pro"/>
|
||||
<file source="tst.pro" target="tests/auto/%TestCaseName:l%/%TestCaseName:l%.pro" />
|
||||
<file source="tst_src.cpp"
|
||||
target="tests/auto/%TestCaseName:l%/tst_%TestCaseName:l%.%CppSourceSuffix%"
|
||||
openeditor="true"/>
|
||||
<file source="main.cpp" target="src/main.%CppSourceSuffix%" openeditor="true" />
|
||||
<file source="tmp.pro" target="%ProjectName:l%.pro" openproject="true" />
|
||||
</files>
|
||||
<!-- Create a 2nd wizard page with parameters -->
|
||||
<fieldpagetitle>Project and Test Information</fieldpagetitle>
|
||||
<fields>
|
||||
<field name="RequireGUI">
|
||||
<fieldcontrol class="QCheckBox" />
|
||||
<fielddescription>GUI Application</fielddescription>
|
||||
</field>
|
||||
<field mandatory="true" name="TestCaseName">
|
||||
<fieldcontrol class="QLineEdit" validator="^[a-zA-Z_0-9]+$"
|
||||
/><!-- defaulttext="" /> -->
|
||||
<fielddescription>Test Case Name:</fielddescription>
|
||||
</field>
|
||||
<field name="RequireApplication">
|
||||
<fieldcontrol class="QCheckBox" />
|
||||
<fielddescription>Requires QApplication</fielddescription>
|
||||
</field>
|
||||
<field name="GenerateInitAndCleanup">
|
||||
<fieldcontrol class="QCheckBox" />
|
||||
<fielddescription>Generate initialization and cleanup code</fielddescription>
|
||||
</field>
|
||||
<field name="BuildAutoTests">
|
||||
<fieldcontrol class="QComboBox" defaultindex="0" >
|
||||
<comboentries>
|
||||
<comboentry value="always" >
|
||||
<comboentrytext>always</comboentrytext>
|
||||
</comboentry>
|
||||
<comboentry value="debug" >
|
||||
<comboentrytext>debug only</comboentrytext>
|
||||
</comboentry>
|
||||
</comboentries>
|
||||
</fieldcontrol>
|
||||
<fielddescription>Build auto tests</fielddescription>
|
||||
</field>
|
||||
</fields>
|
||||
</wizard>
|
19
src/plugins/autotest/AutoTest.json.in
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
\"Name\" : \"AutoTest\",
|
||||
\"Version\" : \"$$QTCREATOR_VERSION\",
|
||||
\"CompatVersion\" : \"$$QTCREATOR_COMPAT_VERSION\",
|
||||
\"Experimental\" : true,
|
||||
\"Vendor\" : \"The Qt Company Ltd\",
|
||||
\"Copyright\" : \"(C) 2015 The Qt Company Ltd\",
|
||||
\"License\" : [ \"Commercial Usage\",
|
||||
\"\",
|
||||
\"Licensees holding valid Qt Commercial licenses may use this plugin in accordance with the Qt Commercial License Agreement provided with the Software or, alternatively, in accordance with the terms contained in a written agreement between you and The Qt Company.\",
|
||||
\"\",
|
||||
\"GNU General Public License Usage\",
|
||||
\"\",
|
||||
\"Alternatively, this file may be used under the terms of the GNU General Public License version 3 as published by the Free Software Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT included in the packaging of this file. Please review the following information to ensure the GNU General Public License requirements will be met: https://www.gnu.org/licenses/gpl-3.0.html.\"
|
||||
],
|
||||
\"Description\" : \"Auto Test plugin.\",
|
||||
\"Url\" : \"http://www.qt.io\",
|
||||
$$dependencyList
|
||||
}
|
63
src/plugins/autotest/autotest.pro
Normal file
@@ -0,0 +1,63 @@
|
||||
TARGET = AutoTest
|
||||
TEMPLATE = lib
|
||||
|
||||
include(../../qtcreatorplugin.pri)
|
||||
include(autotest_dependencies.pri)
|
||||
|
||||
DEFINES += AUTOTEST_LIBRARY
|
||||
|
||||
SOURCES += \
|
||||
testtreeview.cpp \
|
||||
testtreemodel.cpp \
|
||||
testtreeitem.cpp \
|
||||
testvisitor.cpp \
|
||||
testinfo.cpp \
|
||||
testcodeparser.cpp \
|
||||
autotestplugin.cpp \
|
||||
testrunner.cpp \
|
||||
testconfiguration.cpp \
|
||||
testresult.cpp \
|
||||
testresultspane.cpp \
|
||||
testresultmodel.cpp \
|
||||
testresultdelegate.cpp \
|
||||
testtreeitemdelegate.cpp \
|
||||
testsettings.cpp \
|
||||
testsettingspage.cpp \
|
||||
testnavigationwidget.cpp \
|
||||
testoutputreader.cpp
|
||||
|
||||
HEADERS += \
|
||||
testtreeview.h \
|
||||
testtreemodel.h \
|
||||
testtreeitem.h \
|
||||
testvisitor.h \
|
||||
testinfo.h \
|
||||
testcodeparser.h \
|
||||
autotestplugin.h \
|
||||
autotest_global.h \
|
||||
autotest_utils.h \
|
||||
autotestconstants.h \
|
||||
testrunner.h \
|
||||
testconfiguration.h \
|
||||
testresult.h \
|
||||
testresultspane.h \
|
||||
testresultmodel.h \
|
||||
testresultdelegate.h \
|
||||
testtreeitemdelegate.h \
|
||||
testsettings.h \
|
||||
testsettingspage.h \
|
||||
testnavigationwidget.h \
|
||||
testoutputreader.h \
|
||||
autotesticons.h
|
||||
|
||||
RESOURCES += \
|
||||
autotest.qrc
|
||||
|
||||
FORMS += \
|
||||
testsettingspage.ui
|
||||
|
||||
equals(TEST, 1) {
|
||||
HEADERS += autotestunittests.h
|
||||
SOURCES += autotestunittests.cpp
|
||||
RESOURCES += autotestunittests.qrc
|
||||
}
|
96
src/plugins/autotest/autotest.qbs
Normal file
@@ -0,0 +1,96 @@
|
||||
import qbs
|
||||
|
||||
QtcCommercialPlugin {
|
||||
name: "AutoTest"
|
||||
|
||||
Depends { name: "Core" }
|
||||
Depends { name: "CppTools" }
|
||||
Depends { name: "CPlusPlus" }
|
||||
Depends { name: "ProjectExplorer" }
|
||||
Depends { name: "QmlJS" }
|
||||
Depends { name: "QmlJSTools" }
|
||||
Depends { name: "Utils" }
|
||||
|
||||
pluginTestDepends: [
|
||||
"QbsProjectManager",
|
||||
"QmakeProjectManager"
|
||||
]
|
||||
|
||||
Depends {
|
||||
name: "QtSupport"
|
||||
condition: project.testsEnabled
|
||||
}
|
||||
|
||||
Depends {
|
||||
name: "Qt.test"
|
||||
condition: project.testsEnabled
|
||||
}
|
||||
|
||||
Depends { name: "Qt.widgets" }
|
||||
|
||||
files: [
|
||||
"autotest.qrc",
|
||||
"autotesticons.h",
|
||||
"autotest_global.h",
|
||||
"autotest_utils.h",
|
||||
"autotestconstants.h",
|
||||
"autotestplugin.cpp",
|
||||
"autotestplugin.h",
|
||||
"testcodeparser.cpp",
|
||||
"testcodeparser.h",
|
||||
"testconfiguration.cpp",
|
||||
"testconfiguration.h",
|
||||
"testinfo.cpp",
|
||||
"testinfo.h",
|
||||
"testnavigationwidget.cpp",
|
||||
"testnavigationwidget.h",
|
||||
"testresult.cpp",
|
||||
"testresult.h",
|
||||
"testresultdelegate.cpp",
|
||||
"testresultdelegate.h",
|
||||
"testresultmodel.cpp",
|
||||
"testresultmodel.h",
|
||||
"testresultspane.cpp",
|
||||
"testresultspane.h",
|
||||
"testrunner.cpp",
|
||||
"testrunner.h",
|
||||
"testsettings.cpp",
|
||||
"testsettings.h",
|
||||
"testsettingspage.cpp",
|
||||
"testsettingspage.h",
|
||||
"testsettingspage.ui",
|
||||
"testtreeitem.cpp",
|
||||
"testtreeitem.h",
|
||||
"testtreeitemdelegate.cpp",
|
||||
"testtreeitemdelegate.h",
|
||||
"testtreemodel.cpp",
|
||||
"testtreemodel.h",
|
||||
"testtreeview.cpp",
|
||||
"testtreeview.h",
|
||||
"testvisitor.cpp",
|
||||
"testvisitor.h",
|
||||
"testoutputreader.cpp",
|
||||
"testoutputreader.h",
|
||||
]
|
||||
|
||||
Group {
|
||||
name: "Test sources"
|
||||
condition: project.testsEnabled
|
||||
files: [
|
||||
"autotestunittests.cpp",
|
||||
"autotestunittests.h",
|
||||
"autotestunittests.qrc",
|
||||
]
|
||||
}
|
||||
|
||||
Group {
|
||||
name: "Auto Test Wizard"
|
||||
prefix: "../../shared/autotest/"
|
||||
files: [
|
||||
"*"
|
||||
]
|
||||
fileTags: []
|
||||
qbs.install: true
|
||||
qbs.installDir: project.ide_data_path + "/templates/wizards/autotest"
|
||||
}
|
||||
}
|
31
src/plugins/autotest/autotest.qrc
Normal file
@@ -0,0 +1,31 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>images/autotest.png</file>
|
||||
<file>images/class.png</file>
|
||||
<file>images/func.png</file>
|
||||
<file>images/expand.png</file>
|
||||
<file>images/expand@2x.png</file>
|
||||
<file>images/collapse.png</file>
|
||||
<file>images/collapse@2x.png</file>
|
||||
<file>images/sort.png</file>
|
||||
<file>images/sort@2x.png</file>
|
||||
<file>images/leafsort.png</file>
|
||||
<file>images/leafsort@2x.png</file>
|
||||
<file>images/debug.png</file>
|
||||
<file>images/fail.png</file>
|
||||
<file>images/fatal.png</file>
|
||||
<file>images/pass.png</file>
|
||||
<file>images/skip.png</file>
|
||||
<file>images/warn.png</file>
|
||||
<file>images/xfail.png</file>
|
||||
<file>images/xpass.png</file>
|
||||
<file>images/blacklisted_fail.png</file>
|
||||
<file>images/blacklisted_pass.png</file>
|
||||
<file>images/benchmark.png</file>
|
||||
<file>images/runselected_boxes.png</file>
|
||||
<file>images/runselected_boxes@2x.png</file>
|
||||
<file>images/runselected_tickmarks.png</file>
|
||||
<file>images/runselected_tickmarks@2x.png</file>
|
||||
<file>images/data.png</file>
|
||||
</qresource>
|
||||
</RCC>
|
19
src/plugins/autotest/autotest_dependencies.pri
Normal file
@@ -0,0 +1,19 @@
|
||||
QTC_PLUGIN_NAME = AutoTest
|
||||
|
||||
QTC_PLUGIN_DEPENDS += \
|
||||
coreplugin \
|
||||
projectexplorer \
|
||||
cpptools \
|
||||
qmljstools
|
||||
|
||||
QTC_LIB_DEPENDS += \
|
||||
cplusplus \
|
||||
qmljs \
|
||||
utils
|
||||
|
||||
QTC_TEST_DEPENDS += \
|
||||
qbsprojectmanager \
|
||||
qmakeprojectmanager \
|
||||
qtsupport
|
||||
|
||||
#QTC_PLUGIN_RECOMMENDS += \
|
38
src/plugins/autotest/autotest_global.h
Normal file
@@ -0,0 +1,38 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AUTOTEST_GLOBAL_H
|
||||
#define AUTOTEST_GLOBAL_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
#if defined(AUTOTEST_LIBRARY)
|
||||
# define AUTOTESTSHARED_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
# define AUTOTESTSHARED_EXPORT Q_DECL_IMPORT
|
||||
#endif
|
||||
|
||||
#endif // AUTOTEST_GLOBAL_H
|
||||
|
66
src/plugins/autotest/autotest_utils.h
Normal file
@@ -0,0 +1,66 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AUTOTEST_UTILS_H
|
||||
#define AUTOTEST_UTILS_H
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestUtils
|
||||
{
|
||||
public:
|
||||
static bool isGTestMacro(const QString ¯o)
|
||||
{
|
||||
static QStringList valid = {
|
||||
QStringLiteral("TEST"), QStringLiteral("TEST_F"), QStringLiteral("TEST_P")
|
||||
};
|
||||
return valid.contains(macro);
|
||||
}
|
||||
|
||||
static bool isGTestParameterized(const QString ¯o)
|
||||
{
|
||||
return macro == QStringLiteral("TEST_P");
|
||||
}
|
||||
|
||||
static bool isQTestMacro(const QByteArray ¯o)
|
||||
{
|
||||
static QByteArrayList valid = {"QTEST_MAIN", "QTEST_APPLESS_MAIN", "QTEST_GUILESS_MAIN"};
|
||||
return valid.contains(macro);
|
||||
}
|
||||
|
||||
static bool isQuickTestMacro(const QByteArray ¯o)
|
||||
{
|
||||
static const QByteArrayList valid = {"QUICK_TEST_MAIN", "QUICK_TEST_OPENGL_MAIN"};
|
||||
return valid.contains(macro);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // AUTOTEST_UTILS_H
|
59
src/plugins/autotest/autotestconstants.h
Normal file
@@ -0,0 +1,59 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AUTOTESTCONSTANTS_H
|
||||
#define AUTOTESTCONSTANTS_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Constants {
|
||||
|
||||
const char ACTION_SCAN_ID[] = "AutoTest.ScanAction";
|
||||
const char ACTION_RUN_ALL_ID[] = "AutoTest.RunAll";
|
||||
const char ACTION_RUN_SELECTED_ID[] = "AutoTest.RunSelected";
|
||||
const char MENU_ID[] = "AutoTest.Menu";
|
||||
const char AUTOTEST_ID[] = "AutoTest.ATP";
|
||||
const char AUTOTEST_CONTEXT[] = "Auto Tests";
|
||||
const char TASK_INDEX[] = "AutoTest.Task.Index";
|
||||
const char TASK_PARSE[] = "AutoTest.Task.Parse";
|
||||
const char UNNAMED_QUICKTESTS[] = QT_TR_NOOP("<unnamed>");
|
||||
const char AUTOTEST_SETTINGS_CATEGORY[] = "ZY.Tests";
|
||||
|
||||
} // namespace Constants
|
||||
|
||||
namespace Internal {
|
||||
|
||||
enum TestType
|
||||
{
|
||||
TestTypeQt,
|
||||
TestTypeGTest
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // AUTOTESTCONSTANTS_H
|
||||
|
49
src/plugins/autotest/autotesticons.h
Normal file
@@ -0,0 +1,49 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AUTOTESTICONS_H
|
||||
#define AUTOTESTICONS_H
|
||||
|
||||
#include <utils/icon.h>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Icons {
|
||||
|
||||
const Utils::Icon EXPAND({
|
||||
{QLatin1String(":/images/expand.png"), Utils::Theme::IconsBaseColor}});
|
||||
const Utils::Icon COLLAPSE({
|
||||
{QLatin1String(":/images/collapse.png"), Utils::Theme::IconsBaseColor}});
|
||||
const Utils::Icon SORT_ALPHABETICALLY({
|
||||
{QLatin1String(":/images/sort.png"), Utils::Theme::IconsBaseColor}});
|
||||
const Utils::Icon SORT_NATURALLY({
|
||||
{QLatin1String(":/images/leafsort.png"), Utils::Theme::IconsBaseColor}});
|
||||
const Utils::Icon RUN_SELECTED_OVERLAY({
|
||||
{QLatin1String(":/images/runselected_boxes.png"), Utils::Theme::BackgroundColorDark},
|
||||
{QLatin1String(":/images/runselected_tickmarks.png"), Utils::Theme::IconsBaseColor}});
|
||||
|
||||
} // namespace Icons
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // AUTOTESTICONS_H
|
184
src/plugins/autotest/autotestplugin.cpp
Normal file
@@ -0,0 +1,184 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "autotestplugin.h"
|
||||
#include "autotestconstants.h"
|
||||
#include "testcodeparser.h"
|
||||
#include "testrunner.h"
|
||||
#include "testsettings.h"
|
||||
#include "testsettingspage.h"
|
||||
#include "testtreeitem.h"
|
||||
#include "testtreeview.h"
|
||||
#include "testtreemodel.h"
|
||||
#include "testresultspane.h"
|
||||
#include "testnavigationwidget.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <coreplugin/icontext.h>
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
#include <coreplugin/actionmanager/command.h>
|
||||
#include <coreplugin/actionmanager/actioncontainer.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
|
||||
#include <extensionsystem/pluginmanager.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QMessageBox>
|
||||
#include <QMainWindow>
|
||||
#include <QMenu>
|
||||
|
||||
#include <QtPlugin>
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
#include "autotestunittests.h"
|
||||
#endif
|
||||
|
||||
using namespace Autotest::Internal;
|
||||
using namespace Core;
|
||||
|
||||
static AutotestPlugin *m_instance = 0;
|
||||
|
||||
AutotestPlugin::AutotestPlugin()
|
||||
: m_settings(new TestSettings)
|
||||
{
|
||||
// needed to be used in QueuedConnection connects
|
||||
qRegisterMetaType<TestResult>();
|
||||
qRegisterMetaType<TestTreeItem *>();
|
||||
qRegisterMetaType<TestCodeLocationAndType>();
|
||||
qRegisterMetaType<TestTreeModel::Type>();
|
||||
|
||||
m_instance = this;
|
||||
}
|
||||
|
||||
AutotestPlugin::~AutotestPlugin()
|
||||
{
|
||||
// Delete members
|
||||
TestTreeModel *model = TestTreeModel::instance();
|
||||
delete model;
|
||||
TestRunner *runner = TestRunner::instance();
|
||||
delete runner;
|
||||
}
|
||||
|
||||
AutotestPlugin *AutotestPlugin::instance()
|
||||
{
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
QSharedPointer<TestSettings> AutotestPlugin::settings() const
|
||||
{
|
||||
return m_settings;
|
||||
}
|
||||
|
||||
void AutotestPlugin::initializeMenuEntries()
|
||||
{
|
||||
ActionContainer *menu = ActionManager::createMenu(Constants::MENU_ID);
|
||||
menu->menu()->setTitle(tr("Tests"));
|
||||
|
||||
QAction *action = new QAction(tr("Run &All Tests"), this);
|
||||
Command *command = ActionManager::registerAction(action, Constants::ACTION_RUN_ALL_ID);
|
||||
command->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+T,Alt+A")));
|
||||
connect(action, &QAction::triggered,
|
||||
this, &AutotestPlugin::onRunAllTriggered);
|
||||
menu->addAction(command);
|
||||
|
||||
action = new QAction(tr("&Run Selected Tests"), this);
|
||||
command = ActionManager::registerAction(action, Constants::ACTION_RUN_SELECTED_ID);
|
||||
command->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+T,Alt+R")));
|
||||
connect(action, &QAction::triggered,
|
||||
this, &AutotestPlugin::onRunSelectedTriggered);
|
||||
menu->addAction(command);
|
||||
|
||||
action = new QAction(tr("Re&scan Tests"), this);
|
||||
command = ActionManager::registerAction(action, Constants::ACTION_SCAN_ID);
|
||||
command->setDefaultKeySequence(QKeySequence(tr("Alt+Shift+T,Alt+S")));
|
||||
connect(action, &QAction::triggered,
|
||||
TestTreeModel::instance()->parser(), &TestCodeParser::updateTestTree);
|
||||
menu->addAction(command);
|
||||
|
||||
ActionContainer *toolsMenu = ActionManager::actionContainer(Core::Constants::M_TOOLS);
|
||||
toolsMenu->addMenu(menu);
|
||||
connect(toolsMenu->menu(), &QMenu::aboutToShow,
|
||||
this, &AutotestPlugin::updateMenuItemsEnabledState);
|
||||
}
|
||||
|
||||
bool AutotestPlugin::initialize(const QStringList &arguments, QString *errorString)
|
||||
{
|
||||
Q_UNUSED(arguments)
|
||||
Q_UNUSED(errorString)
|
||||
|
||||
initializeMenuEntries();
|
||||
|
||||
m_settings->fromSettings(ICore::settings());
|
||||
addAutoReleasedObject(new TestSettingsPage(m_settings));
|
||||
addAutoReleasedObject(new TestNavigationWidgetFactory);
|
||||
addAutoReleasedObject(TestResultsPane::instance());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AutotestPlugin::extensionsInitialized()
|
||||
{
|
||||
}
|
||||
|
||||
ExtensionSystem::IPlugin::ShutdownFlag AutotestPlugin::aboutToShutdown()
|
||||
{
|
||||
return SynchronousShutdown;
|
||||
}
|
||||
|
||||
void AutotestPlugin::onRunAllTriggered()
|
||||
{
|
||||
TestRunner *runner = TestRunner::instance();
|
||||
TestTreeModel *model = TestTreeModel::instance();
|
||||
runner->setSelectedTests(model->getAllTestCases());
|
||||
runner->prepareToRunTests();
|
||||
}
|
||||
|
||||
void AutotestPlugin::onRunSelectedTriggered()
|
||||
{
|
||||
TestRunner *runner = TestRunner::instance();
|
||||
TestTreeModel *model = TestTreeModel::instance();
|
||||
runner->setSelectedTests(model->getSelectedTests());
|
||||
runner->prepareToRunTests();
|
||||
}
|
||||
|
||||
void AutotestPlugin::updateMenuItemsEnabledState()
|
||||
{
|
||||
const bool enabled = !TestRunner::instance()->isTestRunning()
|
||||
&& TestTreeModel::instance()->parser()->state() == TestCodeParser::Idle;
|
||||
const bool hasTests = TestTreeModel::instance()->hasTests();
|
||||
|
||||
ActionManager::command(Constants::ACTION_RUN_ALL_ID)->action()->setEnabled(enabled && hasTests);
|
||||
ActionManager::command(Constants::ACTION_RUN_SELECTED_ID)->action()->setEnabled(enabled && hasTests);
|
||||
ActionManager::command(Constants::ACTION_SCAN_ID)->action()->setEnabled(enabled);
|
||||
}
|
||||
|
||||
QList<QObject *> AutotestPlugin::createTestObjects() const
|
||||
{
|
||||
QList<QObject *> tests;
|
||||
#ifdef WITH_TESTS
|
||||
tests << new AutoTestUnitTests(TestTreeModel::instance());
|
||||
#endif
|
||||
return tests;
|
||||
}
|
69
src/plugins/autotest/autotestplugin.h
Normal file
@@ -0,0 +1,69 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AUTOTESTPLUGIN_H
|
||||
#define AUTOTESTPLUGIN_H
|
||||
|
||||
#include "autotest_global.h"
|
||||
|
||||
#include <extensionsystem/iplugin.h>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
struct TestSettings;
|
||||
|
||||
class AutotestPlugin : public ExtensionSystem::IPlugin
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_PLUGIN_METADATA(IID "org.qt-project.Qt.QtCreatorPlugin" FILE "AutoTest.json")
|
||||
|
||||
public:
|
||||
AutotestPlugin();
|
||||
~AutotestPlugin();
|
||||
|
||||
static AutotestPlugin *instance();
|
||||
|
||||
QSharedPointer<TestSettings> settings() const;
|
||||
|
||||
bool initialize(const QStringList &arguments, QString *errorString);
|
||||
void extensionsInitialized();
|
||||
ShutdownFlag aboutToShutdown();
|
||||
|
||||
private:
|
||||
bool checkLicense();
|
||||
void initializeMenuEntries();
|
||||
void onRunAllTriggered();
|
||||
void onRunSelectedTriggered();
|
||||
void updateMenuItemsEnabledState();
|
||||
QList<QObject *> createTestObjects() const;
|
||||
const QSharedPointer<TestSettings> m_settings;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // AUTOTESTPLUGIN_H
|
||||
|
243
src/plugins/autotest/autotestunittests.cpp
Normal file
@@ -0,0 +1,243 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "autotestconstants.h"
|
||||
#include "autotestunittests.h"
|
||||
#include "testcodeparser.h"
|
||||
#include "testtreemodel.h"
|
||||
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
#include <cpptools/cpptoolstestcase.h>
|
||||
#include <cpptools/projectinfo.h>
|
||||
|
||||
#include <projectexplorer/kitinformation.h>
|
||||
#include <projectexplorer/kitmanager.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/toolchain.h>
|
||||
|
||||
#include <QSignalSpy>
|
||||
#include <QTest>
|
||||
|
||||
#include <coreplugin/navigationwidget.h>
|
||||
|
||||
#include <qtsupport/qtkitinformation.h>
|
||||
|
||||
using namespace Core;
|
||||
using namespace ProjectExplorer;
|
||||
using namespace Utils;
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
AutoTestUnitTests::AutoTestUnitTests(TestTreeModel *model, QObject *parent)
|
||||
: QObject(parent),
|
||||
m_model(model),
|
||||
m_tmpDir(0),
|
||||
m_isQt4(false)
|
||||
{
|
||||
}
|
||||
|
||||
void AutoTestUnitTests::initTestCase()
|
||||
{
|
||||
const QList<Kit *> allKits = KitManager::kits();
|
||||
if (allKits.count() != 1)
|
||||
QSKIP("This test requires exactly one kit to be present");
|
||||
if (auto qtVersion = QtSupport::QtKitInformation::qtVersion(allKits.first()))
|
||||
m_isQt4 = qtVersion->qtVersionString().startsWith(QLatin1Char('4'));
|
||||
else
|
||||
QSKIP("Could not figure out which Qt version is used for default kit.");
|
||||
const ToolChain * const toolchain = ToolChainKitInformation::toolChain(allKits.first());
|
||||
if (!toolchain)
|
||||
QSKIP("This test requires that there is a kit with a toolchain.");
|
||||
|
||||
m_tmpDir = new CppTools::Tests::TemporaryCopiedDir(QLatin1String(":/unit_test"));
|
||||
}
|
||||
|
||||
void AutoTestUnitTests::cleanupTestCase()
|
||||
{
|
||||
delete m_tmpDir;
|
||||
}
|
||||
|
||||
void AutoTestUnitTests::testCodeParser()
|
||||
{
|
||||
QFETCH(QString, projectFilePath);
|
||||
QFETCH(int, expectedAutoTestsCount);
|
||||
QFETCH(int, expectedNamedQuickTestsCount);
|
||||
QFETCH(int, expectedUnnamedQuickTestsCount);
|
||||
QFETCH(int, expectedDataTagsCount);
|
||||
|
||||
NavigationWidget *navigation = NavigationWidget::instance();
|
||||
navigation->activateSubWidget(Constants::AUTOTEST_ID);
|
||||
|
||||
CppTools::Tests::ProjectOpenerAndCloser projectManager;
|
||||
const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true);
|
||||
QVERIFY(projectInfo.isValid());
|
||||
|
||||
QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
|
||||
QVERIFY(parserSpy.wait(20000));
|
||||
|
||||
if (m_isQt4)
|
||||
expectedNamedQuickTestsCount = expectedUnnamedQuickTestsCount = 0;
|
||||
|
||||
QCOMPARE(m_model->autoTestsCount(), expectedAutoTestsCount);
|
||||
QCOMPARE(m_model->namedQuickTestsCount(), expectedNamedQuickTestsCount);
|
||||
QCOMPARE(m_model->unnamedQuickTestsCount(), expectedUnnamedQuickTestsCount);
|
||||
QCOMPARE(m_model->dataTagsCount(), expectedDataTagsCount);
|
||||
|
||||
QCOMPARE(m_model->parser()->autoTestsCount(), expectedAutoTestsCount);
|
||||
QCOMPARE(m_model->parser()->namedQuickTestsCount(), expectedNamedQuickTestsCount);
|
||||
QCOMPARE(m_model->parser()->unnamedQuickTestsCount(), expectedUnnamedQuickTestsCount);
|
||||
|
||||
}
|
||||
|
||||
void AutoTestUnitTests::testCodeParser_data()
|
||||
{
|
||||
QTest::addColumn<QString>("projectFilePath");
|
||||
QTest::addColumn<int>("expectedAutoTestsCount");
|
||||
QTest::addColumn<int>("expectedNamedQuickTestsCount");
|
||||
QTest::addColumn<int>("expectedUnnamedQuickTestsCount");
|
||||
QTest::addColumn<int>("expectedDataTagsCount");
|
||||
|
||||
|
||||
QTest::newRow("plainAutoTest")
|
||||
<< QString(m_tmpDir->path() + QLatin1String("/plain/plain.pro"))
|
||||
<< 1 << 0 << 0 << 0;
|
||||
QTest::newRow("mixedAutoTestAndQuickTests")
|
||||
<< QString(m_tmpDir->path() + QLatin1String("/mixed_atp/mixed_atp.pro"))
|
||||
<< 3 << 5 << 3 << 8;
|
||||
QTest::newRow("plainAutoTestQbs")
|
||||
<< QString(m_tmpDir->path() + QLatin1String("/plain/plain.qbs"))
|
||||
<< 1 << 0 << 0 << 0;
|
||||
QTest::newRow("mixedAutoTestAndQuickTestsQbs")
|
||||
<< QString(m_tmpDir->path() + QLatin1String("/mixed_atp/mixed_atp.qbs"))
|
||||
<< 3 << 5 << 3 << 8;
|
||||
}
|
||||
|
||||
void AutoTestUnitTests::testCodeParserSwitchStartup()
|
||||
{
|
||||
QFETCH(QStringList, projectFilePaths);
|
||||
QFETCH(QList<int>, expectedAutoTestsCount);
|
||||
QFETCH(QList<int>, expectedNamedQuickTestsCount);
|
||||
QFETCH(QList<int>, expectedUnnamedQuickTestsCount);
|
||||
QFETCH(QList<int>, expectedDataTagsCount);
|
||||
|
||||
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->dataTagsCount(),
|
||||
expectedDataTagsCount.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");
|
||||
QTest::addColumn<QList<int> >("expectedDataTagsCount");
|
||||
|
||||
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;
|
||||
QList<int> expectedDataTagsCount = QList<int>() << 0 << 8 << 0 << 8;
|
||||
|
||||
QTest::newRow("loadMultipleProjects")
|
||||
<< projects << expectedAutoTests << expectedNamedQuickTests
|
||||
<< expectedUnnamedQuickTests << expectedDataTagsCount;
|
||||
}
|
||||
|
||||
void AutoTestUnitTests::testCodeParserGTest()
|
||||
{
|
||||
if (qgetenv("GOOGLETEST_DIR").isEmpty())
|
||||
QSKIP("This test needs googletest - set GOOGLETEST_DIR (point to googletest repository)");
|
||||
|
||||
NavigationWidget *navigation = NavigationWidget::instance();
|
||||
navigation->activateSubWidget(Constants::AUTOTEST_ID);
|
||||
|
||||
CppTools::Tests::ProjectOpenerAndCloser projectManager;
|
||||
CppTools::ProjectInfo projectInfo = projectManager.open(
|
||||
QString(m_tmpDir->path() + QLatin1String("/simple_gt/simple_gt.pro")), true);
|
||||
QVERIFY(projectInfo.isValid());
|
||||
|
||||
QSignalSpy parserSpy(m_model->parser(), SIGNAL(parsingFinished()));
|
||||
QVERIFY(parserSpy.wait(20000));
|
||||
|
||||
QCOMPARE(m_model->gtestNamesCount(), 6);
|
||||
// 11 == 3 + 2 + 2 + 2 + 1 + 1, see below
|
||||
QCOMPARE(m_model->parser()->gtestNamesAndSetsCount(), 11);
|
||||
|
||||
QMultiMap<QString, int> expectedNamesAndSets;
|
||||
expectedNamesAndSets.insert(QStringLiteral("FactorialTest"), 3);
|
||||
expectedNamesAndSets.insert(QStringLiteral("FactorialTest_Iterative"), 2);
|
||||
expectedNamesAndSets.insert(QStringLiteral("Sum"), 2);
|
||||
expectedNamesAndSets.insert(QStringLiteral("QueueTest"), 2);
|
||||
expectedNamesAndSets.insert(QStringLiteral("DummyTest"), 1); // used as parameterized test
|
||||
expectedNamesAndSets.insert(QStringLiteral("DummyTest"), 1); // used as 'normal' test
|
||||
|
||||
QMultiMap<QString, int> foundNamesAndSets = m_model->gtestNamesAndSets();
|
||||
QCOMPARE(expectedNamesAndSets.size(), foundNamesAndSets.size());
|
||||
foreach (const QString &name, expectedNamesAndSets.keys())
|
||||
QCOMPARE(expectedNamesAndSets.values(name), foundNamesAndSets.values(name));
|
||||
|
||||
// check also that no Qt related tests have been found
|
||||
QCOMPARE(m_model->autoTestsCount(), 0);
|
||||
QCOMPARE(m_model->namedQuickTestsCount(), 0);
|
||||
QCOMPARE(m_model->unnamedQuickTestsCount(), 0);
|
||||
QCOMPARE(m_model->dataTagsCount(), 0);
|
||||
|
||||
QCOMPARE(m_model->parser()->autoTestsCount(), 0);
|
||||
QCOMPARE(m_model->parser()->namedQuickTestsCount(), 0);
|
||||
QCOMPARE(m_model->parser()->unnamedQuickTestsCount(), 0);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
65
src/plugins/autotest/autotestunittests.h
Normal file
@@ -0,0 +1,65 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef AUTOTESTUNITTESTS_H
|
||||
#define AUTOTESTUNITTESTS_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QTemporaryDir>
|
||||
|
||||
namespace CppTools { namespace Tests { class TemporaryCopiedDir; } }
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestTreeModel;
|
||||
|
||||
class AutoTestUnitTests : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit AutoTestUnitTests(TestTreeModel *model, QObject *parent = 0);
|
||||
|
||||
signals:
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void testCodeParser();
|
||||
void testCodeParser_data();
|
||||
void testCodeParserSwitchStartup();
|
||||
void testCodeParserSwitchStartup_data();
|
||||
void testCodeParserGTest();
|
||||
|
||||
private:
|
||||
TestTreeModel *m_model;
|
||||
CppTools::Tests::TemporaryCopiedDir *m_tmpDir;
|
||||
bool m_isQt4;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // AUTOTESTUNITTESTS_H
|
57
src/plugins/autotest/autotestunittests.qrc
Normal file
@@ -0,0 +1,57 @@
|
||||
<RCC>
|
||||
<qresource prefix="/">
|
||||
<file>unit_test/mixed_atp/src/main.cpp</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/bench/tst_benchtest.cpp</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/dummy/tst_foo.cpp</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/dummy/tst_foo.h</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/gui/tst_guitest.cpp</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/quickauto/bar/tst_foo.qml</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/quickauto/notlisted/tst_bla.qml</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/quickauto/main.cpp</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/quickauto/TestDummy.qml</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/quickauto/tst_test1.qml</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/quickauto/tst_test2.qml</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/quickauto/tst_test3.qml</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/quickauto2/main.cpp</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/quickauto2/tst_test1.qml</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/quickauto2/tst_test2.qml</file>
|
||||
<file>unit_test/plain/test_plain/tst_simple.cpp</file>
|
||||
<file>unit_test/plain/test_plain/tst_simple.h</file>
|
||||
<file>unit_test/plain/plain.pro</file>
|
||||
<file>unit_test/mixed_atp/mixed_atp.pro</file>
|
||||
<file>unit_test/plain/test_plain/test_plain.pro</file>
|
||||
<file>unit_test/mixed_atp/tests/tests.pro</file>
|
||||
<file>unit_test/mixed_atp/src/src.pro</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/bench/bench.pro</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/dummy/dummy.pro</file>
|
||||
<file>unit_test/mixed_atp/tests/auto/gui/gui.pro</file>
|
||||
<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>
|
||||
<file>unit_test/simple_gt/src/main.cpp</file>
|
||||
<file>unit_test/simple_gt/src/src.pro</file>
|
||||
<file>unit_test/simple_gt/tests/gt1/further.cpp</file>
|
||||
<file>unit_test/simple_gt/tests/gt1/gt1.pro</file>
|
||||
<file>unit_test/simple_gt/tests/gt1/main.cpp</file>
|
||||
<file>unit_test/simple_gt/tests/gt2/gt2.pro</file>
|
||||
<file>unit_test/simple_gt/tests/gt2/main.cpp</file>
|
||||
<file>unit_test/simple_gt/tests/gt2/queuetest.h</file>
|
||||
<file>unit_test/simple_gt/tests/tests.pro</file>
|
||||
<file>unit_test/simple_gt/simple_gt.pro</file>
|
||||
<file>unit_test/simple_gt/tests/gtest_dependency.pri</file>
|
||||
<file>unit_test/simple_gt/tests/gt3/dummytest.h</file>
|
||||
<file>unit_test/simple_gt/tests/gt3/gt3.pro</file>
|
||||
<file>unit_test/simple_gt/tests/gt3/main.cpp</file>
|
||||
</qresource>
|
||||
</RCC>
|
BIN
src/plugins/autotest/images/autotest.png
Normal file
After Width: | Height: | Size: 683 B |
BIN
src/plugins/autotest/images/benchmark.png
Normal file
After Width: | Height: | Size: 374 B |
BIN
src/plugins/autotest/images/blacklisted_fail.png
Normal file
After Width: | Height: | Size: 520 B |
BIN
src/plugins/autotest/images/blacklisted_pass.png
Normal file
After Width: | Height: | Size: 519 B |
BIN
src/plugins/autotest/images/class.png
Normal file
After Width: | Height: | Size: 573 B |
BIN
src/plugins/autotest/images/collapse.png
Normal file
After Width: | Height: | Size: 176 B |
BIN
src/plugins/autotest/images/collapse@2x.png
Normal file
After Width: | Height: | Size: 190 B |
BIN
src/plugins/autotest/images/data.png
Normal file
After Width: | Height: | Size: 646 B |
BIN
src/plugins/autotest/images/debug.png
Normal file
After Width: | Height: | Size: 621 B |
BIN
src/plugins/autotest/images/expand.png
Normal file
After Width: | Height: | Size: 174 B |
BIN
src/plugins/autotest/images/expand@2x.png
Normal file
After Width: | Height: | Size: 190 B |
BIN
src/plugins/autotest/images/fail.png
Normal file
After Width: | Height: | Size: 600 B |
BIN
src/plugins/autotest/images/fatal.png
Normal file
After Width: | Height: | Size: 597 B |
BIN
src/plugins/autotest/images/func.png
Normal file
After Width: | Height: | Size: 709 B |
BIN
src/plugins/autotest/images/leafsort.png
Normal file
After Width: | Height: | Size: 178 B |
BIN
src/plugins/autotest/images/leafsort@2x.png
Normal file
After Width: | Height: | Size: 362 B |
BIN
src/plugins/autotest/images/pass.png
Normal file
After Width: | Height: | Size: 595 B |
BIN
src/plugins/autotest/images/runselected_boxes.png
Normal file
After Width: | Height: | Size: 99 B |
BIN
src/plugins/autotest/images/runselected_boxes@2x.png
Normal file
After Width: | Height: | Size: 104 B |
BIN
src/plugins/autotest/images/runselected_tickmarks.png
Normal file
After Width: | Height: | Size: 151 B |
BIN
src/plugins/autotest/images/runselected_tickmarks@2x.png
Normal file
After Width: | Height: | Size: 162 B |
BIN
src/plugins/autotest/images/skip.png
Normal file
After Width: | Height: | Size: 368 B |
BIN
src/plugins/autotest/images/sort.png
Normal file
After Width: | Height: | Size: 139 B |
BIN
src/plugins/autotest/images/sort@2x.png
Normal file
After Width: | Height: | Size: 205 B |
BIN
src/plugins/autotest/images/warn.png
Normal file
After Width: | Height: | Size: 621 B |
BIN
src/plugins/autotest/images/xfail.png
Normal file
After Width: | Height: | Size: 547 B |
BIN
src/plugins/autotest/images/xpass.png
Normal file
After Width: | Height: | Size: 599 B |
1099
src/plugins/autotest/testcodeparser.cpp
Normal file
142
src/plugins/autotest/testcodeparser.h
Normal file
@@ -0,0 +1,142 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTCODEPARSER_H
|
||||
#define TESTCODEPARSER_H
|
||||
|
||||
#include "testtreeitem.h"
|
||||
#include "testtreemodel.h"
|
||||
|
||||
#include <cplusplus/CppDocument.h>
|
||||
|
||||
#include <qmljs/qmljsdocument.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QMap>
|
||||
|
||||
namespace Core {
|
||||
class Id;
|
||||
}
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
struct TestCodeLocationAndType;
|
||||
class TestInfo;
|
||||
class UnnamedQuickTestInfo;
|
||||
class GTestInfo;
|
||||
struct GTestCaseSpec;
|
||||
|
||||
class TestCodeParser : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum State {
|
||||
Idle,
|
||||
PartialParse,
|
||||
FullParse,
|
||||
Disabled
|
||||
};
|
||||
|
||||
explicit TestCodeParser(TestTreeModel *parent = 0);
|
||||
virtual ~TestCodeParser();
|
||||
void setState(State state);
|
||||
State state() const { return m_parserState; }
|
||||
void setDirty() { m_dirty = true; }
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
int autoTestsCount() const;
|
||||
int namedQuickTestsCount() const;
|
||||
int unnamedQuickTestsCount() const;
|
||||
int gtestNamesAndSetsCount() const;
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void cacheCleared();
|
||||
void testItemCreated(TestTreeItem *item, TestTreeModel::Type type);
|
||||
void testItemModified(TestTreeItem *tItem, TestTreeModel::Type type, const QStringList &file);
|
||||
void testItemsRemoved(const QString &filePath, TestTreeModel::Type type);
|
||||
void unnamedQuickTestsUpdated(const QString &mainFile,
|
||||
const QMap<QString, TestCodeLocationAndType> &functions);
|
||||
void unnamedQuickTestsRemoved(const QString &filePath);
|
||||
void gTestsRemoved(const QString &filePath);
|
||||
void parsingStarted();
|
||||
void parsingFinished();
|
||||
void parsingFailed();
|
||||
void partialParsingFinished();
|
||||
|
||||
public slots:
|
||||
void emitUpdateTestTree();
|
||||
void updateTestTree();
|
||||
void checkDocumentForTestCode(CPlusPlus::Document::Ptr document);
|
||||
void handleQtQuickTest(CPlusPlus::Document::Ptr document);
|
||||
void handleGTest(const QString &filePath);
|
||||
|
||||
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);
|
||||
|
||||
private:
|
||||
bool postponed(const QStringList &fileList);
|
||||
void scanForTests(const QStringList &fileList = QStringList());
|
||||
void clearCache();
|
||||
void removeTestsIfNecessary(const QString &fileName);
|
||||
|
||||
void onTaskStarted(Core::Id type);
|
||||
void onAllTasksFinished(Core::Id type);
|
||||
void onFinished();
|
||||
void onPartialParsingFinished();
|
||||
void updateUnnamedQuickTests(const QString &fileName, const QString &mainFile,
|
||||
const QMap<QString, TestCodeLocationAndType> &functions);
|
||||
void updateModelAndCppDocMap(CPlusPlus::Document::Ptr document,
|
||||
const QString &declaringFile, TestTreeItem *testItem);
|
||||
void updateModelAndQuickDocMap(QmlJS::Document::Ptr document,
|
||||
const QString &referencingFile, TestTreeItem *testItem);
|
||||
void updateGTests(const CPlusPlus::Document::Ptr &doc,
|
||||
const QMap<GTestCaseSpec, TestCodeLocationList> &tests);
|
||||
void removeUnnamedQuickTestsByName(const QString &fileName);
|
||||
void removeGTestsByName(const QString &fileName);
|
||||
|
||||
TestTreeModel *m_model;
|
||||
QMap<QString, TestInfo> m_cppDocMap;
|
||||
QMap<QString, TestInfo> m_quickDocMap;
|
||||
QMap<QString, TestInfo> m_gtestDocMap;
|
||||
QList<UnnamedQuickTestInfo> m_unnamedQuickDocList;
|
||||
QList<GTestInfo> m_gtestDocList;
|
||||
bool m_codeModelParsing;
|
||||
bool m_fullUpdatePostponed;
|
||||
bool m_partialUpdatePostponed;
|
||||
bool m_dirty;
|
||||
bool m_singleShotScheduled;
|
||||
QSet<QString> m_postponedFiles;
|
||||
State m_parserState;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // Autotest
|
||||
|
||||
#endif // TESTCODEPARSER_H
|
255
src/plugins/autotest/testconfiguration.cpp
Normal file
@@ -0,0 +1,255 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "testconfiguration.h"
|
||||
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
#include <cpptools/projectinfo.h>
|
||||
|
||||
#include <projectexplorer/buildtargetinfo.h>
|
||||
#include <projectexplorer/environmentaspect.h>
|
||||
#include <projectexplorer/localapplicationrunconfiguration.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/runconfiguration.h>
|
||||
#include <projectexplorer/session.h>
|
||||
#include <projectexplorer/target.h>
|
||||
|
||||
using namespace ProjectExplorer;
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestConfiguration::TestConfiguration(const QString &testClass, const QStringList &testCases,
|
||||
int testCaseCount, QObject *parent)
|
||||
: QObject(parent),
|
||||
m_testClass(testClass),
|
||||
m_testCases(testCases),
|
||||
m_testCaseCount(testCaseCount),
|
||||
m_unnamedOnly(false),
|
||||
m_project(0),
|
||||
m_guessedConfiguration(false),
|
||||
m_type(TestTypeQt)
|
||||
{
|
||||
if (testCases.size() != 0)
|
||||
m_testCaseCount = testCases.size();
|
||||
}
|
||||
|
||||
TestConfiguration::~TestConfiguration()
|
||||
{
|
||||
m_testCases.clear();
|
||||
}
|
||||
|
||||
void basicProjectInformation(Project *project, const QString &mainFilePath, QString *proFile,
|
||||
QString *displayName, Project **targetProject)
|
||||
{
|
||||
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
|
||||
QList<CppTools::ProjectPart::Ptr> projParts = cppMM->projectInfo(project).projectParts();
|
||||
|
||||
foreach (const CppTools::ProjectPart::Ptr &part, projParts) {
|
||||
foreach (const CppTools::ProjectFile currentFile, part->files) {
|
||||
if (currentFile.path == mainFilePath) {
|
||||
*proFile = part->projectFile;
|
||||
*displayName = part->displayName;
|
||||
*targetProject = part->project;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void basicProjectInformation(Project *project, const QString &proFile, QString *displayName,
|
||||
Project **targetProject)
|
||||
{
|
||||
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
|
||||
QList<CppTools::ProjectPart::Ptr> projParts = cppMM->projectInfo(project).projectParts();
|
||||
|
||||
foreach (const CppTools::ProjectPart::Ptr &part, projParts) {
|
||||
if (part->projectFile == proFile) {
|
||||
*displayName = part->displayName;
|
||||
*targetProject = part->project;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void extractEnvironmentInformation(LocalApplicationRunConfiguration *localRunConfiguration,
|
||||
QString *workDir, Utils::Environment *env)
|
||||
{
|
||||
*workDir = Utils::FileUtils::normalizePathName(localRunConfiguration->workingDirectory());
|
||||
if (auto environmentAspect = localRunConfiguration->extraAspect<EnvironmentAspect>())
|
||||
*env = environmentAspect->environment();
|
||||
}
|
||||
|
||||
void TestConfiguration::completeTestInformation()
|
||||
{
|
||||
QTC_ASSERT(!m_mainFilePath.isEmpty() || !m_proFile.isEmpty(), return);
|
||||
|
||||
typedef LocalApplicationRunConfiguration LocalRunConfig;
|
||||
|
||||
Project *project = SessionManager::startupProject();
|
||||
if (!project)
|
||||
return;
|
||||
|
||||
QString targetFile;
|
||||
QString targetName;
|
||||
QString workDir;
|
||||
QString proFile = m_proFile;
|
||||
QString displayName;
|
||||
Project *targetProject = 0;
|
||||
Utils::Environment env;
|
||||
bool hasDesktopTarget = false;
|
||||
bool guessedRunConfiguration = false;
|
||||
setProject(0);
|
||||
|
||||
if (m_proFile.isEmpty())
|
||||
basicProjectInformation(project, m_mainFilePath, &proFile, &displayName, &targetProject);
|
||||
else
|
||||
basicProjectInformation(project, proFile, &displayName, &targetProject);
|
||||
|
||||
Target *target = project->activeTarget();
|
||||
if (!target)
|
||||
return;
|
||||
|
||||
BuildTargetInfoList appTargets = target->applicationTargets();
|
||||
foreach (const BuildTargetInfo &bti, appTargets.list) {
|
||||
// some project manager store line/column information as well inside ProjectPart
|
||||
if (bti.isValid() && proFile.startsWith(bti.projectFilePath.toString())) {
|
||||
targetFile = Utils::HostOsInfo::withExecutableSuffix(bti.targetFilePath.toString());
|
||||
targetName = bti.targetName;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
QList<RunConfiguration *> rcs = target->runConfigurations();
|
||||
foreach (RunConfiguration *rc, rcs) {
|
||||
auto config = qobject_cast<LocalRunConfig *>(rc);
|
||||
if (config && config->executable() == targetFile) {
|
||||
extractEnvironmentInformation(config, &workDir, &env);
|
||||
hasDesktopTarget = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if we could not figure out the run configuration
|
||||
// try to use the run configuration of the parent project
|
||||
if (!hasDesktopTarget && targetProject && !targetFile.isEmpty()) {
|
||||
if (auto config = qobject_cast<LocalRunConfig *>(target->activeRunConfiguration())) {
|
||||
extractEnvironmentInformation(config, &workDir, &env);
|
||||
hasDesktopTarget = true;
|
||||
guessedRunConfiguration = true;
|
||||
}
|
||||
}
|
||||
|
||||
setProFile(proFile);
|
||||
setDisplayName(displayName);
|
||||
|
||||
if (hasDesktopTarget) {
|
||||
setTargetFile(targetFile);
|
||||
setTargetName(targetName);
|
||||
setWorkingDirectory(workDir);
|
||||
setEnvironment(env);
|
||||
setProject(project);
|
||||
setGuessedConfiguration(guessedRunConfiguration);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @brief sets the test cases for this test configuration.
|
||||
*
|
||||
* Watch out for special handling of test configurations, because this method also
|
||||
* updates the test case count to the current size of \a testCases.
|
||||
*
|
||||
* @param testCases list of names of the test functions / test cases
|
||||
*/
|
||||
void TestConfiguration::setTestCases(const QStringList &testCases)
|
||||
{
|
||||
m_testCases.clear();
|
||||
m_testCases << testCases;
|
||||
m_testCaseCount = m_testCases.size();
|
||||
}
|
||||
|
||||
void TestConfiguration::setTestCaseCount(int count)
|
||||
{
|
||||
m_testCaseCount = count;
|
||||
}
|
||||
|
||||
void TestConfiguration::setMainFilePath(const QString &mainFile)
|
||||
{
|
||||
m_mainFilePath = mainFile;
|
||||
}
|
||||
|
||||
void TestConfiguration::setTargetFile(const QString &targetFile)
|
||||
{
|
||||
m_targetFile = targetFile;
|
||||
}
|
||||
|
||||
void TestConfiguration::setTargetName(const QString &targetName)
|
||||
{
|
||||
m_targetName = targetName;
|
||||
}
|
||||
|
||||
void TestConfiguration::setProFile(const QString &proFile)
|
||||
{
|
||||
m_proFile = proFile;
|
||||
}
|
||||
|
||||
void TestConfiguration::setWorkingDirectory(const QString &workingDirectory)
|
||||
{
|
||||
m_workingDir = workingDirectory;
|
||||
}
|
||||
|
||||
void TestConfiguration::setDisplayName(const QString &displayName)
|
||||
{
|
||||
m_displayName = displayName;
|
||||
}
|
||||
|
||||
void TestConfiguration::setEnvironment(const Utils::Environment &env)
|
||||
{
|
||||
m_environment = env;
|
||||
}
|
||||
|
||||
void TestConfiguration::setProject(Project *project)
|
||||
{
|
||||
m_project = project;
|
||||
}
|
||||
|
||||
void TestConfiguration::setUnnamedOnly(bool unnamedOnly)
|
||||
{
|
||||
m_unnamedOnly = unnamedOnly;
|
||||
}
|
||||
|
||||
void TestConfiguration::setGuessedConfiguration(bool guessed)
|
||||
{
|
||||
m_guessedConfiguration = guessed;
|
||||
}
|
||||
|
||||
void TestConfiguration::setTestType(TestType type)
|
||||
{
|
||||
m_type = type;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
100
src/plugins/autotest/testconfiguration.h
Normal file
@@ -0,0 +1,100 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTCONFIGURATION_H
|
||||
#define TESTCONFIGURATION_H
|
||||
|
||||
#include "autotestconstants.h"
|
||||
|
||||
#include <projectexplorer/project.h>
|
||||
#include <utils/environment.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <QPointer>
|
||||
#include <QStringList>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestConfiguration : public QObject
|
||||
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TestConfiguration(const QString &testClass, const QStringList &testCases,
|
||||
int testCaseCount = 0, QObject *parent = 0);
|
||||
~TestConfiguration();
|
||||
|
||||
void completeTestInformation();
|
||||
|
||||
void setTestCases(const QStringList &testCases);
|
||||
void setTestCaseCount(int count);
|
||||
void setMainFilePath(const QString &mainFile);
|
||||
void setTargetFile(const QString &targetFile);
|
||||
void setTargetName(const QString &targetName);
|
||||
void setProFile(const QString &proFile);
|
||||
void setWorkingDirectory(const QString &workingDirectory);
|
||||
void setDisplayName(const QString &displayName);
|
||||
void setEnvironment(const Utils::Environment &env);
|
||||
void setProject(ProjectExplorer::Project *project);
|
||||
void setUnnamedOnly(bool unnamedOnly);
|
||||
void setGuessedConfiguration(bool guessed);
|
||||
void setTestType(TestType type);
|
||||
|
||||
QString testClass() const { return m_testClass; }
|
||||
QStringList testCases() const { return m_testCases; }
|
||||
int testCaseCount() const { return m_testCaseCount; }
|
||||
QString proFile() const { return m_proFile; }
|
||||
QString targetFile() const { return m_targetFile; }
|
||||
QString targetName() const { return m_targetName; }
|
||||
QString workingDirectory() const { return m_workingDir; }
|
||||
QString displayName() const { return m_displayName; }
|
||||
Utils::Environment environment() const { return m_environment; }
|
||||
ProjectExplorer::Project *project() const { return m_project.data(); }
|
||||
bool unnamedOnly() const { return m_unnamedOnly; }
|
||||
bool guessedConfiguration() const { return m_guessedConfiguration; }
|
||||
TestType testType() const { return m_type; }
|
||||
|
||||
private:
|
||||
QString m_testClass;
|
||||
QStringList m_testCases;
|
||||
int m_testCaseCount;
|
||||
QString m_mainFilePath;
|
||||
bool m_unnamedOnly;
|
||||
QString m_proFile;
|
||||
QString m_targetFile;
|
||||
QString m_targetName;
|
||||
QString m_workingDir;
|
||||
QString m_displayName;
|
||||
Utils::Environment m_environment;
|
||||
QPointer<ProjectExplorer::Project> m_project;
|
||||
bool m_guessedConfiguration;
|
||||
TestType m_type;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTCONFIGURATION_H
|
60
src/plugins/autotest/testinfo.cpp
Normal file
@@ -0,0 +1,60 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "testinfo.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestInfo::TestInfo(const QString &className, const QStringList &functions, unsigned revision,
|
||||
unsigned editorRevision)
|
||||
: m_className(className),
|
||||
m_functions(functions),
|
||||
m_revision(revision),
|
||||
m_editorRevision(editorRevision)
|
||||
{
|
||||
}
|
||||
|
||||
TestInfo::~TestInfo()
|
||||
{
|
||||
m_functions.clear();
|
||||
}
|
||||
|
||||
UnnamedQuickTestInfo::UnnamedQuickTestInfo(const QString &function, const QString &fileName)
|
||||
: m_function(function),
|
||||
m_fileName(fileName)
|
||||
{
|
||||
}
|
||||
|
||||
GTestInfo::GTestInfo(const QString &caseName, const QString &setName, const QString &file)
|
||||
: m_caseName(caseName),
|
||||
m_setName(setName),
|
||||
m_fileName(file),
|
||||
m_enabled(true)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
103
src/plugins/autotest/testinfo.h
Normal file
@@ -0,0 +1,103 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTINFO_H
|
||||
#define TESTINFO_H
|
||||
|
||||
#include <QStringList>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestInfo
|
||||
{
|
||||
public:
|
||||
explicit TestInfo(const QString &className = QString(),
|
||||
const QStringList &functions = QStringList(),
|
||||
unsigned revision = 0, unsigned editorRevision = 0);
|
||||
|
||||
~TestInfo();
|
||||
const QString testClass() const { return m_className; }
|
||||
void setTestClass(const QString &className) { m_className = className; }
|
||||
const QStringList testFunctions() const { return m_functions; }
|
||||
void setTestFunctions(const QStringList &functions) { m_functions = functions; }
|
||||
unsigned revision() const { return m_revision; }
|
||||
void setRevision(unsigned revision) { m_revision = revision; }
|
||||
unsigned editorRevision() const { return m_editorRevision; }
|
||||
void setEditorRevision(unsigned editorRevision) { m_editorRevision = editorRevision; }
|
||||
const QString referencingFile() const { return m_referencingFile; }
|
||||
void setReferencingFile(const QString &refFile) { m_referencingFile = refFile; }
|
||||
const QString proFile() const { return m_proFile; }
|
||||
void setProFile(const QString &proFile) { m_proFile = proFile; }
|
||||
|
||||
private:
|
||||
QString m_className;
|
||||
QStringList m_functions;
|
||||
unsigned m_revision;
|
||||
unsigned m_editorRevision;
|
||||
QString m_referencingFile;
|
||||
QString m_proFile;
|
||||
};
|
||||
|
||||
class UnnamedQuickTestInfo {
|
||||
public:
|
||||
explicit UnnamedQuickTestInfo(const QString &function = QString(),
|
||||
const QString& fileName = QString());
|
||||
~UnnamedQuickTestInfo() {}
|
||||
|
||||
const QString function() const { return m_function; }
|
||||
void setFunction(const QString &function) { m_function = function; }
|
||||
const QString fileName() const { return m_fileName; }
|
||||
void setFileName(const QString &fileName) { m_fileName = fileName; }
|
||||
|
||||
private:
|
||||
QString m_function;
|
||||
QString m_fileName;
|
||||
};
|
||||
|
||||
class GTestInfo {
|
||||
public:
|
||||
explicit GTestInfo(const QString &caseName, const QString &setName, const QString &file);
|
||||
|
||||
const QString caseName() const { return m_caseName; }
|
||||
void setCaseName(const QString &caseName) { m_caseName = caseName; }
|
||||
const QString setName() const { return m_setName; }
|
||||
void setSetName(const QString &setName) { m_setName = setName; }
|
||||
const QString fileName() const { return m_fileName; }
|
||||
void setFileName(const QString &fileName) { m_fileName = fileName; }
|
||||
bool isEnabled() const { return m_enabled; }
|
||||
void setEnabled(bool enabled) { m_enabled = enabled; }
|
||||
|
||||
private:
|
||||
QString m_caseName;
|
||||
QString m_setName;
|
||||
QString m_fileName;
|
||||
bool m_enabled;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTINFO_H
|
293
src/plugins/autotest/testnavigationwidget.cpp
Normal file
@@ -0,0 +1,293 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "testnavigationwidget.h"
|
||||
#include "testtreemodel.h"
|
||||
#include "testtreeview.h"
|
||||
#include "testtreeitemdelegate.h"
|
||||
#include "testcodeparser.h"
|
||||
#include "testrunner.h"
|
||||
#include "autotestconstants.h"
|
||||
#include "autotesticons.h"
|
||||
#include "testtreeitem.h"
|
||||
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/find/itemviewfind.h>
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/coreicons.h>
|
||||
#include <coreplugin/icore.h>
|
||||
#include <texteditor/texteditor.h>
|
||||
#include <utils/progressindicator.h>
|
||||
#include <coreplugin/actionmanager/actionmanager.h>
|
||||
|
||||
#include <QAction>
|
||||
#include <QMenu>
|
||||
#include <QTimer>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestNavigationWidget::TestNavigationWidget(QWidget *parent) :
|
||||
QWidget(parent)
|
||||
{
|
||||
setWindowTitle(tr("Tests"));
|
||||
m_model = TestTreeModel::instance();
|
||||
m_sortFilterModel = new TestTreeSortFilterModel(m_model, m_model);
|
||||
m_sortFilterModel->setDynamicSortFilter(true);
|
||||
m_view = new TestTreeView(this);
|
||||
m_view->setModel(m_sortFilterModel);
|
||||
m_view->setSortingEnabled(true);
|
||||
m_view->setItemDelegate(new TestTreeItemDelegate(this));
|
||||
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
layout->addWidget(Core::ItemViewFind::createSearchableWrapper(m_view));
|
||||
setLayout(layout);
|
||||
|
||||
connect(m_view, &TestTreeView::activated, this, &TestNavigationWidget::onItemActivated);
|
||||
|
||||
m_progressIndicator = new Utils::ProgressIndicator(Utils::ProgressIndicator::Medium, this);
|
||||
m_progressIndicator->attachToWidget(m_view);
|
||||
m_progressIndicator->hide();
|
||||
|
||||
m_progressTimer = new QTimer(this);
|
||||
m_progressTimer->setSingleShot(true);
|
||||
m_progressTimer->setInterval(100); // don't display indicator if progress takes less than 100ms
|
||||
|
||||
connect(m_model->parser(), &TestCodeParser::parsingStarted,
|
||||
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);
|
||||
}
|
||||
|
||||
TestNavigationWidget::~TestNavigationWidget()
|
||||
{
|
||||
m_model->disableParsing();
|
||||
}
|
||||
|
||||
void TestNavigationWidget::contextMenuEvent(QContextMenuEvent *event)
|
||||
{
|
||||
const bool enabled = !TestRunner::instance()->isTestRunning()
|
||||
&& m_model->parser()->state() == TestCodeParser::Idle;
|
||||
const bool hasTests = m_model->hasTests();
|
||||
|
||||
QMenu menu;
|
||||
QAction *runThisTest = 0;
|
||||
const QModelIndexList list = m_view->selectionModel()->selectedIndexes();
|
||||
if (list.size() == 1) {
|
||||
const QModelIndex index = list.first();
|
||||
QRect rect(m_view->visualRect(index));
|
||||
if (rect.contains(event->pos())) {
|
||||
// do not provide this menu entry for unnamed Quick Tests as it makes no sense
|
||||
int type = index.data(TypeRole).toInt();
|
||||
const QString &unnamed = tr(Constants::UNNAMED_QUICKTESTS);
|
||||
if ((type == TestTreeItem::TestFunction && index.parent().data().toString() != unnamed)
|
||||
|| (type == TestTreeItem::TestClass && index.data().toString() != unnamed)
|
||||
|| (type == TestTreeItem::TestDataTag)
|
||||
|| (type == TestTreeItem::GTestCase)
|
||||
|| (type == TestTreeItem::GTestCaseParameterized)
|
||||
|| (type == TestTreeItem::GTestName)) {
|
||||
runThisTest = new QAction(tr("Run This Test"), &menu);
|
||||
runThisTest->setEnabled(enabled);
|
||||
connect(runThisTest, &QAction::triggered,
|
||||
this, &TestNavigationWidget::onRunThisTestTriggered);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QAction *runAll = Core::ActionManager::command(Constants::ACTION_RUN_ALL_ID)->action();
|
||||
QAction *runSelected = Core::ActionManager::command(Constants::ACTION_RUN_SELECTED_ID)->action();
|
||||
QAction *selectAll = new QAction(tr("Select All"), &menu);
|
||||
QAction *deselectAll = new QAction(tr("Deselect All"), &menu);
|
||||
// TODO remove?
|
||||
QAction *rescan = Core::ActionManager::command(Constants::ACTION_SCAN_ID)->action();
|
||||
|
||||
connect(selectAll, &QAction::triggered, m_view, &TestTreeView::selectAll);
|
||||
connect(deselectAll, &QAction::triggered, m_view, &TestTreeView::deselectAll);
|
||||
|
||||
runAll->setEnabled(enabled && hasTests);
|
||||
runSelected->setEnabled(enabled && hasTests);
|
||||
selectAll->setEnabled(enabled && hasTests);
|
||||
deselectAll->setEnabled(enabled && hasTests);
|
||||
rescan->setEnabled(enabled);
|
||||
|
||||
if (runThisTest) {
|
||||
menu.addAction(runThisTest);
|
||||
menu.addSeparator();
|
||||
}
|
||||
menu.addAction(runAll);
|
||||
menu.addAction(runSelected);
|
||||
menu.addSeparator();
|
||||
menu.addAction(selectAll);
|
||||
menu.addAction(deselectAll);
|
||||
menu.addSeparator();
|
||||
menu.addAction(rescan);
|
||||
|
||||
menu.exec(mapToGlobal(event->pos()));
|
||||
}
|
||||
|
||||
QList<QToolButton *> TestNavigationWidget::createToolButtons()
|
||||
{
|
||||
QList<QToolButton *> list;
|
||||
|
||||
m_filterButton = new QToolButton(m_view);
|
||||
m_filterButton->setIcon(Core::Icons::FILTER.icon());
|
||||
m_filterButton->setToolTip(tr("Filter Test Tree"));
|
||||
m_filterButton->setProperty("noArrow", true);
|
||||
m_filterButton->setAutoRaise(true);
|
||||
m_filterButton->setPopupMode(QToolButton::InstantPopup);
|
||||
m_filterMenu = new QMenu(m_filterButton);
|
||||
initializeFilterMenu();
|
||||
connect(m_filterMenu, &QMenu::triggered, this, &TestNavigationWidget::onFilterMenuTriggered);
|
||||
m_filterButton->setMenu(m_filterMenu);
|
||||
|
||||
m_sortAlphabetically = true;
|
||||
m_sort = new QToolButton(this);
|
||||
m_sort->setIcon(Icons::SORT_NATURALLY.icon());
|
||||
m_sort->setToolTip(tr("Sort Naturally"));
|
||||
|
||||
QToolButton *expand = new QToolButton(this);
|
||||
expand->setIcon(Icons::EXPAND.icon());
|
||||
expand->setToolTip(tr("Expand All"));
|
||||
|
||||
QToolButton *collapse = new QToolButton(this);
|
||||
collapse->setIcon(Icons::COLLAPSE.icon());
|
||||
collapse->setToolTip(tr("Collapse All"));
|
||||
|
||||
connect(expand, &QToolButton::clicked, m_view, &TestTreeView::expandAll);
|
||||
connect(collapse, &QToolButton::clicked, m_view, &TestTreeView::collapseAll);
|
||||
connect(m_sort, &QToolButton::clicked, this, &TestNavigationWidget::onSortClicked);
|
||||
|
||||
list << m_filterButton << m_sort << expand << collapse;
|
||||
return list;
|
||||
}
|
||||
|
||||
void TestNavigationWidget::onItemActivated(const QModelIndex &index)
|
||||
{
|
||||
const TextEditor::TextEditorWidget::Link link
|
||||
= index.data(LinkRole).value<TextEditor::TextEditorWidget::Link>();
|
||||
if (link.hasValidTarget()) {
|
||||
Core::EditorManager::openEditorAt(link.targetFileName, link.targetLine,
|
||||
link.targetColumn);
|
||||
}
|
||||
}
|
||||
|
||||
void TestNavigationWidget::onSortClicked()
|
||||
{
|
||||
if (m_sortAlphabetically) {
|
||||
m_sort->setIcon(Icons::SORT_ALPHABETICALLY.icon());
|
||||
m_sort->setToolTip(tr("Sort Alphabetically"));
|
||||
m_sortFilterModel->setSortMode(TestTreeSortFilterModel::Naturally);
|
||||
} else {
|
||||
m_sort->setIcon(Icons::SORT_NATURALLY.icon());
|
||||
m_sort->setToolTip(tr("Sort Naturally"));
|
||||
m_sortFilterModel->setSortMode(TestTreeSortFilterModel::Alphabetically);
|
||||
}
|
||||
m_sortAlphabetically = !m_sortAlphabetically;
|
||||
}
|
||||
|
||||
void TestNavigationWidget::onFilterMenuTriggered(QAction *action)
|
||||
{
|
||||
m_sortFilterModel->toggleFilter(
|
||||
TestTreeSortFilterModel::toFilterMode(action->data().value<int>()));
|
||||
}
|
||||
|
||||
void TestNavigationWidget::onParsingStarted()
|
||||
{
|
||||
m_progressTimer->start();
|
||||
}
|
||||
|
||||
void TestNavigationWidget::onParsingFinished()
|
||||
{
|
||||
m_progressTimer->stop();
|
||||
m_progressIndicator->hide();
|
||||
}
|
||||
|
||||
void TestNavigationWidget::initializeFilterMenu()
|
||||
{
|
||||
QAction *action = new QAction(m_filterMenu);
|
||||
action->setText(tr("Show Init and Cleanup Functions"));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(false);
|
||||
action->setData(TestTreeSortFilterModel::ShowInitAndCleanup);
|
||||
m_filterMenu->addAction(action);
|
||||
action = new QAction(m_filterMenu);
|
||||
action->setText(tr("Show Data Functions"));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(false);
|
||||
action->setData(TestTreeSortFilterModel::ShowTestData);
|
||||
m_filterMenu->addAction(action);
|
||||
}
|
||||
|
||||
void TestNavigationWidget::onRunThisTestTriggered()
|
||||
{
|
||||
const QModelIndexList selected = m_view->selectionModel()->selectedIndexes();
|
||||
// paranoia
|
||||
if (selected.isEmpty())
|
||||
return;
|
||||
const QModelIndex sourceIndex = m_sortFilterModel->mapToSource(selected.first());
|
||||
if (!sourceIndex.isValid())
|
||||
return;
|
||||
|
||||
TestTreeItem *item = static_cast<TestTreeItem *>(sourceIndex.internalPointer());
|
||||
if (item->type() == TestTreeItem::TestClass || item->type() == TestTreeItem::TestFunction
|
||||
|| item->type() == TestTreeItem::TestDataTag
|
||||
|| item->type() == TestTreeItem::GTestCase
|
||||
|| item->type() == TestTreeItem::GTestCaseParameterized
|
||||
|| item->type() == TestTreeItem::GTestName) {
|
||||
if (TestConfiguration *configuration = m_model->getTestConfiguration(item)) {
|
||||
TestRunner *runner = TestRunner::instance();
|
||||
runner->setSelectedTests( {configuration} );
|
||||
runner->prepareToRunTests();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TestNavigationWidgetFactory::TestNavigationWidgetFactory()
|
||||
{
|
||||
setDisplayName(tr("Tests"));
|
||||
setId(Autotest::Constants::AUTOTEST_ID);
|
||||
setPriority(666);
|
||||
}
|
||||
|
||||
Core::NavigationView TestNavigationWidgetFactory::createWidget()
|
||||
{
|
||||
TestNavigationWidget *treeViewWidget = new TestNavigationWidget;
|
||||
Core::NavigationView view;
|
||||
view.widget = treeViewWidget;
|
||||
view.dockToolBarWidgets = treeViewWidget->createToolButtons();
|
||||
TestTreeModel::instance()->enableParsing();
|
||||
return view;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
106
src/plugins/autotest/testnavigationwidget.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTNAVIGATIONWIDGETTREEVIEW_H
|
||||
#define TESTNAVIGATIONWIDGETTREEVIEW_H
|
||||
|
||||
#include <coreplugin/inavigationwidgetfactory.h>
|
||||
|
||||
#include <utils/navigationtreeview.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAction;
|
||||
class QMenu;
|
||||
class QTimer;
|
||||
class QToolButton;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Core {
|
||||
class IContext;
|
||||
}
|
||||
|
||||
namespace Utils {
|
||||
class ProgressIndicator;
|
||||
}
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestTreeModel;
|
||||
class TestTreeSortFilterModel;
|
||||
class TestTreeView;
|
||||
|
||||
class TestNavigationWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit TestNavigationWidget(QWidget *parent = 0);
|
||||
~TestNavigationWidget();
|
||||
void contextMenuEvent(QContextMenuEvent *event);
|
||||
QList<QToolButton *> createToolButtons();
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
|
||||
private slots:
|
||||
void onItemActivated(const QModelIndex &index);
|
||||
void onSortClicked();
|
||||
void onFilterMenuTriggered(QAction *action);
|
||||
void onParsingStarted();
|
||||
void onParsingFinished();
|
||||
|
||||
private:
|
||||
void initializeFilterMenu();
|
||||
void onRunThisTestTriggered();
|
||||
|
||||
TestTreeModel *m_model;
|
||||
TestTreeSortFilterModel *m_sortFilterModel;
|
||||
TestTreeView *m_view;
|
||||
QToolButton *m_sort;
|
||||
QToolButton *m_filterButton;
|
||||
QMenu *m_filterMenu;
|
||||
bool m_sortAlphabetically;
|
||||
Utils::ProgressIndicator *m_progressIndicator;
|
||||
QTimer *m_progressTimer;
|
||||
};
|
||||
|
||||
class TestNavigationWidgetFactory : public Core::INavigationWidgetFactory
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestNavigationWidgetFactory();
|
||||
|
||||
private:
|
||||
Core::NavigationView createWidget();
|
||||
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTNAVIGATIONWIDGETTREEVIEW_H
|
424
src/plugins/autotest/testoutputreader.cpp
Normal file
@@ -0,0 +1,424 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "testoutputreader.h"
|
||||
#include "testresult.h"
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QDebug>
|
||||
#include <QRegExp>
|
||||
#include <QProcess>
|
||||
#include <QFileInfo>
|
||||
#include <QDir>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
static QString decode(const QString& original)
|
||||
{
|
||||
QString result(original);
|
||||
static QRegExp regex(QLatin1String("&#((x[0-9A-F]+)|([0-9]+));"), Qt::CaseInsensitive);
|
||||
regex.setMinimal(true);
|
||||
|
||||
int pos = 0;
|
||||
while ((pos = regex.indexIn(original, pos)) != -1) {
|
||||
const QString value = regex.cap(1);
|
||||
if (value.startsWith(QLatin1Char('x')))
|
||||
result.replace(regex.cap(0), QChar(value.mid(1).toInt(0, 16)));
|
||||
else
|
||||
result.replace(regex.cap(0), QChar(value.toInt(0, 10)));
|
||||
pos += regex.matchedLength();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static QString constructSourceFilePath(const QString &path, const QString &filePath,
|
||||
const QString &app)
|
||||
{
|
||||
if (Utils::HostOsInfo::isMacHost() && !app.isEmpty()) {
|
||||
const QString fileName(QFileInfo(app).fileName());
|
||||
return QFileInfo(path.left(path.lastIndexOf(fileName + QLatin1String(".app"))), filePath)
|
||||
.canonicalFilePath();
|
||||
}
|
||||
return QFileInfo(path, filePath).canonicalFilePath();
|
||||
}
|
||||
|
||||
// adapted from qplaintestlogger.cpp
|
||||
static QString formatResult(double value)
|
||||
{
|
||||
//NAN is not supported with visual studio 2010
|
||||
if (value < 0)// || value == NAN)
|
||||
return QLatin1String("NAN");
|
||||
if (value == 0)
|
||||
return QLatin1String("0");
|
||||
|
||||
int significantDigits = 0;
|
||||
qreal divisor = 1;
|
||||
|
||||
while (value / divisor >= 1) {
|
||||
divisor *= 10;
|
||||
++significantDigits;
|
||||
}
|
||||
|
||||
QString beforeDecimalPoint = QString::number(value, 'f', 0);
|
||||
QString afterDecimalPoint = QString::number(value, 'f', 20);
|
||||
afterDecimalPoint.remove(0, beforeDecimalPoint.count() + 1);
|
||||
|
||||
const int beforeUse = qMin(beforeDecimalPoint.count(), significantDigits);
|
||||
const int beforeRemove = beforeDecimalPoint.count() - beforeUse;
|
||||
|
||||
beforeDecimalPoint.chop(beforeRemove);
|
||||
for (int i = 0; i < beforeRemove; ++i)
|
||||
beforeDecimalPoint.append(QLatin1Char('0'));
|
||||
|
||||
int afterUse = significantDigits - beforeUse;
|
||||
if (beforeDecimalPoint == QLatin1String("0") && !afterDecimalPoint.isEmpty()) {
|
||||
++afterUse;
|
||||
int i = 0;
|
||||
while (i < afterDecimalPoint.count() && afterDecimalPoint.at(i) == QLatin1Char('0'))
|
||||
++i;
|
||||
afterUse += i;
|
||||
}
|
||||
|
||||
const int afterRemove = afterDecimalPoint.count() - afterUse;
|
||||
afterDecimalPoint.chop(afterRemove);
|
||||
|
||||
QString result = beforeDecimalPoint;
|
||||
if (afterUse > 0)
|
||||
result.append(QLatin1Char('.'));
|
||||
result += afterDecimalPoint;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static QString constructBenchmarkInformation(const QString &metric, double value, int iterations)
|
||||
{
|
||||
QString metricsText;
|
||||
if (metric == QLatin1String("WalltimeMilliseconds")) // default
|
||||
metricsText = QLatin1String("msecs");
|
||||
else if (metric == QLatin1String("CPUTicks")) // -tickcounter
|
||||
metricsText = QLatin1String("CPU ticks");
|
||||
else if (metric == QLatin1String("Events")) // -eventcounter
|
||||
metricsText = QLatin1String("events");
|
||||
else if (metric == QLatin1String("InstructionReads")) // -callgrind
|
||||
metricsText = QLatin1String("instruction reads");
|
||||
else if (metric == QLatin1String("CPUCycles")) // -perf
|
||||
metricsText = QLatin1String("CPU cycles");
|
||||
return QObject::tr("%1 %2 per iteration (total: %3, iterations: %4)")
|
||||
.arg(formatResult(value))
|
||||
.arg(metricsText)
|
||||
.arg(formatResult(value * (double)iterations))
|
||||
.arg(iterations);
|
||||
}
|
||||
|
||||
TestOutputReader::TestOutputReader(QFutureInterface<TestResult *> futureInterface,
|
||||
QProcess *testApplication)
|
||||
: m_futureInterface(futureInterface)
|
||||
, m_testApplication(testApplication)
|
||||
{
|
||||
connect(m_testApplication, &QProcess::readyRead, this, &TestOutputReader::processOutput);
|
||||
}
|
||||
|
||||
QtTestOutputReader::QtTestOutputReader(QFutureInterface<TestResult *> futureInterface,
|
||||
QProcess *testApplication)
|
||||
: TestOutputReader(futureInterface, testApplication)
|
||||
{
|
||||
}
|
||||
|
||||
void QtTestOutputReader::processOutput()
|
||||
{
|
||||
if (!m_testApplication || m_testApplication->state() != QProcess::Running)
|
||||
return;
|
||||
static QStringList validEndTags = { QStringLiteral("Incident"),
|
||||
QStringLiteral("Message"),
|
||||
QStringLiteral("BenchmarkResult"),
|
||||
QStringLiteral("QtVersion"),
|
||||
QStringLiteral("QtBuild"),
|
||||
QStringLiteral("QTestVersion") };
|
||||
|
||||
while (m_testApplication->canReadLine()) {
|
||||
m_xmlReader.addData(m_testApplication->readLine());
|
||||
while (!m_xmlReader.atEnd()) {
|
||||
if (m_futureInterface.isCanceled())
|
||||
return;
|
||||
QXmlStreamReader::TokenType token = m_xmlReader.readNext();
|
||||
switch (token) {
|
||||
case QXmlStreamReader::StartDocument:
|
||||
m_className.clear();
|
||||
break;
|
||||
case QXmlStreamReader::EndDocument:
|
||||
m_xmlReader.clear();
|
||||
return;
|
||||
case QXmlStreamReader::StartElement: {
|
||||
const QString currentTag = m_xmlReader.name().toString();
|
||||
if (currentTag == QStringLiteral("TestCase")) {
|
||||
m_className = m_xmlReader.attributes().value(QStringLiteral("name")).toString();
|
||||
QTC_ASSERT(!m_className.isEmpty(), continue);
|
||||
auto testResult = new QTestResult(m_className);
|
||||
testResult->setResult(Result::MessageTestCaseStart);
|
||||
testResult->setDescription(tr("Executing test case %1").arg(m_className));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
} else if (currentTag == QStringLiteral("TestFunction")) {
|
||||
m_testCase = m_xmlReader.attributes().value(QStringLiteral("name")).toString();
|
||||
QTC_ASSERT(!m_testCase.isEmpty(), continue);
|
||||
auto testResult = new QTestResult();
|
||||
testResult->setResult(Result::MessageCurrentTest);
|
||||
testResult->setDescription(tr("Entering test function %1::%2").arg(m_className,
|
||||
m_testCase));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
} else if (currentTag == QStringLiteral("Duration")) {
|
||||
m_duration = m_xmlReader.attributes().value(QStringLiteral("msecs")).toString();
|
||||
QTC_ASSERT(!m_duration.isEmpty(), continue);
|
||||
} else if (currentTag == QStringLiteral("Message")
|
||||
|| currentTag == QStringLiteral("Incident")) {
|
||||
m_dataTag.clear();
|
||||
m_description.clear();
|
||||
m_duration.clear();
|
||||
m_file.clear();
|
||||
m_result = Result::Invalid;
|
||||
m_lineNumber = 0;
|
||||
const QXmlStreamAttributes &attributes = m_xmlReader.attributes();
|
||||
m_result = TestResult::resultFromString(
|
||||
attributes.value(QStringLiteral("type")).toString());
|
||||
m_file = decode(attributes.value(QStringLiteral("file")).toString());
|
||||
if (!m_file.isEmpty()) {
|
||||
const QString base = QFileInfo(m_testApplication->program()).absolutePath();
|
||||
m_file = constructSourceFilePath(base, m_file,
|
||||
m_testApplication->program());
|
||||
}
|
||||
m_lineNumber = attributes.value(QStringLiteral("line")).toInt();
|
||||
} else if (currentTag == QStringLiteral("BenchmarkResult")) {
|
||||
const QXmlStreamAttributes &attributes = m_xmlReader.attributes();
|
||||
const QString metric = attributes.value(QStringLiteral("metrics")).toString();
|
||||
const double value = attributes.value(QStringLiteral("value")).toDouble();
|
||||
const int iterations = attributes.value(QStringLiteral("iterations")).toInt();
|
||||
m_description = constructBenchmarkInformation(metric, value, iterations);
|
||||
m_result = Result::Benchmark;
|
||||
} else if (currentTag == QStringLiteral("DataTag")) {
|
||||
m_cdataMode = DataTag;
|
||||
} else if (currentTag == QStringLiteral("Description")) {
|
||||
m_cdataMode = Description;
|
||||
} else if (currentTag == QStringLiteral("QtVersion")) {
|
||||
m_result = Result::MessageInternal;
|
||||
m_cdataMode = QtVersion;
|
||||
} else if (currentTag == QStringLiteral("QtBuild")) {
|
||||
m_result = Result::MessageInternal;
|
||||
m_cdataMode = QtBuild;
|
||||
} else if (currentTag == QStringLiteral("QTestVersion")) {
|
||||
m_result = Result::MessageInternal;
|
||||
m_cdataMode = QTestVersion;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QXmlStreamReader::Characters: {
|
||||
QStringRef text = m_xmlReader.text().trimmed();
|
||||
if (text.isEmpty())
|
||||
break;
|
||||
|
||||
switch (m_cdataMode) {
|
||||
case DataTag:
|
||||
m_dataTag = text.toString();
|
||||
break;
|
||||
case Description:
|
||||
if (!m_description.isEmpty())
|
||||
m_description.append(QLatin1Char('\n'));
|
||||
m_description.append(text);
|
||||
break;
|
||||
case QtVersion:
|
||||
m_description = tr("Qt version: %1").arg(text.toString());
|
||||
break;
|
||||
case QtBuild:
|
||||
m_description = tr("Qt build: %1").arg(text.toString());
|
||||
break;
|
||||
case QTestVersion:
|
||||
m_description = tr("QTest version: %1").arg(text.toString());
|
||||
break;
|
||||
default:
|
||||
QString message = QString::fromLatin1("unexpected cdatamode %1 for text \"%2\"")
|
||||
.arg(m_cdataMode)
|
||||
.arg(text.toString());
|
||||
QTC_ASSERT(false, qWarning() << message);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case QXmlStreamReader::EndElement: {
|
||||
m_cdataMode = None;
|
||||
const QStringRef currentTag = m_xmlReader.name();
|
||||
if (currentTag == QStringLiteral("TestFunction")) {
|
||||
if (!m_duration.isEmpty()) {
|
||||
auto testResult = new QTestResult(m_className);
|
||||
testResult->setTestCase(m_testCase);
|
||||
testResult->setResult(Result::MessageInternal);
|
||||
testResult->setDescription(tr("Execution took %1 ms.").arg(m_duration));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
}
|
||||
m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
|
||||
} else if (currentTag == QStringLiteral("TestCase")) {
|
||||
auto testResult = new QTestResult(m_className);
|
||||
testResult->setResult(Result::MessageTestCaseEnd);
|
||||
testResult->setDescription(
|
||||
m_duration.isEmpty() ? tr("Test finished.")
|
||||
: tr("Test execution took %1 ms.").arg(m_duration));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
} else if (validEndTags.contains(currentTag.toString())) {
|
||||
auto testResult = new QTestResult(m_className);
|
||||
testResult->setTestCase(m_testCase);
|
||||
testResult->setDataTag(m_dataTag);
|
||||
testResult->setResult(m_result);
|
||||
testResult->setFileName(m_file);
|
||||
testResult->setLine(m_lineNumber);
|
||||
testResult->setDescription(m_description);
|
||||
m_futureInterface.reportResult(testResult);
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GTestOutputReader::GTestOutputReader(QFutureInterface<TestResult *> futureInterface,
|
||||
QProcess *testApplication)
|
||||
: TestOutputReader(futureInterface, testApplication)
|
||||
{
|
||||
}
|
||||
|
||||
void GTestOutputReader::processOutput()
|
||||
{
|
||||
if (!m_testApplication || m_testApplication->state() != QProcess::Running)
|
||||
return;
|
||||
|
||||
static QRegExp newTestStarts(QStringLiteral("^\\[-{10}\\] \\d+ tests? from (.*)$"));
|
||||
static QRegExp testEnds(QStringLiteral("^\\[-{10}\\] \\d+ tests? from (.*) \\((.*)\\)$"));
|
||||
static QRegExp newTestSetStarts(QStringLiteral("^\\[ RUN \\] (.*)$"));
|
||||
static QRegExp testSetSuccess(QStringLiteral("^\\[ OK \\] (.*) \\((.*)\\)$"));
|
||||
static QRegExp testSetFail(QStringLiteral("^\\\[ FAILED \\] (.*) \\((.*)\\)$"));
|
||||
static QRegExp disabledTests(QStringLiteral("^ YOU HAVE (\\d+) DISABLED TESTS?$"));
|
||||
|
||||
while (m_testApplication->canReadLine()) {
|
||||
if (m_futureInterface.isCanceled())
|
||||
return;
|
||||
QByteArray read = m_testApplication->readLine();
|
||||
if (!m_unprocessed.isEmpty()) {
|
||||
read = m_unprocessed + read;
|
||||
m_unprocessed.clear();
|
||||
}
|
||||
if (!read.endsWith('\n')) {
|
||||
m_unprocessed = read;
|
||||
continue;
|
||||
}
|
||||
read.chop(1); // remove the newline from the output
|
||||
|
||||
const QString line = QString::fromLatin1(read);
|
||||
if (line.trimmed().isEmpty())
|
||||
continue;
|
||||
|
||||
if (!line.startsWith(QLatin1Char('['))) {
|
||||
m_description.append(line).append(QLatin1Char('\n'));
|
||||
if (line.startsWith(QStringLiteral("Note:"))) {
|
||||
auto testResult = new GTestResult();
|
||||
testResult->setResult(Result::MessageInternal);
|
||||
testResult->setDescription(line);
|
||||
m_futureInterface.reportResult(testResult);
|
||||
m_description.clear();
|
||||
} else if (disabledTests.exactMatch(line)) {
|
||||
auto testResult = new GTestResult();
|
||||
testResult->setResult(Result::MessageDisabledTests);
|
||||
int disabled = disabledTests.cap(1).toInt();
|
||||
testResult->setDescription(tr("You have %n disabled test(s).", 0, disabled));
|
||||
testResult->setLine(disabled); // misuse line property to hold number of disabled
|
||||
m_futureInterface.reportResult(testResult);
|
||||
m_description.clear();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
if (testEnds.exactMatch(line)) {
|
||||
auto testResult = new GTestResult(m_currentTestName);
|
||||
testResult->setTestCase(m_currentTestSet);
|
||||
testResult->setResult(Result::MessageTestCaseEnd);
|
||||
testResult->setDescription(tr("Test execution took %1").arg(testEnds.cap(2)));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
m_currentTestName.clear();
|
||||
m_currentTestSet.clear();
|
||||
} else if (newTestStarts.exactMatch(line)) {
|
||||
m_currentTestName = newTestStarts.cap(1);
|
||||
auto testResult = new GTestResult(m_currentTestName);
|
||||
testResult->setResult(Result::MessageTestCaseStart);
|
||||
testResult->setDescription(tr("Executing test case %1").arg(m_currentTestName));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
} else if (newTestSetStarts.exactMatch(line)) {
|
||||
m_currentTestSet = newTestSetStarts.cap(1);
|
||||
auto testResult = new GTestResult();
|
||||
testResult->setResult(Result::MessageCurrentTest);
|
||||
testResult->setDescription(tr("Entering test set %1").arg(m_currentTestSet));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
} else if (testSetSuccess.exactMatch(line)) {
|
||||
auto testResult = new GTestResult(m_currentTestName);
|
||||
testResult->setTestCase(m_currentTestSet);
|
||||
testResult->setResult(Result::Pass);
|
||||
m_futureInterface.reportResult(testResult);
|
||||
testResult = new GTestResult(m_currentTestName);
|
||||
testResult->setTestCase(m_currentTestSet);
|
||||
testResult->setResult(Result::MessageInternal);
|
||||
testResult->setDescription(tr("Execution took %1.").arg(testSetSuccess.cap(2)));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
|
||||
} else if (testSetFail.exactMatch(line)) {
|
||||
auto testResult = new GTestResult(m_currentTestName);
|
||||
testResult->setTestCase(m_currentTestSet);
|
||||
testResult->setResult(Result::Fail);
|
||||
m_description.chop(1);
|
||||
testResult->setDescription(m_description);
|
||||
int firstColon = m_description.indexOf(QLatin1Char(':'));
|
||||
if (firstColon != -1) {
|
||||
int secondColon = m_description.indexOf(QLatin1Char(':'), firstColon + 1);
|
||||
const QString base = QFileInfo(m_testApplication->program()).absolutePath();
|
||||
QString file = constructSourceFilePath(base, m_description.left(firstColon),
|
||||
m_testApplication->program());
|
||||
QString line = m_description.mid(firstColon + 1, secondColon - firstColon - 1);
|
||||
testResult->setFileName(file);
|
||||
testResult->setLine(line.toInt());
|
||||
}
|
||||
m_futureInterface.reportResult(testResult);
|
||||
m_description.clear();
|
||||
testResult = new GTestResult(m_currentTestName);
|
||||
testResult->setTestCase(m_currentTestSet);
|
||||
testResult->setResult(Result::MessageInternal);
|
||||
testResult->setDescription(tr("Execution took %1.").arg(testSetFail.cap(2)));
|
||||
m_futureInterface.reportResult(testResult);
|
||||
m_futureInterface.setProgressValue(m_futureInterface.progressValue() + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
108
src/plugins/autotest/testoutputreader.h
Normal file
@@ -0,0 +1,108 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTXMLOUTPUTREADER_H
|
||||
#define TESTXMLOUTPUTREADER_H
|
||||
|
||||
#include "testresult.h"
|
||||
|
||||
#include <QFutureInterface>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QXmlStreamReader>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QProcess;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestOutputReader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestOutputReader(QFutureInterface<TestResult *> futureInterface,
|
||||
QProcess *testApplication);
|
||||
|
||||
protected:
|
||||
virtual void processOutput() = 0;
|
||||
QFutureInterface<TestResult *> m_futureInterface;
|
||||
QProcess *m_testApplication; // not owned
|
||||
};
|
||||
|
||||
class QtTestOutputReader : public TestOutputReader
|
||||
{
|
||||
public:
|
||||
QtTestOutputReader(QFutureInterface<TestResult *> futureInterface,
|
||||
QProcess *testApplication);
|
||||
|
||||
protected:
|
||||
void processOutput() override;
|
||||
|
||||
private:
|
||||
enum CDATAMode
|
||||
{
|
||||
None,
|
||||
DataTag,
|
||||
Description,
|
||||
QtVersion,
|
||||
QtBuild,
|
||||
QTestVersion
|
||||
};
|
||||
|
||||
CDATAMode m_cdataMode = None;
|
||||
QString m_className;
|
||||
QString m_testCase;
|
||||
QString m_dataTag;
|
||||
Result::Type m_result = Result::Invalid;
|
||||
QString m_description;
|
||||
QString m_file;
|
||||
int m_lineNumber = 0;
|
||||
QString m_duration;
|
||||
QXmlStreamReader m_xmlReader;
|
||||
};
|
||||
|
||||
class GTestOutputReader : public TestOutputReader
|
||||
{
|
||||
public:
|
||||
GTestOutputReader(QFutureInterface<TestResult *> futureInterface,
|
||||
QProcess *testApplication);
|
||||
|
||||
protected:
|
||||
void processOutput() override;
|
||||
|
||||
private:
|
||||
QString m_currentTestName;
|
||||
QString m_currentTestSet;
|
||||
QString m_description;
|
||||
QByteArray m_unprocessed;
|
||||
};
|
||||
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTOUTPUTREADER_H
|
168
src/plugins/autotest/testresult.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "testresult.h"
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
FaultyTestResult::FaultyTestResult(Result::Type result, const QString &description)
|
||||
{
|
||||
setResult(result);
|
||||
setDescription(description);
|
||||
}
|
||||
|
||||
TestResult::TestResult()
|
||||
: TestResult(QString())
|
||||
{
|
||||
}
|
||||
|
||||
TestResult::TestResult(const QString &className)
|
||||
: m_class(className)
|
||||
, m_result(Result::Invalid)
|
||||
, m_line(0)
|
||||
, m_type(TestTypeQt)
|
||||
{
|
||||
}
|
||||
|
||||
Result::Type TestResult::resultFromString(const QString &resultString)
|
||||
{
|
||||
if (resultString == QLatin1String("pass"))
|
||||
return Result::Pass;
|
||||
if (resultString == QLatin1String("fail"))
|
||||
return Result::Fail;
|
||||
if (resultString == QLatin1String("xfail"))
|
||||
return Result::ExpectedFail;
|
||||
if (resultString == QLatin1String("xpass"))
|
||||
return Result::UnexpectedPass;
|
||||
if (resultString == QLatin1String("skip"))
|
||||
return Result::Skip;
|
||||
if (resultString == QLatin1String("qdebug"))
|
||||
return Result::MessageDebug;
|
||||
if (resultString == QLatin1String("warn") || resultString == QLatin1String("qwarn"))
|
||||
return Result::MessageWarn;
|
||||
if (resultString == QLatin1String("qfatal"))
|
||||
return Result::MessageFatal;
|
||||
if (resultString == QLatin1String("bpass"))
|
||||
return Result::BlacklistedPass;
|
||||
if (resultString == QLatin1String("bfail"))
|
||||
return Result::BlacklistedFail;
|
||||
qDebug("Unexpected test result: %s", qPrintable(resultString));
|
||||
return Result::Invalid;
|
||||
}
|
||||
|
||||
Result::Type TestResult::toResultType(int rt)
|
||||
{
|
||||
if (rt < Result::FIRST_TYPE || rt > Result::LAST_TYPE)
|
||||
return Result::Invalid;
|
||||
|
||||
return (Result::Type)rt;
|
||||
}
|
||||
|
||||
QString TestResult::resultToString(const Result::Type type)
|
||||
{
|
||||
if (type >= Result::INTERNAL_MESSAGES_BEGIN && type <= Result::INTERNAL_MESSAGES_END)
|
||||
return QString();
|
||||
|
||||
switch (type) {
|
||||
case Result::Pass:
|
||||
return QLatin1String("PASS");
|
||||
case Result::Fail:
|
||||
return QLatin1String("FAIL");
|
||||
case Result::ExpectedFail:
|
||||
return QLatin1String("XFAIL");
|
||||
case Result::UnexpectedPass:
|
||||
return QLatin1String("XPASS");
|
||||
case Result::Skip:
|
||||
return QLatin1String("SKIP");
|
||||
case Result::Benchmark:
|
||||
return QLatin1String("BENCH");
|
||||
case Result::MessageDebug:
|
||||
return QLatin1String("DEBUG");
|
||||
case Result::MessageWarn:
|
||||
return QLatin1String("WARN");
|
||||
case Result::MessageFatal:
|
||||
return QLatin1String("FATAL");
|
||||
case Result::BlacklistedPass:
|
||||
return QLatin1String("BPASS");
|
||||
case Result::BlacklistedFail:
|
||||
return QLatin1String("BFAIL");
|
||||
default:
|
||||
return QLatin1String("UNKNOWN");
|
||||
}
|
||||
}
|
||||
|
||||
QColor TestResult::colorForType(const Result::Type type)
|
||||
{
|
||||
if (type >= Result::INTERNAL_MESSAGES_BEGIN && type <= Result::INTERNAL_MESSAGES_END)
|
||||
return QColor("transparent");
|
||||
|
||||
switch (type) {
|
||||
case Result::Pass:
|
||||
return QColor("#009900");
|
||||
case Result::Fail:
|
||||
return QColor("#a00000");
|
||||
case Result::ExpectedFail:
|
||||
return QColor("#00ff00");
|
||||
case Result::UnexpectedPass:
|
||||
return QColor("#ff0000");
|
||||
case Result::Skip:
|
||||
return QColor("#787878");
|
||||
case Result::BlacklistedPass:
|
||||
return QColor(0, 0, 0);
|
||||
case Result::BlacklistedFail:
|
||||
return QColor(0, 0, 0);
|
||||
case Result::MessageDebug:
|
||||
return QColor("#329696");
|
||||
case Result::MessageWarn:
|
||||
return QColor("#d0bb00");
|
||||
case Result::MessageFatal:
|
||||
return QColor("#640000");
|
||||
default:
|
||||
return QColor("#000000");
|
||||
}
|
||||
}
|
||||
|
||||
bool operator==(const TestResult &t1, const TestResult &t2)
|
||||
{
|
||||
return t1.className() == t2.className()
|
||||
&& t1.testCase() == t2.testCase()
|
||||
&& t1.dataTag() == t2.dataTag()
|
||||
&& t1.result() == t2.result();
|
||||
}
|
||||
|
||||
QTestResult::QTestResult(const QString &className)
|
||||
: TestResult(className)
|
||||
{
|
||||
}
|
||||
|
||||
GTestResult::GTestResult(const QString &className)
|
||||
: TestResult(className)
|
||||
{
|
||||
setTestType(TestTypeGTest);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
132
src/plugins/autotest/testresult.h
Normal file
@@ -0,0 +1,132 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTRESULT_H
|
||||
#define TESTRESULT_H
|
||||
|
||||
#include "autotestconstants.h"
|
||||
|
||||
#include <QString>
|
||||
#include <QColor>
|
||||
#include <QMetaType>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
namespace Result{
|
||||
enum Type {
|
||||
Pass, FIRST_TYPE = Pass,
|
||||
Fail,
|
||||
ExpectedFail,
|
||||
UnexpectedPass,
|
||||
Skip,
|
||||
BlacklistedPass,
|
||||
BlacklistedFail,
|
||||
Benchmark,
|
||||
MessageDebug,
|
||||
MessageWarn,
|
||||
MessageFatal,
|
||||
|
||||
MessageInternal, INTERNAL_MESSAGES_BEGIN = MessageInternal,
|
||||
MessageDisabledTests,
|
||||
MessageTestCaseStart,
|
||||
MessageTestCaseSuccess,
|
||||
MessageTestCaseWarn,
|
||||
MessageTestCaseFail,
|
||||
MessageTestCaseEnd,
|
||||
MessageCurrentTest, INTERNAL_MESSAGES_END = MessageCurrentTest,
|
||||
|
||||
Invalid,
|
||||
LAST_TYPE = Invalid
|
||||
};
|
||||
}
|
||||
|
||||
class TestResult
|
||||
{
|
||||
public:
|
||||
TestResult();
|
||||
TestResult(const QString &className);
|
||||
|
||||
QString className() const { return m_class; }
|
||||
QString testCase() const { return m_case; }
|
||||
QString dataTag() const { return m_dataTag; }
|
||||
Result::Type result() const { return m_result; }
|
||||
QString description() const { return m_description; }
|
||||
QString fileName() const { return m_file; }
|
||||
int line() const { return m_line; }
|
||||
TestType type() const { return m_type; }
|
||||
|
||||
void setDescription(const QString &description) { m_description = description; }
|
||||
void setFileName(const QString &fileName) { m_file = fileName; }
|
||||
void setLine(int line) { m_line = line; }
|
||||
void setResult(Result::Type type) { m_result = type; }
|
||||
void setTestCase(const QString &testCase) { m_case = testCase; }
|
||||
void setDataTag(const QString &dataTag) { m_dataTag = dataTag; }
|
||||
void setTestType(TestType type) { m_type = type; }
|
||||
|
||||
static Result::Type resultFromString(const QString &resultString);
|
||||
static Result::Type toResultType(int rt);
|
||||
static QString resultToString(const Result::Type type);
|
||||
static QColor colorForType(const Result::Type type);
|
||||
|
||||
private:
|
||||
QString m_class;
|
||||
QString m_case;
|
||||
QString m_dataTag;
|
||||
Result::Type m_result;
|
||||
QString m_description;
|
||||
QString m_file;
|
||||
int m_line;
|
||||
TestType m_type;
|
||||
// environment?
|
||||
};
|
||||
|
||||
class FaultyTestResult : public TestResult
|
||||
{
|
||||
public:
|
||||
FaultyTestResult(Result::Type result, const QString &description);
|
||||
};
|
||||
|
||||
class QTestResult : public TestResult
|
||||
{
|
||||
public:
|
||||
QTestResult(const QString &className = QString());
|
||||
};
|
||||
|
||||
class GTestResult : public TestResult
|
||||
{
|
||||
public:
|
||||
GTestResult(const QString &className = QString());
|
||||
};
|
||||
|
||||
bool operator==(const TestResult &t1, const TestResult &t2);
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
Q_DECLARE_METATYPE(Autotest::Internal::TestResult)
|
||||
Q_DECLARE_METATYPE(Autotest::Internal::Result::Type)
|
||||
|
||||
#endif // TESTRESULT_H
|
254
src/plugins/autotest/testresultdelegate.cpp
Normal file
@@ -0,0 +1,254 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "autotestplugin.h"
|
||||
#include "testresultdelegate.h"
|
||||
#include "testresultmodel.h"
|
||||
#include "testsettings.h"
|
||||
|
||||
#include <QAbstractItemView>
|
||||
#include <QDebug>
|
||||
#include <QPainter>
|
||||
#include <QTextLayout>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
const static int outputLimit = 100000;
|
||||
|
||||
TestResultDelegate::TestResultDelegate(QObject *parent)
|
||||
: QStyledItemDelegate(parent)
|
||||
{
|
||||
}
|
||||
|
||||
QString TestResultDelegate::outputString(const TestResult &testResult, bool selected)
|
||||
{
|
||||
const QString desc = testResult.description();
|
||||
QString output;
|
||||
switch (testResult.result()) {
|
||||
case Result::Pass:
|
||||
case Result::Fail:
|
||||
case Result::ExpectedFail:
|
||||
case Result::UnexpectedPass:
|
||||
case Result::BlacklistedFail:
|
||||
case Result::BlacklistedPass:
|
||||
if (testResult.type() == TestTypeQt)
|
||||
output = testResult.className() + QLatin1String("::") + testResult.testCase();
|
||||
else // TestTypeGTest
|
||||
output = testResult.testCase();
|
||||
if (!testResult.dataTag().isEmpty())
|
||||
output.append(QString::fromLatin1(" (%1)").arg(testResult.dataTag()));
|
||||
if (selected && !desc.isEmpty()) {
|
||||
output.append(QLatin1Char('\n')).append(desc);
|
||||
}
|
||||
break;
|
||||
case Result::Benchmark:
|
||||
output = testResult.className() + QLatin1String("::") + testResult.testCase();
|
||||
if (!testResult.dataTag().isEmpty())
|
||||
output.append(QString::fromLatin1(" (%1)").arg(testResult.dataTag()));
|
||||
if (!desc.isEmpty()) {
|
||||
int breakPos = desc.indexOf(QLatin1Char('('));
|
||||
output.append(QLatin1String(": ")).append(desc.left(breakPos));
|
||||
if (selected)
|
||||
output.append(QLatin1Char('\n')).append(desc.mid(breakPos));
|
||||
}
|
||||
break;
|
||||
default:
|
||||
output = desc;
|
||||
if (!selected)
|
||||
output = output.split(QLatin1Char('\n')).first();
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
void TestResultDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QStyleOptionViewItemV4 opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
// make sure we paint the complete delegate instead of keeping an offset
|
||||
opt.rect.adjust(-opt.rect.x(), 0, 0, 0);
|
||||
painter->save();
|
||||
|
||||
QFontMetrics fm(opt.font);
|
||||
QColor foreground;
|
||||
|
||||
const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(opt.widget);
|
||||
const bool selected = view->selectionModel()->currentIndex() == index;
|
||||
|
||||
if (selected) {
|
||||
painter->setBrush(opt.palette.highlight().color());
|
||||
foreground = opt.palette.highlightedText().color();
|
||||
} else {
|
||||
painter->setBrush(opt.palette.background().color());
|
||||
foreground = opt.palette.text().color();
|
||||
}
|
||||
painter->setPen(Qt::NoPen);
|
||||
painter->drawRect(opt.rect);
|
||||
painter->setPen(foreground);
|
||||
|
||||
TestResultFilterModel *resultFilterModel = static_cast<TestResultFilterModel *>(view->model());
|
||||
LayoutPositions positions(opt, resultFilterModel);
|
||||
const TestResult &testResult = resultFilterModel->testResult(index);
|
||||
|
||||
// draw the indicator by ourself as we paint across it with the delegate
|
||||
QStyleOptionViewItemV4 indicatorOpt = option;
|
||||
indicatorOpt.rect = QRect(0, opt.rect.y(), positions.indentation(), opt.rect.height());
|
||||
opt.widget->style()->drawPrimitive(QStyle::PE_IndicatorBranch, &indicatorOpt, painter);
|
||||
|
||||
QIcon icon = index.data(Qt::DecorationRole).value<QIcon>();
|
||||
if (!icon.isNull())
|
||||
painter->drawPixmap(positions.left(), positions.top(),
|
||||
icon.pixmap(positions.iconSize(), positions.iconSize()));
|
||||
|
||||
QString typeStr = TestResult::resultToString(testResult.result());
|
||||
if (selected) {
|
||||
painter->drawText(positions.typeAreaLeft(), positions.top() + fm.ascent(), typeStr);
|
||||
} else {
|
||||
QPen tmp = painter->pen();
|
||||
painter->setPen(TestResult::colorForType(testResult.result()));
|
||||
painter->drawText(positions.typeAreaLeft(), positions.top() + fm.ascent(), typeStr);
|
||||
painter->setPen(tmp);
|
||||
}
|
||||
|
||||
QString output = outputString(testResult, selected);
|
||||
|
||||
if (selected) {
|
||||
output.replace(QLatin1Char('\n'), QChar::LineSeparator);
|
||||
|
||||
if (AutotestPlugin::instance()->settings()->limitResultOutput
|
||||
&& output.length() > outputLimit)
|
||||
output = output.left(outputLimit).append(QLatin1String("..."));
|
||||
|
||||
recalculateTextLayout(index, output, painter->font(), positions.textAreaWidth());
|
||||
|
||||
m_lastCalculatedLayout.draw(painter, QPoint(positions.textAreaLeft(), positions.top()));
|
||||
} else {
|
||||
painter->setClipRect(positions.textArea());
|
||||
// cut output before generating elided text as this takes quite long for exhaustive output
|
||||
painter->drawText(positions.textAreaLeft(), positions.top() + fm.ascent(),
|
||||
fm.elidedText(output.left(2000), Qt::ElideRight, positions.textAreaWidth()));
|
||||
}
|
||||
|
||||
QString file = testResult.fileName();
|
||||
const int pos = file.lastIndexOf(QLatin1Char('/'));
|
||||
if (pos != -1)
|
||||
file = file.mid(pos + 1);
|
||||
|
||||
painter->setClipRect(positions.fileArea());
|
||||
painter->drawText(positions.fileAreaLeft(), positions.top() + fm.ascent(), file);
|
||||
|
||||
|
||||
if (testResult.line()) {
|
||||
QString line = QString::number(testResult.line());
|
||||
painter->setClipRect(positions.lineArea());
|
||||
painter->drawText(positions.lineAreaLeft(), positions.top() + fm.ascent(), line);
|
||||
}
|
||||
|
||||
painter->setClipRect(opt.rect);
|
||||
painter->setPen(opt.palette.midlight().color());
|
||||
painter->drawLine(0, opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
QSize TestResultDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QStyleOptionViewItemV4 opt = option;
|
||||
// make sure opt.rect is initialized correctly - otherwise we might get a width of 0
|
||||
opt.initFrom(opt.widget);
|
||||
initStyleOption(&opt, index);
|
||||
|
||||
const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(opt.widget);
|
||||
const bool selected = view->selectionModel()->currentIndex() == index;
|
||||
|
||||
QFontMetrics fm(opt.font);
|
||||
int fontHeight = fm.height();
|
||||
TestResultFilterModel *resultFilterModel = static_cast<TestResultFilterModel *>(view->model());
|
||||
LayoutPositions positions(opt, resultFilterModel);
|
||||
QSize s;
|
||||
s.setWidth(opt.rect.width());
|
||||
|
||||
if (selected) {
|
||||
const TestResult &testResult = resultFilterModel->testResult(index);
|
||||
|
||||
QString output = outputString(testResult, selected);
|
||||
output.replace(QLatin1Char('\n'), QChar::LineSeparator);
|
||||
|
||||
if (AutotestPlugin::instance()->settings()->limitResultOutput
|
||||
&& output.length() > outputLimit)
|
||||
output = output.left(outputLimit).append(QLatin1String("..."));
|
||||
|
||||
recalculateTextLayout(index, output, opt.font, positions.textAreaWidth());
|
||||
|
||||
s.setHeight(m_lastCalculatedHeight + 3);
|
||||
} else {
|
||||
s.setHeight(fontHeight + 3);
|
||||
}
|
||||
|
||||
if (s.height() < positions.minimumHeight())
|
||||
s.setHeight(positions.minimumHeight());
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
void TestResultDelegate::currentChanged(const QModelIndex ¤t, const QModelIndex &previous)
|
||||
{
|
||||
emit sizeHintChanged(current);
|
||||
emit sizeHintChanged(previous);
|
||||
}
|
||||
|
||||
void TestResultDelegate::recalculateTextLayout(const QModelIndex &index, const QString &output,
|
||||
const QFont &font, int width) const
|
||||
{
|
||||
if (m_lastProcessedIndex == index && m_lastProcessedFont == font)
|
||||
return;
|
||||
|
||||
const QFontMetrics fm(font);
|
||||
const int leading = fm.leading();
|
||||
const int fontHeight = fm.height();
|
||||
|
||||
m_lastProcessedIndex = index;
|
||||
m_lastProcessedFont = font;
|
||||
m_lastCalculatedHeight = 0;
|
||||
m_lastCalculatedLayout.clearLayout();
|
||||
m_lastCalculatedLayout.setText(output);
|
||||
m_lastCalculatedLayout.setFont(font);
|
||||
QTextOption txtOption;
|
||||
txtOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
|
||||
m_lastCalculatedLayout.setTextOption(txtOption);
|
||||
m_lastCalculatedLayout.beginLayout();
|
||||
while (true) {
|
||||
QTextLine line = m_lastCalculatedLayout.createLine();
|
||||
if (!line.isValid())
|
||||
break;
|
||||
line.setLineWidth(width);
|
||||
m_lastCalculatedHeight += leading;
|
||||
line.setPosition(QPoint(0, m_lastCalculatedHeight));
|
||||
m_lastCalculatedHeight += fontHeight;
|
||||
}
|
||||
m_lastCalculatedLayout.endLayout();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
131
src/plugins/autotest/testresultdelegate.h
Normal file
@@ -0,0 +1,131 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTRESULTDELEGATE_H
|
||||
#define TESTRESULTDELEGATE_H
|
||||
|
||||
#include "testresultmodel.h"
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
#include <QTextLayout>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestResultDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TestResultDelegate(QObject *parent = 0);
|
||||
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
|
||||
static QString outputString(const TestResult &testResult, bool selected);
|
||||
|
||||
public slots:
|
||||
void currentChanged(const QModelIndex ¤t, const QModelIndex &previous);
|
||||
|
||||
private:
|
||||
void recalculateTextLayout(const QModelIndex &index, const QString &output,
|
||||
const QFont &font, int width) const;
|
||||
|
||||
mutable QModelIndex m_lastProcessedIndex;
|
||||
mutable QFont m_lastProcessedFont;
|
||||
mutable QTextLayout m_lastCalculatedLayout;
|
||||
mutable int m_lastCalculatedHeight;
|
||||
|
||||
class LayoutPositions
|
||||
{
|
||||
public:
|
||||
LayoutPositions(QStyleOptionViewItemV4 &options, TestResultFilterModel *filterModel)
|
||||
: m_totalWidth(options.rect.width()),
|
||||
m_top(options.rect.top()),
|
||||
m_bottom(options.rect.bottom())
|
||||
{
|
||||
TestResultModel *srcModel = static_cast<TestResultModel *>(filterModel->sourceModel());
|
||||
m_maxFileLength = srcModel->maxWidthOfFileName(options.font);
|
||||
m_maxLineLength = srcModel->maxWidthOfLineNumber(options.font);
|
||||
m_realFileLength = m_maxFileLength;
|
||||
m_typeAreaWidth = QFontMetrics(options.font).width(QLatin1String("XXXXXXXX"));
|
||||
m_indentation = options.widget ? options.widget->style()->pixelMetric(
|
||||
QStyle::PM_TreeViewIndentation, &options) : 0;
|
||||
m_level = srcModel->hasChildren(filterModel->mapToSource(options.index)) ? 1 : 2;
|
||||
int flexibleArea = lineAreaLeft() - textAreaLeft() - ITEM_SPACING;
|
||||
if (m_maxFileLength > flexibleArea / 2)
|
||||
m_realFileLength = flexibleArea / 2;
|
||||
m_fontHeight = QFontMetrics(options.font).height();
|
||||
}
|
||||
|
||||
int top() const { return m_top + ITEM_MARGIN; }
|
||||
int left() const { return ITEM_MARGIN + m_indentation * m_level; }
|
||||
int right() const { return m_totalWidth - ITEM_MARGIN; }
|
||||
int bottom() const { return m_bottom; }
|
||||
int minimumHeight() const { return ICON_SIZE + 2 * ITEM_MARGIN; }
|
||||
|
||||
int iconSize() const { return ICON_SIZE; }
|
||||
int fontHeight() const { return m_fontHeight; }
|
||||
int typeAreaLeft() const { return left() + ICON_SIZE + ITEM_SPACING; }
|
||||
int typeAreaWidth() const { return m_typeAreaWidth; }
|
||||
int textAreaLeft() const { return typeAreaLeft() + m_typeAreaWidth + ITEM_SPACING
|
||||
+ (1 - m_level) * m_indentation; }
|
||||
int textAreaWidth() const { return fileAreaLeft() - ITEM_SPACING - textAreaLeft(); }
|
||||
int fileAreaLeft() const { return lineAreaLeft() - ITEM_SPACING - m_realFileLength; }
|
||||
int lineAreaLeft() const { return right() - m_maxLineLength; }
|
||||
int indentation() const { return m_indentation; }
|
||||
|
||||
QRect typeArea() const { return QRect(typeAreaLeft(), top(),
|
||||
typeAreaWidth(), m_fontHeight); }
|
||||
QRect textArea() const { return QRect(textAreaLeft(), top(),
|
||||
textAreaWidth(), m_fontHeight); }
|
||||
QRect fileArea() const { return QRect(fileAreaLeft(), top(),
|
||||
m_realFileLength + ITEM_SPACING, m_fontHeight); }
|
||||
|
||||
QRect lineArea() const { return QRect(lineAreaLeft(), top(),
|
||||
m_maxLineLength, m_fontHeight); }
|
||||
|
||||
private:
|
||||
int m_totalWidth;
|
||||
int m_maxFileLength;
|
||||
int m_maxLineLength;
|
||||
int m_realFileLength;
|
||||
int m_top;
|
||||
int m_bottom;
|
||||
int m_fontHeight;
|
||||
int m_typeAreaWidth;
|
||||
int m_level;
|
||||
int m_indentation;
|
||||
|
||||
static const int ICON_SIZE = 16;
|
||||
static const int ITEM_MARGIN = 2;
|
||||
static const int ITEM_SPACING = 4;
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTRESULTDELEGATE_H
|
337
src/plugins/autotest/testresultmodel.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "testresultmodel.h"
|
||||
|
||||
#include <QFontMetrics>
|
||||
#include <QIcon>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
/********************************* TestResultItem ******************************************/
|
||||
|
||||
TestResultItem::TestResultItem(TestResult *testResult)
|
||||
: m_testResult(testResult)
|
||||
{
|
||||
}
|
||||
|
||||
TestResultItem::~TestResultItem()
|
||||
{
|
||||
delete m_testResult;
|
||||
}
|
||||
|
||||
static QIcon testResultIcon(Result::Type result) {
|
||||
static QIcon icons[11] = {
|
||||
QIcon(QLatin1String(":/images/pass.png")),
|
||||
QIcon(QLatin1String(":/images/fail.png")),
|
||||
QIcon(QLatin1String(":/images/xfail.png")),
|
||||
QIcon(QLatin1String(":/images/xpass.png")),
|
||||
QIcon(QLatin1String(":/images/skip.png")),
|
||||
QIcon(QLatin1String(":/images/blacklisted_pass.png")),
|
||||
QIcon(QLatin1String(":/images/blacklisted_fail.png")),
|
||||
QIcon(QLatin1String(":/images/benchmark.png")),
|
||||
QIcon(QLatin1String(":/images/debug.png")),
|
||||
QIcon(QLatin1String(":/images/warn.png")),
|
||||
QIcon(QLatin1String(":/images/fatal.png")),
|
||||
}; // provide an icon for unknown??
|
||||
|
||||
if (result < 0 || result >= Result::MessageInternal) {
|
||||
switch (result) {
|
||||
case Result::MessageTestCaseSuccess:
|
||||
return icons[Result::Pass];
|
||||
case Result::MessageTestCaseFail:
|
||||
return icons[Result::Fail];
|
||||
case Result::MessageTestCaseWarn:
|
||||
return icons[Result::MessageWarn];
|
||||
default:
|
||||
return QIcon();
|
||||
}
|
||||
}
|
||||
return icons[result];
|
||||
}
|
||||
|
||||
QVariant TestResultItem::data(int column, int role) const
|
||||
{
|
||||
if (role == Qt::DecorationRole)
|
||||
return m_testResult ? testResultIcon(m_testResult->result()) : QVariant();
|
||||
|
||||
return Utils::TreeItem::data(column, role);
|
||||
}
|
||||
|
||||
void TestResultItem::updateDescription(const QString &description)
|
||||
{
|
||||
QTC_ASSERT(m_testResult, return);
|
||||
|
||||
m_testResult->setDescription(description);
|
||||
}
|
||||
|
||||
void TestResultItem::updateResult()
|
||||
{
|
||||
if (m_testResult->result() != Result::MessageTestCaseStart)
|
||||
return;
|
||||
|
||||
Result::Type newResult = Result::MessageTestCaseSuccess;
|
||||
foreach (Utils::TreeItem *child, children()) {
|
||||
const TestResult *current = static_cast<TestResultItem *>(child)->testResult();
|
||||
if (current) {
|
||||
switch (current->result()) {
|
||||
case Result::Fail:
|
||||
case Result::MessageFatal:
|
||||
case Result::UnexpectedPass:
|
||||
m_testResult->setResult(Result::MessageTestCaseFail);
|
||||
return;
|
||||
case Result::ExpectedFail:
|
||||
case Result::MessageWarn:
|
||||
case Result::Skip:
|
||||
case Result::BlacklistedFail:
|
||||
case Result::BlacklistedPass:
|
||||
newResult = Result::MessageTestCaseWarn;
|
||||
break;
|
||||
default: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
m_testResult->setResult(newResult);
|
||||
}
|
||||
|
||||
/********************************* TestResultModel *****************************************/
|
||||
|
||||
TestResultModel::TestResultModel(QObject *parent)
|
||||
: Utils::TreeModel(parent),
|
||||
m_widthOfLineNumber(0),
|
||||
m_maxWidthOfFileName(0),
|
||||
m_disabled(0)
|
||||
{
|
||||
}
|
||||
|
||||
QVariant TestResultModel::data(const QModelIndex &idx, int role) const
|
||||
{
|
||||
if (!idx.isValid())
|
||||
return QVariant();
|
||||
|
||||
if (role == Qt::DecorationRole)
|
||||
return itemForIndex(idx)->data(0, Qt::DecorationRole);
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
void TestResultModel::addTestResult(TestResult *testResult, bool autoExpand)
|
||||
{
|
||||
const bool isCurrentTestMssg = testResult->result() == Result::MessageCurrentTest;
|
||||
|
||||
QVector<Utils::TreeItem *> topLevelItems = rootItem()->children();
|
||||
int lastRow = topLevelItems.size() - 1;
|
||||
// we'll add the new item, so raising it's counter
|
||||
if (!isCurrentTestMssg) {
|
||||
int count = m_testResultCount.value(testResult->result(), 0);
|
||||
if (testResult->result() == Result::MessageDisabledTests)
|
||||
m_disabled += testResult->line();
|
||||
m_testResultCount.insert(testResult->result(), ++count);
|
||||
} else {
|
||||
// MessageCurrentTest should always be the last top level item
|
||||
if (lastRow >= 0) {
|
||||
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(lastRow));
|
||||
const TestResult *result = current->testResult();
|
||||
if (result && result->result() == Result::MessageCurrentTest) {
|
||||
current->updateDescription(testResult->description());
|
||||
emit dataChanged(current->index(), current->index());
|
||||
delete testResult;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rootItem()->appendChild(new TestResultItem(testResult));
|
||||
return;
|
||||
}
|
||||
|
||||
TestResultItem *newItem = new TestResultItem(testResult);
|
||||
// FIXME this might be totally wrong... we need some more unique information!
|
||||
if (!testResult->className().isEmpty()) {
|
||||
for (int row = lastRow; row >= 0; --row) {
|
||||
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(row));
|
||||
const TestResult *result = current->testResult();
|
||||
if (result && result->className() == testResult->className()) {
|
||||
current->appendChild(newItem);
|
||||
if (autoExpand)
|
||||
current->expand();
|
||||
if (testResult->result() == Result::MessageTestCaseEnd) {
|
||||
current->updateResult();
|
||||
emit dataChanged(current->index(), current->index());
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// if we have a MessageCurrentTest present, add the new top level item before it
|
||||
if (lastRow >= 0) {
|
||||
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(lastRow));
|
||||
const TestResult *result = current->testResult();
|
||||
if (result && result->result() == Result::MessageCurrentTest) {
|
||||
rootItem()->insertChild(current->index().row(), newItem);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
rootItem()->appendChild(newItem);
|
||||
}
|
||||
|
||||
void TestResultModel::removeCurrentTestMessage()
|
||||
{
|
||||
QVector<Utils::TreeItem *> topLevelItems = rootItem()->children();
|
||||
for (int row = topLevelItems.size() - 1; row >= 0; --row) {
|
||||
TestResultItem *current = static_cast<TestResultItem *>(topLevelItems.at(row));
|
||||
if (current->testResult()->result() == Result::MessageCurrentTest) {
|
||||
delete takeItem(current);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestResultModel::clearTestResults()
|
||||
{
|
||||
clear();
|
||||
m_testResultCount.clear();
|
||||
m_disabled = 0;
|
||||
m_processedIndices.clear();
|
||||
m_maxWidthOfFileName = 0;
|
||||
m_widthOfLineNumber = 0;
|
||||
}
|
||||
|
||||
TestResult TestResultModel::testResult(const QModelIndex &idx)
|
||||
{
|
||||
if (idx.isValid())
|
||||
return *(static_cast<TestResultItem *>(itemForIndex(idx))->testResult());
|
||||
|
||||
return TestResult();
|
||||
}
|
||||
|
||||
int TestResultModel::maxWidthOfFileName(const QFont &font)
|
||||
{
|
||||
if (font != m_measurementFont) {
|
||||
m_processedIndices.clear();
|
||||
m_maxWidthOfFileName = 0;
|
||||
m_measurementFont = font;
|
||||
}
|
||||
|
||||
const QFontMetrics fm(font);
|
||||
const QVector<Utils::TreeItem *> &topLevelItems = rootItem()->children();
|
||||
const int count = topLevelItems.size();
|
||||
for (int row = 0; row < count; ++row) {
|
||||
int processed = row < m_processedIndices.size() ? m_processedIndices.at(row) : 0;
|
||||
const QVector<Utils::TreeItem *> &children = topLevelItems.at(row)->children();
|
||||
const int itemCount = children.size();
|
||||
if (processed < itemCount) {
|
||||
for (int childRow = processed; childRow < itemCount; ++childRow) {
|
||||
const TestResultItem *item = static_cast<TestResultItem *>(children.at(childRow));
|
||||
if (const TestResult *result = item->testResult()) {
|
||||
QString fileName = result->fileName();
|
||||
const int pos = fileName.lastIndexOf(QLatin1Char('/'));
|
||||
if (pos != -1)
|
||||
fileName = fileName.mid(pos + 1);
|
||||
m_maxWidthOfFileName = qMax(m_maxWidthOfFileName, fm.width(fileName));
|
||||
}
|
||||
}
|
||||
if (row < m_processedIndices.size())
|
||||
m_processedIndices.replace(row, itemCount);
|
||||
else
|
||||
m_processedIndices.insert(row, itemCount);
|
||||
}
|
||||
}
|
||||
return m_maxWidthOfFileName;
|
||||
}
|
||||
|
||||
int TestResultModel::maxWidthOfLineNumber(const QFont &font)
|
||||
{
|
||||
if (m_widthOfLineNumber == 0 || font != m_measurementFont) {
|
||||
QFontMetrics fm(font);
|
||||
m_measurementFont = font;
|
||||
m_widthOfLineNumber = fm.width(QLatin1String("88888"));
|
||||
}
|
||||
return m_widthOfLineNumber;
|
||||
}
|
||||
|
||||
/********************************** Filter Model **********************************/
|
||||
|
||||
TestResultFilterModel::TestResultFilterModel(TestResultModel *sourceModel, QObject *parent)
|
||||
: QSortFilterProxyModel(parent),
|
||||
m_sourceModel(sourceModel)
|
||||
{
|
||||
setSourceModel(sourceModel);
|
||||
enableAllResultTypes();
|
||||
}
|
||||
|
||||
void TestResultFilterModel::enableAllResultTypes()
|
||||
{
|
||||
m_enabled << Result::Pass << Result::Fail << Result::ExpectedFail
|
||||
<< Result::UnexpectedPass << Result::Skip << Result::MessageDebug
|
||||
<< Result::MessageWarn << Result::MessageInternal
|
||||
<< Result::MessageFatal << Result::Invalid << Result::BlacklistedPass
|
||||
<< Result::BlacklistedFail << Result::Benchmark
|
||||
<< Result::MessageCurrentTest << Result::MessageTestCaseStart
|
||||
<< Result::MessageTestCaseSuccess << Result::MessageTestCaseWarn
|
||||
<< Result::MessageTestCaseFail << Result::MessageTestCaseEnd;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void TestResultFilterModel::toggleTestResultType(Result::Type type)
|
||||
{
|
||||
if (m_enabled.contains(type)) {
|
||||
m_enabled.remove(type);
|
||||
if (type == Result::MessageInternal)
|
||||
m_enabled.remove(Result::MessageTestCaseEnd);
|
||||
} else {
|
||||
m_enabled.insert(type);
|
||||
if (type == Result::MessageInternal)
|
||||
m_enabled.insert(Result::MessageTestCaseEnd);
|
||||
}
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void TestResultFilterModel::clearTestResults()
|
||||
{
|
||||
m_sourceModel->clearTestResults();
|
||||
}
|
||||
|
||||
bool TestResultFilterModel::hasResults()
|
||||
{
|
||||
return rowCount(QModelIndex());
|
||||
}
|
||||
|
||||
TestResult TestResultFilterModel::testResult(const QModelIndex &index) const
|
||||
{
|
||||
return m_sourceModel->testResult(mapToSource(index));
|
||||
}
|
||||
|
||||
bool TestResultFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
QModelIndex index = m_sourceModel->index(sourceRow, 0, sourceParent);
|
||||
if (!index.isValid())
|
||||
return false;
|
||||
return m_enabled.contains(m_sourceModel->testResult(index).result());
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
105
src/plugins/autotest/testresultmodel.h
Normal file
@@ -0,0 +1,105 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTRESULTMODEL_H
|
||||
#define TESTRESULTMODEL_H
|
||||
|
||||
#include "testresult.h"
|
||||
|
||||
#include <QAbstractItemModel>
|
||||
#include <QSortFilterProxyModel>
|
||||
#include <QFont>
|
||||
#include <QSet>
|
||||
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestResultItem : public Utils::TreeItem
|
||||
{
|
||||
public:
|
||||
explicit TestResultItem(TestResult *testResult);
|
||||
~TestResultItem();
|
||||
QVariant data(int column, int role) const;
|
||||
const TestResult *testResult() const { return m_testResult; }
|
||||
void updateDescription(const QString &description);
|
||||
void updateResult();
|
||||
|
||||
private:
|
||||
TestResult *m_testResult;
|
||||
};
|
||||
|
||||
class TestResultModel : public Utils::TreeModel
|
||||
{
|
||||
public:
|
||||
explicit TestResultModel(QObject *parent = 0);
|
||||
QVariant data(const QModelIndex &idx, int role) const;
|
||||
|
||||
void addTestResult(TestResult *testResult, bool autoExpand = false);
|
||||
void removeCurrentTestMessage();
|
||||
void clearTestResults();
|
||||
|
||||
TestResult testResult(const QModelIndex &idx);
|
||||
|
||||
int maxWidthOfFileName(const QFont &font);
|
||||
int maxWidthOfLineNumber(const QFont &font);
|
||||
|
||||
int resultTypeCount(Result::Type type) const { return m_testResultCount.value(type, 0); }
|
||||
int disabledTests() const { return m_disabled; }
|
||||
|
||||
private:
|
||||
QMap<Result::Type, int> m_testResultCount;
|
||||
int m_widthOfLineNumber;
|
||||
int m_maxWidthOfFileName;
|
||||
int m_disabled;
|
||||
QList<int> m_processedIndices;
|
||||
QFont m_measurementFont;
|
||||
};
|
||||
|
||||
class TestResultFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestResultFilterModel(TestResultModel *sourceModel, QObject *parent = 0);
|
||||
|
||||
void enableAllResultTypes();
|
||||
void toggleTestResultType(Result::Type type);
|
||||
void clearTestResults();
|
||||
bool hasResults();
|
||||
TestResult testResult(const QModelIndex &index) const;
|
||||
|
||||
protected:
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
||||
|
||||
private:
|
||||
TestResultModel *m_sourceModel;
|
||||
QSet<Result::Type> m_enabled;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTRESULTMODEL_H
|
581
src/plugins/autotest/testresultspane.cpp
Normal file
@@ -0,0 +1,581 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "autotestplugin.h"
|
||||
#include "autotesticons.h"
|
||||
#include "testresultspane.h"
|
||||
#include "testresultmodel.h"
|
||||
#include "testresultdelegate.h"
|
||||
#include "testrunner.h"
|
||||
#include "testsettings.h"
|
||||
#include "testtreemodel.h"
|
||||
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/coreicons.h>
|
||||
#include <coreplugin/editormanager/editormanager.h>
|
||||
#include <coreplugin/icontext.h>
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <projectexplorer/projectexplorericons.h>
|
||||
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <utils/theme/theme.h>
|
||||
|
||||
#include <QApplication>
|
||||
#include <QClipboard>
|
||||
#include <QDebug>
|
||||
#include <QFileDialog>
|
||||
#include <QHBoxLayout>
|
||||
#include <QMenu>
|
||||
#include <QMessageBox>
|
||||
#include <QScrollBar>
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
ResultsTreeView::ResultsTreeView(QWidget *parent)
|
||||
: Utils::TreeView(parent)
|
||||
{
|
||||
setAttribute(Qt::WA_MacShowFocusRect, false);
|
||||
}
|
||||
|
||||
void ResultsTreeView::keyPressEvent(QKeyEvent *event)
|
||||
{
|
||||
if (event->matches(QKeySequence::Copy)) {
|
||||
emit copyShortcutTriggered();
|
||||
event->accept();
|
||||
}
|
||||
TreeView::keyPressEvent(event);
|
||||
}
|
||||
|
||||
TestResultsPane::TestResultsPane(QObject *parent) :
|
||||
Core::IOutputPane(parent),
|
||||
m_context(new Core::IContext(this)),
|
||||
m_wasVisibleBefore(false),
|
||||
m_autoScroll(false),
|
||||
m_testRunning(false)
|
||||
{
|
||||
m_outputWidget = new QWidget;
|
||||
QVBoxLayout *outputLayout = new QVBoxLayout;
|
||||
outputLayout->setMargin(0);
|
||||
outputLayout->setSpacing(0);
|
||||
m_outputWidget->setLayout(outputLayout);
|
||||
|
||||
QPalette pal;
|
||||
pal.setColor(QPalette::Window,
|
||||
Utils::creatorTheme()->color(Utils::Theme::InfoBarBackground));
|
||||
pal.setColor(QPalette::WindowText,
|
||||
Utils::creatorTheme()->color(Utils::Theme::InfoBarText));
|
||||
m_summaryWidget = new QFrame;
|
||||
m_summaryWidget->setPalette(pal);
|
||||
m_summaryWidget->setAutoFillBackground(true);
|
||||
QHBoxLayout *layout = new QHBoxLayout;
|
||||
layout->setMargin(6);
|
||||
m_summaryWidget->setLayout(layout);
|
||||
m_summaryLabel = new QLabel;
|
||||
m_summaryLabel->setPalette(pal);
|
||||
layout->addWidget(m_summaryLabel);
|
||||
m_summaryWidget->setVisible(false);
|
||||
|
||||
outputLayout->addWidget(m_summaryWidget);
|
||||
|
||||
m_treeView = new ResultsTreeView(m_outputWidget);
|
||||
m_treeView->setHeaderHidden(true);
|
||||
m_treeView->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel);
|
||||
m_treeView->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||
m_model = new TestResultModel(this);
|
||||
m_filterModel = new TestResultFilterModel(m_model, this);
|
||||
m_filterModel->setDynamicSortFilter(true);
|
||||
m_treeView->setModel(m_filterModel);
|
||||
TestResultDelegate *trd = new TestResultDelegate(this);
|
||||
m_treeView->setItemDelegate(trd);
|
||||
|
||||
outputLayout->addWidget(m_treeView);
|
||||
|
||||
createToolButtons();
|
||||
|
||||
connect(m_treeView, &Utils::TreeView::activated, this, &TestResultsPane::onItemActivated);
|
||||
connect(m_treeView->selectionModel(), &QItemSelectionModel::currentChanged,
|
||||
trd, &TestResultDelegate::currentChanged);
|
||||
connect(m_treeView, &Utils::TreeView::customContextMenuRequested,
|
||||
this, &TestResultsPane::onCustomContextMenuRequested);
|
||||
connect(m_treeView, &ResultsTreeView::copyShortcutTriggered, [this] () {
|
||||
onCopyItemTriggered(m_treeView->currentIndex());
|
||||
});
|
||||
connect(m_model, &TestResultModel::requestExpansion, [this] (QModelIndex idx) {
|
||||
m_treeView->expand(m_filterModel->mapFromSource(idx));
|
||||
});
|
||||
connect(TestRunner::instance(), &TestRunner::testRunStarted,
|
||||
this, &TestResultsPane::onTestRunStarted);
|
||||
connect(TestRunner::instance(), &TestRunner::testRunFinished,
|
||||
this, &TestResultsPane::onTestRunFinished);
|
||||
connect(TestRunner::instance(), &TestRunner::testResultReady,
|
||||
this, &TestResultsPane::addTestResult);
|
||||
}
|
||||
|
||||
void TestResultsPane::createToolButtons()
|
||||
{
|
||||
m_expandCollapse = new QToolButton(m_treeView);
|
||||
m_expandCollapse->setIcon(Core::Icons::EXPAND.icon());
|
||||
m_expandCollapse->setToolTip(tr("Expand All"));
|
||||
m_expandCollapse->setCheckable(true);
|
||||
m_expandCollapse->setChecked(false);
|
||||
connect(m_expandCollapse, &QToolButton::clicked, [this] (bool checked) {
|
||||
if (checked)
|
||||
m_treeView->expandAll();
|
||||
else
|
||||
m_treeView->collapseAll();
|
||||
});
|
||||
|
||||
m_runAll = new QToolButton(m_treeView);
|
||||
m_runAll->setIcon(ProjectExplorer::Icons::RUN_SMALL.icon());
|
||||
m_runAll->setToolTip(tr("Run All Tests"));
|
||||
m_runAll->setEnabled(false);
|
||||
connect(m_runAll, &QToolButton::clicked, this, &TestResultsPane::onRunAllTriggered);
|
||||
|
||||
m_runSelected = new QToolButton(m_treeView);
|
||||
Utils::Icon runSelectedIcon = ProjectExplorer::Icons::RUN_SMALL;
|
||||
foreach (const Utils::IconMaskAndColor &maskAndColor, Icons::RUN_SELECTED_OVERLAY)
|
||||
runSelectedIcon.append(maskAndColor);
|
||||
m_runSelected->setIcon(runSelectedIcon.icon());
|
||||
m_runSelected->setToolTip(tr("Run Selected Tests"));
|
||||
m_runSelected->setEnabled(false);
|
||||
connect(m_runSelected, &QToolButton::clicked, this, &TestResultsPane::onRunSelectedTriggered);
|
||||
|
||||
m_stopTestRun = new QToolButton(m_treeView);
|
||||
m_stopTestRun->setIcon(ProjectExplorer::Icons::STOP_SMALL.icon());
|
||||
m_stopTestRun->setToolTip(tr("Stop Test Run"));
|
||||
m_stopTestRun->setEnabled(false);
|
||||
connect(m_stopTestRun, &QToolButton::clicked, TestRunner::instance(), &TestRunner::requestStopTestRun);
|
||||
|
||||
m_filterButton = new QToolButton(m_treeView);
|
||||
m_filterButton->setIcon(Core::Icons::FILTER.icon());
|
||||
m_filterButton->setToolTip(tr("Filter Test Results"));
|
||||
m_filterButton->setProperty("noArrow", true);
|
||||
m_filterButton->setAutoRaise(true);
|
||||
m_filterButton->setPopupMode(QToolButton::InstantPopup);
|
||||
m_filterMenu = new QMenu(m_filterButton);
|
||||
initializeFilterMenu();
|
||||
connect(m_filterMenu, &QMenu::triggered, this, &TestResultsPane::filterMenuTriggered);
|
||||
m_filterButton->setMenu(m_filterMenu);
|
||||
}
|
||||
|
||||
static TestResultsPane *m_instance = 0;
|
||||
|
||||
TestResultsPane *TestResultsPane::instance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new TestResultsPane;
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
TestResultsPane::~TestResultsPane()
|
||||
{
|
||||
delete m_treeView;
|
||||
m_instance = 0;
|
||||
}
|
||||
|
||||
void TestResultsPane::addTestResult(TestResult *result)
|
||||
{
|
||||
const QScrollBar *scrollBar = m_treeView->verticalScrollBar();
|
||||
m_atEnd = scrollBar ? scrollBar->value() == scrollBar->maximum() : true;
|
||||
|
||||
m_model->addTestResult(result, m_expandCollapse->isChecked());
|
||||
if (!m_treeView->isVisible())
|
||||
popup(Core::IOutputPane::NoModeSwitch);
|
||||
flash();
|
||||
navigateStateChanged();
|
||||
}
|
||||
|
||||
QWidget *TestResultsPane::outputWidget(QWidget *parent)
|
||||
{
|
||||
if (m_outputWidget) {
|
||||
m_outputWidget->setParent(parent);
|
||||
} else {
|
||||
qDebug() << "This should not happen...";
|
||||
}
|
||||
return m_outputWidget;
|
||||
}
|
||||
|
||||
QList<QWidget *> TestResultsPane::toolBarWidgets() const
|
||||
{
|
||||
return QList<QWidget *>() << m_expandCollapse << m_runAll << m_runSelected << m_stopTestRun
|
||||
<< m_filterButton;
|
||||
}
|
||||
|
||||
QString TestResultsPane::displayName() const
|
||||
{
|
||||
return tr("Test Results");
|
||||
}
|
||||
|
||||
int TestResultsPane::priorityInStatusBar() const
|
||||
{
|
||||
return -666;
|
||||
}
|
||||
|
||||
void TestResultsPane::clearContents()
|
||||
{
|
||||
m_filterModel->clearTestResults();
|
||||
navigateStateChanged();
|
||||
m_summaryWidget->setVisible(false);
|
||||
m_autoScroll = AutotestPlugin::instance()->settings()->autoScroll;
|
||||
connect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged,
|
||||
this, &TestResultsPane::onScrollBarRangeChanged, Qt::UniqueConnection);
|
||||
}
|
||||
|
||||
void TestResultsPane::visibilityChanged(bool visible)
|
||||
{
|
||||
if (visible == m_wasVisibleBefore)
|
||||
return;
|
||||
if (visible) {
|
||||
connect(TestTreeModel::instance(), &TestTreeModel::testTreeModelChanged,
|
||||
this, &TestResultsPane::onTestTreeModelChanged);
|
||||
// make sure run/run all are in correct state
|
||||
onTestTreeModelChanged();
|
||||
TestTreeModel::instance()->enableParsing();
|
||||
} else {
|
||||
disconnect(TestTreeModel::instance(), &TestTreeModel::testTreeModelChanged,
|
||||
this, &TestResultsPane::onTestTreeModelChanged);
|
||||
TestTreeModel::instance()->disableParsing();
|
||||
}
|
||||
m_wasVisibleBefore = visible;
|
||||
}
|
||||
|
||||
void TestResultsPane::setFocus()
|
||||
{
|
||||
}
|
||||
|
||||
bool TestResultsPane::hasFocus() const
|
||||
{
|
||||
return m_treeView->hasFocus();
|
||||
}
|
||||
|
||||
bool TestResultsPane::canFocus() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestResultsPane::canNavigate() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestResultsPane::canNext() const
|
||||
{
|
||||
return m_filterModel->hasResults();
|
||||
}
|
||||
|
||||
bool TestResultsPane::canPrevious() const
|
||||
{
|
||||
return m_filterModel->hasResults();
|
||||
}
|
||||
|
||||
void TestResultsPane::goToNext()
|
||||
{
|
||||
if (!canNext())
|
||||
return;
|
||||
|
||||
const QModelIndex currentIndex = m_treeView->currentIndex();
|
||||
QModelIndex nextCurrentIndex;
|
||||
|
||||
if (currentIndex.isValid()) {
|
||||
// try to set next to first child or next sibling
|
||||
if (m_filterModel->rowCount(currentIndex)) {
|
||||
nextCurrentIndex = currentIndex.child(0, 0);
|
||||
} else {
|
||||
nextCurrentIndex = currentIndex.sibling(currentIndex.row() + 1, 0);
|
||||
// if it had no sibling check siblings of parent (and grandparents if necessary)
|
||||
if (!nextCurrentIndex.isValid()) {
|
||||
|
||||
QModelIndex parent = currentIndex.parent();
|
||||
do {
|
||||
if (!parent.isValid())
|
||||
break;
|
||||
nextCurrentIndex = parent.sibling(parent.row() + 1, 0);
|
||||
parent = parent.parent();
|
||||
} while (!nextCurrentIndex.isValid());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if we have no current or could not find a next one, use the first item of the whole tree
|
||||
if (!nextCurrentIndex.isValid()) {
|
||||
Utils::TreeItem *rootItem = m_model->itemForIndex(QModelIndex());
|
||||
// if the tree does not contain any item - don't do anything
|
||||
if (!rootItem || !rootItem->childCount())
|
||||
return;
|
||||
|
||||
nextCurrentIndex = m_filterModel->mapFromSource(m_model->indexForItem(rootItem->child(0)));
|
||||
}
|
||||
|
||||
m_treeView->setCurrentIndex(nextCurrentIndex);
|
||||
onItemActivated(nextCurrentIndex);
|
||||
}
|
||||
|
||||
void TestResultsPane::goToPrev()
|
||||
{
|
||||
if (!canPrevious())
|
||||
return;
|
||||
|
||||
const QModelIndex currentIndex = m_treeView->currentIndex();
|
||||
QModelIndex nextCurrentIndex;
|
||||
|
||||
if (currentIndex.isValid()) {
|
||||
// try to set next to prior sibling or parent
|
||||
if (currentIndex.row() > 0) {
|
||||
nextCurrentIndex = currentIndex.sibling(currentIndex.row() - 1, 0);
|
||||
// if the sibling has children, use the last one
|
||||
while (int rowCount = m_filterModel->rowCount(nextCurrentIndex))
|
||||
nextCurrentIndex = nextCurrentIndex.child(rowCount - 1, 0);
|
||||
} else {
|
||||
nextCurrentIndex = currentIndex.parent();
|
||||
}
|
||||
}
|
||||
|
||||
// if we have no current or didn't find a sibling/parent use the last item of the whole tree
|
||||
if (!nextCurrentIndex.isValid()) {
|
||||
const QModelIndex rootIdx = m_filterModel->index(0, 0);
|
||||
// if the tree does not contain any item - don't do anything
|
||||
if (!rootIdx.isValid())
|
||||
return;
|
||||
|
||||
// get the last (visible) top level index
|
||||
nextCurrentIndex = m_filterModel->index(m_filterModel->rowCount(QModelIndex()) - 1, 0);
|
||||
// step through until end
|
||||
while (int rowCount = m_filterModel->rowCount(nextCurrentIndex))
|
||||
nextCurrentIndex = nextCurrentIndex.child(rowCount - 1, 0);
|
||||
}
|
||||
|
||||
m_treeView->setCurrentIndex(nextCurrentIndex);
|
||||
onItemActivated(nextCurrentIndex);
|
||||
}
|
||||
|
||||
void TestResultsPane::onItemActivated(const QModelIndex &index)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return;
|
||||
|
||||
const TestResult tr = m_filterModel->testResult(index);
|
||||
if (!tr.fileName().isEmpty())
|
||||
Core::EditorManager::openEditorAt(tr.fileName(), tr.line(), 0);
|
||||
}
|
||||
|
||||
void TestResultsPane::onRunAllTriggered()
|
||||
{
|
||||
TestRunner *runner = TestRunner::instance();
|
||||
runner->setSelectedTests(TestTreeModel::instance()->getAllTestCases());
|
||||
runner->prepareToRunTests();
|
||||
}
|
||||
|
||||
void TestResultsPane::onRunSelectedTriggered()
|
||||
{
|
||||
TestRunner *runner = TestRunner::instance();
|
||||
runner->setSelectedTests(TestTreeModel::instance()->getSelectedTests());
|
||||
runner->prepareToRunTests();
|
||||
}
|
||||
|
||||
void TestResultsPane::initializeFilterMenu()
|
||||
{
|
||||
const bool omitIntern = AutotestPlugin::instance()->settings()->omitInternalMssg;
|
||||
// FilterModel has all messages enabled by default
|
||||
if (omitIntern)
|
||||
m_filterModel->toggleTestResultType(Result::MessageInternal);
|
||||
|
||||
QMap<Result::Type, QString> textAndType;
|
||||
textAndType.insert(Result::Pass, tr("Pass"));
|
||||
textAndType.insert(Result::Fail, tr("Fail"));
|
||||
textAndType.insert(Result::ExpectedFail, tr("Expected Fail"));
|
||||
textAndType.insert(Result::UnexpectedPass, tr("Unexpected Pass"));
|
||||
textAndType.insert(Result::Skip, tr("Skip"));
|
||||
textAndType.insert(Result::Benchmark, tr("Benchmarks"));
|
||||
textAndType.insert(Result::MessageDebug, tr("Debug Messages"));
|
||||
textAndType.insert(Result::MessageWarn, tr("Warning Messages"));
|
||||
textAndType.insert(Result::MessageInternal, tr("Internal Messages"));
|
||||
foreach (Result::Type result, textAndType.keys()) {
|
||||
QAction *action = new QAction(m_filterMenu);
|
||||
action->setText(textAndType.value(result));
|
||||
action->setCheckable(true);
|
||||
action->setChecked(result != Result::MessageInternal || !omitIntern);
|
||||
action->setData(result);
|
||||
m_filterMenu->addAction(action);
|
||||
}
|
||||
m_filterMenu->addSeparator();
|
||||
QAction *action = new QAction(m_filterMenu);
|
||||
action->setText(tr("Check All Filters"));
|
||||
action->setCheckable(false);
|
||||
m_filterMenu->addAction(action);
|
||||
connect(action, &QAction::triggered, this, &TestResultsPane::enableAllFilter);
|
||||
}
|
||||
|
||||
void TestResultsPane::updateSummaryLabel()
|
||||
{
|
||||
QString labelText = QString::fromLatin1("<p>Test summary: %1 %2, %3 %4")
|
||||
.arg(QString::number(m_model->resultTypeCount(Result::Pass)), tr("passes"),
|
||||
QString::number(m_model->resultTypeCount(Result::Fail)), tr("fails"));
|
||||
int count = m_model->resultTypeCount(Result::UnexpectedPass);
|
||||
if (count)
|
||||
labelText.append(QString::fromLatin1(", %1 %2")
|
||||
.arg(QString::number(count), tr("unexpected passes")));
|
||||
count = m_model->resultTypeCount(Result::ExpectedFail);
|
||||
if (count)
|
||||
labelText.append(QString::fromLatin1(", %1 %2")
|
||||
.arg(QString::number(count), tr("expected fails")));
|
||||
count = m_model->resultTypeCount(Result::MessageFatal);
|
||||
if (count)
|
||||
labelText.append(QString::fromLatin1(", %1 %2")
|
||||
.arg(QString::number(count), tr("fatals")));
|
||||
count = m_model->resultTypeCount(Result::BlacklistedFail)
|
||||
+ m_model->resultTypeCount(Result::BlacklistedPass);
|
||||
if (count)
|
||||
labelText.append(QString::fromLatin1(", %1 %2")
|
||||
.arg(QString::number(count), tr("blacklisted")));
|
||||
|
||||
count = m_model->disabledTests();
|
||||
if (count)
|
||||
labelText.append(tr(", %1 disabled").arg(count));
|
||||
|
||||
labelText.append(QLatin1String(".</p>"));
|
||||
m_summaryLabel->setText(labelText);
|
||||
}
|
||||
|
||||
void TestResultsPane::enableAllFilter()
|
||||
{
|
||||
foreach (QAction *action, m_filterMenu->actions()) {
|
||||
if (action->isCheckable())
|
||||
action->setChecked(true);
|
||||
}
|
||||
m_filterModel->enableAllResultTypes();
|
||||
}
|
||||
|
||||
void TestResultsPane::filterMenuTriggered(QAction *action)
|
||||
{
|
||||
m_filterModel->toggleTestResultType(TestResult::toResultType(action->data().value<int>()));
|
||||
navigateStateChanged();
|
||||
}
|
||||
|
||||
void TestResultsPane::onTestRunStarted()
|
||||
{
|
||||
m_testRunning = true;
|
||||
m_stopTestRun->setEnabled(true);
|
||||
m_runAll->setEnabled(false);
|
||||
m_runSelected->setEnabled(false);
|
||||
m_summaryWidget->setVisible(false);
|
||||
}
|
||||
|
||||
void TestResultsPane::onTestRunFinished()
|
||||
{
|
||||
m_testRunning = false;
|
||||
m_stopTestRun->setEnabled(false);
|
||||
m_runAll->setEnabled(true);
|
||||
m_runSelected->setEnabled(true);
|
||||
updateSummaryLabel();
|
||||
m_summaryWidget->setVisible(true);
|
||||
m_model->removeCurrentTestMessage();
|
||||
disconnect(m_treeView->verticalScrollBar(), &QScrollBar::rangeChanged,
|
||||
this, &TestResultsPane::onScrollBarRangeChanged);
|
||||
}
|
||||
|
||||
void TestResultsPane::onScrollBarRangeChanged(int, int max)
|
||||
{
|
||||
if (m_autoScroll && m_atEnd)
|
||||
m_treeView->verticalScrollBar()->setValue(max);
|
||||
}
|
||||
|
||||
void TestResultsPane::onTestTreeModelChanged()
|
||||
{
|
||||
const bool enable = TestTreeModel::instance()->hasTests();
|
||||
m_runAll->setEnabled(enable);
|
||||
m_runSelected->setEnabled(enable);
|
||||
}
|
||||
|
||||
void TestResultsPane::onCustomContextMenuRequested(const QPoint &pos)
|
||||
{
|
||||
const bool resultsAvailable = m_filterModel->hasResults();
|
||||
const bool enabled = !m_testRunning && resultsAvailable;
|
||||
const QModelIndex clicked = m_treeView->indexAt(pos);
|
||||
QMenu menu;
|
||||
QAction *action = new QAction(tr("Copy"), &menu);
|
||||
action->setShortcut(QKeySequence(QKeySequence::Copy));
|
||||
action->setEnabled(resultsAvailable);
|
||||
connect(action, &QAction::triggered, [this, clicked] () {
|
||||
onCopyItemTriggered(clicked);
|
||||
});
|
||||
menu.addAction(action);
|
||||
|
||||
action = new QAction(tr("Copy All"), &menu);
|
||||
action->setEnabled(enabled);
|
||||
connect(action, &QAction::triggered, this, &TestResultsPane::onCopyWholeTriggered);
|
||||
menu.addAction(action);
|
||||
|
||||
action = new QAction(tr("Save Output to File..."), &menu);
|
||||
action->setEnabled(enabled);
|
||||
connect(action, &QAction::triggered, this, &TestResultsPane::onSaveWholeTriggered);
|
||||
menu.addAction(action);
|
||||
|
||||
menu.exec(m_treeView->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
void TestResultsPane::onCopyItemTriggered(const QModelIndex &idx)
|
||||
{
|
||||
const TestResult result = m_filterModel->testResult(idx);
|
||||
QApplication::clipboard()->setText(TestResultDelegate::outputString(result, true));
|
||||
}
|
||||
|
||||
void TestResultsPane::onCopyWholeTriggered()
|
||||
{
|
||||
QApplication::clipboard()->setText(getWholeOutput());
|
||||
}
|
||||
|
||||
void TestResultsPane::onSaveWholeTriggered()
|
||||
{
|
||||
const QString fileName = QFileDialog::getSaveFileName(Core::ICore::dialogParent(),
|
||||
tr("Save Output To..."));
|
||||
Utils::FileSaver saver(fileName, QIODevice::Text);
|
||||
if (!saver.write(getWholeOutput().toUtf8()) || !saver.finalize()) {
|
||||
QMessageBox::critical(Core::ICore::dialogParent(), tr("Error"),
|
||||
tr("Failed to write \"%1\".\n\n%2").arg(fileName)
|
||||
.arg(saver.errorString()));
|
||||
}
|
||||
}
|
||||
|
||||
// helper for onCopyWholeTriggered() and onSaveWholeTriggered()
|
||||
QString TestResultsPane::getWholeOutput(const QModelIndex &parent)
|
||||
{
|
||||
QString output;
|
||||
for (int row = 0, count = m_model->rowCount(parent); row < count; ++row) {
|
||||
QModelIndex current = m_model->index(row, 0, parent);
|
||||
const TestResult result = m_model->testResult(current);
|
||||
output.append(TestResult::resultToString(result.result())).append(QLatin1Char('\t'));
|
||||
output.append(TestResultDelegate::outputString(result, true)).append(QLatin1Char('\n'));
|
||||
output.append(getWholeOutput(current));
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
139
src/plugins/autotest/testresultspane.h
Normal file
@@ -0,0 +1,139 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTRESULTSPANE_H
|
||||
#define TESTRESULTSPANE_H
|
||||
|
||||
#include <coreplugin/ioutputpane.h>
|
||||
|
||||
#include <utils/itemviews.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QAction;
|
||||
class QFrame;
|
||||
class QKeyEvent;
|
||||
class QLabel;
|
||||
class QModelIndex;
|
||||
class QMenu;
|
||||
class QToolButton;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Core {
|
||||
class IContext;
|
||||
}
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestResult;
|
||||
class TestResultModel;
|
||||
class TestResultFilterModel;
|
||||
|
||||
class ResultsTreeView : public Utils::TreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ResultsTreeView(QWidget *parent = 0);
|
||||
|
||||
signals:
|
||||
void copyShortcutTriggered();
|
||||
|
||||
protected:
|
||||
void keyPressEvent(QKeyEvent *event);
|
||||
};
|
||||
|
||||
class TestResultsPane : public Core::IOutputPane
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
virtual ~TestResultsPane();
|
||||
static TestResultsPane *instance();
|
||||
|
||||
// IOutputPane interface
|
||||
QWidget *outputWidget(QWidget *parent);
|
||||
QList<QWidget *> toolBarWidgets() const;
|
||||
QString displayName() const;
|
||||
int priorityInStatusBar() const;
|
||||
void clearContents();
|
||||
void visibilityChanged(bool visible);
|
||||
void setFocus();
|
||||
bool hasFocus() const;
|
||||
bool canFocus() const;
|
||||
bool canNavigate() const;
|
||||
bool canNext() const;
|
||||
bool canPrevious() const;
|
||||
void goToNext();
|
||||
void goToPrev();
|
||||
|
||||
signals:
|
||||
|
||||
public slots:
|
||||
void addTestResult(TestResult *result);
|
||||
|
||||
private slots:
|
||||
void onItemActivated(const QModelIndex &index);
|
||||
void onRunAllTriggered();
|
||||
void onRunSelectedTriggered();
|
||||
void enableAllFilter();
|
||||
void filterMenuTriggered(QAction *action);
|
||||
|
||||
private:
|
||||
explicit TestResultsPane(QObject *parent = 0);
|
||||
void initializeFilterMenu();
|
||||
void updateSummaryLabel();
|
||||
void createToolButtons();
|
||||
void onTestRunStarted();
|
||||
void onTestRunFinished();
|
||||
void onScrollBarRangeChanged(int, int max);
|
||||
void onTestTreeModelChanged();
|
||||
void onCustomContextMenuRequested(const QPoint &pos);
|
||||
void onCopyItemTriggered(const QModelIndex &idx);
|
||||
void onCopyWholeTriggered();
|
||||
void onSaveWholeTriggered();
|
||||
QString getWholeOutput(const QModelIndex &parent = QModelIndex());
|
||||
|
||||
QWidget *m_outputWidget;
|
||||
QFrame *m_summaryWidget;
|
||||
QLabel *m_summaryLabel;
|
||||
ResultsTreeView *m_treeView;
|
||||
TestResultModel *m_model;
|
||||
TestResultFilterModel *m_filterModel;
|
||||
Core::IContext *m_context;
|
||||
QToolButton *m_expandCollapse;
|
||||
QToolButton *m_runAll;
|
||||
QToolButton *m_runSelected;
|
||||
QToolButton *m_stopTestRun;
|
||||
QToolButton *m_filterButton;
|
||||
QMenu *m_filterMenu;
|
||||
bool m_wasVisibleBefore;
|
||||
bool m_autoScroll;
|
||||
bool m_atEnd;
|
||||
bool m_testRunning;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTRESULTSPANE_H
|
325
src/plugins/autotest/testrunner.cpp
Normal file
@@ -0,0 +1,325 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "testrunner.h"
|
||||
|
||||
#include "autotestconstants.h"
|
||||
#include "autotestplugin.h"
|
||||
#include "testresultspane.h"
|
||||
#include "testsettings.h"
|
||||
#include "testoutputreader.h"
|
||||
|
||||
#include <coreplugin/progressmanager/futureprogress.h>
|
||||
#include <coreplugin/progressmanager/progressmanager.h>
|
||||
|
||||
#include <projectexplorer/buildmanager.h>
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/projectexplorer.h>
|
||||
#include <projectexplorer/projectexplorersettings.h>
|
||||
|
||||
#include <utils/multitask.h>
|
||||
|
||||
#include <QFuture>
|
||||
#include <QFutureInterface>
|
||||
#include <QTime>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
static TestRunner *m_instance = 0;
|
||||
|
||||
static QString executableFilePath(const QString &command, const QProcessEnvironment &environment)
|
||||
{
|
||||
if (command.isEmpty())
|
||||
return QString();
|
||||
|
||||
QFileInfo commandFileInfo(command);
|
||||
if (commandFileInfo.isExecutable() && commandFileInfo.path() != QLatin1String(".")) {
|
||||
return commandFileInfo.absoluteFilePath();
|
||||
} else if (commandFileInfo.path() == QLatin1String(".")){
|
||||
QString fullCommandFileName = command;
|
||||
#ifdef Q_OS_WIN
|
||||
if (!command.endsWith(QLatin1String(".exe")))
|
||||
fullCommandFileName = command + QLatin1String(".exe");
|
||||
|
||||
static const QString pathSeparator(QLatin1Char(';'));
|
||||
#else
|
||||
static const QString pathSeparator(QLatin1Char(':'));
|
||||
#endif
|
||||
QStringList pathList = environment.value(QLatin1String("PATH")).split(pathSeparator);
|
||||
|
||||
foreach (const QString &path, pathList) {
|
||||
QString filePath(path + QDir::separator() + fullCommandFileName);
|
||||
if (QFileInfo(filePath).isExecutable())
|
||||
return commandFileInfo.absoluteFilePath();
|
||||
}
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
TestRunner *TestRunner::instance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new TestRunner;
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
TestRunner::TestRunner(QObject *parent) :
|
||||
QObject(parent),
|
||||
m_executingTests(false)
|
||||
{
|
||||
connect(&m_futureWatcher, &QFutureWatcher<TestResult *>::resultReadyAt,
|
||||
this, [this](int index) { emit testResultReady(m_futureWatcher.resultAt(index)); });
|
||||
connect(&m_futureWatcher, &QFutureWatcher<TestResult *>::finished,
|
||||
this, &TestRunner::onFinished);
|
||||
connect(this, &TestRunner::requestStopTestRun,
|
||||
&m_futureWatcher, &QFutureWatcher<TestResult *>::cancel);
|
||||
connect(&m_futureWatcher, &QFutureWatcher<TestResult *>::canceled,
|
||||
this, [this]() { emit testResultReady(new FaultyTestResult(
|
||||
Result::MessageFatal,
|
||||
QObject::tr("Test run canceled by user.")));
|
||||
});
|
||||
}
|
||||
|
||||
TestRunner::~TestRunner()
|
||||
{
|
||||
qDeleteAll(m_selectedTests);
|
||||
m_selectedTests.clear();
|
||||
m_instance = 0;
|
||||
}
|
||||
|
||||
void TestRunner::setSelectedTests(const QList<TestConfiguration *> &selected)
|
||||
{
|
||||
qDeleteAll(m_selectedTests);
|
||||
m_selectedTests.clear();
|
||||
m_selectedTests = selected;
|
||||
}
|
||||
|
||||
static void performTestRun(QFutureInterface<TestResult *> &futureInterface,
|
||||
const QList<TestConfiguration *> selectedTests, const int timeout,
|
||||
const QString metricsOption)
|
||||
{
|
||||
QEventLoop eventLoop;
|
||||
int testCaseCount = 0;
|
||||
foreach (TestConfiguration *config, selectedTests) {
|
||||
config->completeTestInformation();
|
||||
if (config->project()) {
|
||||
testCaseCount += config->testCaseCount();
|
||||
} else {
|
||||
futureInterface.reportResult(new FaultyTestResult(Result::MessageWarn,
|
||||
QObject::tr("Project is null for \"%1\". Removing from test run.\n"
|
||||
"Check the test environment.").arg(config->displayName())));
|
||||
}
|
||||
}
|
||||
|
||||
QProcess testProcess;
|
||||
testProcess.setReadChannelMode(QProcess::MergedChannels);
|
||||
testProcess.setReadChannel(QProcess::StandardOutput);
|
||||
|
||||
futureInterface.setProgressRange(0, testCaseCount);
|
||||
futureInterface.setProgressValue(0);
|
||||
|
||||
foreach (const TestConfiguration *testConfiguration, selectedTests) {
|
||||
QScopedPointer<TestOutputReader> outputReader;
|
||||
switch (testConfiguration->testType()) {
|
||||
case TestTypeQt:
|
||||
outputReader.reset(new QtTestOutputReader(futureInterface, &testProcess));
|
||||
break;
|
||||
case TestTypeGTest:
|
||||
outputReader.reset(new GTestOutputReader(futureInterface, &testProcess));
|
||||
break;
|
||||
}
|
||||
if (futureInterface.isCanceled())
|
||||
break;
|
||||
|
||||
if (!testConfiguration->project())
|
||||
continue;
|
||||
|
||||
QProcessEnvironment environment = testConfiguration->environment().toProcessEnvironment();
|
||||
QString commandFilePath = executableFilePath(testConfiguration->targetFile(), environment);
|
||||
if (commandFilePath.isEmpty()) {
|
||||
futureInterface.reportResult(new FaultyTestResult(Result::MessageFatal,
|
||||
QObject::tr("Could not find command \"%1\". (%2)")
|
||||
.arg(testConfiguration->targetFile())
|
||||
.arg(testConfiguration->displayName())));
|
||||
continue;
|
||||
}
|
||||
|
||||
if (testConfiguration->testType() == TestTypeQt) {
|
||||
QStringList argumentList(QLatin1String("-xml"));
|
||||
if (!metricsOption.isEmpty())
|
||||
argumentList << metricsOption;
|
||||
if (testConfiguration->testCases().count())
|
||||
argumentList << testConfiguration->testCases();
|
||||
testProcess.setArguments(argumentList);
|
||||
} else { // TestTypeGTest
|
||||
const QStringList &testSets = testConfiguration->testCases();
|
||||
if (testSets.size()) {
|
||||
QStringList argumentList;
|
||||
argumentList << QLatin1String("--gtest_filter=")
|
||||
+ testSets.join(QLatin1Char(':'));
|
||||
testProcess.setArguments(argumentList);
|
||||
}
|
||||
}
|
||||
|
||||
testProcess.setWorkingDirectory(testConfiguration->workingDirectory());
|
||||
if (Utils::HostOsInfo::isWindowsHost())
|
||||
environment.insert(QLatin1String("QT_LOGGING_TO_CONSOLE"), QLatin1String("1"));
|
||||
testProcess.setProcessEnvironment(environment);
|
||||
testProcess.setProgram(commandFilePath);
|
||||
testProcess.start();
|
||||
|
||||
bool ok = testProcess.waitForStarted();
|
||||
QTime executionTimer;
|
||||
executionTimer.start();
|
||||
bool canceledByTimeout = false;
|
||||
if (ok) {
|
||||
while (testProcess.state() == QProcess::Running) {
|
||||
if (executionTimer.elapsed() >= timeout) {
|
||||
canceledByTimeout = true;
|
||||
break;
|
||||
}
|
||||
if (futureInterface.isCanceled()) {
|
||||
testProcess.kill();
|
||||
testProcess.waitForFinished();
|
||||
return;
|
||||
}
|
||||
eventLoop.processEvents();
|
||||
}
|
||||
}
|
||||
|
||||
if (canceledByTimeout) {
|
||||
if (testProcess.state() != QProcess::NotRunning) {
|
||||
testProcess.kill();
|
||||
testProcess.waitForFinished();
|
||||
}
|
||||
futureInterface.reportResult(new FaultyTestResult(Result::MessageFatal, QObject::tr(
|
||||
"Test case canceled due to timeout. \nMaybe raise the timeout?")));
|
||||
}
|
||||
}
|
||||
futureInterface.setProgressValue(testCaseCount);
|
||||
}
|
||||
|
||||
void TestRunner::prepareToRunTests()
|
||||
{
|
||||
ProjectExplorer::Internal::ProjectExplorerSettings projectExplorerSettings =
|
||||
ProjectExplorer::ProjectExplorerPlugin::projectExplorerSettings();
|
||||
if (projectExplorerSettings.buildBeforeDeploy && !projectExplorerSettings.saveBeforeBuild) {
|
||||
if (!ProjectExplorer::ProjectExplorerPlugin::saveModifiedFiles())
|
||||
return;
|
||||
}
|
||||
|
||||
const bool omitRunConfigWarnings = AutotestPlugin::instance()->settings()->omitRunConfigWarn;
|
||||
|
||||
m_executingTests = true;
|
||||
emit testRunStarted();
|
||||
|
||||
// clear old log and output pane
|
||||
TestResultsPane::instance()->clearContents();
|
||||
|
||||
foreach (TestConfiguration *config, m_selectedTests) {
|
||||
if (!omitRunConfigWarnings && config->guessedConfiguration()) {
|
||||
emit testResultReady(new FaultyTestResult(Result::MessageWarn,
|
||||
tr("Project's run configuration was guessed for \"%1\".\n"
|
||||
"This might cause trouble during execution.").arg(config->displayName())));
|
||||
}
|
||||
}
|
||||
|
||||
if (m_selectedTests.empty()) {
|
||||
emit testResultReady(new FaultyTestResult(Result::MessageWarn,
|
||||
tr("No tests selected. Canceling test run.")));
|
||||
onFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
ProjectExplorer::Project *project = m_selectedTests.at(0)->project();
|
||||
if (!project) {
|
||||
emit testResultReady(new FaultyTestResult(Result::MessageWarn,
|
||||
tr("Project is null. Canceling test run.\n"
|
||||
"Only desktop kits are supported. Make sure the "
|
||||
"currently active kit is a desktop kit.")));
|
||||
onFinished();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!projectExplorerSettings.buildBeforeDeploy) {
|
||||
runTests();
|
||||
} else {
|
||||
if (project->hasActiveBuildSettings()) {
|
||||
buildProject(project);
|
||||
} else {
|
||||
emit testResultReady(new FaultyTestResult(Result::MessageFatal,
|
||||
tr("Project is not configured. Canceling test run.")));
|
||||
onFinished();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void TestRunner::runTests()
|
||||
{
|
||||
const QSharedPointer<TestSettings> settings = AutotestPlugin::instance()->settings();
|
||||
const QString &metricsOption = TestSettings::metricsTypeToOption(settings->metrics);
|
||||
|
||||
QFuture<TestResult *> future = Utils::runAsync<TestResult *>(&performTestRun, m_selectedTests,
|
||||
settings->timeout, metricsOption);
|
||||
m_futureWatcher.setFuture(future);
|
||||
Core::ProgressManager::addTask(future, tr("Running Tests"), Autotest::Constants::TASK_INDEX);
|
||||
}
|
||||
|
||||
void TestRunner::buildProject(ProjectExplorer::Project *project)
|
||||
{
|
||||
ProjectExplorer::BuildManager *buildManager = ProjectExplorer::BuildManager::instance();
|
||||
m_buildConnect = connect(this, &TestRunner::requestStopTestRun,
|
||||
buildManager, &ProjectExplorer::BuildManager::cancel);
|
||||
connect(buildManager, &ProjectExplorer::BuildManager::buildQueueFinished,
|
||||
this, &TestRunner::buildFinished);
|
||||
ProjectExplorer::ProjectExplorerPlugin::buildProject(project);
|
||||
}
|
||||
|
||||
void TestRunner::buildFinished(bool success)
|
||||
{
|
||||
disconnect(m_buildConnect);
|
||||
ProjectExplorer::BuildManager *buildManager = ProjectExplorer::BuildManager::instance();
|
||||
disconnect(buildManager, &ProjectExplorer::BuildManager::buildQueueFinished,
|
||||
this, &TestRunner::buildFinished);
|
||||
|
||||
if (success) {
|
||||
runTests();
|
||||
} else {
|
||||
emit testResultReady(new FaultyTestResult(Result::MessageFatal,
|
||||
tr("Build failed. Canceling test run.")));
|
||||
onFinished();
|
||||
}
|
||||
}
|
||||
|
||||
void TestRunner::onFinished()
|
||||
{
|
||||
m_executingTests = false;
|
||||
emit testRunFinished();
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
83
src/plugins/autotest/testrunner.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTRUNNER_H
|
||||
#define TESTRUNNER_H
|
||||
|
||||
#include "testconfiguration.h"
|
||||
#include "testresult.h"
|
||||
|
||||
#include <QFutureWatcher>
|
||||
#include <QObject>
|
||||
#include <QProcess>
|
||||
|
||||
namespace ProjectExplorer {
|
||||
class Project;
|
||||
}
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestRunner : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
static TestRunner* instance();
|
||||
~TestRunner();
|
||||
|
||||
void setSelectedTests(const QList<TestConfiguration *> &selected);
|
||||
bool isTestRunning() const { return m_executingTests; }
|
||||
|
||||
signals:
|
||||
void testRunStarted();
|
||||
void testRunFinished();
|
||||
void requestStopTestRun();
|
||||
void testResultReady(TestResult *result);
|
||||
|
||||
public slots:
|
||||
void prepareToRunTests();
|
||||
|
||||
private slots:
|
||||
void buildProject(ProjectExplorer::Project *project);
|
||||
void buildFinished(bool success);
|
||||
void onFinished();
|
||||
|
||||
private:
|
||||
void runTests();
|
||||
explicit TestRunner(QObject *parent = 0);
|
||||
|
||||
QFutureWatcher<TestResult *> m_futureWatcher;
|
||||
QList<TestConfiguration *> m_selectedTests;
|
||||
bool m_executingTests;
|
||||
|
||||
// temporarily used if building before running is necessary
|
||||
QMetaObject::Connection m_buildConnect;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTRUNNER_H
|
117
src/plugins/autotest/testsettings.cpp
Normal file
@@ -0,0 +1,117 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "testsettings.h"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
static const char group[] = "Autotest";
|
||||
static const char timeoutKey[] = "Timeout";
|
||||
static const char metricsKey[] = "Metrics";
|
||||
static const char omitInternalKey[] = "OmitInternal";
|
||||
static const char omitRunConfigWarnKey[] = "OmitRCWarnings";
|
||||
static const char limitResultOutputKey[] = "LimitResultOutput";
|
||||
static const char autoScrollKey[] = "AutoScrollResults";
|
||||
static const int defaultTimeout = 60000;
|
||||
|
||||
TestSettings::TestSettings()
|
||||
: timeout(defaultTimeout), metrics(Walltime), omitInternalMssg(true), omitRunConfigWarn(false),
|
||||
limitResultOutput(true), autoScroll(true)
|
||||
{
|
||||
}
|
||||
|
||||
void TestSettings::toSettings(QSettings *s) const
|
||||
{
|
||||
s->beginGroup(QLatin1String(group));
|
||||
s->setValue(QLatin1String(timeoutKey), timeout);
|
||||
s->setValue(QLatin1String(metricsKey), metrics);
|
||||
s->setValue(QLatin1String(omitInternalKey), omitInternalMssg);
|
||||
s->setValue(QLatin1String(omitRunConfigWarnKey), omitRunConfigWarn);
|
||||
s->setValue(QLatin1String(limitResultOutputKey), limitResultOutput);
|
||||
s->setValue(QLatin1String(autoScrollKey), autoScroll);
|
||||
s->endGroup();
|
||||
}
|
||||
|
||||
static MetricsType intToMetrics(int value)
|
||||
{
|
||||
switch (value) {
|
||||
case Walltime:
|
||||
return Walltime;
|
||||
case TickCounter:
|
||||
return TickCounter;
|
||||
case EventCounter:
|
||||
return EventCounter;
|
||||
case CallGrind:
|
||||
return CallGrind;
|
||||
case Perf:
|
||||
return Perf;
|
||||
default:
|
||||
return Walltime;
|
||||
}
|
||||
}
|
||||
|
||||
void TestSettings::fromSettings(const QSettings *s)
|
||||
{
|
||||
const QString root = QLatin1String(group) + QLatin1Char('/');
|
||||
timeout = s->value(root + QLatin1String(timeoutKey), defaultTimeout).toInt();
|
||||
metrics = intToMetrics(s->value(root + QLatin1String(metricsKey), Walltime).toInt());
|
||||
omitInternalMssg = s->value(root + QLatin1String(omitInternalKey), true).toBool();
|
||||
omitRunConfigWarn = s->value(root + QLatin1String(omitRunConfigWarnKey), false).toBool();
|
||||
limitResultOutput = s->value(root + QLatin1String(limitResultOutputKey), true).toBool();
|
||||
autoScroll = s->value(root + QLatin1String(autoScrollKey), true).toBool();
|
||||
}
|
||||
|
||||
bool TestSettings::equals(const TestSettings &rhs) const
|
||||
{
|
||||
return timeout == rhs.timeout && metrics == rhs.metrics
|
||||
&& omitInternalMssg == rhs.omitInternalMssg
|
||||
&& omitRunConfigWarn == rhs.omitRunConfigWarn
|
||||
&& limitResultOutput == rhs.limitResultOutput
|
||||
&& autoScroll == rhs.autoScroll;
|
||||
}
|
||||
|
||||
QString TestSettings::metricsTypeToOption(const MetricsType type)
|
||||
{
|
||||
switch (type) {
|
||||
case MetricsType::Walltime:
|
||||
return QString();
|
||||
case MetricsType::TickCounter:
|
||||
return QLatin1String("-tickcounter");
|
||||
case MetricsType::EventCounter:
|
||||
return QLatin1String("-eventcounter");
|
||||
case MetricsType::CallGrind:
|
||||
return QLatin1String("-callgrind");
|
||||
case MetricsType::Perf:
|
||||
return QLatin1String("-perf");
|
||||
default:
|
||||
return QString();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
68
src/plugins/autotest/testsettings.h
Normal file
@@ -0,0 +1,68 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTSETTINGS_H
|
||||
#define TESTSETTINGS_H
|
||||
|
||||
#include <QtGlobal>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QSettings;
|
||||
QT_END_NAMESPACE
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
enum MetricsType {
|
||||
Walltime,
|
||||
TickCounter,
|
||||
EventCounter,
|
||||
CallGrind,
|
||||
Perf
|
||||
};
|
||||
|
||||
struct TestSettings
|
||||
{
|
||||
TestSettings();
|
||||
void toSettings(QSettings *s) const;
|
||||
void fromSettings(const QSettings *s);
|
||||
bool equals(const TestSettings &rhs) const;
|
||||
static QString metricsTypeToOption(const MetricsType type);
|
||||
|
||||
int timeout;
|
||||
MetricsType metrics;
|
||||
bool omitInternalMssg;
|
||||
bool omitRunConfigWarn;
|
||||
bool limitResultOutput;
|
||||
bool autoScroll;
|
||||
};
|
||||
|
||||
inline bool operator==(const TestSettings &s1, const TestSettings &s2) { return s1.equals(s2); }
|
||||
inline bool operator!=(const TestSettings &s1, const TestSettings &s2) { return !s1.equals(s2); }
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTSETTINGS_H
|
132
src/plugins/autotest/testsettingspage.cpp
Normal file
@@ -0,0 +1,132 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "autotestconstants.h"
|
||||
#include "testsettingspage.h"
|
||||
#include "testsettings.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <utils/hostosinfo.h>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestSettingsWidget::TestSettingsWidget(QWidget *parent)
|
||||
: QWidget(parent)
|
||||
{
|
||||
m_ui.setupUi(this);
|
||||
m_ui.callgrindRB->setEnabled(Utils::HostOsInfo::isAnyUnixHost()); // valgrind available on UNIX
|
||||
m_ui.perfRB->setEnabled(Utils::HostOsInfo::isLinuxHost()); // according to docs perf Linux only
|
||||
}
|
||||
|
||||
void TestSettingsWidget::setSettings(const TestSettings &settings)
|
||||
{
|
||||
m_ui.timeoutSpin->setValue(settings.timeout / 1000); // we store milliseconds
|
||||
m_ui.omitInternalMsgCB->setChecked(settings.omitInternalMssg);
|
||||
m_ui.omitRunConfigWarnCB->setChecked(settings.omitRunConfigWarn);
|
||||
m_ui.limitResultOutputCB->setChecked(settings.limitResultOutput);
|
||||
m_ui.autoScrollCB->setChecked(settings.autoScroll);
|
||||
|
||||
switch (settings.metrics) {
|
||||
case MetricsType::Walltime:
|
||||
m_ui.walltimeRB->setChecked(true);
|
||||
break;
|
||||
case MetricsType::TickCounter:
|
||||
m_ui.tickcounterRB->setChecked(true);
|
||||
break;
|
||||
case MetricsType::EventCounter:
|
||||
m_ui.eventCounterRB->setChecked(true);
|
||||
break;
|
||||
case MetricsType::CallGrind:
|
||||
m_ui.callgrindRB->setChecked(true);
|
||||
break;
|
||||
case MetricsType::Perf:
|
||||
m_ui.perfRB->setChecked(true);
|
||||
break;
|
||||
default:
|
||||
m_ui.walltimeRB->setChecked(true);
|
||||
}
|
||||
}
|
||||
|
||||
TestSettings TestSettingsWidget::settings() const
|
||||
{
|
||||
TestSettings result;
|
||||
result.timeout = m_ui.timeoutSpin->value() * 1000; // we display seconds
|
||||
result.omitInternalMssg = m_ui.omitInternalMsgCB->isChecked();
|
||||
result.omitRunConfigWarn = m_ui.omitRunConfigWarnCB->isChecked();
|
||||
result.limitResultOutput = m_ui.limitResultOutputCB->isChecked();
|
||||
result.autoScroll = m_ui.autoScrollCB->isChecked();
|
||||
|
||||
if (m_ui.walltimeRB->isChecked())
|
||||
result.metrics = MetricsType::Walltime;
|
||||
else if (m_ui.tickcounterRB->isChecked())
|
||||
result.metrics = MetricsType::TickCounter;
|
||||
else if (m_ui.eventCounterRB->isChecked())
|
||||
result.metrics = MetricsType::EventCounter;
|
||||
else if (m_ui.callgrindRB->isChecked())
|
||||
result.metrics = MetricsType::CallGrind;
|
||||
else if (m_ui.perfRB->isChecked())
|
||||
result.metrics = MetricsType::Perf;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TestSettingsPage::TestSettingsPage(const QSharedPointer<TestSettings> &settings)
|
||||
: m_settings(settings), m_widget(0)
|
||||
{
|
||||
setId("A.AutoTest.General");
|
||||
setDisplayName(tr("General"));
|
||||
setCategory(Constants::AUTOTEST_SETTINGS_CATEGORY);
|
||||
setDisplayCategory(tr("Test Settings"));
|
||||
setCategoryIcon(QLatin1String(":/images/autotest.png"));
|
||||
}
|
||||
|
||||
TestSettingsPage::~TestSettingsPage()
|
||||
{
|
||||
}
|
||||
|
||||
QWidget *TestSettingsPage::widget()
|
||||
{
|
||||
if (!m_widget) {
|
||||
m_widget = new TestSettingsWidget;
|
||||
m_widget->setSettings(*m_settings);
|
||||
}
|
||||
return m_widget;
|
||||
}
|
||||
|
||||
void TestSettingsPage::apply()
|
||||
{
|
||||
if (!m_widget) // page was not shown at all
|
||||
return;
|
||||
const TestSettings newSettings = m_widget->settings();
|
||||
if (newSettings != *m_settings) {
|
||||
*m_settings = newSettings;
|
||||
m_settings->toSettings(Core::ICore::settings());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
73
src/plugins/autotest/testsettingspage.h
Normal file
@@ -0,0 +1,73 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTSETTINGSPAGE_H
|
||||
#define TESTSETTINGSPAGE_H
|
||||
|
||||
#include "ui_testsettingspage.h"
|
||||
|
||||
#include <coreplugin/dialogs/ioptionspage.h>
|
||||
|
||||
#include <QPointer>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
struct TestSettings;
|
||||
|
||||
class TestSettingsWidget : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TestSettingsWidget(QWidget *parent = 0);
|
||||
|
||||
void setSettings(const TestSettings &settings);
|
||||
TestSettings settings() const;
|
||||
|
||||
private:
|
||||
Ui::TestSettingsPage m_ui;
|
||||
|
||||
};
|
||||
|
||||
class TestSettingsPage : public Core::IOptionsPage
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit TestSettingsPage(const QSharedPointer<TestSettings> &settings);
|
||||
~TestSettingsPage();
|
||||
|
||||
QWidget *widget();
|
||||
void apply();
|
||||
void finish() { }
|
||||
|
||||
private:
|
||||
QSharedPointer<TestSettings> m_settings;
|
||||
QPointer<TestSettingsWidget> m_widget;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTSETTINGSPAGE_H
|
261
src/plugins/autotest/testsettingspage.ui
Normal file
@@ -0,0 +1,261 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>Autotest::Internal::TestSettingsPage</class>
|
||||
<widget class="QWidget" name="Autotest::Internal::TestSettingsPage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>407</width>
|
||||
<height>228</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string>Form</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_5">
|
||||
<item>
|
||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||
<item>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="toolTip">
|
||||
<string>Timeout used when executing each test case.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Timeout:</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QSpinBox" name="timeoutSpin">
|
||||
<property name="toolTip">
|
||||
<string>Timeout used when executing test cases. This will apply for each test case on its own, not the whole project.</string>
|
||||
</property>
|
||||
<property name="suffix">
|
||||
<string> s</string>
|
||||
</property>
|
||||
<property name="minimum">
|
||||
<number>5</number>
|
||||
</property>
|
||||
<property name="maximum">
|
||||
<number>36000</number>
|
||||
</property>
|
||||
<property name="value">
|
||||
<number>60</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">
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_4">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox">
|
||||
<property name="title">
|
||||
<string>Benchmark Metrics</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_3">
|
||||
<item>
|
||||
<widget class="QRadioButton" name="walltimeRB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uses walltime metrics for executing benchmarks (default).</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Walltime</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="tickcounterRB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uses tick counter when executing benchmarks.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Tick counter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="eventCounterRB">
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uses event counter when executing benchmarks.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Event counter</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="callgrindRB">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uses Valgrind Callgrind when executing benchmarks (it must be installed).</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Callgrind</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QRadioButton" name="perfRB">
|
||||
<property name="enabled">
|
||||
<bool>false</bool>
|
||||
</property>
|
||||
<property name="sizePolicy">
|
||||
<sizepolicy hsizetype="Minimum" vsizetype="Minimum">
|
||||
<horstretch>0</horstretch>
|
||||
<verstretch>0</verstretch>
|
||||
</sizepolicy>
|
||||
</property>
|
||||
<property name="toolTip">
|
||||
<string>Uses Perf when executing benchmarks (it must be installed).</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Perf</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer_2">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
<item>
|
||||
<layout class="QVBoxLayout" name="verticalLayout_2">
|
||||
<item>
|
||||
<widget class="QGroupBox" name="groupBox_2">
|
||||
<property name="title">
|
||||
<string>General</string>
|
||||
</property>
|
||||
<layout class="QVBoxLayout" name="verticalLayout">
|
||||
<item>
|
||||
<widget class="QCheckBox" name="omitInternalMsgCB">
|
||||
<property name="toolTip">
|
||||
<string>Hides internal messages by default. You can still enable them by using the test results filter.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Omit internal messages</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="omitRunConfigWarnCB">
|
||||
<property name="toolTip">
|
||||
<string>Hides warnings related to a guessed run configuration.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Omit run configuration warnings</string>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="limitResultOutputCB">
|
||||
<property name="toolTip">
|
||||
<string>Limit result output to 100000 characters.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Limit result output</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<widget class="QCheckBox" name="autoScrollCB">
|
||||
<property name="toolTip">
|
||||
<string>Automatically scroll down when new items are added and scrollbar is at bottom.</string>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string>Automatically scroll results</string>
|
||||
</property>
|
||||
<property name="checked">
|
||||
<bool>true</bool>
|
||||
</property>
|
||||
</widget>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
</item>
|
||||
<item>
|
||||
<spacer name="verticalSpacer">
|
||||
<property name="orientation">
|
||||
<enum>Qt::Vertical</enum>
|
||||
</property>
|
||||
<property name="sizeHint" stdset="0">
|
||||
<size>
|
||||
<width>20</width>
|
||||
<height>40</height>
|
||||
</size>
|
||||
</property>
|
||||
</spacer>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</item>
|
||||
</layout>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
285
src/plugins/autotest/testtreeitem.cpp
Normal file
@@ -0,0 +1,285 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "autotestconstants.h"
|
||||
#include "testtreeitem.h"
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QIcon>
|
||||
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestTreeItem::TestTreeItem(const QString &name, const QString &filePath, Type type)
|
||||
: TreeItem( { name } ),
|
||||
m_name(name),
|
||||
m_filePath(filePath),
|
||||
m_type(type),
|
||||
m_line(0),
|
||||
m_state(Enabled)
|
||||
{
|
||||
switch (m_type) {
|
||||
case TestClass:
|
||||
case TestFunction:
|
||||
case GTestCase:
|
||||
case GTestCaseParameterized:
|
||||
case GTestName:
|
||||
m_checked = Qt::Checked;
|
||||
break;
|
||||
default:
|
||||
m_checked = Qt::Unchecked;
|
||||
}
|
||||
}
|
||||
|
||||
TestTreeItem::~TestTreeItem()
|
||||
{
|
||||
removeChildren();
|
||||
}
|
||||
|
||||
TestTreeItem::TestTreeItem(const TestTreeItem &other)
|
||||
: TreeItem( { other.m_name } ),
|
||||
m_name(other.m_name),
|
||||
m_filePath(other.m_filePath),
|
||||
m_checked(other.m_checked),
|
||||
m_type(other.m_type),
|
||||
m_line(other.m_line),
|
||||
m_column(other.m_column),
|
||||
m_mainFile(other.m_mainFile),
|
||||
m_state(other.m_state)
|
||||
{
|
||||
for (int row = 0, count = other.childCount(); row < count; ++row)
|
||||
appendChild(new TestTreeItem(*childItem(row)));
|
||||
}
|
||||
|
||||
static QIcon testTreeIcon(TestTreeItem::Type type)
|
||||
{
|
||||
static QIcon icons[] = {
|
||||
QIcon(),
|
||||
QIcon(QLatin1String(":/images/class.png")),
|
||||
QIcon(QLatin1String(":/images/func.png")),
|
||||
QIcon(QLatin1String(":/images/data.png"))
|
||||
};
|
||||
if (type == TestTreeItem::GTestCase || type == TestTreeItem::GTestCaseParameterized)
|
||||
return icons[1];
|
||||
|
||||
if (int(type) >= int(sizeof icons / sizeof *icons))
|
||||
return icons[2];
|
||||
return icons[type];
|
||||
}
|
||||
|
||||
QVariant TestTreeItem::data(int /*column*/, int role) const
|
||||
{
|
||||
switch (role) {
|
||||
case Qt::DisplayRole:
|
||||
if (m_type == Root && childCount() == 0)
|
||||
return QString(m_name + QObject::tr(" (none)"));
|
||||
else if (m_name.isEmpty())
|
||||
return QObject::tr(Constants::UNNAMED_QUICKTESTS);
|
||||
else if (m_type == GTestCaseParameterized)
|
||||
return QString(m_name + QObject::tr(" [parameterized]"));
|
||||
else
|
||||
return m_name;
|
||||
case Qt::ToolTipRole:
|
||||
if (m_type == TestClass && m_name.isEmpty()) {
|
||||
return QObject::tr("<p>Give all test cases a name to ensure correct behavior "
|
||||
"when running test cases and to be able to select them.</p>");
|
||||
}
|
||||
return m_filePath;
|
||||
case Qt::DecorationRole:
|
||||
return testTreeIcon(m_type);
|
||||
case Qt::CheckStateRole:
|
||||
switch (m_type) {
|
||||
case Root:
|
||||
case TestDataFunction:
|
||||
case TestSpecialFunction:
|
||||
case TestDataTag:
|
||||
return QVariant();
|
||||
case TestClass:
|
||||
case GTestCase:
|
||||
case GTestCaseParameterized:
|
||||
return m_name.isEmpty() ? QVariant() : checked();
|
||||
case TestFunction:
|
||||
case GTestName:
|
||||
if (parentItem() && parentItem()->name().isEmpty())
|
||||
return QVariant();
|
||||
return checked();
|
||||
default:
|
||||
return checked();
|
||||
}
|
||||
case LinkRole: {
|
||||
QVariant itemLink;
|
||||
itemLink.setValue(TextEditor::TextEditorWidget::Link(m_filePath, m_line, m_column));
|
||||
return itemLink;
|
||||
}
|
||||
case ItalicRole:
|
||||
switch (m_type) {
|
||||
case TestDataFunction:
|
||||
case TestSpecialFunction:
|
||||
return true;
|
||||
case TestClass:
|
||||
return m_name.isEmpty();
|
||||
case TestFunction:
|
||||
return parentItem() ? parentItem()->name().isEmpty() : false;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
case TypeRole:
|
||||
return m_type;
|
||||
case StateRole:
|
||||
return (int)m_state;
|
||||
}
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
bool TestTreeItem::setData(int /*column*/, const QVariant &data, int role)
|
||||
{
|
||||
if (role == Qt::CheckStateRole) {
|
||||
Qt::CheckState old = checked();
|
||||
setChecked((Qt::CheckState)data.toInt());
|
||||
return checked() != old;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestTreeItem::modifyContent(const TestTreeItem *modified)
|
||||
{
|
||||
bool hasBeenModified = false;
|
||||
if (m_filePath != modified->m_filePath) {
|
||||
m_filePath = modified->m_filePath;
|
||||
hasBeenModified = true;
|
||||
}
|
||||
if (m_name != modified->m_name) {
|
||||
m_name = modified->m_name;
|
||||
hasBeenModified = true;
|
||||
}
|
||||
if (m_line != modified->m_line) {
|
||||
m_line = modified->m_line;
|
||||
hasBeenModified = true;
|
||||
}
|
||||
if (m_mainFile != modified->m_mainFile) {
|
||||
m_mainFile = modified->m_mainFile;
|
||||
hasBeenModified = true;
|
||||
}
|
||||
if (m_type != modified->m_type) {
|
||||
m_type = modified->m_type;
|
||||
hasBeenModified = true;
|
||||
}
|
||||
if (m_state != modified->m_state) {
|
||||
m_state = modified->m_state;
|
||||
hasBeenModified = true;
|
||||
}
|
||||
return hasBeenModified;
|
||||
}
|
||||
|
||||
void TestTreeItem::setChecked(const Qt::CheckState checkState)
|
||||
{
|
||||
switch (m_type) {
|
||||
case TestFunction:
|
||||
case GTestName: {
|
||||
m_checked = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked);
|
||||
parentItem()->revalidateCheckState();
|
||||
break;
|
||||
}
|
||||
case TestClass:
|
||||
case GTestCase:
|
||||
case GTestCaseParameterized: {
|
||||
Qt::CheckState usedState = (checkState == Qt::Unchecked ? Qt::Unchecked : Qt::Checked);
|
||||
for (int row = 0, count = childCount(); row < count; ++row)
|
||||
childItem(row)->setChecked(usedState);
|
||||
m_checked = usedState;
|
||||
}
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Qt::CheckState TestTreeItem::checked() const
|
||||
{
|
||||
switch (m_type) {
|
||||
case TestClass:
|
||||
case TestFunction:
|
||||
case GTestCase:
|
||||
case GTestCaseParameterized:
|
||||
case GTestName:
|
||||
return m_checked;
|
||||
case TestDataFunction:
|
||||
case TestSpecialFunction:
|
||||
return Qt::Unchecked;
|
||||
default:
|
||||
if (parent())
|
||||
return parentItem()->m_checked;
|
||||
}
|
||||
return Qt::Unchecked;
|
||||
}
|
||||
|
||||
QList<QString> TestTreeItem::getChildNames() const
|
||||
{
|
||||
QList<QString> names;
|
||||
for (int row = 0, count = childCount(); row < count; ++row)
|
||||
names << childItem(row)->name();
|
||||
return names;
|
||||
}
|
||||
|
||||
TestTreeItem *TestTreeItem::parentItem() const
|
||||
{
|
||||
return static_cast<TestTreeItem *>(parent());
|
||||
}
|
||||
|
||||
TestTreeItem *TestTreeItem::childItem(int row) const
|
||||
{
|
||||
return static_cast<TestTreeItem *>(child(row));
|
||||
}
|
||||
|
||||
void TestTreeItem::revalidateCheckState()
|
||||
{
|
||||
if (childCount() == 0)
|
||||
return;
|
||||
bool foundChecked = false;
|
||||
bool foundUnchecked = false;
|
||||
for (int row = 0, count = childCount(); row < count; ++row) {
|
||||
TestTreeItem *child = childItem(row);
|
||||
switch (child->type()) {
|
||||
case TestDataFunction:
|
||||
case TestSpecialFunction:
|
||||
continue;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
foundChecked |= (child->checked() != Qt::Unchecked);
|
||||
foundUnchecked |= (child->checked() == Qt::Unchecked);
|
||||
if (foundChecked && foundUnchecked) {
|
||||
m_checked = Qt::PartiallyChecked;
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_checked = (foundUnchecked ? Qt::Unchecked : Qt::Checked);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
128
src/plugins/autotest/testtreeitem.h
Normal file
@@ -0,0 +1,128 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTTREEITEM_H
|
||||
#define TESTTREEITEM_H
|
||||
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#include <QList>
|
||||
#include <QString>
|
||||
#include <QMetaType>
|
||||
|
||||
namespace {
|
||||
enum ItemRole {
|
||||
LinkRole = Qt::UserRole + 2, // can be removed if AnnotationRole comes back
|
||||
ItalicRole, // used only inside the delegate
|
||||
TypeRole,
|
||||
StateRole
|
||||
};
|
||||
}
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestTreeItem : public Utils::TreeItem
|
||||
{
|
||||
|
||||
public:
|
||||
enum Type
|
||||
{
|
||||
Root,
|
||||
TestClass,
|
||||
TestFunction,
|
||||
TestDataTag,
|
||||
TestDataFunction,
|
||||
TestSpecialFunction,
|
||||
GTestCase,
|
||||
GTestCaseParameterized,
|
||||
GTestName
|
||||
};
|
||||
|
||||
enum TestState
|
||||
{
|
||||
Enabled = 0x00,
|
||||
Disabled = 0x01,
|
||||
};
|
||||
|
||||
Q_FLAGS(TestState)
|
||||
Q_DECLARE_FLAGS(TestStates, TestState)
|
||||
|
||||
TestTreeItem(const QString &name = QString(), const QString &filePath = QString(),
|
||||
Type type = Root);
|
||||
virtual ~TestTreeItem();
|
||||
TestTreeItem(const TestTreeItem& other);
|
||||
|
||||
virtual QVariant data(int column, int role) const override;
|
||||
virtual bool setData(int column, const QVariant &data, int role) override;
|
||||
bool modifyContent(const TestTreeItem *modified);
|
||||
|
||||
const QString name() const { return m_name; }
|
||||
const QString filePath() const { return m_filePath; }
|
||||
void setLine(unsigned line) { m_line = line;}
|
||||
unsigned line() const { return m_line; }
|
||||
void setColumn(unsigned column) { m_column = column; }
|
||||
unsigned column() const { return m_column; }
|
||||
QString mainFile() const { return m_mainFile; }
|
||||
void setMainFile(const QString &mainFile) { m_mainFile = mainFile; }
|
||||
void setChecked(const Qt::CheckState checked);
|
||||
Qt::CheckState checked() const;
|
||||
Type type() const { return m_type; }
|
||||
void setState(TestStates states) { m_state = states; }
|
||||
TestStates state() const { return m_state; }
|
||||
QList<QString> getChildNames() const;
|
||||
TestTreeItem *parentItem() const;
|
||||
TestTreeItem *childItem(int row) const;
|
||||
|
||||
private:
|
||||
void revalidateCheckState();
|
||||
|
||||
QString m_name;
|
||||
QString m_filePath;
|
||||
Qt::CheckState m_checked;
|
||||
Type m_type;
|
||||
unsigned m_line;
|
||||
unsigned m_column;
|
||||
QString m_mainFile; // main for Quick tests, project file for gtest
|
||||
TestStates m_state;
|
||||
};
|
||||
|
||||
struct TestCodeLocationAndType {
|
||||
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;
|
||||
TestTreeItem::TestStates m_state;
|
||||
};
|
||||
|
||||
typedef QVector<TestCodeLocationAndType> TestCodeLocationList;
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
Q_DECLARE_METATYPE(Autotest::Internal::TestTreeItem *)
|
||||
Q_DECLARE_METATYPE(Autotest::Internal::TestCodeLocationAndType)
|
||||
|
||||
#endif // TESTTREEITEM_H
|
81
src/plugins/autotest/testtreeitemdelegate.cpp
Normal file
@@ -0,0 +1,81 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "testtreeitem.h"
|
||||
#include "testtreeitemdelegate.h"
|
||||
#include "testtreemodel.h"
|
||||
|
||||
#include <QPainter>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestTreeItemDelegate::TestTreeItemDelegate(QObject *parent)
|
||||
: QStyledItemDelegate(parent)
|
||||
{
|
||||
}
|
||||
|
||||
TestTreeItemDelegate::~TestTreeItemDelegate()
|
||||
{
|
||||
}
|
||||
|
||||
void TestTreeItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
|
||||
{
|
||||
QStyleOptionViewItem opt = option;
|
||||
initStyleOption(&opt, index);
|
||||
|
||||
bool italic = index.data(ItalicRole).toBool();
|
||||
if (italic) {
|
||||
QFont font(option.font);
|
||||
font.setItalic(true);
|
||||
opt.font = font;
|
||||
|
||||
// correct margin of items without a checkbox (except for root items)
|
||||
QStyleOptionButton styleOpt;
|
||||
styleOpt.initFrom(opt.widget);
|
||||
const QSize sz; // no text, no icon - we just need the size of the check box
|
||||
QSize cbSize = opt.widget->style()->sizeFromContents(QStyle::CT_CheckBox, &styleOpt, sz);
|
||||
// the 6 results from hard coded margins of the checkbox itself (2x2) and the item (1x2)
|
||||
opt.rect.setLeft(opt.rect.left() + cbSize.width() + 6);
|
||||
|
||||
// HACK make sure the pixels that have been moved right are painted for selections
|
||||
if (opt.state & QStyle::State_Selected) {
|
||||
QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled
|
||||
? QPalette::Normal : QPalette::Disabled;
|
||||
if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active))
|
||||
cg = QPalette::Inactive;
|
||||
painter->fillRect(option.rect, opt.palette.brush(cg, QPalette::Highlight));
|
||||
}
|
||||
}
|
||||
|
||||
// paint disabled googletests in gray
|
||||
if (index.data(StateRole).toInt() & TestTreeItem::Disabled)
|
||||
opt.palette.setColor(QPalette::Text, QColor(0xa0, 0xa0, 0xa0));
|
||||
|
||||
QStyledItemDelegate::paint(painter, opt, index);
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
48
src/plugins/autotest/testtreeitemdelegate.h
Normal file
@@ -0,0 +1,48 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTTREEITEMDELEGATE_H
|
||||
#define TESTTREEITEMDELEGATE_H
|
||||
|
||||
#include <QStyledItemDelegate>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestTreeItemDelegate : public QStyledItemDelegate
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
TestTreeItemDelegate(QObject *parent = 0);
|
||||
~TestTreeItemDelegate();
|
||||
|
||||
public:
|
||||
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTTREEITEMDELEGATE_H
|
947
src/plugins/autotest/testtreemodel.cpp
Normal file
@@ -0,0 +1,947 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "autotestconstants.h"
|
||||
#include "testcodeparser.h"
|
||||
#include "testtreeitem.h"
|
||||
#include "testtreemodel.h"
|
||||
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/session.h>
|
||||
|
||||
#include <qmljs/qmljsmodelmanagerinterface.h>
|
||||
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestTreeModel::TestTreeModel(QObject *parent) :
|
||||
TreeModel(parent),
|
||||
m_autoTestRootItem(new TestTreeItem(tr("Auto Tests"), QString(), TestTreeItem::Root)),
|
||||
m_quickTestRootItem(new TestTreeItem(tr("Qt Quick Tests"), QString(), TestTreeItem::Root)),
|
||||
m_googleTestRootItem(new TestTreeItem(tr("Google Tests"), QString(), TestTreeItem::Root)),
|
||||
m_parser(new TestCodeParser(this)),
|
||||
m_connectionsInitialized(false)
|
||||
{
|
||||
rootItem()->appendChild(m_autoTestRootItem);
|
||||
rootItem()->appendChild(m_quickTestRootItem);
|
||||
rootItem()->appendChild(m_googleTestRootItem);
|
||||
|
||||
connect(m_parser, &TestCodeParser::cacheCleared, this,
|
||||
&TestTreeModel::removeAllTestItems, Qt::QueuedConnection);
|
||||
connect(m_parser, &TestCodeParser::testItemCreated,
|
||||
this, &TestTreeModel::addTestTreeItem, Qt::QueuedConnection);
|
||||
connect(m_parser, &TestCodeParser::testItemModified,
|
||||
this, &TestTreeModel::modifyTestTreeItem, Qt::QueuedConnection);
|
||||
connect(m_parser, &TestCodeParser::testItemsRemoved,
|
||||
this, &TestTreeModel::removeTestTreeItems, Qt::QueuedConnection);
|
||||
connect(m_parser, &TestCodeParser::unnamedQuickTestsUpdated,
|
||||
this, &TestTreeModel::updateUnnamedQuickTest, Qt::QueuedConnection);
|
||||
connect(m_parser, &TestCodeParser::unnamedQuickTestsRemoved,
|
||||
this, &TestTreeModel::removeUnnamedQuickTests, Qt::QueuedConnection);
|
||||
connect(m_parser, &TestCodeParser::gTestsRemoved,
|
||||
this, &TestTreeModel::removeGTests, Qt::QueuedConnection);
|
||||
|
||||
// CppTools::CppModelManagerInterface *cppMM = CppTools::CppModelManagerInterface::instance();
|
||||
// if (cppMM) {
|
||||
// // replace later on by
|
||||
// // cppMM->registerAstProcessor([this](const CplusPlus::Document::Ptr &doc,
|
||||
// // const CPlusPlus::Snapshot &snapshot) {
|
||||
// // checkForQtTestStuff(doc, snapshot);
|
||||
// // });
|
||||
// connect(cppMM, SIGNAL(documentUpdated(CPlusPlus::Document::Ptr)),
|
||||
// this, SLOT(checkForQtTestStuff(CPlusPlus::Document::Ptr)),
|
||||
// Qt::DirectConnection);
|
||||
|
||||
// }
|
||||
}
|
||||
|
||||
static TestTreeModel *m_instance = 0;
|
||||
|
||||
TestTreeModel *TestTreeModel::instance()
|
||||
{
|
||||
if (!m_instance)
|
||||
m_instance = new TestTreeModel;
|
||||
return m_instance;
|
||||
}
|
||||
|
||||
TestTreeModel::~TestTreeModel()
|
||||
{
|
||||
m_instance = 0;
|
||||
}
|
||||
|
||||
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::onStartupProjectChanged);
|
||||
|
||||
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
|
||||
connect(cppMM, &CppTools::CppModelManager::documentUpdated,
|
||||
m_parser, &TestCodeParser::onCppDocumentUpdated, Qt::QueuedConnection);
|
||||
connect(cppMM, &CppTools::CppModelManager::aboutToRemoveFiles,
|
||||
m_parser, &TestCodeParser::removeFiles, Qt::QueuedConnection);
|
||||
connect(cppMM, &CppTools::CppModelManager::projectPartsUpdated,
|
||||
m_parser, &TestCodeParser::onProjectPartsUpdated);
|
||||
|
||||
QmlJS::ModelManagerInterface *qmlJsMM = QmlJS::ModelManagerInterface::instance();
|
||||
connect(qmlJsMM, &QmlJS::ModelManagerInterface::documentUpdated,
|
||||
m_parser, &TestCodeParser::onQmlDocumentUpdated, Qt::QueuedConnection);
|
||||
connect(qmlJsMM, &QmlJS::ModelManagerInterface::aboutToRemoveFiles,
|
||||
m_parser, &TestCodeParser::removeFiles, Qt::QueuedConnection);
|
||||
m_connectionsInitialized = true;
|
||||
}
|
||||
|
||||
void TestTreeModel::disableParsing()
|
||||
{
|
||||
if (!m_refCounter.deref())
|
||||
m_parser->setState(TestCodeParser::Disabled);
|
||||
}
|
||||
|
||||
bool TestTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
|
||||
{
|
||||
if (!index.isValid())
|
||||
return false;
|
||||
|
||||
TestTreeItem *item = static_cast<TestTreeItem *>(index.internalPointer());
|
||||
if (item && item->setData(index.column(), value, role)) {
|
||||
emit dataChanged(index, index);
|
||||
if (role == Qt::CheckStateRole) {
|
||||
switch (item->type()) {
|
||||
case TestTreeItem::TestClass:
|
||||
case TestTreeItem::GTestCase:
|
||||
case TestTreeItem::GTestCaseParameterized:
|
||||
if (item->childCount() > 0)
|
||||
emit dataChanged(index.child(0, 0), index.child(item->childCount() - 1, 0));
|
||||
break;
|
||||
case TestTreeItem::TestFunction:
|
||||
case TestTreeItem::GTestName:
|
||||
emit dataChanged(index.parent(), index.parent());
|
||||
break;
|
||||
default: // avoid warning regarding unhandled enum member
|
||||
break;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::ItemFlags TestTreeModel::flags(const QModelIndex &index) const
|
||||
{
|
||||
if (!index.isValid())
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
|
||||
TestTreeItem *item = static_cast<TestTreeItem *>(itemForIndex(index));
|
||||
switch(item->type()) {
|
||||
case TestTreeItem::TestClass:
|
||||
case TestTreeItem::GTestCase:
|
||||
case TestTreeItem::GTestCaseParameterized:
|
||||
if (item->name().isEmpty())
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsTristate | Qt::ItemIsUserCheckable;
|
||||
case TestTreeItem::TestFunction:
|
||||
case TestTreeItem::GTestName:
|
||||
if (item->parentItem()->name().isEmpty())
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
|
||||
case TestTreeItem::Root:
|
||||
return Qt::ItemIsEnabled;
|
||||
case TestTreeItem::TestDataFunction:
|
||||
case TestTreeItem::TestSpecialFunction:
|
||||
case TestTreeItem::TestDataTag:
|
||||
default:
|
||||
return Qt::ItemIsEnabled | Qt::ItemIsSelectable;
|
||||
}
|
||||
}
|
||||
|
||||
bool TestTreeModel::hasTests() const
|
||||
{
|
||||
return m_autoTestRootItem->childCount() > 0 || m_quickTestRootItem->childCount() > 0
|
||||
|| m_googleTestRootItem->childCount() > 0;
|
||||
}
|
||||
|
||||
QList<TestConfiguration *> TestTreeModel::getAllTestCases() const
|
||||
{
|
||||
QList<TestConfiguration *> result;
|
||||
|
||||
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||
if (!project)
|
||||
return result;
|
||||
|
||||
// get all Auto Tests
|
||||
for (int row = 0, count = m_autoTestRootItem->childCount(); row < count; ++row) {
|
||||
const TestTreeItem *child = m_autoTestRootItem->childItem(row);
|
||||
|
||||
TestConfiguration *tc = new TestConfiguration(child->name(), QStringList(),
|
||||
child->childCount());
|
||||
tc->setMainFilePath(child->filePath());
|
||||
tc->setProject(project);
|
||||
result << tc;
|
||||
}
|
||||
|
||||
// get all Quick Tests
|
||||
QMap<QString, int> foundMains;
|
||||
for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row) {
|
||||
const TestTreeItem *child = m_quickTestRootItem->childItem(row);
|
||||
// unnamed Quick Tests must be handled separately
|
||||
if (child->name().isEmpty()) {
|
||||
for (int childRow = 0, ccount = child->childCount(); childRow < ccount; ++ childRow) {
|
||||
const TestTreeItem *grandChild = child->childItem(childRow);
|
||||
const QString mainFile = grandChild->mainFile();
|
||||
foundMains.insert(mainFile, foundMains.contains(mainFile)
|
||||
? foundMains.value(mainFile) + 1 : 1);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// named Quick Test
|
||||
const QString mainFile = child->mainFile();
|
||||
foundMains.insert(mainFile, foundMains.contains(mainFile)
|
||||
? foundMains.value(mainFile) + child->childCount()
|
||||
: child->childCount());
|
||||
}
|
||||
// create TestConfiguration for each main
|
||||
foreach (const QString &mainFile, foundMains.keys()) {
|
||||
TestConfiguration *tc = new TestConfiguration(QString(), QStringList(),
|
||||
foundMains.value(mainFile));
|
||||
tc->setMainFilePath(mainFile);
|
||||
tc->setProject(project);
|
||||
result << tc;
|
||||
}
|
||||
|
||||
foundMains.clear();
|
||||
|
||||
// get all Google Tests
|
||||
for (int row = 0, count = m_googleTestRootItem->childCount(); row < count; ++row) {
|
||||
const TestTreeItem *child = m_googleTestRootItem->childItem(row);
|
||||
for (int childRow = 0, childCount = child->childCount(); childRow < childCount; ++childRow) {
|
||||
const QString &proFilePath = child->childItem(childRow)->mainFile();
|
||||
foundMains.insert(proFilePath, foundMains.contains(proFilePath)
|
||||
? foundMains.value(proFilePath) + 1 : 1);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const QString &proFile, foundMains.keys()) {
|
||||
TestConfiguration *tc = new TestConfiguration(QString(), QStringList(),
|
||||
foundMains.value(proFile));
|
||||
tc->setProFile(proFile);
|
||||
tc->setProject(project);
|
||||
tc->setTestType(TestTypeGTest);
|
||||
result << tc;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QList<TestConfiguration *> TestTreeModel::getSelectedTests() const
|
||||
{
|
||||
QList<TestConfiguration *> result;
|
||||
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||
if (!project)
|
||||
return result;
|
||||
|
||||
TestConfiguration *testConfiguration = 0;
|
||||
|
||||
for (int row = 0, count = m_autoTestRootItem->childCount(); row < count; ++row) {
|
||||
const TestTreeItem *child = m_autoTestRootItem->childItem(row);
|
||||
|
||||
switch (child->checked()) {
|
||||
case Qt::Unchecked:
|
||||
continue;
|
||||
case Qt::Checked:
|
||||
testConfiguration = new TestConfiguration(child->name(), QStringList(), child->childCount());
|
||||
testConfiguration->setMainFilePath(child->filePath());
|
||||
testConfiguration->setProject(project);
|
||||
result << testConfiguration;
|
||||
continue;
|
||||
case Qt::PartiallyChecked:
|
||||
default:
|
||||
const QString childName = child->name();
|
||||
int grandChildCount = child->childCount();
|
||||
QStringList testCases;
|
||||
for (int grandChildRow = 0; grandChildRow < grandChildCount; ++grandChildRow) {
|
||||
const TestTreeItem *grandChild = child->childItem(grandChildRow);
|
||||
if (grandChild->checked() == Qt::Checked)
|
||||
testCases << grandChild->name();
|
||||
}
|
||||
|
||||
testConfiguration = new TestConfiguration(childName, testCases);
|
||||
testConfiguration->setMainFilePath(child->filePath());
|
||||
testConfiguration->setProject(project);
|
||||
result << testConfiguration;
|
||||
}
|
||||
}
|
||||
// Quick Tests must be handled differently - need the calling cpp file to use this in
|
||||
// addProjectInformation() - additionally this must be unique to not execute the same executable
|
||||
// on and on and on...
|
||||
// TODO: do this later on for Auto Tests as well to support strange setups? or redo the model
|
||||
|
||||
QMap<QString, TestConfiguration *> foundMains;
|
||||
|
||||
if (TestTreeItem *unnamed = unnamedQuickTests()) {
|
||||
for (int childRow = 0, ccount = unnamed->childCount(); childRow < ccount; ++ childRow) {
|
||||
const TestTreeItem *grandChild = unnamed->childItem(childRow);
|
||||
const QString mainFile = grandChild->mainFile();
|
||||
if (foundMains.contains(mainFile)) {
|
||||
QTC_ASSERT(testConfiguration,
|
||||
qWarning() << "Illegal state (unnamed Quick Test listed as named)";
|
||||
return QList<TestConfiguration *>());
|
||||
foundMains[mainFile]->setTestCaseCount(testConfiguration->testCaseCount() + 1);
|
||||
} else {
|
||||
testConfiguration = new TestConfiguration(QString(), QStringList());
|
||||
testConfiguration->setTestCaseCount(1);
|
||||
testConfiguration->setUnnamedOnly(true);
|
||||
testConfiguration->setMainFilePath(mainFile);
|
||||
testConfiguration->setProject(project);
|
||||
foundMains.insert(mainFile, testConfiguration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row) {
|
||||
const TestTreeItem *child = m_quickTestRootItem->childItem(row);
|
||||
// unnamed Quick Tests have been handled separately already
|
||||
if (child->name().isEmpty())
|
||||
continue;
|
||||
|
||||
// named Quick Tests
|
||||
switch (child->checked()) {
|
||||
case Qt::Unchecked:
|
||||
continue;
|
||||
case Qt::Checked:
|
||||
case Qt::PartiallyChecked:
|
||||
default:
|
||||
QStringList testFunctions;
|
||||
int grandChildCount = child->childCount();
|
||||
for (int grandChildRow = 0; grandChildRow < grandChildCount; ++grandChildRow) {
|
||||
const TestTreeItem *grandChild = child->childItem(grandChildRow);
|
||||
if (grandChild->type() != TestTreeItem::TestFunction)
|
||||
continue;
|
||||
testFunctions << child->name() + QLatin1String("::") + grandChild->name();
|
||||
}
|
||||
TestConfiguration *tc;
|
||||
if (foundMains.contains(child->mainFile())) {
|
||||
tc = foundMains[child->mainFile()];
|
||||
QStringList oldFunctions(tc->testCases());
|
||||
// if oldFunctions.size() is 0 this test configuration is used for at least one
|
||||
// unnamed test case
|
||||
if (oldFunctions.size() == 0) {
|
||||
tc->setTestCaseCount(tc->testCaseCount() + testFunctions.size());
|
||||
tc->setUnnamedOnly(false);
|
||||
} else {
|
||||
oldFunctions << testFunctions;
|
||||
tc->setTestCases(oldFunctions);
|
||||
}
|
||||
} else {
|
||||
tc = new TestConfiguration(QString(), testFunctions);
|
||||
tc->setMainFilePath(child->mainFile());
|
||||
tc->setProject(project);
|
||||
foundMains.insert(child->mainFile(), tc);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
foreach (TestConfiguration *config, foundMains.values())
|
||||
if (!config->unnamedOnly())
|
||||
result << config;
|
||||
|
||||
// get selected Google Tests
|
||||
QMap<QString, QStringList> proFilesWithEnabledTestSets;
|
||||
|
||||
for (int row = 0, count = m_googleTestRootItem->childCount(); row < count; ++row) {
|
||||
const TestTreeItem *child = m_googleTestRootItem->childItem(row);
|
||||
if (child->checked() == Qt::Unchecked) // add this test name to disabled list ?
|
||||
continue;
|
||||
|
||||
int grandChildCount = child->childCount();
|
||||
for (int grandChildRow = 0; grandChildRow < grandChildCount; ++grandChildRow) {
|
||||
const TestTreeItem *grandChild = child->childItem(grandChildRow);
|
||||
const QString &proFile = grandChild->mainFile();
|
||||
QStringList enabled = proFilesWithEnabledTestSets.value(proFile);
|
||||
if (grandChild->checked() == Qt::Checked) {
|
||||
QString testSpecifier = child->name() + QLatin1Char('.') + grandChild->name();
|
||||
if (child->type() == TestTreeItem::GTestCaseParameterized) {
|
||||
testSpecifier.prepend(QLatin1String("*/"));
|
||||
testSpecifier.append(QLatin1String("/*"));
|
||||
}
|
||||
enabled << testSpecifier;
|
||||
}
|
||||
proFilesWithEnabledTestSets.insert(proFile, enabled);
|
||||
}
|
||||
}
|
||||
|
||||
foreach (const QString &proFile, proFilesWithEnabledTestSets.keys()) {
|
||||
TestConfiguration *tc = new TestConfiguration(QString(),
|
||||
proFilesWithEnabledTestSets.value(proFile));
|
||||
tc->setTestType(TestTypeGTest);
|
||||
tc->setProFile(proFile);
|
||||
tc->setProject(project);
|
||||
result << tc;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
TestConfiguration *TestTreeModel::getTestConfiguration(const TestTreeItem *item) const
|
||||
{
|
||||
QTC_ASSERT(item != 0, return 0);
|
||||
ProjectExplorer::Project *project = ProjectExplorer::SessionManager::startupProject();
|
||||
QTC_ASSERT(project, return 0);
|
||||
|
||||
TestConfiguration *config = 0;
|
||||
switch (item->type()) {
|
||||
case TestTreeItem::TestClass: {
|
||||
if (item->parent() == m_quickTestRootItem) {
|
||||
// Quick Test TestCase
|
||||
QStringList testFunctions;
|
||||
for (int row = 0, count = item->childCount(); row < count; ++row) {
|
||||
testFunctions << item->name() + QLatin1String("::")
|
||||
+ item->childItem(row)->name();
|
||||
}
|
||||
config = new TestConfiguration(QString(), testFunctions);
|
||||
config->setMainFilePath(item->mainFile());
|
||||
config->setProject(project);
|
||||
} else {
|
||||
// normal auto test
|
||||
config = new TestConfiguration(item->name(), QStringList(), item->childCount());
|
||||
config->setMainFilePath(item->filePath());
|
||||
config->setProject(project);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TestTreeItem::TestFunction: {
|
||||
const TestTreeItem *parent = item->parentItem();
|
||||
if (parent->parent() == m_quickTestRootItem) {
|
||||
// it's a Quick Test function of a named TestCase
|
||||
QStringList testFunction(parent->name() + QLatin1String("::") + item->name());
|
||||
config = new TestConfiguration(QString(), testFunction);
|
||||
config->setMainFilePath(parent->mainFile());
|
||||
config->setProject(project);
|
||||
} else {
|
||||
// normal auto test
|
||||
config = new TestConfiguration(parent->name(), QStringList() << item->name());
|
||||
config->setMainFilePath(parent->filePath());
|
||||
config->setProject(project);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TestTreeItem::TestDataTag: {
|
||||
const TestTreeItem *function = item->parentItem();
|
||||
const TestTreeItem *parent = function ? function->parentItem() : 0;
|
||||
if (!parent)
|
||||
return 0;
|
||||
const QString functionWithTag = function->name() + QLatin1Char(':') + item->name();
|
||||
config = new TestConfiguration(parent->name(), QStringList() << functionWithTag);
|
||||
config->setMainFilePath(parent->filePath());
|
||||
config->setProject(project);
|
||||
break;
|
||||
}
|
||||
case TestTreeItem::GTestCase:
|
||||
case TestTreeItem::GTestCaseParameterized: {
|
||||
QString testSpecifier = item->name() + QLatin1String(".*");
|
||||
if (item->type() == TestTreeItem::GTestCaseParameterized)
|
||||
testSpecifier.prepend(QLatin1String("*/"));
|
||||
|
||||
if (int childCount = item->childCount()) {
|
||||
config = new TestConfiguration(QString(), QStringList(testSpecifier));
|
||||
config->setTestCaseCount(childCount);
|
||||
config->setProFile(item->childItem(0)->mainFile());
|
||||
config->setProject(project);
|
||||
config->setTestType(TestTypeGTest);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case TestTreeItem::GTestName: {
|
||||
const TestTreeItem *parent = item->parentItem();
|
||||
QString testSpecifier = parent->name() + QLatin1Char('.') + item->name();
|
||||
|
||||
if (parent->type() == TestTreeItem::GTestCaseParameterized) {
|
||||
testSpecifier.prepend(QLatin1String("*/"));
|
||||
testSpecifier.append(QLatin1String("/*"));
|
||||
}
|
||||
config = new TestConfiguration(QString(), QStringList(testSpecifier));
|
||||
config->setProFile(item->mainFile());
|
||||
config->setProject(project);
|
||||
config->setTestType(TestTypeGTest);
|
||||
break;
|
||||
}
|
||||
// not supported items
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
return config;
|
||||
}
|
||||
|
||||
QString TestTreeModel::getMainFileForUnnamedQuickTest(const QString &qmlFile) const
|
||||
{
|
||||
const TestTreeItem *unnamed = unnamedQuickTests();
|
||||
const int count = unnamed ? unnamed->childCount() : 0;
|
||||
for (int row = 0; row < count; ++row) {
|
||||
const TestTreeItem *child = unnamed->childItem(row);
|
||||
if (qmlFile == child->filePath())
|
||||
return child->mainFile();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
||||
void TestTreeModel::qmlFilesForMainFile(const QString &mainFile, QSet<QString> *filePaths) const
|
||||
{
|
||||
const TestTreeItem *unnamed = unnamedQuickTests();
|
||||
if (!unnamed)
|
||||
return;
|
||||
for (int row = 0, count = unnamed->childCount(); row < count; ++row) {
|
||||
const TestTreeItem *child = unnamed->childItem(row);
|
||||
if (child->mainFile() == mainFile)
|
||||
filePaths->insert(child->filePath());
|
||||
}
|
||||
}
|
||||
|
||||
QList<QString> TestTreeModel::getUnnamedQuickTestFunctions() const
|
||||
{
|
||||
const TestTreeItem *unnamed = unnamedQuickTests();
|
||||
if (unnamed)
|
||||
return unnamed->getChildNames();
|
||||
return QList<QString>();
|
||||
}
|
||||
|
||||
bool TestTreeModel::hasUnnamedQuickTests() const
|
||||
{
|
||||
for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row)
|
||||
if (m_quickTestRootItem->childItem(row)->name().isEmpty())
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
TestTreeItem *TestTreeModel::unnamedQuickTests() const
|
||||
{
|
||||
for (int row = 0, count = m_quickTestRootItem->childCount(); row < count; ++row) {
|
||||
TestTreeItem *child = m_quickTestRootItem->childItem(row);
|
||||
if (child->name().isEmpty())
|
||||
return child;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TestTreeModel::removeUnnamedQuickTests(const QString &filePath)
|
||||
{
|
||||
TestTreeItem *unnamedQT = unnamedQuickTests();
|
||||
if (!unnamedQT)
|
||||
return;
|
||||
|
||||
for (int childRow = unnamedQT->childCount() - 1; childRow >= 0; --childRow) {
|
||||
TestTreeItem *child = unnamedQT->childItem(childRow);
|
||||
if (filePath == child->filePath())
|
||||
delete takeItem(child);
|
||||
}
|
||||
|
||||
if (unnamedQT->childCount() == 0)
|
||||
delete takeItem(unnamedQT);
|
||||
emit testTreeModelChanged();
|
||||
}
|
||||
|
||||
void TestTreeModel::removeGTests(const QString &filePath)
|
||||
{
|
||||
for (int childRow = m_googleTestRootItem->childCount() - 1; childRow >= 0; --childRow) {
|
||||
TestTreeItem *child = m_googleTestRootItem->childItem(childRow);
|
||||
for (int grandChildRow = child->childCount() - 1; grandChildRow >= 0; --grandChildRow) {
|
||||
TestTreeItem *grandChild = child->childItem(grandChildRow);
|
||||
if (filePath == grandChild->filePath())
|
||||
delete takeItem(grandChild);
|
||||
}
|
||||
if (child->childCount() == 0)
|
||||
delete takeItem(child);
|
||||
}
|
||||
emit testTreeModelChanged();
|
||||
}
|
||||
|
||||
void TestTreeModel::addTestTreeItem(TestTreeItem *item, TestTreeModel::Type type)
|
||||
{
|
||||
TestTreeItem *parent = rootItemForType(type);
|
||||
if (type == TestTreeModel::GoogleTest) {
|
||||
// check if there's already an item with the same test name...
|
||||
TestTreeItem *toBeUpdated = 0;
|
||||
for (int row = 0, count = parent->childCount(); row < count; ++row) {
|
||||
TestTreeItem *current = parent->childItem(row);
|
||||
if (current->name() == item->name() && current->type() == item->type()) {
|
||||
toBeUpdated = current;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// ...if so we have, to update this one instead of adding a new item
|
||||
if (toBeUpdated) {
|
||||
for (int row = 0, count = item->childCount(); row < count; ++row)
|
||||
toBeUpdated->appendChild(new TestTreeItem(*item->childItem(row)));
|
||||
delete item;
|
||||
} else {
|
||||
parent->appendChild(item);
|
||||
}
|
||||
} else {
|
||||
parent->appendChild(item);
|
||||
}
|
||||
emit testTreeModelChanged();
|
||||
}
|
||||
|
||||
void TestTreeModel::updateUnnamedQuickTest(const QString &mainFile,
|
||||
const QMap<QString, TestCodeLocationAndType> &functions)
|
||||
{
|
||||
if (functions.isEmpty())
|
||||
return;
|
||||
|
||||
if (!hasUnnamedQuickTests())
|
||||
addTestTreeItem(new TestTreeItem(QString(), QString(), TestTreeItem::TestClass), QuickTest);
|
||||
|
||||
TestTreeItem *unnamed = unnamedQuickTests();
|
||||
foreach (const QString &functionName, functions.keys()) {
|
||||
const TestCodeLocationAndType locationAndType = functions.value(functionName);
|
||||
TestTreeItem *testFunction = new TestTreeItem(functionName, locationAndType.m_name,
|
||||
locationAndType.m_type);
|
||||
testFunction->setLine(locationAndType.m_line);
|
||||
testFunction->setColumn(locationAndType.m_column);
|
||||
testFunction->setMainFile(mainFile);
|
||||
unnamed->appendChild(testFunction);
|
||||
}
|
||||
}
|
||||
|
||||
void TestTreeModel::modifyTestTreeItem(TestTreeItem *item, TestTreeModel::Type type, const QStringList &files)
|
||||
{
|
||||
QModelIndex index = rootIndexForType(type);
|
||||
TestTreeItem *parent = rootItemForType(type);
|
||||
if (files.isEmpty()) {
|
||||
if (TestTreeItem *unnamed = unnamedQuickTests()) {
|
||||
if (unnamed == item) // no need to update or delete
|
||||
return;
|
||||
|
||||
index = indexForItem(unnamed);
|
||||
modifyTestSubtree(index, item);
|
||||
}
|
||||
} else {
|
||||
for (int row = 0; row < parent->childCount(); ++row) {
|
||||
if (files.contains(parent->childItem(row)->filePath())) {
|
||||
index = index.child(row, 0);
|
||||
modifyTestSubtree(index, item);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
// item was created as temporary, destroy it if it won't get destroyed by its parent
|
||||
if (!item->parent())
|
||||
delete item;
|
||||
}
|
||||
|
||||
void TestTreeModel::removeAllTestItems()
|
||||
{
|
||||
m_autoTestRootItem->removeChildren();
|
||||
m_quickTestRootItem->removeChildren();
|
||||
m_googleTestRootItem->removeChildren();
|
||||
emit testTreeModelChanged();
|
||||
}
|
||||
|
||||
void TestTreeModel::removeTestTreeItems(const QString &filePath, Type type)
|
||||
{
|
||||
bool removed = false;
|
||||
const TestTreeItem *rootItem = rootItemForType(type);
|
||||
for (int row = rootItem->childCount() - 1; row >= 0; --row) {
|
||||
TestTreeItem *childItem = rootItem->childItem(row);
|
||||
if (filePath == childItem->filePath()) {
|
||||
delete takeItem(childItem);
|
||||
removed = true;
|
||||
}
|
||||
}
|
||||
if (removed)
|
||||
emit testTreeModelChanged();
|
||||
}
|
||||
|
||||
TestTreeItem *TestTreeModel::rootItemForType(TestTreeModel::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case AutoTest:
|
||||
return m_autoTestRootItem;
|
||||
case QuickTest:
|
||||
return m_quickTestRootItem;
|
||||
case GoogleTest:
|
||||
return m_googleTestRootItem;
|
||||
}
|
||||
QTC_ASSERT(false, return 0);
|
||||
}
|
||||
|
||||
QModelIndex TestTreeModel::rootIndexForType(TestTreeModel::Type type)
|
||||
{
|
||||
switch (type) {
|
||||
case AutoTest:
|
||||
return index(0, 0);
|
||||
case QuickTest:
|
||||
return index(1, 0);
|
||||
case GoogleTest:
|
||||
return index(2, 0);
|
||||
}
|
||||
QTC_ASSERT(false, return QModelIndex());
|
||||
}
|
||||
|
||||
void TestTreeModel::modifyTestSubtree(QModelIndex &toBeModifiedIndex, const TestTreeItem *newItem)
|
||||
{
|
||||
if (!toBeModifiedIndex.isValid())
|
||||
return;
|
||||
|
||||
TestTreeItem *toBeModifiedItem = static_cast<TestTreeItem *>(itemForIndex(toBeModifiedIndex));
|
||||
if (toBeModifiedItem->modifyContent(newItem))
|
||||
emit dataChanged(toBeModifiedIndex, toBeModifiedIndex,
|
||||
QVector<int>() << Qt::DisplayRole << Qt::ToolTipRole << LinkRole);
|
||||
|
||||
// process sub-items as well...
|
||||
const int childCount = toBeModifiedItem->childCount();
|
||||
const int newChildCount = newItem->childCount();
|
||||
|
||||
// for keeping the CheckState on modifications
|
||||
// TODO might still fail for duplicate entries
|
||||
QHash<QString, Qt::CheckState> checkStates;
|
||||
for (int row = 0; row < childCount; ++row) {
|
||||
const TestTreeItem *child = toBeModifiedItem->childItem(row);
|
||||
checkStates.insert(child->name(), child->checked());
|
||||
}
|
||||
|
||||
if (childCount <= newChildCount) {
|
||||
processChildren(toBeModifiedIndex, newItem, childCount, checkStates);
|
||||
// add additional items
|
||||
for (int row = childCount; row < newChildCount; ++row) {
|
||||
const TestTreeItem *newChild = newItem->childItem(row);
|
||||
TestTreeItem *toBeAdded = new TestTreeItem(*newChild);
|
||||
if (checkStates.contains(toBeAdded->name())
|
||||
&& checkStates.value(toBeAdded->name()) != Qt::Checked)
|
||||
toBeAdded->setChecked(checkStates.value(toBeAdded->name()));
|
||||
toBeModifiedItem->appendChild(toBeAdded);
|
||||
}
|
||||
} else {
|
||||
processChildren(toBeModifiedIndex, newItem, newChildCount, checkStates);
|
||||
// remove rest of the items
|
||||
for (int row = childCount - 1; row > newChildCount; --row)
|
||||
delete takeItem(toBeModifiedItem->childItem(row));
|
||||
}
|
||||
emit testTreeModelChanged();
|
||||
}
|
||||
|
||||
void TestTreeModel::processChildren(QModelIndex &parentIndex, const TestTreeItem *newItem,
|
||||
const int upperBound,
|
||||
const QHash<QString, Qt::CheckState> &checkStates)
|
||||
{
|
||||
static QVector<int> modificationRoles = QVector<int>() << Qt::DisplayRole
|
||||
<< Qt::ToolTipRole
|
||||
<< LinkRole;
|
||||
TestTreeItem *toBeModifiedItem = static_cast<TestTreeItem *>(itemForIndex(parentIndex));
|
||||
for (int row = 0; row < upperBound; ++row) {
|
||||
QModelIndex child = parentIndex.child(row, 0);
|
||||
TestTreeItem *toBeModifiedChild = toBeModifiedItem->childItem(row);
|
||||
const TestTreeItem *modifiedChild = newItem->childItem(row);
|
||||
if (toBeModifiedChild->modifyContent(modifiedChild))
|
||||
emit dataChanged(child, child, modificationRoles);
|
||||
|
||||
// handle data tags - just remove old and add them
|
||||
if (modifiedChild->childCount() || toBeModifiedChild->childCount()) {
|
||||
toBeModifiedChild->removeChildren();
|
||||
const int count = modifiedChild->childCount();
|
||||
for (int childRow = 0; childRow < count; ++childRow)
|
||||
toBeModifiedChild->appendChild(new TestTreeItem(*modifiedChild->childItem(childRow)));
|
||||
}
|
||||
|
||||
if (checkStates.contains(toBeModifiedChild->name())) {
|
||||
Qt::CheckState state = checkStates.value(toBeModifiedChild->name());
|
||||
if (state != toBeModifiedChild->checked()) {
|
||||
toBeModifiedChild->setChecked(state);
|
||||
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
|
||||
}
|
||||
} else { // newly added (BAD: happens for renaming as well)
|
||||
toBeModifiedChild->setChecked(Qt::Checked);
|
||||
emit dataChanged(child, child, QVector<int>() << Qt::CheckStateRole);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
int TestTreeModel::autoTestsCount() const
|
||||
{
|
||||
return m_autoTestRootItem ? m_autoTestRootItem->childCount() : 0;
|
||||
}
|
||||
|
||||
int TestTreeModel::namedQuickTestsCount() const
|
||||
{
|
||||
return m_quickTestRootItem
|
||||
? m_quickTestRootItem->childCount() - (hasUnnamedQuickTests() ? 1 : 0)
|
||||
: 0;
|
||||
}
|
||||
|
||||
int TestTreeModel::unnamedQuickTestsCount() const
|
||||
{
|
||||
if (TestTreeItem *unnamed = unnamedQuickTests())
|
||||
return unnamed->childCount();
|
||||
return 0;
|
||||
}
|
||||
|
||||
int TestTreeModel::dataTagsCount() const
|
||||
{
|
||||
int dataTagCount = 0;
|
||||
foreach (Utils::TreeItem *item, m_autoTestRootItem->children()) {
|
||||
TestTreeItem *classItem = static_cast<TestTreeItem *>(item);
|
||||
foreach (Utils::TreeItem *functionItem, classItem->children())
|
||||
dataTagCount += functionItem->childCount();
|
||||
}
|
||||
return dataTagCount;
|
||||
}
|
||||
|
||||
int TestTreeModel::gtestNamesCount() const
|
||||
{
|
||||
return m_googleTestRootItem ? m_googleTestRootItem->childCount() : 0;
|
||||
}
|
||||
|
||||
QMultiMap<QString, int> TestTreeModel::gtestNamesAndSets() const
|
||||
{
|
||||
QMultiMap<QString, int> result;
|
||||
|
||||
if (m_googleTestRootItem) {
|
||||
for (int row = 0, count = m_googleTestRootItem->childCount(); row < count; ++row) {
|
||||
const TestTreeItem *current = m_googleTestRootItem->childItem(row);
|
||||
result.insert(current->name(), current->childCount());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
#endif
|
||||
|
||||
/***************************** Sort/Filter Model **********************************/
|
||||
|
||||
TestTreeSortFilterModel::TestTreeSortFilterModel(TestTreeModel *sourceModel, QObject *parent)
|
||||
: QSortFilterProxyModel(parent),
|
||||
m_sourceModel(sourceModel),
|
||||
m_sortMode(Alphabetically),
|
||||
m_filterMode(Basic)
|
||||
{
|
||||
setSourceModel(sourceModel);
|
||||
}
|
||||
|
||||
void TestTreeSortFilterModel::setSortMode(SortMode sortMode)
|
||||
{
|
||||
m_sortMode = sortMode;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
void TestTreeSortFilterModel::setFilterMode(FilterMode filterMode)
|
||||
{
|
||||
m_filterMode = filterMode;
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
void TestTreeSortFilterModel::toggleFilter(FilterMode filterMode)
|
||||
{
|
||||
m_filterMode = toFilterMode(m_filterMode ^ filterMode);
|
||||
invalidateFilter();
|
||||
}
|
||||
|
||||
TestTreeSortFilterModel::FilterMode TestTreeSortFilterModel::toFilterMode(int f)
|
||||
{
|
||||
switch (f) {
|
||||
case TestTreeSortFilterModel::ShowInitAndCleanup:
|
||||
return TestTreeSortFilterModel::ShowInitAndCleanup;
|
||||
case TestTreeSortFilterModel::ShowTestData:
|
||||
return TestTreeSortFilterModel::ShowTestData;
|
||||
case TestTreeSortFilterModel::ShowAll:
|
||||
return TestTreeSortFilterModel::ShowAll;
|
||||
default:
|
||||
return TestTreeSortFilterModel::Basic;
|
||||
}
|
||||
}
|
||||
|
||||
bool TestTreeSortFilterModel::lessThan(const QModelIndex &left, const QModelIndex &right) const
|
||||
{
|
||||
// root items keep the intended order: 1st Auto Tests, 2nd Quick Tests
|
||||
const TestTreeItem *leftItem = static_cast<TestTreeItem *>(left.internalPointer());
|
||||
if (leftItem->type() == TestTreeItem::Root)
|
||||
return left.row() > right.row();
|
||||
|
||||
const QString leftVal = m_sourceModel->data(left, Qt::DisplayRole).toString();
|
||||
const QString rightVal = m_sourceModel->data(right, Qt::DisplayRole).toString();
|
||||
|
||||
// unnamed Quick Tests will always be listed first
|
||||
if (leftVal == tr(Constants::UNNAMED_QUICKTESTS))
|
||||
return false;
|
||||
if (rightVal == tr(Constants::UNNAMED_QUICKTESTS))
|
||||
return true;
|
||||
|
||||
switch (m_sortMode) {
|
||||
case Alphabetically:
|
||||
if (leftVal == rightVal)
|
||||
return left.row() > right.row();
|
||||
return leftVal > rightVal;
|
||||
case Naturally: {
|
||||
const TextEditor::TextEditorWidget::Link leftLink =
|
||||
m_sourceModel->data(left, LinkRole).value<TextEditor::TextEditorWidget::Link>();
|
||||
const TextEditor::TextEditorWidget::Link rightLink =
|
||||
m_sourceModel->data(right, LinkRole).value<TextEditor::TextEditorWidget::Link>();
|
||||
|
||||
if (leftLink.targetFileName == rightLink.targetFileName) {
|
||||
return leftLink.targetLine == rightLink.targetLine
|
||||
? leftLink.targetColumn > rightLink.targetColumn
|
||||
: leftLink.targetLine > rightLink.targetLine;
|
||||
} else {
|
||||
return leftLink.targetFileName > rightLink.targetFileName;
|
||||
}
|
||||
}
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
bool TestTreeSortFilterModel::filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
||||
{
|
||||
QModelIndex index = m_sourceModel->index(sourceRow, 0,sourceParent);
|
||||
if (!index.isValid())
|
||||
return false;
|
||||
|
||||
const TestTreeItem *item = static_cast<TestTreeItem *>(index.internalPointer());
|
||||
|
||||
switch (item->type()) {
|
||||
case TestTreeItem::TestDataFunction:
|
||||
return m_filterMode & ShowTestData;
|
||||
case TestTreeItem::TestSpecialFunction:
|
||||
return m_filterMode & ShowInitAndCleanup;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
152
src/plugins/autotest/testtreemodel.h
Normal file
@@ -0,0 +1,152 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTTREEMODEL_H
|
||||
#define TESTTREEMODEL_H
|
||||
|
||||
#include "testconfiguration.h"
|
||||
|
||||
#include <cplusplus/CppDocument.h>
|
||||
|
||||
#include <utils/treemodel.h>
|
||||
|
||||
#include <QSortFilterProxyModel>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
struct TestCodeLocationAndType;
|
||||
class TestCodeParser;
|
||||
class TestInfo;
|
||||
class TestTreeItem;
|
||||
|
||||
class TestTreeModel : public Utils::TreeModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum Type {
|
||||
AutoTest,
|
||||
QuickTest,
|
||||
GoogleTest
|
||||
};
|
||||
|
||||
static TestTreeModel* instance();
|
||||
~TestTreeModel();
|
||||
void enableParsing();
|
||||
void disableParsing();
|
||||
|
||||
bool setData(const QModelIndex &index, const QVariant &value, int role) override;
|
||||
Qt::ItemFlags flags(const QModelIndex &index) const override;
|
||||
|
||||
TestCodeParser *parser() const { return m_parser; }
|
||||
bool hasTests() const;
|
||||
QList<TestConfiguration *> getAllTestCases() const;
|
||||
QList<TestConfiguration *> getSelectedTests() const;
|
||||
TestConfiguration *getTestConfiguration(const TestTreeItem *item) const;
|
||||
QString getMainFileForUnnamedQuickTest(const QString &qmlFile) const;
|
||||
void qmlFilesForMainFile(const QString &mainFile, QSet<QString> *filePaths) const;
|
||||
QList<QString> getUnnamedQuickTestFunctions() const;
|
||||
bool hasUnnamedQuickTests() const;
|
||||
|
||||
#ifdef WITH_TESTS
|
||||
int autoTestsCount() const;
|
||||
int namedQuickTestsCount() const;
|
||||
int unnamedQuickTestsCount() const;
|
||||
int dataTagsCount() const;
|
||||
int gtestNamesCount() const;
|
||||
QMultiMap<QString, int> gtestNamesAndSets() const;
|
||||
#endif
|
||||
|
||||
signals:
|
||||
void testTreeModelChanged();
|
||||
|
||||
public slots:
|
||||
|
||||
private:
|
||||
void addTestTreeItem(TestTreeItem *item, Type type);
|
||||
void updateUnnamedQuickTest(const QString &mainFile,
|
||||
const QMap<QString, TestCodeLocationAndType> &functions);
|
||||
void modifyTestTreeItem(TestTreeItem *item, Type type, const QStringList &file);
|
||||
void removeAllTestItems();
|
||||
void removeTestTreeItems(const QString &filePath, Type type);
|
||||
void removeUnnamedQuickTests(const QString &filePath);
|
||||
void removeGTests(const QString &filePath);
|
||||
|
||||
TestTreeItem *unnamedQuickTests() const;
|
||||
TestTreeItem *rootItemForType(Type type);
|
||||
QModelIndex rootIndexForType(Type type);
|
||||
|
||||
explicit TestTreeModel(QObject *parent = 0);
|
||||
void modifyTestSubtree(QModelIndex &toBeModifiedIndex, const TestTreeItem *newItem);
|
||||
void processChildren(QModelIndex &parentIndex, const TestTreeItem *newItem,
|
||||
const int upperBound, const QHash<QString, Qt::CheckState> &checkStates);
|
||||
|
||||
TestTreeItem *m_autoTestRootItem;
|
||||
TestTreeItem *m_quickTestRootItem;
|
||||
TestTreeItem *m_googleTestRootItem;
|
||||
TestCodeParser *m_parser;
|
||||
bool m_connectionsInitialized;
|
||||
QAtomicInt m_refCounter;
|
||||
};
|
||||
|
||||
class TestTreeSortFilterModel : public QSortFilterProxyModel
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
enum SortMode {
|
||||
Alphabetically,
|
||||
Naturally
|
||||
};
|
||||
|
||||
enum FilterMode {
|
||||
Basic,
|
||||
ShowInitAndCleanup = 0x01,
|
||||
ShowTestData = 0x02,
|
||||
ShowAll = ShowInitAndCleanup | ShowTestData
|
||||
};
|
||||
|
||||
TestTreeSortFilterModel(TestTreeModel *sourceModel, QObject *parent = 0);
|
||||
void setSortMode(SortMode sortMode);
|
||||
void setFilterMode(FilterMode filterMode);
|
||||
void toggleFilter(FilterMode filterMode);
|
||||
static FilterMode toFilterMode(int f);
|
||||
|
||||
protected:
|
||||
bool lessThan(const QModelIndex &left, const QModelIndex &right) const;
|
||||
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const;
|
||||
|
||||
private:
|
||||
TestTreeModel *m_sourceModel;
|
||||
SortMode m_sortMode;
|
||||
FilterMode m_filterMode;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
Q_DECLARE_METATYPE(Autotest::Internal::TestTreeModel::Type)
|
||||
|
||||
#endif // TESTTREEMODEL_H
|
107
src/plugins/autotest/testtreeview.cpp
Normal file
@@ -0,0 +1,107 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "autotestconstants.h"
|
||||
#include "testcodeparser.h"
|
||||
#include "testrunner.h"
|
||||
#include "testtreeitem.h"
|
||||
#include "testtreeitemdelegate.h"
|
||||
#include "testtreemodel.h"
|
||||
#include "testtreeview.h"
|
||||
|
||||
#include <coreplugin/coreconstants.h>
|
||||
#include <coreplugin/icore.h>
|
||||
|
||||
#include <coreplugin/find/itemviewfind.h>
|
||||
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
|
||||
#include <projectexplorer/project.h>
|
||||
#include <projectexplorer/session.h>
|
||||
|
||||
#include <qmljstools/qmljsmodelmanager.h>
|
||||
|
||||
#include <texteditor/texteditor.h>
|
||||
|
||||
#include <QToolButton>
|
||||
#include <QVBoxLayout>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
TestTreeView::TestTreeView(QWidget *parent)
|
||||
: NavigationTreeView(parent),
|
||||
m_context(new Core::IContext(this))
|
||||
{
|
||||
setExpandsOnDoubleClick(false);
|
||||
m_context->setWidget(this);
|
||||
m_context->setContext(Core::Context(Constants::AUTOTEST_CONTEXT));
|
||||
Core::ICore::addContextObject(m_context);
|
||||
}
|
||||
|
||||
void TestTreeView::selectAll()
|
||||
{
|
||||
changeCheckStateAll(Qt::Checked);
|
||||
}
|
||||
|
||||
void TestTreeView::deselectAll()
|
||||
{
|
||||
changeCheckStateAll(Qt::Unchecked);
|
||||
}
|
||||
|
||||
// this avoids the re-evaluation of parent nodes when modifying the child nodes (setData())
|
||||
void TestTreeView::changeCheckStateAll(const Qt::CheckState checkState)
|
||||
{
|
||||
const TestTreeModel *model = TestTreeModel::instance();
|
||||
|
||||
// 3 == Auto Tests, Quick Tests and Google Tests - must be raised if there will be others
|
||||
for (int rootRow = 0; rootRow < 3; ++rootRow) {
|
||||
QModelIndex currentRootIndex = model->index(rootRow, 0, rootIndex());
|
||||
if (!currentRootIndex.isValid())
|
||||
return;
|
||||
int count = model->rowCount(currentRootIndex);
|
||||
QModelIndex last;
|
||||
for (int classesRow = 0; classesRow < count; ++classesRow) {
|
||||
const QModelIndex classesIndex = model->index(classesRow, 0, currentRootIndex);
|
||||
int funcCount = model->rowCount(classesIndex);
|
||||
TestTreeItem *item = static_cast<TestTreeItem *>(classesIndex.internalPointer());
|
||||
if (item) {
|
||||
item->setChecked(checkState);
|
||||
if (!item->childCount())
|
||||
last = classesIndex;
|
||||
}
|
||||
for (int functionRow = 0; functionRow < funcCount; ++functionRow) {
|
||||
last = model->index(functionRow, 0, classesIndex);
|
||||
TestTreeItem *item = static_cast<TestTreeItem *>(last.internalPointer());
|
||||
if (item)
|
||||
item->setChecked(checkState);
|
||||
}
|
||||
}
|
||||
emit dataChanged(currentRootIndex, last);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
56
src/plugins/autotest/testtreeview.h
Normal file
@@ -0,0 +1,56 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTTREEVIEW_H
|
||||
#define TESTTREEVIEW_H
|
||||
|
||||
#include <utils/navigationtreeview.h>
|
||||
|
||||
namespace Core {
|
||||
class IContext;
|
||||
}
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestTreeView : public Utils::NavigationTreeView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
TestTreeView(QWidget *parent = 0);
|
||||
|
||||
void selectAll();
|
||||
void deselectAll();
|
||||
|
||||
private:
|
||||
void changeCheckStateAll(const Qt::CheckState checkState);
|
||||
Core::IContext *m_context;
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTTREEVIEW_H
|
405
src/plugins/autotest/testvisitor.cpp
Normal file
@@ -0,0 +1,405 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "autotest_utils.h"
|
||||
#include "testvisitor.h"
|
||||
|
||||
#include <cplusplus/FullySpecifiedType.h>
|
||||
#include <cplusplus/LookupContext.h>
|
||||
#include <cplusplus/Symbols.h>
|
||||
#include <cplusplus/TypeOfExpression.h>
|
||||
|
||||
#include <cpptools/cppmodelmanager.h>
|
||||
|
||||
#include <qmljs/parser/qmljsast_p.h>
|
||||
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include <QList>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
// names of special functions (applies for QTest as well as Quick Tests)
|
||||
static QList<QString> specialFunctions = QList<QString>() << QLatin1String("initTestCase")
|
||||
<< QLatin1String("cleanupTestCase")
|
||||
<< QLatin1String("init")
|
||||
<< QLatin1String("cleanup");
|
||||
|
||||
/************************** Cpp Test Symbol Visitor ***************************/
|
||||
|
||||
TestVisitor::TestVisitor(const QString &fullQualifiedClassName)
|
||||
: m_className(fullQualifiedClassName)
|
||||
{
|
||||
}
|
||||
|
||||
TestVisitor::~TestVisitor()
|
||||
{
|
||||
}
|
||||
|
||||
bool TestVisitor::visit(CPlusPlus::Class *symbol)
|
||||
{
|
||||
const CPlusPlus::Overview o;
|
||||
CPlusPlus::LookupContext lc;
|
||||
|
||||
unsigned count = symbol->memberCount();
|
||||
for (unsigned i = 0; i < count; ++i) {
|
||||
CPlusPlus::Symbol *member = symbol->memberAt(i);
|
||||
CPlusPlus::Type *type = member->type().type();
|
||||
|
||||
const QString className = o.prettyName(lc.fullyQualifiedName(member->enclosingClass()));
|
||||
if (className != m_className)
|
||||
continue;
|
||||
|
||||
if (const auto func = type->asFunctionType()) {
|
||||
if (func->isSlot() && member->isPrivate()) {
|
||||
const QString name = o.prettyName(func->name());
|
||||
TestCodeLocationAndType locationAndType;
|
||||
|
||||
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::TestSpecialFunction;
|
||||
else if (name.endsWith(QLatin1String("_data")))
|
||||
locationAndType.m_type = TestTreeItem::TestDataFunction;
|
||||
else
|
||||
locationAndType.m_type = TestTreeItem::TestFunction;
|
||||
locationAndType.m_state = TestTreeItem::Enabled;
|
||||
m_privSlots.insert(name, locationAndType);
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**************************** Cpp Test AST Visitor ****************************/
|
||||
|
||||
TestAstVisitor::TestAstVisitor(CPlusPlus::Document::Ptr doc)
|
||||
: ASTVisitor(doc->translationUnit()),
|
||||
m_currentDoc(doc)
|
||||
{
|
||||
}
|
||||
|
||||
TestAstVisitor::~TestAstVisitor()
|
||||
{
|
||||
}
|
||||
|
||||
bool TestAstVisitor::visit(CPlusPlus::CallAST *ast)
|
||||
{
|
||||
if (!m_currentScope || m_currentDoc.isNull())
|
||||
return false;
|
||||
|
||||
if (const auto expressionAST = ast->base_expression) {
|
||||
if (const auto idExpressionAST = expressionAST->asIdExpression()) {
|
||||
if (const auto qualifiedNameAST = idExpressionAST->name->asQualifiedName()) {
|
||||
const CPlusPlus::Overview o;
|
||||
const QString prettyName = o.prettyName(qualifiedNameAST->name);
|
||||
if (prettyName == QLatin1String("QTest::qExec")) {
|
||||
if (const auto expressionListAST = ast->expression_list) {
|
||||
// first argument is the one we need
|
||||
if (const auto argumentExpressionAST = expressionListAST->value) {
|
||||
CPlusPlus::TypeOfExpression toe;
|
||||
CppTools::CppModelManager *cppMM = CppTools::CppModelManager::instance();
|
||||
toe.init(m_currentDoc, cppMM->snapshot());
|
||||
QList<CPlusPlus::LookupItem> toeItems
|
||||
= toe(argumentExpressionAST, m_currentDoc, m_currentScope);
|
||||
|
||||
if (toeItems.size()) {
|
||||
if (const auto pointerType = toeItems.first().type()->asPointerType())
|
||||
m_className = o.prettyType(pointerType->elementType());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestAstVisitor::visit(CPlusPlus::CompoundStatementAST *ast)
|
||||
{
|
||||
m_currentScope = ast->symbol->asScope();
|
||||
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 || !ast->symbol || ast->symbol->argumentCount() != 0)
|
||||
return false;
|
||||
|
||||
CPlusPlus::LookupContext lc;
|
||||
const QString prettyName = m_overview.prettyName(lc.fullyQualifiedName(ast->symbol));
|
||||
// do not handle functions that aren't real test data functions
|
||||
if (!prettyName.endsWith(QLatin1String("_data")))
|
||||
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::TestDataTag;
|
||||
locationAndType.m_state = TestTreeItem::Enabled;
|
||||
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)
|
||||
: m_currentDoc(doc)
|
||||
{
|
||||
}
|
||||
|
||||
TestQmlVisitor::~TestQmlVisitor()
|
||||
{
|
||||
}
|
||||
|
||||
bool TestQmlVisitor::visit(QmlJS::AST::UiObjectDefinition *ast)
|
||||
{
|
||||
const QStringRef name = ast->qualifiedTypeNameId->name;
|
||||
if (name != QLatin1String("TestCase"))
|
||||
return true; // find nested TestCase items as well
|
||||
|
||||
m_currentTestCaseName.clear();
|
||||
const auto sourceLocation = ast->firstSourceLocation();
|
||||
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::TestClass;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TestQmlVisitor::visit(QmlJS::AST::ExpressionStatement *ast)
|
||||
{
|
||||
const QmlJS::AST::ExpressionNode *expr = ast->expression;
|
||||
return expr->kind == QmlJS::AST::Node::Kind_StringLiteral;
|
||||
}
|
||||
|
||||
bool TestQmlVisitor::visit(QmlJS::AST::UiScriptBinding *ast)
|
||||
{
|
||||
const QStringRef name = ast->qualifiedId->name;
|
||||
return name == QLatin1String("name");
|
||||
}
|
||||
|
||||
bool TestQmlVisitor::visit(QmlJS::AST::FunctionDeclaration *ast)
|
||||
{
|
||||
const QStringRef name = ast->name;
|
||||
if (name.startsWith(QLatin1String("test_"))
|
||||
|| name.startsWith(QLatin1String("benchmark_"))
|
||||
|| name.endsWith(QLatin1String("_data"))
|
||||
|| specialFunctions.contains(name.toString())) {
|
||||
const auto sourceLocation = ast->firstSourceLocation();
|
||||
TestCodeLocationAndType locationAndType;
|
||||
locationAndType.m_name = m_currentDoc->fileName();
|
||||
locationAndType.m_line = sourceLocation.startLine;
|
||||
locationAndType.m_column = sourceLocation.startColumn - 1;
|
||||
if (specialFunctions.contains(name.toString()))
|
||||
locationAndType.m_type = TestTreeItem::TestSpecialFunction;
|
||||
else if (name.endsWith(QLatin1String("_data")))
|
||||
locationAndType.m_type = TestTreeItem::TestDataFunction;
|
||||
else
|
||||
locationAndType.m_type = TestTreeItem::TestFunction;
|
||||
|
||||
locationAndType.m_state = TestTreeItem::Enabled;
|
||||
m_testFunctions.insert(name.toString(), locationAndType);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool TestQmlVisitor::visit(QmlJS::AST::StringLiteral *ast)
|
||||
{
|
||||
m_currentTestCaseName = ast->value.toString();
|
||||
return false;
|
||||
}
|
||||
|
||||
/********************** Google Test Function AST Visitor **********************/
|
||||
|
||||
GTestVisitor::GTestVisitor(CPlusPlus::Document::Ptr doc)
|
||||
: CPlusPlus::ASTVisitor(doc->translationUnit())
|
||||
, m_document(doc)
|
||||
{
|
||||
}
|
||||
|
||||
bool GTestVisitor::visit(CPlusPlus::FunctionDefinitionAST *ast)
|
||||
{
|
||||
static QString disabledPrefix = QString::fromLatin1("DISABLED_");
|
||||
if (!ast || !ast->declarator || !ast->declarator->core_declarator)
|
||||
return false;
|
||||
|
||||
CPlusPlus::DeclaratorIdAST *id = ast->declarator->core_declarator->asDeclaratorId();
|
||||
if (!id || !ast->symbol || ast->symbol->argumentCount() != 2)
|
||||
return false;
|
||||
|
||||
CPlusPlus::LookupContext lc;
|
||||
const QString prettyName = m_overview.prettyName(lc.fullyQualifiedName(ast->symbol));
|
||||
if (!TestUtils::isGTestMacro(prettyName))
|
||||
return false;
|
||||
|
||||
CPlusPlus::Argument *testCaseNameArg = ast->symbol->argumentAt(0)->asArgument();
|
||||
CPlusPlus::Argument *testNameArg = ast->symbol->argumentAt(1)->asArgument();
|
||||
if (testCaseNameArg && testNameArg) {
|
||||
const QString &testCaseName = m_overview.prettyType(testCaseNameArg->type());
|
||||
const QString &testName = m_overview.prettyType(testNameArg->type());
|
||||
|
||||
const bool disabled = testName.startsWith(disabledPrefix);
|
||||
const bool disabledCase = testCaseName.startsWith(disabledPrefix);
|
||||
unsigned line = 0;
|
||||
unsigned column = 0;
|
||||
unsigned token = id->firstToken();
|
||||
m_document->translationUnit()->getTokenStartPosition(token, &line, &column);
|
||||
|
||||
TestCodeLocationAndType locationAndType;
|
||||
locationAndType.m_name = disabled ? testName.mid(9) : testName;
|
||||
locationAndType.m_line = line;
|
||||
locationAndType.m_column = column - 1;
|
||||
locationAndType.m_type = TestTreeItem::GTestName;
|
||||
locationAndType.m_state = (disabled || disabledCase) ? TestTreeItem::Disabled
|
||||
: TestTreeItem::Enabled;
|
||||
GTestCaseSpec spec;
|
||||
spec.testCaseName = disabledCase ? testCaseName.mid(9) : testCaseName;
|
||||
spec.parameterized = TestUtils::isGTestParameterized(prettyName);
|
||||
m_gtestFunctions[spec].append(locationAndType);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
163
src/plugins/autotest/testvisitor.h
Normal file
@@ -0,0 +1,163 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef TESTVISITOR_H
|
||||
#define TESTVISITOR_H
|
||||
|
||||
#include "testtreeitem.h"
|
||||
|
||||
#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>
|
||||
|
||||
#include <QMap>
|
||||
#include <QString>
|
||||
|
||||
namespace Autotest {
|
||||
namespace Internal {
|
||||
|
||||
class TestVisitor : public CPlusPlus::SymbolVisitor
|
||||
{
|
||||
public:
|
||||
TestVisitor(const QString &fullQualifiedClassName);
|
||||
virtual ~TestVisitor();
|
||||
|
||||
QMap<QString, TestCodeLocationAndType> privateSlots() const { return m_privSlots; }
|
||||
|
||||
bool visit(CPlusPlus::Class *symbol);
|
||||
|
||||
private:
|
||||
CppTools::SymbolFinder m_symbolFinder;
|
||||
QString m_className;
|
||||
QMap<QString, TestCodeLocationAndType> m_privSlots;
|
||||
};
|
||||
|
||||
class TestAstVisitor : public CPlusPlus::ASTVisitor
|
||||
{
|
||||
public:
|
||||
TestAstVisitor(CPlusPlus::Document::Ptr doc);
|
||||
virtual ~TestAstVisitor();
|
||||
|
||||
bool visit(CPlusPlus::CallAST *ast);
|
||||
bool visit(CPlusPlus::CompoundStatementAST *ast);
|
||||
|
||||
QString className() const { return m_className; }
|
||||
|
||||
private:
|
||||
QString m_className;
|
||||
CPlusPlus::Scope *m_currentScope;
|
||||
CPlusPlus::Document::Ptr m_currentDoc;
|
||||
|
||||
};
|
||||
|
||||
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:
|
||||
TestQmlVisitor(QmlJS::Document::Ptr doc);
|
||||
virtual ~TestQmlVisitor();
|
||||
|
||||
bool visit(QmlJS::AST::UiObjectDefinition *ast);
|
||||
bool visit(QmlJS::AST::ExpressionStatement *ast);
|
||||
bool visit(QmlJS::AST::UiScriptBinding *ast);
|
||||
bool visit(QmlJS::AST::FunctionDeclaration *ast);
|
||||
bool visit(QmlJS::AST::StringLiteral *ast);
|
||||
|
||||
QString testCaseName() const { return m_currentTestCaseName; }
|
||||
TestCodeLocationAndType testCaseLocation() const { return m_testCaseLocation; }
|
||||
QMap<QString, TestCodeLocationAndType> testFunctions() const { return m_testFunctions; }
|
||||
|
||||
private:
|
||||
QmlJS::Document::Ptr m_currentDoc;
|
||||
QString m_currentTestCaseName;
|
||||
TestCodeLocationAndType m_testCaseLocation;
|
||||
QMap<QString, TestCodeLocationAndType> m_testFunctions;
|
||||
|
||||
};
|
||||
|
||||
struct GTestCaseSpec
|
||||
{
|
||||
QString testCaseName;
|
||||
bool parameterized;
|
||||
};
|
||||
|
||||
inline bool operator<(const GTestCaseSpec &spec1, const GTestCaseSpec &spec2)
|
||||
{
|
||||
if (spec1.testCaseName != spec2.testCaseName)
|
||||
return spec1.testCaseName < spec2.testCaseName;
|
||||
return spec1.parameterized == spec2.parameterized ? false : !spec1.parameterized;
|
||||
}
|
||||
|
||||
class GTestVisitor : public CPlusPlus::ASTVisitor
|
||||
{
|
||||
public:
|
||||
GTestVisitor(CPlusPlus::Document::Ptr doc);
|
||||
bool visit(CPlusPlus::FunctionDefinitionAST *ast);
|
||||
|
||||
QMap<GTestCaseSpec, TestCodeLocationList> gtestFunctions() const { return m_gtestFunctions; }
|
||||
|
||||
private:
|
||||
CPlusPlus::Document::Ptr m_document;
|
||||
CPlusPlus::Overview m_overview;
|
||||
QMap<GTestCaseSpec, TestCodeLocationList> m_gtestFunctions;
|
||||
|
||||
};
|
||||
|
||||
} // namespace Internal
|
||||
} // namespace Autotest
|
||||
|
||||
#endif // TESTVISITOR_H
|
5
src/plugins/autotest/unit_test/mixed_atp/mixed_atp.pro
Normal file
@@ -0,0 +1,5 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += src \
|
||||
tests
|
||||
|
8
src/plugins/autotest/unit_test/mixed_atp/mixed_atp.qbs
Normal file
@@ -0,0 +1,8 @@
|
||||
import qbs
|
||||
|
||||
Project {
|
||||
references: [
|
||||
"src/src.qbs",
|
||||
"tests/tests.qbs"
|
||||
]
|
||||
}
|
8
src/plugins/autotest/unit_test/mixed_atp/src/main.cpp
Normal file
@@ -0,0 +1,8 @@
|
||||
#include <QApplication>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QApplication a(argc, argv);
|
||||
return a.exec();
|
||||
}
|
||||
|
6
src/plugins/autotest/unit_test/mixed_atp/src/src.pro
Normal file
@@ -0,0 +1,6 @@
|
||||
QT += gui widgets
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += main.cpp
|
||||
|
11
src/plugins/autotest/unit_test/mixed_atp/src/src.qbs
Normal file
@@ -0,0 +1,11 @@
|
||||
import qbs
|
||||
|
||||
CppApplication {
|
||||
type: "application"
|
||||
name: "Dummy Application"
|
||||
|
||||
Depends { name: "Qt.gui" }
|
||||
Depends { name: "Qt.widgets" }
|
||||
|
||||
files: [ "main.cpp" ]
|
||||
}
|
15
src/plugins/autotest/unit_test/mixed_atp/tests/auto/auto.pro
Normal file
@@ -0,0 +1,15 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS = \
|
||||
bench \
|
||||
dummy \
|
||||
gui
|
||||
|
||||
greaterThan(QT_MAJOR_VERSION, 4) {
|
||||
message("enabling quick tests")
|
||||
SUBDIRS += quickauto \
|
||||
quickauto2
|
||||
} else {
|
||||
message("quick tests disabled")
|
||||
}
|
||||
|
13
src/plugins/autotest/unit_test/mixed_atp/tests/auto/auto.qbs
Normal file
@@ -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"
|
||||
]
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
QT += testlib
|
||||
|
||||
QT -= gui
|
||||
|
||||
TARGET = tst_benchtest
|
||||
CONFIG += console
|
||||
CONFIG -= app_bundle
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += tst_benchtest.cpp
|
||||
DEFINES += SRCDIR=\\\"$$PWD/\\\"
|
@@ -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)
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include <QString>
|
||||
#include <QtTest>
|
||||
|
||||
class BenchTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
BenchTest();
|
||||
|
||||
private Q_SLOTS:
|
||||
void testCase1();
|
||||
void testCase1_data();
|
||||
};
|
||||
|
||||
BenchTest::BenchTest()
|
||||
{
|
||||
}
|
||||
|
||||
void BenchTest::testCase1()
|
||||
{
|
||||
QFETCH(bool, localAware);
|
||||
|
||||
QString str1 = QLatin1String("Hello World");
|
||||
QString str2 = QLatin1String("Hallo Welt");
|
||||
if (!localAware) {
|
||||
QBENCHMARK {
|
||||
str1 == str2;
|
||||
}
|
||||
} else {
|
||||
QBENCHMARK {
|
||||
str1.localeAwareCompare(str2) == 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BenchTest::testCase1_data()
|
||||
{
|
||||
QTest::addColumn<bool>("localAware");
|
||||
QTest::newRow("localAware") << true;
|
||||
QTest::newRow("simple") << false;
|
||||
}
|
||||
|
||||
QTEST_MAIN(BenchTest)
|
||||
|
||||
#include "tst_benchtest.moc"
|
@@ -0,0 +1,10 @@
|
||||
QT += testlib
|
||||
QT += gui
|
||||
CONFIG += qt warn_on depend_includepath testcase
|
||||
TEMPLATE = app
|
||||
|
||||
TARGET = tst_FooBar
|
||||
|
||||
HEADERS += tst_foo.h
|
||||
SOURCES += tst_foo.cpp
|
||||
|
@@ -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" ]
|
||||
}
|
@@ -0,0 +1,89 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#include "tst_foo.h"
|
||||
#include <QtTest>
|
||||
#include <QCoreApplication>
|
||||
#include <QDebug>
|
||||
|
||||
Foo::Foo()
|
||||
{
|
||||
}
|
||||
|
||||
Foo::~Foo()
|
||||
{
|
||||
}
|
||||
|
||||
void Foo::initTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void Foo::cleanupTestCase()
|
||||
{
|
||||
QWARN("Warning!");
|
||||
}
|
||||
|
||||
void Foo::test_caseZero()
|
||||
{
|
||||
QCOMPARE(1, 2);
|
||||
}
|
||||
|
||||
void Foo::test_case1()
|
||||
{
|
||||
qDebug() << "test_case1";
|
||||
QFETCH(int, val);
|
||||
|
||||
QEXPECT_FAIL("test2", "2", Continue);
|
||||
QCOMPARE(val, 1);
|
||||
QEXPECT_FAIL("test1", "bla", Abort);
|
||||
QCOMPARE(val, 2);
|
||||
QVERIFY2(true, "Hallo");
|
||||
}
|
||||
|
||||
void Foo::test_case1_data()
|
||||
{
|
||||
QTest::addColumn<int>("val");
|
||||
QTest::newRow("test1") << 1;
|
||||
QTest::newRow("test2") << 2;
|
||||
QTest::newRow("test3") << 3;
|
||||
QTest::newRow("test4") << 4;
|
||||
}
|
||||
|
||||
void Foo::test_case2()
|
||||
{
|
||||
QThread::sleep(1);
|
||||
qDebug() << "test_case2 - all pass";
|
||||
QSKIP("Skip for now", SkipAll);
|
||||
QCOMPARE(1 ,1);
|
||||
QVERIFY(true);
|
||||
}
|
||||
|
||||
void Foo::test_case4()
|
||||
{
|
||||
qDebug("äøæ");
|
||||
QSKIP("Skipping test_case4", SkipSingle);
|
||||
QFAIL("bla");
|
||||
}
|
||||
|
||||
QTEST_MAIN(Foo)
|
@@ -0,0 +1,50 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2016 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
#ifndef FOO_H_INCLUDED
|
||||
#define FOO_H_INCLUDED
|
||||
|
||||
#include <QObject>
|
||||
|
||||
class Foo : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Foo();
|
||||
~Foo();
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
void test_case1();
|
||||
void test_case1_data();
|
||||
void test_caseZero();
|
||||
void test_case2();
|
||||
// void test_case3() {}
|
||||
void test_case4();
|
||||
void test_case5() {}
|
||||
};
|
||||
|
||||
#endif
|
@@ -0,0 +1,8 @@
|
||||
QT += testlib gui widgets
|
||||
|
||||
TARGET = tst_guitest
|
||||
|
||||
TEMPLATE = app
|
||||
|
||||
SOURCES += tst_guitest.cpp
|
||||
DEFINES += SRCDIR=\\\"$$PWD/\\\"
|