diff --git a/api/dbus/org.desktopspec.ApplicationManager1.Application.xml b/api/dbus/org.desktopspec.ApplicationManager1.Application.xml
index cf18f2f..a73205b 100644
--- a/api/dbus/org.desktopspec.ApplicationManager1.Application.xml
+++ b/api/dbus/org.desktopspec.ApplicationManager1.Application.xml
@@ -44,7 +44,7 @@
@@ -56,10 +56,10 @@
/>
-
+
@@ -69,7 +69,7 @@
name="org.freedesktop.DBus.Description"
value="The type of IconName is a Map, where the key represents the action and the value is the corresponding content."
/>
-
+
@@ -77,7 +77,7 @@
name="org.freedesktop.DBus.Description"
value="The meaning of this property's type is same as which in ActionName."
/>
-
+
@@ -85,7 +85,7 @@
name="org.freedesktop.DBus.Description"
value="The meaning of this property's type is same as which in ActionName."
/>
-
+
diff --git a/api/dbus/org.desktopspec.ApplicationManager1.xml b/api/dbus/org.desktopspec.ApplicationManager1.xml
index 2e31463..2dd570a 100644
--- a/api/dbus/org.desktopspec.ApplicationManager1.xml
+++ b/api/dbus/org.desktopspec.ApplicationManager1.xml
@@ -30,5 +30,20 @@
1. You should use pidfd_open(2) to get a pidfd."
/>
+
+
+
+
+
+
+
diff --git a/src/constant.h b/src/constant.h
index f449603..4f3e707 100644
--- a/src/constant.h
+++ b/src/constant.h
@@ -21,6 +21,7 @@ constexpr auto DDEApplicationManager1JobManager1ObjectPath = u8"/org/desktopspec
constexpr auto DDEApplicationManager1MimeManager1ObjectPath = u8"/org/desktopspec/ApplicationManager1/MimeManager1";
constexpr auto DesktopFileEntryKey = u8"Desktop Entry";
constexpr auto DesktopFileActionKey = u8"Desktop Action ";
+constexpr auto DesktopFileDefaultKeyLocale = "default";
constexpr auto ApplicationManagerServerDBusName =
#ifdef DDE_AM_USE_DEBUG_DBUS_NAME
@@ -52,7 +53,7 @@ constexpr auto AppExecOption = u8"appExec";
constexpr auto STORAGE_VERSION = 0;
constexpr auto ApplicationPropertiesGroup = u8"Application Properties";
constexpr auto LastLaunchedTime = u8"LastLaunchedTime";
-constexpr auto ScaleFactor=u8"ScaleFactor";
+constexpr auto ScaleFactor = u8"ScaleFactor";
constexpr auto ApplicationManagerHookDir = u8"/deepin/dde-application-manager/hooks.d";
diff --git a/src/dbus/applicationmanager1service.cpp b/src/dbus/applicationmanager1service.cpp
index 32d2364..1608a6f 100644
--- a/src/dbus/applicationmanager1service.cpp
+++ b/src/dbus/applicationmanager1service.cpp
@@ -8,6 +8,7 @@
#include "systemdsignaldispatcher.h"
#include "propertiesForwarder.h"
#include "applicationHooks.h"
+#include "desktopfilegenerator.h"
#include
#include
#include
@@ -548,3 +549,67 @@ ApplicationManager1Service::findApplicationsByIds(const QStringList &appIds) con
return ret;
}
+
+QString ApplicationManager1Service::addUserApplication(const QVariantMap &desktop_file, const QString &name) noexcept
+{
+ if (name.isEmpty()) {
+ sendErrorReply(QDBusError::Failed, "file name is empty.");
+ return {};
+ }
+
+ QDir xdgDataHome{getXDGDataHome() + "/applications"};
+ const auto &filePath = xdgDataHome.filePath(name);
+
+ if (QFileInfo info{filePath}; info.exists() and info.isFile()) {
+ sendErrorReply(QDBusError::Failed, QString{"file already exists:%1"}.arg(info.absoluteFilePath()));
+ return {};
+ }
+
+ QFile file{filePath};
+ if (!file.open(QFile::NewOnly | QFile::WriteOnly | QFile::Text)) {
+ sendErrorReply(QDBusError::Failed, file.errorString());
+ return {};
+ }
+
+ QString errMsg;
+ auto fileContent = DesktopFileGenerator::generate(desktop_file, errMsg);
+ if (fileContent.isEmpty() or !errMsg.isEmpty()) {
+ file.remove();
+ sendErrorReply(QDBusError::Failed, errMsg);
+ return {};
+ }
+
+ auto writeContent = fileContent.toLocal8Bit();
+ if (file.write(writeContent) != writeContent.size()) {
+ file.remove();
+ sendErrorReply(QDBusError::Failed, "incomplete file content.this file will be removed.");
+ return {};
+ }
+
+ file.flush();
+
+ ParserError err{ParserError::NoError};
+ auto ret = DesktopFile::searchDesktopFileByPath(filePath, err);
+ if (err != ParserError::NoError) {
+ file.remove();
+ qDebug() << "add user's application failed:" << err;
+ sendErrorReply(QDBusError::Failed, "search failed.");
+ return {};
+ }
+
+ if (!ret) {
+ file.remove();
+ sendErrorReply(QDBusError::InternalError);
+ return {};
+ }
+
+ auto desktopSource = std::move(ret).value();
+ auto appId = desktopSource.desktopId();
+ if (!addApplication(std::move(desktopSource))) {
+ file.remove();
+ sendErrorReply(QDBusError::Failed, "add application to ApplicationManager failed.");
+ return {};
+ }
+
+ return appId;
+}
diff --git a/src/dbus/applicationmanager1service.h b/src/dbus/applicationmanager1service.h
index 5445d49..7eea1e1 100644
--- a/src/dbus/applicationmanager1service.h
+++ b/src/dbus/applicationmanager1service.h
@@ -20,7 +20,7 @@
class ApplicationService;
-class ApplicationManager1Service final : public QObject
+class ApplicationManager1Service final : public QObject, public QDBusContext
{
Q_OBJECT
public:
@@ -54,6 +54,7 @@ public Q_SLOTS:
QDBusObjectPath &instance,
ObjectInterfaceMap &application_instance_info) const noexcept;
void ReloadApplications();
+ QString addUserApplication(const QVariantMap &desktop_file, const QString &name) noexcept;
[[nodiscard]] ObjectMap GetManagedObjects() const;
Q_SIGNALS:
diff --git a/src/dbus/applicationservice.cpp b/src/dbus/applicationservice.cpp
index 9b74c91..cf1dcab 100644
--- a/src/dbus/applicationservice.cpp
+++ b/src/dbus/applicationservice.cpp
@@ -384,59 +384,64 @@ QStringList ApplicationService::categories() const noexcept
PropMap ApplicationService::actionName() const noexcept
{
PropMap ret;
- auto actionList = actions();
+ const auto &actionList = actions();
- for (auto &action : actionList) {
- action.prepend(DesktopFileActionKey);
- auto value = m_entry->value(action, "Name");
+ for (const auto &action : actionList) {
+ auto rawActionKey = DesktopFileActionKey % action;
+ auto value = m_entry->value(rawActionKey, "Name");
if (!value.has_value()) {
continue;
}
- ret.insert(action, std::move(value).value());
+ ret.insert(action, std::move(value).value().value());
}
return ret;
}
-PropMap ApplicationService::name() const noexcept
+QStringMap ApplicationService::name() const noexcept
{
- PropMap ret;
auto value = m_entry->value(DesktopFileEntryKey, "Name");
if (!value) {
- return ret;
+ return {};
}
- ret.insert(QString{"Name"}, {std::move(value).value()});
- return ret;
+ if (!value->canConvert()) {
+ return {};
+ }
+
+ return value->value();
}
-PropMap ApplicationService::genericName() const noexcept
+QStringMap ApplicationService::genericName() const noexcept
{
- PropMap ret;
auto value = m_entry->value(DesktopFileEntryKey, "GenericName");
if (!value) {
- return ret;
+ return {};
}
- ret.insert(QString{"GenericName"}, {std::move(value).value()});
- return ret;
+ if (!value->canConvert()) {
+ return {};
+ }
+
+ return value->value();
}
-PropMap ApplicationService::icons() const noexcept
+QStringMap ApplicationService::icons() const noexcept
{
- PropMap ret;
+ QStringMap ret;
auto actionList = actions();
for (const auto &action : actionList) {
- auto value = m_entry->value(QString{action}.prepend(DesktopFileActionKey), "Icon");
+ const auto &actionKey = QString{action}.prepend(DesktopFileActionKey);
+ auto value = m_entry->value(actionKey, "Icon");
if (!value.has_value()) {
continue;
}
- ret.insert(action, {std::move(value).value()});
+ ret.insert(actionKey, value->value());
}
auto mainIcon = m_entry->value(DesktopFileEntryKey, "Icon");
if (mainIcon.has_value()) {
- ret.insert(defaultKeyStr, {std::move(mainIcon).value()});
+ ret.insert(DesktopFileEntryKey, mainIcon->value());
}
return ret;
diff --git a/src/dbus/applicationservice.h b/src/dbus/applicationservice.h
index 2f8a190..f8e1f7a 100644
--- a/src/dbus/applicationservice.h
+++ b/src/dbus/applicationservice.h
@@ -50,14 +50,14 @@ public:
Q_PROPERTY(QString ID READ id CONSTANT)
[[nodiscard]] QString id() const noexcept;
- Q_PROPERTY(PropMap Name READ name NOTIFY nameChanged)
- [[nodiscard]] PropMap name() const noexcept;
+ Q_PROPERTY(QStringMap Name READ name NOTIFY nameChanged)
+ [[nodiscard]] QStringMap name() const noexcept;
- Q_PROPERTY(PropMap GenericName READ genericName NOTIFY genericNameChanged)
- [[nodiscard]] PropMap genericName() const noexcept;
+ Q_PROPERTY(QStringMap GenericName READ genericName NOTIFY genericNameChanged)
+ [[nodiscard]] QStringMap genericName() const noexcept;
- Q_PROPERTY(PropMap Icons READ icons NOTIFY iconsChanged)
- [[nodiscard]] PropMap icons() const noexcept;
+ Q_PROPERTY(QStringMap Icons READ icons NOTIFY iconsChanged)
+ [[nodiscard]] QStringMap icons() const noexcept;
Q_PROPERTY(qulonglong LastLaunchedTime READ lastLaunchedTime NOTIFY lastLaunchedTimeChanged)
[[nodiscard]] qulonglong lastLaunchedTime() const noexcept;
diff --git a/src/desktopentry.cpp b/src/desktopentry.cpp
index 96e6cf2..4c41c29 100644
--- a/src/desktopentry.cpp
+++ b/src/desktopentry.cpp
@@ -285,7 +285,7 @@ QString toString(const DesktopEntry::Value &value) noexcept
QString str;
if (value.canConvert()) { // get default locale
- str = value.value()[defaultKeyStr];
+ str = value.value()[DesktopFileDefaultKeyLocale];
} else {
str = value.toString();
}
@@ -312,7 +312,7 @@ QString toLocaleString(const QStringMap &localeMap, const QLocale &locale) noexc
}
}
- return toString(localeMap[defaultKeyStr]);
+ return toString(localeMap[DesktopFileDefaultKeyLocale]);
}
QString toIconString(const DesktopEntry::Value &value) noexcept
diff --git a/src/desktopentry.h b/src/desktopentry.h
index 933c86f..74f5155 100644
--- a/src/desktopentry.h
+++ b/src/desktopentry.h
@@ -15,8 +15,6 @@
#include "iniParser.h"
#include "global.h"
-constexpr static auto defaultKeyStr = "default";
-
enum class EntryContext { Unknown, EntryOuter, Entry, Done };
enum class EntryValueType { String, LocaleString, Boolean, IconString };
@@ -126,6 +124,9 @@ private:
[[nodiscard]] bool checkMainEntryValidation() const noexcept;
QMap> m_entryMap;
bool m_parsed{false};
+
+public:
+ using container_type = decltype(m_entryMap);
};
bool operator==(const DesktopEntry &lhs, const DesktopEntry &rhs);
diff --git a/src/desktopfilegenerator.cpp b/src/desktopfilegenerator.cpp
new file mode 100644
index 0000000..d70022a
--- /dev/null
+++ b/src/desktopfilegenerator.cpp
@@ -0,0 +1,183 @@
+// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+#include "desktopfilegenerator.h"
+#include "desktopfileparser.h"
+
+bool DesktopFileGenerator::checkValidation(const QVariantMap &desktopFile, QString &err) noexcept
+{
+ if (!desktopFile.contains("Type") or !desktopFile.contains("Name")) {
+ err = "required key doesn't exists.";
+ return false;
+ }
+
+ auto type = qdbus_cast(desktopFile["Type"]);
+ if (type.isEmpty()) {
+ err = "Type's type is invalid";
+ return false;
+ }
+
+ if (type == "Link" and !desktopFile.contains("URL")) {
+ err = "URL must be set when Type is 'Link'";
+ return false;
+ }
+ return true;
+}
+
+int DesktopFileGenerator::processMainGroupLocaleEntry(DesktopEntry::container_type::iterator mainEntry,
+ const QString &key,
+ const QVariant &value) noexcept
+{
+ if (key == "ActionName") {
+ return 1;
+ }
+
+ if (key == "Name") {
+ const auto &nameMap = qdbus_cast(value);
+ if (nameMap.isEmpty()) {
+ qDebug() << "Name's type mismatch:" << nameMap;
+ return -1;
+ }
+
+ mainEntry->insert("Name", QVariant::fromValue(nameMap));
+ return 1;
+ }
+
+ if (key == "Icon") {
+ const auto &iconMap = qdbus_cast(value);
+ if (auto icon = iconMap.constFind(DesktopFileDefaultKeyLocale); icon != iconMap.cend() and !icon->isEmpty()) {
+ mainEntry->insert("Icon", *icon);
+ }
+ return 1;
+ }
+
+ if (key == "Exec") {
+ const auto &execMap = qdbus_cast(value);
+ if (auto exec = execMap.constFind(DesktopFileDefaultKeyLocale); exec != execMap.cend() and !exec->isEmpty()) {
+ mainEntry->insert("Exec", *exec);
+ }
+ return 1;
+ }
+
+ return 0;
+}
+
+bool DesktopFileGenerator::processMainGroup(DesktopEntry::container_type &content, const QVariantMap &rawValue) noexcept
+{
+ auto mainEntry = content.insert(DesktopFileEntryKey, {});
+ for (auto it = rawValue.constKeyValueBegin(); it != rawValue.constKeyValueEnd(); ++it) {
+ const auto &[key, value] = *it;
+
+ if (mainEntry->contains(key)) {
+ qDebug() << "duplicate key:" << key << ",skip";
+ return false;
+ }
+
+ auto ret = processMainGroupLocaleEntry(mainEntry, key, value);
+ if (ret == 1) {
+ continue;
+ }
+
+ if (ret == -1) {
+ return false;
+ }
+
+ mainEntry->insert(key, value);
+ }
+
+ mainEntry->insert("X-Deepin-CreateBy", QString{"dde-application-manager"});
+ return true;
+}
+
+bool DesktopFileGenerator::processActionGroup(QStringList actions,
+ DesktopEntry::container_type &content,
+ const QVariantMap &rawValue) noexcept
+{
+ actions.removeDuplicates();
+ if (actions.isEmpty()) {
+ qDebug() << "empty actions";
+ return false;
+ }
+
+ auto nameMap = qdbus_cast(rawValue["ActionName"]);
+ if (nameMap.isEmpty()) {
+ qDebug() << "ActionName's type mismatch.";
+ return false;
+ }
+
+ QStringMap iconMap;
+ if (auto actionIcon = rawValue.constFind("Icon"); actionIcon != rawValue.cend()) {
+ iconMap = qdbus_cast(*actionIcon);
+ if (iconMap.isEmpty()) {
+ qDebug() << "Icon's type mismatch.";
+ return false;
+ }
+ }
+
+ QStringMap execMap;
+ if (auto actionExec = rawValue.constFind("Exec"); actionExec != rawValue.cend()) {
+ execMap = qdbus_cast(*actionExec);
+ if (execMap.isEmpty()) {
+ qDebug() << "Exec's type mismatch:" << actionExec->typeName();
+ return false;
+ }
+ }
+
+ for (const auto &action : actions) {
+ if (action.isEmpty()) {
+ qDebug() << "action's content is empty. skip";
+ continue;
+ }
+
+ if (!nameMap.contains(action)) {
+ qDebug() << "couldn't find actionName, current action:" << action;
+ return false;
+ }
+
+ auto actionGroup = content.insert(DesktopFileActionKey % action, {});
+ auto curVal = qdbus_cast(nameMap[action]);
+ if (curVal.isEmpty()) {
+ qDebug() << "inner type of actionName is mismatched";
+ return false;
+ }
+ actionGroup->insert("Name", QVariant::fromValue(curVal));
+
+ if (auto actionIcon = iconMap.constFind(action); actionIcon != iconMap.cend() and !actionIcon->isEmpty()) {
+ actionGroup->insert("Icon", iconMap[action]);
+ }
+ if (auto actionExec = execMap.constFind(action); actionExec != execMap.cend() and !actionExec->isEmpty()) {
+ actionGroup->insert("Exec", execMap[action]);
+ }
+ };
+
+ return true;
+}
+
+QString DesktopFileGenerator::generate(const QVariantMap &desktopFile, QString &err) noexcept
+{
+ DesktopEntry::container_type content{};
+ if (auto actions = desktopFile.find("Actions"); actions != desktopFile.end()) {
+ if (!desktopFile.contains("ActionName")) {
+ err = "'ActionName' doesn't exists";
+ return {};
+ }
+
+ if (!processActionGroup(actions->toStringList(), content, desktopFile)) {
+ err = "please check action group";
+ return {};
+ }
+ }
+
+ if (!processMainGroup(content, desktopFile)) {
+ err = "please check main group.";
+ return {};
+ }
+
+ auto fileContent = toString(content);
+ if (fileContent.isEmpty()) {
+ err = "couldn't convert to desktop file.";
+ return {};
+ }
+
+ return fileContent;
+}
diff --git a/src/desktopfilegenerator.h b/src/desktopfilegenerator.h
new file mode 100644
index 0000000..2693b15
--- /dev/null
+++ b/src/desktopfilegenerator.h
@@ -0,0 +1,24 @@
+// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#ifndef DESKTOPFILEGENERATOR_H
+#define DESKTOPFILEGENERATOR_H
+
+#include "desktopentry.h"
+
+struct DesktopFileGenerator
+{
+ static QString generate(const QVariantMap &desktopFile, QString &err) noexcept;
+
+private:
+ static bool checkValidation(const QVariantMap &desktopFile, QString &err) noexcept;
+ static bool processMainGroup(DesktopEntry::container_type &content, const QVariantMap &rawValue) noexcept;
+ static bool
+ processActionGroup(QStringList actions, DesktopEntry::container_type &content, const QVariantMap &rawValue) noexcept;
+ static int processMainGroupLocaleEntry(DesktopEntry::container_type::iterator mainEntry,
+ const QString &key,
+ const QVariant &value) noexcept;
+};
+
+#endif
diff --git a/src/desktopfileparser.cpp b/src/desktopfileparser.cpp
index 2bb2092..e8327aa 100644
--- a/src/desktopfileparser.cpp
+++ b/src/desktopfileparser.cpp
@@ -116,7 +116,7 @@ ParserError DesktopFileParser::addEntry(typename Groups::iterator &group) noexce
auto valueStr = line.sliced(splitCharIndex + 1).trimmed();
QString key{""};
- QString localeStr{defaultKeyStr};
+ QString localeStr{DesktopFileDefaultKeyLocale};
// NOTE:
// We are process "localized keys" here, for usage check:
// https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#localized-keys
@@ -146,7 +146,7 @@ ParserError DesktopFileParser::addEntry(typename Groups::iterator &group) noexce
return ParserError::NoError;
}
- if (localeStr != defaultKeyStr and !isInvalidLocaleString(localeStr)) {
+ if (localeStr != DesktopFileDefaultKeyLocale and !isInvalidLocaleString(localeStr)) {
qWarning().noquote() << QString("invalid LOCALE (%2) for key \"%1\"").arg(key, localeStr);
return ParserError::NoError;
}
@@ -180,3 +180,47 @@ ParserError DesktopFileParser::addEntry(typename Groups::iterator &group) noexce
return ParserError::NoError;
}
+
+QString toString(const DesktopFileParser::Groups &map)
+{
+ QString ret;
+ auto groupToString = [&ret, map](const QString &group) {
+ const auto &groupEntry = map[group];
+ ret.append('[' % group % "]\n");
+ for (auto entryIt = groupEntry.constKeyValueBegin(); entryIt != groupEntry.constKeyValueEnd(); ++entryIt) {
+ const auto &key = entryIt->first;
+ const auto &value = entryIt->second;
+ if (value.canConvert()) {
+ const auto &rawMap = value.value();
+ std::for_each(rawMap.constKeyValueBegin(), rawMap.constKeyValueEnd(), [key, &ret](const auto &inner) {
+ const auto &[locale, rawVal] = inner;
+ ret.append(key);
+ if (locale != DesktopFileDefaultKeyLocale) {
+ ret.append('[' % locale % ']');
+ }
+ ret.append('=' % rawVal % '\n');
+ });
+ } else if (value.canConvert()) {
+ const auto &rawVal = value.value();
+ auto str = rawVal.join(';');
+ ret.append(key % '=' % str % '\n');
+ } else if (value.canConvert()) {
+ const auto &rawVal = value.value();
+ ret.append(key % '=' % rawVal % '\n');
+ } else {
+ qWarning() << "value type mismatch:" << value;
+ }
+ }
+ ret.append('\n');
+ };
+
+ groupToString(DesktopFileEntryKey);
+ for (const auto &groupName : map.keys()) {
+ if (groupName == DesktopFileEntryKey) {
+ continue;
+ }
+ groupToString(groupName);
+ }
+
+ return ret;
+}
diff --git a/src/desktopfileparser.h b/src/desktopfileparser.h
index a0d0666..8d9571f 100644
--- a/src/desktopfileparser.h
+++ b/src/desktopfileparser.h
@@ -17,4 +17,6 @@ public:
ParserError addEntry(Groups::iterator &group) noexcept override;
};
+QString toString(const DesktopFileParser::Groups &map);
+
#endif
diff --git a/src/global.h b/src/global.h
index f9355ff..f6986a1 100644
--- a/src/global.h
+++ b/src/global.h
@@ -30,7 +30,7 @@ Q_DECLARE_LOGGING_CATEGORY(DDEAMProf)
using ObjectInterfaceMap = QMap;
using ObjectMap = QMap;
using QStringMap = QMap;
-using PropMap = QVariantMap;
+using PropMap = QMap;
Q_DECLARE_METATYPE(ObjectInterfaceMap)
Q_DECLARE_METATYPE(ObjectMap)
@@ -44,6 +44,21 @@ struct SystemdUnitDBusMessage
QDBusObjectPath objectPath;
};
+inline const QDBusArgument &operator>>(const QDBusArgument &argument, QStringMap &map)
+{
+ argument.beginMap();
+ while (!argument.atEnd()) {
+ QString key;
+ QString value;
+ argument.beginMapEntry();
+ argument >> key >> value;
+ argument.endMapEntry();
+ map.insert(key, value);
+ }
+ argument.endMap();
+ return argument;
+}
+
inline const QDBusArgument &operator>>(const QDBusArgument &argument, QList &units)
{
argument.beginArray();
diff --git a/tests/ut_desktopfilegenerator.cpp b/tests/ut_desktopfilegenerator.cpp
new file mode 100644
index 0000000..9acf7ba
--- /dev/null
+++ b/tests/ut_desktopfilegenerator.cpp
@@ -0,0 +1,61 @@
+// SPDX-FileCopyrightText: 2023 UnionTech Software Technology Co., Ltd.
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+#include "desktopfilegenerator.h"
+#include
+#include
+
+TEST(DesktopFileGenerator, generate)
+{
+ QVariantMap map;
+ map.insert("Type", QString{"Application"});
+ map.insert("Name",
+ QVariant::fromValue(QStringMap{{"default", "UserApp"}, {"zh_CN", "yonghuyingyong"}, {"en_US", "setApplication"}}));
+
+ map.insert("Actions", QStringList{"one", "two"});
+ map.insert("Exec",
+ QVariant::fromValue(QStringMap{
+ {"default", "/usr/bin/exec"}, {"one", "/usr/bin/exec --type=one"}, {"two", "/usr/bin/exec --type=two"}}));
+ map.insert("Icon", QVariant::fromValue(QStringMap{{"default", "default-icon"}, {"one", "one-icon"}, {"two", "two-icon"}}));
+ map.insert("ActionName",
+ QVariantMap{{"one", QVariant::fromValue(QStringMap{{"default", "oneName"}, {"zh_CN", "yi"}, {"en_US", "one"}})},
+ {"two", QVariant::fromValue(QStringMap{{"default", "twoname"}, {"zh_CN", "er"}, {"en_US", "two"}})}});
+ map.insert("Version", 1.0);
+ map.insert("Terminal", false);
+ map.insert("MimeType", QStringList{"text/html", "text/xml", "application/xhtml+xml"});
+
+ QString errMsg{"NO ERROR"};
+ auto content = DesktopFileGenerator::generate(map, errMsg);
+ EXPECT_EQ(errMsg.toStdString(), QString{"NO ERROR"}.toStdString());
+
+ QString expect{R"([Desktop Entry]
+Actions=one;two
+Exec=/usr/bin/exec
+Icon=default-icon
+MimeType=text/html;text/xml;application/xhtml+xml
+Name=UserApp
+Name[en_US]=setApplication
+Name[zh_CN]=yonghuyingyong
+Terminal=false
+Type=Application
+Version=1
+X-Deepin-CreateBy=dde-application-manager
+
+[Desktop Action one]
+Exec=/usr/bin/exec --type=one
+Icon=one-icon
+Name=oneName
+Name[en_US]=one
+Name[zh_CN]=yi
+
+[Desktop Action two]
+Exec=/usr/bin/exec --type=two
+Icon=two-icon
+Name=twoname
+Name[en_US]=two
+Name[zh_CN]=er
+
+)"};
+ EXPECT_EQ(expect.toStdString(), content.toStdString());
+}