File indexing completed on 2025-02-02 14:22:23
0001 /* 0002 SPDX-FileCopyrightText: 2016 Volker Krause <vkrause@kde.org> 0003 0004 SPDX-License-Identifier: MIT 0005 */ 0006 0007 #include "test-config.h" 0008 0009 #include <KSyntaxHighlighting/AbstractHighlighter> 0010 #include <KSyntaxHighlighting/Definition> 0011 #include <KSyntaxHighlighting/FoldingRegion> 0012 #include <KSyntaxHighlighting/Repository> 0013 #include <KSyntaxHighlighting/State> 0014 0015 #include <QDir> 0016 #include <QFile> 0017 #include <QObject> 0018 #include <QStandardPaths> 0019 #include <QTest> 0020 #include <QTextStream> 0021 0022 #include <unordered_map> 0023 0024 using namespace KSyntaxHighlighting; 0025 0026 class FoldingHighlighter : public AbstractHighlighter 0027 { 0028 public: 0029 void highlightFile(const QString &inFileName, const QString &outFileName) 0030 { 0031 QFile outFile(outFileName); 0032 if (!outFile.open(QFile::WriteOnly | QFile::Truncate)) { 0033 qWarning() << "Failed to open output file" << outFileName << ":" << outFile.errorString(); 0034 return; 0035 } 0036 m_out.setDevice(&outFile); 0037 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0038 m_out.setCodec("UTF-8"); 0039 #endif 0040 0041 QFile f(inFileName); 0042 if (!f.open(QFile::ReadOnly)) { 0043 qWarning() << "Failed to open input file" << inFileName << ":" << f.errorString(); 0044 return; 0045 } 0046 0047 QTextStream in(&f); 0048 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 0049 in.setCodec("UTF-8"); 0050 #endif 0051 State state; 0052 bool indentationFoldEnabled = definition().indentationBasedFoldingEnabled(); 0053 if (indentationFoldEnabled) { 0054 m_out << "<indentfold>"; 0055 } 0056 while (!in.atEnd()) { 0057 const auto currentLine = in.readLine(); 0058 state = highlightLine(currentLine, state); 0059 0060 if (indentationFoldEnabled != state.indentationBasedFoldingEnabled()) { 0061 indentationFoldEnabled = state.indentationBasedFoldingEnabled(); 0062 if (indentationFoldEnabled) { 0063 m_out << "<indentfold>"; 0064 } else { 0065 m_out << "</indentfold>"; 0066 } 0067 } 0068 0069 int offset = 0; 0070 for (const auto &fold : std::as_const(m_folds)) { 0071 // use stable ids for output, see below docs for m_stableFoldingIds 0072 const auto stableId = m_stableFoldingIds[fold.region.id()]; 0073 m_out << currentLine.mid(offset, fold.offset - offset); 0074 if (fold.region.type() == FoldingRegion::Begin) { 0075 m_out << "<beginfold id='" << stableId << "'>"; 0076 } else { 0077 m_out << "<endfold id='" << stableId << "'>"; 0078 } 0079 m_out << currentLine.mid(fold.offset, fold.length); 0080 if (fold.region.type() == FoldingRegion::Begin) { 0081 m_out << "</beginfold id='" << stableId << "'>"; 0082 } else { 0083 m_out << "</endfold id='" << stableId << "'>"; 0084 } 0085 offset = fold.offset + fold.length; 0086 } 0087 m_out << currentLine.mid(offset) << '\n'; 0088 m_folds.clear(); 0089 } 0090 0091 m_out.flush(); 0092 } 0093 0094 protected: 0095 void applyFormat(int offset, int length, const Format &format) override 0096 { 0097 Q_UNUSED(offset); 0098 Q_UNUSED(length); 0099 Q_UNUSED(format); 0100 } 0101 0102 void applyFolding(int offset, int length, FoldingRegion region) override 0103 { 0104 Q_ASSERT(region.isValid()); 0105 m_folds.push_back({offset, length, region}); 0106 0107 // create stable id if needed, see below m_stableFoldingIds docs for details 0108 // start with 1 0109 m_stableFoldingIds.emplace(region.id(), m_stableFoldingIds.size() + 1); 0110 } 0111 0112 private: 0113 QTextStream m_out; 0114 struct Fold { 0115 int offset; 0116 int length; 0117 FoldingRegion region; 0118 }; 0119 QVector<Fold> m_folds; 0120 0121 // we use one repository for all tests 0122 // => the folding ids might change even if just unrelated highlighings are added 0123 // => construct some stable id per test based on occurrence of id 0124 std::unordered_map<uint32_t, size_t> m_stableFoldingIds; 0125 }; 0126 0127 class FoldingTest : public QObject 0128 { 0129 Q_OBJECT 0130 public: 0131 explicit FoldingTest(QObject *parent = nullptr) 0132 : QObject(parent) 0133 , m_repo(nullptr) 0134 { 0135 } 0136 0137 private: 0138 Repository *m_repo; 0139 0140 private Q_SLOTS: 0141 void initTestCase() 0142 { 0143 QStandardPaths::setTestModeEnabled(true); 0144 m_repo = new Repository; 0145 initRepositorySearchPaths(*m_repo); 0146 } 0147 0148 void cleanupTestCase() 0149 { 0150 delete m_repo; 0151 m_repo = nullptr; 0152 } 0153 0154 void testFolding_data() 0155 { 0156 QTest::addColumn<QString>("inFile"); 0157 QTest::addColumn<QString>("outFile"); 0158 QTest::addColumn<QString>("refFile"); 0159 QTest::addColumn<QString>("syntax"); 0160 0161 const QDir dir(QStringLiteral(TESTSRCDIR "/input")); 0162 for (const auto &fileName : dir.entryList(QDir::Files | QDir::NoSymLinks | QDir::Readable | QDir::Hidden, QDir::Name)) { 0163 // skip .clang-format file we use to avoid formatting test files 0164 if (fileName == QLatin1String(".clang-format")) { 0165 continue; 0166 } 0167 0168 const auto inFile = dir.absoluteFilePath(fileName); 0169 if (inFile.endsWith(QLatin1String(".syntax"))) { 0170 continue; 0171 } 0172 0173 QString syntax; 0174 QFile syntaxOverride(inFile + QStringLiteral(".syntax")); 0175 if (syntaxOverride.exists() && syntaxOverride.open(QFile::ReadOnly)) { 0176 syntax = QString::fromUtf8(syntaxOverride.readAll()).trimmed(); 0177 } 0178 0179 QTest::newRow(fileName.toUtf8().constData()) << inFile << (QStringLiteral(TESTBUILDDIR "/folding.out/") + fileName + QStringLiteral(".fold")) 0180 << (QStringLiteral(TESTSRCDIR "/folding/") + fileName + QStringLiteral(".fold")) << syntax; 0181 } 0182 0183 // cleanup before we test 0184 QDir(QStringLiteral(TESTBUILDDIR "/folding.out/")).removeRecursively(); 0185 QDir().mkpath(QStringLiteral(TESTBUILDDIR "/folding.out/")); 0186 } 0187 0188 void testFolding() 0189 { 0190 QFETCH(QString, inFile); 0191 QFETCH(QString, outFile); 0192 QFETCH(QString, refFile); 0193 QFETCH(QString, syntax); 0194 QVERIFY(m_repo); 0195 0196 auto def = m_repo->definitionForFileName(inFile); 0197 if (!syntax.isEmpty()) { 0198 def = m_repo->definitionForName(syntax); 0199 } 0200 0201 FoldingHighlighter highlighter; 0202 QVERIFY(def.isValid()); 0203 highlighter.setDefinition(def); 0204 highlighter.highlightFile(inFile, outFile); 0205 0206 /** 0207 * compare results 0208 */ 0209 compareFiles(refFile, outFile); 0210 } 0211 }; 0212 0213 QTEST_GUILESS_MAIN(FoldingTest) 0214 0215 #include "foldingtest.moc"