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: