diff --git a/.gitignore b/.gitignore index ac0e8a5..3c31623 100644 --- a/.gitignore +++ b/.gitignore @@ -2,5 +2,4 @@ cmake-build-*/ *build*/ .idea/ squashfs-root/ -tests/ *.AppImage diff --git a/.gitmodules b/.gitmodules index 16d0a42..1ce3cc8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -76,3 +76,6 @@ [submodule "lib/CImg"] path = lib/CImg url = https://github.com/dtschump/CImg.git +[submodule "lib/googletest"] + path = lib/googletest + url = https://github.com/google/googletest diff --git a/.travis.yml b/.travis.yml index cf380d1..09bb3c7 100644 --- a/.travis.yml +++ b/.travis.yml @@ -44,7 +44,7 @@ script: after_success: - ls -lh # make sure only pushes to rewrite create a new release, otherwise pretend PR and upload to transfer.sh - - if [ "$TRAVIS_BRANCH" != "master" ]; then export TRAVIS_EVENT_TYPE=pull_request; fi + - if [ "$TRAVIS_TAG" != "$TRAVIS_BRANCH" ] && [ "$TRAVIS_BRANCH" == "master" ]; then export TRAVIS_EVENT_TYPE=pull_request; fi - wget -c https://github.com/probonopd/uploadtool/raw/master/upload.sh - bash upload.sh linuxdeploy-"$ARCH".AppImage* diff --git a/CMakeLists.txt b/CMakeLists.txt index 139842f..f753055 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,3 +13,8 @@ set(USE_SYSTEM_CIMG ON CACHE BOOL "Set to OFF to use CImg library bundled in lib add_subdirectory(lib) add_subdirectory(src) + +include(CTest) +if(BUILD_TESTING) + add_subdirectory(tests) +endif() diff --git a/cmake/toolchains/i386-linux-gnu.cmake b/cmake/toolchains/i386-linux-gnu.cmake index 65c7b8b..ba35ddd 100644 --- a/cmake/toolchains/i386-linux-gnu.cmake +++ b/cmake/toolchains/i386-linux-gnu.cmake @@ -3,3 +3,6 @@ set(CMAKE_SYSTEM_PROCESSOR i386 CACHE STRING "" FORCE) set(CMAKE_C_FLAGS "-m32" CACHE STRING "" FORCE) set(CMAKE_CXX_FLAGS "-m32" CACHE STRING "" FORCE) + +# https://gitlab.kitware.com/cmake/cmake/issues/16920#note_299077 +set(THREADS_PTHREAD_ARG "2" CACHE STRING "Forcibly set by CMakeLists.txt" FORCE) diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index a7befff..ba4ccf3 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -1,3 +1,5 @@ +include(CTest) + add_library(subprocess INTERFACE) target_sources(subprocess INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/cpp-subprocess/subprocess.hpp) target_include_directories(subprocess INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/cpp-subprocess) @@ -11,9 +13,11 @@ add_library(cpp-feather-ini-parser INTERFACE) target_sources(cpp-feather-ini-parser INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}/cpp-feather-ini-parser/INI.h) target_include_directories(cpp-feather-ini-parser INTERFACE cpp-feather-ini-parser) -add_executable(test_cpp_feather_ini_parser EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/cpp-feather-ini-parser/example/example.cpp) -target_link_libraries(test_cpp_feather_ini_parser PRIVATE cpp-feather-ini-parser) -add_test(test_cpp_feather_ini_parser test_cpp_feather_ini_parser) +if(BUILD_TESTING) + add_executable(test_cpp_feather_ini_parser ${CMAKE_CURRENT_SOURCE_DIR}/cpp-feather-ini-parser/example/example.cpp) + target_link_libraries(test_cpp_feather_ini_parser PRIVATE cpp-feather-ini-parser) + add_test(test_cpp_feather_ini_parser test_cpp_feather_ini_parser) +endif() if(NOT USE_SYSTEM_BOOST) add_library(boost_config INTERFACE) @@ -108,3 +112,7 @@ if(NOT USE_SYSTEM_BOOST) boost_type_traits boost_static_assert boost_integer boost_preprocessor boost_functional boost_detail ) endif() + +if(BUILD_TESTING) + add_subdirectory(googletest) +endif() diff --git a/lib/googletest b/lib/googletest new file mode 160000 index 0000000..3bb00b7 --- /dev/null +++ b/lib/googletest @@ -0,0 +1 @@ +Subproject commit 3bb00b7ead35ca3a9b5817ce1ab78050fe6be0e3 diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..9c9d1b6 --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,6 @@ +# first build dependencies for tests +add_subdirectory(simple_library) +add_subdirectory(simple_executable) + +# now include actual tests +add_subdirectory(core) diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt new file mode 100644 index 0000000..58c3fc4 --- /dev/null +++ b/tests/core/CMakeLists.txt @@ -0,0 +1,18 @@ +add_executable(test_appdir test_appdir.cpp) +target_link_libraries(test_appdir PRIVATE linuxdeploy_core gtest) +target_include_directories(test_appdir PRIVATE ${PROJECT_SOURCE_DIR}/include) + +# calculate paths to resources using CMake and hardcode them in the test binary +target_compile_definitions(test_appdir PRIVATE + -DSIMPLE_LIBRARY_PATH="$" + -DSIMPLE_EXECUTABLE_PATH="$" + -DSIMPLE_DESKTOP_ENTRY_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../data/simple_app.desktop" + -DSIMPLE_ICON_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../data/simple_icon.svg" + -DSIMPLE_FILE_PATH="${CMAKE_CURRENT_SOURCE_DIR}/../data/simple_file.txt" +) + +# register in CTest +add_test(test_appdir test_appdir) + +# make sure library and executable are built before test_appdir +add_dependencies(test_appdir simple_library simple_executable) diff --git a/tests/core/test_appdir.cpp b/tests/core/test_appdir.cpp new file mode 100644 index 0000000..39d9b00 --- /dev/null +++ b/tests/core/test_appdir.cpp @@ -0,0 +1,129 @@ +#include "gtest/gtest.h" +#include "linuxdeploy/core/appdir.h" + +using namespace linuxdeploy::core::appdir; +using namespace linuxdeploy::core::desktopfile; +using namespace boost::filesystem; + +namespace AppDirTest { + class AppDirUnitTestsFixture : public ::testing::Test { + public: + path tmpAppDir; + AppDir appDir; + + public: + AppDirUnitTestsFixture() : + tmpAppDir(temp_directory_path() / unique_path("linuxdeploy-tests-%%%%-%%%%-%%%%")), + appDir(tmpAppDir) { + } + + void SetUp() override { + } + + void TearDown() override { + remove_all(tmpAppDir); + } + + ~AppDirUnitTestsFixture() override = default; + + void listDeployedFiles() { + std::cout << "Files deployed in AppDir:"; + recursive_directory_iterator end_itr; // default construction yields past-the-end + for (recursive_directory_iterator itr(tmpAppDir); itr != end_itr; itr++) { + std::cout << relative(itr->path(), tmpAppDir).string() << std::endl; + } + } + }; + + TEST_F(AppDirUnitTestsFixture, createBasicStructure) { + // in this test case we expect the following exact directory set to be created in the AppDir + std::set expected = { + "usr", + "usr/bin", + "usr/share", + "usr/share/icons", + "usr/share/icons/hicolor", + "usr/share/icons/hicolor/scalable", + "usr/share/icons/hicolor/scalable/apps", + "usr/share/icons/hicolor/32x32", + "usr/share/icons/hicolor/32x32/apps", + "usr/share/icons/hicolor/256x256", + "usr/share/icons/hicolor/256x256/apps", + "usr/share/icons/hicolor/16x16", + "usr/share/icons/hicolor/16x16/apps", + "usr/share/icons/hicolor/128x128", + "usr/share/icons/hicolor/128x128/apps", + "usr/share/icons/hicolor/64x64", + "usr/share/icons/hicolor/64x64/apps", + "usr/share/applications", + "usr/lib", + }; + + appDir.createBasicStructure(); + + recursive_directory_iterator end_itr; // default construction yields past-the-end + for (recursive_directory_iterator itr(tmpAppDir); itr != end_itr; itr++) { + std::string path = relative(itr->path(), tmpAppDir).string(); + ASSERT_NE(expected.find(path), expected.end()); + expected.erase(path); + } + ASSERT_TRUE(expected.empty()); + } + + + TEST_F(AppDirUnitTestsFixture, depoloyLibraryWrongPath) { + ASSERT_THROW(appDir.deployLibrary("/lib/fakelib.so"), std::exception); + } + + TEST_F(AppDirUnitTestsFixture, depoloyLibrary) { + appDir.deployLibrary(SIMPLE_LIBRARY_PATH); + appDir.executeDeferredOperations(); + + ASSERT_TRUE(is_regular_file(tmpAppDir / "usr/lib" / path(SIMPLE_LIBRARY_PATH).filename())); + } + + TEST_F(AppDirUnitTestsFixture, deployExecutable) { + appDir.deployExecutable(SIMPLE_EXECUTABLE_PATH); + appDir.executeDeferredOperations(); + + ASSERT_TRUE(is_regular_file(tmpAppDir / "usr/bin" / path(SIMPLE_EXECUTABLE_PATH).filename())); + ASSERT_TRUE(is_regular_file(tmpAppDir / "usr/lib" / path(SIMPLE_LIBRARY_PATH).filename())); + } + + TEST_F(AppDirUnitTestsFixture, deployDesktopFile) { + DesktopFile desktopFile{SIMPLE_DESKTOP_ENTRY_PATH}; + appDir.deployDesktopFile(desktopFile); + appDir.executeDeferredOperations(); + + ASSERT_TRUE(is_regular_file(tmpAppDir / "usr/share/applications" / path(SIMPLE_DESKTOP_ENTRY_PATH).filename())); + } + + + TEST_F(AppDirUnitTestsFixture, deployIcon) { + appDir.deployIcon(SIMPLE_ICON_PATH); + appDir.executeDeferredOperations(); + + ASSERT_TRUE(is_regular_file(tmpAppDir / "usr/share/icons/hicolor/scalable/apps" / path(SIMPLE_ICON_PATH).filename())); + } + + TEST_F(AppDirUnitTestsFixture, deployFileToDirectory) { + auto destination = tmpAppDir / "usr/share/doc/simple_application/"; + appDir.deployFile(SIMPLE_FILE_PATH, destination); + appDir.executeDeferredOperations(); + + ASSERT_TRUE(is_regular_file(destination / path(SIMPLE_FILE_PATH).filename())); + } + + TEST_F(AppDirUnitTestsFixture, deployFileToAbsoluteFilePath) { + auto destination = tmpAppDir / "usr/share/doc/simple_application/test123"; + appDir.deployFile(SIMPLE_FILE_PATH, destination); + appDir.executeDeferredOperations(); + + ASSERT_TRUE(is_regular_file(destination)); + } +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/data/simple_app.desktop b/tests/data/simple_app.desktop new file mode 100644 index 0000000..f49e6be --- /dev/null +++ b/tests/data/simple_app.desktop @@ -0,0 +1,19 @@ +[Desktop Entry] +Version=1.0 +Type=Application +Name=Simple Application +Comment=The most simple application available! +TryExec=simple_executable +Exec=simple_executable %F +Icon=simple_icon +MimeType=image/x-foo; +Actions=SimpleAction;AnotherSimpleAction; + +[Desktop Action SimpleAction] +Exec=simple_executable --do-it +Name=Do something simple! + +[Desktop Action AnotherSimpleAction] +Exec=simple_executable --do-it-again +Name=Do another simple thing! +Icon=simple_icon diff --git a/tests/data/simple_file.txt b/tests/data/simple_file.txt new file mode 100644 index 0000000..980a0d5 --- /dev/null +++ b/tests/data/simple_file.txt @@ -0,0 +1 @@ +Hello World! diff --git a/tests/data/simple_icon.svg b/tests/data/simple_icon.svg new file mode 100644 index 0000000..39e3940 --- /dev/null +++ b/tests/data/simple_icon.svg @@ -0,0 +1,38 @@ + + + + + + + image/svg+xml + + + + + + + + + diff --git a/tests/simple_executable/CMakeLists.txt b/tests/simple_executable/CMakeLists.txt new file mode 100644 index 0000000..a552550 --- /dev/null +++ b/tests/simple_executable/CMakeLists.txt @@ -0,0 +1,2 @@ +add_executable(simple_executable simple_executable.cpp) +target_link_libraries(simple_executable simple_library pthread) \ No newline at end of file diff --git a/tests/simple_executable/simple_executable.cpp b/tests/simple_executable/simple_executable.cpp new file mode 100644 index 0000000..73906dd --- /dev/null +++ b/tests/simple_executable/simple_executable.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main() { + printf("Hello World"); + hello_world(); + + return 0; +} diff --git a/tests/simple_library/CMakeLists.txt b/tests/simple_library/CMakeLists.txt new file mode 100644 index 0000000..41fd25d --- /dev/null +++ b/tests/simple_library/CMakeLists.txt @@ -0,0 +1,3 @@ +add_library(simple_library SHARED simple_library.cpp simple_library.h) +target_link_libraries(simple_library PUBLIC CImg) +target_include_directories(simple_library PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/tests/simple_library/simple_library.cpp b/tests/simple_library/simple_library.cpp new file mode 100644 index 0000000..4346a90 --- /dev/null +++ b/tests/simple_library/simple_library.cpp @@ -0,0 +1,6 @@ +#include "CImg.h" +using namespace cimg_library; + +void hello_world() { + cimg::info(); +} diff --git a/tests/simple_library/simple_library.h b/tests/simple_library/simple_library.h new file mode 100644 index 0000000..100fd32 --- /dev/null +++ b/tests/simple_library/simple_library.h @@ -0,0 +1 @@ +void hello_world(); diff --git a/travis/build.sh b/travis/build.sh index 0dd8ccf..23f8f2d 100755 --- a/travis/build.sh +++ b/travis/build.sh @@ -39,6 +39,9 @@ cmake "$REPO_ROOT" -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_BUILD_TYPE=RelWithDebInfo make -j$(nproc) +## Run Unit Tests +ctest -V + # args are used more than once LINUXDEPLOY_ARGS=("--appdir" "AppDir" "-e" "bin/linuxdeploy" "-i" "$REPO_ROOT/resources/linuxdeploy.png" "--create-desktop-file" "-e" "/usr/bin/patchelf" "-e" "/usr/bin/strip")