1 /*
2 SPDX-FileCopyrightText: 2010 Esben Mose Hansen <kde@mosehansen.dk>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6
7 #include "test_cmakemanager.h"
8 #include "testhelpers.h"
9 #include "cmakemodelitems.h"
10 #include "cmakeutils.h"
11 #include "cmakeimportjsonjob.h"
12
13 #include <QLoggingCategory>
14 #include <QTemporaryFile>
15
16 #include <interfaces/icore.h>
17 #include <interfaces/itestsuite.h>
18 #include <interfaces/itestcontroller.h>
19 #include <project/interfaces/ibuildsystemmanager.h>
20 #include <project/abstractfilemanagerplugin.h>
21 #include <tests/autotestshell.h>
22 #include <tests/testproject.h>
23 #include <tests/testcore.h>
24 #include <testing/ctestsuite.h>
25
26 QTEST_MAIN(TestCMakeManager)
27
28 using namespace KDevelop;
29
initTestCase()30 void TestCMakeManager::initTestCase()
31 {
32 QLoggingCategory::setFilterRules("*.debug=false\nkdevplatform.outputview.debug=true\n"
33 "kdevelop.plugins.cmake.debug=true\ndefault.debug=true\n");
34
35 AutoTestShell::init({"KDevCMakeManager", "KDevCMakeBuilder", "KDevMakeBuilder", "KDevStandardOutputView"});
36 TestCore::initialize();
37
38 cleanup();
39 }
40
cleanupTestCase()41 void TestCMakeManager::cleanupTestCase()
42 {
43 TestCore::shutdown();
44 }
45
cleanup()46 void TestCMakeManager::cleanup()
47 {
48 const auto projects = ICore::self()->projectController()->projects();
49 for (IProject* p : projects) {
50 ICore::self()->projectController()->closeProject(p);
51 }
52 QVERIFY(ICore::self()->projectController()->projects().isEmpty());
53 }
54
testWithBuildDirProject()55 void TestCMakeManager::testWithBuildDirProject()
56 {
57 loadProject(QStringLiteral("with_build_dir"));
58 }
59
testIncludePaths()60 void TestCMakeManager::testIncludePaths()
61 {
62 IProject* project = loadProject(QStringLiteral("single_subdirectory"));
63 Path sourceDir = project->path();
64
65 Path fooCpp(sourceDir, QStringLiteral("subdir/foo.cpp"));
66 QVERIFY(QFile::exists(fooCpp.toLocalFile()));
67 QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(fooCpp.pathOrUrl()));
68 ProjectBaseItem* fooCppItem = items.first();
69
70 Path::List includeDirs = project->buildSystemManager()->includeDirectories(fooCppItem);
71 QVERIFY(includeDirs.size() >= 3);
72
73 Path buildDir(project->buildSystemManager()->buildDirectory(fooCppItem));
74 QVERIFY(includeDirs.contains(buildDir));
75
76 Path subBuildDir(buildDir, QStringLiteral("subdir/"));
77 QVERIFY(includeDirs.contains(subBuildDir));
78
79 Path subDir(sourceDir, QStringLiteral("subdir/"));
80 QVERIFY(includeDirs.contains(subDir));
81 }
82
testRelativePaths()83 void TestCMakeManager::testRelativePaths()
84 {
85 IProject* project = loadProject(QStringLiteral("relative_paths"), QStringLiteral("/out"));
86
87 Path codeCpp(project->path(), QStringLiteral("../src/code.cpp"));
88 QVERIFY(QFile::exists( codeCpp.toLocalFile()));
89 QList< ProjectBaseItem* > items = project->itemsForPath(IndexedString(codeCpp.pathOrUrl()));
90 QCOMPARE(items.size(), 1); // once in the target
91 ProjectBaseItem* fooCppItem = items.first();
92
93 Path::List includeDirs = project->buildSystemManager()->includeDirectories(fooCppItem);
94
95 Path incDir(project->path(), QStringLiteral("../inc/"));
96 QVERIFY(includeDirs.contains( incDir ));
97 }
98
testTargetIncludePaths()99 void TestCMakeManager::testTargetIncludePaths()
100 {
101 IProject* project = loadProject(QStringLiteral("target_includes"));
102
103 Path mainCpp(project->path(), QStringLiteral("main.cpp"));
104 QVERIFY(QFile::exists(mainCpp.toLocalFile()));
105 const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
106 QCOMPARE(items.size(), 2); // once the plain file, once the target
107
108 bool foundInTarget = false;
109 for (ProjectBaseItem* mainCppItem : items) {
110 ProjectBaseItem* mainContainer = mainCppItem->parent();
111
112 Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem);
113
114 if (mainContainer->target()) {
115 foundInTarget = true;
116 Path targetIncludesDir(project->path(), QStringLiteral("includes/"));
117 QVERIFY(includeDirs.contains(targetIncludesDir));
118 }
119 }
120 QVERIFY(foundInTarget);
121 }
122
testTargetIncludeDirectories()123 void TestCMakeManager::testTargetIncludeDirectories()
124 {
125 IProject* project = loadProject(QStringLiteral("target_include_directories"));
126
127 Path mainCpp(project->path(), QStringLiteral("main.cpp"));
128 QVERIFY(QFile::exists(mainCpp.toLocalFile()));
129 const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
130 QCOMPARE(items.size(), 2); // once the plain file, once the target
131
132 bool foundInTarget = false;
133 for (ProjectBaseItem* mainCppItem : items) {
134 ProjectBaseItem* mainContainer = mainCppItem->parent();
135
136 Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem);
137
138 if (mainContainer->target()) {
139 foundInTarget = true;
140 QVERIFY(includeDirs.contains(Path(project->path(), "includes/")));
141 QVERIFY(includeDirs.contains(Path(project->path(), "libincludes/")));
142 }
143 }
144 QVERIFY(foundInTarget);
145 }
146
testQt5App()147 void TestCMakeManager::testQt5App()
148 {
149 IProject* project = loadProject(QStringLiteral("qt5_app"));
150
151 Path mainCpp(project->path(), QStringLiteral("main.cpp"));
152 QVERIFY(QFile::exists(mainCpp.toLocalFile()));
153 const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
154 QCOMPARE(items.size(), 2); // once the plain file, once the target
155
156 bool foundCore = false, foundGui = false, foundWidgets = false;
157 for (ProjectBaseItem* mainCppItem : items) {
158 const Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem);
159 for (const Path& include : includeDirs) {
160 QString filename = include.lastPathSegment();
161 foundCore |= filename == QLatin1String("QtCore");
162 foundGui |= filename == QLatin1String("QtGui");
163 foundWidgets |= filename == QLatin1String("QtWidgets");
164 }
165 }
166 QVERIFY(foundCore);
167 QVERIFY(foundGui);
168 QVERIFY(foundWidgets);
169 }
170
testQt5AppOld()171 void TestCMakeManager::testQt5AppOld()
172 {
173 IProject* project = loadProject(QStringLiteral("qt5_app_old"));
174
175 Path mainCpp(project->path(), QStringLiteral("main.cpp"));
176 QVERIFY(QFile::exists(mainCpp.toLocalFile()));
177 const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
178 QCOMPARE(items.size(), 2); // once the plain file, once the target
179
180 bool foundCore = false, foundGui = false, foundWidgets = false;
181 for (ProjectBaseItem* mainCppItem : items) {
182 const Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem);
183 for (const Path& include : includeDirs) {
184 QString filename = include.lastPathSegment();
185 foundCore |= filename == QLatin1String("QtCore");
186 foundGui |= filename == QLatin1String("QtGui");
187 foundWidgets |= filename == QLatin1String("QtWidgets");
188 }
189 }
190 QVERIFY(foundCore);
191 QVERIFY(foundGui);
192 QVERIFY(foundWidgets);
193 }
194
testKF5App()195 void TestCMakeManager::testKF5App()
196 {
197 IProject* project = loadProject(QStringLiteral("kf5_app"));
198
199 Path mainCpp(project->path(), QStringLiteral("main.cpp"));
200 QVERIFY(QFile::exists(mainCpp.toLocalFile()));
201 const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
202 QCOMPARE(items.size(), 2); // once the plain file, once the target
203
204 bool foundCore = false, foundGui = false, foundWidgets = false, foundWidgetsAddons = false;
205 for (ProjectBaseItem* mainCppItem : items) {
206 const Path::List includeDirs = project->buildSystemManager()->includeDirectories(mainCppItem);
207 qDebug() << "xxxxxxxxx" << includeDirs;
208 for (const Path& include : includeDirs) {
209 QString filename = include.lastPathSegment();
210 foundCore |= filename == QLatin1String("QtCore");
211 foundGui |= filename == QLatin1String("QtGui");
212 foundWidgets |= filename == QLatin1String("QtWidgets");
213 foundWidgetsAddons |= filename == QLatin1String("KWidgetsAddons");
214 }
215 }
216 QVERIFY(foundCore);
217 QVERIFY(foundGui);
218 QVERIFY(foundWidgets);
219 QVERIFY(foundWidgetsAddons);
220 }
221
testDefines()222 void TestCMakeManager::testDefines()
223 {
224 IProject* project = loadProject(QStringLiteral("defines"));
225
226 Path mainCpp(project->path(), QStringLiteral("main.cpp"));
227 QVERIFY(QFile::exists(mainCpp.toLocalFile()));
228 const QList<ProjectBaseItem*> items = project->itemsForPath(IndexedString(mainCpp.pathOrUrl()));
229 QCOMPARE(items.size(), 2); // once the plain file, once the target
230
231 bool foundInTarget = false;
232 for (ProjectBaseItem* mainCppItem : items) {
233 QHash<QString, QString> defines = project->buildSystemManager()->defines(mainCppItem);
234
235 QCOMPARE(defines.value("B", QStringLiteral("not found")), QString());
236 QCOMPARE(defines.value("BV", QStringLiteral("not found")), QStringLiteral("1"));
237 QCOMPARE(defines.value("BV2", QStringLiteral("not found")), QStringLiteral("2"));
238
239 // QCOMPARE(defines.value("BAR", QStringLiteral("not found")), QStringLiteral("foo"));
240 // QCOMPARE(defines.value("FOO", QStringLiteral("not found")), QStringLiteral("bar"));
241 // QCOMPARE(defines.value("BLA", QStringLiteral("not found")), QStringLiteral("blub"));
242 QCOMPARE(defines.value("ASDF", QStringLiteral("not found")), QStringLiteral("asdf"));
243 QCOMPARE(defines.value("XYZ", QStringLiteral("not found")), QString());
244 QCOMPARE(defines.value("A", QStringLiteral("not found")), QString());
245 QCOMPARE(defines.value("AV", QStringLiteral("not found")), QStringLiteral("1"));
246 QCOMPARE(defines.value("AV2", QStringLiteral("not found")), QStringLiteral("2"));
247 QCOMPARE(defines.value("C", QStringLiteral("not found")), QString());
248 QCOMPARE(defines.value("CV", QStringLiteral("not found")), QStringLiteral("1"));
249 QCOMPARE(defines.value("CV2", QStringLiteral("not found")), QStringLiteral("2"));
250 QCOMPARE(defines.size(), 13);
251 foundInTarget = true;
252 }
253 QVERIFY(foundInTarget);
254 }
255
testCustomTargetSources()256 void TestCMakeManager::testCustomTargetSources()
257 {
258 IProject* project = loadProject(QStringLiteral("custom_target_sources"));
259
260 QList<ProjectTargetItem*> targets = project->buildSystemManager()->targets(project->projectItem());
261 QVERIFY(targets.size() == 1);
262
263 ProjectTargetItem *target = targets.first();
264 QCOMPARE(target->fileList().size(), 1);
265 QCOMPARE(target->fileList().first()->baseName(), QStringLiteral("foo.cpp"));
266 }
267
testConditionsInSubdirectoryBasedOnRootVariables()268 void TestCMakeManager::testConditionsInSubdirectoryBasedOnRootVariables()
269 {
270 IProject* project = loadProject(QStringLiteral("conditions_in_subdirectory_based_on_root_variables"));
271
272 Path rootFooCpp(project->path(), QStringLiteral("foo.cpp"));
273 QVERIFY(QFile::exists(rootFooCpp.toLocalFile()));
274 QList< ProjectBaseItem* > rootFooItems = project->itemsForPath(IndexedString(rootFooCpp.pathOrUrl()));
275 QCOMPARE(rootFooItems.size(), 4); // three items for the targets, one item for the plain file
276
277 Path subdirectoryFooCpp(project->path(), QStringLiteral("subdirectory/foo.cpp"));
278 QVERIFY(QFile::exists(subdirectoryFooCpp.toLocalFile()));
279 QList< ProjectBaseItem* > subdirectoryFooItems = project->itemsForPath(IndexedString(subdirectoryFooCpp.pathOrUrl()));
280
281 QCOMPARE(subdirectoryFooItems.size(), 4); // three items for the targets, one item for the plain file
282 }
283
testEnumerateTargets()284 void TestCMakeManager::testEnumerateTargets()
285 {
286 QString tempDir = QDir::tempPath();
287
288 QTemporaryFile targetDirectoriesFile;
289 QTemporaryDir subdir;
290
291 auto opened = targetDirectoriesFile.open();
292 QVERIFY(opened);
293 QVERIFY(subdir.isValid());
294
295 const QString targetDirectoriesContent = tempDir + "/CMakeFiles/first_target.dir\n" +
296 tempDir + "/CMakeFiles/second_target.dir\r\n" +
297 tempDir + "/" + subdir.path() + "/CMakeFiles/third_target.dir";
298
299 targetDirectoriesFile.write(targetDirectoriesContent.toLatin1());
300 targetDirectoriesFile.close();
301
302 QHash<KDevelop::Path, QStringList> targets =
303 CMake::enumerateTargets(Path(targetDirectoriesFile.fileName()),
304 tempDir, Path(tempDir));
305
306 QCOMPARE(targets.value(Path(tempDir)).value(0), QStringLiteral("first_target"));
307 QCOMPARE(targets.value(Path(tempDir)).value(1), QStringLiteral("second_target"));
308 QCOMPARE(targets.value(Path(tempDir + "/" + subdir.path())).value(0), QStringLiteral("third_target"));
309 }
310
testReload()311 void TestCMakeManager::testReload()
312 {
313 IProject* project = loadProject(QStringLiteral("tiny_project"));
314 const Path sourceDir = project->path();
315
316 auto fmp = dynamic_cast<AbstractFileManagerPlugin*>(project->projectFileManager());
317 QVERIFY(fmp);
318
319 auto projectItem = project->projectItem();
320
321 auto targets = projectItem->targetList();
322 QCOMPARE(targets.size(), 1);
323 auto target = dynamic_cast<CMakeTargetItem*>(targets.first());
324 QVERIFY(target);
325 QCOMPARE(target->text(), QStringLiteral("foo"));
326
327 auto job = fmp->createImportJob(project->projectItem());
328 project->setReloadJob(job);
329
330 QSignalSpy spy(job, &KJob::finished);
331 job->start();
332 QVERIFY(spy.wait());
333 QCOMPARE(spy.count(), 1);
334
335 QCOMPARE(projectItem, project->projectItem());
336 targets = projectItem->targetList();
337 QCOMPARE(targets.size(), 1);
338 target = dynamic_cast<CMakeTargetItem*>(targets.first());
339 QVERIFY(target);
340 QCOMPARE(target->text(), QStringLiteral("foo"));
341 }
342
testFaultyTarget()343 void TestCMakeManager::testFaultyTarget()
344 {
345 loadProject(QStringLiteral("faulty_target"));
346 }
347
testParenthesesInTestArguments()348 void TestCMakeManager::testParenthesesInTestArguments()
349 {
350 IProject* project = loadProject(QStringLiteral("parentheses_in_test_arguments"));
351
352 auto job = new CMakeImportJsonJob(project, this);
353 QVERIFY(job->exec());
354 }
355
testExecutableOutputPath()356 void TestCMakeManager::testExecutableOutputPath()
357 {
358 const auto prevSuitesCount = ICore::self()->testController()->testSuites().count();
359 qRegisterMetaType<KDevelop::ITestSuite*>("KDevelop::ITestSuite*");
360 QSignalSpy spy(ICore::self()->testController(), &ITestController::testSuiteAdded);
361
362 IProject* project = loadProject(QStringLiteral("randomexe"));
363 const auto targets = project->projectItem()->targetList();
364 QCOMPARE(targets.count(), 1);
365
366 const auto target = targets.first()->executable();
367 QVERIFY(target);
368 const KDevelop::Path exePath(target->executable()->builtUrl());
369 QCOMPARE(exePath, KDevelop::Path(project->buildSystemManager()->buildDirectory(project->projectItem()), QLatin1String("randomplace/mytest")));
370
371 QVERIFY(spy.count() || spy.wait(100000));
372
373 auto suites = ICore::self()->testController()->testSuites();
374 QCOMPARE(suites.count(), prevSuitesCount + 1);
375 const CTestSuite* suite = static_cast<CTestSuite*>(ICore::self()->testController()->findTestSuite(project, "mytest"));
376 QCOMPARE(suite->executable(), exePath);
377 }
378