/**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. ** Contact: http://www.qt.io/licensing ** ** This file is part of Qt Creator. ** ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms and ** conditions see http://www.qt.io/terms-conditions. For further information ** use the contact form at http://www.qt.io/contact-us. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 or version 3 as published by the Free ** Software Foundation and appearing in the file LICENSE.LGPLv21 and ** LICENSE.LGPLv3 included in the packaging of this file. Please review the ** following information to ensure the GNU Lesser General Public License ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, The Qt Company gives you certain additional ** rights. These rights are described in The Qt Company LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ****************************************************************************/ #include "cpptoolstestcase.h" #include "cppworkingcopy.h" #include #include #include #include #include #include #include #include #include using namespace ProjectExplorer; static bool closeEditorsWithoutGarbageCollectorInvocation(const QList &editors) { CppTools::CppModelManager::instance()->enableGarbageCollector(false); const bool closeEditorsSucceeded = Core::EditorManager::closeEditors(editors, false); CppTools::CppModelManager::instance()->enableGarbageCollector(true); return closeEditorsSucceeded; } static bool snapshotContains(const CPlusPlus::Snapshot &snapshot, const QSet &filePaths) { foreach (const QString &filePath, filePaths) { if (!snapshot.contains(filePath)) { const QString warning = QLatin1String("Missing file in snapshot: ") + filePath; QWARN(qPrintable(warning)); return false; } } return true; } namespace CppTools { namespace Tests { TestDocument::TestDocument(const QByteArray &fileName, const QByteArray &source, char cursorMarker) : m_fileName(QString::fromUtf8(fileName)) , m_source(QString::fromUtf8(source)) , m_cursorMarker(cursorMarker) {} QString TestDocument::filePath() const { if (!m_baseDirectory.isEmpty()) return QDir::cleanPath(m_baseDirectory + QLatin1Char('/') + m_fileName); if (!QFileInfo(m_fileName).isAbsolute()) return QDir::tempPath() + QLatin1Char('/') + m_fileName; return m_fileName; } bool TestDocument::writeToDisk() const { return TestCase::writeFile(filePath(), m_source.toUtf8()); } TestCase::TestCase(bool runGarbageCollector) : m_modelManager(CppModelManager::instance()) , m_succeededSoFar(false) , m_runGarbageCollector(runGarbageCollector) { if (m_runGarbageCollector) QVERIFY(garbageCollectGlobalSnapshot()); m_succeededSoFar = true; } TestCase::~TestCase() { QVERIFY(closeEditorsWithoutGarbageCollectorInvocation(m_editorsToClose)); QCoreApplication::processEvents(); if (m_runGarbageCollector) QVERIFY(garbageCollectGlobalSnapshot()); } bool TestCase::succeededSoFar() const { return m_succeededSoFar; } bool TestCase::openBaseTextEditor(const QString &fileName, TextEditor::BaseTextEditor **editor) { typedef TextEditor::BaseTextEditor BTEditor; if (BTEditor *e = qobject_cast(Core::EditorManager::openEditor(fileName))) { if (editor) { *editor = e; return true; } } return false; } CPlusPlus::Snapshot TestCase::globalSnapshot() { return CppModelManager::instance()->snapshot(); } bool TestCase::garbageCollectGlobalSnapshot() { CppModelManager::instance()->GC(); return globalSnapshot().isEmpty(); } bool TestCase::parseFiles(const QSet &filePaths) { CppModelManager::instance()->updateSourceFiles(filePaths).waitForFinished(); QCoreApplication::processEvents(); const CPlusPlus::Snapshot snapshot = globalSnapshot(); if (snapshot.isEmpty()) { QWARN("After parsing: snapshot is empty."); return false; } if (!snapshotContains(snapshot, filePaths)) { QWARN("After parsing: snapshot does not contain all expected files."); return false; } return true; } bool TestCase::parseFiles(const QString &filePath) { return parseFiles(QSet() << filePath); } void TestCase::closeEditorAtEndOfTestCase(Core::IEditor *editor) { if (editor && !m_editorsToClose.contains(editor)) m_editorsToClose.append(editor); } bool TestCase::closeEditorWithoutGarbageCollectorInvocation(Core::IEditor *editor) { return closeEditorsWithoutGarbageCollectorInvocation(QList() << editor); } CPlusPlus::Document::Ptr TestCase::waitForFileInGlobalSnapshot(const QString &filePath) { return waitForFilesInGlobalSnapshot(QStringList(filePath)).first(); } QList TestCase::waitForFilesInGlobalSnapshot( const QStringList &filePaths) { QList result; foreach (const QString &filePath, filePaths) { forever { if (CPlusPlus::Document::Ptr document = globalSnapshot().document(filePath)) { result.append(document); break; } QCoreApplication::processEvents(); } } return result; } bool TestCase::waitUntilCppModelManagerIsAwareOf(Project *project, int timeOut) { if (!project) return false; QTime t; t.start(); CppModelManager *modelManager = CppModelManager::instance(); forever { if (modelManager->projectInfo(project).isValid()) return true; if (t.elapsed() > timeOut) return false; QCoreApplication::processEvents(); } } bool TestCase::writeFile(const QString &filePath, const QByteArray &contents) { Utils::FileSaver saver(filePath); if (!saver.write(contents) || !saver.finalize()) { const QString warning = QLatin1String("Failed to write file to disk: ") + filePath; QWARN(qPrintable(warning)); return false; } return true; } ProjectOpenerAndCloser::ProjectOpenerAndCloser(bool waitForFinishedGcOnDestruction) : m_waitForFinishedGcOnDestruction(waitForFinishedGcOnDestruction) , m_gcFinished(false) { QVERIFY(!SessionManager::hasProjects()); if (m_waitForFinishedGcOnDestruction) { CppModelManager *mm = CppModelManager::instance(); connect(mm, &CppModelManager::gcFinished, this, &ProjectOpenerAndCloser::onGcFinished); } } ProjectOpenerAndCloser::~ProjectOpenerAndCloser() { foreach (Project *project, m_openProjects) ProjectExplorerPlugin::unloadProject(project); if (m_waitForFinishedGcOnDestruction) { m_gcFinished = false; while (!m_gcFinished) QCoreApplication::processEvents(); } } ProjectInfo ProjectOpenerAndCloser::open(const QString &projectFile, bool configureAsExampleProject) { QString error; Project *project = ProjectExplorerPlugin::openProject(projectFile, &error); if (!error.isEmpty()) qWarning() << error; if (!project) return ProjectInfo(); m_openProjects.append(project); if (configureAsExampleProject) project->configureAsExampleProject(QStringList()); if (TestCase::waitUntilCppModelManagerIsAwareOf(project)) return CppModelManager::instance()->projectInfo(project); return ProjectInfo(); } void ProjectOpenerAndCloser::onGcFinished() { m_gcFinished = true; } TemporaryDir::TemporaryDir() : m_temporaryDir(QFileInfo(QDir::tempPath()).canonicalFilePath() + QLatin1String("/qtcreator-tests-XXXXXX")) , m_isValid(m_temporaryDir.isValid()) { } QString TemporaryDir::createFile(const QByteArray &relativePath, const QByteArray &contents) { const QString relativePathString = QString::fromUtf8(relativePath); if (relativePathString.isEmpty() || QFileInfo(relativePathString).isAbsolute()) return QString(); const QString filePath = m_temporaryDir.path() + QLatin1Char('/') + relativePathString; if (!TestCase::writeFile(filePath, contents)) return QString(); return filePath; } TemporaryCopiedDir::TemporaryCopiedDir(const QString &sourceDirPath) { if (!m_isValid) return; if (!sourceDirPath.isEmpty()) { QFileInfo fi(sourceDirPath); if (!fi.exists() || !fi.isReadable()) { m_isValid = false; return; } if (!Utils::FileUtils::copyRecursively(Utils::FileName::fromString(sourceDirPath), Utils::FileName::fromString(path()))) { m_isValid = false; return; } } } QString TemporaryCopiedDir::absolutePath(const QByteArray &relativePath) const { return m_temporaryDir.path() + QLatin1Char('/') + QString::fromUtf8(relativePath); } FileWriterAndRemover::FileWriterAndRemover(const QString &filePath, const QByteArray &contents) : m_filePath(filePath) { if (QFileInfo::exists(filePath)) { const QString warning = QString::fromLatin1( "Will not overwrite existing file: \"%1\"." " If this file is left over due to a(n) abort/crash, please remove manually.") .arg(m_filePath); QWARN(qPrintable(warning)); m_writtenSuccessfully = false; } else { m_writtenSuccessfully = TestCase::writeFile(filePath, contents); } } FileWriterAndRemover::~FileWriterAndRemover() { if (m_writtenSuccessfully && !QFile::remove(m_filePath)) { const QString warning = QLatin1String("Failed to remove file from disk: ") + m_filePath; QWARN(qPrintable(warning)); } } IAssistProposalScopedPointer::IAssistProposalScopedPointer(TextEditor::IAssistProposal *proposal) : d(proposal) {} IAssistProposalScopedPointer::~IAssistProposalScopedPointer() { if (d && d->model()) delete d->model(); } VerifyCleanCppModelManager::VerifyCleanCppModelManager() { QVERIFY(isClean()); } VerifyCleanCppModelManager::~VerifyCleanCppModelManager() { QVERIFY(isClean()); } #define RETURN_FALSE_IF_NOT(check) if (!(check)) return false; bool VerifyCleanCppModelManager::isClean() { CppModelManager *mm = CppModelManager::instance(); RETURN_FALSE_IF_NOT(mm); RETURN_FALSE_IF_NOT(mm->projectInfos().isEmpty()); RETURN_FALSE_IF_NOT(mm->headerPaths().isEmpty()); RETURN_FALSE_IF_NOT(mm->definedMacros().isEmpty()); RETURN_FALSE_IF_NOT(mm->projectFiles().isEmpty()); RETURN_FALSE_IF_NOT(mm->snapshot().isEmpty()); RETURN_FALSE_IF_NOT(mm->workingCopy().size() == 1); RETURN_FALSE_IF_NOT(mm->workingCopy().contains(mm->configurationFileName())); return true; } #undef RETURN_FALSE_IF_NOT } // namespace Tests } // namespace CppTools