mirror of
https://github.com/audacity/linuxdeploy.git
synced 2026-04-17 08:32:53 -05:00
Switch to extracted desktop file library
This commit is contained in:
21
.gitmodules
vendored
21
.gitmodules
vendored
@@ -76,24 +76,9 @@
|
||||
[submodule "lib/googletest"]
|
||||
path = lib/googletest
|
||||
url = https://github.com/google/googletest
|
||||
[submodule "lib/boost-lexical_cast"]
|
||||
path = lib/boost-lexical_cast
|
||||
url = https://github.com/boostorg/lexical_cast
|
||||
[submodule "lib/boost-concept_check"]
|
||||
path = lib/boost-concept_check
|
||||
url = https://github.com/boostorg/concept_check
|
||||
[submodule "lib/boost-numeric_conversion"]
|
||||
path = lib/boost-numeric_conversion
|
||||
url = https://github.com/boostorg/numeric_conversion
|
||||
[submodule "lib/boost-array"]
|
||||
path = lib/boost-array
|
||||
url = https://github.com/boostorg/array
|
||||
[submodule "lib/boost-container"]
|
||||
path = lib/boost-container
|
||||
url = https://github.com/boostorg/container
|
||||
[submodule "lib/boost-move"]
|
||||
path = lib/boost-move
|
||||
url = https://github.com/boostorg/move
|
||||
[submodule "lib/boost-math"]
|
||||
path = lib/boost-math
|
||||
url = https://github.com/boostorg/math
|
||||
[submodule "lib/linuxdeploy-desktopfile"]
|
||||
path = lib/linuxdeploy-desktopfile
|
||||
url = https://github.com/linuxdeploy/linuxdeploy-desktopfile
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// local includes
|
||||
#include "linuxdeploy/core/desktopfile/desktopfile.h"
|
||||
#include "linuxdeploy/desktopfile/desktopfile.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@@ -1,126 +0,0 @@
|
||||
// system includes
|
||||
#include <unordered_map>
|
||||
|
||||
// library includes
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// local includes
|
||||
#include "desktopfileentry.h"
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace desktopfile {
|
||||
/*
|
||||
* Parse and read desktop files.
|
||||
*/
|
||||
class DesktopFile {
|
||||
public:
|
||||
// describes a single section
|
||||
typedef std::unordered_map<std::string, DesktopFileEntry> section_t;
|
||||
|
||||
// describes all sections in the desktop file
|
||||
typedef std::unordered_map<std::string, section_t> sections_t;
|
||||
|
||||
private:
|
||||
// private data class pattern
|
||||
class PrivateData;
|
||||
std::shared_ptr<PrivateData> d;
|
||||
|
||||
// (in)equality operators are implemented outside this class
|
||||
friend bool operator==(const DesktopFile& first, const DesktopFile& second);
|
||||
friend bool operator!=(const DesktopFile& first, const DesktopFile& second);
|
||||
|
||||
public:
|
||||
// default constructor
|
||||
DesktopFile();
|
||||
|
||||
// construct from existing desktop file
|
||||
// if the file exists, it will be read using DesktopFileReader
|
||||
// if reading fails, exceptions will be thrown (see DesktopFileReader for more information)
|
||||
explicit DesktopFile(const boost::filesystem::path& path);
|
||||
|
||||
// construct by reading an existing stream
|
||||
// file must exist, otherwise std::runtime_error is thrown
|
||||
explicit DesktopFile(std::istream& is);
|
||||
|
||||
// copy constructor
|
||||
DesktopFile(const DesktopFile& other);
|
||||
|
||||
// copy assignment constructor
|
||||
DesktopFile& operator=(const DesktopFile& other);
|
||||
|
||||
// move assignment operator
|
||||
DesktopFile& operator=(DesktopFile&& other) noexcept;
|
||||
|
||||
public:
|
||||
// returns true if a file has been loaded, false otherwise
|
||||
bool isEmpty() const;
|
||||
|
||||
// read desktop file
|
||||
// sets path associated with this file
|
||||
// throws exceptions in case of issues, see DesktopFileReader for more information
|
||||
void read(const boost::filesystem::path& path);
|
||||
|
||||
// read desktop file from existing stream
|
||||
// throws exceptions in case of issues, see DesktopFileReader for more information
|
||||
void read(std::istream& is);
|
||||
|
||||
// get path associated with this file
|
||||
boost::filesystem::path path() const;
|
||||
|
||||
// sets the path associated with this desktop file
|
||||
// used to e.g., save the desktop file
|
||||
void setPath(const boost::filesystem::path& path);
|
||||
|
||||
// clear contents of desktop file
|
||||
void clear();
|
||||
|
||||
// save desktop file
|
||||
bool save() const;
|
||||
|
||||
// save desktop file to path
|
||||
// does not change path associated with desktop file
|
||||
// throws exceptions in case of errors, see DesktopFileWriter::save(...) for more information
|
||||
bool save(const boost::filesystem::path& path) const;
|
||||
|
||||
// save desktop file to ostream
|
||||
// does not change path associated with desktop file
|
||||
// throws exceptions in case of errors, see DesktopFileWriter::save(...) for more information
|
||||
bool save(std::ostream& os) const;
|
||||
|
||||
// check if entry exists in given section and key
|
||||
bool entryExists(const std::string& section, const std::string& key) const;
|
||||
|
||||
// get key from desktop file
|
||||
// an std::string passed as value parameter will be populated with the contents
|
||||
// returns true (and populates value) if the key exists, false otherwise
|
||||
bool getEntry(const std::string& section, const std::string& key, DesktopFileEntry& value) const;
|
||||
|
||||
// add key to section in desktop file
|
||||
// the section will be created if it doesn't exist already
|
||||
// returns true if an existing key was overwritten, false otherwise
|
||||
bool setEntry(const std::string& section, const DesktopFileEntry& entry);
|
||||
|
||||
// add key to section in desktop file
|
||||
// the section will be created if it doesn't exist already
|
||||
// returns true if an existing key was overwritten, false otherwise
|
||||
bool setEntry(const std::string& section, DesktopFileEntry&& entry);
|
||||
|
||||
// create common application entries in desktop file
|
||||
// returns false if one of the keys exists and was left unmodified
|
||||
bool addDefaultKeys(const std::string& executableFileName);
|
||||
|
||||
// validate desktop file
|
||||
bool validate() const;
|
||||
};
|
||||
|
||||
// DesktopFile equality operator
|
||||
bool operator==(const DesktopFile& first, const DesktopFile& second);
|
||||
|
||||
// DesktopFile inequality operator
|
||||
bool operator!=(const DesktopFile& first, const DesktopFile& second);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// system headers
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
// library headers
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace desktopfile {
|
||||
class DesktopFileEntry {
|
||||
private:
|
||||
// opaque data class pattern
|
||||
class PrivateData;
|
||||
|
||||
std::shared_ptr<PrivateData> d;
|
||||
|
||||
public:
|
||||
// default constructor
|
||||
DesktopFileEntry();
|
||||
|
||||
// construct from key and value
|
||||
explicit DesktopFileEntry(std::string key, std::string value);
|
||||
|
||||
// copy constructor
|
||||
DesktopFileEntry(const DesktopFileEntry& other);
|
||||
|
||||
// copy assignment constructor
|
||||
DesktopFileEntry& operator=(const DesktopFileEntry& other);
|
||||
|
||||
// move assignment operator
|
||||
DesktopFileEntry& operator=(DesktopFileEntry&& other) noexcept;
|
||||
|
||||
// equality operator
|
||||
bool operator==(const DesktopFileEntry& other) const;
|
||||
|
||||
// inequality operator
|
||||
bool operator!=(const DesktopFileEntry& other) const;
|
||||
|
||||
public:
|
||||
// checks whether a key and value have been set
|
||||
bool isEmpty() const;
|
||||
|
||||
// return entry's key
|
||||
const std::string& key() const;
|
||||
|
||||
// return entry's value
|
||||
const std::string& value() const;
|
||||
|
||||
public:
|
||||
// convert value to integer
|
||||
// throws boost::bad_lexical_cast in case of type errors
|
||||
int32_t asInt() const;
|
||||
|
||||
// convert value to long
|
||||
// throws boost::bad_lexical_cast in case of type errors
|
||||
int64_t asLong() const;
|
||||
|
||||
// convert value to double
|
||||
// throws boost::bad_lexical_cast in case of type errors
|
||||
double asDouble() const;
|
||||
|
||||
// split CSV list value into vector
|
||||
// the separator used to split the string is a semicolon as per desktop file spec
|
||||
std::vector<std::string> parseStringList() const;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace desktopfile {
|
||||
/**
|
||||
* Desktop file library's base exception.
|
||||
*/
|
||||
class DesktopFileError : public std::runtime_error {
|
||||
public:
|
||||
explicit DesktopFileError(const std::string& message = "unknown desktop file error") : runtime_error(message) {};
|
||||
};
|
||||
|
||||
/**
|
||||
* Exception thrown by DesktopFileReader on parsing errors.
|
||||
*/
|
||||
class ParseError : public DesktopFileError {
|
||||
public:
|
||||
explicit ParseError(const std::string& message = "unknown parse error") : DesktopFileError(message) {};
|
||||
};
|
||||
|
||||
/**
|
||||
* I/O exception, thrown if files cannot be opened, reading or writing fails etc.
|
||||
*/
|
||||
class IOError : public DesktopFileError {
|
||||
public:
|
||||
explicit IOError(const std::string& message = "unknown I/O error") : DesktopFileError(message) {};
|
||||
};
|
||||
|
||||
class UnknownSectionError : public DesktopFileError {
|
||||
public:
|
||||
explicit UnknownSectionError(const std::string& section) : DesktopFileError("unknown section: " + section) {};
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,6 +9,8 @@ add_library(args INTERFACE)
|
||||
target_sources(args INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/args/args.hxx)
|
||||
target_include_directories(args INTERFACE args)
|
||||
|
||||
add_subdirectory(linuxdeploy-desktopfile)
|
||||
|
||||
if(NOT USE_SYSTEM_BOOST)
|
||||
add_library(boost_config INTERFACE)
|
||||
set_property(TARGET boost_config PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-config/include>")
|
||||
@@ -101,38 +103,8 @@ if(NOT USE_SYSTEM_BOOST)
|
||||
boost_config boost_predef boost_assert boost_throw_exception boost_smart_ptr boost_core boost_mpl
|
||||
boost_type_traits boost_static_assert boost_integer boost_preprocessor boost_functional boost_detail
|
||||
)
|
||||
|
||||
add_library(boost_numeric_conversion INTERFACE)
|
||||
set_property(TARGET boost_numeric_conversion PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-numeric_conversion/include>")
|
||||
set_property(TARGET boost_numeric_conversion PROPERTY INTERFACE_LINK_LIBRARIES "boost_config")
|
||||
|
||||
add_library(boost_concept_check INTERFACE)
|
||||
set_property(TARGET boost_concept_check PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-concept_check/include>")
|
||||
set_property(TARGET boost_concept_check PROPERTY INTERFACE_LINK_LIBRARIES "boost_config")
|
||||
target_link_libraries(boost_concept_check INTERFACE boost_numeric_conversion)
|
||||
|
||||
add_library(boost_array INTERFACE)
|
||||
set_property(TARGET boost_array PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-array/include>")
|
||||
set_property(TARGET boost_array PROPERTY INTERFACE_LINK_LIBRARIES "boost_config")
|
||||
|
||||
add_library(boost_move INTERFACE)
|
||||
set_property(TARGET boost_move PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-move/include>")
|
||||
set_property(TARGET boost_move PROPERTY INTERFACE_LINK_LIBRARIES "boost_config")
|
||||
|
||||
add_library(boost_container INTERFACE)
|
||||
set_property(TARGET boost_container PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-container/include>")
|
||||
set_property(TARGET boost_container PROPERTY INTERFACE_LINK_LIBRARIES boost_config boost_move)
|
||||
|
||||
add_library(boost_math INTERFACE)
|
||||
set_property(TARGET boost_math PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-math/include>")
|
||||
set_property(TARGET boost_math PROPERTY INTERFACE_LINK_LIBRARIES boost_config boost_move)
|
||||
|
||||
add_library(boost_lexical_cast INTERFACE)
|
||||
set_property(TARGET boost_lexical_cast PROPERTY INTERFACE_INCLUDE_DIRECTORIES "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/boost-lexical_cast/include>")
|
||||
set_property(TARGET boost_lexical_cast PROPERTY INTERFACE_LINK_LIBRARIES "boost_config")
|
||||
target_link_libraries(boost_lexical_cast INTERFACE boost_concept_check boost_numeric_conversion boost_array boost_container boost_math)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
if(BUILD_TESTING AND NOT TARGET gtest)
|
||||
add_subdirectory(googletest)
|
||||
endif()
|
||||
|
||||
Submodule lib/boost-array deleted from cef221d8b4
Submodule lib/boost-concept_check deleted from 7dbaa85413
Submodule lib/boost-lexical_cast deleted from 5cd9771337
Submodule lib/boost-math deleted from 6bbba17f9e
Submodule lib/boost-move deleted from 3ce9452f93
Submodule lib/boost-numeric_conversion deleted from b900379a74
1
lib/linuxdeploy-desktopfile
Submodule
1
lib/linuxdeploy-desktopfile
Submodule
Submodule lib/linuxdeploy-desktopfile added at 31718acde5
@@ -10,7 +10,7 @@ if(USE_SYSTEM_BOOST)
|
||||
set(BOOST_LIBS Boost::filesystem Boost::regex)
|
||||
else()
|
||||
# use custom built libs
|
||||
set(BOOST_LIBS boost_system boost_filesystem boost_regex boost_lexical_cast)
|
||||
set(BOOST_LIBS boost_system boost_filesystem boost_regex)
|
||||
endif()
|
||||
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
using namespace linuxdeploy::core;
|
||||
using namespace linuxdeploy::core::log;
|
||||
using namespace linuxdeploy::desktopfile;
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
namespace linuxdeploy {
|
||||
|
||||
@@ -18,11 +18,10 @@ target_include_directories(linuxdeploy_core_log PUBLIC ${PROJECT_SOURCE_DIR}/inc
|
||||
target_link_libraries(linuxdeploy_core_log PUBLIC ${BOOST_LIBS})
|
||||
|
||||
add_subdirectory(copyright)
|
||||
add_subdirectory(desktopfile)
|
||||
|
||||
add_library(linuxdeploy_core STATIC elf.cpp appdir.cpp ${HEADERS})
|
||||
target_link_libraries(linuxdeploy_core PUBLIC
|
||||
linuxdeploy_plugin linuxdeploy_core_log linuxdeploy_util linuxdeploy_core_desktopfile
|
||||
linuxdeploy_plugin linuxdeploy_core_log linuxdeploy_util linuxdeploy_desktopfile
|
||||
${BOOST_LIBS} CImg ${CMAKE_THREAD_LIBS_INIT}
|
||||
)
|
||||
target_link_libraries(linuxdeploy_core PRIVATE linuxdeploy_core_copyright)
|
||||
|
||||
@@ -12,9 +12,9 @@
|
||||
|
||||
// local headers
|
||||
#include "linuxdeploy/core/appdir.h"
|
||||
#include "linuxdeploy/core/desktopfile/desktopfileentry.h"
|
||||
#include "linuxdeploy/core/elf.h"
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "linuxdeploy/desktopfile/desktopfileentry.h"
|
||||
#include "linuxdeploy/util/util.h"
|
||||
#include "copyright.h"
|
||||
|
||||
@@ -22,7 +22,7 @@
|
||||
#include "excludelist.h"
|
||||
|
||||
using namespace linuxdeploy::core;
|
||||
using namespace linuxdeploy::core::desktopfile;
|
||||
using namespace linuxdeploy::desktopfile;
|
||||
using namespace linuxdeploy::core::log;
|
||||
|
||||
using namespace cimg_library;
|
||||
@@ -651,7 +651,7 @@ namespace linuxdeploy {
|
||||
}), paths.end());
|
||||
|
||||
for (const auto& path : paths) {
|
||||
desktopFiles.emplace_back(path);
|
||||
desktopFiles.emplace_back(path.string());
|
||||
}
|
||||
|
||||
return desktopFiles;
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
cmake_minimum_required(VERSION 3.0)
|
||||
|
||||
file(GLOB HEADERS ${PROJECT_SOURCE_DIR}/include/linuxdeploy/core/desktopfile/*.h)
|
||||
|
||||
add_library(linuxdeploy_core_desktopfile STATIC
|
||||
desktopfile.cpp
|
||||
desktopfileentry.cpp
|
||||
desktopfilereader.cpp
|
||||
desktopfilewriter.cpp
|
||||
desktopfilereader.h
|
||||
desktopfilewriter.h
|
||||
${HEADERS}
|
||||
)
|
||||
|
||||
target_include_directories(linuxdeploy_core_desktopfile PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
target_link_libraries(linuxdeploy_core_desktopfile PUBLIC linuxdeploy_util linuxdeploy_core_log ${BOOST_LIBS})
|
||||
@@ -1,214 +0,0 @@
|
||||
// local headers
|
||||
#include "linuxdeploy/core/desktopfile/exceptions.h"
|
||||
#include "linuxdeploy/core/desktopfile/desktopfile.h"
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "desktopfilereader.h"
|
||||
#include "desktopfilewriter.h"
|
||||
|
||||
using namespace linuxdeploy::core;
|
||||
using namespace linuxdeploy::core::log;
|
||||
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace desktopfile {
|
||||
class DesktopFile::PrivateData {
|
||||
public:
|
||||
bf::path path;
|
||||
sections_t data;
|
||||
|
||||
public:
|
||||
PrivateData() = default;
|
||||
|
||||
void copyData(const std::shared_ptr<PrivateData>& other) {
|
||||
path = other->path;
|
||||
data = other->data;
|
||||
}
|
||||
|
||||
public:
|
||||
bool isEmpty() const {
|
||||
return data.empty();
|
||||
}
|
||||
};
|
||||
|
||||
DesktopFile::DesktopFile() : d(std::make_shared<PrivateData>()) {}
|
||||
|
||||
DesktopFile::DesktopFile(const bf::path& path) : DesktopFile() {
|
||||
// if the file doesn't exist, an exception shall be thrown
|
||||
// otherwise, a user cannot know for sure whether a file was actually read (would need to check this
|
||||
// manually beforehand
|
||||
if (!bf::exists(path)) {
|
||||
throw IOError("Could not find file " + path.string());
|
||||
}
|
||||
|
||||
// will throw exceptions in case of issues
|
||||
read(path);
|
||||
};
|
||||
|
||||
DesktopFile::DesktopFile(std::istream& is) : DesktopFile() {
|
||||
// will throw exceptions in case of issues
|
||||
read(is);
|
||||
};
|
||||
|
||||
// copy constructor
|
||||
DesktopFile::DesktopFile(const DesktopFile& other) : DesktopFile() {
|
||||
d->copyData(other.d);
|
||||
}
|
||||
|
||||
// copy assignment constructor
|
||||
DesktopFile& DesktopFile::operator=(const DesktopFile& other) {
|
||||
if (this != &other) {
|
||||
d->copyData(other.d);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
// move assignment operator
|
||||
DesktopFile& DesktopFile::operator=(DesktopFile&& other) noexcept {
|
||||
if (this != &other) {
|
||||
d = other.d;
|
||||
other.d = nullptr;
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
void DesktopFile::read(const boost::filesystem::path& path) {
|
||||
setPath(path);
|
||||
|
||||
// clear data before reading a new file
|
||||
clear();
|
||||
|
||||
DesktopFileReader reader(path);
|
||||
d->data = std::move(reader.data());
|
||||
}
|
||||
|
||||
void DesktopFile::read(std::istream& is) {
|
||||
// clear data before reading a new file
|
||||
clear();
|
||||
|
||||
DesktopFileReader reader(is);
|
||||
d->data = reader.data();
|
||||
}
|
||||
|
||||
boost::filesystem::path DesktopFile::path() const {
|
||||
return d->path;
|
||||
}
|
||||
|
||||
void DesktopFile::setPath(const boost::filesystem::path& path) {
|
||||
d->path = path;
|
||||
}
|
||||
|
||||
bool DesktopFile::isEmpty() const {
|
||||
return d->isEmpty();
|
||||
}
|
||||
|
||||
void DesktopFile::clear() {
|
||||
d->data.clear();
|
||||
}
|
||||
|
||||
bool DesktopFile::save() const {
|
||||
return save(d->path);
|
||||
}
|
||||
|
||||
bool DesktopFile::save(const boost::filesystem::path& path) const {
|
||||
DesktopFileWriter writer(d->data);
|
||||
writer.save(path);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DesktopFile::save(std::ostream& os) const {
|
||||
DesktopFileWriter writer(d->data);
|
||||
writer.save(os);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DesktopFile::entryExists(const std::string& section, const std::string& key) const {
|
||||
auto it = d->data.find(section);
|
||||
if (it == d->data.end())
|
||||
return false;
|
||||
|
||||
return (it->second.find(key) != it->second.end());
|
||||
}
|
||||
|
||||
bool DesktopFile::setEntry(const std::string& section, const DesktopFileEntry& entry) {
|
||||
// check if value exists -- used for return value
|
||||
auto rv = entryExists(section, entry.key());
|
||||
|
||||
d->data[section][entry.key()] = entry;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool DesktopFile::setEntry(const std::string& section, DesktopFileEntry&& entry) {
|
||||
// check if value exists -- used for return value
|
||||
auto rv = entryExists(section, entry.key());
|
||||
|
||||
d->data[section][entry.key()] = entry;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool DesktopFile::getEntry(const std::string& section, const std::string& key, DesktopFileEntry& entry) const {
|
||||
if (!entryExists(section, key))
|
||||
return false;
|
||||
|
||||
entry = d->data[section][key];
|
||||
|
||||
// make sure keys are equal
|
||||
assert(key == entry.key());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DesktopFile::addDefaultKeys(const std::string& executableFileName) {
|
||||
ldLog() << "Adding default values to desktop file:" << path() << std::endl;
|
||||
|
||||
auto rv = true;
|
||||
|
||||
auto setDefault = [&rv, this](const std::string& section, const std::string& key, const std::string& value) {
|
||||
if (entryExists(section, key)) {
|
||||
DesktopFileEntry entry;
|
||||
|
||||
// this should never return false
|
||||
auto entryExists = getEntry(section, key, entry);
|
||||
assert(entryExists);
|
||||
|
||||
ldLog() << LD_WARNING << "Key exists, not modified:" << key << "(current value:" << entry.value() << LD_NO_SPACE << ")" << std::endl;
|
||||
rv = false;
|
||||
} else {
|
||||
auto entryOverwritten = setEntry(section, DesktopFileEntry(key, value));
|
||||
assert(!entryOverwritten);
|
||||
}
|
||||
};
|
||||
|
||||
setDefault("Desktop Entry", "Name", executableFileName);
|
||||
setDefault("Desktop Entry", "Exec", executableFileName);
|
||||
setDefault("Desktop Entry", "Icon", executableFileName);
|
||||
setDefault("Desktop Entry", "Type", "Application");
|
||||
setDefault("Desktop Entry", "Categories", "Utility;");
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
bool DesktopFile::validate() const {
|
||||
// FIXME: call desktop-file-validate
|
||||
return true;
|
||||
}
|
||||
|
||||
bool operator==(const DesktopFile& first, const DesktopFile& second) {
|
||||
return first.d->path == second.d->path && first.d->data == second.d->data;
|
||||
}
|
||||
|
||||
bool operator !=(const DesktopFile& first, const DesktopFile& second) {
|
||||
return !operator==(first, second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -1,125 +0,0 @@
|
||||
// library headers
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
// local headers
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "linuxdeploy/core/desktopfile/desktopfileentry.h"
|
||||
|
||||
using boost::lexical_cast;
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
using namespace log;
|
||||
|
||||
namespace desktopfile {
|
||||
class DesktopFileEntry::PrivateData {
|
||||
public:
|
||||
std::string key;
|
||||
std::string value;
|
||||
|
||||
public:
|
||||
void copyData(const std::shared_ptr<PrivateData>& other) {
|
||||
key = other->key;
|
||||
value = other->value;
|
||||
}
|
||||
|
||||
void assertValueNotEmpty() {
|
||||
if (value.empty())
|
||||
throw std::invalid_argument("value is empty");
|
||||
}
|
||||
};
|
||||
|
||||
DesktopFileEntry::DesktopFileEntry() : d(new PrivateData) {}
|
||||
|
||||
DesktopFileEntry::DesktopFileEntry(std::string key, std::string value) : DesktopFileEntry() {
|
||||
d->key = std::move(key);
|
||||
d->value = std::move(value);
|
||||
}
|
||||
|
||||
DesktopFileEntry::DesktopFileEntry(const DesktopFileEntry& other) : DesktopFileEntry() {
|
||||
d->copyData(other.d);
|
||||
}
|
||||
|
||||
DesktopFileEntry& DesktopFileEntry::operator=(const DesktopFileEntry& other) {
|
||||
if (this != &other) {
|
||||
d.reset(new PrivateData);
|
||||
d->copyData(other.d);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DesktopFileEntry& DesktopFileEntry::operator=(DesktopFileEntry&& other) noexcept {
|
||||
if (this != &other) {
|
||||
d = other.d;
|
||||
other.d = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DesktopFileEntry::operator==(const DesktopFileEntry& other) const {
|
||||
return d->key == other.d->key && d->value == other.d->value;
|
||||
}
|
||||
|
||||
bool DesktopFileEntry::operator!=(const DesktopFileEntry& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
bool DesktopFileEntry::isEmpty() const {
|
||||
return d->key.empty();
|
||||
}
|
||||
|
||||
const std::string& DesktopFileEntry::key() const {
|
||||
return d->key;
|
||||
}
|
||||
|
||||
const std::string& DesktopFileEntry::value() const {
|
||||
return d->value;
|
||||
}
|
||||
|
||||
int32_t DesktopFileEntry::asInt() const {
|
||||
d->assertValueNotEmpty();
|
||||
|
||||
return lexical_cast<int32_t>(value());
|
||||
}
|
||||
|
||||
int64_t DesktopFileEntry::asLong() const {
|
||||
d->assertValueNotEmpty();
|
||||
|
||||
return lexical_cast<int64_t>(value());
|
||||
}
|
||||
|
||||
double DesktopFileEntry::asDouble() const {
|
||||
d->assertValueNotEmpty();
|
||||
|
||||
return lexical_cast<double>(value());
|
||||
}
|
||||
|
||||
std::vector<std::string> DesktopFileEntry::parseStringList() const {
|
||||
const auto& value = this->value();
|
||||
|
||||
if (value.empty())
|
||||
return {};
|
||||
|
||||
if (value.back() != ';')
|
||||
ldLog() << LD_DEBUG << "desktop file string list does not end with semicolon:" << value
|
||||
<< std::endl;
|
||||
|
||||
std::vector<std::string> list;
|
||||
|
||||
std::stringstream ss(value);
|
||||
|
||||
std::string currentVal;
|
||||
while (std::getline(ss, currentVal, ';')) {
|
||||
// the last value will be empty, as in desktop files, lists shall end with a semicolon
|
||||
// therefore we skip all empty values (assuming that empty values in lists in desktop files don't make sense anyway)
|
||||
if (!currentVal.empty())
|
||||
list.emplace_back(currentVal);
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -1,216 +0,0 @@
|
||||
// system includes
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
// local headers
|
||||
#include "linuxdeploy/util/util.h"
|
||||
#include "linuxdeploy/core/desktopfile/desktopfileentry.h"
|
||||
#include "linuxdeploy/core/desktopfile/exceptions.h"
|
||||
#include "desktopfilereader.h"
|
||||
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace desktopfile {
|
||||
class DesktopFileReader::PrivateData {
|
||||
public:
|
||||
bf::path path;
|
||||
DesktopFile::sections_t sections;
|
||||
|
||||
public:
|
||||
bool isEmpty() {
|
||||
return sections.empty();
|
||||
}
|
||||
|
||||
void assertPathIsNotEmptyAndFileExists() {
|
||||
if (path.empty())
|
||||
throw IOError("empty path is not permitted");
|
||||
}
|
||||
|
||||
void copyData(const std::shared_ptr<PrivateData>& other) {
|
||||
path = other->path;
|
||||
sections = other->sections;
|
||||
}
|
||||
|
||||
void parse(std::istream& file) {
|
||||
std::string line;
|
||||
bool first = true;
|
||||
|
||||
std::string currentSectionName;
|
||||
|
||||
while (std::getline(file, line)) {
|
||||
if (first) {
|
||||
first = false;
|
||||
// said to allow handling of UTF-16/32 documents, not entirely sure why
|
||||
if (line[0] == static_cast<std::string::value_type>(0xEF)) {
|
||||
line.erase(0, 3);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!line.empty()) {
|
||||
auto len = line.length();
|
||||
if (len > 0 &&
|
||||
!((len >= 2 && (line[0] == '/' && line[1] == '/')) || (len >= 1 && line[0] == '#'))) {
|
||||
if (line[0] == '[') {
|
||||
if (line.find_last_of('[') != 0)
|
||||
throw ParseError("Multiple opening [ brackets");
|
||||
|
||||
// this line apparently introduces a new section
|
||||
auto closingBracketPos = line.find(']');
|
||||
auto lastClosingBracketPos = line.find_last_of(']');
|
||||
|
||||
if (closingBracketPos == std::string::npos)
|
||||
throw ParseError("No closing ] bracket in section header");
|
||||
else if (closingBracketPos != lastClosingBracketPos)
|
||||
throw ParseError("Two or more closing ] brackets in section header");
|
||||
|
||||
size_t length = len - 2;
|
||||
auto title = line.substr(1, closingBracketPos - 1);
|
||||
|
||||
// set up the new section
|
||||
sections.insert(std::make_pair(title, DesktopFile::section_t()));
|
||||
currentSectionName = std::move(title);
|
||||
} else {
|
||||
// we require at least one section to be present in the desktop file
|
||||
if (currentSectionName.empty())
|
||||
throw ParseError("No section in desktop file");
|
||||
|
||||
auto delimiterPos = line.find('=');
|
||||
if (delimiterPos == std::string::npos)
|
||||
throw ParseError("No = key/value delimiter found");
|
||||
|
||||
// this line should be a normal key-value pair
|
||||
std::string key = line.substr(0, delimiterPos);
|
||||
std::string value = line.substr(delimiterPos + 1, line.size());
|
||||
|
||||
// we can strip away any sort of leading or trailing whitespace safely
|
||||
linuxdeploy::util::trim(key);
|
||||
linuxdeploy::util::trim(value);
|
||||
|
||||
// empty keys are not allowed for obvious reasons
|
||||
if (key.empty())
|
||||
throw ParseError("Empty keys are not allowed");
|
||||
|
||||
// keys may only contain A-Za-z- characters according to specification
|
||||
for (const char c : key) {
|
||||
if (!(
|
||||
(c >= 'A' && c <= 'Z') ||
|
||||
(c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') ||
|
||||
(c == '-') ||
|
||||
// FIXME: remove this hack after introducing localization support to
|
||||
// conform to desktop file spec again
|
||||
(c == '[') || (c == ']')
|
||||
)
|
||||
) {
|
||||
throw ParseError(
|
||||
"Key " + key + " contains invalid character " + std::string{c}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (std::count(key.begin(), key.end(), '[') > 1 ||
|
||||
std::count(key.begin(), key.end(), ']') > 1 ||
|
||||
// make sure that both [ and ] are present
|
||||
(key.find('[') != std::string::npos && key.find(']') == std::string::npos) ||
|
||||
(key.find('[') == std::string::npos && key.find(']') != std::string::npos) ||
|
||||
// disallow empty locale names
|
||||
(key.find('[') != std::string::npos && key.find(']') != std::string::npos && (key.find(']') - key.find('[')) < 2) ||
|
||||
// ensure order of [ and ]
|
||||
(key.find('[') != std::string::npos && key.find('[' ) > key.find(']'))
|
||||
) {
|
||||
throw ParseError("Invalid localization syntax used in key " + key);
|
||||
}
|
||||
|
||||
auto& section = sections[currentSectionName];
|
||||
|
||||
// keys must be unique in the same section
|
||||
if (section.find(key) != section.end())
|
||||
throw ParseError("Key " + key + " found more than once");
|
||||
|
||||
section[key] = DesktopFileEntry(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
DesktopFileReader::DesktopFileReader() : d(new PrivateData) {}
|
||||
|
||||
DesktopFileReader::DesktopFileReader(boost::filesystem::path path) : DesktopFileReader() {
|
||||
d->path = std::move(path);
|
||||
d->assertPathIsNotEmptyAndFileExists();
|
||||
|
||||
std::ifstream ifs(d->path.string());
|
||||
if (!ifs)
|
||||
throw IOError("could not open file: " + d->path.string());
|
||||
|
||||
d->parse(ifs);
|
||||
}
|
||||
|
||||
DesktopFileReader::DesktopFileReader(std::istream& is) : DesktopFileReader() {
|
||||
d->parse(is);
|
||||
}
|
||||
|
||||
DesktopFileReader::DesktopFileReader(const DesktopFileReader& other) : DesktopFileReader() {
|
||||
d->copyData(other.d);
|
||||
}
|
||||
|
||||
DesktopFileReader& DesktopFileReader::operator=(const DesktopFileReader& other) {
|
||||
if (this != &other) {
|
||||
// set up a new instance of PrivateData, and copy data over from other object
|
||||
d.reset(new PrivateData);
|
||||
d->copyData(other.d);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DesktopFileReader& DesktopFileReader::operator=(DesktopFileReader&& other) noexcept {
|
||||
if (this != &other) {
|
||||
// move other object's data into this one, and remove reference there
|
||||
d = other.d;
|
||||
other.d = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DesktopFileReader::isEmpty() const {
|
||||
return d->isEmpty();
|
||||
}
|
||||
|
||||
bool DesktopFileReader::operator==(const DesktopFileReader& other) const {
|
||||
return d->path == other.d->path && d->sections == other.d->sections;
|
||||
}
|
||||
|
||||
bool DesktopFileReader::operator!=(const DesktopFileReader& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
boost::filesystem::path DesktopFileReader::path() const {
|
||||
return d->path;
|
||||
}
|
||||
|
||||
DesktopFile::sections_t DesktopFileReader::data() const {
|
||||
return d->sections;
|
||||
}
|
||||
|
||||
DesktopFile::section_t DesktopFileReader::operator[](const std::string& name) const {
|
||||
auto it = d->sections.find(name);
|
||||
|
||||
// the map would lazy-initialize a new entry in case the section doesn't exist
|
||||
// therefore explicitly checking whether the section exists, throwing an exception in case it does not
|
||||
if (it == d->sections.end())
|
||||
throw UnknownSectionError(name);
|
||||
|
||||
return it->second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
|
||||
// library includes
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// local includes
|
||||
#include "linuxdeploy/core/desktopfile/desktopfile.h"
|
||||
#include "linuxdeploy/core/desktopfile/desktopfileentry.h"
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace desktopfile {
|
||||
class DesktopFileReader {
|
||||
private:
|
||||
// opaque data class pattern
|
||||
class PrivateData;
|
||||
|
||||
std::shared_ptr<PrivateData> d;
|
||||
|
||||
public:
|
||||
// default constructor
|
||||
DesktopFileReader();
|
||||
|
||||
// construct from path
|
||||
explicit DesktopFileReader(boost::filesystem::path path);
|
||||
|
||||
// construct from existing istream
|
||||
explicit DesktopFileReader(std::istream& is);
|
||||
|
||||
// copy constructor
|
||||
DesktopFileReader(const DesktopFileReader& other);
|
||||
|
||||
// copy assignment constructor
|
||||
DesktopFileReader& operator=(const DesktopFileReader& other);
|
||||
|
||||
// move assignment operator
|
||||
DesktopFileReader& operator=(DesktopFileReader&& other) noexcept;
|
||||
|
||||
// equality operator
|
||||
bool operator==(const DesktopFileReader& other) const;
|
||||
|
||||
// inequality operator
|
||||
bool operator!=(const DesktopFileReader& other) const;
|
||||
|
||||
public:
|
||||
// checks whether parsed data is available
|
||||
bool isEmpty() const;
|
||||
|
||||
// returns desktop file path
|
||||
boost::filesystem::path path() const;
|
||||
|
||||
// get a specific section from the parsed data
|
||||
// throws std::range_error if section does not exist
|
||||
DesktopFile::section_t operator[](const std::string& name) const;
|
||||
|
||||
// get copy of internal data storage
|
||||
// can be handed to a DesktopFileWriter instance, or to manually hack on the data
|
||||
DesktopFile::sections_t data() const;
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,102 +0,0 @@
|
||||
// system includes
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
// local headers
|
||||
#include "linuxdeploy/util/util.h"
|
||||
#include "linuxdeploy/core/desktopfile/exceptions.h"
|
||||
#include "desktopfilewriter.h"
|
||||
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace desktopfile {
|
||||
class DesktopFileWriter::PrivateData {
|
||||
public:
|
||||
DesktopFile::sections_t data;
|
||||
|
||||
public:
|
||||
void copyData(const std::shared_ptr<PrivateData>& other) {
|
||||
data = other->data;
|
||||
}
|
||||
|
||||
std::string dumpString() {
|
||||
std::stringstream ss;
|
||||
|
||||
for (const auto& section : data) {
|
||||
ss << "[" << section.first << "]" << std::endl;
|
||||
|
||||
for (const auto& pair : section.second) {
|
||||
auto key = pair.first;
|
||||
util::trim(key);
|
||||
auto value = pair.second.value();
|
||||
util::trim(value);
|
||||
ss << key << "=" << value << std::endl;
|
||||
}
|
||||
|
||||
// insert an empty line between sections
|
||||
ss << std::endl;
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
};
|
||||
|
||||
DesktopFileWriter::DesktopFileWriter() : d(std::make_shared<PrivateData>()) {}
|
||||
|
||||
DesktopFileWriter::DesktopFileWriter(DesktopFile::sections_t data) : DesktopFileWriter() {
|
||||
d->data = std::move(data);
|
||||
}
|
||||
|
||||
DesktopFileWriter::DesktopFileWriter(const DesktopFileWriter& other) : DesktopFileWriter() {
|
||||
d->copyData(other.d);
|
||||
}
|
||||
|
||||
DesktopFileWriter& DesktopFileWriter::operator=(const DesktopFileWriter& other) {
|
||||
if (this != &other) {
|
||||
// set up a new instance of PrivateData, and copy data over from other object
|
||||
d.reset(new PrivateData);
|
||||
d->copyData(other.d);
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
DesktopFileWriter& DesktopFileWriter::operator=(DesktopFileWriter&& other) noexcept {
|
||||
if (this != &other) {
|
||||
// move other object's data into this one, and remove reference there
|
||||
d = other.d;
|
||||
other.d = nullptr;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool DesktopFileWriter::operator==(const DesktopFileWriter& other) const {
|
||||
return d->data == other.d->data;
|
||||
}
|
||||
|
||||
bool DesktopFileWriter::operator!=(const DesktopFileWriter& other) const {
|
||||
return !operator==(other);
|
||||
}
|
||||
|
||||
DesktopFile::sections_t DesktopFileWriter::data() const {
|
||||
return d->data;
|
||||
}
|
||||
|
||||
void DesktopFileWriter::save(const boost::filesystem::path& path) {
|
||||
std::ofstream ofs(path.string());
|
||||
|
||||
if (!ofs)
|
||||
throw IOError("could not open file for writing: " + path.string());
|
||||
|
||||
save(ofs);
|
||||
}
|
||||
|
||||
void DesktopFileWriter::save(std::ostream& os) {
|
||||
os << d->dumpString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,58 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
// system includes
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
|
||||
// library includes
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// local includes
|
||||
#include "linuxdeploy/core/desktopfile/desktopfile.h"
|
||||
#include "linuxdeploy/core/desktopfile/desktopfileentry.h"
|
||||
|
||||
namespace linuxdeploy {
|
||||
namespace core {
|
||||
namespace desktopfile {
|
||||
class DesktopFileWriter {
|
||||
private:
|
||||
// opaque data class pattern
|
||||
class PrivateData;
|
||||
std::shared_ptr<PrivateData> d;
|
||||
|
||||
public:
|
||||
// default constructor
|
||||
DesktopFileWriter();
|
||||
|
||||
// construct from data
|
||||
explicit DesktopFileWriter(DesktopFile::sections_t data);
|
||||
|
||||
// copy constructor
|
||||
DesktopFileWriter(const DesktopFileWriter& other);
|
||||
|
||||
// copy assignment constructor
|
||||
DesktopFileWriter& operator=(const DesktopFileWriter& other);
|
||||
|
||||
// move assignment operator
|
||||
DesktopFileWriter& operator=(DesktopFileWriter&& other) noexcept;
|
||||
|
||||
// equality operator
|
||||
bool operator==(const DesktopFileWriter& other) const;
|
||||
|
||||
// inequality operator
|
||||
bool operator!=(const DesktopFileWriter& other) const;
|
||||
|
||||
public:
|
||||
// returns desktop file path
|
||||
DesktopFile::sections_t data() const;
|
||||
|
||||
public:
|
||||
// save to given path
|
||||
void save(const boost::filesystem::path& path);
|
||||
|
||||
// save to given ostream
|
||||
void save(std::ostream& os);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -7,13 +7,14 @@
|
||||
|
||||
// local headers
|
||||
#include "linuxdeploy/core/appdir.h"
|
||||
#include "linuxdeploy/core/desktopfile/desktopfile.h"
|
||||
#include "linuxdeploy/desktopfile/desktopfile.h"
|
||||
#include "linuxdeploy/core/elf.h"
|
||||
#include "linuxdeploy/core/log.h"
|
||||
#include "linuxdeploy/plugin/plugin.h"
|
||||
#include "linuxdeploy/util/util.h"
|
||||
#include "core.h"
|
||||
|
||||
using namespace linuxdeploy;
|
||||
using namespace linuxdeploy::core;
|
||||
using namespace linuxdeploy::core::log;
|
||||
using namespace linuxdeploy::util;
|
||||
@@ -249,7 +250,7 @@ int main(int argc, char** argv) {
|
||||
ldLog() << LD_WARNING << "Tried to overwrite existing entries in desktop file:" << desktopFilePath << std::endl;
|
||||
}
|
||||
|
||||
if (!desktopFile.save(desktopFilePath)) {
|
||||
if (!desktopFile.save(desktopFilePath.string())) {
|
||||
ldLog() << LD_ERROR << "Failed to save desktop file:" << desktopFilePath << std::endl;
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -37,7 +37,3 @@ add_test(test_linuxdeploy test_linuxdeploy)
|
||||
|
||||
# make sure library and executable are built before test_appdir
|
||||
add_dependencies(test_linuxdeploy simple_library simple_executable)
|
||||
|
||||
|
||||
# include desktop file tests
|
||||
add_subdirectory(desktopfile)
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
# build a single test binary
|
||||
add_executable(test_desktopfile
|
||||
test_desktopfile.cpp
|
||||
test_desktopfileentry.cpp
|
||||
test_desktopfilereader.cpp
|
||||
test_desktopfilewriter.cpp
|
||||
test_desktopfile_conformance.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
target_link_libraries(test_desktopfile PRIVATE linuxdeploy_core_desktopfile gtest)
|
||||
|
||||
set_property(TARGET test_desktopfile
|
||||
PROPERTY COMPILE_FLAGS "-DDESKTOP_FILE_PATH=\\\"${TEST_DATA_DIR}/simple_app.desktop\\\""
|
||||
)
|
||||
|
||||
add_test(test_desktopfile test_desktopfile)
|
||||
@@ -1,15 +0,0 @@
|
||||
// library headers
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
// local headers
|
||||
#include <linuxdeploy/core/log.h>
|
||||
|
||||
using namespace linuxdeploy::core::log;
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
// set loglevel to prevent unnecessary log messages
|
||||
ldLog::setVerbosity(LD_ERROR);
|
||||
|
||||
::testing::InitGoogleTest(&argc, argv);
|
||||
return RUN_ALL_TESTS();
|
||||
}
|
||||
@@ -1,279 +0,0 @@
|
||||
// library headers
|
||||
#include <gtest/gtest.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// local headers
|
||||
#include "linuxdeploy/core/desktopfile/desktopfile.h"
|
||||
#include "linuxdeploy/core/desktopfile/exceptions.h"
|
||||
#include "../../src/core/desktopfile/desktopfilereader.h"
|
||||
|
||||
using boost::bad_lexical_cast;
|
||||
using namespace linuxdeploy::core::desktopfile;
|
||||
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
class DesktopFileTest : public ::testing::Test {
|
||||
public:
|
||||
std::string testType;
|
||||
std::string testName;
|
||||
std::string testExec;
|
||||
std::string testIcon;
|
||||
std::string testDesktopFile;
|
||||
|
||||
private:
|
||||
void SetUp() override {
|
||||
testType = "Application";
|
||||
testName = "Simple Application";
|
||||
testExec = "simple_app";
|
||||
testIcon = "simple_app";
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop Entry]" << std::endl
|
||||
<< "Type=Application" << std::endl
|
||||
<< "Name=Simple Application" << std::endl
|
||||
<< "Exec=" << testExec << std::endl
|
||||
<< "Icon=" << testIcon << std::endl;
|
||||
|
||||
testDesktopFile = ss.str();
|
||||
}
|
||||
|
||||
void TearDown() override {}
|
||||
|
||||
public:
|
||||
void assertIsTestDesktopFile(const DesktopFile& file, ssize_t expectedKeys = -1) {
|
||||
std::stringstream ss;
|
||||
file.save(ss);
|
||||
|
||||
assertHasTestDesktopFileKeys(ss, expectedKeys);
|
||||
}
|
||||
|
||||
void assertHasTestDesktopFileKeys(std::stringstream& ss, ssize_t expectedKeys = -1) const {
|
||||
DesktopFileReader reader(ss);
|
||||
|
||||
if (expectedKeys < 0)
|
||||
expectedKeys = 4;
|
||||
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Name"].value(), testName);
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Exec"].value(), testExec);
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Icon"].value(), testIcon);
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Type"].value(), testType);
|
||||
EXPECT_EQ(reader["Desktop Entry"].size(), expectedKeys);
|
||||
}
|
||||
};
|
||||
|
||||
TEST_F(DesktopFileTest, testDefaultConstructor) {
|
||||
DesktopFile file;
|
||||
EXPECT_TRUE(file.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testPathConstructor) {
|
||||
ASSERT_THROW(DesktopFile nonExistingPath("/a/b/c/d/e/f/g/h/1/2/3/4/5/6/7/8"), IOError);
|
||||
|
||||
DesktopFile emptyFile("/dev/null");
|
||||
EXPECT_TRUE(emptyFile.isEmpty());
|
||||
|
||||
DesktopFile file(DESKTOP_FILE_PATH);
|
||||
EXPECT_FALSE(file.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testStreamConstructor) {
|
||||
std::stringstream emptyString;
|
||||
DesktopFile emptyFile(emptyString);
|
||||
EXPECT_TRUE(emptyFile.isEmpty());
|
||||
|
||||
std::stringstream ss;
|
||||
ss << testDesktopFile;
|
||||
DesktopFile file(ss);
|
||||
EXPECT_FALSE(file.isEmpty());
|
||||
|
||||
assertIsTestDesktopFile(file);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testCopyConstructor) {
|
||||
DesktopFile empty;
|
||||
EXPECT_TRUE(empty == empty);
|
||||
EXPECT_FALSE(empty != empty);
|
||||
|
||||
DesktopFile copyOfEmpty(empty);
|
||||
EXPECT_TRUE(empty == copyOfEmpty);
|
||||
EXPECT_FALSE(empty != copyOfEmpty);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << testDesktopFile;
|
||||
DesktopFile file(ss);
|
||||
|
||||
DesktopFile copy(file);
|
||||
|
||||
EXPECT_TRUE(file == copy);
|
||||
EXPECT_FALSE(file != copy);
|
||||
|
||||
assertIsTestDesktopFile(file);
|
||||
assertIsTestDesktopFile(copy);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testCopyAssignmentConstructor) {
|
||||
std::stringstream ss;
|
||||
ss << testDesktopFile;
|
||||
DesktopFile file(ss);
|
||||
|
||||
DesktopFile copy;
|
||||
copy = file;
|
||||
|
||||
EXPECT_TRUE(file == copy);
|
||||
EXPECT_FALSE(file != copy);
|
||||
|
||||
assertIsTestDesktopFile(file);
|
||||
assertIsTestDesktopFile(copy);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testMoveAssignmentConstructor) {
|
||||
std::stringstream ss;
|
||||
ss << testDesktopFile;
|
||||
DesktopFile file(ss);
|
||||
|
||||
DesktopFile copy;
|
||||
copy = std::move(file);
|
||||
EXPECT_FALSE(copy.isEmpty());
|
||||
|
||||
assertIsTestDesktopFile(copy);
|
||||
}
|
||||
|
||||
void assertDefaultKeysExistInDesktopFile(const DesktopFile& file) {
|
||||
DesktopFileEntry entry;
|
||||
|
||||
for (const auto& key : {"Name", "Exec", "Icon", "Type"})
|
||||
EXPECT_TRUE(file.getEntry("Desktop Entry", key, entry)) << "Could not find key in desktop file: " << key;
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testAddDefaultValues) {
|
||||
const auto& value = "testExecutable";
|
||||
|
||||
DesktopFile file;
|
||||
file.addDefaultKeys(value);
|
||||
|
||||
// make sure keys exist in desktop files
|
||||
assertDefaultKeysExistInDesktopFile(file);
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
file.save(ss);
|
||||
|
||||
DesktopFileReader reader(ss);
|
||||
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Name"].value(), value);
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Exec"].value(), value);
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Icon"].value(), value);
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Type"].value(), "Application");
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Categories"].value(), "Utility;");
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testAddDefaultValuesExistingKeys) {
|
||||
const auto& value = "testExecutable";
|
||||
|
||||
std::stringstream iss;
|
||||
iss << "[Desktop Entry]" << std::endl
|
||||
<< "Name=A Different Name" << std::endl
|
||||
<< "Exec=a_different_exec" << std::endl;
|
||||
|
||||
DesktopFile file(iss);
|
||||
file.addDefaultKeys(value);
|
||||
|
||||
// make sure keys exist in desktop files
|
||||
assertDefaultKeysExistInDesktopFile(file);
|
||||
|
||||
file.save(std::cout);
|
||||
|
||||
std::stringstream ss;
|
||||
|
||||
file.save(ss);
|
||||
|
||||
DesktopFileReader reader(ss);
|
||||
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Name"].value(), "A Different Name");
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Exec"].value(), "a_different_exec");
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Icon"].value(), value);
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Categories"].value(), "Utility;");
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testAddDefaultValuesNoOverwrite) {
|
||||
const auto& value = "testExecutable";
|
||||
|
||||
std::stringstream iss;
|
||||
iss << testDesktopFile;
|
||||
DesktopFile file(iss);
|
||||
|
||||
file.addDefaultKeys(value);
|
||||
|
||||
// make sure keys exist in desktop files
|
||||
assertDefaultKeysExistInDesktopFile(file);
|
||||
|
||||
{
|
||||
std::stringstream oss;
|
||||
file.save(oss);
|
||||
|
||||
// keys should not have been overwritten, and should still have the original values
|
||||
// however, there should be 5 keys, as the Categories one is coming from the testDesktopFile string
|
||||
assertHasTestDesktopFileKeys(oss, 5);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream oss;
|
||||
file.save(oss);
|
||||
|
||||
DesktopFileReader reader(oss);
|
||||
EXPECT_EQ(reader["Desktop Entry"]["Categories"].parseStringList(), std::vector<std::string>({"Utility"}));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testSaveToPath) {
|
||||
std::stringstream ins;
|
||||
ins << testDesktopFile;
|
||||
|
||||
DesktopFile file(ins);
|
||||
|
||||
EXPECT_NO_THROW(file.save("/dev/null"));
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testSave) {
|
||||
DesktopFile file("/dev/null");
|
||||
EXPECT_NO_THROW(file.save());
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testSaveToStream) {
|
||||
std::stringstream ins;
|
||||
ins << testDesktopFile;
|
||||
|
||||
DesktopFile file(ins);
|
||||
|
||||
std::stringstream outs;
|
||||
file.save(outs);
|
||||
|
||||
assertHasTestDesktopFileKeys(outs);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testEquality) {
|
||||
std::stringstream ins0(testDesktopFile);
|
||||
std::stringstream ins1(testDesktopFile);
|
||||
|
||||
DesktopFile file0(ins0);
|
||||
DesktopFile file1(ins1);
|
||||
|
||||
EXPECT_TRUE(file0 == file1);
|
||||
EXPECT_FALSE(file0 != file1);
|
||||
|
||||
EXPECT_EQ(file0, file1);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileTest, testInequality) {
|
||||
std::stringstream ins;
|
||||
ins << testDesktopFile;
|
||||
|
||||
DesktopFile file(ins);
|
||||
|
||||
DesktopFile emptyFile;
|
||||
|
||||
EXPECT_TRUE(file != emptyFile);
|
||||
EXPECT_FALSE(file == emptyFile);
|
||||
|
||||
EXPECT_NE(file, emptyFile);
|
||||
}
|
||||
@@ -1,90 +0,0 @@
|
||||
// library headers
|
||||
#include <gtest/gtest.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// local headers
|
||||
#include "linuxdeploy/core/desktopfile/desktopfile.h"
|
||||
#include "linuxdeploy/core/desktopfile/exceptions.h"
|
||||
|
||||
using boost::bad_lexical_cast;
|
||||
using namespace linuxdeploy::core::desktopfile;
|
||||
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
class DesktopFileConformanceTest : public ::testing::Test {
|
||||
private:
|
||||
void SetUp() override {}
|
||||
void TearDown() override {}
|
||||
};
|
||||
|
||||
TEST_F(DesktopFileConformanceTest, testBasicFormatInvalidKeyCharacters) {
|
||||
// test conformance with a couple of invalid values
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop Entry]" << std::endl
|
||||
<< "no spaces in key=foo" << std::endl;
|
||||
|
||||
EXPECT_THROW(DesktopFile file(ss), ParseError);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop Entry]" << std::endl
|
||||
<< "UmlautTestÄöÜ=foo" << std::endl;
|
||||
|
||||
EXPECT_THROW(DesktopFile file(ss), ParseError);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop Entry]" << std::endl
|
||||
<< "NoUnderscores_=foo" << std::endl;
|
||||
|
||||
EXPECT_THROW(DesktopFile file(ss), ParseError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileConformanceTest, testBasicFormatValidKeyCharacters) {
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop Entry]" << std::endl
|
||||
<< "TestKey=foo" << std::endl;
|
||||
|
||||
EXPECT_NO_THROW(DesktopFile file(ss));
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop Entry]" << std::endl
|
||||
<< "4242trolol0=foo" << std::endl;
|
||||
|
||||
EXPECT_NO_THROW(DesktopFile file(ss));
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop Entry]" << std::endl
|
||||
<< "----=foo" << std::endl;
|
||||
|
||||
EXPECT_NO_THROW(DesktopFile file(ss));
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop Entry]" << std::endl
|
||||
<< "allLowerCase=foo" << std::endl;
|
||||
|
||||
EXPECT_NO_THROW(DesktopFile file(ss));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileConformanceTest, testBasicFormatCheckKeysUnique) {
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop Entry]" << std::endl
|
||||
<< "foo=bar" << std::endl
|
||||
<< "foo=baz" << std::endl;
|
||||
|
||||
EXPECT_THROW(DesktopFile file(ss), ParseError);
|
||||
}
|
||||
}
|
||||
@@ -1,146 +0,0 @@
|
||||
// library headers
|
||||
#include <gtest/gtest.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
// local headers
|
||||
#include "linuxdeploy/core/desktopfile/desktopfileentry.h"
|
||||
|
||||
using boost::bad_lexical_cast;
|
||||
using namespace linuxdeploy::core::desktopfile;
|
||||
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
class DesktopFileEntryTest : public ::testing::Test {
|
||||
public:
|
||||
const std::string key;
|
||||
const std::string value;
|
||||
|
||||
protected:
|
||||
DesktopFileEntryTest() : key("testKey"), value("testValue") {}
|
||||
|
||||
private:
|
||||
void SetUp() override {}
|
||||
|
||||
void TearDown() override {}
|
||||
};
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testDefaultConstructor) {
|
||||
DesktopFileEntry entry;
|
||||
EXPECT_TRUE(entry.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testKeyValueConstructor) {
|
||||
DesktopFileEntry entry(key, value);
|
||||
EXPECT_FALSE(entry.isEmpty());
|
||||
EXPECT_EQ(entry.key(), key);
|
||||
EXPECT_EQ(entry.value(), value);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testGetters) {
|
||||
DesktopFileEntry entry(key, value);
|
||||
EXPECT_EQ(entry.key(), key);
|
||||
EXPECT_EQ(entry.value(), value);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testEqualityAndInequalityOperators) {
|
||||
DesktopFileEntry emptyEntry;
|
||||
EXPECT_TRUE(emptyEntry == emptyEntry);
|
||||
EXPECT_FALSE(emptyEntry != emptyEntry);
|
||||
|
||||
DesktopFileEntry nonEmptyEntry(key, value);
|
||||
EXPECT_NE(emptyEntry, nonEmptyEntry);
|
||||
|
||||
DesktopFileEntry nonEmptyEntryWithDifferentValue(key, value + "abc");
|
||||
EXPECT_NE(nonEmptyEntry, nonEmptyEntryWithDifferentValue);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testCopyConstructor) {
|
||||
DesktopFileEntry entry(key, value);
|
||||
EXPECT_FALSE(entry.isEmpty());
|
||||
|
||||
DesktopFileEntry copy = entry;
|
||||
EXPECT_FALSE(copy.isEmpty());
|
||||
|
||||
EXPECT_EQ(entry, copy);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testCopyAssignmentConstructor) {
|
||||
DesktopFileEntry entry;
|
||||
EXPECT_TRUE(entry.isEmpty());
|
||||
|
||||
DesktopFileEntry otherEntry(key, value);
|
||||
EXPECT_FALSE(otherEntry.isEmpty());
|
||||
|
||||
entry = otherEntry;
|
||||
EXPECT_EQ(entry.key(), key);
|
||||
EXPECT_EQ(entry.value(), value);
|
||||
|
||||
// test self-assignment
|
||||
entry = entry;
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testMoveAssignmentConstructor) {
|
||||
DesktopFileEntry entry;
|
||||
EXPECT_TRUE(entry.isEmpty());
|
||||
|
||||
DesktopFileEntry otherEntry(key, value);
|
||||
EXPECT_FALSE(otherEntry.isEmpty());
|
||||
|
||||
entry = std::move(otherEntry);
|
||||
EXPECT_EQ(entry.key(), key);
|
||||
EXPECT_EQ(entry.value(), value);
|
||||
|
||||
// test self-assignment
|
||||
entry = std::move(entry);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testConversionToInt) {
|
||||
DesktopFileEntry intEntry(key, "1234");
|
||||
EXPECT_EQ(intEntry.asInt(), 1234);
|
||||
|
||||
DesktopFileEntry brokenValueEntry(key, "abcd");
|
||||
ASSERT_THROW(brokenValueEntry.asInt(), bad_lexical_cast);
|
||||
|
||||
DesktopFileEntry emptyEntry(key, "");
|
||||
ASSERT_THROW(emptyEntry.asInt(), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testConversionToLong) {
|
||||
DesktopFileEntry intEntry(key, "123456789123456789");
|
||||
EXPECT_EQ(intEntry.asLong(), 123456789123456789L);
|
||||
|
||||
DesktopFileEntry brokenValueEntry(key, "abcd");
|
||||
ASSERT_THROW(brokenValueEntry.asLong(), bad_lexical_cast);
|
||||
|
||||
DesktopFileEntry emptyEntry(key, "");
|
||||
ASSERT_THROW(emptyEntry.asLong(), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testConversionToDouble) {
|
||||
DesktopFileEntry doubleEntry(key, "1.234567");
|
||||
EXPECT_NEAR(doubleEntry.asDouble(), 1.234567, 0.00000001);
|
||||
|
||||
DesktopFileEntry brokenValueEntry(key, "abcd");
|
||||
ASSERT_THROW(brokenValueEntry.asDouble(), bad_lexical_cast);
|
||||
|
||||
DesktopFileEntry emptyEntry(key, "");
|
||||
ASSERT_THROW(emptyEntry.asDouble(), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileEntryTest, testParsingStringList) {
|
||||
DesktopFileEntry emptyEntry(key, "");
|
||||
EXPECT_EQ(emptyEntry.parseStringList(), std::vector<std::string>({}));
|
||||
|
||||
DesktopFileEntry nonListEntry(key, value);
|
||||
EXPECT_EQ(nonListEntry.parseStringList(), std::vector<std::string>({value}));
|
||||
|
||||
DesktopFileEntry listEntry(key, "val1;val2;");
|
||||
EXPECT_EQ(listEntry.parseStringList(), std::vector<std::string>({"val1", "val2"}));
|
||||
|
||||
DesktopFileEntry listEntryWithoutTrailingSemicolon(key, "val1;val2");
|
||||
EXPECT_EQ(listEntry.parseStringList(), std::vector<std::string>({"val1", "val2"}));
|
||||
|
||||
DesktopFileEntry listEntryWithEmptyItems(key, "val1;;;val2;;");
|
||||
EXPECT_EQ(listEntry.parseStringList(), std::vector<std::string>({"val1", "val2"}));
|
||||
}
|
||||
@@ -1,468 +0,0 @@
|
||||
// library headers
|
||||
#include <gtest/gtest.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// local headers
|
||||
#include "../../src/core/desktopfile/desktopfilereader.h"
|
||||
#include "linuxdeploy/core/desktopfile/exceptions.h"
|
||||
|
||||
using namespace linuxdeploy::core::desktopfile;
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
class DesktopFileReaderTest : public ::testing::Test {
|
||||
void SetUp() override {}
|
||||
void TearDown() override {}
|
||||
};
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testDefaultConstructor) {
|
||||
DesktopFileReader reader;
|
||||
EXPECT_TRUE(reader.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testPathConstructor) {
|
||||
bf::path path = "/dev/null";
|
||||
|
||||
DesktopFileReader reader(path);
|
||||
EXPECT_TRUE(reader.isEmpty());
|
||||
|
||||
ASSERT_THROW(DesktopFileReader("/no/such/file/or/directory"), IOError);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testStreamConstructor) {
|
||||
std::stringstream ss;
|
||||
DesktopFileReader reader(ss);
|
||||
|
||||
EXPECT_TRUE(reader.isEmpty());
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testPathConstructorWithEmptyPath) {
|
||||
ASSERT_THROW(DesktopFileReader(""), IOError);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testPathConstructorWithNonExistingPath) {
|
||||
ASSERT_THROW(DesktopFileReader("/no/such/path/42"), IOError);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testEqualityAndInequalityOperators) {
|
||||
{
|
||||
DesktopFileReader emptyReader;
|
||||
EXPECT_TRUE(emptyReader == emptyReader);
|
||||
EXPECT_FALSE(emptyReader != emptyReader);
|
||||
}
|
||||
|
||||
{
|
||||
// make sure that files with different paths are recognized as different
|
||||
DesktopFile nullFile("/dev/null");
|
||||
DesktopFile fileWithoutPath;
|
||||
|
||||
EXPECT_TRUE(nullFile != fileWithoutPath);
|
||||
EXPECT_FALSE(nullFile == fileWithoutPath);
|
||||
}
|
||||
|
||||
{
|
||||
// make sure that files with different contents are recognized as different
|
||||
DesktopFile fileWithContents;
|
||||
fileWithContents.setEntry("Desktop Entry", DesktopFileEntry("test", "test"));
|
||||
|
||||
DesktopFile emptyFile;
|
||||
|
||||
EXPECT_TRUE(emptyFile != fileWithContents);
|
||||
EXPECT_FALSE(emptyFile == fileWithContents);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testCopyConstructor) {
|
||||
{
|
||||
bf::path path = "/dev/null";
|
||||
|
||||
DesktopFileReader reader(path);
|
||||
EXPECT_TRUE(reader.isEmpty());
|
||||
|
||||
DesktopFileReader copy = reader;
|
||||
EXPECT_TRUE(copy.isEmpty());
|
||||
|
||||
EXPECT_EQ(reader, copy);
|
||||
}
|
||||
|
||||
{
|
||||
// make sure that contents are copied, too
|
||||
DesktopFile file;
|
||||
file.setEntry("Desktop Entry", DesktopFileEntry("test", "test"));
|
||||
|
||||
std::stringstream ss;
|
||||
file.save(ss);
|
||||
|
||||
DesktopFileReader reader(ss);
|
||||
DesktopFileReader copy(reader);
|
||||
|
||||
EXPECT_TRUE(reader.data() == copy.data());
|
||||
EXPECT_TRUE(reader == copy);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testCopyAssignmentConstructor) {
|
||||
bf::path path = "/dev/null";
|
||||
|
||||
DesktopFileReader reader;
|
||||
EXPECT_TRUE(reader.isEmpty());
|
||||
|
||||
DesktopFileReader otherReader(path);
|
||||
EXPECT_TRUE(otherReader.isEmpty());
|
||||
|
||||
reader = otherReader;
|
||||
EXPECT_EQ(reader.path(), path);
|
||||
|
||||
// test self-assignment
|
||||
reader = reader;
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testMoveAssignmentConstructor) {
|
||||
bf::path path = "/dev/null";
|
||||
|
||||
DesktopFileReader reader;
|
||||
EXPECT_TRUE(reader.isEmpty());
|
||||
|
||||
DesktopFileReader otherReader(path);
|
||||
EXPECT_TRUE(otherReader.isEmpty());
|
||||
|
||||
reader = std::move(otherReader);
|
||||
EXPECT_EQ(reader.path(), path);
|
||||
|
||||
// test self-assignment
|
||||
reader = std::move(reader);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testParseSimpleDesktopFile) {
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "Version=1.0" << std::endl
|
||||
<< "Name=name" << std::endl
|
||||
<< "Exec=exec" << std::endl
|
||||
<< "Icon=icon" << std::endl
|
||||
<< "Type=Application" << std::endl
|
||||
<< "Categories=Utility;Multimedia;" << std::endl;
|
||||
|
||||
DesktopFileReader reader;
|
||||
reader = DesktopFileReader(ss);
|
||||
|
||||
auto section = reader["Desktop File"];
|
||||
EXPECT_FALSE(section.empty());
|
||||
EXPECT_EQ(section.size(), 6);
|
||||
|
||||
EXPECT_NEAR(section["Version"].asDouble(), 1.0, 0.000001);
|
||||
EXPECT_EQ(section["Name"].value(), "name");
|
||||
EXPECT_EQ(section["Exec"].value(), "exec");
|
||||
EXPECT_EQ(section["Icon"].value(), "icon");
|
||||
EXPECT_EQ(section["Type"].value(), "Application");
|
||||
EXPECT_EQ(section["Name"].value(), "name");
|
||||
EXPECT_EQ(section["Categories"].parseStringList(), std::vector<std::string>({"Utility", "Multimedia"}));
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testParseSimpleDesktopFileWithComments) {
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "Version=1.0" << std::endl
|
||||
<< "Name=name" << std::endl
|
||||
<< "# a comment" << std::endl
|
||||
<< "Exec=exec" << std::endl
|
||||
<< "Icon=icon" << std::endl
|
||||
<< "Type=Application" << std::endl
|
||||
<< "# another comment" << std::endl
|
||||
<< "Categories=Utility;Multimedia;" << std::endl;
|
||||
|
||||
DesktopFileReader reader;
|
||||
reader = DesktopFileReader(ss);
|
||||
|
||||
auto section = reader["Desktop File"];
|
||||
EXPECT_FALSE(section.empty());
|
||||
EXPECT_EQ(section.size(), 6);
|
||||
|
||||
// TODO: check for comments in data
|
||||
// right now, they're just discarded, but they shall be preserved in the right positions in the future
|
||||
|
||||
EXPECT_NEAR(section["Version"].asDouble(), 1.0f, 0.000001);
|
||||
EXPECT_EQ(section["Name"].value(), "name");
|
||||
EXPECT_EQ(section["Exec"].value(), "exec");
|
||||
EXPECT_EQ(section["Icon"].value(), "icon");
|
||||
EXPECT_EQ(section["Type"].value(), "Application");
|
||||
EXPECT_EQ(section["Name"].value(), "name");
|
||||
EXPECT_EQ(section["Categories"].parseStringList(), std::vector<std::string>({"Utility", "Multimedia"}));
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testParseFileGetNonExistingSection) {
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl;
|
||||
|
||||
DesktopFileReader reader;
|
||||
reader = DesktopFileReader(ss);
|
||||
|
||||
ASSERT_THROW(reader["Non-existing Section"], UnknownSectionError);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testParseFileMissingSectionHeader) {
|
||||
std::stringstream ss;
|
||||
ss << "Name=name" << std::endl
|
||||
<< "Exec=exec" << std::endl
|
||||
<< "Icon=icon" << std::endl
|
||||
<< "Type=Application" << std::endl
|
||||
<< "Categories=Utility;Multimedia;" << std::endl;
|
||||
|
||||
DesktopFileReader reader;
|
||||
ASSERT_THROW(reader = DesktopFileReader(ss), ParseError);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testParseFileEmptyKey) {
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "=name" << std::endl
|
||||
<< "Exec=exec" << std::endl
|
||||
<< "Icon=icon" << std::endl
|
||||
<< "Type=Application" << std::endl
|
||||
<< "Categories=Utility;Multimedia;" << std::endl;
|
||||
|
||||
DesktopFileReader reader;
|
||||
ASSERT_THROW(reader = DesktopFileReader(ss), ParseError);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testParseFileMissingDelimiterInLine) {
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "Exec" << std::endl;
|
||||
|
||||
DesktopFileReader reader;
|
||||
ASSERT_THROW(reader = DesktopFileReader(ss), ParseError);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "Name name" << std::endl;
|
||||
|
||||
DesktopFileReader reader;
|
||||
ASSERT_THROW(reader = DesktopFileReader(ss), ParseError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testParseFile) {
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "Name name" << std::endl
|
||||
<< "Exec" << std::endl;
|
||||
|
||||
DesktopFileReader reader;
|
||||
ASSERT_THROW(reader = DesktopFileReader(ss), ParseError);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testParseFileMultipleDelimitersInLine) {
|
||||
// TODO: verify that ==Name would be a legal value according to desktop file specification
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "Name===name" << std::endl;
|
||||
|
||||
DesktopFileReader reader;
|
||||
ASSERT_NO_THROW(reader = DesktopFileReader(ss));
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testParseFileWithLeadingAndTrailingWhitespaceInLines) {
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "Name= name" << std::endl
|
||||
<< "Exec =exec" << std::endl;
|
||||
|
||||
DesktopFileReader reader(ss);
|
||||
|
||||
auto section = reader["Desktop File"];
|
||||
EXPECT_FALSE(section.empty());
|
||||
|
||||
EXPECT_EQ(section["Name"].value(), "name");
|
||||
EXPECT_EQ(section["Exec"].value(), "exec");
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testDataGetter) {
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "Name= name" << std::endl
|
||||
<< "Exec =exec" << std::endl;
|
||||
|
||||
DesktopFileReader reader(ss);
|
||||
|
||||
auto section = reader["Desktop File"];
|
||||
EXPECT_FALSE(section.empty());
|
||||
|
||||
auto data = reader.data();
|
||||
|
||||
auto expected = DesktopFile::section_t({
|
||||
{"Name", DesktopFileEntry("Name", "name")},
|
||||
{"Exec", DesktopFileEntry("Exec", "exec")},
|
||||
});
|
||||
|
||||
EXPECT_EQ(data["Desktop File"], expected);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testParseLinesWithMultipleSpaces) {
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "Name= What a great name " << std::endl;
|
||||
|
||||
DesktopFileReader reader(ss);
|
||||
|
||||
auto section = reader["Desktop File"];
|
||||
EXPECT_FALSE(section.empty());
|
||||
|
||||
EXPECT_EQ(section["Name"].value(), "What a great name");
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testReadBrokenSectionHeaderMissingClosingBracket) {
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry" << std::endl
|
||||
<< "test=test" << std::endl;
|
||||
|
||||
ASSERT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
|
||||
// also test for brokenness in a later section, as the first section is normally treated specially
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]" << std::endl
|
||||
<< "test=test" << std::endl
|
||||
<< "[Another Section" << std::endl;
|
||||
|
||||
ASSERT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testReadBrokenSectionHeaderTooManyClosingBrackets) {
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]]" << std::endl
|
||||
<< "test=test" << std::endl;
|
||||
|
||||
ASSERT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
|
||||
// also test for brokenness in a later section, as the first section is normally treated specially
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]" << std::endl
|
||||
<< "test=test" << std::endl
|
||||
<< "[Another Section]]" << std::endl;
|
||||
|
||||
ASSERT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testReadBrokenSectionHeaderTooManyOpeningBrackets) {
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[[Desktop Entry]" << std::endl
|
||||
<< "test=test" << std::endl;
|
||||
|
||||
ASSERT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
|
||||
// also test for brokenness in a later section, as the first section is normally treated specially
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]" << std::endl
|
||||
<< "test=test" << std::endl
|
||||
<< "[[Another Section]";
|
||||
|
||||
ASSERT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testReadBrokenSectionMissingOpeningBracket) {
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "Desktop Entry]" << std::endl
|
||||
<< "test=test" << std::endl;
|
||||
ASSERT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
|
||||
// also test for brokenness in a later section, as the first section is normally treated specially
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]" << std::endl
|
||||
<< "test=test" << std::endl
|
||||
<< "Another Section]" << std::endl;
|
||||
|
||||
ASSERT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
}
|
||||
|
||||
// FIXME: introduce proper localization support
|
||||
TEST_F(DesktopFileReaderTest, testReadLocalizedEntriesWithoutProperLocalizationSupport) {
|
||||
std::stringstream ss;
|
||||
ss << "[Desktop File]" << std::endl
|
||||
<< "Name=name" << std::endl
|
||||
<< "Name[de]=name" << std::endl
|
||||
<< "Exec=exec" << std::endl;
|
||||
|
||||
DesktopFileReader reader(ss);
|
||||
|
||||
auto section = reader["Desktop File"];
|
||||
EXPECT_FALSE(section.empty());
|
||||
|
||||
auto data = reader.data();
|
||||
|
||||
auto expected = DesktopFile::section_t({
|
||||
{"Name", DesktopFileEntry("Name", "name")},
|
||||
// FIXME: revise after introduction of localization support
|
||||
{"Name[de]", DesktopFileEntry("Name[de]", "name")},
|
||||
{"Exec", DesktopFileEntry("Exec", "exec")},
|
||||
});
|
||||
|
||||
EXPECT_EQ(data["Desktop File"], expected);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileReaderTest, testBrokenLocalizedKeys) {
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]" << std::endl
|
||||
<< "test]de[=test" << std::endl;
|
||||
|
||||
EXPECT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]" << std::endl
|
||||
<< "test[de]]=test" << std::endl;
|
||||
|
||||
EXPECT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]" << std::endl
|
||||
<< "test[[de]=test" << std::endl;
|
||||
|
||||
EXPECT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]" << std::endl
|
||||
<< "test[]de=test" << std::endl;
|
||||
|
||||
EXPECT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]" << std::endl
|
||||
<< "test[de=test" << std::endl;
|
||||
|
||||
EXPECT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
|
||||
{
|
||||
std::stringstream ins;
|
||||
ins << "[Desktop Entry]" << std::endl
|
||||
<< "testde]=test" << std::endl;
|
||||
|
||||
EXPECT_THROW(DesktopFileReader reader(ins), ParseError);
|
||||
}
|
||||
}
|
||||
@@ -1,108 +0,0 @@
|
||||
// library headers
|
||||
#include <gtest/gtest.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
// local headers
|
||||
#include "../../src/core/desktopfile/desktopfilewriter.h"
|
||||
#include "../../src/core/desktopfile/desktopfilereader.h"
|
||||
#include "linuxdeploy/core/desktopfile/exceptions.h"
|
||||
|
||||
using namespace linuxdeploy::core::desktopfile;
|
||||
namespace bf = boost::filesystem;
|
||||
|
||||
class DesktopFileWriterTest : public ::testing::Test {
|
||||
void SetUp() override {}
|
||||
void TearDown() override {}
|
||||
};
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testDefaultConstructor) {
|
||||
DesktopFileWriter writer;
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testDataConstructor) {
|
||||
DesktopFile::sections_t data;
|
||||
DesktopFileWriter writer(data);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testCopyConstructor) {
|
||||
DesktopFile::sections_t data;
|
||||
DesktopFileWriter writer(data);
|
||||
|
||||
DesktopFileWriter copy(writer);
|
||||
EXPECT_EQ(copy, writer);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testCopyAssignmentConstructor) {
|
||||
DesktopFile::sections_t data;
|
||||
DesktopFileWriter writer(data);
|
||||
|
||||
DesktopFileWriter otherWriter;
|
||||
otherWriter = writer;
|
||||
EXPECT_EQ(writer, otherWriter);
|
||||
}
|
||||
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testEqualityAndInequalityOperators) {
|
||||
DesktopFile::sections_t data;
|
||||
DesktopFileWriter writer(data);
|
||||
|
||||
DesktopFileWriter otherWriter(data);
|
||||
|
||||
EXPECT_TRUE(writer == otherWriter);
|
||||
EXPECT_FALSE(writer != otherWriter);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testMoveAssignmentConstructor) {
|
||||
DesktopFile::sections_t data;
|
||||
DesktopFileWriter writer(data);
|
||||
|
||||
DesktopFileWriter otherWriter;
|
||||
otherWriter = std::move(writer);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testSaveToPath) {
|
||||
DesktopFile::sections_t data;
|
||||
DesktopFileWriter writer(data);
|
||||
|
||||
writer.save("/dev/null");
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testSaveToInvalidPath) {
|
||||
DesktopFileWriter writer;
|
||||
ASSERT_THROW(writer.save("/a/b/c/d/e/f/g/h/1/2/3/4/5/6/7/8"), IOError);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testSaveToStream) {
|
||||
DesktopFile::sections_t data;
|
||||
DesktopFileWriter writer(data);
|
||||
|
||||
std::stringstream ss;
|
||||
writer.save(ss);
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testDataGetter) {
|
||||
DesktopFile::sections_t data;
|
||||
DesktopFileWriter writer(data);
|
||||
|
||||
EXPECT_EQ(data, writer.data());
|
||||
}
|
||||
|
||||
TEST_F(DesktopFileWriterTest, testSerialization) {
|
||||
DesktopFile::section_t section = {
|
||||
{"Exec", DesktopFileEntry("Exec", "exec")},
|
||||
{"Name", DesktopFileEntry("Name", "name")},
|
||||
};
|
||||
|
||||
DesktopFile::sections_t data = {
|
||||
{"Desktop File", section},
|
||||
};
|
||||
|
||||
DesktopFileWriter writer(data);
|
||||
EXPECT_EQ(data, writer.data());
|
||||
|
||||
std::stringstream ss;
|
||||
writer.save(ss);
|
||||
|
||||
DesktopFileReader reader(ss);
|
||||
EXPECT_EQ(reader["Desktop File"], section);
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
#include "linuxdeploy/core/appdir.h"
|
||||
|
||||
using namespace linuxdeploy::core::appdir;
|
||||
using namespace linuxdeploy::core::desktopfile;
|
||||
using namespace linuxdeploy::desktopfile;
|
||||
using namespace boost::filesystem;
|
||||
|
||||
namespace AppDirTest {
|
||||
|
||||
Reference in New Issue
Block a user