File indexing completed on 2024-05-19 04:39:59
0001 /* 0002 SPDX-FileCopyrightText: 2016 Anton Anikin <anton.anikin@htower.ru> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "test_kdevformatsource.h" 0008 #include "../kdevformatfile.h" 0009 #include "../filesystemhelpers.h" 0010 0011 #include <QTest> 0012 #include <QByteArray> 0013 #include <QByteArrayList> 0014 #include <QDebug> 0015 #include <QDir> 0016 #include <QFile> 0017 #include <QFileInfo> 0018 #include <QString> 0019 #include <QStringList> 0020 #include <QTemporaryDir> 0021 #include <QTextStream> 0022 #include <QStandardPaths> 0023 0024 #include <vector> 0025 0026 QTEST_MAIN(KDevelop::TestKdevFormatSource) 0027 0028 using namespace KDevelop; 0029 0030 namespace { 0031 QString applyFormatting(const QString& path, bool expectedFormattingResult) 0032 { 0033 KDevFormatFile formatFile(path, path); 0034 if (!formatFile.find()) { 0035 return "found no format_sources file for " + path; 0036 } 0037 if (!formatFile.read()) { 0038 return "reading format_sources file failed for " + path; 0039 } 0040 if (formatFile.apply() != expectedFormattingResult) { 0041 if (expectedFormattingResult) { 0042 return "formatting was expected to succeed but actually failed for " + path; 0043 } else { 0044 return "formatting was expected to fail but actually succeeded for " + path; 0045 } 0046 } 0047 return QString{}; 0048 } 0049 } 0050 0051 TestKdevFormatSource::TestKdevFormatSource() 0052 { 0053 } 0054 0055 TestKdevFormatSource::~TestKdevFormatSource() 0056 { 0057 } 0058 0059 void TestKdevFormatSource::initTestCase() 0060 { 0061 QStandardPaths::setTestModeEnabled(true); 0062 } 0063 0064 void TestKdevFormatSource::testNotFound_data() 0065 { 0066 static const QStringList formatFileData = {}; 0067 0068 QCOMPARE(initTest(formatFileData), true); 0069 0070 for (const Source& source : qAsConst(m_sources)) { 0071 QTest::newRow(source.path.toUtf8()) << source.path << false << false << false << source.lines; 0072 } 0073 } 0074 0075 void TestKdevFormatSource::testNotFound() 0076 { 0077 runTest(); 0078 } 0079 0080 void TestKdevFormatSource::testNoCommands_data() 0081 { 0082 static const QStringList formatFileData = {QStringLiteral("# some comment")}; 0083 0084 QCOMPARE(initTest(formatFileData), true); 0085 0086 for (const Source& source : qAsConst(m_sources)) { 0087 QTest::newRow(source.path.toUtf8()) << source.path << true << false << false << source.lines; 0088 } 0089 } 0090 0091 void TestKdevFormatSource::testNoCommands() 0092 { 0093 runTest(); 0094 } 0095 0096 void TestKdevFormatSource::testNotMatch_data() 0097 { 0098 static const QStringList formatFileData = {QStringLiteral("notmatched.cpp : unused_command")}; 0099 0100 QCOMPARE(initTest(formatFileData), true); 0101 0102 for (const Source& source : qAsConst(m_sources)) { 0103 QTest::newRow(source.path.toUtf8()) << source.path << true << true << false << source.lines; 0104 } 0105 } 0106 0107 void TestKdevFormatSource::testNotMatch() 0108 { 0109 runTest(); 0110 } 0111 0112 void TestKdevFormatSource::testMatch1_data() 0113 { 0114 static const QStringList formatFileData({ 0115 QStringLiteral("src1/source_1.cpp : cat $ORIGFILE | sed 's/foo/FOO/' > tmp && mv tmp $ORIGFILE"), 0116 QStringLiteral("src2/source_2.cpp : cat $ORIGFILE | sed 's/sqrt/std::sqrt/' > tmp && mv tmp $ORIGFILE"), 0117 QStringLiteral("*.cpp : cat $ORIGFILE | sed 's/z/Z/' > tmp && mv tmp $ORIGFILE"), 0118 QStringLiteral("notmatched.cpp : unused_command"), 0119 }); 0120 0121 QCOMPARE(initTest(formatFileData), true); 0122 0123 m_sources[0].lines.replaceInStrings(QStringLiteral("foo"), QStringLiteral("FOO")); 0124 m_sources[1].lines.replaceInStrings(QStringLiteral("sqrt"), QStringLiteral("std::sqrt")); 0125 m_sources[2].lines.replaceInStrings(QStringLiteral("z"), QStringLiteral("Z")); 0126 0127 for (const Source& source : qAsConst(m_sources)) { 0128 QTest::newRow(source.path.toUtf8()) << source.path << true << true << true << source.lines; 0129 } 0130 } 0131 0132 void TestKdevFormatSource::testMatch1() 0133 { 0134 runTest(); 0135 } 0136 0137 void TestKdevFormatSource::testMatch2_data() 0138 { 0139 static const QStringList formatFileData({QStringLiteral("cat $ORIGFILE | sed 's/;/;;/' > tmp && mv tmp $ORIGFILE")}); 0140 0141 QCOMPARE(initTest(formatFileData), true); 0142 0143 for (Source& source : m_sources) { 0144 source.lines.replaceInStrings(QStringLiteral(";"), QStringLiteral(";;")); 0145 QTest::newRow(source.path.toUtf8()) << source.path << true << true << true << source.lines; 0146 } 0147 } 0148 0149 void TestKdevFormatSource::testMatch2() 0150 { 0151 runTest(); 0152 } 0153 0154 void TestKdevFormatSource::testWildcardPathMatching_data() 0155 { 0156 struct FormatInfo{ const char* dir; const char* contents; }; 0157 struct Row{ 0158 const char* dataTag; 0159 std::vector<FormatInfo> formatInfos; 0160 std::vector<const char*> unmatchedPaths; 0161 std::vector<const char*> matchedPaths; 0162 }; 0163 0164 const std::vector<Row> dataRows{ 0165 Row{"format_sources without wildcards (simple syntax)", 0166 {FormatInfo{"", "true"}}, 0167 {}, 0168 {"x", "a/b", "exclude", "x.c", "p q\tr", "v/l/p/a/t/h.x"} 0169 }, Row{"Single root format_sources with a single command", 0170 {FormatInfo{"", "rd/* *include* *.h : true"}}, 0171 {"x", "r", "r.d", "includ", "includh", "rdh", "rd.h/x", "a/b.hh", "rc/x.h/y"}, 0172 {"x.h", "rd/x", "rd/x.h", "aincludeb", "include", "include.h", "rd/a/b/c", "a/b/c.h", "a/include"} 0173 }, Row{"Single root format_sources with different commands", 0174 {FormatInfo{"", "*inc/*:\n q/* *x?z:true \n dd/*: \n *.c:false \n *ab*:true"}}, 0175 {"q", "a.b", "xz", "xyzc", "c", "ac", "inc", "inc-/x", "ayz", "xy", "add/s", "incc", "a./c", "x/yz", "a-b", "minc"}, 0176 {"xyz", "x.c", "incxyz", "ainc/b.c", "a/b/.c", "a/.c", "x/z", "a/x-z", "p/x.z", "asinc/v", "a/b/cab/d/e", "dd/d", "dd/.c"} 0177 }, Row{"Multiple format_sources files", 0178 {FormatInfo{"a/b/", "q/* *x?z : false"}, FormatInfo{"", "*.c *cab* : true"}}, 0179 {"a/q", "a/xyz", "q/x", "xz", "a/b/qu", "a/bu/xyz", "ab/q/x", "a/b/qt/x", "a/bxyz", "a/x/z", "a/b/xz", "a/b/.c", "a/b/x-z.c"}, 0180 {"a/b/xyz", "x.c", "a/b/cdxyz", "a/b/cd/xyz", "a/b/q/x", "a/.c", "a/b/x/z", "exclude.c", "a/bcab/d/e"} 0181 }, Row{"Case sensitivity", 0182 {FormatInfo{"", "pQ* *RS* : true"}}, 0183 {"a/b/CDE", "cdpq", "a/b/.e", "a/b/cDe", "prcpQ.Eqs"}, 0184 {"a/b/pQrs", "a/b/c/d/pq/rs", "a/b/RSPQ", "pq", "uvrs", "PQa/b"} 0185 }}; 0186 0187 QTest::addColumn<QStringList>("formatDirs"); 0188 QTest::addColumn<QByteArrayList>("formatContents"); 0189 QTest::addColumn<QStringList>("unmatchedPaths"); 0190 QTest::addColumn<QStringList>("matchedPaths"); 0191 0192 for (const Row& row : dataRows) { 0193 QStringList formatDirs; 0194 QByteArrayList formatContents; 0195 for (const FormatInfo& info : row.formatInfos) { 0196 formatDirs.push_back(info.dir); 0197 formatContents.push_back(info.contents); 0198 } 0199 const QStringList unmatchedPaths(row.unmatchedPaths.cbegin(), row.unmatchedPaths.cend()); 0200 const QStringList matchedPaths(row.matchedPaths.cbegin(), row.matchedPaths.cend()); 0201 QTest::newRow(row.dataTag) << formatDirs << formatContents << unmatchedPaths << matchedPaths; 0202 } 0203 } 0204 0205 void TestKdevFormatSource::testWildcardPathMatching() 0206 { 0207 QFETCH(QStringList, formatDirs); 0208 QFETCH(QByteArrayList, formatContents); 0209 QFETCH(QStringList, unmatchedPaths); 0210 QFETCH(QStringList, matchedPaths); 0211 0212 QTemporaryDir tmpDir; 0213 QVERIFY2(tmpDir.isValid(), qPrintable("couldn't create temporary directory: " + tmpDir.errorString())); 0214 0215 using FilesystemHelpers::makeAbsoluteCreateAndWrite; 0216 0217 for (auto& dir : formatDirs) { 0218 dir = QFileInfo{QDir{dir}, "format_sources"}.filePath(); 0219 } 0220 QString errorPath = makeAbsoluteCreateAndWrite(tmpDir.path(), formatDirs, formatContents); 0221 QVERIFY2(errorPath.isEmpty(), qPrintable("couldn't create or write to temporary file or directory " + errorPath)); 0222 0223 errorPath = makeAbsoluteCreateAndWrite(tmpDir.path(), unmatchedPaths); 0224 if (errorPath.isEmpty()) { 0225 errorPath = makeAbsoluteCreateAndWrite(tmpDir.path(), matchedPaths); 0226 } 0227 QVERIFY2(errorPath.isEmpty(), qPrintable("couldn't create temporary file or directory " + errorPath)); 0228 0229 bool expectedFormattingResult = false; // for unmatchedPaths 0230 for (const auto& paths : { unmatchedPaths, matchedPaths }) { 0231 for (const auto& path : paths) { 0232 QVERIFY2(QFileInfo{path}.isFile(), qPrintable(path + ": file was not created or was deleted")); 0233 const QString error = applyFormatting(path, expectedFormattingResult); 0234 QVERIFY2(error.isEmpty(), qPrintable(error)); 0235 } 0236 expectedFormattingResult = true; // for matchedPaths 0237 } 0238 } 0239 0240 bool TestKdevFormatSource::initTest(const QStringList& formatFileData) 0241 { 0242 QTest::addColumn<QString>("path"); 0243 QTest::addColumn<bool>("isFound"); 0244 QTest::addColumn<bool>("isRead"); 0245 QTest::addColumn<bool>("isApplied"); 0246 QTest::addColumn<QStringList>("lines"); 0247 0248 m_temporaryDir.reset(new QTemporaryDir); 0249 const QString workPath = m_temporaryDir->path(); 0250 qDebug() << "Using temporary dir:" << workPath; 0251 0252 if (!mkPath(workPath + "/src1")) 0253 return false; 0254 0255 if (!mkPath(workPath + "/src2")) 0256 return false; 0257 0258 if (!QDir::setCurrent(workPath)) { 0259 qDebug() << "unable to set current directory to" << workPath; 0260 return false; 0261 } 0262 0263 m_sources.resize(3); 0264 0265 m_sources[0].path = workPath + "/src1/source_1.cpp"; 0266 m_sources[0].lines = QStringList({ 0267 QStringLiteral("void foo(int x) {"), 0268 QStringLiteral(" printf(\"squared x = %d\\n\", x * x);"), 0269 QStringLiteral("}") 0270 }); 0271 0272 m_sources[1].path = workPath + "/src2/source_2.cpp"; 0273 m_sources[1].lines = QStringList({ 0274 QStringLiteral("void bar(double x) {"), 0275 QStringLiteral(" x = sqrt(x);"), 0276 QStringLiteral(" printf(\"sqrt(x) = %e\\n\", x);"), 0277 QStringLiteral("}") 0278 }); 0279 0280 m_sources[2].path = workPath + "/source_3.cpp"; 0281 m_sources[2].lines = QStringList({ 0282 QStringLiteral("void baz(double x, double y) {"), 0283 QStringLiteral(" double z = pow(x, y);"), 0284 QStringLiteral(" printf(\"x^y = %e\\n\", z);"), 0285 QStringLiteral("}") 0286 }); 0287 0288 for (const Source& source : qAsConst(m_sources)) { 0289 if (!writeLines(source.path, source.lines)) 0290 return false; 0291 } 0292 0293 if (!formatFileData.isEmpty() && !writeLines(QStringLiteral("format_sources"), formatFileData)) 0294 return false; 0295 0296 return true; 0297 } 0298 0299 void TestKdevFormatSource::runTest() const 0300 { 0301 QFETCH(QString, path); 0302 QFETCH(bool, isFound); 0303 QFETCH(bool, isRead); 0304 QFETCH(bool, isApplied); 0305 QFETCH(QStringList, lines); 0306 0307 KDevFormatFile formatFile(path, path); 0308 0309 QCOMPARE(formatFile.find(), isFound); 0310 0311 if (isFound) 0312 QCOMPARE(formatFile.read(), isRead); 0313 0314 if (isRead) 0315 QCOMPARE(formatFile.apply(), isApplied); 0316 0317 QStringList processedLines; 0318 QCOMPARE(readLines(path, processedLines), true); 0319 0320 QCOMPARE(processedLines, lines); 0321 } 0322 0323 bool TestKdevFormatSource::mkPath(const QString& path) const 0324 { 0325 if (!QDir().exists(path) && !QDir().mkpath(path)) { 0326 qDebug() << "unable to create directory" << path; 0327 return false; 0328 } 0329 0330 return true; 0331 } 0332 0333 bool TestKdevFormatSource::writeLines(const QString& path, const QStringList& lines) const 0334 { 0335 QFile outFile(path); 0336 if (!outFile.open(QIODevice::WriteOnly)) { 0337 qDebug() << "unable to open file" << path << "for writing"; 0338 return false; 0339 } 0340 0341 QTextStream outStream(&outFile); 0342 for (const QString& line : lines) { 0343 outStream << line << "\n"; 0344 } 0345 0346 outStream.flush(); 0347 outFile.close(); 0348 0349 return true; 0350 } 0351 0352 bool TestKdevFormatSource::readLines(const QString& path, QStringList& lines) const 0353 { 0354 QFile inFile(path); 0355 if (!inFile.open(QIODevice::ReadOnly)) { 0356 qDebug() << "unable to open file" << path << "for reading"; 0357 return false; 0358 } 0359 0360 lines.clear(); 0361 0362 QTextStream inStream(&inFile); 0363 while (!inStream.atEnd()) { 0364 lines += inStream.readLine(); 0365 } 0366 inFile.close(); 0367 0368 return true; 0369 } 0370 0371 #include "moc_test_kdevformatsource.cpp"