diff options
Diffstat (limited to 'src/plugins/cpptools/cppmodelmanager_test.cpp')
-rw-r--r-- | src/plugins/cpptools/cppmodelmanager_test.cpp | 372 |
1 files changed, 311 insertions, 61 deletions
diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp index 4971974a25..8f4f588390 100644 --- a/src/plugins/cpptools/cppmodelmanager_test.cpp +++ b/src/plugins/cpptools/cppmodelmanager_test.cpp @@ -32,74 +32,51 @@ #include "cpppreprocessor.h" #include "modelmanagertesthelper.h" +#include <coreplugin/editormanager/editormanager.h> +#include <coreplugin/testdatadir.h> #include <projectexplorer/projectexplorer.h> #include <projectexplorer/session.h> +#include <utils/hostosinfo.h> #include <QDebug> #include <QFileInfo> #include <QtTest> +#if QT_VERSION >= 0x050000 +#define MSKIP_SINGLE(x) QSKIP(x) +#else +#define MSKIP_SINGLE(x) QSKIP(x, SkipSingle) +#endif + using namespace CppTools::Internal; +using namespace ProjectExplorer; typedef CPlusPlus::Document Document; typedef CppTools::CppModelManagerInterface::ProjectInfo ProjectInfo; typedef CppTools::ProjectPart ProjectPart; typedef CppTools::ProjectFile ProjectFile; -typedef ProjectExplorer::Project Project; + +Q_DECLARE_METATYPE(QList<ProjectFile>) namespace { -class TestDataDirectory +class MyTestDataDir : public Core::Internal::Tests::TestDataDir { public: - TestDataDirectory(const QString &testDataDirectory) - : m_testDataDirectory(QLatin1String(SRCDIR "/../../../tests/cppmodelmanager/") - + testDataDirectory) - { - QFileInfo testDataDir(m_testDataDirectory); - QVERIFY(testDataDir.exists()); - QVERIFY(testDataDir.isDir()); - } - + MyTestDataDir(const QString &dir) + : TestDataDir(QLatin1String(SRCDIR "/../../../tests/cppmodelmanager/") + dir) + {} QString includeDir(bool cleaned = true) const - { - return testDataDir(QLatin1String("include"), cleaned); - } + { return directory(QLatin1String("include"), cleaned); } QString frameworksDir(bool cleaned = true) const - { - return testDataDir(QLatin1String("frameworks"), cleaned); - } + { return directory(QLatin1String("frameworks"), cleaned); } QString fileFromSourcesDir(const QString &fileName) const - { - return testDataDir(QLatin1String("sources")) + fileName; - } - - /// File from the test data directory (top leve) - QString file(const QString &fileName) const - { - return testDataDir(QString()) + fileName; - } - -private: - QString testDataDir(const QString& subdir, bool cleaned = true) const - { - QString path = m_testDataDirectory; - if (!subdir.isEmpty()) - path += QLatin1String("/") + subdir; - if (cleaned) - return CppPreprocessor::cleanPath(path); - else - return path; - } - -private: - const QString m_testDataDirectory; + { return directory(QLatin1String("sources")) + fileName; } }; - // TODO: When possible, use this helper class in all tests class ProjectCreator { @@ -111,7 +88,7 @@ public: /// 'files' is expected to be a list of file names that reside in 'dir'. void create(const QString &name, const QString &dir, const QStringList files) { - const TestDataDirectory projectDir(dir); + const MyTestDataDir projectDir(dir); foreach (const QString &file, files) projectFiles << projectDir.file(file); @@ -142,7 +119,7 @@ class ExampleProjectConfigurator { public: ExampleProjectConfigurator(const QString &projectFile, - ProjectExplorer::ProjectExplorerPlugin *projectExplorer) + ProjectExplorerPlugin *projectExplorer) { const QString projectUserFile = projectFile + QLatin1String(".user"); QVERIFY(!QFileInfo(projectUserFile).exists()); @@ -165,16 +142,62 @@ public: QVERIFY(QFile::remove(m_fileToRemove)); } - ProjectExplorer::Project *project() const + Project *project() const { return m_project; } private: - ProjectExplorer::Project *m_project; + Project *m_project; QString m_fileToRemove; }; +/// Changes a file on the disk and restores its original contents on destruction +class FileChangerAndRestorer +{ +public: + FileChangerAndRestorer(const QString &filePath) + : m_filePath(filePath) + { + } + + ~FileChangerAndRestorer() + { + restoreContents(); + } + + /// Saves the contents also internally so it can be restored on destruction + bool readContents(QByteArray *contents) + { + Utils::FileReader fileReader; + const bool isFetchOk = fileReader.fetch(m_filePath); + if (isFetchOk) { + m_originalFileContents = fileReader.data(); + if (contents) + *contents = m_originalFileContents; + } + return isFetchOk; + } + + void writeContents(const QByteArray &contents) const + { + Utils::FileSaver fileSaver(m_filePath); + fileSaver.write(contents); + fileSaver.finalize(); + } + +private: + void restoreContents() const + { + Utils::FileSaver fileSaver(m_filePath); + fileSaver.write(m_originalFileContents); + fileSaver.finalize(); + } + + QByteArray m_originalFileContents; + const QString &m_filePath; +}; + } // anonymous namespace /// Check: The preprocessor cleans include and framework paths. @@ -183,7 +206,7 @@ void CppToolsPlugin::test_modelmanager_paths_are_clean() ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); - const TestDataDirectory testDataDir(QLatin1String("testdata")); + const MyTestDataDir testDataDir(QLatin1String("testdata")); Project *project = helper.createProject(QLatin1String("test_modelmanager_paths_are_clean")); ProjectInfo pi = mm->projectInfo(project); @@ -211,10 +234,13 @@ void CppToolsPlugin::test_modelmanager_paths_are_clean() /// Check: Frameworks headers are resolved. void CppToolsPlugin::test_modelmanager_framework_headers() { + if (Utils::HostOsInfo::isWindowsHost()) + MSKIP_SINGLE("Can't resolve framework soft links on Windows."); + ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); - const TestDataDirectory testDataDir(QLatin1String("testdata")); + const MyTestDataDir testDataDir(QLatin1String("testdata")); Project *project = helper.createProject(QLatin1String("test_modelmanager_framework_headers")); ProjectInfo pi = mm->projectInfo(project); @@ -231,8 +257,7 @@ void CppToolsPlugin::test_modelmanager_framework_headers() part->files << ProjectFile(source, ProjectFile::CXXSource); pi.appendProjectPart(part); - mm->updateProjectInfo(pi); - mm->updateSourceFiles(QStringList(source)).waitForFinished(); + mm->updateProjectInfo(pi).waitForFinished(); QCoreApplication::processEvents(); QVERIFY(mm->snapshot().contains(source)); @@ -260,7 +285,7 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); - const TestDataDirectory testDataDir(QLatin1String("testdata")); + const MyTestDataDir testDataDir(QLatin1String("testdata")); const QString testCpp(testDataDir.fileFromSourcesDir( QLatin1String("test_modelmanager_refresh.cpp"))); @@ -279,7 +304,6 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() part->includePaths = QStringList() << testDataDir.includeDir(false); part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); pi.appendProjectPart(part); - mm->updateProjectInfo(pi); QStringList refreshedFiles = helper.waitForRefreshedSourceFiles(); @@ -300,6 +324,7 @@ void CppToolsPlugin::test_modelmanager_refresh_also_includes_of_project_files() pi.clearProjectParts(); pi.appendProjectPart(part); mm->updateProjectInfo(pi); + refreshedFiles = helper.waitForRefreshedSourceFiles(); QCOMPARE(refreshedFiles.size(), 1); @@ -323,7 +348,7 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times() ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); - const TestDataDirectory testDataDir(QLatin1String("testdata_refresh")); + const MyTestDataDir testDataDir(QLatin1String("testdata_refresh")); const QString testHeader1(testDataDir.file(QLatin1String("defines.h"))); const QString testHeader2(testDataDir.file(QLatin1String("header.h"))); @@ -341,7 +366,6 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times() part->files.append(ProjectFile(testHeader2, ProjectFile::CXXHeader)); part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); pi.appendProjectPart(part); - mm->updateProjectInfo(pi); CPlusPlus::Snapshot snapshot; @@ -363,6 +387,7 @@ void CppToolsPlugin::test_modelmanager_refresh_several_times() pi.appendProjectPart(part); mm->updateProjectInfo(pi); + refreshedFiles = helper.waitForRefreshedSourceFiles(); QCOMPARE(refreshedFiles.size(), 3); @@ -394,7 +419,7 @@ void CppToolsPlugin::test_modelmanager_refresh_test_for_changes() ModelManagerTestHelper helper; CppModelManager *mm = CppModelManager::instance(); - const TestDataDirectory testDataDir(QLatin1String("testdata_refresh")); + const MyTestDataDir testDataDir(QLatin1String("testdata_refresh")); const QString testCpp(testDataDir.file(QLatin1String("source.cpp"))); Project *project = helper.createProject(QLatin1String("test_modelmanager_refresh_2")); @@ -419,6 +444,165 @@ void CppToolsPlugin::test_modelmanager_refresh_test_for_changes() QVERIFY(subsequentFuture.isCanceled() && subsequentFuture.isFinished()); } +/// Check: (1) Added project files are recognized and parsed. +/// Check: (2) Removed project files are recognized and purged from the snapshot. +void CppToolsPlugin::test_modelmanager_refresh_added_and_purge_removed() +{ + ModelManagerTestHelper helper; + CppModelManager *mm = CppModelManager::instance(); + + const MyTestDataDir testDataDir(QLatin1String("testdata_refresh")); + + const QString testHeader1(testDataDir.file(QLatin1String("header.h"))); + const QString testHeader2(testDataDir.file(QLatin1String("defines.h"))); + const QString testCpp(testDataDir.file(QLatin1String("source.cpp"))); + + Project *project = helper.createProject(QLatin1String("test_modelmanager_refresh_3")); + ProjectInfo pi = mm->projectInfo(project); + QCOMPARE(pi.project().data(), project); + + ProjectPart::Ptr part(new ProjectPart); + part->cxxVersion = ProjectPart::CXX98; + part->qtVersion = ProjectPart::Qt5; + part->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); + part->files.append(ProjectFile(testHeader1, ProjectFile::CXXHeader)); + pi.appendProjectPart(part); + + CPlusPlus::Snapshot snapshot; + QStringList refreshedFiles; + + mm->updateProjectInfo(pi); + refreshedFiles = helper.waitForRefreshedSourceFiles(); + + QCOMPARE(refreshedFiles.size(), 2); + QVERIFY(refreshedFiles.contains(testHeader1)); + QVERIFY(refreshedFiles.contains(testCpp)); + + snapshot = mm->snapshot(); + QVERIFY(snapshot.contains(testHeader1)); + QVERIFY(snapshot.contains(testCpp)); + + // Now add testHeader2 and remove testHeader1 + pi.clearProjectParts(); + ProjectPart::Ptr newPart(new ProjectPart); + newPart->cxxVersion = ProjectPart::CXX98; + newPart->qtVersion = ProjectPart::Qt5; + newPart->files.append(ProjectFile(testCpp, ProjectFile::CXXSource)); + newPart->files.append(ProjectFile(testHeader2, ProjectFile::CXXHeader)); + pi.appendProjectPart(newPart); + + mm->updateProjectInfo(pi); + refreshedFiles = helper.waitForRefreshedSourceFiles(); + + // Only the added project file was reparsed + QCOMPARE(refreshedFiles.size(), 1); + QVERIFY(refreshedFiles.contains(testHeader2)); + + snapshot = mm->snapshot(); + QVERIFY(snapshot.contains(testHeader2)); + QVERIFY(snapshot.contains(testCpp)); + // The removed project file is not anymore in the snapshot + QVERIFY(!snapshot.contains(testHeader1)); +} + +/// Check: Timestamp modified files are reparsed if project files are added or removed +/// while the project configuration stays the same +void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_change() +{ + QFETCH(QString, fileToChange); + QFETCH(QList<ProjectFile>, initialProjectFiles); + QFETCH(QList<ProjectFile>, finalProjectFiles); + + ModelManagerTestHelper helper; + CppModelManager *mm = CppModelManager::instance(); + + Project *project = helper.createProject( + QLatin1String("test_modelmanager_refresh_timeStampModified")); + ProjectInfo pi = mm->projectInfo(project); + QCOMPARE(pi.project().data(), project); + + ProjectPart::Ptr part(new ProjectPart); + part->cxxVersion = ProjectPart::CXX98; + part->qtVersion = ProjectPart::Qt5; + foreach (const ProjectFile &file, initialProjectFiles) + part->files.append(file); + pi.appendProjectPart(part); + + Document::Ptr document; + CPlusPlus::Snapshot snapshot; + QStringList refreshedFiles; + + mm->updateProjectInfo(pi); + refreshedFiles = helper.waitForRefreshedSourceFiles(); + + QCOMPARE(refreshedFiles.size(), initialProjectFiles.size()); + snapshot = mm->snapshot(); + foreach (const ProjectFile &file, initialProjectFiles) { + QVERIFY(refreshedFiles.contains(file.path)); + QVERIFY(snapshot.contains(file.path)); + } + + document = snapshot.document(fileToChange); + const QDateTime lastModifiedBefore = document->lastModified(); + QCOMPARE(document->globalSymbolCount(), 1U); + QCOMPARE(document->globalSymbolAt(0)->name()->identifier()->chars(), "someGlobal"); + + // Modify the file + QTest::qSleep(1000); // Make sure the timestamp is different + FileChangerAndRestorer fileChangerAndRestorer(fileToChange); + QByteArray originalContents; + QVERIFY(fileChangerAndRestorer.readContents(&originalContents)); + const QByteArray newFileContentes = originalContents + "\nint addedOtherGlobal;"; + fileChangerAndRestorer.writeContents(newFileContentes); + + // Add or remove source file. The configuration stays the same. + part->files.clear(); + foreach (const ProjectFile &file, finalProjectFiles) + part->files.append(file); + pi.clearProjectParts(); + pi.appendProjectPart(part); + + mm->updateProjectInfo(pi); + refreshedFiles = helper.waitForRefreshedSourceFiles(); + + QCOMPARE(refreshedFiles.size(), finalProjectFiles.size()); + snapshot = mm->snapshot(); + foreach (const ProjectFile &file, finalProjectFiles) { + QVERIFY(refreshedFiles.contains(file.path)); + QVERIFY(snapshot.contains(file.path)); + } + document = snapshot.document(fileToChange); + const QDateTime lastModifiedAfter = document->lastModified(); + QVERIFY(lastModifiedAfter > lastModifiedBefore); + QCOMPARE(document->globalSymbolCount(), 2U); + QCOMPARE(document->globalSymbolAt(0)->name()->identifier()->chars(), "someGlobal"); + QCOMPARE(document->globalSymbolAt(1)->name()->identifier()->chars(), "addedOtherGlobal"); +} + +void CppToolsPlugin::test_modelmanager_refresh_timeStampModified_if_sourcefiles_change_data() +{ + QTest::addColumn<QString>("fileToChange"); + QTest::addColumn<QList<ProjectFile> >("initialProjectFiles"); + QTest::addColumn<QList<ProjectFile> >("finalProjectFiles"); + + const MyTestDataDir testDataDir(QLatin1String("testdata_refresh2")); + const QString testCpp(testDataDir.file(QLatin1String("source.cpp"))); + const QString testCpp2(testDataDir.file(QLatin1String("source2.cpp"))); + + const QString fileToChange = testCpp; + QList<ProjectFile> projectFiles1 = QList<ProjectFile>() + << ProjectFile(testCpp, ProjectFile::CXXSource); + QList<ProjectFile> projectFiles2 = QList<ProjectFile>() + << ProjectFile(testCpp, ProjectFile::CXXSource) + << ProjectFile(testCpp2, ProjectFile::CXXSource); + + // Add a file + QTest::newRow("case: add project file") << fileToChange << projectFiles1 << projectFiles2; + + // Remove a file + QTest::newRow("case: remove project file") << fileToChange << projectFiles2 << projectFiles1; +} + /// Check: If a second project is opened, the code model is still aware of /// files of the first project. void CppToolsPlugin::test_modelmanager_snapshot_after_two_projects() @@ -471,11 +655,13 @@ void CppToolsPlugin::test_modelmanager_snapshot_after_two_projects() /// though it might not be actually generated in the build dir. void CppToolsPlugin::test_modelmanager_extraeditorsupport_uiFiles() { - TestDataDirectory testDataDirectory(QLatin1String("testdata_guiproject1")); + ModelManagerTestHelper helper; + + MyTestDataDir testDataDirectory(QLatin1String("testdata_guiproject1")); const QString projectFile = testDataDirectory.file(QLatin1String("testdata_guiproject1.pro")); // Open project with *.ui file - ProjectExplorer::ProjectExplorerPlugin *pe = ProjectExplorer::ProjectExplorerPlugin::instance(); + ProjectExplorerPlugin *pe = ProjectExplorerPlugin::instance(); ExampleProjectConfigurator exampleProjectConfigurator(projectFile, pe); Project *project = exampleProjectConfigurator.project(); @@ -487,7 +673,7 @@ void CppToolsPlugin::test_modelmanager_extraeditorsupport_uiFiles() QCOMPARE(workingCopy.size(), 2); // mm->configurationFileName() and "ui_*.h" QStringList fileNamesInWorkinCopy; - QHashIterator<QString, QPair<QString, unsigned> > it = workingCopy.iterator(); + QHashIterator<QString, QPair<QByteArray, unsigned> > it = workingCopy.iterator(); while (it.hasNext()) { it.next(); fileNamesInWorkinCopy << QFileInfo(it.key()).fileName(); @@ -512,7 +698,71 @@ void CppToolsPlugin::test_modelmanager_extraeditorsupport_uiFiles() QCOMPARE(QFileInfo(includedFiles.at(1)).fileName(), QLatin1String("ui_mainwindow.h")); // Close Project - ProjectExplorer::SessionManager *sm = pe->session(); - sm->removeProject(project); - ModelManagerTestHelper::verifyClean(); + SessionManager::removeProject(project); + helper.waitForFinishedGc(); +} + +/// QTCREATORBUG-9828: Locator shows symbols of closed files +/// Check: The garbage collector should be run if the last CppEditor is closed. +void CppToolsPlugin::test_modelmanager_gc_if_last_cppeditor_closed() +{ + ModelManagerTestHelper helper; + + MyTestDataDir testDataDirectory(QLatin1String("testdata_guiproject1")); + const QString file = testDataDirectory.file(QLatin1String("main.cpp")); + + CppModelManager *mm = CppModelManager::instance(); + + // Open a file in the editor + QCOMPARE(Core::EditorManager::documentModel()->openedDocuments().size(), 0); + Core::IEditor *editor = Core::EditorManager::openEditor(file); + QVERIFY(editor); + QCOMPARE(Core::EditorManager::documentModel()->openedDocuments().size(), 1); + QVERIFY(mm->isCppEditor(editor)); + QVERIFY(mm->workingCopy().contains(file)); + + // Check: File is in the snapshot + QVERIFY(mm->snapshot().contains(file)); + + // Close file/editor + Core::EditorManager::closeEditor(editor, /*askAboutModifiedEditors=*/ false); + helper.waitForFinishedGc(); + + // Check: File is removed from the snapshpt + QVERIFY(!mm->workingCopy().contains(file)); + QVERIFY(!mm->snapshot().contains(file)); +} + +/// Check: Files that are open in the editor are not garbage collected. +void CppToolsPlugin::test_modelmanager_dont_gc_opened_files() +{ + ModelManagerTestHelper helper; + + MyTestDataDir testDataDirectory(QLatin1String("testdata_guiproject1")); + const QString file = testDataDirectory.file(QLatin1String("main.cpp")); + + CppModelManager *mm = CppModelManager::instance(); + + // Open a file in the editor + QCOMPARE(Core::EditorManager::documentModel()->openedDocuments().size(), 0); + Core::IEditor *editor = Core::EditorManager::openEditor(file); + QVERIFY(editor); + QCOMPARE(Core::EditorManager::documentModel()->openedDocuments().size(), 1); + QVERIFY(mm->isCppEditor(editor)); + + // Check: File is in the working copy and snapshot + QVERIFY(mm->workingCopy().contains(file)); + QVERIFY(mm->snapshot().contains(file)); + + // Run the garbage collector + mm->GC(); + + // Check: File is still there + QVERIFY(mm->workingCopy().contains(file)); + QVERIFY(mm->snapshot().contains(file)); + + // Close editor + Core::EditorManager::closeEditors(QList<Core::IEditor*>() << editor); + helper.waitForFinishedGc(); + QVERIFY(mm->snapshot().isEmpty()); } |