diff --git a/api/dbus/org.desktopspec.ApplicationManager1.Application.xml b/api/dbus/org.desktopspec.ApplicationManager1.Application.xml index 476cf87..57a09aa 100644 --- a/api/dbus/org.desktopspec.ApplicationManager1.Application.xml +++ b/api/dbus/org.desktopspec.ApplicationManager1.Application.xml @@ -17,10 +17,6 @@ - - - - + + + + + + + + + + + + diff --git a/src/dbus/applicationmanager1service.cpp b/src/dbus/applicationmanager1service.cpp index ad06dee..8f9e369 100644 --- a/src/dbus/applicationmanager1service.cpp +++ b/src/dbus/applicationmanager1service.cpp @@ -218,12 +218,5 @@ void ApplicationManager1Service::UpdateApplicationInfo(const QStringList &appIdL ObjectMap ApplicationManager1Service::GetManagedObjects() const { - ObjectMap objs; - - for (const auto &[key, value] : m_applicationList.asKeyValueRange()) { - auto interfaces = getInterfacesListFromObject(value.data()); - objs.insert(key, interfaces); - } - - return objs; + return dumpDBusObject(m_applicationList); } diff --git a/src/dbus/applicationmanager1service.h b/src/dbus/applicationmanager1service.h index b9add4b..3db3c91 100644 --- a/src/dbus/applicationmanager1service.h +++ b/src/dbus/applicationmanager1service.h @@ -83,7 +83,8 @@ private: }; template -QSharedPointer makeApplication(T &&source, ApplicationManager1Service *parent) +QSharedPointer makeApplication(T &&source, + ApplicationManager1Service *parent) // NOTE: maybe we should refactor { static_assert(std::is_same_v or std::is_same_v, "param type must be QString or DesktopFile."); QString objectPath; diff --git a/src/dbus/applicationservice.cpp b/src/dbus/applicationservice.cpp index 7b73744..4b54d2d 100644 --- a/src/dbus/applicationservice.cpp +++ b/src/dbus/applicationservice.cpp @@ -25,7 +25,7 @@ ApplicationService::~ApplicationService() } } -QString ApplicationService::GetActionName(const QString &identifier, const QStringList &env) +QString ApplicationService::GetActionName(const QString &identifier, const QStringList &env) const { const auto &supportedActions = actions(); if (supportedActions.isEmpty()) { @@ -68,6 +68,67 @@ QString ApplicationService::GetActionName(const QString &identifier, const QStri return name; } +QString ApplicationService::GetDisplayName(const QStringList &env) const +{ + const auto &displayName = m_entry->value(DesktopFileEntryKey, "Name"); + if (!displayName) { + return {}; + } + + QString locale{""}; + bool ok; + if (!env.isEmpty()) { + QString lcStr; + for (const auto &lc : env) { + if (lc.startsWith("LANG")) { + locale = lc.split('=').last(); + } + + if (lc.startsWith("LC_ALL")) { + locale = lc.split('=').last(); + break; + } + } + } + + QLocale lc = locale.isEmpty() ? getUserLocale() : QLocale{locale}; + + const auto &name = displayName->toLocaleString(lc, ok); + if (!ok) { + qWarning() << "convert to locale string failed, dest locale:" << lc; + return {}; + } + return name; +} + +QString ApplicationService::GetIconName(const QString &action) const +{ + std::optional iconName{std::nullopt}; + + if (action.isEmpty()) { + iconName = m_entry->value(DesktopFileEntryKey, "Icon"); + + } else { + const auto &supportedActions = actions(); + + if (auto index = supportedActions.indexOf(action); index == -1) { + qWarning() << "can't find " << action << " in supported actions List."; + return {}; + } + + const auto &actionHeader = QString{"%1%2"}.arg(DesktopFileActionKey, action); + iconName = m_entry->value(actionHeader, "Icon"); + } + + if (!iconName) { + return {}; + } + + bool ok{false}; + const auto &name = iconName->toIconString(ok); + return ok ? name : ""; +} + QDBusObjectPath ApplicationService::Launch(QString action, QStringList fields, QVariantMap options) { QString execStr; @@ -208,14 +269,7 @@ QStringList ApplicationService::actions() const noexcept ObjectMap ApplicationService::GetManagedObjects() const { - ObjectMap objs; - - for (const auto &[key, value] : m_Instances.asKeyValueRange()) { - auto interfaces = getInterfacesListFromObject(value.data()); - objs.insert(key, interfaces); - } - - return objs; + return dumpDBusObject(m_Instances); } QString ApplicationService::id() const noexcept @@ -254,50 +308,6 @@ QList ApplicationService::instances() const noexcept return m_Instances.keys(); } -QString ApplicationService::iconName() const noexcept -{ - if (m_entry.isNull()) { - qWarning() << "desktop entry is empty, isPersistence:" << m_isPersistence; - return {}; - } - - const auto &actions = m_entry->value(DesktopFileEntryKey, "Icon"); - if (!actions) { - return {}; - } - - bool ok{false}; - const auto &icon = actions->toIconString(ok); - if (!ok) { - qWarning() << "Icon convert to String failed."; - return {}; - } - - return icon; -} - -QString ApplicationService::displayName() const noexcept -{ - if (m_entry.isNull()) { - qWarning() << "desktop entry is empty, isPersistence:" << m_isPersistence; - return {}; - } - - const auto &actions = m_entry->value(DesktopFileEntryKey, "Name"); - if (!actions) { - return {}; - } - - bool ok{false}; - const auto &name = actions->toString(ok); - if (!ok) { - qWarning() << "Icon convert to String failed."; - return {}; - } - - return name; -} - bool ApplicationService::addOneInstance(const QString &instanceId, const QString &application, const QString &systemdUnitPath) { auto service = new InstanceService{instanceId, application, systemdUnitPath}; diff --git a/src/dbus/applicationservice.h b/src/dbus/applicationservice.h index 5eb5128..11b246f 100644 --- a/src/dbus/applicationservice.h +++ b/src/dbus/applicationservice.h @@ -48,12 +48,6 @@ public: Q_PROPERTY(QList Instances READ instances) [[nodiscard]] QList instances() const noexcept; - Q_PROPERTY(QString IconName READ iconName CONSTANT) - [[nodiscard]] QString iconName() const noexcept; - - Q_PROPERTY(QString DisplayName READ displayName CONSTANT) - [[nodiscard]] QString displayName() const noexcept; - [[nodiscard]] QDBusObjectPath findInstance(const QString &instanceId) const; [[nodiscard]] const QString &getLauncher() const noexcept { return m_launcher; } @@ -65,8 +59,10 @@ public: void removeAllInstance(); public Q_SLOTS: - QString GetActionName(const QString &identifier, const QStringList &env); + [[nodiscard]] QString GetActionName(const QString &identifier, const QStringList &env) const; QDBusObjectPath Launch(QString action, QStringList fields, QVariantMap options); + [[nodiscard]] QString GetIconName(const QString &action) const; + [[nodiscard]] QString GetDisplayName(const QStringList &env) const; [[nodiscard]] ObjectMap GetManagedObjects() const; Q_SIGNALS: diff --git a/src/dbus/jobservice.cpp b/src/dbus/jobservice.cpp index 4549195..d9eae30 100644 --- a/src/dbus/jobservice.cpp +++ b/src/dbus/jobservice.cpp @@ -11,27 +11,29 @@ JobService::JobService(const QFuture &job) JobService::~JobService() = default; -QString JobService::status() const // FIXME: job status aren't mutually exclusive +QString JobService::status() const { - if (m_job.isFinished()) { - return "finished"; - } - if (m_job.isCanceled()) { - return "canceled"; - } - if (m_job.isSuspended()) { - return "suspended"; - } - if (m_job.isSuspending()) { - return "suspending"; - } if (m_job.isStarted()) { - return "started"; + if (m_job.isRunning()) { + if (m_job.isSuspending()) { + return "suspending"; + } + if (m_job.isSuspended()) { + return "suspended"; + } + return "running"; + } + + if (m_job.isCanceled()) { + return "canceled"; + } + + if (m_job.isFinished()) { + return "finished"; + } } - if (m_job.isRunning()) { - return "running"; - } - Q_UNREACHABLE(); + + return "pending"; } void JobService::Cancel() diff --git a/src/global.h b/src/global.h index 4271cb6..041e077 100644 --- a/src/global.h +++ b/src/global.h @@ -361,4 +361,17 @@ inline QPair processUnitName(const QString &unitName) return qMakePair(unescapeApplicationId(applicationId), std::move(instanceId)); } +template +ObjectMap dumpDBusObject(const QMap> &map) +{ + ObjectMap objs; + + for (const auto &[key, value] : map.asKeyValueRange()) { + auto interfaces = getInterfacesListFromObject(value.data()); + objs.insert(key, interfaces); + } + + return objs; +} + #endif diff --git a/tests/ut_job.cpp b/tests/ut_job.cpp new file mode 100644 index 0000000..80522e4 --- /dev/null +++ b/tests/ut_job.cpp @@ -0,0 +1,104 @@ +// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd. +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "dbus/jobservice.h" +#include +#include +#include +#include +#include +#include + +using namespace std::chrono_literals; + +class TestJob : public testing::Test +{ +public: + void SetUp() override + { + QPromise promise; + m_jobService = new JobService{promise.future()}; + + m_thread = QThread::create( + [](QPromise promise) { + std::this_thread::sleep_for(2ms); + promise.start(); + std::this_thread::sleep_for(1ms); + if (promise.isCanceled()) { + return; + } + + std::this_thread::sleep_for(1ms); + promise.suspendIfRequested(); + std::this_thread::sleep_for(1ms); + + std::size_t x{0}; + for (std::size_t i = 0; i < 20000; ++i) { + x += 1; + } + + promise.addResult(QVariantList{QVariant::fromValue(x)}); + promise.finish(); + }, + std::move(promise)); + } + + void TearDown() override + { + m_thread->quit(); + m_thread->deleteLater(); + m_thread = nullptr; + + m_jobService->deleteLater(); + m_jobService = nullptr; + } + + QThread *m_thread{nullptr}; + JobService *m_jobService{nullptr}; +}; + +TEST_F(TestJob, cancelJob) +{ + m_thread->start(); + + std::this_thread::sleep_for(1ms); + EXPECT_EQ(m_jobService->status().toStdString(), std::string{"pending"}); + std::this_thread::sleep_for(2ms); + EXPECT_EQ(m_jobService->status().toStdString(), std::string{"running"}); + + m_jobService->Cancel(); + + std::this_thread::sleep_for(1ms); + + EXPECT_EQ(m_jobService->status().toStdString(), std::string{"canceled"}); +} + +TEST_F(TestJob, suspendAndResumeJob) +{ + m_thread->start(); + + std::this_thread::sleep_for(3ms); + + EXPECT_EQ(m_jobService->status().toStdString(), std::string{"running"}); + + m_jobService->Suspend(); + + std::this_thread::sleep_for(1ms); + + EXPECT_EQ(m_jobService->status().toStdString(), std::string{"suspending"}); + std::this_thread::sleep_for(1ms); + EXPECT_EQ(m_jobService->status().toStdString(), std::string{"suspended"}); + m_jobService->Resume(); + + std::this_thread::sleep_for(1ms); + EXPECT_EQ(m_jobService->status().toStdString(), std::string{"running"}); + + EXPECT_TRUE(m_thread->wait()); + EXPECT_EQ(m_jobService->status().toStdString(), std::string{"finished"}); + + auto ret = m_jobService->m_job.result(); + EXPECT_FALSE(ret.isEmpty()); + + EXPECT_EQ(ret.first().value(), std::size_t{20000}); +} diff --git a/tests/ut_jobmanager.cpp b/tests/ut_jobmanager.cpp index c0300f6..54805f5 100644 --- a/tests/ut_jobmanager.cpp +++ b/tests/ut_jobmanager.cpp @@ -10,7 +10,7 @@ class TestJobManager : public testing::Test public: static void SetUpTestCase() { m_jobManager = new JobManager1Service(nullptr); } - static void TearDownTestCase() { delete m_jobManager; } + static void TearDownTestCase() { m_jobManager->deleteLater(); } JobManager1Service &service() { return *m_jobManager; } private: