diff --git a/src/libappimage/desktop_integration/CMakeLists.txt b/src/libappimage/desktop_integration/CMakeLists.txt index 10d4df6f..92539255 100644 --- a/src/libappimage/desktop_integration/CMakeLists.txt +++ b/src/libappimage/desktop_integration/CMakeLists.txt @@ -4,6 +4,7 @@ set( integrator/Integrator.cpp integrator/DesktopEntryEditError.h integrator/DesktopEntryEditor.cpp + integrator/MimeInfoEditor.cpp ) if(LIBAPPIMAGE_THUMBNAILER_ENABLED) diff --git a/src/libappimage/desktop_integration/integrator/Integrator.cpp b/src/libappimage/desktop_integration/integrator/Integrator.cpp index 86ca32b8..1e073c08 100644 --- a/src/libappimage/desktop_integration/integrator/Integrator.cpp +++ b/src/libappimage/desktop_integration/integrator/Integrator.cpp @@ -24,6 +24,7 @@ #include "utils/IconHandle.h" #include "utils/path_utils.h" #include "DesktopEntryEditor.h" +#include "MimeInfoEditor.h" #include "Integrator.h" #include "constants.h" @@ -50,6 +51,7 @@ namespace appimage { ResourcesExtractor resourcesExtractor; DesktopEntry desktopEntry; + std::map mimeInfoFiles; Priv(const AppImage& appImage, const bf::path& xdgDataHome) : appImage(appImage), xdgDataHome(xdgDataHome), @@ -58,7 +60,16 @@ namespace appimage { if (xdgDataHome.empty()) throw DesktopIntegrationError("Invalid XDG_DATA_HOME: " + xdgDataHome.string()); - // Extract desktop entry, DesktopIntegrationError will be throw if missing + extractDesktopEntry(); + extractMimeInfoFiles(); + + appImageId = hashPath(appImage.getPath()); + } + + /** + * Extract desktop entry, DesktopIntegrationError will be thrown in case there is no desktop file + */ + void extractDesktopEntry() { auto desktopEntryPath = resourcesExtractor.getDesktopEntryPath(); auto desktopEntryData = resourcesExtractor.extractText(desktopEntryPath); try { @@ -66,9 +77,18 @@ namespace appimage { } catch (const DesktopEntryError& error) { throw DesktopIntegrationError(std::string("Malformed desktop entry: ") + error.what()); } + } + /** + * Extract and store mime info files to be used later + */ + void extractMimeInfoFiles() { + std::vector mimeInfoPaths = resourcesExtractor.getMimeTypePackagesPaths(); - appImageId = hashPath(appImage.getPath()); + for (const std::string& path: mimeInfoPaths) { + std::string mimeInfoFileData = resourcesExtractor.extractText(path); + mimeInfoFiles.insert(std::make_pair(path, MimeInfoEditor(mimeInfoFileData))); + } } /** @@ -160,6 +180,11 @@ namespace appimage { * Icons at usr/share/icons will be preferred if not available the ".DirIcon" will be used. */ void deployIcons() { + deployApplicationIcon(); + deployMimeTypeIcons(); + } + + void deployApplicationIcon() const { static const std::string dirIconPath = ".DirIcon"; static const auto iconsDirPath = "usr/share/icons"; @@ -177,7 +202,7 @@ namespace appimage { try { Logger::warning("Using .DirIcon as default app icon"); auto dirIconData = resourcesExtractor.extract(dirIconPath); - deployApplicationIcon(desktopEntryIconName, dirIconData);; + deployIcon("apps", desktopEntryIconName, dirIconData);; } catch (const PayloadIteratorError& error) { Logger::error(error.what()); Logger::error("No icon was generated for: " + appImage.getPath()); @@ -192,17 +217,41 @@ namespace appimage { } } + void deployMimeTypeIcons() { + // Resolve mime type icon names + std::list mimeTypeIconNames; + for (const auto& editor : mimeInfoFiles) { + auto names = editor.second.getMimeTypeIconNames(); + mimeTypeIconNames.merge(names); + } + // Resolve file paths for the icon names + std::vector mimeTypeIconPaths; + for (const auto& iconName: mimeTypeIconNames) { + auto paths = resourcesExtractor.getIconFilePaths(iconName); + mimeTypeIconPaths.insert(mimeTypeIconPaths.end(), paths.begin(), paths.end()); + } + + // Generate deploy paths + std::map mimeTypeIconsTargetPaths; + for (const auto& path: mimeTypeIconPaths) + mimeTypeIconsTargetPaths[path] = generateDeployPath(path).string(); + + resourcesExtractor.extractTo(mimeTypeIconsTargetPaths); + } + /** * Deploy as the main application icon to - * XDG_DATA_HOME/icons/hicolor//apps/__. + * XDG_DATA_HOME/icons/hicolor///__. * - * size: actual icon dimenzions, in case of vectorial image "scalable" is used + * size: actual icon dimensions, in case of vector image "scalable" is used * format extension: in case of vectorial image "svg" otherwise "png" * + * @param iconGroup * @param iconName * @param iconData */ - void deployApplicationIcon(const std::string& iconName, std::vector& iconData) const { + void deployIcon(const std::string& iconGroup, const std::string& iconName, + std::vector& iconData) const { try { IconHandle icon(iconData); @@ -224,30 +273,41 @@ namespace appimage { iconPath /= (iconSize + "x" + iconSize); } - iconPath /= "apps"; + iconPath /= iconGroup; iconPath /= iconNameBuilder.str(); auto deployPath = generateDeployPath(iconPath); icon.save(deployPath.string(), icon.format()); - } catch (const IconHandleError& er) { - Logger::error(er.what()); - Logger::error("No icon was generated for: " + appImage.getPath()); + } catch (const IconHandleError& error) { + Logger::error("Icon deploy failed " + iconGroup + "/" + iconName + + " from " + appImage.getPath() + " : " + error.what()); } } /** - * Append vendor prefix and appImage id to the file names to identify the appImage that owns + * Prepend vendor prefix and appImage id to the file names to identify the appImage that owns * this file. Replace the default XDG_DATA_DIR by the one at * * @param path resource path - * @return path with a prefixed file name + * @return //__. */ bf::path generateDeployPath(bf::path path) const { // add appImage resource identification prefix to the filename std::stringstream fileNameBuilder; fileNameBuilder << VENDOR_PREFIX << "_" << appImageId << "_" << path.filename().string(); - // build the relative parent path ignoring the default XDG_DATA_DIR prefix ("usr/share") + bf::path relativeParentPath = generateDeployParentPath(path); + + bf::path newPath = xdgDataHome / relativeParentPath / fileNameBuilder.str(); + return newPath; + } + + /** + * Build the relative parent path ignoring the default XDG_DATA_DIR prefix ("usr/share") + * @param path + * @return + */ + boost::filesystem::path generateDeployParentPath(boost::filesystem::path& path) const { path.remove_filename(); bf::path relativeParentPath; const bf::path defaultXdgDataDirPath = "usr/share"; @@ -259,19 +319,24 @@ namespace appimage { relativeParentPath.clear(); } - bf::path newPath = xdgDataHome / relativeParentPath / fileNameBuilder.str(); - return newPath; + return relativeParentPath; } void deployMimeTypePackages() { - auto mimeTypePackagesPaths = resourcesExtractor.getMimeTypePackagesPaths(); - std::map mimeTypePackagesTargetPaths; - - // Generate deploy paths - for (const auto& path: mimeTypePackagesPaths) - mimeTypePackagesTargetPaths[path] = generateDeployPath(path).string(); - - resourcesExtractor.extractTo(mimeTypePackagesTargetPaths); + for (auto& itr: mimeInfoFiles) { + // Prepare mime type package to be deployed + MimeInfoEditor& editor = itr.second; + editor.prependDeployIdToIcons(VENDOR_PREFIX + '_' + appImageId); + bf::path deployPath = generateDeployPath(itr.first); + + // ensure parent dir exists + create_directories(deployPath.parent_path()); + + // write mime type file + std::ofstream out(deployPath.string()); + out << editor.getResult(); + out.close(); + } } void setExecutionPermission() { diff --git a/src/libappimage/desktop_integration/integrator/MimeInfoEditor.cpp b/src/libappimage/desktop_integration/integrator/MimeInfoEditor.cpp new file mode 100644 index 00000000..21415fb9 --- /dev/null +++ b/src/libappimage/desktop_integration/integrator/MimeInfoEditor.cpp @@ -0,0 +1,97 @@ +// libraries +#include +#include +#include +#include + +// local +#include "utils/Logger.h" +#include "MimeInfoEditor.h" + +namespace appimage { + namespace desktop_integration { + namespace integrator { + MimeInfoEditor::MimeInfoEditor(std::string data) { + try { + std::stringstream in(data); + using boost::property_tree::ptree; + + // populate tree structure pt + read_xml(in, pt); + } catch (const std::runtime_error& error) { + appimage::utils::Logger::warning(std::string("Unable to read MimeInfo: ") + error.what()); + } + } + + std::string MimeInfoEditor::getResult() { + try { + using boost::property_tree::ptree; + std::stringstream out; + + write_xml(out, pt); + + return out.str(); + } catch (const std::runtime_error& error) { + appimage::utils::Logger::warning(std::string("Unable to edit MimeInfo: ") + error.what()); + return std::string{}; + } + } + + void MimeInfoEditor::prependDeployIdToIcons(const std::string& deployId) { + try { + using boost::property_tree::ptree; + + // traverse pt + for (auto& node: pt.get_child("mime-info")) { + if (node.first == "mime-type") { + auto& subTree = node.second; + // get original icon name from the icon entry + std::string originalName = subTree.get("icon..name", ""); + + // or fallback to the mime-type name + if (originalName.empty()) { + originalName = subTree.get(".type"); + boost::replace_all(originalName, "/", "-"); + } + + std::string newIconName = deployId + '_' + originalName; + + subTree.put("icon..name", newIconName); + } + } + } catch (const std::runtime_error& error) { + appimage::utils::Logger::warning(std::string("Unable to edit MimeInfo: ") + error.what()); + } + } + + std::list MimeInfoEditor::getMimeTypeIconNames() const { + std::list icons; + + try { + using boost::property_tree::ptree; + + // traverse pt + for (auto& node: pt.get_child("mime-info")) { + if (node.first == "mime-type") { + auto& subTree = node.second; + // get original icon name from the icon entry + std::string iconName = subTree.get("icon..name", ""); + + // or fallback to the mime-type name + if (iconName.empty()) { + iconName = subTree.get(".type"); + boost::replace_all(iconName, "/", "-"); + } + + icons.push_back(iconName); + } + } + } catch (const std::runtime_error& error) { + appimage::utils::Logger::warning(std::string("Unable to read MimeInfo: ") + error.what()); + } + + return icons; + } + } + } +} diff --git a/src/libappimage/desktop_integration/integrator/MimeInfoEditor.h b/src/libappimage/desktop_integration/integrator/MimeInfoEditor.h new file mode 100644 index 00000000..ef6bb898 --- /dev/null +++ b/src/libappimage/desktop_integration/integrator/MimeInfoEditor.h @@ -0,0 +1,49 @@ +#pragma once + +// system +#include +#include + +// libraries +#include + +namespace appimage { + namespace desktop_integration { + namespace integrator { + /** + * @brief Edit MimeInfo files to be deployed + */ + class MimeInfoEditor { + public: + /** + * Create an editor instance to modify the + * @param data + * @param deployId + */ + MimeInfoEditor(std::string data); + + /** + * @brief Prepends to the icon file name + * + * + * @param deployId + */ + void prependDeployIdToIcons(const std::string& deployId); + + /** + * @return modified MimeInfo + */ + std::string getResult(); + + /** + * Extract the icon names from from the icon entry or the mime type name + * @return names of the icons related to the mime types + */ + std::list getMimeTypeIconNames() const; + + private: + boost::property_tree::ptree pt; + }; + } + } +} diff --git a/src/libappimage/utils/resources_extractor/ResourcesExtractor.cpp b/src/libappimage/utils/resources_extractor/ResourcesExtractor.cpp index e62894f3..ac80ecc1 100644 --- a/src/libappimage/utils/resources_extractor/ResourcesExtractor.cpp +++ b/src/libappimage/utils/resources_extractor/ResourcesExtractor.cpp @@ -24,24 +24,27 @@ namespace appimage { public: explicit Priv(const AppImage& appImage) : appImage(appImage), entriesCache(appImage) {} - core::AppImage appImage; PayloadEntriesCache entriesCache; + bool isFile(const std::string& fileName) const { + return appimage::core::PayloadEntryType::REGULAR == entriesCache.getEntryType(fileName) || + appimage::core::PayloadEntryType::LINK == entriesCache.getEntryType(fileName); + } bool isIconFile(const std::string& fileName) const { - return (fileName.find("usr/share/icons") != std::string::npos); + return (fileName.find("usr/share/icons") != std::string::npos) && isFile(fileName); } bool isMainDesktopFile(const std::string& fileName) const { return fileName.find(".desktop") != std::string::npos && - fileName.find('/') == std::string::npos; + fileName.find('/') == std::string::npos && isFile(fileName); } bool isMimeFile(const std::string& fileName) const { return fileName.find("usr/share/mime/packages") != std::string::npos && - fileName.find(".xml") == std::string::npos; + fileName.find(".xml") != std::string::npos && isFile(fileName); } std::vector readDataFile(std::istream& istream) const { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 4594c7aa..1e49bdf1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -6,10 +6,12 @@ if(BUILD_TESTING) # global definitions add_definitions( -DTEST_DATA_DIR="${CMAKE_CURRENT_SOURCE_DIR}/data/" + -DNEW_TEST_DATA_DIR="${CMAKE_CURRENT_BINARY_DIR}/data/" -DGIT_COMMIT="AppImageKit unit tests" ) + add_subdirectory(data) add_subdirectory(libappimage) if(ENABLE_COVERAGE) diff --git a/tests/data/CMakeLists.txt b/tests/data/CMakeLists.txt new file mode 100644 index 00000000..7d43d91c --- /dev/null +++ b/tests/data/CMakeLists.txt @@ -0,0 +1,35 @@ +message(STATUS "Generating test data AppImage") + +# Get appimagetool +set(APPIMAGETOOL_BIN ${CMAKE_CURRENT_BINARY_DIR}/appimagetool) +if(NOT EXISTS ${APPIMAGETOOL_BIN}) + if(DEFINED ENV{ARCH} ) + set(APPIMAGETOOL_BIN_ARCH $ENV{ARCH}) + else() + set(APPIMAGETOOL_BIN_ARCH ${CMAKE_SYSTEM_PROCESSOR}) + endif() + + message(STATUS "Downloading appimagetool for ${APPIMAGETOOL_BIN_ARCH}") + file(DOWNLOAD "https://github.com/AppImage/AppImageKit/releases/download/12/appimagetool-${APPIMAGETOOL_BIN_ARCH}.AppImage" "${APPIMAGETOOL_BIN}") + + execute_process(COMMAND chmod +x ${APPIMAGETOOL_BIN}) +endif() +message(STATUS "Using appimagetool: ${APPIMAGETOOL_BIN}") + +# Generate Echo AppImage with custom mime type package +set(APPIMAGE_NAME "echo.with.mimetype") +set(TARGET_APPDIR ${CMAKE_CURRENT_BINARY_DIR}/${APPIMAGE_NAME}.AppDir) + +if(NOT EXISTS ${TARGET_APPDIR}) + message(STATUS "Generating ${APPIMAGE_NAME} AppDir") + + file(MAKE_DIRECTORY ${TARGET_APPDIR}) + configure_file(squashfs-root/echo.desktop.in ${TARGET_APPDIR}/echo.desktop @ONLY) + file(COPY squashfs-root/.DirIcon DESTINATION ${TARGET_APPDIR}) + file(COPY squashfs-root/AppRun DESTINATION ${TARGET_APPDIR}) + file(COPY squashfs-root/utilities-terminal.svg DESTINATION ${TARGET_APPDIR}) + file(COPY squashfs-root/usr DESTINATION ${TARGET_APPDIR}) + + message(STATUS "Creating ${APPIMAGE_NAME} AppImage") + execute_process(COMMAND ${APPIMAGETOOL_BIN} ${TARGET_APPDIR} ${CMAKE_CURRENT_BINARY_DIR}/${APPIMAGE_NAME}.AppImage) +endif() diff --git a/tests/data/squashfs-root/echo.desktop.in b/tests/data/squashfs-root/echo.desktop.in new file mode 100644 index 00000000..2010cde7 --- /dev/null +++ b/tests/data/squashfs-root/echo.desktop.in @@ -0,0 +1,10 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=@APPIMAGE_NAME@ +Name[de]=Echo DE +Comment=Just echo. +Exec=echo %F +Icon=utilities-terminal +X-AppImage-Version=1234 +Categories=Utility; diff --git a/tests/data/squashfs-root/usr/share/icons/hicolor/scalable/mimetypes/application-x-custom-file.svg b/tests/data/squashfs-root/usr/share/icons/hicolor/scalable/mimetypes/application-x-custom-file.svg new file mode 100644 index 00000000..4a0250da --- /dev/null +++ b/tests/data/squashfs-root/usr/share/icons/hicolor/scalable/mimetypes/application-x-custom-file.svg @@ -0,0 +1,320 @@ + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/tests/data/squashfs-root/usr/share/mime/packages/custom.xml b/tests/data/squashfs-root/usr/share/mime/packages/custom.xml new file mode 100644 index 00000000..508aa35c --- /dev/null +++ b/tests/data/squashfs-root/usr/share/mime/packages/custom.xml @@ -0,0 +1,8 @@ + + + + + Custom File + + + diff --git a/tests/libappimage/desktop_integration/CMakeLists.txt b/tests/libappimage/desktop_integration/CMakeLists.txt index a5296f78..44831e86 100644 --- a/tests/libappimage/desktop_integration/CMakeLists.txt +++ b/tests/libappimage/desktop_integration/CMakeLists.txt @@ -5,6 +5,7 @@ set( integrator/TestDesktopIntegration.cpp integrator/TestDesktopEntryEditor.cpp + integrator/TestMimeInfoEditor.cpp $ $ diff --git a/tests/libappimage/desktop_integration/TestIntegrationManager.cpp b/tests/libappimage/desktop_integration/TestIntegrationManager.cpp index 54762213..f2fb5db2 100644 --- a/tests/libappimage/desktop_integration/TestIntegrationManager.cpp +++ b/tests/libappimage/desktop_integration/TestIntegrationManager.cpp @@ -74,11 +74,14 @@ TEST_F(TestIntegrationManager, shallAppImageBeRegistered) { IntegrationManager manager; ASSERT_TRUE(manager.shallAppImageBeRegistered( - appimage::core::AppImage(TEST_DATA_DIR "Echo-x86_64.AppImage"))); + appimage::core::AppImage(TEST_DATA_DIR + "Echo-x86_64.AppImage"))); ASSERT_FALSE(manager.shallAppImageBeRegistered( - appimage::core::AppImage(TEST_DATA_DIR "Echo-no-integrate-x86_64.AppImage"))); + appimage::core::AppImage(TEST_DATA_DIR + "Echo-no-integrate-x86_64.AppImage"))); ASSERT_THROW(manager.shallAppImageBeRegistered( - appimage::core::AppImage(TEST_DATA_DIR "elffile")), appimage::core::AppImageError); + appimage::core::AppImage(TEST_DATA_DIR + "elffile")), appimage::core::AppImageError); } diff --git a/tests/libappimage/desktop_integration/integrator/TestDesktopIntegration.cpp b/tests/libappimage/desktop_integration/integrator/TestDesktopIntegration.cpp index ab52417e..9fb1f067 100644 --- a/tests/libappimage/desktop_integration/integrator/TestDesktopIntegration.cpp +++ b/tests/libappimage/desktop_integration/integrator/TestDesktopIntegration.cpp @@ -1,5 +1,7 @@ // system #include +#include +#include // library headers #include @@ -9,6 +11,7 @@ // local #include "appimage/desktop_integration/exceptions.h" #include "integrator/Integrator.h" +#include "integrator/MimeInfoEditor.h" #include "utils/hashlib.h" #include "utils/path_utils.h" @@ -84,3 +87,32 @@ TEST_F(DesktopIntegrationTests, malformedDesktopEntry) { ASSERT_THROW(Integrator(appImage, userDirPath.string()), appimage::desktop_integration::DesktopIntegrationError); } + +TEST_F(DesktopIntegrationTests, integrateMimeType) { + std::string appImagePath = NEW_TEST_DATA_DIR "echo.with.mimetype.AppImage"; + appimage::core::AppImage appImage(appImagePath); + Integrator i(appImage, userDirPath.string()); + + i.integrate(); + + std::string md5 = appimage::utils::hashPath(appImagePath.c_str()); + + bf::path mimeTypeIconFilePath = + userDirPath / ("icons/hicolor/scalable/mimetypes/appimagekit_" + md5 + "_application-x-custom-file.svg"); + ASSERT_TRUE(bf::exists(mimeTypeIconFilePath)); + + bf::path mimeTypePackageFilePath = userDirPath / ("mime/packages/appimagekit_" + md5 + "_custom.xml"); + ASSERT_TRUE(bf::exists(mimeTypePackageFilePath)); + + // Read generated mime info package file + std::ifstream mimeTypePackageFile(mimeTypePackageFilePath.string()); + std::string fileData((std::istreambuf_iterator(mimeTypePackageFile)), + std::istreambuf_iterator()); + + MimeInfoEditor editor(fileData); + // Compare icon names + auto result = editor.getMimeTypeIconNames(); + std::list expected = {"appimagekit_" + md5 + "_application-x-custom-file"}; + ASSERT_EQ(result, expected); +} + diff --git a/tests/libappimage/desktop_integration/integrator/TestMimeInfoEditor.cpp b/tests/libappimage/desktop_integration/integrator/TestMimeInfoEditor.cpp new file mode 100644 index 00000000..9d1a44cb --- /dev/null +++ b/tests/libappimage/desktop_integration/integrator/TestMimeInfoEditor.cpp @@ -0,0 +1,111 @@ +//system +#include + +// library +#include +#include +#include +#include + +// local +#include +#include "utils/hashlib.h" +#include "integrator/MimeInfoEditor.h" + +using namespace appimage::desktop_integration::integrator; +using namespace appimage::utils; + +class MimeInfoEditorTests : public ::testing::Test { +protected: + std::stringstream mimeInfo; + std::stringstream mimeInfoWithIconEntry; + std::stringstream expectedMimeInfo; +protected: + + void SetUp() override { + mimeInfo << "\n" + "\n" + " \n" + " \n" + " Starbright File\n" + " \n" + " \n" + ""; + + mimeInfoWithIconEntry + << "\n" + "\n" + " \n" + " \n" + " Starbright File\n" + " \n" + " \n" + " \n" + ""; + + expectedMimeInfo + << "\n" + "\n" + " \n" + " \n" + " Starbright File\n" + " \n" + " \n" + " \n" + ""; + } +}; + +TEST_F(MimeInfoEditorTests, setIcon) { + MimeInfoEditor editor(mimeInfo.str()); + editor.prependDeployIdToIcons("appimaged_d41d8cd98f00b204e9800998ecf8427e"); + std::string result = editor.getResult(); + + + using boost::property_tree::ptree; + + std::stringstream resultStream(result); + + // populate tree structure pt + ptree resultPt, expectedPt; + read_xml(resultStream, resultPt, boost::property_tree::xml_parser::trim_whitespace); + read_xml(expectedMimeInfo, expectedPt, boost::property_tree::xml_parser::trim_whitespace); + + + ASSERT_EQ(resultPt, expectedPt); +} + +TEST_F(MimeInfoEditorTests, updateIcon) { + MimeInfoEditor editor(mimeInfoWithIconEntry.str()); + editor.prependDeployIdToIcons("appimaged_d41d8cd98f00b204e9800998ecf8427e"); + std::string result = editor.getResult(); + + + using boost::property_tree::ptree; + + std::stringstream resultStream(result); + + // populate tree structure pt + ptree resultPt, expectedPt; + read_xml(resultStream, resultPt, boost::property_tree::xml_parser::trim_whitespace); + read_xml(expectedMimeInfo, expectedPt, boost::property_tree::xml_parser::trim_whitespace); + + + ASSERT_EQ(resultPt, expectedPt); +} + +TEST_F(MimeInfoEditorTests, getIconNamesFromMimeTypeType) { + MimeInfoEditor editor(mimeInfo.str()); + std::list iconNames = editor.getMimeTypeIconNames(); + std::list expectedIconNames = {"application-x-starbright-file"}; + + ASSERT_EQ(iconNames, expectedIconNames); +} + +TEST_F(MimeInfoEditorTests, getIconNamesFromMimeTypeIconName) { + MimeInfoEditor editor(mimeInfoWithIconEntry.str()); + std::list iconNames = editor.getMimeTypeIconNames(); + std::list expectedIconNames = {"application-x-starbright-file"}; + + ASSERT_EQ(iconNames, expectedIconNames); +}