summaryrefslogtreecommitdiff
path: root/src/plugins/cpptools/cppmodelmanager_test.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/cpptools/cppmodelmanager_test.cpp')
-rw-r--r--src/plugins/cpptools/cppmodelmanager_test.cpp372
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());
}