diff --git a/projectmanagers/cmake/cmakeutils.cpp b/projectmanagers/cmake/cmakeutils.cpp --- a/projectmanagers/cmake/cmakeutils.cpp +++ b/projectmanagers/cmake/cmakeutils.cpp @@ -643,7 +643,6 @@ const auto contents = CMakeListsParser::readCMakeFile(buildDir.toLocalFile() + "/CTestTestfile.cmake"); QVector tests; - QVector subdirs; for (const auto& entry: contents) { if (entry.name == QLatin1String("add_test")) { auto args = entry.arguments; @@ -655,11 +654,29 @@ tests += test; } else if (entry.name == QLatin1String("subdirs")) { tests += importTestSuites(Path(buildDir, entry.arguments.constFirst().value)); + } else if (entry.name == QLatin1String("set_tests_properties")) { + if(entry.arguments.count() < 4 || entry.arguments.count() % 2) { + qCWarning(CMAKE) << "found set_tests_properties() with unexpected number of arguments:" + << entry.arguments.count(); + continue; + } + if (tests.isEmpty() || entry.arguments.constFirst().value != tests.constLast().name) { + qCWarning(CMAKE) << "found set_tests_properties(" << entry.arguments.constFirst().value + << " ...), but expected test " << tests.constLast().name; + continue; + } + if (entry.arguments[1].value != QLatin1String("PROPERTIES")) { + qCWarning(CMAKE) << "found set_tests_properties(" << entry.arguments.constFirst().value + << entry.arguments.at(1).value << "...), but expected PROPERTIES as second argument"; + continue; + } + Test &test = tests.last(); + for (int i = 2; i < entry.arguments.count(); i += 2) + test.properties[entry.arguments[i].value] = entry.arguments[i + 1].value; } } return tests; } } - diff --git a/projectmanagers/cmake/testing/ctestrunjob.h b/projectmanagers/cmake/testing/ctestrunjob.h --- a/projectmanagers/cmake/testing/ctestrunjob.h +++ b/projectmanagers/cmake/testing/ctestrunjob.h @@ -30,7 +30,7 @@ { Q_OBJECT public: - CTestRunJob(CTestSuite* suite, const QStringList& cases, KDevelop::OutputJob::OutputJobVerbosity verbosity, bool expectFail, QObject* parent = nullptr); + CTestRunJob(CTestSuite* suite, const QStringList& cases, KDevelop::OutputJob::OutputJobVerbosity verbosity, QObject* parent = nullptr); void start() override; protected: @@ -47,7 +47,6 @@ KJob* m_job; KDevelop::OutputJob* m_outputJob; KDevelop::OutputJob::OutputJobVerbosity m_verbosity; - bool m_expectFail; }; #endif // CTESTRUNJOB_H diff --git a/projectmanagers/cmake/testing/ctestrunjob.cpp b/projectmanagers/cmake/testing/ctestrunjob.cpp --- a/projectmanagers/cmake/testing/ctestrunjob.cpp +++ b/projectmanagers/cmake/testing/ctestrunjob.cpp @@ -36,14 +36,13 @@ using namespace KDevelop; -CTestRunJob::CTestRunJob(CTestSuite* suite, const QStringList& cases, OutputJob::OutputJobVerbosity verbosity, bool expectFail, QObject* parent) +CTestRunJob::CTestRunJob(CTestSuite* suite, const QStringList& cases, OutputJob::OutputJobVerbosity verbosity, QObject* parent) : KJob(parent) , m_suite(suite) , m_cases(cases) , m_job(nullptr) , m_outputJob(nullptr) , m_verbosity(verbosity) -, m_expectFail(expectFail) { foreach (const QString& testCase, cases) { @@ -54,7 +53,7 @@ } -KJob* createTestJob(const QString& launchModeId, const QStringList& arguments ) +static KJob* createTestJob(const QString& launchModeId, const QStringList& arguments, const QString &workingDirectory) { LaunchConfigurationType* type = ICore::self()->runController()->launchConfigurationTypeForId( QStringLiteral("Native Application") ); ILaunchMode* mode = ICore::self()->runController()->launchModeForId( launchModeId ); @@ -91,6 +90,8 @@ } else { //qCDebug(CMAKE) << "reusing generated config, launching"; } + if (!workingDirectory.isEmpty()) + ilaunch->config().writeEntry( "Working Directory", QUrl::fromLocalFile( workingDirectory ) ); type->configureLaunchFromCmdLineArguments( ilaunch->config(), arguments ); return ICore::self()->runController()->execute(launchModeId, ilaunch); } @@ -113,14 +114,16 @@ QStringList cases_selected = arguments; arguments.prepend(m_suite->executable().toLocalFile()); - m_job = createTestJob(QStringLiteral("execute"), arguments); + const QString workingDirectory = m_suite->properties().value(QLatin1String("WORKING_DIRECTORY"), QString()); + + m_job = createTestJob(QStringLiteral("execute"), arguments, workingDirectory); if (ExecuteCompositeJob* cjob = qobject_cast(m_job)) { m_outputJob = cjob->findChild(); Q_ASSERT(m_outputJob); m_outputJob->setVerbosity(m_verbosity); - QString testName = arguments.value(0).split('/').last(); + QString testName = m_suite->name(); QString title; if (cases_selected.count() == 1) title = i18nc("running test %1, %2 test case", "CTest %1: %2", testName, cases_selected.value(0)); @@ -188,13 +191,14 @@ if (prevResult == TestResult::Passed || prevResult == TestResult::NotRun) { TestResult::TestCaseResult result = TestResult::NotRun; + const bool expectFail = m_suite->properties().value(QStringLiteral("WILL_FAIL"), QStringLiteral("FALSE")) == QLatin1String("TRUE"); if (line.startsWith(QLatin1String("PASS :"))) { - result = m_expectFail ? TestResult::UnexpectedPass : TestResult::Passed; + result = expectFail ? TestResult::UnexpectedPass : TestResult::Passed; } else if (line.startsWith(QLatin1String("FAIL! :"))) { - result = m_expectFail ? TestResult::ExpectedFail : TestResult::Failed; + result = expectFail ? TestResult::ExpectedFail : TestResult::Failed; } else if (line.startsWith(QLatin1String("XFAIL :"))) { diff --git a/projectmanagers/cmake/testing/ctestsuite.h b/projectmanagers/cmake/testing/ctestsuite.h --- a/projectmanagers/cmake/testing/ctestsuite.h +++ b/projectmanagers/cmake/testing/ctestsuite.h @@ -32,38 +32,39 @@ class CTestSuite : public KDevelop::ITestSuite { public: - CTestSuite(const QString& name, const KDevelop::Path &executable, const QList& files, KDevelop::IProject* project, const QStringList& args, bool expectFail); + CTestSuite(const QString& name, const KDevelop::Path &executable, const QList& files, KDevelop::IProject* project, const QStringList& args, const QHash& properties); virtual ~CTestSuite(); - + virtual KJob* launchCase(const QString& testCase, TestJobVerbosity verbosity); virtual KJob* launchCases(const QStringList& testCases, TestJobVerbosity verbosity); virtual KJob* launchAllCases(TestJobVerbosity verbosity); - + virtual KDevelop::Path executable() const; virtual QStringList cases() const; virtual QString name() const; virtual KDevelop::IProject* project() const; - + virtual KDevelop::IndexedDeclaration declaration() const; virtual KDevelop::IndexedDeclaration caseDeclaration(const QString& testCase) const; - + + virtual QHash properties() const; + QStringList arguments() const; void setTestCases(const QStringList& cases); QList sourceFiles() const; void loadDeclarations(const KDevelop::IndexedString& document, const KDevelop::ReferencedTopDUContext& context); - + private: KDevelop::Path m_executable; QString m_name; QStringList m_cases; QStringList m_args; QList m_files; KDevelop::IProject* m_project; - + QHash m_declarations; + QHash m_properties; KDevelop::IndexedDeclaration m_suiteDeclaration; - - bool m_expectFail; }; #endif // CTESTSUITE_H diff --git a/projectmanagers/cmake/testing/ctestsuite.cpp b/projectmanagers/cmake/testing/ctestsuite.cpp --- a/projectmanagers/cmake/testing/ctestsuite.cpp +++ b/projectmanagers/cmake/testing/ctestsuite.cpp @@ -38,13 +38,13 @@ using namespace KDevelop; -CTestSuite::CTestSuite(const QString& name, const KDevelop::Path &executable, const QList& files, IProject* project, const QStringList& args, bool expectFail): +CTestSuite::CTestSuite(const QString& name, const KDevelop::Path &executable, const QList& files, IProject* project, const QStringList& args, const QHash& properties): m_executable(executable), m_name(name), m_args(args), m_files(files), m_project(project), -m_expectFail(expectFail) +m_properties(properties) { Q_ASSERT(project); qCDebug(CMAKE) << m_name << m_executable << m_project->name(); @@ -139,7 +139,7 @@ qCDebug(CMAKE) << "Launching test run" << m_name << "with cases" << testCases; OutputJob::OutputJobVerbosity outputVerbosity = (verbosity == Verbose) ? OutputJob::Verbose : OutputJob::Silent; - return new CTestRunJob(this, testCases, outputVerbosity, m_expectFail); + return new CTestRunJob(this, testCases, outputVerbosity); } KJob* CTestSuite::launchAllCases(TestJobVerbosity verbosity) @@ -192,5 +192,7 @@ return m_files; } - - +QHash CTestSuite::properties() const +{ + return m_properties; +} diff --git a/projectmanagers/cmake/testing/ctestutils.cpp b/projectmanagers/cmake/testing/ctestutils.cpp --- a/projectmanagers/cmake/testing/ctestutils.cpp +++ b/projectmanagers/cmake/testing/ctestutils.cpp @@ -54,8 +54,7 @@ foreach (const Test& test, testSuites) { const auto target = targetByName(targets, test.executable); - const bool willFail = test.properties.value(QStringLiteral("WILL_FAIL"), QStringLiteral("FALSE")) == QLatin1String("TRUE"); - CTestSuite* suite = new CTestSuite(test.name, target.artifacts.constFirst(), {}, project, test.arguments, willFail); + CTestSuite* suite = new CTestSuite(test.name, target.artifacts.constFirst(), {}, project, test.arguments, test.properties); ICore::self()->runController()->registerJob(new CTestFindJob(suite)); } } diff --git a/projectmanagers/cmake/tests/manual/unit_tests/CMakeLists.txt b/projectmanagers/cmake/tests/manual/unit_tests/CMakeLists.txt --- a/projectmanagers/cmake/tests/manual/unit_tests/CMakeLists.txt +++ b/projectmanagers/cmake/tests/manual/unit_tests/CMakeLists.txt @@ -7,9 +7,18 @@ add_executable(test_fail fail.cpp) set_target_properties(test_fail PROPERTIES RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) add_test(fail test_fail) +set_tests_properties(fail PROPERTIES + FOO "foo" + BAR TRUE + MULTILINE "this is +a multi +line property" + QUOTES "\"\\\\\"\\\\\\" + WORKING_DIRECTORY "/bar/baz" + WILL_FAIL TRUE) add_executable(four_test math_test.cpp) -add_test(test_three four_test 3) +add_test(NAME test_three COMMAND four_test 3 WORKING_DIRECTORY "/foo") add_test(test_four four_test 4) add_subdirectory(five) diff --git a/projectmanagers/cmake/tests/manual/unit_tests/five/CMakeLists.txt b/projectmanagers/cmake/tests/manual/unit_tests/five/CMakeLists.txt --- a/projectmanagers/cmake/tests/manual/unit_tests/five/CMakeLists.txt +++ b/projectmanagers/cmake/tests/manual/unit_tests/five/CMakeLists.txt @@ -1 +1,2 @@ add_test(test_five four_test 5) +set_property(TEST test_five PROPERTY WORKING_DIRECTORY "${CMAKE_BINARY_DIR}/bin") diff --git a/projectmanagers/cmake/tests/manual/unit_tests/math_test.cpp b/projectmanagers/cmake/tests/manual/unit_tests/math_test.cpp --- a/projectmanagers/cmake/tests/manual/unit_tests/math_test.cpp +++ b/projectmanagers/cmake/tests/manual/unit_tests/math_test.cpp @@ -1,11 +1,13 @@ +#include + int main(int argc, char** argv) { if (argc < 2) { return 1; } - - if (atoi(argv[1]) == 4) + + if (std::atoi(argv[1]) == 4) { return 0; } diff --git a/projectmanagers/cmake/tests/test_ctestfindsuites.cpp b/projectmanagers/cmake/tests/test_ctestfindsuites.cpp --- a/projectmanagers/cmake/tests/test_ctestfindsuites.cpp +++ b/projectmanagers/cmake/tests/test_ctestfindsuites.cpp @@ -85,12 +85,31 @@ foreach (auto suite, suites) { + qDebug() << "checking suite" << suite->name(); QCOMPARE(suite->cases(), QStringList()); QVERIFY(!suite->declaration().isValid()); - CTestSuite* ctestSuite = (CTestSuite*)(suite); + CTestSuite* ctestSuite = static_cast(suite); const auto buildDir = Path(CMake::allBuildDirs(project).at(0)); QString exeSubdir = buildDir.relativePath(ctestSuite->executable().parent()); QCOMPARE(exeSubdir, ctestSuite->name() == "fail" ? QStringLiteral("bin") : QString() ); + QString willFail; + const QString workingDirectory = ctestSuite->properties().value(QLatin1String("WORKING_DIRECTORY"), QString()); + if (ctestSuite->name() == QLatin1String("fail")) { + willFail = QLatin1String("TRUE"); + QCOMPARE(workingDirectory, QLatin1String("/bar/baz")); + QCOMPARE(ctestSuite->properties().value(QLatin1String("FOO"), QString()), QLatin1String("foo")); + QCOMPARE(ctestSuite->properties().value(QLatin1String("BAR"), QString()), QLatin1String("TRUE")); + QCOMPARE(ctestSuite->properties().value(QLatin1String("MULTILINE"), QString()), QLatin1String("this is \na multi\nline property")); + QCOMPARE(ctestSuite->properties().value(QLatin1String("QUOTES"), QString()), QLatin1String("\"\\\\\"\\\\\\")); + } else if (ctestSuite->name() == QLatin1String("test_three")) + QCOMPARE(workingDirectory, QLatin1String("/foo")); + else if (ctestSuite->name() == QLatin1String("test_three")) + QCOMPARE(workingDirectory, QLatin1String("/foo")); + else if (ctestSuite->name() == QLatin1String("test_five")) + QCOMPARE(workingDirectory, QString(buildDir.path() + QLatin1String("/bin"))); + else + QCOMPARE(workingDirectory, QString()); + QCOMPARE(ctestSuite->properties().value(QLatin1String("WILL_FAIL")), willFail); } }