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