feat: support application hooks
Signed-off-by: ComixHe <heyuming@deepin.org>
This commit is contained in:
parent
f233279466
commit
fb0fc0a8ee
29
docs/applicationHooks.md
Normal file
29
docs/applicationHooks.md
Normal file
@ -0,0 +1,29 @@
|
||||
# Application Hooks
|
||||
|
||||
本文档描述了 dde-application-manager 的hook机制。
|
||||
|
||||
## 功能描述
|
||||
|
||||
hook 允许系统组件在应用启动前对应用的运行时环境做出配置(如cgroups)。
|
||||
|
||||
## 配置文件
|
||||
|
||||
hook 的配置文件需要放在'/usr/share/deepin/dde-application-manager/hook.d/'下,文件名必须符合以下规范:
|
||||
- 以数字开头,作为hook的顺序标识。
|
||||
- 以`-`分割顺序和hook名。
|
||||
- 文件格式需要是`json`,文件扩展名同样以`json`结尾。
|
||||
|
||||
例如: `1-proxy.json`就是一个符合要求的hook配置文件。
|
||||
|
||||
### 文件格式
|
||||
|
||||
文件中需要写明hook二进制的绝对位置和所需要的参数,例如:
|
||||
|
||||
```json
|
||||
{
|
||||
"Exec": "/usr/bin/proxy",
|
||||
"Args": ["--protocol=https","--port=12345"]
|
||||
}
|
||||
```
|
||||
|
||||
需要注意的是,配置文件的键是大小写敏感的。
|
66
src/applicationHooks.cpp
Normal file
66
src/applicationHooks.cpp
Normal file
@ -0,0 +1,66 @@
|
||||
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#include "applicationHooks.h"
|
||||
#include <QFile>
|
||||
#include <QDir>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonParseError>
|
||||
|
||||
std::optional<ApplicationHook> ApplicationHook::loadFromFile(const QString &filePath) noexcept
|
||||
{
|
||||
auto index = filePath.lastIndexOf(QDir::separator());
|
||||
auto fileName = filePath.last(filePath.size() - index - 1);
|
||||
|
||||
QFile file{filePath};
|
||||
if (!file.open(QFile::Text | QFile::ReadOnly | QFile::ExistingOnly)) {
|
||||
qWarning() << "open hook file:" << filePath << "failed:" << file.errorString() << ", skip.";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto content = file.readAll();
|
||||
QJsonParseError err;
|
||||
auto json = QJsonDocument::fromJson(content, &err);
|
||||
if (err.error != QJsonParseError::NoError) {
|
||||
qWarning() << "parse hook failed:" << err.errorString();
|
||||
return std::nullopt;
|
||||
}
|
||||
if (json.isEmpty()) {
|
||||
qWarning() << "empty hook, skip.";
|
||||
return std::nullopt;
|
||||
}
|
||||
auto obj = json.object();
|
||||
|
||||
auto it = obj.constFind("Exec");
|
||||
if (it == obj.constEnd()) {
|
||||
qWarning() << "invalid hook: lack of Exec.";
|
||||
return std::nullopt;
|
||||
}
|
||||
auto exec = it->toString();
|
||||
QFileInfo info{exec};
|
||||
if (!(info.exists() and info.isExecutable())) {
|
||||
qWarning() << "exec maybe doesn't exists or be executed.";
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
it = obj.constFind("Args");
|
||||
if (it == obj.constEnd()) {
|
||||
qWarning() << "invalid hook: lack of Args.";
|
||||
return std::nullopt;
|
||||
}
|
||||
auto args = it->toVariant().toStringList();
|
||||
|
||||
return ApplicationHook{std::move(fileName), std::move(exec), std::move(args)};
|
||||
}
|
||||
|
||||
QStringList generateHooks(const QList<ApplicationHook> &hooks) noexcept
|
||||
{
|
||||
QStringList ret;
|
||||
for (const auto &hook : hooks) {
|
||||
ret.append(hook.execPath());
|
||||
ret.append(hook.args());
|
||||
}
|
||||
return ret;
|
||||
}
|
39
src/applicationHooks.h
Normal file
39
src/applicationHooks.h
Normal file
@ -0,0 +1,39 @@
|
||||
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#ifndef APPLICATIONHOOKS_H
|
||||
#define APPLICATIONHOOKS_H
|
||||
|
||||
#include <optional>
|
||||
#include <QStringList>
|
||||
|
||||
class ApplicationHook
|
||||
{
|
||||
public:
|
||||
static std::optional<ApplicationHook> loadFromFile(const QString &filePath) noexcept;
|
||||
|
||||
[[nodiscard]] const QString &hookName() const noexcept { return m_hookName; }
|
||||
[[nodiscard]] const QString &execPath() const noexcept { return m_execPath; }
|
||||
[[nodiscard]] const QStringList &args() const noexcept { return m_args; }
|
||||
|
||||
friend bool operator>(const ApplicationHook &lhs, const ApplicationHook &rhs) { return lhs.m_hookName > rhs.m_hookName; };
|
||||
friend bool operator<(const ApplicationHook &lhs, const ApplicationHook &rhs) { return lhs.m_hookName < rhs.m_hookName; };
|
||||
friend bool operator==(const ApplicationHook &lhs, const ApplicationHook &rhs) { return lhs.m_hookName == rhs.m_hookName; };
|
||||
friend bool operator!=(const ApplicationHook &lhs, const ApplicationHook &rhs) { return !(lhs == rhs); };
|
||||
|
||||
private:
|
||||
ApplicationHook(QString &&hookName, QString &&execPath, QStringList &&args)
|
||||
: m_hookName(hookName)
|
||||
, m_execPath(std::move(execPath))
|
||||
, m_args(std::move(args))
|
||||
{
|
||||
}
|
||||
QString m_hookName;
|
||||
QString m_execPath;
|
||||
QStringList m_args;
|
||||
};
|
||||
|
||||
QStringList generateHooks(const QList<ApplicationHook> &hooks) noexcept;
|
||||
|
||||
#endif
|
@ -51,4 +51,6 @@ constexpr auto STORAGE_VERSION = 0;
|
||||
constexpr auto ApplicationPropertiesGroup = u8"Application Properties";
|
||||
constexpr auto LastLaunchedTime = u8"LastLaunchedTime";
|
||||
|
||||
constexpr auto ApplicationManagerHookDir = u8"/deepin/dde-application-manager/hooks.d";
|
||||
|
||||
#endif
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include "dbus/AMobjectmanager1adaptor.h"
|
||||
#include "systemdsignaldispatcher.h"
|
||||
#include "propertiesForwarder.h"
|
||||
#include "applicationHooks.h"
|
||||
#include <QFile>
|
||||
#include <QDBusMessage>
|
||||
#include <unistd.h>
|
||||
@ -70,6 +71,8 @@ void ApplicationManager1Service::initService(QDBusConnection &connection) noexce
|
||||
|
||||
scanInstances();
|
||||
|
||||
loadHooks();
|
||||
|
||||
if (auto *ptr = new (std::nothrow) PropertiesForwarder{DDEApplicationManager1ObjectPath, this}; ptr == nullptr) {
|
||||
qCritical() << "new PropertiesForwarder of Application Manager failed.";
|
||||
}
|
||||
@ -209,18 +212,23 @@ void ApplicationManager1Service::scanApplications() noexcept
|
||||
{
|
||||
const auto &desktopFileDirs = getDesktopFileDirs();
|
||||
|
||||
applyIteratively(QList<QDir>(desktopFileDirs.cbegin(), desktopFileDirs.cend()), [this](const QFileInfo &info) -> bool {
|
||||
DesktopErrorCode err{DesktopErrorCode::NoError};
|
||||
auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err);
|
||||
if (!ret.has_value()) {
|
||||
qWarning() << "failed to search File:" << err;
|
||||
return false;
|
||||
}
|
||||
if (!this->addApplication(std::move(ret).value())) {
|
||||
qWarning() << "add Application failed, skip...";
|
||||
}
|
||||
return false; // means to apply this function to the rest of the files
|
||||
});
|
||||
applyIteratively(
|
||||
QList<QDir>(desktopFileDirs.cbegin(), desktopFileDirs.cend()),
|
||||
[this](const QFileInfo &info) -> bool {
|
||||
DesktopErrorCode err{DesktopErrorCode::NoError};
|
||||
auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err);
|
||||
if (!ret.has_value()) {
|
||||
qWarning() << "failed to search File:" << err;
|
||||
return false;
|
||||
}
|
||||
if (!this->addApplication(std::move(ret).value())) {
|
||||
qWarning() << "add Application failed, skip...";
|
||||
}
|
||||
return false; // means to apply this function to the rest of the files
|
||||
},
|
||||
QDir::Readable | QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot,
|
||||
{"*.desktop"},
|
||||
QDir::Name | QDir::DirsLast);
|
||||
}
|
||||
|
||||
void ApplicationManager1Service::scanInstances() noexcept
|
||||
@ -250,12 +258,17 @@ void ApplicationManager1Service::scanAutoStart() noexcept
|
||||
{
|
||||
auto autostartDirs = getAutoStartDirs();
|
||||
QStringList needToLaunch;
|
||||
applyIteratively(QList<QDir>{autostartDirs.cbegin(), autostartDirs.cend()}, [&needToLaunch](const QFileInfo &info) {
|
||||
if (info.isSymbolicLink()) {
|
||||
needToLaunch.emplace_back(info.symLinkTarget());
|
||||
}
|
||||
return false;
|
||||
});
|
||||
applyIteratively(
|
||||
QList<QDir>{autostartDirs.cbegin(), autostartDirs.cend()},
|
||||
[&needToLaunch](const QFileInfo &info) {
|
||||
if (info.isSymbolicLink()) {
|
||||
needToLaunch.emplace_back(info.symLinkTarget());
|
||||
}
|
||||
return false;
|
||||
},
|
||||
QDir::Readable | QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot,
|
||||
{"*.desktop"},
|
||||
QDir::Name | QDir::DirsLast);
|
||||
|
||||
while (!needToLaunch.isEmpty()) {
|
||||
const auto &filePath = needToLaunch.takeFirst();
|
||||
@ -269,6 +282,32 @@ void ApplicationManager1Service::scanAutoStart() noexcept
|
||||
}
|
||||
}
|
||||
|
||||
void ApplicationManager1Service::loadHooks() noexcept
|
||||
{
|
||||
auto hookDirs = getXDGDataDirs();
|
||||
std::for_each(hookDirs.begin(), hookDirs.end(), [](QString &str) { str.append(ApplicationManagerHookDir); });
|
||||
QHash<QString, ApplicationHook> hooks;
|
||||
|
||||
applyIteratively(
|
||||
QList<QDir>(hookDirs.begin(), hookDirs.end()),
|
||||
[&hooks](const QFileInfo &info) -> bool {
|
||||
auto fileName = info.fileName();
|
||||
if (!hooks.contains(fileName)) {
|
||||
if (auto hook = ApplicationHook::loadFromFile(info.absoluteFilePath()); hook) {
|
||||
hooks.insert(fileName, std::move(hook).value());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
},
|
||||
QDir::Files | QDir::NoDotAndDotDot | QDir::NoSymLinks | QDir::Readable,
|
||||
{"*.json"},
|
||||
QDir::Name);
|
||||
|
||||
auto hookList = hooks.values();
|
||||
std::sort(hookList.begin(), hookList.end());
|
||||
m_hookElements = generateHooks(hookList);
|
||||
}
|
||||
|
||||
QList<QDBusObjectPath> ApplicationManager1Service::list() const
|
||||
{
|
||||
return m_applicationList.keys();
|
||||
@ -433,34 +472,39 @@ void ApplicationManager1Service::ReloadApplications()
|
||||
|
||||
auto apps = m_applicationList.keys();
|
||||
|
||||
applyIteratively(QList<QDir>(desktopFileDirs.cbegin(), desktopFileDirs.cend()), [this, &apps](const QFileInfo &info) -> bool {
|
||||
DesktopErrorCode err{DesktopErrorCode::NoError};
|
||||
auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err);
|
||||
if (!ret.has_value()) {
|
||||
applyIteratively(
|
||||
QList<QDir>(desktopFileDirs.cbegin(), desktopFileDirs.cend()),
|
||||
[this, &apps](const QFileInfo &info) -> bool {
|
||||
DesktopErrorCode err{DesktopErrorCode::NoError};
|
||||
auto ret = DesktopFile::searchDesktopFileByPath(info.absoluteFilePath(), err);
|
||||
if (!ret.has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file = std::move(ret).value();
|
||||
|
||||
auto destApp =
|
||||
std::find_if(m_applicationList.cbegin(),
|
||||
m_applicationList.cend(),
|
||||
[&file](const QSharedPointer<ApplicationService> &app) { return file.desktopId() == app->id(); });
|
||||
|
||||
if (err != DesktopErrorCode::NoError) {
|
||||
qWarning() << "error occurred:" << err << " skip this application.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (destApp != m_applicationList.cend() and apps.contains(destApp.key())) {
|
||||
apps.removeOne(destApp.key());
|
||||
updateApplication(destApp.value(), std::move(file));
|
||||
return false;
|
||||
}
|
||||
|
||||
addApplication(std::move(file));
|
||||
return false;
|
||||
}
|
||||
|
||||
auto file = std::move(ret).value();
|
||||
|
||||
auto destApp =
|
||||
std::find_if(m_applicationList.cbegin(),
|
||||
m_applicationList.cend(),
|
||||
[&file](const QSharedPointer<ApplicationService> &app) { return file.desktopId() == app->id(); });
|
||||
|
||||
if (err != DesktopErrorCode::NoError) {
|
||||
qWarning() << "error occurred:" << err << " skip this application.";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (destApp != m_applicationList.cend() and apps.contains(destApp.key())) {
|
||||
apps.removeOne(destApp.key());
|
||||
updateApplication(destApp.value(), std::move(file));
|
||||
return false;
|
||||
}
|
||||
|
||||
addApplication(std::move(file));
|
||||
return false;
|
||||
});
|
||||
},
|
||||
QDir::Readable | QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot,
|
||||
{"*.desktop"},
|
||||
QDir::Name | QDir::DirsLast);
|
||||
|
||||
for (const auto &key : apps) {
|
||||
removeOneApplication(key);
|
||||
|
@ -41,7 +41,9 @@ public:
|
||||
|
||||
void updateApplication(const QSharedPointer<ApplicationService> &destApp, DesktopFile desktopFile) noexcept;
|
||||
|
||||
JobManager1Service &jobManager() noexcept { return *m_jobManager; }
|
||||
[[nodiscard]] JobManager1Service &jobManager() noexcept { return *m_jobManager; }
|
||||
[[nodiscard]] const JobManager1Service &jobManager() const noexcept { return *m_jobManager; }
|
||||
[[nodiscard]] const QStringList &applicationHooks() const noexcept { return m_hookElements; }
|
||||
|
||||
public Q_SLOTS:
|
||||
QString Identify(const QDBusUnixFileDescriptor &pidfd,
|
||||
@ -59,11 +61,13 @@ private:
|
||||
std::unique_ptr<Identifier> m_identifier;
|
||||
std::weak_ptr<ApplicationManager1Storage> m_storage;
|
||||
QScopedPointer<JobManager1Service> m_jobManager{nullptr};
|
||||
QStringList m_hookElements;
|
||||
QMap<QDBusObjectPath, QSharedPointer<ApplicationService>> m_applicationList;
|
||||
|
||||
void scanApplications() noexcept;
|
||||
void scanInstances() noexcept;
|
||||
void scanAutoStart() noexcept;
|
||||
void loadHooks() noexcept;
|
||||
void addInstanceToApplication(const QString &unitName, const QDBusObjectPath &systemdUnitPath);
|
||||
void removeInstanceFromApplication(const QString &unitName, const QDBusObjectPath &systemdUnitPath);
|
||||
};
|
||||
|
@ -180,6 +180,10 @@ QDBusObjectPath ApplicationService::Launch(const QString &action, const QStringL
|
||||
}
|
||||
}
|
||||
|
||||
optionsMap.remove("_hooks"); // this is internal property, user shouldn't pass it to Application Manager
|
||||
if (const auto &hooks = parent()->applicationHooks(); !hooks.isEmpty()) {
|
||||
optionsMap.insert("_hooks", hooks);
|
||||
}
|
||||
auto cmds = generateCommand(optionsMap);
|
||||
|
||||
auto [bin, execCmds, res] = unescapeExec(execStr, fields);
|
||||
@ -190,7 +194,7 @@ QDBusObjectPath ApplicationService::Launch(const QString &action, const QStringL
|
||||
}
|
||||
|
||||
cmds.append(std::move(execCmds));
|
||||
auto &jobManager = static_cast<ApplicationManager1Service *>(parent())->jobManager();
|
||||
auto &jobManager = parent()->jobManager();
|
||||
return jobManager.addJob(
|
||||
m_applicationPath.path(),
|
||||
[this, binary = std::move(bin), commands = std::move(cmds)](const QVariant &variantValue) mutable -> QVariant {
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
#include "applicationmanagerstorage.h"
|
||||
#include "dbus/applicationmanager1service.h"
|
||||
#include "dbus/instanceservice.h"
|
||||
#include "global.h"
|
||||
#include "desktopentry.h"
|
||||
@ -151,6 +152,11 @@ private:
|
||||
void updateAfterLaunch(bool isLaunch) noexcept;
|
||||
static bool shouldBeShown(const std::unique_ptr<DesktopEntry> &entry) noexcept;
|
||||
[[nodiscard]] bool autostartCheck(const QString &linkPath) const noexcept;
|
||||
[[nodiscard]] ApplicationManager1Service *parent() { return dynamic_cast<ApplicationManager1Service *>(QObject::parent()); }
|
||||
[[nodiscard]] const ApplicationManager1Service *parent() const
|
||||
{
|
||||
return dynamic_cast<ApplicationManager1Service *>(QObject::parent());
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
|
18
src/global.h
18
src/global.h
@ -79,7 +79,11 @@ template <typename T>
|
||||
using remove_cvr_t = std::remove_reference_t<std::remove_cv_t<T>>;
|
||||
|
||||
template <typename T>
|
||||
void applyIteratively(QList<QDir> dirs, T &&func)
|
||||
void applyIteratively(QList<QDir> dirs,
|
||||
T &&func,
|
||||
QDir::Filters filter = QDir::NoFilter,
|
||||
QStringList nameFilter = {},
|
||||
QDir::SortFlags sortFlag = QDir::SortFlag::NoSort)
|
||||
{
|
||||
static_assert(std::is_invocable_v<T, const QFileInfo &>, "apply function should only accept one QFileInfo");
|
||||
static_assert(std::is_same_v<decltype(func(QFileInfo{})), bool>,
|
||||
@ -93,9 +97,7 @@ void applyIteratively(QList<QDir> dirs, T &&func)
|
||||
qWarning() << "apply function to an non-existent directory:" << dir.absolutePath() << ", skip.";
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto &infoList = dir.entryInfoList(
|
||||
{"*.desktop"}, QDir::Readable | QDir::AllDirs | QDir::Files | QDir::NoDotAndDotDot, QDir::Name | QDir::DirsLast);
|
||||
const auto &infoList = dir.entryInfoList(nameFilter, filter, sortFlag);
|
||||
|
||||
for (const auto &info : infoList) {
|
||||
if (info.isFile() and func(info)) {
|
||||
@ -374,7 +376,7 @@ inline QString getXDGDataHome()
|
||||
return XDGDataHome;
|
||||
}
|
||||
|
||||
inline QStringList getDesktopFileDirs()
|
||||
inline QStringList getXDGDataDirs()
|
||||
{
|
||||
auto XDGDataDirs = QString::fromLocal8Bit(qgetenv("XDG_DATA_DIRS")).split(':', Qt::SkipEmptyParts);
|
||||
|
||||
@ -384,14 +386,18 @@ inline QStringList getDesktopFileDirs()
|
||||
}
|
||||
|
||||
XDGDataDirs.push_front(getXDGDataHome());
|
||||
return XDGDataDirs;
|
||||
}
|
||||
|
||||
inline QStringList getDesktopFileDirs()
|
||||
{
|
||||
auto XDGDataDirs = getXDGDataDirs();
|
||||
std::for_each(XDGDataDirs.begin(), XDGDataDirs.end(), [](QString &str) {
|
||||
if (!str.endsWith(QDir::separator())) {
|
||||
str.append(QDir::separator());
|
||||
}
|
||||
str.append("applications");
|
||||
});
|
||||
|
||||
return XDGDataDirs;
|
||||
}
|
||||
|
||||
|
@ -11,12 +11,14 @@
|
||||
QStringList generateCommand(const QVariantMap &props) noexcept
|
||||
{
|
||||
std::vector<std::unique_ptr<LaunchOption>> options;
|
||||
for (auto it = props.constKeyValueBegin(); it!= props.constKeyValueEnd(); ++it) {
|
||||
for (auto it = props.constKeyValueBegin(); it != props.constKeyValueEnd(); ++it) {
|
||||
const auto &[key, value] = *it;
|
||||
if (key == setUserLaunchOption::key()) {
|
||||
options.push_back(std::make_unique<setUserLaunchOption>(value));
|
||||
options.emplace_back(std::make_unique<setUserLaunchOption>(value));
|
||||
} else if (key == setEnvLaunchOption::key()) {
|
||||
options.push_back(std::make_unique<setEnvLaunchOption>(value));
|
||||
options.emplace_back(std::make_unique<setEnvLaunchOption>(value));
|
||||
} else if (key == hookLaunchOption::key()) {
|
||||
options.emplace_back(std::make_unique<hookLaunchOption>(value));
|
||||
} else {
|
||||
qWarning() << "unsupported options" << key;
|
||||
}
|
||||
@ -27,6 +29,10 @@ QStringList generateCommand(const QVariantMap &props) noexcept
|
||||
std::sort(options.begin(),
|
||||
options.end(),
|
||||
[](const std::unique_ptr<LaunchOption> &lOption, const std::unique_ptr<LaunchOption> &rOption) {
|
||||
if (lOption->type() == rOption->type()) {
|
||||
return lOption->m_priority >= rOption->m_priority;
|
||||
}
|
||||
|
||||
if (lOption->type() == AppExecOption and rOption->type() == systemdOption) {
|
||||
return false;
|
||||
}
|
||||
|
@ -20,7 +20,11 @@ struct LaunchOption
|
||||
};
|
||||
[[nodiscard]] virtual const QString &type() const noexcept = 0;
|
||||
|
||||
uint32_t m_priority{0};
|
||||
QVariant m_val;
|
||||
|
||||
protected:
|
||||
LaunchOption() = default;
|
||||
};
|
||||
|
||||
struct setUserLaunchOption : public LaunchOption
|
||||
@ -70,4 +74,24 @@ struct splitLaunchOption : public LaunchOption
|
||||
[[nodiscard]] QStringList generateCommandLine() const noexcept override;
|
||||
};
|
||||
|
||||
struct hookLaunchOption : public LaunchOption
|
||||
{
|
||||
explicit hookLaunchOption(QVariant v)
|
||||
: LaunchOption(std::move(v))
|
||||
{
|
||||
m_priority = 1;
|
||||
}
|
||||
[[nodiscard]] const QString &type() const noexcept override
|
||||
{
|
||||
static QString tp{AppExecOption};
|
||||
return tp;
|
||||
}
|
||||
[[nodiscard]] static const QString &key() noexcept
|
||||
{
|
||||
static QString hook{"hook"};
|
||||
return hook;
|
||||
}
|
||||
[[nodiscard]] QStringList generateCommandLine() const noexcept override { return m_val.toStringList(); };
|
||||
};
|
||||
|
||||
QStringList generateCommand(const QVariantMap &props) noexcept;
|
||||
|
6
tests/data/hooks.d/1-test.json
Normal file
6
tests/data/hooks.d/1-test.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"Exec": "/usr/bin/echo",
|
||||
"Args": [
|
||||
"for test"
|
||||
]
|
||||
}
|
25
tests/ut_hook.cpp
Normal file
25
tests/ut_hook.cpp
Normal file
@ -0,0 +1,25 @@
|
||||
// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: LGPL-3.0-or-later
|
||||
|
||||
#include "applicationHooks.h"
|
||||
#include <QDir>
|
||||
#include <QStringList>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
TEST(ApplicationHookTest, load)
|
||||
{
|
||||
auto file =
|
||||
QDir::currentPath() + QDir::separator() + "data" + QDir::separator() + "hooks.d" + QDir::separator() + "1-test.json";
|
||||
auto hook = ApplicationHook::loadFromFile(file);
|
||||
EXPECT_TRUE(hook);
|
||||
EXPECT_EQ(hook->hookName(), QString{"1-test.json"});
|
||||
EXPECT_EQ(hook->execPath(), QString{"/usr/bin/echo"});
|
||||
|
||||
QStringList tmp{"for test"};
|
||||
EXPECT_EQ(hook->args(), tmp);
|
||||
|
||||
tmp.push_front("/usr/bin/echo");
|
||||
auto elem = generateHooks({std::move(hook).value()});
|
||||
EXPECT_EQ(elem, tmp);
|
||||
}
|
Loading…
Reference in New Issue
Block a user