forked from qt-creator/qt-creator
TextEditor: add tests for semantic highlighter
Change-Id: Id64c933f01c0dbc0e077656b6f4260b93e124311 Reviewed-by: Christian Stenger <christian.stenger@qt.io>
This commit is contained in:
@@ -23,8 +23,12 @@ class SyntaxHighlighterPrivate
|
|||||||
Q_DECLARE_PUBLIC(SyntaxHighlighter)
|
Q_DECLARE_PUBLIC(SyntaxHighlighter)
|
||||||
public:
|
public:
|
||||||
SyntaxHighlighterPrivate()
|
SyntaxHighlighterPrivate()
|
||||||
|
: SyntaxHighlighterPrivate(TextEditorSettings::fontSettings())
|
||||||
|
{ }
|
||||||
|
|
||||||
|
SyntaxHighlighterPrivate(const FontSettings &fontSettings)
|
||||||
{
|
{
|
||||||
updateFormats(TextEditorSettings::fontSettings());
|
updateFormats(fontSettings);
|
||||||
}
|
}
|
||||||
|
|
||||||
QPointer<QTextDocument> doc;
|
QPointer<QTextDocument> doc;
|
||||||
@@ -76,6 +80,16 @@ void SyntaxHighlighter::delayedRehighlight()
|
|||||||
rehighlight();
|
rehighlight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
SyntaxHighlighter::SyntaxHighlighter(QTextDocument *parent, const FontSettings &fontsettings)
|
||||||
|
: QObject(parent), d_ptr(new SyntaxHighlighterPrivate(fontsettings))
|
||||||
|
{
|
||||||
|
d_ptr->q_ptr = this;
|
||||||
|
if (parent)
|
||||||
|
setDocument(parent);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, int charsAdded)
|
void SyntaxHighlighterPrivate::applyFormatChanges(int from, int charsRemoved, int charsAdded)
|
||||||
{
|
{
|
||||||
bool formatsChanged = false;
|
bool formatsChanged = false;
|
||||||
|
|||||||
@@ -91,6 +91,11 @@ private:
|
|||||||
void delayedRehighlight();
|
void delayedRehighlight();
|
||||||
|
|
||||||
QScopedPointer<SyntaxHighlighterPrivate> d_ptr;
|
QScopedPointer<SyntaxHighlighterPrivate> d_ptr;
|
||||||
|
|
||||||
|
#ifdef WITH_TESTS
|
||||||
|
friend class tst_highlighter;
|
||||||
|
SyntaxHighlighter(QTextDocument *parent, const FontSettings &fontsettings);
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace TextEditor
|
} // namespace TextEditor
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ add_subdirectory(qml)
|
|||||||
add_subdirectory(runextensions)
|
add_subdirectory(runextensions)
|
||||||
add_subdirectory(sdktool)
|
add_subdirectory(sdktool)
|
||||||
add_subdirectory(solutions)
|
add_subdirectory(solutions)
|
||||||
|
add_subdirectory(texteditor)
|
||||||
add_subdirectory(toolchaincache)
|
add_subdirectory(toolchaincache)
|
||||||
add_subdirectory(tracing)
|
add_subdirectory(tracing)
|
||||||
add_subdirectory(treeviewfind)
|
add_subdirectory(treeviewfind)
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ Project {
|
|||||||
"runextensions/runextensions.qbs",
|
"runextensions/runextensions.qbs",
|
||||||
"sdktool/sdktool.qbs",
|
"sdktool/sdktool.qbs",
|
||||||
"solutions/solutions.qbs",
|
"solutions/solutions.qbs",
|
||||||
|
"texteditor/texteditor.qbs",
|
||||||
"toolchaincache/toolchaincache.qbs",
|
"toolchaincache/toolchaincache.qbs",
|
||||||
"tracing/tracing.qbs",
|
"tracing/tracing.qbs",
|
||||||
"treeviewfind/treeviewfind.qbs",
|
"treeviewfind/treeviewfind.qbs",
|
||||||
|
|||||||
1
tests/auto/texteditor/CMakeLists.txt
Normal file
1
tests/auto/texteditor/CMakeLists.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
add_subdirectory(highlighter)
|
||||||
4
tests/auto/texteditor/highlighter/CMakeLists.txt
Normal file
4
tests/auto/texteditor/highlighter/CMakeLists.txt
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
add_qtc_test(tst_highlighter
|
||||||
|
DEPENDS TextEditor Utils Qt::Widgets
|
||||||
|
SOURCES tst_highlighter.cpp
|
||||||
|
)
|
||||||
15
tests/auto/texteditor/highlighter/highlighter.qbs
Normal file
15
tests/auto/texteditor/highlighter/highlighter.qbs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
import qbs
|
||||||
|
|
||||||
|
QtcAutotest {
|
||||||
|
|
||||||
|
Depends { name: "TextEditor" }
|
||||||
|
Depends { name: "Utils" }
|
||||||
|
Depends { name: "Qt.widgets" } // For QTextDocument & friends
|
||||||
|
|
||||||
|
name: "Highlighter autotest"
|
||||||
|
|
||||||
|
Group {
|
||||||
|
name: "Source Files"
|
||||||
|
files: "tst_highlighter.cpp"
|
||||||
|
}
|
||||||
|
}
|
||||||
317
tests/auto/texteditor/highlighter/tst_highlighter.cpp
Normal file
317
tests/auto/texteditor/highlighter/tst_highlighter.cpp
Normal file
@@ -0,0 +1,317 @@
|
|||||||
|
// Copyright (C) 2023 The Qt Company Ltd.
|
||||||
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||||
|
|
||||||
|
#ifndef WITH_TESTS
|
||||||
|
#define WITH_TESTS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <texteditor/semantichighlighter.h>
|
||||||
|
#include <texteditor/syntaxhighlighter.h>
|
||||||
|
#include <texteditor/texteditorsettings.h>
|
||||||
|
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTextBlock>
|
||||||
|
#include <QTextDocument>
|
||||||
|
#include <QtTest>
|
||||||
|
|
||||||
|
namespace TextEditor {
|
||||||
|
|
||||||
|
class tst_highlighter: public QObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void init_testCase();
|
||||||
|
void init();
|
||||||
|
void test_setExtraAdditionalFormats();
|
||||||
|
void test_clearExtraFormats();
|
||||||
|
void test_incrementalApplyAdditionalFormats();
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QTextDocument *doc = nullptr;
|
||||||
|
SyntaxHighlighter *highlighter = nullptr;
|
||||||
|
FontSettings fontsettings;
|
||||||
|
QHash<int, QTextCharFormat> formatHash;
|
||||||
|
QTextCharFormat whitespaceFormat;
|
||||||
|
};
|
||||||
|
|
||||||
|
void tst_highlighter::init_testCase()
|
||||||
|
{
|
||||||
|
QTextCharFormat c0;
|
||||||
|
c0.setFontItalic(true);
|
||||||
|
formatHash[0] = c0;
|
||||||
|
QTextCharFormat c1;
|
||||||
|
c1.setFontOverline(true);
|
||||||
|
formatHash[1] = c1;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_highlighter::init()
|
||||||
|
{
|
||||||
|
const QString text =
|
||||||
|
R"(First
|
||||||
|
Second with spaces
|
||||||
|
|
||||||
|
Last)";
|
||||||
|
|
||||||
|
|
||||||
|
doc = new QTextDocument();
|
||||||
|
doc->setPlainText(text);
|
||||||
|
|
||||||
|
highlighter = new SyntaxHighlighter(doc, fontsettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const HighlightingResults &highlightingResults()
|
||||||
|
{
|
||||||
|
static HighlightingResults results{HighlightingResult(),
|
||||||
|
HighlightingResult(1, 1, 5, 0),
|
||||||
|
HighlightingResult(2, 4, 7, 0),
|
||||||
|
HighlightingResult(2, 17, 5, 1),
|
||||||
|
HighlightingResult(4, 1, 8, 0),
|
||||||
|
HighlightingResult(6, 1, 8, 1)};
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_highlighter::test_setExtraAdditionalFormats()
|
||||||
|
{
|
||||||
|
QCOMPARE(doc->blockCount(), 4);
|
||||||
|
|
||||||
|
SemanticHighlighter::setExtraAdditionalFormats(highlighter, highlightingResults(), formatHash);
|
||||||
|
|
||||||
|
for (auto block = doc->firstBlock(); block.isValid(); block = block.next()) {
|
||||||
|
QVERIFY(block.blockNumber() < 4);
|
||||||
|
QVERIFY(block.layout());
|
||||||
|
auto formats = block.layout()->formats();
|
||||||
|
switch (block.blockNumber()) {
|
||||||
|
case 0: // First
|
||||||
|
QCOMPARE(formats.size(), 1);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), true);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
|
QCOMPARE(formats.at(0).length, 5);
|
||||||
|
break;
|
||||||
|
case 1: // Second with spaces
|
||||||
|
QCOMPARE(formats.size(), 4);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(0).start, 6);
|
||||||
|
QCOMPARE(formats.at(0).length, 1);
|
||||||
|
|
||||||
|
QCOMPARE(formats.at(1).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(1).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(1).start, 11);
|
||||||
|
QCOMPARE(formats.at(1).length, 1);
|
||||||
|
|
||||||
|
QCOMPARE(formats.at(2).format.fontItalic(), true);
|
||||||
|
QCOMPARE(formats.at(2).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(2).start, 3);
|
||||||
|
QCOMPARE(formats.at(2).length, 7);
|
||||||
|
|
||||||
|
QCOMPARE(formats.at(3).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(3).format.fontOverline(), true);
|
||||||
|
QCOMPARE(formats.at(3).start, 16);
|
||||||
|
QCOMPARE(formats.at(3).length, 3);
|
||||||
|
break;
|
||||||
|
case 2: //
|
||||||
|
QCOMPARE(formats.size(), 1);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), true);
|
||||||
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
|
QCOMPARE(formats.at(0).length, 1);
|
||||||
|
break;
|
||||||
|
case 3: // Last
|
||||||
|
QCOMPARE(formats.size(), 2);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), true);
|
||||||
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
|
QCOMPARE(formats.at(0).length, 1);
|
||||||
|
|
||||||
|
QCOMPARE(formats.at(1).format.fontItalic(), true);
|
||||||
|
QCOMPARE(formats.at(1).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(1).start, 0);
|
||||||
|
QCOMPARE(formats.at(1).length, 5);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_highlighter::test_clearExtraFormats()
|
||||||
|
{
|
||||||
|
QCOMPARE(doc->blockCount(), 4);
|
||||||
|
|
||||||
|
SemanticHighlighter::setExtraAdditionalFormats(highlighter, highlightingResults(), formatHash);
|
||||||
|
|
||||||
|
QTextBlock firstBlock = doc->findBlockByNumber(0);
|
||||||
|
QTextBlock spacesLineBlock = doc->findBlockByNumber(1);
|
||||||
|
QTextBlock emptyLineBlock = doc->findBlockByNumber(2);
|
||||||
|
QTextBlock lastBlock = doc->findBlockByNumber(3);
|
||||||
|
|
||||||
|
highlighter->clearExtraFormats(emptyLineBlock);
|
||||||
|
QVERIFY(emptyLineBlock.layout()->formats().isEmpty());
|
||||||
|
|
||||||
|
highlighter->clearExtraFormats(spacesLineBlock);
|
||||||
|
|
||||||
|
auto formats = spacesLineBlock.layout()->formats();
|
||||||
|
// the spaces are not extra formats and should remain when clearing extra formats
|
||||||
|
QCOMPARE(formats.size(), 2);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(0).start, 6);
|
||||||
|
QCOMPARE(formats.at(0).length, 1);
|
||||||
|
|
||||||
|
QCOMPARE(formats.at(1).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(1).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(1).start, 11);
|
||||||
|
QCOMPARE(formats.at(1).length, 1);
|
||||||
|
|
||||||
|
// first and last should be untouched
|
||||||
|
formats = firstBlock.layout()->formats();
|
||||||
|
QCOMPARE(formats.size(), 1);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), true);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
|
QCOMPARE(formats.at(0).length, 5);
|
||||||
|
|
||||||
|
formats = lastBlock.layout()->formats();
|
||||||
|
QCOMPARE(formats.size(), 2);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), true);
|
||||||
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
|
QCOMPARE(formats.at(0).length, 1);
|
||||||
|
|
||||||
|
QCOMPARE(formats.at(1).format.fontItalic(), true);
|
||||||
|
QCOMPARE(formats.at(1).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(1).start, 0);
|
||||||
|
QCOMPARE(formats.at(1).length, 5);
|
||||||
|
|
||||||
|
highlighter->clearAllExtraFormats();
|
||||||
|
|
||||||
|
QVERIFY(firstBlock.layout()->formats().isEmpty());
|
||||||
|
QVERIFY(emptyLineBlock.layout()->formats().isEmpty());
|
||||||
|
formats = spacesLineBlock.layout()->formats();
|
||||||
|
|
||||||
|
QCOMPARE(formats.size(), 2);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(0).start, 6);
|
||||||
|
QCOMPARE(formats.at(0).length, 1);
|
||||||
|
|
||||||
|
QCOMPARE(formats.at(1).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(1).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(1).start, 11);
|
||||||
|
QCOMPARE(formats.at(1).length, 1);
|
||||||
|
|
||||||
|
QVERIFY(lastBlock.layout()->formats().isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_highlighter::test_incrementalApplyAdditionalFormats()
|
||||||
|
{
|
||||||
|
const HighlightingResults newResults{HighlightingResult(),
|
||||||
|
HighlightingResult(1, 1, 5, 0),
|
||||||
|
HighlightingResult(2, 4, 7, 0),
|
||||||
|
HighlightingResult(4, 1, 8, 0),
|
||||||
|
HighlightingResult(6, 1, 8, 1)};
|
||||||
|
|
||||||
|
QCOMPARE(doc->blockCount(), 4);
|
||||||
|
QTextBlock firstBlock = doc->findBlockByNumber(0);
|
||||||
|
QTextBlock spacesLineBlock = doc->findBlockByNumber(1);
|
||||||
|
QTextBlock emptyLineBlock = doc->findBlockByNumber(2);
|
||||||
|
QTextBlock lastBlock = doc->findBlockByNumber(3);
|
||||||
|
|
||||||
|
QFutureInterface<HighlightingResult> fiOld;
|
||||||
|
fiOld.reportResults(highlightingResults());
|
||||||
|
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
|
||||||
|
fiOld.future(),
|
||||||
|
2,
|
||||||
|
0,
|
||||||
|
formatHash);
|
||||||
|
auto formats = firstBlock.layout()->formats();
|
||||||
|
QVERIFY(formats.isEmpty());
|
||||||
|
|
||||||
|
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
|
||||||
|
fiOld.future(),
|
||||||
|
0,
|
||||||
|
2,
|
||||||
|
formatHash);
|
||||||
|
|
||||||
|
formats = firstBlock.layout()->formats();
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), true);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
|
QCOMPARE(formats.at(0).length, 5);
|
||||||
|
|
||||||
|
formats = spacesLineBlock.layout()->formats();
|
||||||
|
QCOMPARE(formats.size(), 2);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(0).start, 6);
|
||||||
|
QCOMPARE(formats.at(0).length, 1);
|
||||||
|
|
||||||
|
QCOMPARE(formats.at(1).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(1).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(1).start, 11);
|
||||||
|
QCOMPARE(formats.at(1).length, 1);
|
||||||
|
|
||||||
|
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
|
||||||
|
fiOld.future(),
|
||||||
|
3,
|
||||||
|
6,
|
||||||
|
formatHash);
|
||||||
|
|
||||||
|
formats = firstBlock.layout()->formats();
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), true);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
|
QCOMPARE(formats.at(0).length, 5);
|
||||||
|
|
||||||
|
formats = lastBlock.layout()->formats();
|
||||||
|
QCOMPARE(formats.size(), 2);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), true);
|
||||||
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
|
QCOMPARE(formats.at(0).length, 1);
|
||||||
|
|
||||||
|
QCOMPARE(formats.at(1).format.fontItalic(), true);
|
||||||
|
QCOMPARE(formats.at(1).format.fontOverline(), false);
|
||||||
|
QCOMPARE(formats.at(1).start, 0);
|
||||||
|
QCOMPARE(formats.at(1).length, 5);
|
||||||
|
|
||||||
|
QFutureInterface<HighlightingResult> fiNew;
|
||||||
|
fiNew.reportResults(newResults);
|
||||||
|
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
|
||||||
|
fiNew.future(),
|
||||||
|
0,
|
||||||
|
3,
|
||||||
|
formatHash);
|
||||||
|
|
||||||
|
// should still contain the highlight from oldResults
|
||||||
|
formats = emptyLineBlock.layout()->formats();
|
||||||
|
QCOMPARE(formats.size(), 1);
|
||||||
|
QCOMPARE(formats.at(0).format.fontItalic(), false);
|
||||||
|
QCOMPARE(formats.at(0).format.fontOverline(), true);
|
||||||
|
QCOMPARE(formats.at(0).start, 0);
|
||||||
|
QCOMPARE(formats.at(0).length, 1);
|
||||||
|
|
||||||
|
SemanticHighlighter::incrementalApplyExtraAdditionalFormats(highlighter,
|
||||||
|
fiNew.future(),
|
||||||
|
3,
|
||||||
|
4,
|
||||||
|
formatHash);
|
||||||
|
|
||||||
|
// should have no results since the new results do not contain a highlight at that position
|
||||||
|
formats = emptyLineBlock.layout()->formats();
|
||||||
|
QVERIFY(formats.isEmpty());
|
||||||
|
}
|
||||||
|
|
||||||
|
void tst_highlighter::cleanup()
|
||||||
|
{
|
||||||
|
delete doc;
|
||||||
|
doc = nullptr;
|
||||||
|
highlighter = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace TextEditor
|
||||||
|
|
||||||
|
QTEST_MAIN(TextEditor::tst_highlighter)
|
||||||
|
|
||||||
|
#include "tst_highlighter.moc"
|
||||||
6
tests/auto/texteditor/texteditor.qbs
Normal file
6
tests/auto/texteditor/texteditor.qbs
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
import qbs
|
||||||
|
|
||||||
|
Project {
|
||||||
|
name: "TextEditor autotests"
|
||||||
|
references: [ "highlighter/highlighter.qbs" ]
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user