File indexing completed on 2024-05-12 04:39:29

0001 /*
0002     SPDX-FileCopyrightText: 2010 Esben Mose Hansen <kde@mosehansen.dk>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "test_cmakemanager.h"
0008 #include "testhelpers.h"
0009 #include "cmakemodelitems.h"
0010 #include "cmakeutils.h"
0011 #include "cmakeimportjsonjob.h"
0012 
0013 #include <QLoggingCategory>
0014 #include <QTemporaryFile>
0015 
0016 #include <interfaces/icore.h>
0017 #include <interfaces/itestsuite.h>
0018 #include <interfaces/itestcontroller.h>
0019 #include <project/interfaces/ibuildsystemmanager.h>
0020 #include <project/abstractfilemanagerplugin.h>
0021 #include <tests/autotestshell.h>
0022 #include <tests/testproject.h>
0023 #include <tests/testcore.h>
0024 #include <testing/ctestsuite.h>
0025 
0026 QTEST_MAIN(TestCMakeManager)
0027 
0028 using namespace KDevelop;
0029 
0030 void TestCMakeManager::initTestCase()
0031 {
0032     QLoggingCategory::setFilterRules("*.debug=false\nkdevplatform.outputview.debug=true\n"
0033                                      "kdevelop.plugins.cmake.debug=true\ndefault.debug=true\n");
0034 
0035     AutoTestShell::init({"KDevCMakeManager", "KDevCMakeBuilder", "KDevMakeBuilder", "KDevStandardOutputView"});
0036     TestCore::initialize();
0037 
0038     cleanup();
0039 }
0040 
0041 void TestCMakeManager::cleanupTestCase()
0042 {
0043     TestCore::shutdown();
0044 }
0045 
0046 void TestCMakeManager::cleanup()
0047 {
0048     const auto projects = ICore::self()->projectController()->projects();
0049     for (IProject* p : projects) {
0050         ICore::self()->projectController()->closeProject(p);
0051     }
0052     QVERIFY(ICore::self()->projectController()->projects().isEmpty());
0053 }
0054 
0055 void TestCMakeManager::testWithBuildDirProject()
0056 {
0057     loadProject(QStringLiteral("with_build_dir"));
0058 }
0059 
0060 void TestCMakeManager::testIncludePaths()
0061 {
0062     IProject* project = loadProject(QStringLiteral("single_subdirectory"));
0063     Path sourceDir = project->path();
0064 
0065     Path fooCpp(sourceDir, QStringLiteral("subdir/foo.cpp"));
0066     QVERIFY(QFile::exists(fooCpp.toLocalFile()));
0067     QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(fooCpp.pathOrUrl()));
0068     ProjectBaseItem* fooCppItem = items.first();
0069 
0070     Path::List includeDirs = project->buildSystemManager()->includeDirectories(fooCppItem);
0071     QVERIFY(includeDirs.size() >= 3);
0072 
0073     Path buildDir(project->buildSystemManager()->buildDirectory(fooCppItem));
0074     QVERIFY(includeDirs.contains(buildDir));
0075 
0076     Path subBuildDir(buildDir, QStringLiteral("subdir/"));
0077     QVERIFY(includeDirs.contains(subBuildDir));
0078 
0079     Path subDir(sourceDir, QStringLiteral("subdir/"));
0080     QVERIFY(includeDirs.contains(subDir));
0081 }
0082 
0083 void TestCMakeManager::testRelativePaths()
0084 {
0085     IProject* project = loadProject(QStringLiteral("relative_paths"), QStringLiteral("/out"));
0086 
0087     Path codeCpp(project->path(), QStringLiteral("../src/code.cpp"));
0088     QVERIFY(QFile::exists( codeCpp.toLocalFile()));
0089     QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(codeCpp.pathOrUrl()));
0090     QCOMPARE(items.size(), 1); // once in the target
0091     ProjectBaseItem* fooCppItem = items.first();
0092 
0093     Path::List includeDirs = project->buildSystemManager()->includeDirectories(fooCppItem);
0094 
0095     Path incDir(project->path(), QStringLiteral("../inc/"));
0096     QVERIFY(includeDirs.contains( incDir ));
0097 }
0098 
0099 void TestCMakeManager::testTargetIncludePaths()
0100 {
0101     IProject* project = loadProject(QStringLiteral("target_includes"));
0102 
0103     Path mainCpp(project->path(), QStringLiteral("main.cpp"));
0104     QVERIFY(QFile::exists(mainCpp.toLocalFile()));
0105     const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
0106     QCOMPARE(items.size(), 2); // once the plain file, once the target
0107 
0108     bool foundInTarget = false;
0109     for (ProjectBaseItem* mainCppItem : items) {
0110         ProjectBaseItem* mainContainer = mainCppItem->parent();
0111 
0112         Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem);
0113 
0114         if (mainContainer->target()) {
0115             foundInTarget = true;
0116             Path targetIncludesDir(project->path(), QStringLiteral("includes/"));
0117             QVERIFY(includeDirs.contains(targetIncludesDir));
0118         }
0119     }
0120     QVERIFY(foundInTarget);
0121 }
0122 
0123 void TestCMakeManager::testTargetIncludeDirectories()
0124 {
0125     IProject* project = loadProject(QStringLiteral("target_include_directories"));
0126 
0127     Path mainCpp(project->path(), QStringLiteral("main.cpp"));
0128     QVERIFY(QFile::exists(mainCpp.toLocalFile()));
0129     const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
0130     QCOMPARE(items.size(), 2); // once the plain file, once the target
0131 
0132     bool foundInTarget = false;
0133     for (ProjectBaseItem* mainCppItem : items) {
0134         ProjectBaseItem* mainContainer = mainCppItem->parent();
0135 
0136         Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem);
0137 
0138         if (mainContainer->target()) {
0139             foundInTarget = true;
0140             QVERIFY(includeDirs.contains(Path(project->path(), "includes/")));
0141             QVERIFY(includeDirs.contains(Path(project->path(), "libincludes/")));
0142         }
0143     }
0144     QVERIFY(foundInTarget);
0145 }
0146 
0147 void TestCMakeManager::testQt5App()
0148 {
0149     IProject* project = loadProject(QStringLiteral("qt5_app"));
0150 
0151     Path mainCpp(project->path(), QStringLiteral("main.cpp"));
0152     QVERIFY(QFile::exists(mainCpp.toLocalFile()));
0153     const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
0154     QCOMPARE(items.size(), 2); // once the plain file, once the target
0155 
0156     bool foundCore = false, foundGui = false, foundWidgets = false;
0157     for (ProjectBaseItem* mainCppItem : items) {
0158         const Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem);
0159         for (const Path& include : includeDirs) {
0160             QString filename = include.lastPathSegment();
0161             foundCore |= filename == QLatin1String("QtCore");
0162             foundGui |= filename == QLatin1String("QtGui");
0163             foundWidgets |= filename == QLatin1String("QtWidgets");
0164         }
0165     }
0166     QVERIFY(foundCore);
0167     QVERIFY(foundGui);
0168     QVERIFY(foundWidgets);
0169 }
0170 
0171 void TestCMakeManager::testQt5AppOld()
0172 {
0173     IProject* project = loadProject(QStringLiteral("qt5_app_old"));
0174 
0175     Path mainCpp(project->path(), QStringLiteral("main.cpp"));
0176     QVERIFY(QFile::exists(mainCpp.toLocalFile()));
0177     const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
0178     QCOMPARE(items.size(), 2); // once the plain file, once the target
0179 
0180     bool foundCore = false, foundGui = false, foundWidgets = false;
0181     for (ProjectBaseItem* mainCppItem : items) {
0182         const Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem);
0183         for (const Path& include : includeDirs) {
0184             QString filename = include.lastPathSegment();
0185             foundCore |= filename == QLatin1String("QtCore");
0186             foundGui |= filename == QLatin1String("QtGui");
0187             foundWidgets |= filename == QLatin1String("QtWidgets");
0188         }
0189     }
0190     QVERIFY(foundCore);
0191     QVERIFY(foundGui);
0192     QVERIFY(foundWidgets);
0193 }
0194 
0195 void TestCMakeManager::testKF5App()
0196 {
0197     IProject* project = loadProject(QStringLiteral("kf5_app"));
0198 
0199     Path mainCpp(project->path(), QStringLiteral("main.cpp"));
0200     QVERIFY(QFile::exists(mainCpp.toLocalFile()));
0201     const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
0202     QCOMPARE(items.size(), 2); // once the plain file, once the target
0203 
0204     bool foundCore = false, foundGui = false, foundWidgets = false, foundWidgetsAddons = false;
0205     for (ProjectBaseItem* mainCppItem : items) {
0206         const Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem);
0207         qDebug() << "xxxxxxxxx" << includeDirs;
0208         for (const Path& include : includeDirs) {
0209             QString filename = include.lastPathSegment();
0210             foundCore |= filename == QLatin1String("QtCore");
0211             foundGui |= filename == QLatin1String("QtGui");
0212             foundWidgets |= filename == QLatin1String("QtWidgets");
0213             foundWidgetsAddons |= filename == QLatin1String("KWidgetsAddons");
0214         }
0215     }
0216     QVERIFY(foundCore);
0217     QVERIFY(foundGui);
0218     QVERIFY(foundWidgets);
0219     QVERIFY(foundWidgetsAddons);
0220 }
0221 
0222 void TestCMakeManager::testDefines()
0223 {
0224     IProject* project = loadProject(QStringLiteral("defines"));
0225 
0226     Path mainCpp(project->path(), QStringLiteral("main.cpp"));
0227     QVERIFY(QFile::exists(mainCpp.toLocalFile()));
0228     const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
0229     QCOMPARE(items.size(), 2); // once the plain file, once the target
0230 
0231     bool foundInTarget = false;
0232     for (ProjectBaseItem* mainCppItem : items) {
0233         QHash<QString, QString> defines = project->buildSystemManager()->defines(mainCppItem);
0234 
0235         QCOMPARE(defines.value("B", QStringLiteral("not found")), QString());
0236         QCOMPARE(defines.value("BV", QStringLiteral("not found")), QStringLiteral("1"));
0237         QCOMPARE(defines.value("BV2", QStringLiteral("not found")), QStringLiteral("2"));
0238 
0239 //         QCOMPARE(defines.value("BAR", QStringLiteral("not found")), QStringLiteral("foo"));
0240 //         QCOMPARE(defines.value("FOO", QStringLiteral("not found")), QStringLiteral("bar"));
0241 //         QCOMPARE(defines.value("BLA", QStringLiteral("not found")), QStringLiteral("blub"));
0242         QCOMPARE(defines.value("ASDF", QStringLiteral("not found")), QStringLiteral("asdf"));
0243         QCOMPARE(defines.value("XYZ", QStringLiteral("not found")), QString());
0244         QCOMPARE(defines.value("A", QStringLiteral("not found")), QString());
0245         QCOMPARE(defines.value("AV", QStringLiteral("not found")), QStringLiteral("1"));
0246         QCOMPARE(defines.value("AV2", QStringLiteral("not found")), QStringLiteral("2"));
0247         QCOMPARE(defines.value("C", QStringLiteral("not found")), QString());
0248         QCOMPARE(defines.value("CV", QStringLiteral("not found")), QStringLiteral("1"));
0249         QCOMPARE(defines.value("CV2", QStringLiteral("not found")), QStringLiteral("2"));
0250         QCOMPARE(defines.size(), 13);
0251         foundInTarget = true;
0252     }
0253     QVERIFY(foundInTarget);
0254 }
0255 
0256 void TestCMakeManager::testCustomTargetSources()
0257 {
0258     IProject* project = loadProject(QStringLiteral("custom_target_sources"));
0259 
0260     QList<ProjectTargetItem*> targets = project->buildSystemManager()->targets(project->projectItem());
0261     QVERIFY(targets.size() == 1);
0262 
0263     ProjectTargetItem *target = targets.first();
0264     QCOMPARE(target->fileList().size(), 1);
0265     QCOMPARE(target->fileList().first()->baseName(), QStringLiteral("foo.cpp"));
0266 }
0267 
0268 void TestCMakeManager::testConditionsInSubdirectoryBasedOnRootVariables()
0269 {
0270     IProject* project = loadProject(QStringLiteral("conditions_in_subdirectory_based_on_root_variables"));
0271 
0272     Path rootFooCpp(project->path(), QStringLiteral("foo.cpp"));
0273     QVERIFY(QFile::exists(rootFooCpp.toLocalFile()));
0274     QList< ProjectBaseItem* > rootFooItems = project->itemsForPath(IndexedString(rootFooCpp.pathOrUrl()));
0275     QCOMPARE(rootFooItems.size(), 4); // three items for the targets, one item for the plain file
0276 
0277     Path subdirectoryFooCpp(project->path(), QStringLiteral("subdirectory/foo.cpp"));
0278     QVERIFY(QFile::exists(subdirectoryFooCpp.toLocalFile()));
0279     QList< ProjectBaseItem* > subdirectoryFooItems = project->itemsForPath(IndexedString(subdirectoryFooCpp.pathOrUrl()));
0280 
0281     QCOMPARE(subdirectoryFooItems.size(), 4); // three items for the targets, one item for the plain file
0282 }
0283 
0284 void TestCMakeManager::testEnumerateTargets()
0285 {
0286     QString tempDir = QDir::tempPath();
0287 
0288     QTemporaryFile targetDirectoriesFile;
0289     QTemporaryDir subdir;
0290 
0291     auto opened = targetDirectoriesFile.open();
0292     QVERIFY(opened);
0293     QVERIFY(subdir.isValid());
0294 
0295     const QString targetDirectoriesContent = tempDir + "/CMakeFiles/first_target.dir\n" +
0296                                              tempDir + "/CMakeFiles/second_target.dir\r\n" +
0297                                              tempDir + "/" + subdir.path() + "/CMakeFiles/third_target.dir";
0298 
0299     targetDirectoriesFile.write(targetDirectoriesContent.toLatin1());
0300     targetDirectoriesFile.close();
0301 
0302     QHash<KDevelop::Path, QStringList> targets = 
0303         CMake::enumerateTargets(Path(targetDirectoriesFile.fileName()),
0304             tempDir, Path(tempDir));
0305 
0306     QCOMPARE(targets.value(Path(tempDir)).value(0), QStringLiteral("first_target"));
0307     QCOMPARE(targets.value(Path(tempDir)).value(1), QStringLiteral("second_target"));
0308     QCOMPARE(targets.value(Path(tempDir + "/" + subdir.path())).value(0), QStringLiteral("third_target"));
0309 }
0310 
0311 void TestCMakeManager::testReload()
0312 {
0313     IProject* project = loadProject(QStringLiteral("tiny_project"));
0314     const Path sourceDir = project->path();
0315 
0316     auto fmp = dynamic_cast<AbstractFileManagerPlugin*>(project->projectFileManager());
0317     QVERIFY(fmp);
0318 
0319     auto projectItem = project->projectItem();
0320 
0321     auto targets = projectItem->targetList();
0322     QCOMPARE(targets.size(), 1);
0323     auto target = dynamic_cast<CMakeTargetItem*>(targets.first());
0324     QVERIFY(target);
0325     QCOMPARE(target->text(), QStringLiteral("foo"));
0326 
0327     auto job = fmp->createImportJob(project->projectItem());
0328     project->setReloadJob(job);
0329 
0330     QSignalSpy spy(job, &KJob::finished);
0331     job->start();
0332     QVERIFY(spy.wait());
0333     QCOMPARE(spy.count(), 1);
0334 
0335     QCOMPARE(projectItem, project->projectItem());
0336     targets = projectItem->targetList();
0337     QCOMPARE(targets.size(), 1);
0338     target = dynamic_cast<CMakeTargetItem*>(targets.first());
0339     QVERIFY(target);
0340     QCOMPARE(target->text(), QStringLiteral("foo"));
0341 }
0342 
0343 void TestCMakeManager::testFaultyTarget()
0344 {
0345     loadProject(QStringLiteral("faulty_target"));
0346 }
0347 
0348 void TestCMakeManager::testParenthesesInTestArguments()
0349 {
0350     IProject* project = loadProject(QStringLiteral("parentheses_in_test_arguments"));
0351 
0352     auto job = new CMakeImportJsonJob(project, this);
0353     QVERIFY(job->exec());
0354 }
0355 
0356 void TestCMakeManager::testExecutableOutputPath()
0357 {
0358     const auto prevSuitesCount = ICore::self()->testController()->testSuites().count();
0359     qRegisterMetaType<KDevelop::ITestSuite*>("KDevelop::ITestSuite*");
0360     QSignalSpy spy(ICore::self()->testController(), &ITestController::testSuiteAdded);
0361 
0362     IProject* project = loadProject(QStringLiteral("randomexe"));
0363     const auto targets = project->projectItem()->targetList();
0364     QCOMPARE(targets.count(), 1);
0365 
0366     const auto target = targets.first()->executable();
0367     QVERIFY(target);
0368     const KDevelop::Path exePath(target->executable()->builtUrl());
0369     QString executableSuffix;
0370 #ifdef Q_OS_WIN
0371     executableSuffix = ".exe";
0372 #endif
0373     QCOMPARE(exePath,
0374              Path(project->buildSystemManager()->buildDirectory(project->projectItem()),
0375                   "randomplace/mytest" + executableSuffix));
0376 
0377     QVERIFY(spy.count() || spy.wait(100000));
0378 
0379     auto suites = ICore::self()->testController()->testSuites();
0380     QCOMPARE(suites.count(), prevSuitesCount + 1);
0381     const CTestSuite* suite = static_cast<CTestSuite*>(ICore::self()->testController()->findTestSuite(project, "mytest"));
0382     QCOMPARE(suite->executable(), exePath);
0383 }
0384 
0385 #include "moc_test_cmakemanager.cpp"