File indexing completed on 2024-05-12 04:39:18
0001 /* 0002 SPDX-FileCopyrightText: 2013 Milian Wolff <mail@milianw.de> 0003 SPDX-FileCopyrightText: 2013 Olivier de Gaalon <olivier.jg@gmail.com> 0004 0005 SPDX-License-Identifier: GPL-2.0-or-later 0006 */ 0007 0008 #include "test_files.h" 0009 0010 #include <language/duchain/duchain.h> 0011 #include <language/duchain/problem.h> 0012 #include <language/codegen/coderepresentation.h> 0013 #include <language/backgroundparser/backgroundparser.h> 0014 0015 #include <tests/testcore.h> 0016 #include <tests/autotestshell.h> 0017 #include <tests/json/declarationvalidator.h> 0018 0019 #include "testfilepaths.h" 0020 #include "testprovider.h" 0021 #include "duchain/clanghelpers.h" 0022 0023 //Include all used json tests, otherwise "Test not found" 0024 #include <tests/json/jsondeclarationtests.h> 0025 #include <tests/json/jsonducontexttests.h> 0026 #include <tests/json/jsontypetests.h> 0027 #include <interfaces/ilanguagecontroller.h> 0028 0029 #include <QDebug> 0030 #include <QTest> 0031 #include <QLoggingCategory> 0032 #include <QProcess> 0033 #include <QRegularExpression> 0034 #include <QVersionNumber> 0035 0036 using namespace KDevelop; 0037 0038 QTEST_MAIN(TestFiles) 0039 0040 namespace { 0041 bool isCudaAvailable() 0042 { 0043 return QProcess::execute(QStringLiteral("clang"), {QStringLiteral("-xcuda"), QStringLiteral("-fsyntax-only"), QProcess::nullDevice()}) == 0; 0044 } 0045 0046 QRegularExpression rangeRegularExpression() 0047 { 0048 const auto capturedCursorRegexp = QStringLiteral("(\\(\\d+, \\d+\\))"); 0049 const auto rangeRegexp = QStringLiteral("\\[%1, %2\\]").arg(capturedCursorRegexp, capturedCursorRegexp); 0050 return QRegularExpression{QRegularExpression::anchoredPattern(rangeRegexp)}; 0051 } 0052 0053 void expandTestFilesDir(QVariantMap& testData) 0054 { 0055 const auto idIt = testData.find("identifier"); 0056 if (idIt == testData.end()) { 0057 return; // nothing to adjust 0058 } 0059 0060 static const QLatin1String testFilesDirVariableName("${TEST_FILES_DIR}"); 0061 0062 QCOMPARE(idIt->userType(), QMetaType::QString); 0063 auto identifier = idIt->toString(); 0064 if (!identifier.contains(testFilesDirVariableName)) { 0065 return; // nothing to adjust 0066 } 0067 0068 if (QVersionNumber::fromString(ClangHelpers::clangVersion()) >= QVersionNumber(13, 0, 0)) { 0069 *idIt = identifier.replace(testFilesDirVariableName, TEST_FILES_DIR); 0070 return; // done 0071 } 0072 0073 // Older Clang versions return an empty identifier for unnamed struct and anonymous union. 0074 *idIt = QString(); 0075 0076 // KDevelop's Visitor::createDeclarationCommon() assigns the range's end to its start when 0077 // the identifier is empty, so the same is done below. 0078 const auto rangeIt = testData.find("range"); 0079 if (rangeIt == testData.end()) { 0080 return; // no range => nothing left to adjust 0081 } 0082 0083 QCOMPARE(rangeIt->userType(), QMetaType::QString); 0084 auto range = rangeIt->toString(); 0085 0086 static const auto regexp = rangeRegularExpression(); 0087 QVERIFY(range.contains(regexp)); 0088 *rangeIt = range.replace(regexp, "[\\1, \\1]"); 0089 } 0090 0091 /// libclang 16.0.0 and later does not qualify template arguments redundantly 0092 /// and does not spell out default template argument values. 0093 void adjustTemplateArguments(QVariantMap& testData) 0094 { 0095 if (QVersionNumber::fromString(ClangHelpers::clangVersion()) < QVersionNumber(16, 0, 0)) { 0096 return; // nothing to adjust 0097 } 0098 0099 const auto typeIt = testData.find("type"); 0100 if (typeIt == testData.end()) { 0101 return; // nothing to adjust 0102 } 0103 0104 QCOMPARE(typeIt->userType(), QMetaType::QVariantMap); 0105 auto typeData = typeIt->toMap(); 0106 0107 const auto toStringIt = typeData.find("toString"); 0108 QVERIFY(toStringIt != typeData.end()); 0109 QCOMPARE(toStringIt->userType(), QMetaType::QString); 0110 auto typeString = toStringIt->toString(); 0111 0112 const auto replaceInTypeString = [&](const char* before, const char* after, const char* replacementDescription) { 0113 if (!typeString.contains(QLatin1String{before})) 0114 return; 0115 typeString.replace(QLatin1String{before}, QLatin1String{after}); 0116 qDebug() << replacementDescription << toStringIt->toString() << "=>" << typeString; 0117 *toStringIt = typeString; 0118 *typeIt = typeData; 0119 }; 0120 0121 replaceInTypeString("FormattingBug::X", "X", "Removing template argument qualification:"); 0122 replaceInTypeString("SpacedDefaultParam< 20 >", "SpacedDefaultParam< >", 0123 "Unspecifying default template argument:"); 0124 } 0125 0126 void adjustTestData(QVariantMap& testData) 0127 { 0128 expandTestFilesDir(testData); 0129 adjustTemplateArguments(testData); 0130 } 0131 } // unnamed namespace 0132 0133 void TestFiles::initTestCase() 0134 { 0135 qputenv("KDEV_CLANG_JSON_TEST_RUN", "1"); 0136 qputenv("KDEV_CLANG_EXTRA_ARGUMENTS", "-Wno-unused-variable -Wno-unused-parameter -Wno-unused-comparison -Wno-unused-value -Wno-unused-private-field -Wno-ignored-attributes"); 0137 0138 QLoggingCategory::setFilterRules(QStringLiteral("*.debug=false\ndefault.debug=true\nkdevelop.plugins.clang.debug=true\n")); 0139 QVERIFY(qputenv("KDEV_CLANG_DISPLAY_DIAGS", "1")); 0140 AutoTestShell::init({"kdevclangsupport"}); 0141 TestCore::initialize(Core::NoUi); 0142 DUChain::self()->disablePersistentStorage(); 0143 Core::self()->languageController()->backgroundParser()->setDelay(0); 0144 CodeRepresentation::setDiskChangesForbidden(true); 0145 0146 m_provider = new TestEnvironmentProvider; 0147 IDefinesAndIncludesManager::manager()->registerBackgroundProvider(m_provider); 0148 } 0149 0150 void TestFiles::cleanupTestCase() 0151 { 0152 delete m_provider; 0153 TestCore::shutdown(); 0154 } 0155 0156 void TestFiles::cleanup() 0157 { 0158 m_provider->clear(); 0159 } 0160 0161 void TestFiles::testFiles_data() 0162 { 0163 QTest::addColumn<QString>("fileName"); 0164 const QString testDirPath = TEST_FILES_DIR; 0165 auto patterns = QStringList{"*.h", "*.cpp", "*.c", "*.cl"}; 0166 if (isCudaAvailable()) { 0167 patterns.append("*.cu"); 0168 } 0169 const QStringList files = QDir(testDirPath).entryList(patterns, QDir::Files); 0170 for (const QString& file : files) { 0171 QTest::newRow(file.toUtf8().constData()) << QString(testDirPath + '/' + file); 0172 } 0173 } 0174 0175 void TestFiles::testFiles() 0176 { 0177 QFETCH(QString, fileName); 0178 0179 if (QTest::currentDataTag() == QLatin1String("lambdas.cpp")) { 0180 m_provider->parserArguments += "-std=c++14"; 0181 } else if (QTest::currentDataTag() == QLatin1String("templates.cpp")) { 0182 m_provider->parserArguments += "-std=c++17"; 0183 } 0184 0185 const IndexedString indexedFileName(fileName); 0186 ReferencedTopDUContext top = 0187 DUChain::self()->waitForUpdate(indexedFileName, TopDUContext::AllDeclarationsContextsAndUses); 0188 QVERIFY(top); 0189 DUChainReadLocker lock; 0190 DeclarationValidator validator(adjustTestData); 0191 top->visit(validator); 0192 0193 const auto problems = top->problems(); 0194 for (auto& problem : problems) { 0195 qDebug() << problem; 0196 } 0197 0198 if (QVersionNumber::fromString(ClangHelpers::clangVersion()) < QVersionNumber(9, 0, 0)) 0199 QEXPECT_FAIL("lambdas.cpp", "capture with identifier and initializer aren't visited apparently", Abort); 0200 QVERIFY(validator.testsPassed()); 0201 0202 if (!QTest::currentDataTag() || strcmp("invalid.cpp", QTest::currentDataTag()) != 0) { 0203 QVERIFY(top->problems().isEmpty()); 0204 } 0205 } 0206 0207 #include "moc_test_files.cpp"