Add unit tests.

These test the complete workflow as the user experiences it when
clicking "Start".
Intended usage:
    (1) Run sdktool to set up a kit with the toolchain you want
        to test against (using a temporary directory).
        The tests assume exactly one Kit to be present.
    (2) Start Creator with a matching settings path and
        "-load ClangStaticAnalyzer -test ClangStaticAnalyzer".
    (3) Repeat until all toolchains have been tested.
The initial implementation tests one trivial source file
with both qbs and qmake.

Change-Id: I810f23e2990a789a4dd9f1dd16335fbcf5c5f39f
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@theqtcompany.com>
This commit is contained in:
Christian Kandeler
2015-02-04 15:19:30 +01:00
parent c632be5c92
commit 2946364ce6
13 changed files with 227 additions and 0 deletions

View File

@@ -37,5 +37,11 @@ HEADERS += \
FORMS += \ FORMS += \
clangstaticanalyzerconfigwidget.ui clangstaticanalyzerconfigwidget.ui
equals(TEST, 1) {
HEADERS += clangstaticanalyzerunittests.h
SOURCES += clangstaticanalyzerunittests.cpp
RESOURCES += clangstaticanalyzerunittests.qrc
}
DISTFILES += \ DISTFILES += \
tests/tests.pri tests/tests.pri

View File

@@ -44,4 +44,15 @@ QtcPlugin {
"clangstaticanalyzerutils.h", "clangstaticanalyzerutils.h",
"clangstaticanalyzer_global.h", "clangstaticanalyzer_global.h",
] ]
Group {
name: "Unit tests"
condition: project.testsEnabled
files: [
"clangstaticanalyzerunittests.cpp",
"clangstaticanalyzerunittests.h",
"clangstaticanalyzerunittests.qrc",
"unit-tests/**/*",
]
}
} }

View File

@@ -34,6 +34,7 @@ public:
ClangStaticAnalyzerDiagnosticModel(QObject *parent = 0); ClangStaticAnalyzerDiagnosticModel(QObject *parent = 0);
void addDiagnostics(const QList<Diagnostic> &diagnostics); void addDiagnostics(const QList<Diagnostic> &diagnostics);
QList<Diagnostic> diagnostics() const { return m_diagnostics; }
void clear(); void clear();
// QAbstractListModel interface // QAbstractListModel interface

View File

@@ -22,6 +22,10 @@
#include "clangstaticanalyzerruncontrolfactory.h" #include "clangstaticanalyzerruncontrolfactory.h"
#include "clangstaticanalyzertool.h" #include "clangstaticanalyzertool.h"
#ifdef WITH_TESTS
#include "clangstaticanalyzerunittests.h"
#endif
#include <analyzerbase/analyzermanager.h> #include <analyzerbase/analyzermanager.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/icontext.h> #include <coreplugin/icontext.h>
@@ -156,5 +160,14 @@ ExtensionSystem::IPlugin::ShutdownFlag ClangStaticAnalyzerPlugin::aboutToShutdow
return SynchronousShutdown; return SynchronousShutdown;
} }
QList<QObject *> ClangStaticAnalyzerPlugin::createTestObjects() const
{
QList<QObject *> tests;
#ifdef WITH_TESTS
tests << new ClangStaticAnalyzerUnitTests(m_analyzerTool);
#endif
return tests;
}
} // namespace Internal } // namespace Internal
} // namespace ClangStaticAnalyzerPlugin } // namespace ClangStaticAnalyzerPlugin

View File

@@ -42,6 +42,8 @@ public:
ShutdownFlag aboutToShutdown(); ShutdownFlag aboutToShutdown();
private: private:
QList<QObject *> createTestObjects() const override;
ClangStaticAnalyzerTool *m_analyzerTool; ClangStaticAnalyzerTool *m_analyzerTool;
}; };

View File

@@ -54,6 +54,7 @@ ClangStaticAnalyzerTool::ClangStaticAnalyzerTool(QObject *parent)
, m_diagnosticView(0) , m_diagnosticView(0)
, m_goBack(0) , m_goBack(0)
, m_goNext(0) , m_goNext(0)
, m_running(false)
{ {
setObjectName(QLatin1String("ClangStaticAnalyzerTool")); setObjectName(QLatin1String("ClangStaticAnalyzerTool"));
setRunMode(ProjectExplorer::ClangStaticAnalyzerMode); setRunMode(ProjectExplorer::ClangStaticAnalyzerMode);
@@ -206,6 +207,7 @@ void ClangStaticAnalyzerTool::startTool(StartMode mode)
QTC_ASSERT(project, return); QTC_ASSERT(project, return);
m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(project); m_projectInfoBeforeBuild = CppTools::CppModelManager::instance()->projectInfo(project);
QTC_ASSERT(m_projectInfoBeforeBuild.isValid(), return); QTC_ASSERT(m_projectInfoBeforeBuild.isValid(), return);
m_running = true;
ProjectExplorerPlugin::instance()->runProject(project, runMode()); ProjectExplorerPlugin::instance()->runProject(project, runMode());
} }
@@ -220,6 +222,11 @@ void ClangStaticAnalyzerTool::resetCursorAndProjectInfoBeforeBuild()
m_projectInfoBeforeBuild = CppTools::ProjectInfo(); m_projectInfoBeforeBuild = CppTools::ProjectInfo();
} }
QList<Diagnostic> ClangStaticAnalyzerTool::diagnostics() const
{
return m_diagnosticModel->diagnostics();
}
void ClangStaticAnalyzerTool::onEngineIsStarting() void ClangStaticAnalyzerTool::onEngineIsStarting()
{ {
QTC_ASSERT(m_diagnosticModel, return); QTC_ASSERT(m_diagnosticModel, return);
@@ -246,6 +253,8 @@ void ClangStaticAnalyzerTool::onEngineFinished()
AnalyzerManager::showStatusMessage(issuesFound > 0 AnalyzerManager::showStatusMessage(issuesFound > 0
? AnalyzerManager::tr("Clang Static Analyzer finished, %n issues were found.", 0, issuesFound) ? AnalyzerManager::tr("Clang Static Analyzer finished, %n issues were found.", 0, issuesFound)
: AnalyzerManager::tr("Clang Static Analyzer finished, no issues were found.")); : AnalyzerManager::tr("Clang Static Analyzer finished, no issues were found."));
m_running = false;
emit finished();
} }
void ClangStaticAnalyzerTool::setBusyCursor(bool busy) void ClangStaticAnalyzerTool::setBusyCursor(bool busy)

View File

@@ -40,6 +40,13 @@ public:
CppTools::ProjectInfo projectInfoBeforeBuild() const; CppTools::ProjectInfo projectInfoBeforeBuild() const;
void resetCursorAndProjectInfoBeforeBuild(); void resetCursorAndProjectInfoBeforeBuild();
// For testing.
bool isRunning() const { return m_running; }
QList<Diagnostic> diagnostics() const;
signals:
void finished(); // For testing.
private: private:
QWidget *createWidgets(); QWidget *createWidgets();
Analyzer::AnalyzerRunControl *createRunControl(const Analyzer::AnalyzerStartParameters &sp, Analyzer::AnalyzerRunControl *createRunControl(const Analyzer::AnalyzerStartParameters &sp,
@@ -60,6 +67,7 @@ private:
QAction *m_goBack; QAction *m_goBack;
QAction *m_goNext; QAction *m_goNext;
bool m_running;
}; };
} // namespace Internal } // namespace Internal

View File

@@ -0,0 +1,104 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc
** All rights reserved.
** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
**
** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
**
** Licensees holding valid Qt Enterprise licenses may use this file in
** accordance with the Qt Enterprise License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.
**
** If you have questions regarding the use of this file, please use
** contact form at http://qt.digia.com <http://qt.digia.com/>
**
****************************************************************************/
#include "clangstaticanalyzerunittests.h"
#include "clangstaticanalyzerdiagnostic.h"
#include "clangstaticanalyzertool.h"
#include "clangstaticanalyzerutils.h"
#include <analyzerbase/analyzermanager.h>
#include <cpptools/cppmodelmanager.h>
#include <cpptools/cpptoolstestcase.h>
#include <projectexplorer/kitinformation.h>
#include <projectexplorer/kitmanager.h>
#include <projectexplorer/projectexplorer.h>
#include <projectexplorer/toolchain.h>
#include <utils/fileutils.h>
#include <QEventLoop>
#include <QSignalSpy>
#include <QTemporaryDir>
#include <QTimer>
#include <QtTest>
using namespace Analyzer;
using namespace ProjectExplorer;
using namespace Utils;
namespace ClangStaticAnalyzer {
namespace Internal {
ClangStaticAnalyzerUnitTests::ClangStaticAnalyzerUnitTests(ClangStaticAnalyzerTool *analyzerTool,
QObject *parent)
: QObject(parent)
, m_analyzerTool(analyzerTool)
, m_tmpDir(0)
{
}
void ClangStaticAnalyzerUnitTests::initTestCase()
{
const QList<Kit *> allKits = KitManager::kits();
if (allKits.count() != 1)
QSKIP("This test requires exactly one kit to be present");
const ToolChain * const toolchain = ToolChainKitInformation::toolChain(allKits.first());
if (!toolchain)
QSKIP("This test requires that there is a kit with a toolchain.");
bool hasClangExecutable;
clangExecutableFromSettings(toolchain->type(), &hasClangExecutable);
if (!hasClangExecutable)
QSKIP("No clang suitable for analyzing found");
m_tmpDir = new CppTools::Tests::TemporaryCopiedDir(QLatin1String(":/unit-tests"));
}
void ClangStaticAnalyzerUnitTests::cleanupTestCase()
{
delete m_tmpDir;
}
void ClangStaticAnalyzerUnitTests::testProject()
{
QFETCH(QString, projectFilePath);
QFETCH(int, expectedDiagCount);
CppTools::Tests::ProjectOpenerAndCloser projectManager;
const CppTools::ProjectInfo projectInfo = projectManager.open(projectFilePath, true);
QVERIFY(projectInfo.isValid());
AnalyzerManager::selectTool(m_analyzerTool, Analyzer::StartLocal);
AnalyzerManager::startTool();
if (m_analyzerTool->isRunning()) {
QSignalSpy waiter(m_analyzerTool, SIGNAL(finished()));
QVERIFY(waiter.wait(30000));
}
QCOMPARE(m_analyzerTool->diagnostics().count(), expectedDiagCount);
}
void ClangStaticAnalyzerUnitTests::testProject_data()
{
QTest::addColumn<QString>("projectFilePath");
QTest::addColumn<int>("expectedDiagCount");
QTest::newRow("qbs project")
<< QString(m_tmpDir->path() + QLatin1String("/simple/simple.qbs")) << 1;
QTest::newRow("qbs project")
<< QString(m_tmpDir->path() + QLatin1String("/simple/simple.pro")) << 1;
}
} // namespace Internal
} // namespace ClangStaticAnalyzerPlugin

View File

@@ -0,0 +1,53 @@
/****************************************************************************
**
** Copyright (C) 2014 Digia Plc
** All rights reserved.
** For any questions to Digia, please use contact form at http://qt.digia.com <http://qt.digia.com/>
**
** This file is part of the Qt Enterprise Qt Quick Profiler Add-on.
**
** Licensees holding valid Qt Enterprise licenses may use this file in
** accordance with the Qt Enterprise License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.
**
** If you have questions regarding the use of this file, please use
** contact form at http://qt.digia.com <http://qt.digia.com/>
**
****************************************************************************/
#ifndef CLANGSTATICANALYZERUNITTESTS_H
#define CLANGSTATICANALYZERUNITTESTS_H
#include <QObject>
#include <QTemporaryDir>
namespace CppTools { namespace Tests { class TemporaryCopiedDir; } }
namespace ClangStaticAnalyzer {
namespace Internal {
class ClangStaticAnalyzerTool;
class ClangStaticAnalyzerUnitTests : public QObject
{
Q_OBJECT
public:
ClangStaticAnalyzerUnitTests(ClangStaticAnalyzerTool *analyzerTool, QObject *parent = 0);
private slots:
void initTestCase();
void cleanupTestCase();
void testProject();
void testProject_data();
private:
ClangStaticAnalyzerTool * const m_analyzerTool;
CppTools::Tests::TemporaryCopiedDir *m_tmpDir;
};
} // namespace Internal
} // namespace ClangStaticAnalyzerPlugin
#endif // Include guard

View File

@@ -0,0 +1,7 @@
<RCC>
<qresource prefix="/">
<file>unit-tests/simple/main.cpp</file>
<file>unit-tests/simple/simple.qbs</file>
<file>unit-tests/simple/simple.pro</file>
</qresource>
</RCC>

View File

@@ -0,0 +1,5 @@
int main()
{
int *i = 0;
*i = 42;
}

View File

@@ -0,0 +1,3 @@
CONFIG -= QT
SOURCES = main.cpp

View File

@@ -0,0 +1,5 @@
import qbs
CppApplication {
files: ["main.cpp"]
}