// Copyright (C) 2016 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 #include "clangtoolspreconfiguredsessiontests.h" #include "clangtool.h" #include "clangtoolsdiagnostic.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Core; using namespace CppEditor; using namespace ProjectExplorer; using namespace Utils; static bool processEventsUntil(const std::function condition, int timeOutInMs = 30000) { QElapsedTimer t; t.start(); while (true) { if (t.elapsed() > timeOutInMs) return false; if (condition()) return true; QCoreApplication::processEvents(); } } class WaitForParsedProjects : public QObject { public: WaitForParsedProjects(const FilePaths &projects) : m_projectsToWaitFor(projects) { connect(ProjectManager::instance(), &ProjectExplorer::ProjectManager::projectFinishedParsing, this, &WaitForParsedProjects::onProjectFinishedParsing); } void onProjectFinishedParsing(ProjectExplorer::Project *project) { m_projectsToWaitFor.removeOne(project->projectFilePath()); } bool wait() { return processEventsUntil([this] { return m_projectsToWaitFor.isEmpty(); }); } private: FilePaths m_projectsToWaitFor; }; namespace ClangTools { namespace Internal { void PreconfiguredSessionTests::initTestCase() { const QString preconfiguredSessionName = QLatin1String("ClangToolsTest"); if (!SessionManager::sessions().contains(preconfiguredSessionName)) QSKIP("Manually preconfigured session 'ClangToolsTest' needed."); if (SessionManager::activeSession() == preconfiguredSessionName) QSKIP("Session must not be already active."); // Load session const FilePaths projects = ProjectManager::projectsForSessionName(preconfiguredSessionName); WaitForParsedProjects waitForParsedProjects(projects); QVERIFY(SessionManager::loadSession(preconfiguredSessionName)); QVERIFY(waitForParsedProjects.wait()); } void PreconfiguredSessionTests::testPreconfiguredSession() { QFETCH(Project *, project); QFETCH(Target *, target); QVERIFY(switchToProjectAndTarget(project, target)); for (ClangTool * const tool : {ClangTidyTool::instance(), ClazyTool::instance()}) { tool->startTool(ClangTool::FileSelectionType::AllFiles); QSignalSpy waitUntilAnalyzerFinished(tool, &ClangTool::finished); QVERIFY(waitUntilAnalyzerFinished.wait(30000)); const QList arguments = waitUntilAnalyzerFinished.takeFirst(); const bool analyzerFinishedSuccessfully = arguments.first().toBool(); QVERIFY(analyzerFinishedSuccessfully); QCOMPARE(tool->diagnostics().count(), 0); } } static const QList validProjects(const QList projectsOfSession) { const QList sortedProjects = Utils::sorted(projectsOfSession, [](Project *lhs, Project *rhs) { return lhs->displayName() < rhs->displayName(); } ); const auto isValidProject = [](Project *project) { const QList targets = project->targets(); if (targets.isEmpty()) { qWarning("Skipping project \"%s\" since it has no targets.", qPrintable(project->projectFilePath().fileName())); } return !targets.isEmpty(); }; return Utils::filtered(sortedProjects, isValidProject); } static QList validTargets(Project *project) { const QList sortedTargets = Utils::sorted(project->targets(), [](Target *lhs, Target *rhs) { return lhs->displayName() < rhs->displayName(); } ); const QString projectFileName = project->projectFilePath().fileName(); const auto isValidTarget = [projectFileName](Target *target) { Kit *kit = target->kit(); if (!kit || !kit->isValid()) { qWarning("Project \"%s\": Skipping target \"%s\" since it has no (valid) kits.", qPrintable(projectFileName), qPrintable(target->displayName())); return false; } const Toolchain * const toolchain = ToolchainKitAspect::cxxToolchain(kit); QTC_ASSERT(toolchain, return false); if (!Core::ICore::clangExecutable(CLANG_BINDIR)) { qWarning("Project \"%s\": Skipping target \"%s\" since no suitable clang was found for the toolchain.", qPrintable(projectFileName), qPrintable(target->displayName())); return false; } return true; }; return Utils::filtered(sortedTargets, isValidTarget); } static QByteArray dataTagName(Project *project, Target *target) { const QString projectFileName = project->projectFilePath().fileName(); const QString dataTagAsString = projectFileName + " -- " + target->displayName(); return dataTagAsString.toUtf8(); } void PreconfiguredSessionTests::testPreconfiguredSession_data() { QTest::addColumn("project"); QTest::addColumn("target"); bool hasAddedTestData = false; for (Project *project : validProjects(ProjectManager::projects())) { for (Target *target : validTargets(project)) { hasAddedTestData = true; QTest::newRow(dataTagName(project, target)) << project << target; } } if (!hasAddedTestData) QSKIP("Session has no valid projects/targets to test."); } bool PreconfiguredSessionTests::switchToProjectAndTarget(Project *project, Target *target) { Project * const activeProject = ProjectManager::startupProject(); if (project == activeProject && target == activeProject->activeTarget()) return true; // OK, desired project/target already active. if (project != activeProject) ProjectManager::setStartupProject(project); if (target != project->activeTarget()) { QSignalSpy spyFinishedParsing(ProjectExplorer::ProjectManager::instance(), &ProjectExplorer::ProjectManager::projectFinishedParsing); project->setActiveTarget(target, ProjectExplorer::SetActive::NoCascade); QTC_ASSERT(spyFinishedParsing.wait(30000), return false); const QVariant projectArgument = spyFinishedParsing.takeFirst().constFirst(); QTC_ASSERT(projectArgument.canConvert(), return false); return projectArgument.value() == project; } return true; } } // namespace Internal } // namespace ClangTools