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: