1 /*
2     SPDX-FileCopyrightText: 2008 Manuel Breugelmans <mbr.nxi@gmail.com>
3 
4     SPDX-License-Identifier: LGPL-2.0-or-later
5 */
6 
7 #include "test_projectcontroller.h"
8 
9 #include <QFile>
10 #include <QSignalSpy>
11 #include <QTest>
12 
13 #include <KAboutData>
14 
15 #include <tests/testhelpers.h>
16 #include <tests/autotestshell.h>
17 #include <tests/testcore.h>
18 
19 #include <interfaces/iplugin.h>
20 #include <project/interfaces/iprojectfilemanager.h>
21 #include <project/projectmodel.h>
22 #include <shell/core.h>
23 #include <shell/projectcontroller.h>
24 #include <shell/plugincontroller.h>
25 #include <shell/project.h>
26 
27 using namespace KDevelop;
28 
29 namespace {
30 
31 class DialogProviderFake : public IProjectDialogProvider
32 {
33 Q_OBJECT
34 public:
DialogProviderFake()35     DialogProviderFake()
36     {}
~DialogProviderFake()37     ~DialogProviderFake() override {}
38     bool m_reopen = true;
39 
40 public Q_SLOTS:
askProjectConfigLocation(bool,const QUrl &,const QUrl &,IPlugin *)41     QUrl askProjectConfigLocation(bool /*fetch*/, const QUrl& /*startUrl*/,
42                                   const QUrl& /*repoUrl*/, IPlugin* /*plugin*/) override
43     { return QUrl(); }
userWantsReopen()44     bool userWantsReopen() override { return m_reopen; }
45 };
46 
47 }
48 
49 /*! A Filemanager plugin that allows you to setup a file & directory structure */
50 class FakeFileManager : public IPlugin, public IProjectFileManager
51 {
52     Q_OBJECT
53     Q_INTERFACES(KDevelop::IProjectFileManager)
54 public:
FakeFileManager(QObject *,const QVariantList &)55     FakeFileManager(QObject*, const QVariantList&)
56         : IPlugin(QStringLiteral("FakeFileManager"), Core::self())
57     {
58     }
59 
FakeFileManager()60     FakeFileManager()
61         : IPlugin(QStringLiteral("FakeFileManager"), Core::self())
62     {
63     }
64 
~FakeFileManager()65     ~FakeFileManager() override {}
66 
features() const67     Features features() const override
68     {
69         return IProjectFileManager::Files | IProjectFileManager::Folders;
70     }
71 
72     QMap<Path, Path::List> m_filesInFolder; // initialize
73     QMap<Path, Path::List> m_subFoldersInFolder;
74 
75     /*! Setup this manager such that @p folder contains @p file */
addFileToFolder(const Path & folder,const Path & file)76     void addFileToFolder(const Path& folder, const Path& file)
77     {
78         if (!m_filesInFolder.contains(folder)) {
79             m_filesInFolder[folder] = Path::List();
80         }
81         m_filesInFolder[folder] << file;
82     }
83 
84     /*! Setup this manager such that @p folder has @p subFolder */
addSubFolderTo(const Path & folder,const Path & subFolder)85     void addSubFolderTo(const Path& folder, const Path& subFolder)
86     {
87         if (!m_subFoldersInFolder.contains(folder)) {
88             m_subFoldersInFolder[folder] = Path::List();
89         }
90         m_subFoldersInFolder[folder] << subFolder;
91     }
92 
parse(ProjectFolderItem * dom)93     QList<ProjectFolderItem*> parse(ProjectFolderItem *dom) override
94     {
95         const Path::List files = m_filesInFolder[dom->path()];
96         for (const Path& file : files) {
97             new ProjectFileItem(dom->project(), file, dom);
98         }
99         const Path::List folderPaths = m_subFoldersInFolder[dom->path()];
100         QList<ProjectFolderItem*> folders;
101         for (const Path& folderPath : folderPaths) {
102             folders << new ProjectFolderItem(dom->project(), folderPath, dom);
103         }
104         return folders;
105     }
106 
import(IProject * project)107     ProjectFolderItem *import(IProject *project) override
108     {
109         auto* it = new ProjectFolderItem(project, project->path());
110         return it;
111     }
112 
addFolder(const Path &,ProjectFolderItem *)113     ProjectFolderItem* addFolder(const Path& /*folder*/, ProjectFolderItem* /*parent*/) override { return nullptr; }
addFile(const Path &,ProjectFolderItem *)114     ProjectFileItem* addFile(const Path& /*file*/, ProjectFolderItem* /*parent*/) override { return nullptr; }
removeFilesAndFolders(const QList<ProjectBaseItem * > &)115     bool removeFilesAndFolders(const QList<ProjectBaseItem*> &/*items*/) override { return false; }
moveFilesAndFolders(const QList<KDevelop::ProjectBaseItem * > &,KDevelop::ProjectFolderItem *)116     bool moveFilesAndFolders(const QList< KDevelop::ProjectBaseItem* > &/*items*/, KDevelop::ProjectFolderItem* /*newParent*/) override { return false; }
copyFilesAndFolders(const Path::List &,KDevelop::ProjectFolderItem *)117     bool copyFilesAndFolders(const Path::List &/*items*/, KDevelop::ProjectFolderItem* /*newParent*/) override { return false; }
renameFile(ProjectFileItem *,const Path &)118     bool renameFile(ProjectFileItem* /*file*/, const Path& /*newPath*/) override { return false; }
renameFolder(ProjectFolderItem *,const Path &)119     bool renameFolder(ProjectFolderItem* /*oldFolder*/, const Path& /*newPath*/ ) override { return false; }
reload(ProjectFolderItem *)120     bool reload(ProjectFolderItem* /*item*/) override { return false; }
121 };
122 
123 class FakePluginController : public PluginController
124 {
125     Q_OBJECT
126 public:
127     using PluginController::PluginController;
128 
pluginForExtension(const QString & extension,const QString & pluginName={},const QVariantMap & constraints=QVariantMap ())129     IPlugin* pluginForExtension(const QString& extension, const QString& pluginName = {}, const QVariantMap& constraints = QVariantMap()) override
130     {
131         if (extension == qobject_interface_iid<IProjectFileManager*>()) {
132             if (!m_fakeFileManager) {
133                 // Can't initialize in the constructor, because the pluginController must be setup
134                 //  before constructing a plugin, and this _is_ the pluginController.
135                 m_fakeFileManager = new FakeFileManager;
136             }
137             return m_fakeFileManager;
138         }
139         return PluginController::pluginForExtension(extension, pluginName, constraints);
140     }
141 
142 private:
143     FakeFileManager* m_fakeFileManager = nullptr;
144 };
145 
146 ////////////////////// Fixture ///////////////////////////////////////////////
147 
initTestCase()148 void TestProjectController::initTestCase()
149 {
150     AutoTestShell::init({{}});
151     auto* testCore = new TestCore;
152     testCore->setPluginController( new FakePluginController(testCore) );
153     testCore->initialize();
154     qRegisterMetaType<KDevelop::IProject*>();
155     m_core = Core::self();
156     m_scratchDir = QDir(QDir::tempPath());
157     m_scratchDir.mkdir(QStringLiteral("prjctrltest"));
158     m_scratchDir.cd(QStringLiteral("prjctrltest"));
159 
160     QSignalSpy projectControllerInitializedSpy(m_core->projectControllerInternal(),
161                                                &ProjectController::initialized);
162     QVERIFY(projectControllerInitializedSpy.wait(100));
163 }
164 
cleanupTestCase()165 void TestProjectController::cleanupTestCase()
166 {
167     TestCore::shutdown();
168 }
169 
init()170 void TestProjectController::init()
171 {
172     m_projName = QStringLiteral("foo");
173     m_projFilePath = writeProjectConfig(m_projName);
174     m_projCtrl = m_core->projectControllerInternal();
175     m_tmpConfigs << m_projFilePath;
176     m_projFolder = Path(m_scratchDir.absolutePath() + '/');
177 }
178 
cleanup()179 void TestProjectController::cleanup()
180 {
181     // also close any opened projects as we do not get a clean fixture,
182     // following tests should start off clean.
183     const auto projects = m_projCtrl->projects();
184     for (IProject* p : projects) {
185         m_projCtrl->closeProject(p);
186     }
187     for (const Path& cfg : qAsConst(m_tmpConfigs)) {
188         QFile::remove(cfg.pathOrUrl());
189     }
190     qDeleteAll(m_fileManagerGarbage);
191     m_fileManagerGarbage.clear();
192 }
193 
194 ////////////////////// Commands //////////////////////////////////////////////
195 
196 #define WAIT_FOR_OPEN_SIGNAL \
197 {\
198     QSignalSpy signal(m_projCtrl, SIGNAL(projectOpened(KDevelop::IProject*)));\
199     QVERIFY2(signal.wait(30000), "Timeout while waiting for opened signal");\
200 } void(0)
201 
openProject()202 void TestProjectController::openProject()
203 {
204     auto spy = createOpenedSpy();
205     QVERIFY(!m_projCtrl->isProjectNameUsed(m_projName));
206     m_projCtrl->openProject(m_projFilePath.toUrl());
207     WAIT_FOR_OPEN_SIGNAL;
208     QCOMPARE(m_projCtrl->projectCount(), 1);
209     auto* proj = assertProjectOpened(m_projName);
210     assertSpyCaughtProject(spy.get(), proj);
211     QCOMPARE(proj->projectFile(), m_projFilePath);
212     QCOMPARE(proj->path(), Path(m_scratchDir.absolutePath()+'/'));
213     QVERIFY(m_projCtrl->isProjectNameUsed(m_projName));
214 }
215 
closeProject()216 void TestProjectController::closeProject()
217 {
218     m_projCtrl->openProject(m_projFilePath.toUrl());
219     WAIT_FOR_OPEN_SIGNAL;
220     IProject* proj = m_projCtrl->findProjectByName(m_projName);
221     Q_ASSERT(proj);
222 
223     auto spy1 = createClosedSpy();
224     auto spy2 = createClosingSpy();
225     m_projCtrl->closeProject(proj);
226 
227     QVERIFY(!m_projCtrl->isProjectNameUsed(m_projName));
228     QCOMPARE(m_projCtrl->projectCount(), 0);
229     assertProjectClosed(proj);
230     assertSpyCaughtProject(spy1.get(), proj);
231     assertSpyCaughtProject(spy2.get(), proj);
232 }
233 
openCloseOpen()234 void TestProjectController::openCloseOpen()
235 {
236     m_projCtrl->openProject(m_projFilePath.toUrl());
237     WAIT_FOR_OPEN_SIGNAL;
238     auto* proj = assertProjectOpened(m_projName);
239     m_projCtrl->closeProject(proj);
240     auto spy = createOpenedSpy();
241     m_projCtrl->openProject(m_projFilePath.toUrl());
242     WAIT_FOR_OPEN_SIGNAL;
243     QVERIFY(m_projCtrl->isProjectNameUsed(m_projName));
244     QCOMPARE(m_projCtrl->projectCount(), 1);
245     proj = assertProjectOpened(m_projName);
246     assertSpyCaughtProject(spy.get(), proj);
247 }
248 
reopen()249 void TestProjectController::reopen()
250 {
251     m_projCtrl->setDialogProvider(new DialogProviderFake);
252     m_projCtrl->openProject(m_projFilePath.toUrl());
253     WAIT_FOR_OPEN_SIGNAL;
254     auto spy = createOpenedSpy();
255     m_projCtrl->openProject(m_projFilePath.toUrl());
256     WAIT_FOR_OPEN_SIGNAL;
257     QCOMPARE(m_projCtrl->projectCount(), 1);
258     QVERIFY(m_projCtrl->isProjectNameUsed(m_projName));
259     auto* proj = assertProjectOpened(m_projName);
260     assertSpyCaughtProject(spy.get(), proj);
261 }
262 
reopenWhileLoading()263 void TestProjectController::reopenWhileLoading()
264 {
265     // Open the same project again while the first is still
266     // loading. The second open request should be blocked.
267     m_projCtrl->setDialogProvider(new DialogProviderFake);
268     auto spy = createOpenedSpy();
269     m_projCtrl->openProject(m_projFilePath.toUrl());
270     //m_projCtrl->openProject(m_projFilePath.toUrl());
271     WAIT_FOR_OPEN_SIGNAL;
272     // wait a bit for a second signal, this should timeout
273     QSignalSpy signal(m_projCtrl, SIGNAL(projectOpened(KDevelop::IProject*)));
274     QVERIFY2(!signal.wait(100), "Received 2 projectOpened signals.");
275     QCOMPARE(m_projCtrl->projectCount(), 1);
276     auto* proj = assertProjectOpened(m_projName);
277     assertSpyCaughtProject(spy.get(), proj);
278 }
279 
openMultiple()280 void TestProjectController::openMultiple()
281 {
282     QString secondProj(QStringLiteral("bar"));
283     Path secondCfgUrl = writeProjectConfig(secondProj);
284     auto spy = createOpenedSpy();
285     m_projCtrl->openProject(m_projFilePath.toUrl());
286     WAIT_FOR_OPEN_SIGNAL;
287     m_projCtrl->openProject(secondCfgUrl.toUrl());
288     WAIT_FOR_OPEN_SIGNAL;
289 
290     QCOMPARE(m_projCtrl->projectCount(), 2);
291     auto* proj1 = assertProjectOpened(m_projName);
292     auto* proj2 = assertProjectOpened(secondProj);
293 
294     QVERIFY(m_projCtrl->isProjectNameUsed(m_projName));
295     QVERIFY(m_projCtrl->isProjectNameUsed(QStringLiteral("bar")));
296 
297     QCOMPARE(spy->size(), 2);
298     auto* emittedProj1 = (*spy)[0][0].value<IProject*>();
299     auto* emittedProj2 = (*spy)[1][0].value<IProject*>();
300     QCOMPARE(emittedProj1, proj1);
301     QCOMPARE(emittedProj2, proj2);
302 
303     m_tmpConfigs << secondCfgUrl;
304 }
305 
306 /*! Verify that the projectmodel contains a single project. Put this project's
307  *  ProjectFolderItem in the output parameter @p RootItem */
308 #define ASSERT_SINGLE_PROJECT_IN_MODEL(rootItem) \
309 {\
310     QCOMPARE(m_projCtrl->projectModel()->rowCount(), 1); \
311     QModelIndex projIndex = m_projCtrl->projectModel()->index(0,0); \
312     QVERIFY(projIndex.isValid()); \
313     ProjectBaseItem* i = m_projCtrl->projectModel()->itemFromIndex( projIndex ); \
314     QVERIFY(i); \
315     QVERIFY(i->folder()); \
316     rootItem = i->folder();\
317 } void(0)
318 
319 /*! Verify that the projectitem @p item has a single child item
320  *  named @p name with url @p url. @p subFolder is an output parameter
321  *  that contains the sub-folder projectitem. */
322 #define ASSERT_SINGLE_SUBFOLDER_IN(item, name, path__, subFolder) \
323 {\
324     QCOMPARE(item->rowCount(), 1);\
325     QCOMPARE(item->folderList().size(), 1);\
326     ProjectFolderItem* fo = item->folderList().at(0);\
327     QVERIFY(fo);\
328     QCOMPARE(fo->path(), path__);\
329     QCOMPARE(fo->folderName(), QStringLiteral(name));\
330     subFolder = fo;\
331 } void(0)
332 
333 #define ASSERT_SINGLE_FILE_IN(rootFolder, name, path__, fileItem)\
334 {\
335     QCOMPARE(rootFolder->rowCount(), 1);\
336     QCOMPARE(rootFolder->fileList().size(), 1);\
337     fileItem = rootFolder->fileList().at(0);\
338     QVERIFY(fileItem);\
339     QCOMPARE(fileItem->path(), path__);\
340     QCOMPARE(fileItem->fileName(), QStringLiteral(name));\
341 } void(0)
342 
343 // command
emptyProject()344 void TestProjectController::emptyProject()
345 {
346     // verify that the project model contains a single top-level folder after loading
347     // an empty project
348 
349     assertEmptyProjectModel();
350 
351     m_projCtrl->openProject(m_projFilePath.toUrl());
352     WAIT_FOR_OPEN_SIGNAL;
353     auto* proj = assertProjectOpened(m_projName);
354 
355     FakeFileManager* fileMng = createFileManager();
356     Q_ASSERT(fileMng);
357 
358     proj->setManagerPlugin(fileMng);
359     proj->reloadModel();
360     QTest::qWait(100);
361 
362     ProjectFolderItem* rootFolder;
363     ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder);
364 
365     // check that the project is empty
366     QCOMPARE(rootFolder->rowCount(), 0);
367     QCOMPARE(rootFolder->project()->name(), m_projName);
368     QCOMPARE(rootFolder->path(), m_projFolder);
369 }
370 
371 // command
singleFile()372 void TestProjectController::singleFile()
373 {
374     // verify that the project model contains a single file in the
375     // top folder. First setup a FakeFileManager with this file
376 
377     m_projCtrl->openProject(m_projFilePath.toUrl());
378     WAIT_FOR_OPEN_SIGNAL;
379     auto* proj = assertProjectOpened(m_projName);
380 
381     FakeFileManager* fileMng = createFileManager();
382     proj->setManagerPlugin(fileMng);
383 
384     Path filePath = Path(m_projFolder, QStringLiteral("foobar"));
385     fileMng->addFileToFolder(m_projFolder, filePath);
386 
387     proj->reloadModel();
388     QTest::qWait(100); // NO signals for reload ...
389 
390     ProjectFolderItem* rootFolder;
391     ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder);
392     ProjectFileItem* fi;
393     ASSERT_SINGLE_FILE_IN(rootFolder, "foobar", filePath, fi);
394     QCOMPARE(fi->rowCount(), 0);
395 
396     ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder);
397     ASSERT_SINGLE_FILE_IN(rootFolder, "foobar", filePath, fi);
398 }
399 
400 // command
singleDirectory()401 void TestProjectController::singleDirectory()
402 {
403     // verify that the project model contains a single folder in the
404     // top folder. First setup a FakeFileManager with this folder
405 
406     m_projCtrl->openProject(m_projFilePath.toUrl());
407     WAIT_FOR_OPEN_SIGNAL;
408     auto* proj = assertProjectOpened(m_projName);
409 
410     Path folderPath = Path(m_projFolder, QStringLiteral("foobar/"));
411     FakeFileManager* fileMng = createFileManager();
412     fileMng->addSubFolderTo(m_projFolder, folderPath);
413 
414     proj->setManagerPlugin(fileMng);
415     proj->reloadModel();
416     QTest::qWait(100);
417 
418     ProjectFolderItem* rootFolder;
419     ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder);
420 
421     // check that the project contains a single subfolder
422     ProjectFolderItem* sub;
423     ASSERT_SINGLE_SUBFOLDER_IN(rootFolder, "foobar", folderPath, sub);
424     QCOMPARE(sub->rowCount(), 0);
425 }
426 
427 // command
fileInSubdirectory()428 void TestProjectController::fileInSubdirectory()
429 {
430     // verify that the project model contains a single file in a subfolder
431     // First setup a FakeFileManager with this folder + file
432 
433     m_projCtrl->openProject(m_projFilePath.toUrl());
434     WAIT_FOR_OPEN_SIGNAL;
435     auto* proj = assertProjectOpened(m_projName);
436 
437     Path folderPath = Path(m_projFolder, QStringLiteral("foobar/"));
438     FakeFileManager* fileMng = createFileManager();
439     fileMng->addSubFolderTo(m_projFolder, folderPath);
440     Path filePath = Path(folderPath, QStringLiteral("zoo"));
441     fileMng->addFileToFolder(folderPath, filePath);
442 
443     proj->setManagerPlugin(fileMng);
444     ProjectFolderItem* rootFolder = nullptr;
445     ProjectFolderItem* sub = nullptr;
446     ProjectFileItem* file = nullptr;
447 
448     proj->reloadModel();
449     QTest::qWait(100);
450 
451     ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder);
452     ASSERT_SINGLE_SUBFOLDER_IN(rootFolder, "foobar", folderPath, sub);
453     ASSERT_SINGLE_FILE_IN(sub,"zoo",filePath,file);
454 
455     ASSERT_SINGLE_PROJECT_IN_MODEL(rootFolder);
456     ASSERT_SINGLE_SUBFOLDER_IN(rootFolder, "foobar", folderPath, sub);
457     ASSERT_SINGLE_FILE_IN(sub,"zoo",filePath,file);
458 }
459 
prettyFileName_data()460 void TestProjectController::prettyFileName_data()
461 {
462     QTest::addColumn<QString>("relativeFilePath");
463 
464     QTest::newRow("basic")
465         << "foobar.txt";
466     QTest::newRow("subfolder")
467         << "sub/foobar.txt";
468 }
469 
prettyFileName()470 void TestProjectController::prettyFileName()
471 {
472     QFETCH(QString, relativeFilePath);
473 
474     m_projCtrl->openProject(m_projFilePath.toUrl());
475     WAIT_FOR_OPEN_SIGNAL;
476     auto* proj = assertProjectOpened(m_projName);
477 
478     FakeFileManager* fileMng = createFileManager();
479     proj->setManagerPlugin(fileMng);
480 
481     Path filePath = Path(m_projFolder, relativeFilePath);
482     fileMng->addFileToFolder(m_projFolder, filePath);
483 
484     QCOMPARE(m_projCtrl->prettyFileName(filePath.toUrl(), ProjectController::FormattingOptions::FormatPlain), QString(m_projName + ':' + relativeFilePath));
485 }
486 
487 ////////////////////// Helpers ///////////////////////////////////////////////
488 
writeProjectConfig(const QString & name)489 Path TestProjectController::writeProjectConfig(const QString& name)
490 {
491     Path configPath = Path(m_scratchDir.absolutePath() + '/' + name + ".kdev4");
492     QFile f(configPath.pathOrUrl());
493     f.open(QIODevice::WriteOnly);
494     QTextStream str(&f);
495     str << "[Project]\n"
496         << "Name=" << name << "\n";
497     f.close();
498     return configPath;
499 }
500 
501 ////////////////// Custom assertions /////////////////////////////////////////
502 
assertProjectOpened(const QString & name)503 KDevelop::Project* TestProjectController::assertProjectOpened(const QString& name)
504 {
505     auto* projRaw = m_projCtrl->findProjectByName(name);
506     QVERIFY_RETURN(projRaw, nullptr);
507     QVERIFY_RETURN(m_projCtrl->projects().contains(projRaw), nullptr);
508 
509     auto proj = dynamic_cast<KDevelop::Project*>(projRaw);
510     QVERIFY_RETURN(projRaw, nullptr);
511     return proj;
512 }
513 
assertSpyCaughtProject(QSignalSpy * spy,IProject * proj)514 void TestProjectController::assertSpyCaughtProject(QSignalSpy* spy, IProject* proj)
515 {
516     QCOMPARE(spy->size(), 1);
517     auto* emittedProj = (*spy)[0][0].value<IProject*>();
518     QCOMPARE(proj, emittedProj);
519 }
520 
assertProjectClosed(IProject * proj)521 void TestProjectController::assertProjectClosed(IProject* proj)
522 {
523     IProject* p = m_projCtrl->findProjectByName(proj->name());
524     QVERIFY(p == nullptr);
525     QVERIFY(!m_projCtrl->projects().contains(proj));
526 }
527 
assertEmptyProjectModel()528 void TestProjectController::assertEmptyProjectModel()
529 {
530     ProjectModel* m = m_projCtrl->projectModel();
531     Q_ASSERT(m);
532     QCOMPARE(m->rowCount(), 0);
533 }
534 
535 ///////////////////// Creation stuff /////////////////////////////////////////
536 
createOpenedSpy()537 std::unique_ptr<QSignalSpy> TestProjectController::createOpenedSpy()
538 {
539     return std::make_unique<QSignalSpy>(m_projCtrl, SIGNAL(projectOpened(KDevelop::IProject*)));
540 }
541 
createClosedSpy()542 std::unique_ptr<QSignalSpy> TestProjectController::createClosedSpy()
543 {
544     return std::make_unique<QSignalSpy>(m_projCtrl, SIGNAL(projectClosed(KDevelop::IProject*)));
545 }
546 
createClosingSpy()547 std::unique_ptr<QSignalSpy> TestProjectController::createClosingSpy()
548 {
549     return std::make_unique<QSignalSpy>(m_projCtrl, SIGNAL(projectClosing(KDevelop::IProject*)));
550 }
551 
createFileManager()552 FakeFileManager* TestProjectController::createFileManager()
553 {
554     auto* fileMng = new FakeFileManager;
555     m_fileManagerGarbage << fileMng;
556     return fileMng;
557 }
558 
559 QTEST_MAIN(TestProjectController)
560 #include "moc_test_projectcontroller.cpp"
561 #include "test_projectcontroller.moc"
562