refact: change dbus api and service impl

1. change IconName and DisplayName to method
2. refactor impl of GetManagedObject and move it to global.h
3. refactor impl of JobService::status
4. add ut_job.cpp

Signed-off-by: ComixHe <heyuming@deepin.org>
This commit is contained in:
ComixHe 2023-08-18 17:22:38 +08:00 committed by Comix
parent d49a99d252
commit 0e7d84f61d
9 changed files with 225 additions and 92 deletions

View File

@ -17,10 +17,6 @@
<property name="AutoStart" type="b" access="readwrite"/>
<property name="IconName" type="s" access="read"/>
<property name="DisplayName" type="s" access="read"/>
<property name="Instances" type="ao" access="read">
<annotation
name="org.freedesktop.DBus.Description"
@ -102,5 +98,23 @@
this method will use the locale config of that user."
/>
</method>
<method name="GetIconName">
<arg name="action" type="s" direction="in"/>
<arg name="name" type="s" direction="out"/>
<annotation
name="org.freedesktop.DBus.Description"
value="The meaning of arg `env` is same as which in GetActionName."
/>
</method>
<method name="GetDisplayName">
<arg name="env" type="as" direction="in"/>
<arg name="name" type="s" direction="out"/>
<annotation
name="org.freedesktop.DBus.Description"
value="The meaning of arg `env` is same as which in GetActionName."
/>
</method>
</interface>
</node>

View File

@ -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);
}

View File

@ -83,7 +83,8 @@ private:
};
template <typename T>
QSharedPointer<ApplicationService> makeApplication(T &&source, ApplicationManager1Service *parent)
QSharedPointer<ApplicationService> makeApplication(T &&source,
ApplicationManager1Service *parent) // NOTE: maybe we should refactor
{
static_assert(std::is_same_v<T, DesktopFile> or std::is_same_v<T, QString>, "param type must be QString or DesktopFile.");
QString objectPath;

View File

@ -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<DesktopEntry::Value> 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<QDBusObjectPath> 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};

View File

@ -48,12 +48,6 @@ public:
Q_PROPERTY(QList<QDBusObjectPath> Instances READ instances)
[[nodiscard]] QList<QDBusObjectPath> 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:

View File

@ -11,27 +11,29 @@ JobService::JobService(const QFuture<QVariantList> &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()

View File

@ -361,4 +361,17 @@ inline QPair<QString, QString> processUnitName(const QString &unitName)
return qMakePair(unescapeApplicationId(applicationId), std::move(instanceId));
}
template <typename T>
ObjectMap dumpDBusObject(const QMap<QDBusObjectPath, QSharedPointer<T>> &map)
{
ObjectMap objs;
for (const auto &[key, value] : map.asKeyValueRange()) {
auto interfaces = getInterfacesListFromObject(value.data());
objs.insert(key, interfaces);
}
return objs;
}
#endif

104
tests/ut_job.cpp Normal file
View File

@ -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 <gtest/gtest.h>
#include <QFuture>
#include <QPromise>
#include <thread>
#include <chrono>
#include <QSemaphore>
using namespace std::chrono_literals;
class TestJob : public testing::Test
{
public:
void SetUp() override
{
QPromise<QVariantList> promise;
m_jobService = new JobService{promise.future()};
m_thread = QThread::create(
[](QPromise<QVariantList> 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<std::size_t>(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>(), std::size_t{20000});
}

View File

@ -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: