From 96d622417b69b519e2a513047cad7ee3848bc3b2 Mon Sep 17 00:00:00 2001 From: Damian Pietruchowski Date: Mon, 21 Aug 2017 11:27:35 +0200 Subject: [PATCH] Add new lib with common smoke utils for tests Wgt-backend and tpk-backend have common util functions for smoke tests. They should be exported to lib in app-installers. Submit together: - https://review.tizen.org/gerrit/#/c/149137/ - https://review.tizen.org/gerrit/#/c/149601/ Change-Id: Ifef1e9f38790133c7337650c8641e0cd0bab57f1 Signed-off-by: Damian Pietruchowski --- CMakeLists.txt | 2 + packaging/app-installers.spec | 4 +- src/unit_tests/CMakeLists.txt | 1 + src/unit_tests/common/CMakeLists.txt | 16 + src/unit_tests/common/smoke_utils.cc | 929 +++++++++++++++++++++++++++++++++++ src/unit_tests/common/smoke_utils.h | 269 ++++++++++ 6 files changed, 1220 insertions(+), 1 deletion(-) create mode 100644 src/unit_tests/common/CMakeLists.txt create mode 100644 src/unit_tests/common/smoke_utils.cc create mode 100644 src/unit_tests/common/smoke_utils.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 911f052..c406144 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -32,6 +32,7 @@ SET(TARGET_PKGDIR_TOOL "pkgdir-tool") SET(TARGET_PKG_INITDB "pkg_initdb") SET(TARGET_PKG_RECOVERY "pkg_recovery") SET(TARGET_PKG_RECOVERY_HELPER "pkg_recovery_helper") +SET(TARGET_SMOKE_UTILS "smoke-utils") ADD_DEFINITIONS("-DPROJECT_TAG=\"APP_INSTALLERS\"") @@ -65,6 +66,7 @@ PKG_CHECK_MODULES(STORAGE_DEPS REQUIRED storage) PKG_CHECK_MODULES(LIBSYSTEMD_DEPS REQUIRED libsystemd) PKG_CHECK_MODULES(TTRACE_DEPS REQUIRED ttrace) PKG_CHECK_MODULES(TRUST_ANCHOR_DEPS REQUIRED tanchor) +PKG_CHECK_MODULES(GUM_DEPS REQUIRED libgum) FIND_PACKAGE(Boost REQUIRED COMPONENTS system filesystem program_options) FIND_PACKAGE(GTest REQUIRED) diff --git a/packaging/app-installers.spec b/packaging/app-installers.spec index 5116652..d7e379c 100644 --- a/packaging/app-installers.spec +++ b/packaging/app-installers.spec @@ -63,7 +63,7 @@ Summary: Unit tests for app-installers Requires: %{name} = %{version} %description tests -Unit tests for al modules of app-installers +Unit tests for al modules of app-installers and common utils for smoke test %prep %setup -q @@ -128,6 +128,8 @@ chsmack -a System %{unpackdir} %{_bindir}/app-installers-ut/* %{_datadir}/app-installers-ut/* %{_libdir}/libtest-assessor-lib.so* +%{_libdir}/libsmoke-utils.so* +%{_includedir}/app-installers/unit_tests/common/smoke_utils.h %changelog * Tue Sep 01 2015 Pawel Sikorski 1.8-1 diff --git a/src/unit_tests/CMakeLists.txt b/src/unit_tests/CMakeLists.txt index 7746348..f2ca8d8 100644 --- a/src/unit_tests/CMakeLists.txt +++ b/src/unit_tests/CMakeLists.txt @@ -51,3 +51,4 @@ INSTALL(TARGETS ${TARGET_SIGNATURE_TEST} DESTINATION ${BINDIR}/${DESTINATION_DIR INSTALL(TARGETS ${TARGET_PLUGINS_TEST} DESTINATION ${BINDIR}/${DESTINATION_DIR}) ADD_SUBDIRECTORY(libs) +ADD_SUBDIRECTORY(common) diff --git a/src/unit_tests/common/CMakeLists.txt b/src/unit_tests/common/CMakeLists.txt new file mode 100644 index 0000000..e200daa --- /dev/null +++ b/src/unit_tests/common/CMakeLists.txt @@ -0,0 +1,16 @@ +ADD_LIBRARY(${TARGET_SMOKE_UTILS} SHARED + smoke_utils.cc +) + +APPLY_PKG_CONFIG(${TARGET_SMOKE_UTILS} PUBLIC + Boost + GTEST + GUM_DEPS +) + +TARGET_LINK_LIBRARIES(${TARGET_SMOKE_UTILS} PRIVATE + ${TARGET_LIBNAME_COMMON} +) + +INSTALL(TARGETS ${TARGET_SMOKE_UTILS} DESTINATION ${LIB_INSTALL_DIR}) +INSTALL(FILES smoke_utils.h DESTINATION ${INCLUDEDIR}/app-installers/unit_tests/common/) diff --git a/src/unit_tests/common/smoke_utils.cc b/src/unit_tests/common/smoke_utils.cc new file mode 100644 index 0000000..30b49c0 --- /dev/null +++ b/src/unit_tests/common/smoke_utils.cc @@ -0,0 +1,929 @@ +// Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by an apache-2.0 license that can be +// found in the LICENSE file. + +#include "unit_tests/common/smoke_utils.h" + +#include +#include +#include +#include + +#include +#include + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace bf = boost::filesystem; +namespace bs = boost::system; +namespace ci = common_installer; +namespace bo = boost::program_options; + +namespace { + +const uid_t kDefaultUserUid = tzplatform_getuid(TZ_SYS_DEFAULT_USER); +const char kNormalUserName[] = "smokeuser"; +const char kSystemShareGroupName[] = "system_share"; +const char kMigrateTestDBName[] = "app2sd_migrate.db"; +// common entries +const std::vector kDBEntries = { + {".pkgmgr_parser.db"}, + {".pkgmgr_parser.db-journal"}, + {".pkgmgr_cert.db"}, + {".pkgmgr_cert.db-journal"}, + {".app2sd.db"}, + {".app2sd.db-journal"}, +}; +// globaluser entries +const char kGlobalManifestDir[] = "/opt/share/packages"; +const char kSkelDir[] = "/etc/skel/apps_rw"; +const char kPreloadApps[] = "/usr/apps"; +const char kPreloadManifestDir[] = "/usr/share/packages"; +const char kPreloadIcons[] = "/usr/share/icons"; + +enum RWDirectory { + DATA, + CACHE, + SHARED_CACHE, + SHARED_DATA, + SHARED_TRUSTED +}; + +const char* rwDirectories[] = { + "data", + "cache", + "shared/cache", + "shared/data", + "shared/trusted", +}; + +} // namespace + +namespace smoke_test { + +const char kLegacyExtImageDir[] = "legacy_extimage_dir"; +const std::string& kDefaultUserIdStr = std::to_string(kDefaultUserUid); +const uid_t kGlobalUserUid = tzplatform_getuid(TZ_SYS_GLOBALAPP_USER); +const uid_t kGlobalUserGid = tzplatform_getgid(TZ_SYS_GLOBALAPP_USER); + +ci::RequestMode ParseRequestMode(int argc, char** argv) { + bo::options_description desc("Available options"); + desc.add_options() + ("request-mode", bo::value(), "set request mode") + ("global-request,g", "set request mode to global") + ("user-request,u", "set request mode to user"); + + bo::variables_map vm; + bo::store(bo::command_line_parser(argc, argv). + options(desc).allow_unregistered().run(), vm); + bo::notify(vm); + + if (vm.count("global-request")) { + std::cout << "Request mode was set to global." << std::endl; + return ci::RequestMode::GLOBAL; + } + if (vm.count("user-request")) { + std::cout << "Request mode was set to user." << std::endl; + return ci::RequestMode::USER; + } + if (vm.count("request-mode")) { + if (vm["request-mode"].as() == "global") { + std::cout << "Request mode was set to global." << std::endl; + return ci::RequestMode::GLOBAL; + } + if (vm["request-mode"].as() == "user") { + std::cout << "Request mode was set to user." << std::endl; + return ci::RequestMode::USER; + } + std::cout << "Cannot set request mode to " + << vm["request-mode"].as() << std::endl; + } + std::cout << "Request mode was set to global." << std::endl; + return ci::RequestMode::GLOBAL; +} + +static bool AddUser(const char* user_name) { + GumUser* user = nullptr; + user = gum_user_create_sync(FALSE); + if (user == nullptr) + LOG(WARNING) << "Failed to create gum user! (user name: " + << user_name << ")"; + g_object_set(G_OBJECT(user), "username", user_name, "usertype", + GUM_USERTYPE_NORMAL, NULL); + gboolean rval = FALSE; + rval = gum_user_add_sync(user); + g_object_unref(user); + return rval; +} + +static bool DeleteUser(const char* user_name, bool rem_home_dir) { + bool rval = FALSE; + GumUser* guser = gum_user_get_by_name_sync(user_name, FALSE); + if (guser) + rval = gum_user_delete_sync(guser, rem_home_dir); + return rval; +} + +bool AddTestUser(User* test_user) { + std::cout << "Adding test user: " << kNormalUserName << std::endl; + AddUser(kNormalUserName); + if (boost::optional uid = ci::GetUidByUserName(kNormalUserName)) { + test_user->uid = *uid; + std::cout << "User created properly: uid=" << *uid; + if (boost::optional gid = ci::GetGidByUid(*uid)) { + test_user->gid = *gid; + std::cout << " gid=" << *gid; + } + std::cout << std::endl; + return true; + } + LOG(ERROR) << "Adding test user failed"; + return false; +} + +bool DeleteTestUser() { + std::cout << "Deleting test user: " << kNormalUserName << std::endl; + uid_t test_uid; + if (boost::optional uid = ci::GetUidByUserName(kNormalUserName)) + test_uid = *uid; + else + return false; + DeleteUser(kNormalUserName, true); + if (!ci::GetUidByUserName(kNormalUserName)) { + std::cout << "User deleted properly: user_name=" << kNormalUserName + << " uid=" << test_uid << std::endl; + return true; + } + LOG(ERROR) << "Deleting test user failed"; + return false; +} + +bool TouchFile(const bf::path& path) { + FILE* f = fopen(path.c_str(), "w+"); + if (!f) + return false; + fclose(f); + return true; +} + +void AddDataFiles(const std::string& pkgid, uid_t uid) { + if (uid == kGlobalUserUid) { + ci::UserList list = ci::GetUserList(); + for (auto l : list) { + auto pkg_path = GetPackageRoot(pkgid, std::get<0>(l)); + ASSERT_TRUE(TouchFile(pkg_path / "data" / "file1.txt")); + ASSERT_TRUE(TouchFile(pkg_path / "data" / "file2.txt")); + } + } else { + auto pkg_path = GetPackageRoot(pkgid, uid); + ASSERT_TRUE(TouchFile(pkg_path / "data" / "file1.txt")); + ASSERT_TRUE(TouchFile(pkg_path / "data" / "file2.txt")); + } +} + +void RemoveAllRecoveryFiles(const std::string& prefix, uid_t uid) { + bf::path root_path = ci::GetRootAppPath(false, uid); + if (!bf::exists(root_path)) + return; + for (auto& dir_entry : boost::make_iterator_range( + bf::directory_iterator(root_path), bf::directory_iterator())) { + if (bf::is_regular_file(dir_entry)) { + if (dir_entry.path().string().find(prefix) != std::string::npos) { + bs::error_code error; + bf::remove(dir_entry.path(), error); + if (error) + LOG(ERROR) << "Failed to remove " << dir_entry.path() + << ": " << error.message(); + } + } + } +} + +bf::path FindRecoveryFile(const std::string& prefix, uid_t uid) { + bf::path root_path = ci::GetRootAppPath(false, uid); + for (auto& dir_entry : boost::make_iterator_range( + bf::directory_iterator(root_path), bf::directory_iterator())) { + if (bf::is_regular_file(dir_entry)) { + if (dir_entry.path().string().find(prefix) != std::string::npos) { + return dir_entry.path(); + } + } + } + return {}; +} + +bf::path GetPackageRoot(const std::string& pkgid, uid_t uid) { + bf::path root_path = ci::GetRootAppPath(false, uid); + return root_path / pkgid; +} + +bool ValidateFileContentInPackage(const std::string& pkgid, + const std::string& relative, + const std::string& expected, + const TestParameters& params) { + bf::path file_path = ci::GetRootAppPath(params.is_readonly, + params.test_user.uid); + file_path = file_path / pkgid / relative; + if (!bf::exists(file_path)) { + LOG(ERROR) << file_path << " doesn't exist"; + return false; + } + FILE* handle = fopen(file_path.c_str(), "r"); + if (!handle) { + LOG(ERROR) << file_path << " cannot be open"; + return false; + } + std::string content; + std::array buffer; + while (fgets(buffer.data(), buffer.size(), handle)) { + content += buffer.data(); + } + fclose(handle); + return content == expected; +} + +static bool ValidatePackageRWFS(const std::string& pkgid, uid_t uid) { + bf::path root_path = ci::GetRootAppPath(false, uid); + bf::path package_path = root_path / pkgid; + bf::path data_path = package_path / rwDirectories[DATA]; + bf::path cache_path = package_path / rwDirectories[CACHE]; + bf::path shared_data_path = package_path / rwDirectories[SHARED_DATA]; + + EXTENDED_ASSERT_TRUE(bf::exists(data_path)); + EXTENDED_ASSERT_TRUE(bf::exists(cache_path)); + + struct stat stats; + stat(data_path.c_str(), &stats); + // gid of RW dirs should be system_share + boost::optional system_share = + ci::GetGidByGroupName(kSystemShareGroupName); + EXTENDED_ASSERT_EQ(uid, stats.st_uid); + EXTENDED_ASSERT_EQ(*system_share, stats.st_gid); + if (bf::exists(shared_data_path)) { + stat(shared_data_path.c_str(), &stats); + EXTENDED_ASSERT_EQ(uid, stats.st_uid); + EXTENDED_ASSERT_EQ(*system_share, stats.st_gid); + } + + stat(cache_path.c_str(), &stats); + EXTENDED_ASSERT_EQ(uid, stats.st_uid); + EXTENDED_ASSERT_EQ(*system_share, stats.st_gid); + return true; +} + +static bool ValidatePackageFS(const std::string& pkgid, + const std::vector& appids, + const TestParameters& params) { + bf::path root_path = ci::GetRootAppPath(params.is_readonly, + params.test_user.uid); + bf::path package_path = root_path / pkgid; + bf::path shared_path = package_path / "shared"; + EXTENDED_ASSERT_TRUE(bf::exists(root_path)); + EXTENDED_ASSERT_TRUE(bf::exists(package_path)); + EXTENDED_ASSERT_TRUE(bf::exists(shared_path)); + + bf::path manifest_path = + bf::path(getUserManifestPath(params.test_user.uid, + params.is_readonly)) / (pkgid + ".xml"); + EXTENDED_ASSERT_TRUE(bf::exists(manifest_path)); + + for (auto& appid : appids) { + bf::path binary_path = package_path / "bin" / appid; + EXTENDED_ASSERT_TRUE(bf::exists(binary_path)); + } + + if (params.pkg_type == PackageType::WGT || + params.pkg_type == PackageType::HYBRID) { + bf::path widget_root_path = package_path / "res" / "wgt"; + bf::path config_path = widget_root_path / "config.xml"; + EXTENDED_ASSERT_TRUE(bf::exists(widget_root_path)); + EXTENDED_ASSERT_TRUE(bf::exists(config_path)); + + bf::path private_tmp_path = package_path / "tmp"; + EXTENDED_ASSERT_TRUE(bf::exists(private_tmp_path)); + } + + // backups should not exist + bf::path package_backup = ci::GetBackupPathForPackagePath(package_path); + bf::path manifest_backup = ci::GetBackupPathForManifestFile(manifest_path); + EXTENDED_ASSERT_FALSE(bf::exists(package_backup)); + EXTENDED_ASSERT_FALSE(bf::exists(manifest_backup)); + + for (bf::recursive_directory_iterator iter(package_path); + iter != bf::recursive_directory_iterator(); ++iter) { + if (bf::is_symlink(symlink_status(iter->path()))) + continue; + bool is_rw_dir = false; + for (const auto rw_dir : rwDirectories) { + bf::path rw_dir_path = rw_dir; + is_rw_dir |= ci::MakeRelativePath(iter->path(), package_path) + == rw_dir_path; + } + if (is_rw_dir || iter->path().filename() == ".mmc") { + iter.no_push(); + continue; + } + struct stat stats; + stat(iter->path().c_str(), &stats); + EXTENDED_ASSERT_EQ(params.test_user.uid, stats.st_uid); + EXTENDED_ASSERT_EQ(params.test_user.gid, stats.st_gid); + } + return true; +} + +bool ValidatePackage(const std::string& pkgid, + const std::vector& appids, const TestParameters& params) { + ci::PkgQueryInterface pkg_query(pkgid, params.test_user.uid); + EXTENDED_ASSERT_TRUE(pkg_query.IsPackageInstalled( + ci::GetRequestMode(params.test_user.uid))); + EXTENDED_ASSERT_TRUE(ValidatePackageFS(pkgid, appids, params)); + if (params.test_user.uid == kGlobalUserUid) { + ci::UserList list = ci::GetUserList(); + for (auto& l : list) + EXTENDED_ASSERT_TRUE(ValidatePackageRWFS(pkgid, std::get<0>(l))); + } else { + EXTENDED_ASSERT_TRUE(ValidatePackageRWFS(pkgid, params.test_user.uid)); + } + return true; +} + +bool ValidateDataFiles(const std::string& pkgid, uid_t uid) { + if (uid == kGlobalUserUid) { + ci::UserList list = ci::GetUserList(); + for (auto l : list) { + auto pkg_path = GetPackageRoot(pkgid, std::get<0>(l)); + EXTENDED_ASSERT_TRUE(bf::exists(pkg_path / "data" / "file1.txt")); + EXTENDED_ASSERT_TRUE(bf::exists(pkg_path / "data" / "file2.txt")); + } + } else { + auto pkg_path = GetPackageRoot(pkgid, uid); + EXTENDED_ASSERT_TRUE(bf::exists(pkg_path / "data" / "file1.txt")); + EXTENDED_ASSERT_TRUE(bf::exists(pkg_path / "data" / "file2.txt")); + } + return true; +} + +static bool ValidateExternalPackageFS(const std::string& pkgid, + const std::vector& appids, + const TestParameters& params) { + EXTENDED_ASSERT_EQ(app2ext_usr_enable_external_pkg(pkgid.c_str(), + params.test_user.uid), 0); + bf::path root_path = ci::GetRootAppPath(false, params.test_user.uid); + if (params.pkg_type == PackageType::TPK) { + EXTENDED_ASSERT_TRUE(bf::exists(root_path / pkgid / ".mmc" / "bin")); + EXTENDED_ASSERT_TRUE(bf::exists(root_path / pkgid / ".mmc" / "lib")); + } + EXTENDED_ASSERT_TRUE(bf::exists(root_path / pkgid / ".mmc" / "res")); + EXTENDED_ASSERT_TRUE(ValidatePackageFS(pkgid, appids, params)); + EXTENDED_ASSERT_EQ(app2ext_usr_disable_external_pkg(pkgid.c_str(), + params.test_user.uid), 0); + return true; +} + +bool ValidateExternalPackage(const std::string& pkgid, + const std::vector& appids, const TestParameters& params) { + ci::PkgQueryInterface pkg_query(pkgid, params.test_user.uid); + std::string storage = pkg_query.StorageForPkgId(); + bf::path ext_mount_path = ci::GetExternalCardPath(); + if (bf::is_empty(ext_mount_path)) { + LOG(INFO) << "Sdcard not exists!"; + EXTENDED_ASSERT_EQ(storage, "installed_internal"); + } else { + EXTENDED_ASSERT_EQ(storage, "installed_external"); + } + EXTENDED_ASSERT_TRUE(ValidateExternalPackageFS(pkgid, appids, params)); + return true; +} + +bool ValidateExtendedPackage(const std::string& pkgid, + const std::vector& appids, const TestParameters& params) { + ci::PkgQueryInterface pkg_query(pkgid, params.test_user.uid); + std::string storage = pkg_query.StorageForPkgId(); + bf::path extended_path = + bf::path(ci::GetExtendedRootAppPath(params.test_user.uid)) / pkgid; + if (!bf::exists(extended_path)) { + LOG(INFO) << "Extended storage not exists!"; + EXTENDED_ASSERT_EQ(storage, "installed_internal"); + } else { + EXTENDED_ASSERT_EQ(storage, "installed_extended"); + } + EXTENDED_ASSERT_TRUE(ValidatePackage(pkgid, appids, params)); + return true; +} + +static bool PackageCheckCleanup(const std::string& pkgid, + const TestParameters& params) { + bf::path root_path = ci::GetRootAppPath(params.is_readonly, + params.test_user.uid); + bf::path package_path = root_path / pkgid; + EXTENDED_ASSERT_FALSE(bf::exists(package_path)); + + bf::path manifest_path = bf::path(getUserManifestPath(params.test_user.uid, + params.is_readonly)) / (pkgid + ".xml"); + EXTENDED_ASSERT_FALSE(bf::exists(manifest_path)); + + // backups should not exist + bf::path package_backup = ci::GetBackupPathForPackagePath(package_path); + bf::path manifest_backup = ci::GetBackupPathForManifestFile(manifest_path); + EXTENDED_ASSERT_FALSE(bf::exists(package_backup)); + EXTENDED_ASSERT_FALSE(bf::exists(manifest_backup)); + return true; +} + +bool CheckPackageNonExistance(const std::string& pkgid, + const TestParameters& params) { + ci::PkgQueryInterface pkg_query(pkgid, params.test_user.uid); + EXTENDED_ASSERT_FALSE(pkg_query.IsPackageInstalled( + ci::GetRequestMode(params.test_user.uid))); + EXTENDED_ASSERT_TRUE(PackageCheckCleanup(pkgid, params)); + if (params.test_user.uid == kGlobalUserUid) { + bf::path skel_path(kSkelDir); + EXTENDED_ASSERT_FALSE(bf::exists(skel_path / pkgid)); + ci::UserList list = ci::GetUserList(); + for (auto& l : list) { + bf::path root_path = ci::GetRootAppPath(false, std::get<0>(l)); + bf::path package_path = root_path / pkgid; + EXTENDED_ASSERT_FALSE(bf::exists(package_path)); + } + } + return true; +} + +bool CheckAvailableExternalPath() { + bf::path ext_mount_path = ci::GetExternalCardPath(); + LOG(DEBUG) << "ext_mount_path :" << ext_mount_path; + if (ext_mount_path.empty()) { + LOG(ERROR) << "Sdcard not exists!"; + return false; + } + return true; +} + +bool CheckAvailableExtendedPath() { + bf::path extended_path = bf::path(tzplatform_getenv(TZ_SYS_EXTENDEDSD)); + LOG(DEBUG) << "extended_path :" << extended_path; + // TODO(jeremy.jang): It should be checked by libstorage API. + if (!bf::exists(extended_path)) { + LOG(ERROR) << "Extended storage not exists!"; + return false; + } + return true; +} + +bool CheckPackageReadonlyNonExistance(const std::string& pkgid, + const TestParameters& params) { + ci::PkgQueryInterface pkg_query(pkgid, params.test_user.uid); + EXTENDED_ASSERT_FALSE(pkg_query.IsPackageInstalled( + ci::GetRequestMode(params.test_user.uid))); + EXTENDED_ASSERT_TRUE(PackageCheckCleanup(pkgid, params)); + return true; +} + +void BackendInterface::TestRollbackAfterEachStep(int argc, const char* argv[], + std::function validator) const { + TestPkgmgrInstaller pkgmgr_installer; + std::unique_ptr query_interface = + CreateQueryInterface(); + auto pkgmgr = + ci::PkgMgrInterface::Create(argc, const_cast(argv), + &pkgmgr_installer, + query_interface.get()); + if (!pkgmgr) { + LOG(ERROR) << "Failed to initialize pkgmgr interface"; + return; + } + auto backend = CreateInstaller(pkgmgr); + int i; + for (i = backend->StepCount() - 1; i >= 0; i--) { + backend->AddStepAtIndex(i); + LOG(DEBUG) << "StepFail is inserted at: " << i; + ASSERT_EQ(ci::AppInstaller::Result::ERROR, backend->Run()); + if (!validator()) + break; + } + ASSERT_EQ(-1, i); +} + +void BackendInterface::CrashAfterEachStep(std::vector* args, + std::function validator, PackageType type) const { + std::unique_ptr argv(new const char*[args->size()]); + for (size_t i = 0; i < args->size(); ++i) { + argv[i] = args->at(i).c_str(); + } + TestPkgmgrInstaller pkgmgr_installer; + auto query_interface = CreateQueryInterface(); + auto pkgmgr = + ci::PkgMgrInterface::Create(args->size(), const_cast(argv.get()), + &pkgmgr_installer, + query_interface.get()); + if (!pkgmgr) { + LOG(ERROR) << "Failed to initialize pkgmgr interface"; + return; + } + auto backend = CreateInstaller(pkgmgr); + int stepCount = backend->StepCount(); + + args->push_back("-idx"); + args->push_back(std::to_string(stepCount)); + int i; + std::string prefix = (type == PackageType::TPK) ? "tpk" : "wgt"; + for (i = 0; i < stepCount; i++) { + ci::Subprocess backend_crash( + "/usr/bin/" + prefix + "-backend-ut/smoke-test-helper"); + args->back() = std::to_string(i); + backend_crash.Run(*args); + ASSERT_NE(backend_crash.Wait(), 0); + if (!validator(i)) + break; + } + ASSERT_EQ(stepCount, i); +} + +BackendInterface::CommandResult BackendInterface::RunInstallerWithPkgrmgr( + ci::PkgMgrPtr pkgmgr) const { + std::unique_ptr installer = CreateInstaller(pkgmgr); + switch (mode_) { + case RequestResult::FAIL: + installer->AddStep(); + break; + default: + break; + } + return installer->Run(); +} + +BackendInterface::CommandResult BackendInterface::CallBackend(int argc, + const char* argv[]) const { + TestPkgmgrInstaller pkgmgr_installer; + auto query_interface = CreateQueryInterface(); + auto pkgmgr = ci::PkgMgrInterface::Create(argc, const_cast(argv), + &pkgmgr_installer, query_interface.get()); + if (!pkgmgr) { + LOG(ERROR) << "Failed to initialize pkgmgr interface"; + return BackendInterface::CommandResult::UNKNOWN; + } + return RunInstallerWithPkgrmgr(pkgmgr); +} + +BackendInterface::CommandResult BackendInterface::Install( + const bf::path& path) const { + const char* argv[] = {"", "-i", path.c_str(), "-u", uid_str_.c_str()}; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::InstallPreload( + const bf::path& path) const { + const char* argv[] = {"", "-i", path.c_str(), "--preload"}; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::InstallWithStorage( + const bf::path& path, StorageType type) const { + int default_storage = 0; + int storage = 0; + switch (type) { + case StorageType::EXTERNAL: + storage = 1; + break; + case StorageType::EXTENDED: + storage = 2; + break; + default: + LOG(ERROR) << "Unknown storage type"; + break; + } + vconf_get_int(VCONFKEY_SETAPPL_DEFAULT_MEM_INSTALL_APPLICATIONS_INT, + &default_storage); + vconf_set_int(VCONFKEY_SETAPPL_DEFAULT_MEM_INSTALL_APPLICATIONS_INT, storage); + + const char* argv[] = {"", "-i", path.c_str(), "-u", uid_str_.c_str()}; + BackendInterface::CommandResult result = CallBackend(SIZEOFARRAY(argv), argv); + + vconf_set_int(VCONFKEY_SETAPPL_DEFAULT_MEM_INSTALL_APPLICATIONS_INT, + default_storage); + return result; +} + +BackendInterface::CommandResult BackendInterface::MigrateLegacyExternalImage( + const std::string& pkgid, + const bf::path& path, + const bf::path& legacy_path) const { + if (InstallWithStorage(path, StorageType::EXTERNAL) != + BackendInterface::CommandResult::OK) { + LOG(ERROR) << "Failed to install application. Cannot perform Migrate"; + return BackendInterface::CommandResult::ERROR; + } + + bf::path ext_mount_path = ci::GetExternalCardPath(); + if (bf::is_empty(ext_mount_path)) { + LOG(ERROR) << "Sdcard not exists!"; + return BackendInterface::CommandResult::ERROR; + } + bf::path app2sd_path = ext_mount_path / "app2sd"; + + char* image_name = app2ext_usr_getname_image(pkgid.c_str(), + kGlobalUserUid); + if (!image_name) { + LOG(ERROR) << "Failed to get external image name"; + return BackendInterface::CommandResult::ERROR; + } + bf::path org_image = app2sd_path / image_name; + free(image_name); + + bs::error_code error; + bf::remove(org_image, error); + if (error) { + LOG(ERROR) << "Failed to remove org image"; + return BackendInterface::CommandResult::ERROR; + } + + bf::path db_path = tzplatform_getenv(TZ_SYS_DB); + bf::path app2sd_db = db_path / ".app2sd.db"; + bf::path app2sd_db_journal = db_path / ".app2sd.db-journal"; + bf::remove(app2sd_db, error); + if (error) { + LOG(ERROR) << "Failed to remove app2sd db"; + return BackendInterface::CommandResult::ERROR; + } + bf::remove(app2sd_db_journal, error); + if (error) { + LOG(ERROR) << "Failed to remove app2sd journal db"; + return BackendInterface::CommandResult::ERROR; + } + + bf::path app2sd_migrate_db = legacy_path / kMigrateTestDBName; + if (!ci::CopyFile(app2sd_migrate_db, app2sd_db)) { + LOG(ERROR) << "Failed to copy test db"; + return BackendInterface::CommandResult::ERROR; + } + + bf::path legacy_src = legacy_path / pkgid; + bf::path legacy_dst = app2sd_path / pkgid; + if (!ci::CopyFile(legacy_src, legacy_dst)) { + LOG(ERROR) << "Failed to copy test image"; + return BackendInterface::CommandResult::ERROR; + } + const char* argv[] = {"", "--migrate-extimg", pkgid.c_str(), + "-u", uid_str_.c_str()}; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::RDSUpdate( + const bf::path& path, + const std::string& pkgid) const { + RequestResult tmp_mode = mode_; + RequestResult &original_mode = const_cast(mode_); + original_mode = RequestResult::NORMAL; + if (Install(path) != BackendInterface::CommandResult::OK) { + LOG(ERROR) << "Failed to install application. Cannot perform RDS"; + return BackendInterface::CommandResult::UNKNOWN; + } + const char* argv[] = {"", "-r", pkgid.c_str(), "-u", + uid_str_.c_str()}; + original_mode = tmp_mode; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::EnablePackage( + const std::string& pkgid) const { + const char* argv[] = {"", "-A", pkgid.c_str(), "-u", uid_str_.c_str()}; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::DisablePackage( + const std::string& pkgid) const { + const char* argv[] = {"", "-D", pkgid.c_str(), "-u", uid_str_.c_str()}; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::Recover( + const bf::path& recovery_file) const { + const char* argv[] = {"", "-b", recovery_file.c_str(), "-u", + uid_str_.c_str()}; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::ManifestDirectInstall( + const std::string& pkgid) const { + const char* argv[] = {"", "-y", pkgid.c_str(), "-u", uid_str_.c_str()}; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::Uninstall( + const std::string& pkgid) const { + const char* argv[] = {"", "-d", pkgid.c_str(), "-u", uid_str_.c_str()}; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::UninstallPreload( + const std::string& pkgid) const { + const char* argv[] = {"", "-d", pkgid.c_str(), "--preload", + "--force-remove"}; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::InstallSuccess( + const bf::path& path) const { + RequestResult tmp_mode = mode_; + RequestResult &original_mode = const_cast(mode_); + original_mode = RequestResult::NORMAL; + if (Install(path) != BackendInterface::CommandResult::OK) { + LOG(ERROR) << "Failed to install application. Cannot update"; + return BackendInterface::CommandResult::UNKNOWN; + } + original_mode = tmp_mode; + return BackendInterface::CommandResult::OK; +} + +BackendInterface::CommandResult BackendInterface::InstallPreloadSuccess( + const bf::path& path) const { + RequestResult tmp_mode = mode_; + RequestResult &original_mode = const_cast(mode_); + original_mode = RequestResult::NORMAL; + if (InstallPreload(path) != BackendInterface::CommandResult::OK) { + LOG(ERROR) << "Failed to install application. Cannot update"; + return BackendInterface::CommandResult::UNKNOWN; + } + original_mode = tmp_mode; + return BackendInterface::CommandResult::OK; +} + +BackendInterface::CommandResult BackendInterface::MountInstall( + const bf::path& path) const { + const char* argv[] = {"", "-w", path.c_str(), "-u", uid_str_.c_str()}; + return CallBackend(SIZEOFARRAY(argv), argv); +} + +BackendInterface::CommandResult BackendInterface::MountInstallSuccess( + const bf::path& path) const { + RequestResult tmp_mode = mode_; + RequestResult &original_mode = const_cast(mode_); + original_mode = RequestResult::NORMAL; + if (MountInstall(path) != BackendInterface::CommandResult::OK) { + LOG(ERROR) << "Failed to mount-install application. Cannot mount-update"; + return BackendInterface::CommandResult::UNKNOWN; + } + original_mode = tmp_mode; + return BackendInterface::CommandResult::OK; +} + +static boost::filesystem::path GetTrashPath( + const boost::filesystem::path& path) { + return path.string() + ".trash"; +} + +bool BackupPath(const bf::path& path) { + bf::path trash_path = GetTrashPath(path); + if (bf::exists(trash_path)) { + LOG(ERROR) << trash_path << " exists. Please remove " + << trash_path << " manually!"; + return false; + } + bf::path backup_path = path.string() + ".bck"; + std::cout << "Backup path: " << path << " to " << backup_path << std::endl; + bs::error_code error; + bf::remove_all(backup_path, error); + if (error) + LOG(ERROR) << "Remove failed: " << backup_path + << " (" << error.message() << ")"; + if (bf::exists(path)) { + bf::rename(path, backup_path, error); + if (error) { + LOG(ERROR) << "Failed to setup test environment. Does some previous" + << " test crashed? Path: " + << backup_path << " should not exist."; + return false; + } + assert(!error); + } + return true; +} + +bool RestorePath(const bf::path& path) { + bf::path backup_path = path.string() + ".bck"; + std::cout << "Restore path: " << path << " from " << backup_path << std::endl; + bs::error_code error; + bf::remove_all(path, error); + if (error) { + bf::path trash_path = GetTrashPath(path); + LOG(ERROR) << "Remove failed: " << path << " (" << error.message() << ")"; + std::cout << "Moving " << path << " to " << trash_path << std::endl; + bf::rename(path, trash_path, error); + if (error) + LOG(ERROR) << "Failed to move " << path << " to " << trash_path + << " (" << error.message() << ")"; + else + LOG(ERROR) << trash_path << " should be removed manually!"; + } + if (bf::exists(backup_path)) { + bf::rename(backup_path, path, error); + if (error) { + LOG(ERROR) << "Failed to restore backup path: " << backup_path + << " (" << error.message() << ")"; + return false; + } + } + return true; +} + +std::vector SetupBackupDirectories(uid_t test_uid) { + std::vector entries; + bf::path db_dir = bf::path(tzplatform_getenv(TZ_SYS_DB)); + if (test_uid != kGlobalUserUid) + db_dir = db_dir / "user" / std::to_string(test_uid); + for (auto e : kDBEntries) { + bf::path path = db_dir / e; + entries.emplace_back(path); + } + + if (getuid() == 0) { + entries.emplace_back(kPreloadApps); + entries.emplace_back(kPreloadManifestDir); + entries.emplace_back(kPreloadIcons); + } + + if (test_uid == kGlobalUserUid) { + entries.emplace_back(kSkelDir); + entries.emplace_back(kGlobalManifestDir); + ci::UserList list = ci::GetUserList(); + for (auto l : list) { + bf::path apps = std::get<2>(l) / "apps_rw"; + entries.emplace_back(apps); + } + } else { + tzplatform_set_user(test_uid); + bf::path approot = tzplatform_getenv(TZ_USER_APPROOT); + tzplatform_reset_user(); + entries.emplace_back(approot); + } + + bf::path apps_rw = ci::GetRootAppPath(false, test_uid); + entries.emplace_back(apps_rw); + + return entries; +} + +void UninstallAllAppsInDirectory(bf::path dir, bool is_preload, + BackendInterface* backend) { + if (bf::exists(dir)) { + for (auto& dir_entry : boost::make_iterator_range( + bf::directory_iterator(dir), bf::directory_iterator())) { + if (dir_entry.path().string().find("smoke") != std::string::npos && + bf::is_directory(dir_entry)) { + std::string package = dir_entry.path().filename().string(); + std::regex pkg_regex("smoke[a-zA-Z0-9]{5,}"); + if (std::regex_match(package, pkg_regex)) { + BackendInterface::CommandResult result = + BackendInterface::CommandResult::OK; + if (is_preload) + result = backend->UninstallPreload( + dir_entry.path().filename().string()); + else + result = backend->Uninstall( + dir_entry.path().filename().string()); + if (result != BackendInterface::CommandResult::OK) { + LOG(ERROR) << "Cannot uninstall smoke test app: " + << dir_entry.path().filename().string(); + } + } + } + } + } +} + +void UninstallAllSmokeApps(ci::RequestMode request_mode, uid_t test_uid, + BackendInterface *backend) { + std::cout << "Uninstalling all smoke apps" << std::endl; + bf::path apps_rw = ci::GetRootAppPath(false, test_uid); + UninstallAllAppsInDirectory(apps_rw, false, backend); + if (getuid() == 0 && request_mode == ci::RequestMode::GLOBAL) { + bf::path root_path = kPreloadApps; + UninstallAllAppsInDirectory(root_path, true, backend); + } +} + +int GetAppInstalledTime(const char* appid, uid_t uid) { + int ret = 0; + int installed_time = 0; + pkgmgrinfo_appinfo_h handle = NULL; + ret = pkgmgrinfo_appinfo_get_usr_appinfo(appid, uid, &handle); + if (ret != PMINFO_R_OK) + return -1; + ret = pkgmgrinfo_appinfo_get_installed_time(handle, &installed_time); + if (ret != PMINFO_R_OK) { + pkgmgrinfo_appinfo_destroy_appinfo(handle); + return -1; + } + pkgmgrinfo_appinfo_destroy_appinfo(handle); + return installed_time; +} + +} // namespace smoke_test diff --git a/src/unit_tests/common/smoke_utils.h b/src/unit_tests/common/smoke_utils.h new file mode 100644 index 0000000..db78547 --- /dev/null +++ b/src/unit_tests/common/smoke_utils.h @@ -0,0 +1,269 @@ +// Copyright (c) 2017 Samsung Electronics Co., Ltd All Rights Reserved +// Use of this source code is governed by an apache-2.0 license that can be +// found in the LICENSE file. + +#ifndef UNIT_TESTS_COMMON_SMOKE_UTILS_H_ +#define UNIT_TESTS_COMMON_SMOKE_UTILS_H_ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#define SIZEOFARRAY(ARR) \ + sizeof(ARR) / sizeof(ARR[0]) \ + +#define EXTENDED_ASSERT_TRUE(expression) do { \ + bool tmp = expression; \ + EXPECT_TRUE(tmp) << #expression << " is not true"; \ + if (!tmp) \ + return false; \ +} while (0); + +#define EXTENDED_ASSERT_FALSE(expression) do { \ + bool tmp = expression; \ + EXPECT_FALSE(tmp) << #expression << " is not false"; \ + if (tmp) \ + return false; \ +} while (0); + +#define EXTENDED_ASSERT_EQ(expression, value) do { \ + auto ret = expression; \ + EXPECT_EQ(ret, value) << #expression << " is not equal to " << #value; \ + if (ret != value) \ + return false; \ +} while (0); + +namespace smoke_test { + +extern const bf::path kSmokePackagesDirectory; +extern const uid_t kGlobalUserUid; +extern const uid_t kGlobalUserGid; +extern const char kLegacyExtImageDir[]; +extern const std::string& kDefaultUserIdStr; + +enum class RequestResult { + NORMAL, + FAIL +}; + +enum class StorageType { + INTERNAL, + EXTERNAL, + EXTENDED +}; + +class ScopedTzipInterface { + public: + explicit ScopedTzipInterface(const std::string& pkgid, uid_t test_user) + : pkg_path_(boost::filesystem::path( + common_installer::GetRootAppPath(false, test_user)) / pkgid), + interface_(common_installer::GetMountLocation(pkg_path_)), + mounted_(true) { + interface_.MountZip(common_installer::GetZipPackageLocation(pkg_path_, + pkgid)); + } + + void Release() { + if (mounted_) { + interface_.UnmountZip(); + mounted_ = false; + } + } + + ~ScopedTzipInterface() { + Release(); + } + + private: + boost::filesystem::path pkg_path_; + common_installer::TzipInterface interface_; + bool mounted_; +}; + +class TestPkgmgrInstaller : public common_installer::PkgmgrInstallerInterface { + public: + bool CreatePkgMgrInstaller(pkgmgr_installer** installer, + common_installer::InstallationMode* mode) { + *installer = pkgmgr_installer_offline_new(); + if (!*installer) + return false; + *mode = common_installer::InstallationMode::ONLINE; + return true; + } + + bool ShouldCreateSignal() const { + return false; + } +}; + +enum class PackageType { + TPK, + WGT, + HYBRID +}; + +struct User { + uid_t uid = kGlobalUserUid; + gid_t gid = kGlobalUserGid; +}; + +struct TestParameters { + TestParameters(PackageType type, bool readonly) : + pkg_type(type), is_readonly(readonly) {} + TestParameters(const TestParameters& other) : pkg_type(other.pkg_type), + is_readonly(other.is_readonly), test_user{other.test_user} {} + PackageType pkg_type; + bool is_readonly; + User test_user; +}; + + +common_installer::RequestMode ParseRequestMode(int argc, char** argv); + +bool TouchFile(const boost::filesystem::path& path); + +void AddDataFiles(const std::string& pkgid, uid_t uid); + +bool AddTestUser(User* test_user); + +bool DeleteTestUser(); + +void RemoveAllRecoveryFiles(const std::string& prefix, uid_t uid); + +boost::filesystem::path FindRecoveryFile(const std::string& prefix, uid_t uid); + +boost::filesystem::path GetPackageRoot(const std::string& pkgid, uid_t uid); + +bool ValidateFileContentInPackage(const std::string& pkgid, + const std::string& relative, + const std::string& expected, + const TestParameters& params); + +bool ValidatePackage(const std::string& pkgid, + const std::vector& appids, + const TestParameters& params); + +bool ValidateDataFiles(const std::string& pkgid, uid_t uid); + +bool ValidateExternalPackage(const std::string& pkgid, + const std::vector& appids, + const TestParameters& params); +bool ValidateExtendedPackage(const std::string& pkgid, + const std::vector& appid, + const TestParameters& params); + +bool CheckPackageNonExistance(const std::string& pkgid, + const TestParameters& params); + +bool CheckPackageReadonlyNonExistance(const std::string& pkgid, + const TestParameters& params); + +bool TouchFile(const boost::filesystem::path& path); + +class BackendInterface { + public: + using CommandResult = common_installer::AppInstaller::Result; + explicit BackendInterface(std::string uid, + RequestResult mode = RequestResult::NORMAL) + : uid_str_(uid), mode_(mode) {} + virtual ~BackendInterface() {} + + void TestRollbackAfterEachStep(int argc, const char* argv[], + std::function validator) const; + void CrashAfterEachStep(std::vector* args, + std::function validator, + PackageType type) const; + CommandResult Install(const boost::filesystem::path& path) const; + CommandResult InstallPreload(const boost::filesystem::path& path) const; + CommandResult InstallWithStorage(const boost::filesystem::path& path, + StorageType type = StorageType::INTERNAL) const; + CommandResult InstallSuccess(const bf::path& path) const; + CommandResult InstallPreloadSuccess( + const boost::filesystem::path& path) const; + + CommandResult Uninstall(const std::string& pkgid) const; + CommandResult UninstallPreload(const std::string& pkgid) const; + + CommandResult EnablePackage(const std::string& pkgid) const; + CommandResult DisablePackage(const std::string& pkgid) const; + + CommandResult MountInstall(const boost::filesystem::path& path) const; + CommandResult MountInstallSuccess(const bf::path& path) const; + CommandResult ManifestDirectInstall(const std::string& pkgid) const; + + CommandResult MigrateLegacyExternalImage(const std::string& pkgid, + const boost::filesystem::path& path, + const boost::filesystem::path& legacy_path) const; + + CommandResult RDSUpdate(const boost::filesystem::path& path, + const std::string& pkgid) const; + + CommandResult Recover(const boost::filesystem::path& recovery_file) const; + + protected: + CommandResult CallBackend(int argc, const char* argv[]) const; + using AppQueryInterfacePtr = + std::unique_ptr; + using AppInstallerPtr = std::unique_ptr; + std::string uid_str_; + RequestResult mode_; + + private: + CommandResult RunInstallerWithPkgrmgr( + common_installer::PkgMgrPtr pkgmgr) const; + virtual AppQueryInterfacePtr CreateQueryInterface() const = 0; + virtual AppInstallerPtr CreateInstaller( + common_installer::PkgMgrPtr pkgmgr) const = 0; +}; + +bool CheckAvailableExternalPath(); + +bool CheckAvailableExtendedPath(); + +bool BackupPath(const boost::filesystem::path& path); + +bool RestorePath(const boost::filesystem::path& path); + +std::vector SetupBackupDirectories(uid_t test_uid); + +void UninstallAllAppsInDirectory(boost::filesystem::path dir, bool is_preload, + BackendInterface* backend); + +void UninstallAllSmokeApps(common_installer::RequestMode request_mode, + uid_t test_uid, BackendInterface* backend); + +int GetAppInstalledTime(const char* appid, uid_t uid); + +} // namespace smoke_test + +#endif // UNIT_TESTS_COMMON_SMOKE_UTILS_H_ -- 2.7.4