From b52c0d5d00ee100eddd0215ad4c891f8067df76e Mon Sep 17 00:00:00 2001 From: "Jesper K. Pedersen" Date: Tue, 14 May 2013 10:44:09 +0200 Subject: the birth of a unit testing framework Change-Id: Ibb634bce4db330167b1492bc7ce13f7af53a18c7 Reviewed-by: Jesper K. Pedersen Reviewed-by: Nicolas Arnaud-Cormos --- objects/editors.cpp | 3 ++- scripting.pro | 14 +++++++--- scriptrunner.cpp | 41 +++++++++++++++++++++++----- scriptrunner.h | 3 +++ tests/mark/mark.qs | 61 +++++++++++++++++++++++++++++++++++++++++ tests/mark/test.cpp | 5 ++++ tests/positions/positions.qs | 18 +++++++++++++ tests/positions/test.cpp | 5 ++++ tests/runtests.qs | 19 +++++++++++++ tests/test.js | 53 ++++++++++++++++++++++++++++++++++++ utils/utils.cpp | 64 ++++++++++++++++++++++++++++++++++++++++++++ utils/utils.h | 56 ++++++++++++++++++++++++++++++++++++++ 12 files changed, 332 insertions(+), 10 deletions(-) create mode 100644 tests/mark/mark.qs create mode 100644 tests/mark/test.cpp create mode 100644 tests/positions/positions.qs create mode 100644 tests/positions/test.cpp create mode 100644 tests/runtests.qs create mode 100644 tests/test.js create mode 100644 utils/utils.cpp create mode 100644 utils/utils.h diff --git a/objects/editors.cpp b/objects/editors.cpp index 85e73c3..17fad9f 100644 --- a/objects/editors.cpp +++ b/objects/editors.cpp @@ -40,6 +40,7 @@ #include #include #include +#include "scriptrunner.h" using namespace Scripting; using namespace Scripting::Internal; @@ -84,7 +85,7 @@ QStringList Editors::existingEditors() Editor *Editors::openFile(const QString &fileName) { - Core::IEditor* editor = Core::EditorManager::instance()->openEditor(fileName); + Core::IEditor* editor = Core::EditorManager::instance()->openEditor(ScriptRunner::absolutePath(fileName)); if (editor) { Editor* wrapper = wrapEditor(editor); wrapper->waitForInitialized(); diff --git a/scripting.pro b/scripting.pro index ac421bb..fed3031 100644 --- a/scripting.pro +++ b/scripting.pro @@ -22,7 +22,8 @@ SOURCES += scriptingplugin.cpp \ objects/cppfunction.cpp \ objects/cppargument.cpp \ objects/mark.cpp \ - utils/position.cpp + utils/position.cpp \ + utils/utils.cpp HEADERS += scriptingplugin.h \ scripting_global.h \ @@ -44,7 +45,8 @@ HEADERS += scriptingplugin.h \ objects/cppfunction.h \ objects/cppargument.h \ objects/mark.h \ - utils/position.h + utils/position.h \ + utils/utils.h # Qt Creator linking @@ -69,7 +71,13 @@ PROVIDER = KDAB include($$QTCREATOR_SOURCES/src/qtcreatorplugin.pri) OTHER_FILES += \ - examples/* + examples/* \ + tests/mark/test.cpp \ + tests/mark/mark.qs \ + tests/runtests.qs \ + tests/positions/test.cpp \ + tests/positions/positions.qs \ + tests/test.js diff --git a/scriptrunner.cpp b/scriptrunner.cpp index 44a34fa..7f8d8ac 100644 --- a/scriptrunner.cpp +++ b/scriptrunner.cpp @@ -51,23 +51,32 @@ #include #include #include "objects/mark.h" +#include "utils/utils.h" using namespace Scripting; using namespace Scripting::Internal; +namespace { + QString rootDir; +} + +ScriptRunner* ScriptRunner::m_instance = 0; ScriptRunner::ScriptRunner(QObject *parent) : QObject(parent), m_engine(0) { + m_instance = this; } -ScriptRunner::~ScriptRunner() +ScriptRunner *ScriptRunner::instance() { + return m_instance; } -// Path to the topmost script loaded. -static QString currentPath; +ScriptRunner::~ScriptRunner() +{ +} // The backtrace does unfortunately not include the file in which an error occurred, // we therefore need this variable to store this information. @@ -108,8 +117,14 @@ static QScriptValue run(QScriptEngine* engine, const QString& fileName, bool rec static QScriptValue load(QScriptContext *context, QScriptEngine *engine) { QScriptValue callee = context->callee(); - if (context->argumentCount() == 1) - return run(engine, currentPath + QLatin1String("/") + context->argument(0).toString(), true); + if (context->argumentCount() == 1) { + QString oldRoot = rootDir; + QString path = rootDir + QLatin1String("/") + context->argument(0).toString(); + rootDir = QFileInfo(path).absolutePath(); + const QScriptValue result = run(engine, path, true); + rootDir = oldRoot; + return result; + } else context->throwError(QObject::tr("Wrong number of arguments given to import")); return QScriptValue(); @@ -118,7 +133,7 @@ static QScriptValue load(QScriptContext *context, QScriptEngine *engine) ErrorMessage ScriptRunner::runScript(const QString fileName) { errorFileName = QString(); - currentPath = QFileInfo(fileName).absolutePath(); + rootDir = QFileInfo(fileName).absolutePath(); ensureEngineInitialized(); // Ensure no polution of environment between script runs @@ -140,6 +155,19 @@ ErrorMessage ScriptRunner::runScript(const QString fileName) return ErrorMessage(); } +/** + @brief Convert a relative path to an absolute path relative to the loaded scripts direcotry + + Relative files referenced in the scripts, should always be relative to the script loaded. + */ +QString ScriptRunner::absolutePath(const QString &path) +{ + if (QFileInfo(path).isRelative() ) + return rootDir + QLatin1String("/") + path; + else + return path; +} + Q_DECLARE_METATYPE(QList) ScriptRunner::QScriptEnginePtr ScriptRunner::ensureEngineInitialized() @@ -167,6 +195,7 @@ ScriptRunner::QScriptEnginePtr ScriptRunner::ensureEngineInitialized() registerGlobal(new Console, QLatin1String("console")); registerGlobal(new Editors, QLatin1String("editors")); registerGlobal(new Dialogs, QLatin1String("dialogs")); + registerGlobal(new Utils, QLatin1String("utils")); registerWrappers(m_engine.data()); registerEnums(m_engine.data()); diff --git a/scriptrunner.h b/scriptrunner.h index 86dea44..c26eefb 100644 --- a/scriptrunner.h +++ b/scriptrunner.h @@ -64,12 +64,14 @@ public: typedef QSharedPointer QScriptEnginePtr; explicit ScriptRunner(QObject *parent = 0); + static ScriptRunner* instance(); virtual ~ScriptRunner(); // Run a script ErrorMessage runScript(const QString fileName); QScriptEnginePtr scriptEngine() { return ensureEngineInitialized(); } + static QString absolutePath(const QString& path); private: QScriptEnginePtr ensureEngineInitialized(); @@ -77,6 +79,7 @@ private: private: QScriptEnginePtr m_engine; + static ScriptRunner* m_instance; }; } // namespace Internal diff --git a/tests/mark/mark.qs b/tests/mark/mark.qs new file mode 100644 index 0000000..1c2521a --- /dev/null +++ b/tests/mark/mark.qs @@ -0,0 +1,61 @@ +include("../test.js") + +markMove() +markStays() +markMoveOnPrevChar() +markStaysOnNextChar() + +function findYay(editor) { + editor.gotoLine(1,0) + editor.find("yay") +} + +function markMove() { + var editor = editors.openFile("test.cpp") + findYay(editor) + var mark = editor.createMark() + editor.gotoLine(1,0) + editor.insert("Hello World\n") + findYay(editor) + comparePositions(editor.position(),mark, "ensure mark moves on new lines") + editor.close() +} + +function markStays() { + var editor = editors.openFile("test.cpp") + findYay(editor) + var mark = editor.createMark() + editor.gotoLineEnd() + editor.insert("Hello world") + findYay(editor) + comparePositions(editor.position(),mark, "ensure mark stays when inserting after the mark") + editor.close() +} + +function markMoveOnPrevChar() { + var editor = editors.openFile("test.cpp") + findYay(editor) + var mark = editor.createMark() + var pos = editor.position() + editor.gotoPreviousCharacter(3) + editor.insert("WOW") + findYay(editor) + comparePositions(editor.position(),mark, "ensure mark moves on insert at prev character") + compare(mark.line,pos.line) + compare(mark.column, pos.column+3) + editor.close() +} + +function markStaysOnNextChar() { + var editor = editors.openFile("test.cpp") + findYay(editor) + var mark = editor.createMark() + var pos = editor.position() + editor.insert("WOW") + findYay(editor) + comparePositions(editor.position(),mark, "ensure mark stays the same when inserting after point") + compare(mark.line,pos.line) + compare(mark.column, pos.column) + editor.close() + +} diff --git a/tests/mark/test.cpp b/tests/mark/test.cpp new file mode 100644 index 0000000..4304ec5 --- /dev/null +++ b/tests/mark/test.cpp @@ -0,0 +1,5 @@ +class Foo { + void bar() { + qDebug("yay"); + } +}; diff --git a/tests/positions/positions.qs b/tests/positions/positions.qs new file mode 100644 index 0000000..13d7955 --- /dev/null +++ b/tests/positions/positions.qs @@ -0,0 +1,18 @@ +include("../test.js") + +var editor = editors.openFile("test.cpp") + +editor.gotoLine(1,0) +verifyPosition(1,0, "beginning of file") + +editor.gotoLineEnd() +verifyPosition(1,11, "end of line") + +editor.gotoLineStart() +verifyPosition(1,0, "start of line") + +editor.gotoLineEnd() +editor.gotoBlockEnd() +verifyPosition(5,1, "end of block") + +editor.close() diff --git a/tests/positions/test.cpp b/tests/positions/test.cpp new file mode 100644 index 0000000..4304ec5 --- /dev/null +++ b/tests/positions/test.cpp @@ -0,0 +1,5 @@ +class Foo { + void bar() { + qDebug("yay"); + } +}; diff --git a/tests/runtests.qs b/tests/runtests.qs new file mode 100644 index 0000000..9d74e00 --- /dev/null +++ b/tests/runtests.qs @@ -0,0 +1,19 @@ +include("test.js") +//-------------------- Main -------------------- + +tests = utils.subDirectories(".") +for (var i = 0; i < tests.length; i++) { + runTest(tests[i]) +} + +if ( errorCount == 0) + console.log("There were no errors") +else + console.log("+++++++++ There were " + errorCount + " error(s) ++++++++++") + + +function runTest(name) { + console.log("Running test " + name) + include(name+"/" + name + ".qs") +} + diff --git a/tests/test.js b/tests/test.js new file mode 100644 index 0000000..5202f5c --- /dev/null +++ b/tests/test.js @@ -0,0 +1,53 @@ +var TEST_JS_LOADED // Purposfully left undefined - this is to avoid the global variables being loaded more than once +if (TEST_JS_LOADED == undefined) { + TEST_JS_LOADED = true + // Set to true to print out every single comparison + var verbose = false + + // Set to true to "break" on the first error + var haltOnError = false + + var errorCount = 0 + + function error(message) { + console.log("+++ " + message) + printBackTrace() + errorCount++ + if ( haltOnError ) + throw "error" + } + + function printBackTrace() { + var bt = utils.backtrace() + // We start at 3, so we don't show backtrakce(), printBackTrace() and error() + for ( var i=3;i +#include "scriptrunner.h" + +namespace Scripting { +namespace Internal { + +Utils::Utils(QObject *parent) : + QObject(parent) +{ +} + +QStringList Utils::subDirectories(const QString &directory) const +{ + QDir dir(ScriptRunner::absolutePath(directory)); + return dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); +} + +QStringList Utils::backtrace() const +{ + QStringList result; + foreach ( const QString& str, ScriptRunner::instance()->scriptEngine()->currentContext()->backtrace() ) { + // Remove internal calls that is of no use to the script side + if ( !str.startsWith(QLatin1String("<"))) + result.append(QLatin1String("\t") + str); + } + return result; +} + + +} // namespace Internal +} // namespace Scripting diff --git a/utils/utils.h b/utils/utils.h new file mode 100644 index 0000000..09044c9 --- /dev/null +++ b/utils/utils.h @@ -0,0 +1,56 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (C) 2013 Kläralvdalens Datakonsult AB, a KDAB Group company. +** +** Contact: Kläralvdalens Datakonsult AB (info@kdab.com) +** +** +** GNU Lesser General Public License Usage +** +** This file may be used under the terms of the GNU Lesser General Public +** License version 2.1 as published by the Free Software Foundation and +** appearing in the file LICENSE.LGPL included in the packaging of this file. +** Please review the following information to ensure the GNU Lesser General +** Public License version 2.1 requirements will be met: +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** Other Usage +** +** Alternatively, this file may be used in accordance with the terms and +** conditions contained in a signed written agreement between you and Nokia. +** +** If you have questions regarding the use of this file, please contact +** Nokia at info@qt.nokia.com. +** +**************************************************************************/ + +#ifndef SCRIPTING_INTERNAL_UTILS_H +#define SCRIPTING_INTERNAL_UTILS_H + +#include +#include + +namespace Scripting { +namespace Internal { + +class Utils : public QObject +{ + Q_OBJECT +public: + explicit Utils(QObject *parent = 0); + +public slots: + QStringList subDirectories(const QString& directory) const; + QStringList backtrace() const; +}; + +} // namespace Internal +} // namespace Scripting + +#endif // SCRIPTING_INTERNAL_UTILS_H -- cgit v1.2.3