From b97bfaa7f74158fa009fce9844ec256929e837f9 Mon Sep 17 00:00:00 2001 From: Leandro Melo <leandro.melo@nokia.com> Date: Wed, 2 Jun 2010 17:34:27 +0200 Subject: [PATCH] Generic highlighter: New unit tests for the highlighter engine. --- .../generichighlighter/generichighlighter.pro | 2 +- .../basetextdocumentlayout.h | 44 + .../highlighterengine/formats.cpp | 89 ++ .../highlighterengine/formats.h | 75 ++ .../highlighterengine/highlighterengine.pro | 25 + .../highlighterengine/highlightermock.cpp | 125 +++ .../highlighterengine/highlightermock.h | 92 +++ .../tst_highlighterengine.cpp | 780 ++++++++++++++++++ 8 files changed, 1231 insertions(+), 1 deletion(-) create mode 100644 tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h create mode 100644 tests/auto/generichighlighter/highlighterengine/formats.cpp create mode 100644 tests/auto/generichighlighter/highlighterengine/formats.h create mode 100644 tests/auto/generichighlighter/highlighterengine/highlighterengine.pro create mode 100644 tests/auto/generichighlighter/highlighterengine/highlightermock.cpp create mode 100644 tests/auto/generichighlighter/highlighterengine/highlightermock.h create mode 100644 tests/auto/generichighlighter/highlighterengine/tst_highlighterengine.cpp diff --git a/tests/auto/generichighlighter/generichighlighter.pro b/tests/auto/generichighlighter/generichighlighter.pro index 97a6f0b863c..0656a4345d3 100644 --- a/tests/auto/generichighlighter/generichighlighter.pro +++ b/tests/auto/generichighlighter/generichighlighter.pro @@ -1,2 +1,2 @@ TEMPLATE = subdirs -SUBDIRS += specificrules +SUBDIRS += specificrules highlighterengine diff --git a/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h b/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h new file mode 100644 index 00000000000..37320e4361d --- /dev/null +++ b/tests/auto/generichighlighter/highlighterengine/basetextdocumentlayout.h @@ -0,0 +1,44 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef BASETEXTDOCUMENTLAYOUT_H +#define BASETEXTDOCUMENTLAYOUT_H + +/* + Since the text editor plugin directory is not included in the search list of the pro file, this + file replaces the "real" basetextdocumentlayout.h file. The objective is to simply use + QTextBlockUserData instead of TextEditor::TextBlockUserData to avoid "external" + dependencies or intrusive defines. + */ + +#include <QtGui/QTextBlockUserData> + +typedef QTextBlockUserData TextBlockUserData; + +#endif // BASETEXTDOCUMENTLAYOUT_H diff --git a/tests/auto/generichighlighter/highlighterengine/formats.cpp b/tests/auto/generichighlighter/highlighterengine/formats.cpp new file mode 100644 index 00000000000..cec5d119ef8 --- /dev/null +++ b/tests/auto/generichighlighter/highlighterengine/formats.cpp @@ -0,0 +1,89 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "formats.h" + +#include <QtCore/Qt> + +Formats::Formats() +{ + m_keywordFormat.setForeground(Qt::darkGray); + m_dataTypeFormat.setForeground(Qt::gray); + m_decimalFormat.setForeground(Qt::lightGray); + m_baseNFormat.setForeground(Qt::red); + m_floatFormat.setForeground(Qt::green); + m_charFormat.setForeground(Qt::blue); + m_stringFormat.setForeground(Qt::cyan); + m_commentFormat.setForeground(Qt::magenta); + m_alertFormat.setForeground(Qt::yellow); + m_errorFormat.setForeground(Qt::darkRed); + m_functionFormat.setForeground(Qt::darkGreen); + m_regionMarkerFormat.setForeground(Qt::darkBlue); + m_othersFormat.setForeground(Qt::darkCyan); +} + +Formats &Formats::instance() +{ + static Formats formats; + return formats; +} + +QString Formats::name(const QTextCharFormat &format) const +{ + if (format == QTextCharFormat()) + return "Default format"; + else if (format == m_keywordFormat) + return "Keyword"; + else if (format == m_dataTypeFormat) + return "Data type format"; + else if (format == m_decimalFormat) + return "Decimal format"; + else if (format == m_baseNFormat) + return "Base N format"; + else if (format == m_floatFormat) + return "Float format"; + else if (format == m_charFormat) + return "Char format"; + else if (format == m_stringFormat) + return "String format"; + else if (format == m_commentFormat) + return "Comment format"; + else if (format == m_alertFormat) + return "Alert format"; + else if (format == m_errorFormat) + return "Error format"; + else if (format == m_functionFormat) + return "Function format"; + else if (format == m_regionMarkerFormat) + return "Region Marker format"; + else if (format == m_othersFormat) + return "Others format"; + else + return "Unidentified format"; +} diff --git a/tests/auto/generichighlighter/highlighterengine/formats.h b/tests/auto/generichighlighter/highlighterengine/formats.h new file mode 100644 index 00000000000..e753732cc19 --- /dev/null +++ b/tests/auto/generichighlighter/highlighterengine/formats.h @@ -0,0 +1,75 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef FORMATS_H +#define FORMATS_H + +#include <QtGui/QTextCharFormat> + +class Formats +{ +public: + static Formats &instance(); + + const QTextCharFormat &keywordFormat() const { return m_keywordFormat; } + const QTextCharFormat &dataTypeFormat() const { return m_dataTypeFormat; } + const QTextCharFormat &decimalFormat() const { return m_decimalFormat; } + const QTextCharFormat &baseNFormat() const { return m_baseNFormat; } + const QTextCharFormat &floatFormat() const { return m_floatFormat; } + const QTextCharFormat &charFormat() const { return m_charFormat; } + const QTextCharFormat &stringFormat() const { return m_stringFormat; } + const QTextCharFormat &commentFormat() const { return m_commentFormat; } + const QTextCharFormat &alertFormat() const { return m_alertFormat; } + const QTextCharFormat &errorFormat() const { return m_errorFormat; } + const QTextCharFormat &functionFormat() const { return m_functionFormat; } + const QTextCharFormat ®ionMarketFormat() const { return m_regionMarkerFormat; } + const QTextCharFormat &othersFormat() const { return m_othersFormat; } + + QString name(const QTextCharFormat &format) const; + +private: + Formats(); + Q_DISABLE_COPY(Formats); + + QTextCharFormat m_keywordFormat; + QTextCharFormat m_dataTypeFormat; + QTextCharFormat m_decimalFormat; + QTextCharFormat m_baseNFormat; + QTextCharFormat m_floatFormat; + QTextCharFormat m_charFormat; + QTextCharFormat m_stringFormat; + QTextCharFormat m_commentFormat; + QTextCharFormat m_alertFormat; + QTextCharFormat m_errorFormat; + QTextCharFormat m_functionFormat; + QTextCharFormat m_regionMarkerFormat; + QTextCharFormat m_othersFormat; +}; + +#endif // FORMATS_H diff --git a/tests/auto/generichighlighter/highlighterengine/highlighterengine.pro b/tests/auto/generichighlighter/highlighterengine/highlighterengine.pro new file mode 100644 index 00000000000..dec3fa3bd89 --- /dev/null +++ b/tests/auto/generichighlighter/highlighterengine/highlighterengine.pro @@ -0,0 +1,25 @@ +QT += gui testlib + +GENERICHIGHLIGHTERDIR = ../../../../src/plugins/texteditor/generichighlighter + +SOURCES += tst_highlighterengine.cpp \ + highlightermock.cpp \ + $$GENERICHIGHLIGHTERDIR/highlighter.cpp \ + $$GENERICHIGHLIGHTERDIR/context.cpp \ + $$GENERICHIGHLIGHTERDIR/dynamicrule.cpp \ + $$GENERICHIGHLIGHTERDIR/rule.cpp \ + $$GENERICHIGHLIGHTERDIR/specificrules.cpp \ + $$GENERICHIGHLIGHTERDIR/progressdata.cpp \ + $$GENERICHIGHLIGHTERDIR/highlightdefinition.cpp \ + $$GENERICHIGHLIGHTERDIR/keywordlist.cpp \ + $$GENERICHIGHLIGHTERDIR/itemdata.cpp \ + formats.cpp + +HEADERS += \ + highlightermock.h \ + basetextdocumentlayout.h \ + formats.h + +INCLUDEPATH += $$GENERICHIGHLIGHTERDIR + +TARGET=tst_$$TARGET diff --git a/tests/auto/generichighlighter/highlighterengine/highlightermock.cpp b/tests/auto/generichighlighter/highlighterengine/highlightermock.cpp new file mode 100644 index 00000000000..f3de75d19b4 --- /dev/null +++ b/tests/auto/generichighlighter/highlighterengine/highlightermock.cpp @@ -0,0 +1,125 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "highlightermock.h" +#include "context.h" +#include "highlightdefinition.h" +#include "formats.h" + +#include <QtCore/QDebug> +#include <QtTest/QtTest> + +namespace QTest { +template<> +char *toString(const QTextCharFormat &format) +{ + QByteArray ba = Formats::instance().name(format).toLatin1(); + return qstrdup(ba.data()); +} +} + +using namespace TextEditor; +using namespace Internal; + +HighlighterMock::HighlighterMock(const QSharedPointer<Context> &defaultContext, + QTextDocument *parent) : + Highlighter(defaultContext, parent), + m_debugDetails(false), + m_statesCounter(0), + m_formatsCounter(0), + m_noTestCall(false), + m_considerEmptyLines(false) +{} + +void HighlighterMock::reset() +{ + m_states.clear(); + m_statesCounter = 0; + m_formatSequence.clear(); + m_formatsCounter = 0; + m_debugDetails = false; + m_noTestCall = false; + m_considerEmptyLines = false; +} + +void HighlighterMock::showDebugDetails() +{ m_debugDetails = true; } + +void HighlighterMock::considerEmptyLines() +{ m_considerEmptyLines = true; } + +void HighlighterMock::startNoTestCalls() +{ m_noTestCall = true; } + +void HighlighterMock::endNoTestCalls() +{ m_noTestCall = false; } + +void HighlighterMock::setExpectedBlockState(const int state) +{ m_states << state; } + +void HighlighterMock::setExpectedBlockStates(const QList<int> &states) +{ m_states = states; } + +void HighlighterMock::setExpectedHighlightSequence(const HighlightSequence &format) +{ m_formatSequence << format; } + +void HighlighterMock::setExpectedHighlightSequences(const QList<HighlightSequence> &formats) +{ m_formatSequence = formats; } + +void HighlighterMock::highlightBlock(const QString &text) +{ + Highlighter::highlightBlock(text); + + if (text.isEmpty() && !m_considerEmptyLines) + return; + + if (m_noTestCall) + return; + + if (m_states.isEmpty() || m_formatSequence.isEmpty()) + QFAIL("No expected data setup."); + + if (m_debugDetails) + qDebug() << "Highlighting" << text; + + if (m_states.size() <= m_statesCounter) + QFAIL("Expected state for current block not set."); + QCOMPARE(currentBlockState(), m_states.at(m_statesCounter++)); + + if (m_formatSequence.size() <= m_formatsCounter) + QFAIL("Expected highlight sequence for current block not set."); + for (int i = 0; i < text.length(); ++i) { + if (text.at(i).isSpace()) + continue; + if (m_debugDetails) + qDebug() << "at offset" << i; + QCOMPARE(format(i), m_formatSequence.at(m_formatsCounter).m_formats.at(i)); + } + ++m_formatsCounter; +} diff --git a/tests/auto/generichighlighter/highlighterengine/highlightermock.h b/tests/auto/generichighlighter/highlighterengine/highlightermock.h new file mode 100644 index 00000000000..cd8cdb84072 --- /dev/null +++ b/tests/auto/generichighlighter/highlighterengine/highlightermock.h @@ -0,0 +1,92 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#ifndef HIGHLIGHTERMOCK_H +#define HIGHLIGHTERMOCK_H + +#include "highlighter.h" + +#include <QtCore/QList> + +namespace TextEditor { +namespace Internal { +class Context; +class HighlightDefinition; +} +} + +struct HighlightSequence +{ + HighlightSequence() {} + HighlightSequence(int from, int to, const QTextCharFormat &format = QTextCharFormat()) + { add(from, to, format); } + HighlightSequence(const HighlightSequence &sequence) + { m_formats = sequence.m_formats; } + + void add(int from, int to, const QTextCharFormat &format = QTextCharFormat()) + { + for (int i = from; i < to; ++i) + m_formats.append(format); + } + + QList<QTextCharFormat> m_formats; +}; + +class HighlighterMock : public TextEditor::Internal::Highlighter +{ +public: + HighlighterMock(const QSharedPointer<TextEditor::Internal::Context> &defaultContext, + QTextDocument *parent = 0); + + void reset(); + void showDebugDetails(); + void considerEmptyLines(); + + void startNoTestCalls(); + void endNoTestCalls(); + + void setExpectedBlockState(const int state); + void setExpectedBlockStates(const QList<int> &states); + void setExpectedHighlightSequence(const HighlightSequence &format); + void setExpectedHighlightSequences(const QList<HighlightSequence> &formats); + +protected: + virtual void highlightBlock(const QString &text); + +private: + QList<int> m_states; + int m_statesCounter; + QList<HighlightSequence> m_formatSequence; + int m_formatsCounter; + bool m_debugDetails; + bool m_noTestCall; + bool m_considerEmptyLines; +}; + +#endif // HIGHLIGHTERMOCK_H diff --git a/tests/auto/generichighlighter/highlighterengine/tst_highlighterengine.cpp b/tests/auto/generichighlighter/highlighterengine/tst_highlighterengine.cpp new file mode 100644 index 00000000000..da28d88d3e9 --- /dev/null +++ b/tests/auto/generichighlighter/highlighterengine/tst_highlighterengine.cpp @@ -0,0 +1,780 @@ +/************************************************************************** +** +** This file is part of Qt Creator +** +** Copyright (c) 2010 Nokia Corporation and/or its subsidiary(-ies). +** +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** Commercial Usage +** +** Licensees holding valid Qt Commercial licenses may use this file 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 Nokia. +** +** GNU Lesser General Public License Usage +** +** Alternatively, 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. +** +** If you are unsure which license is appropriate for your use, please +** contact the sales department at http://qt.nokia.com/contact. +** +**************************************************************************/ + +#include "highlightdefinition.h" +#include "keywordlist.h" +#include "itemdata.h" +#include "context.h" +#include "specificrules.h" +#include "highlightermock.h" +#include "formats.h" + +#include <QtCore/QSharedPointer> +#include <QtCore/QScopedPointer> +#include <QtCore/QList> +#include <QtCore/QMetaType> +#include <QtGui/QPlainTextEdit> +#include <QtTest/QtTest> + +using namespace TextEditor; +using namespace Internal; + +Q_DECLARE_METATYPE(HighlightSequence) +Q_DECLARE_METATYPE(QList<int>) +Q_DECLARE_METATYPE(QList<HighlightSequence>) + +class tst_HighlighterEngine : public QObject +{ + Q_OBJECT +public: + tst_HighlighterEngine(); + +private slots: + void initTestCase(); + void init(); + + void testSimpleLine(); + void testSimpleLine_data(); + + void testLineContinue(); + void testLineContinue_data(); + + void testEditingLineContinue0(); + void testEditingLineContinue1(); + void testEditingLineContinue2(); + void testEditingLineContinue3(); + void testEditingLineContinue4(); + void testEditingLineContinue5(); + + void testPersistentStates(); + void testPersistentStates_data(); + +private: + void createKeywords(); + void createContexts(); + void createItemDatas(); + + void setExpectedData(int state, const HighlightSequence &seq); + void setExpectedData(const QList<int> &states, const QList<HighlightSequence> &seqs); + void createColumns(); + void test(); + void test(int state, const HighlightSequence &seq, const QString &line); + void test(const QList<int> &states, const QList<HighlightSequence> &seqs, const QString &lines); + + void clear(QList<int> *states, QList<HighlightSequence> *sequences) const; + + QList<int> createlDefaultStatesList(int size) const; + + void addCharactersToBegin(QTextBlock block, const QString &s); + void addCharactersToEnd(QTextBlock block, const QString &s); + void removeFirstCharacters(const QTextBlock &block, int n); + void removeLastCharacters(const QTextBlock &block, int n); + + void setupForEditingLineContinue(); + + QSharedPointer<HighlightDefinition> m_definition; + QScopedPointer<HighlighterMock> m_highlighterMock; + QPlainTextEdit m_text; +}; + +tst_HighlighterEngine::tst_HighlighterEngine() : + m_definition(new HighlightDefinition) +{} + +void tst_HighlighterEngine::initTestCase() +{ + QWARN("\n***********************************************************\ + \n*** Spaces are ignored when comparing text char formats ***\ + \n***********************************************************"); + + createKeywords(); + createContexts(); + createItemDatas(); + + m_highlighterMock.reset(new HighlighterMock(m_definition->initialContext())); + m_highlighterMock->setDocument(m_text.document()); + m_highlighterMock->configureFormat(Highlighter::Keyword, Formats::instance().keywordFormat()); + m_highlighterMock->configureFormat(Highlighter::DataType, Formats::instance().dataTypeFormat()); + m_highlighterMock->configureFormat(Highlighter::Decimal, Formats::instance().decimalFormat()); + m_highlighterMock->configureFormat(Highlighter::BaseN, Formats::instance().baseNFormat()); + m_highlighterMock->configureFormat(Highlighter::Float, Formats::instance().floatFormat()); + m_highlighterMock->configureFormat(Highlighter::Char, Formats::instance().charFormat()); + m_highlighterMock->configureFormat(Highlighter::String, Formats::instance().stringFormat()); + m_highlighterMock->configureFormat(Highlighter::Comment, Formats::instance().commentFormat()); + m_highlighterMock->configureFormat(Highlighter::Alert, Formats::instance().alertFormat()); + m_highlighterMock->configureFormat(Highlighter::Error, Formats::instance().errorFormat()); + m_highlighterMock->configureFormat(Highlighter::Function, Formats::instance().functionFormat()); + m_highlighterMock->configureFormat(Highlighter::RegionMarker, + Formats::instance().regionMarketFormat()); + m_highlighterMock->configureFormat(Highlighter::Others, Formats::instance().othersFormat()); +} + +void tst_HighlighterEngine::init() +{ + m_highlighterMock->reset(); +} + +void tst_HighlighterEngine::createKeywords() +{ + QSharedPointer<KeywordList> keywords = m_definition->createKeywordList("keywords"); + keywords->addKeyword("int"); + keywords->addKeyword("long"); +} + +void tst_HighlighterEngine::createContexts() +{ + // Normal context + QSharedPointer<Context> normal = m_definition->createContext("Normal", true); + normal->setItemData("Normal Text"); + normal->setLineEndContext("#stay"); + normal->setDefinition(m_definition); + + // AfterHash context + QSharedPointer<Context> afterHash = m_definition->createContext("AfterHash", false); + afterHash->setItemData("Error"); + afterHash->setLineEndContext("#pop"); + afterHash->setDefinition(m_definition); + + // Define context + QSharedPointer<Context> define = m_definition->createContext("Define", false); + define->setItemData("Preprocessor"); + define->setLineEndContext("#pop"); + define->setDefinition(m_definition); + + // Preprocessor context + QSharedPointer<Context> preprocessor = m_definition->createContext("Preprocessor", false); + preprocessor->setItemData("Preprocessor"); + preprocessor->setLineEndContext("#pop"); + preprocessor->setDefinition(m_definition); + + // SimpleComment context + QSharedPointer<Context> simpleComment = m_definition->createContext("SimpleComment", false); + simpleComment->setItemData("Comment"); + simpleComment->setLineEndContext("#pop"); + simpleComment->setDefinition(m_definition); + + // MultilineComment context + QSharedPointer<Context> multiComment = m_definition->createContext("MultilineComment", false); + multiComment->setItemData("Comment"); + multiComment->setLineEndContext("#stay"); + multiComment->setDefinition(m_definition); + + // NestedComment context + QSharedPointer<Context> nestedComment = m_definition->createContext("NestedComment", false); + nestedComment->setItemData("Other Comment"); + nestedComment->setLineEndContext("#stay"); + nestedComment->setDefinition(m_definition); + + // Dummy context + QSharedPointer<Context> dummy = m_definition->createContext("Dummy", false); + dummy->setItemData("Dummy"); + dummy->setLineEndContext("#pop"); + dummy->setDefinition(m_definition); + + // Rules + DetectCharRule *r0 = new DetectCharRule; + r0->setChar("#"); + r0->setContext("AfterHash"); + r0->setFirstNonSpace("true"); + r0->setLookAhead("true"); + r0->setDefinition(m_definition); + normal->addRule(QSharedPointer<Rule>(r0)); + + RegExprRule *r1 = new RegExprRule; + r1->setPattern("#\\s*define.*((?=\\\\))"); + r1->setInsensitive("true"); + r1->setContext("Define"); + r1->setItemData("Preprocessor"); + r1->setFirstNonSpace("true"); + r1->setDefinition(m_definition); + afterHash->addRule(QSharedPointer<Rule>(r1)); + + RegExprRule *r2 = new RegExprRule; + r2->setPattern("#\\s*(?:define|undef)"); + r2->setInsensitive("true"); + r2->setContext("Preprocessor"); + r2->setItemData("Preprocessor"); + r2->setFirstNonSpace("true"); + r2->setDefinition(m_definition); + afterHash->addRule(QSharedPointer<Rule>(r2)); + + LineContinueRule *r3 = new LineContinueRule; + r3->setItemData("Preprocessor"); + r3->setContext("#stay"); + r3->setDefinition(m_definition); + define->addRule(QSharedPointer<Rule>(r3)); + + LineContinueRule *r4 = new LineContinueRule; + r4->setItemData("Preprocessor"); + r4->setContext("#stay"); + r4->setDefinition(m_definition); + preprocessor->addRule(QSharedPointer<Rule>(r4)); + + KeywordRule *r5 = new KeywordRule(m_definition); + r5->setList("keywords"); + r5->setItemData("Keyword"); + r5->setContext("#stay"); + r5->setDefinition(m_definition); + normal->addRule(QSharedPointer<Rule>(r5)); + + IntRule *r6 = new IntRule; + r6->setItemData("Decimal"); + r6->setContext("#stay"); + r6->setDefinition(m_definition); + normal->addRule(QSharedPointer<Rule>(r6)); + + StringDetectRule *r7 = new StringDetectRule; + r7->setItemData("Decimal"); + r7->setContext("#stay"); + r7->setString("LL"); + r7->setInsensitive("true"); + r7->setDefinition(m_definition); + r6->addChild(QSharedPointer<Rule>(r7)); + + StringDetectRule *r8 = new StringDetectRule; + r8->setItemData("Decimal"); + r8->setContext("#stay"); + r8->setString("UL"); + r8->setInsensitive("true"); + r8->setDefinition(m_definition); + r6->addChild(QSharedPointer<Rule>(r8)); + + HlCOctRule *r9 = new HlCOctRule; + r9->setItemData("Octal"); + r9->setContext("#stay"); + r9->setDefinition(m_definition); + normal->addRule(QSharedPointer<Rule>(r9)); + + Detect2CharsRule *r10 = new Detect2CharsRule; + r10->setChar("/"); + r10->setChar1("/"); + r10->setItemData("Comment"); + r10->setContext("SimpleComment"); + r10->setDefinition(m_definition); + normal->addRule(QSharedPointer<Rule>(r10)); + + DetectIdentifierRule *r11 = new DetectIdentifierRule; + r11->setDefinition(m_definition); + simpleComment->addRule(QSharedPointer<Rule>(r11)); + + Detect2CharsRule *r12 = new Detect2CharsRule; + r12->setChar("/"); + r12->setChar1("*"); + r12->setItemData("Comment"); + r12->setContext("MultilineComment"); + r12->setDefinition(m_definition); + normal->addRule(QSharedPointer<Rule>(r12)); + + Detect2CharsRule *r13 = new Detect2CharsRule; + r13->setChar("*"); + r13->setChar1("/"); + r13->setItemData("Comment"); + r13->setContext("#pop"); + r13->setDefinition(m_definition); + multiComment->addRule(QSharedPointer<Rule>(r13)); + + Detect2CharsRule *r14 = new Detect2CharsRule; + r14->setChar("/"); + r14->setChar1("#"); + r14->setItemData("Other Comment"); + r14->setContext("NestedComment"); + r14->setDefinition(m_definition); + QSharedPointer<Rule> sr14(r14); + multiComment->addRule(sr14); + + Detect2CharsRule *r15 = new Detect2CharsRule; + r15->setChar("#"); + r15->setChar1("/"); + r15->setItemData("Other Comment"); + r15->setContext("#pop"); + r15->setDefinition(m_definition); + nestedComment->addRule(QSharedPointer<Rule>(r15)); + + DetectCharRule *r16 = new DetectCharRule; + r16->setChar("@"); + r16->setItemData("Marker"); + r16->setContext("Dummy"); + r16->setDefinition(m_definition); + multiComment->addRule(QSharedPointer<Rule>(r16)); + + StringDetectRule *r17 = new StringDetectRule; + r17->setString("dummy"); + r17->setItemData("Dummy"); + r17->setContext("#stay"); + r17->setDefinition(m_definition); + dummy->addRule(QSharedPointer<Rule>(r17)); + + dummy->addRule(sr14); +} + +void tst_HighlighterEngine::createItemDatas() +{ + QSharedPointer<ItemData> normalText = m_definition->createItemData("Normal Text"); + normalText->setStyle("dsNormal"); + QSharedPointer<ItemData> preprocessor = m_definition->createItemData("Preprocessor"); + preprocessor->setStyle("dsOthers"); + QSharedPointer<ItemData> error = m_definition->createItemData("Error"); + error->setStyle("dsError"); + QSharedPointer<ItemData> keyword = m_definition->createItemData("Keyword"); + keyword->setStyle("dsKeyword"); + QSharedPointer<ItemData> decimal = m_definition->createItemData("Decimal"); + decimal->setStyle("dsDecVal"); + QSharedPointer<ItemData> octal = m_definition->createItemData("Octal"); + octal->setStyle("dsBaseN"); + QSharedPointer<ItemData> comment = m_definition->createItemData("Comment"); + comment->setStyle("dsComment"); + QSharedPointer<ItemData> otherComment = m_definition->createItemData("Other Comment"); + otherComment->setStyle("dsError"); + QSharedPointer<ItemData> marker = m_definition->createItemData("Marker"); + marker->setStyle("dsRegionMarker"); + QSharedPointer<ItemData> dummy = m_definition->createItemData("Dummy"); + dummy->setStyle("dsDataType"); +} + +void tst_HighlighterEngine::setExpectedData(int state, const HighlightSequence &seq) +{ + m_highlighterMock->setExpectedBlockState(state); + m_highlighterMock->setExpectedHighlightSequence(seq); +} + +void tst_HighlighterEngine::setExpectedData(const QList<int> &states, + const QList<HighlightSequence> &seqs) +{ + m_highlighterMock->setExpectedBlockStates(states); + m_highlighterMock->setExpectedHighlightSequences(seqs); +} + +void tst_HighlighterEngine::createColumns() +{ + QTest::addColumn<QList<int> >("states"); + QTest::addColumn<QList<HighlightSequence> >("sequences"); + QTest::addColumn<QString>("lines"); +} + +void tst_HighlighterEngine::test() +{ + QFETCH(QList<int>, states); + QFETCH(QList<HighlightSequence>, sequences); + QFETCH(QString, lines); + + test(states, sequences, lines); +} + +void tst_HighlighterEngine::test(int state, const HighlightSequence &seq, const QString &line) +{ + setExpectedData(state, seq); + m_text.setPlainText(line); +} + +void tst_HighlighterEngine::test(const QList<int> &states, + const QList<HighlightSequence> &seqs, + const QString &lines) +{ + setExpectedData(states, seqs); + m_text.setPlainText(lines); +} + +void tst_HighlighterEngine::clear(QList<int> *states, QList<HighlightSequence> *sequences) const +{ + states->clear(); + sequences->clear(); +} + +QList<int> tst_HighlighterEngine::createlDefaultStatesList(int size) const +{ + QList<int> states; + for (int i = 0; i < size; ++i) + states.append(0); + return states; +} + +void tst_HighlighterEngine::addCharactersToBegin(QTextBlock block, const QString &s) +{ + QTextCursor cursor = m_text.textCursor(); + cursor.beginEditBlock(); + cursor.setPosition(block.position()); + cursor.insertText(s); + cursor.endEditBlock(); +} + +void tst_HighlighterEngine::addCharactersToEnd(QTextBlock block, const QString &s) +{ + QTextCursor cursor = m_text.textCursor(); + cursor.beginEditBlock(); + cursor.setPosition(block.position() + block.length() - 1); + cursor.insertText(s); + cursor.endEditBlock(); +} + +void tst_HighlighterEngine::removeFirstCharacters(const QTextBlock &block, int n) +{ + QTextCursor cursor = m_text.textCursor(); + cursor.beginEditBlock(); + cursor.setPosition(block.position()); + cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, n); + cursor.removeSelectedText(); + cursor.endEditBlock(); +} + +void tst_HighlighterEngine::removeLastCharacters(const QTextBlock &block, int n) +{ + QTextCursor cursor = m_text.textCursor(); + cursor.beginEditBlock(); + cursor.setPosition(block.position() + block.length() - 1); + cursor.movePosition(QTextCursor::PreviousCharacter, QTextCursor::KeepAnchor, n); + cursor.removeSelectedText(); + cursor.endEditBlock(); +} + +void tst_HighlighterEngine::testSimpleLine() +{ + test(); +} + +void tst_HighlighterEngine::testSimpleLine_data() +{ + createColumns(); + + QList<int> states; + QList<HighlightSequence> sequences; + QString text; + + HighlightSequence seqa(0, 3); + HighlightSequence seqb(0, 15, Formats::instance().othersFormat()); + HighlightSequence seqc(0, 1, Formats::instance().errorFormat()); + HighlightSequence seqd(0, 3, Formats::instance().keywordFormat()); + seqd.add(3, 8); + seqd.add(8, 9, Formats::instance().baseNFormat()); + HighlightSequence seqe(0, 4, Formats::instance().keywordFormat()); + seqe.add(4, 9); + seqe.add(9, 12, Formats::instance().decimalFormat()); + HighlightSequence seqf(seqe); + seqf.add(12, 13); + HighlightSequence seqg(seqf); + seqg.add(13, 14); + HighlightSequence seqh(0, 8, Formats::instance().commentFormat()); + HighlightSequence seqi(seqd); + seqi.add(9, 17, Formats::instance().commentFormat()); + HighlightSequence seqj(seqd); + seqj.add(9, 11, Formats::instance().commentFormat()); + HighlightSequence seqk(0, 3); + HighlightSequence seql(0, 3, Formats::instance().keywordFormat()); + HighlightSequence seqm(0, 2); + HighlightSequence seqn(0, 8, Formats::instance().commentFormat()); + + states << 0; + sequences << seqa; + text = "abc"; + QTest::newRow("case 0") << states << sequences << text; + + sequences.clear(); + sequences << seqb; + text = "#define max 100"; + QTest::newRow("case 1") << states << sequences << text; + + sequences.clear(); + sequences << seqc; + text = "#"; + QTest::newRow("case 2") << states << sequences << text; + + sequences.clear(); + sequences << seqd; + text = "int i = 0"; + QTest::newRow("case 3") << states << sequences << text; + + sequences.clear(); + sequences << seqe; + text = "long i = 1LL"; + QTest::newRow("case 4") << states << sequences << text; + + text = "long i = 1ul"; + QTest::newRow("case 5") << states << sequences << text; + + sequences.clear(); + sequences << seqf; + text = "long i = 1ULL"; + QTest::newRow("case 6") << states << sequences << text; + + sequences.clear(); + sequences << seqg; + text = "long i = 1LLUL"; + QTest::newRow("case 7") << states << sequences << text; + + text = "long i = 1ULLL"; + QTest::newRow("case 8") << states << sequences << text; + + sequences.clear(); + sequences << seqh; + text = "//int i;"; + QTest::newRow("case 9") << states << sequences << text; + + sequences.clear(); + sequences << seqi; + text = "int i = 0//int i;"; + QTest::newRow("case 10") << states << sequences << text; + + sequences.clear(); + sequences << seqj; + text = "int i = 0//"; + QTest::newRow("case 11") << states << sequences << text; + + sequences.clear(); + sequences << seqk << seqk; + text = "bla\nbla"; + QTest::newRow("case 12") << createlDefaultStatesList(2) << sequences << text; + + sequences.clear(); + sequences << seql << seqm; + text = "int\ni;"; + QTest::newRow("case 13") << createlDefaultStatesList(2) << sequences << text; + + sequences.clear(); + sequences << seqn << seqm; + text = "//int i;\ni;"; + QTest::newRow("case 14") << createlDefaultStatesList(2) << sequences << text; +} + +void tst_HighlighterEngine::testLineContinue() +{ + test(); +} + +void tst_HighlighterEngine::testLineContinue_data() +{ + createColumns(); + + QList<int> states; + QList<HighlightSequence> sequences; + QString lines; + + HighlightSequence seqa(0, 12, Formats::instance().othersFormat()); + HighlightSequence seqb(0, 7, Formats::instance().othersFormat()); + HighlightSequence seqc(0, 8, Formats::instance().othersFormat()); + HighlightSequence seqd(0, 3, Formats::instance().othersFormat()); + HighlightSequence seqe(0, 3, Formats::instance().keywordFormat()); + seqe.add(3, 8); + seqe.add(8, 9, Formats::instance().baseNFormat()); + seqe.add(9, 10); + + states << 1 << 2; + sequences << seqa << seqb; + lines = "#define max\\\n 100"; + QTest::newRow("case 0") << states << sequences << lines; + + clear(&states, &sequences); + states << 1 << 1; + sequences << seqa << seqc; + lines = "#define max\\\n 100\\"; + QTest::newRow("case 1") << states << sequences << lines; + + clear(&states, &sequences); + states << 1 << 1 << 2; + sequences << seqa << seqc << seqd; + lines = "#define max\\\n 100\\\n000"; + QTest::newRow("case 2") << states << sequences << lines; + + clear(&states, &sequences); + states << 1 << 1 << 2 << 0; + sequences << seqa << seqc << seqd << seqe; + lines = "#define max\\\n 100\\\n000\nint i = 0;"; + QTest::newRow("case 3") << states << sequences << lines; +} + +void tst_HighlighterEngine::testPersistentStates() +{ + test(); +} + +void tst_HighlighterEngine::testPersistentStates_data() +{ + createColumns(); + + QList<int> states; + QList<HighlightSequence> sequences; + QString text; + + HighlightSequence seqa(0, 3, Formats::instance().keywordFormat()); + seqa.add(3, 6); + seqa.add(6, 15, Formats::instance().commentFormat()); + seqa.add(15, 16); + HighlightSequence seqb(0, 8, Formats::instance().commentFormat()); + HighlightSequence seqc(0, 2, Formats::instance().commentFormat()); + HighlightSequence seqd(0, 9, Formats::instance().commentFormat()); + seqd.add(9, 18, Formats::instance().errorFormat()); + HighlightSequence seqe(0, 5, Formats::instance().errorFormat()); + seqe.add(5, 8, Formats::instance().commentFormat()); + HighlightSequence seqf(0, 2, Formats::instance().commentFormat()); + seqf.add(2, 6); + HighlightSequence seqg(0, 1); + seqg.add(1, 7, Formats::instance().commentFormat()); + seqg.add(7, 8, Formats::instance().regionMarketFormat()); + seqg.add(8, 15, Formats::instance().errorFormat()); + seqg.add(15, 16, Formats::instance().regionMarketFormat()); + seqg.add(16, 21, Formats::instance().dataTypeFormat()); + seqg.add(21, 22, Formats::instance().regionMarketFormat()); + HighlightSequence seqh(0, 2); + + states << 0; + sequences << seqa; + text = "int i /* = 0 */;"; + QTest::newRow("case 0") << states << sequences << text; + + clear(&states, &sequences); + states << 3 << 3; + sequences << seqb << seqc; + text = "/*int i;\ni;"; + QTest::newRow("case 1") << states << sequences << text; + + clear(&states, &sequences); + states << 3 << 3 << 3; + sequences << seqb << seqc << seqb; + text = "/*int i;\ni;\nint abc;"; + QTest::newRow("case 2") << states << sequences << text; + + clear(&states, &sequences); + states << 3 << 3 << 0; + sequences << seqb << seqc << seqc; + text = "/*int i;\ni;\n*/"; + QTest::newRow("case 3") << states << sequences << text; + + clear(&states, &sequences); + states << 4 << 3 << 0; + sequences << seqd << seqe << seqf; + text = "/*int i; /# int j;\nfoo#/bar\n*/f();"; + QTest::newRow("case 4") << states << sequences << text; + + clear(&states, &sequences); + states << 3; + sequences << seqg; + text = "i/*bla @/#foo#/ dummy "; + QTest::newRow("case 5") << states << sequences << text; + + clear(&states, &sequences); + states << 3 << 3; + sequences << seqg << seqc; + text = "i/*bla @/#foo#/ dummy \ni;"; + QTest::newRow("case 6") << states << sequences << text; + + clear(&states, &sequences); + states << 3 << 3 << 0; + sequences << seqg << seqc << seqh; + text = "i/*bla @/#foo#/ dummy \ni;\n*/"; + QTest::newRow("case 7") << states << sequences << text; +} + +void tst_HighlighterEngine::setupForEditingLineContinue() +{ + m_highlighterMock->startNoTestCalls(); + m_text.setPlainText("#define max\\\n xxx\\\nzzz"); + m_highlighterMock->endNoTestCalls(); +} + +void tst_HighlighterEngine::testEditingLineContinue0() +{ + setupForEditingLineContinue(); + + QList<HighlightSequence> sequences; + HighlightSequence seqa(0, 11, Formats::instance().othersFormat()); + HighlightSequence seqb(0, 8); + HighlightSequence seqc(0, 3); + sequences << seqa << seqb << seqc; + setExpectedData(createlDefaultStatesList(3), sequences); + + removeLastCharacters(m_text.document()->firstBlock(), 1); +} + +void tst_HighlighterEngine::testEditingLineContinue1() +{ + setupForEditingLineContinue(); + + setExpectedData(1, HighlightSequence(0, 6, Formats::instance().othersFormat())); + + // In this case highlighting should be triggered only for the modified line. + removeFirstCharacters(m_text.document()->firstBlock().next(), 2); +} + +void tst_HighlighterEngine::testEditingLineContinue2() +{ + setupForEditingLineContinue(); + + QList<HighlightSequence> sequences; + HighlightSequence seqa(0, 17, Formats::instance().othersFormat()); + HighlightSequence seqb(0, 8); + HighlightSequence seqc(0, 3); + sequences << seqa << seqb << seqc; + setExpectedData(createlDefaultStatesList(3), sequences); + + addCharactersToEnd(m_text.document()->firstBlock(), "ixum"); +} + +void tst_HighlighterEngine::testEditingLineContinue3() +{ + setupForEditingLineContinue(); + + setExpectedData(1, HighlightSequence(0, 12, Formats::instance().othersFormat())); + + // In this case highlighting should be triggered only for the modified line. + addCharactersToBegin(m_text.document()->firstBlock().next(), "ixum"); +} + +void tst_HighlighterEngine::testEditingLineContinue4() +{ + setupForEditingLineContinue(); + + QList<int> states; + states << 2 << 0 << 0; + QList<HighlightSequence> sequences; + HighlightSequence seqa(0, 0); + HighlightSequence seqb(0, 8); + HighlightSequence seqc(0, 3); + sequences << seqa << seqb << seqc; + setExpectedData(states, sequences); + + m_highlighterMock->considerEmptyLines(); + addCharactersToBegin(m_text.document()->firstBlock().next(), "\n"); +} + +void tst_HighlighterEngine::testEditingLineContinue5() +{ + setupForEditingLineContinue(); + + QList<int> states; + states << 2 << 0; + QList<HighlightSequence> sequences; + HighlightSequence seqa(0, 9, Formats::instance().othersFormat()); + HighlightSequence seqb(0, 3); + sequences << seqa << seqb; + setExpectedData(states, sequences); + + addCharactersToEnd(m_text.document()->firstBlock().next(), "x"); +} + +QTEST_MAIN(tst_HighlighterEngine) +#include "tst_highlighterengine.moc" + -- GitLab