From bf67a47eed467841e30eff091f02894ecd130fe5 Mon Sep 17 00:00:00 2001 From: Ilho Kim Date: Wed, 5 Jun 2019 16:45:54 +0900 Subject: [PATCH] Implement recovery test for forced termination Using time interval, kill install process and perform a recovery test Requires: - https://review.tizen.org/gerrit/#/c/platform/core/appfw/app-installers/+/209477/ Change-Id: Ifa90cab0b5b0da70f620bfcfcb8f39a81d2ee180 Signed-off-by: Ilho Kim --- CMakeLists.txt | 1 + src/unit_tests/CMakeLists.txt | 15 ++ src/unit_tests/recovery_test.cc | 373 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 389 insertions(+) create mode 100644 src/unit_tests/recovery_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 41bda08..ce42c94 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ SET(TARGET_SMOKE_TEST "smoke-test") SET(TARGET_SMOKE_TEST_EXTENSIVE "extensive-smoke-test") SET(TARGET_SMOKE_TEST_HELPER "smoke-test-helper") SET(TARGET_MANIFEST_TEST "manifest-test") +SET(TARGET_RECOVERY_TEST "recovery-test") ADD_DEFINITIONS("-DPROJECT_TAG=\"TPK_BACKEND\"") diff --git a/src/unit_tests/CMakeLists.txt b/src/unit_tests/CMakeLists.txt index 45d5c6c..85cadae 100644 --- a/src/unit_tests/CMakeLists.txt +++ b/src/unit_tests/CMakeLists.txt @@ -27,10 +27,17 @@ ADD_EXECUTABLE(${TARGET_MANIFEST_TEST} manifest_test_widget_application.cc ) +ADD_EXECUTABLE(${TARGET_RECOVERY_TEST} + recovery_test.cc + smoke_utils.h + smoke_utils.cc +) + TARGET_INCLUDE_DIRECTORIES(${TARGET_SMOKE_TEST} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../) TARGET_INCLUDE_DIRECTORIES(${TARGET_SMOKE_TEST_EXTENSIVE} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../) TARGET_INCLUDE_DIRECTORIES(${TARGET_SMOKE_TEST_HELPER} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../) TARGET_INCLUDE_DIRECTORIES(${TARGET_MANIFEST_TEST} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../) +TARGET_INCLUDE_DIRECTORIES(${TARGET_RECOVERY_TEST} PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/../) INSTALL(DIRECTORY test_samples/ DESTINATION ${SHAREDIR}/${DESTINATION_DIR}/test_samples) @@ -50,6 +57,12 @@ APPLY_PKG_CONFIG(${TARGET_MANIFEST_TEST} PUBLIC Boost GTEST ) +APPLY_PKG_CONFIG(${TARGET_RECOVERY_TEST} PUBLIC + Boost + GTEST + GUM_DEPS + VCONF_DEPS +) # FindGTest module do not sets all needed libraries in GTEST_LIBRARIES and # GTest main libraries is still missing, so additional linking of @@ -58,8 +71,10 @@ TARGET_LINK_LIBRARIES(${TARGET_SMOKE_TEST} PRIVATE ${TARGET_LIBNAME_TPK} ${GTEST TARGET_LINK_LIBRARIES(${TARGET_SMOKE_TEST_EXTENSIVE} PRIVATE ${TARGET_LIBNAME_TPK} ${GTEST_MAIN_LIBRARIES} ${TARGET_SMOKE_UTILS}) TARGET_LINK_LIBRARIES(${TARGET_SMOKE_TEST_HELPER} PRIVATE ${TARGET_LIBNAME_TPK}) TARGET_LINK_LIBRARIES(${TARGET_MANIFEST_TEST} PRIVATE ${TARGET_LIBNAME_TPK} ${GTEST_MAIN_LIBRARIES}) +TARGET_LINK_LIBRARIES(${TARGET_RECOVERY_TEST} PRIVATE ${TARGET_LIBNAME_TPK} ${GTEST_MAIN_LIBRARIES} ${TARGET_SMOKE_UTILS}) INSTALL(TARGETS ${TARGET_SMOKE_TEST} DESTINATION ${BINDIR}/${DESTINATION_DIR}) INSTALL(TARGETS ${TARGET_SMOKE_TEST_EXTENSIVE} DESTINATION ${BINDIR}/${DESTINATION_DIR}) INSTALL(TARGETS ${TARGET_SMOKE_TEST_HELPER} DESTINATION ${BINDIR}/${DESTINATION_DIR}) INSTALL(TARGETS ${TARGET_MANIFEST_TEST} DESTINATION ${BINDIR}/${DESTINATION_DIR}) +INSTALL(TARGETS ${TARGET_RECOVERY_TEST} DESTINATION ${BINDIR}/${DESTINATION_DIR}) diff --git a/src/unit_tests/recovery_test.cc b/src/unit_tests/recovery_test.cc new file mode 100644 index 0000000..7161aea --- /dev/null +++ b/src/unit_tests/recovery_test.cc @@ -0,0 +1,373 @@ +// Copyright (c) 2019 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 +#include + +#include + +#include + +#include +#include + +#include "unit_tests/smoke_utils.h" + +namespace bf = boost::filesystem; +namespace ci = common_installer; + +typedef enum { + UNKNOWN_REQ = -1, + INSTALL_REQ = 0, + UPDATE_REQ = 1, + DELTA_REQ = 2, + MOUNT_INSTALL_REQ = 3, + MOUNT_UPDATE_REQ = 4, +} REQ_TYPE; + +char *install_req_filter[] = { + "--gtest_filter=SmokeTest.RecoveryMode_Tpk_Installation", + "--gtest_filter=SmokeTest.RecoveryMode_Tpk_Update", + "--gtest_filter=SmokeTest.RecoveryMode_ForDelta", + "--gtest_filter=SmokeTest.RecoveryMode_ForMountInstall", + "--gtest_filter=SmokeTest.RecoveryMode_ForMountUpdate" +}; + +int delay = 1000000; +int interval = 500000; +int test_count = 0; + +namespace smoke_test { + +class SmokeEnvironment : public testing::Environment { + public: + explicit SmokeEnvironment(ci::RequestMode mode) : request_mode_(mode) { + } + + void SetUp() override { + if (request_mode_ == ci::RequestMode::USER) + ASSERT_TRUE(AddTestUser(&test_user)); + + backups_ = SetupBackupDirectories(test_user.uid); + for (auto& path : backups_) + ASSERT_TRUE(BackupPath(path)); + } + + void TearDown() override { + ASSERT_TRUE(request_mode_ == ci::RequestMode::GLOBAL || + (request_mode_ == ci::RequestMode::USER && + kGlobalUserUid != test_user.uid)); + + TpkBackendInterface backend(std::to_string(test_user.uid)); + UninstallAllSmokeApps(request_mode_, test_user.uid, &backend); + + for (auto& path : backups_) + ASSERT_TRUE(RestorePath(path)); + + if (request_mode_ == ci::RequestMode::USER) + ASSERT_TRUE(DeleteTestUser()); + + test_count++; + } + + User test_user; + + private: + ci::RequestMode request_mode_; + std::vector backups_; +}; + +} // namespace smoke_test + +namespace { + +smoke_test::SmokeEnvironment *env = nullptr; + +void signalHandler(int signum) { + env->TearDown(); + exit(signum); +} + +} // namespace + +namespace smoke_test { + +class SmokeTest : public testing::Test { + public: + SmokeTest() : backend(std::to_string(env->test_user.uid)), + params{PackageType::TPK, false} { + params.test_user.uid = env->test_user.uid; + params.test_user.gid = env->test_user.gid; + } + + protected: + TpkBackendInterface backend; + TestParameters params; +}; + +TEST_F(SmokeTest, RecoveryMode_Tpk_Installation) { + RemoveAllRecoveryFiles("/tpk-recovery", params.test_user.uid); + + bf::path path = kSmokePackagesDirectory / "RecoveryMode_Tpk_Installation.tpk"; + ci::Subprocess tpk_backend("/usr/bin/tpk-backend"); + std::string test_uid_str = std::to_string(params.test_user.uid); + + tpk_backend.Run("-i", path.string(), "-u", test_uid_str.c_str()); + usleep(delay + interval * test_count); + tpk_backend.Kill(); + + if (tpk_backend.Wait() == 9) { + std::string pkgid = "smokeapp15"; + bf::path recovery_file = FindRecoveryFile("/tpk-recovery", + params.test_user.uid); + + ASSERT_FALSE(recovery_file.empty()); + ASSERT_EQ(backend.Recover(recovery_file), ci::AppInstaller::Result::OK); + ASSERT_TRUE(CheckPackageNonExistance(pkgid, params)); + } else { + std::cout << "install finished before process killed" << std::endl; + } +} + +TEST_F(SmokeTest, RecoveryMode_Tpk_Update) { + RemoveAllRecoveryFiles("/tpk-recovery", params.test_user.uid); + + bf::path path_old = kSmokePackagesDirectory / "RecoveryMode_Tpk_Update.tpk"; + bf::path path_new = kSmokePackagesDirectory / "RecoveryMode_Tpk_Update_2.tpk"; + ASSERT_EQ(backend.Install(path_old), ci::AppInstaller::Result::OK); + ci::Subprocess tpk_backend("/usr/bin/tpk-backend"); + std::string test_uid_str = std::to_string(params.test_user.uid); + + tpk_backend.Run("-i", path_new.string(), "-u", test_uid_str.c_str()); + usleep(delay + interval * test_count); + tpk_backend.Kill(); + + if (tpk_backend.Wait() == 9) { + std::string pkgid = "smokeapp16"; + std::string appid = "smokeapp16.RecoveryModeTpkUpdate"; + std::string exec = "native"; + bf::path recovery_file = FindRecoveryFile("/tpk-recovery", + params.test_user.uid); + + ASSERT_FALSE(recovery_file.empty()); + ASSERT_EQ(backend.Recover(recovery_file), ci::AppInstaller::Result::OK); + ASSERT_TRUE(ValidatePackage(pkgid, {appid, exec}, params)); + ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "VERSION", "1\n", params)); + } else { + std::cout << "update finished before process killed" << std::endl; + } +} + +TEST_F(SmokeTest, RecoveryMode_ForDelta) { + RemoveAllRecoveryFiles("/tpk-recovery", params.test_user.uid); + + bf::path path_old = kSmokePackagesDirectory / "RecoveryMode_ForDelta.tpk"; + bf::path path_new = kSmokePackagesDirectory / "RecoveryMode_ForDelta.delta"; + ASSERT_EQ(backend.Install(path_old), ci::AppInstaller::Result::OK); + ci::Subprocess tpk_backend("/usr/bin/tpk-backend"); + std::string test_uid_str = std::to_string(params.test_user.uid); + + tpk_backend.Run("-i", path_new.string(), "-u", test_uid_str.c_str()); + usleep(delay + interval * test_count); + tpk_backend.Kill(); + + if (tpk_backend.Wait() == 9) { + std::string pkgid = "smoketpk35"; + std::string appid = "smoketpk35.RecoveryMode_ForDelta"; + std::string exec = "smoketpk35"; + bf::path recovery_file = FindRecoveryFile("/tpk-recovery", + params.test_user.uid); + bf::path root_path = ci::GetRootAppPath(params.is_readonly, + params.test_user.uid); + + ASSERT_FALSE(recovery_file.empty()); + ASSERT_EQ(ci::AppInstaller::Result::OK, backend.Recover(recovery_file)); + ASSERT_TRUE(ValidatePackage(pkgid, {appid, exec}, params)); + ASSERT_TRUE(ValidateFileContentInPackage(pkgid, "res/MODIFIED", "version 1", + params)); + ASSERT_TRUE(bf::exists(root_path / pkgid / "res/DELETED")); + ASSERT_FALSE(bf::exists(root_path / pkgid / "res/ADDED")); + } else { + std::cout << "delta finished before process killed" << std::endl; + } +} + +TEST_F(SmokeTest, RecoveryMode_ForMountInstall) { + RemoveAllRecoveryFiles("/tpk-recovery", params.test_user.uid); + + bf::path path = kSmokePackagesDirectory / "RecoveryMode_ForMountInstall.tpk"; + ci::Subprocess tpk_backend("/usr/bin/tpk-backend"); + std::string test_uid_str = std::to_string(params.test_user.uid); + + tpk_backend.Run("-w", path.string(), "-u", test_uid_str.c_str()); + usleep(delay + interval * test_count); + tpk_backend.Kill(); + + if (tpk_backend.Wait() == 9) { + std::string pkgid = "smoketpk37"; + bf::path recovery_file = FindRecoveryFile("/tpk-recovery", + params.test_user.uid); + + ASSERT_FALSE(recovery_file.empty()); + ASSERT_EQ(ci::AppInstaller::Result::OK, backend.Recover(recovery_file)); + ASSERT_TRUE(CheckPackageNonExistance(pkgid, params)); + } else { + std::cout << "mount install finished before process killed" << std::endl; + } +} + +TEST_F(SmokeTest, RecoveryMode_ForMountUpdate) { + RemoveAllRecoveryFiles("/tpk-recovery", params.test_user.uid); + + bf::path path_old = + kSmokePackagesDirectory / "RecoveryMode_ForMountUpdate.tpk"; + bf::path path_new = + kSmokePackagesDirectory / "RecoveryMode_ForMountUpdate2.tpk"; + ASSERT_EQ(ci::AppInstaller::Result::OK, backend.MountInstall(path_old)); + ci::Subprocess tpk_backend("/usr/bin/tpk-backend"); + std::string test_uid_str = std::to_string(params.test_user.uid); + + tpk_backend.Run("-w", path_new.string(), "-u", test_uid_str.c_str()); + usleep(delay + interval * test_count); + tpk_backend.Kill(); + + if (tpk_backend.Wait() == 9) { + std::string pkgid = "smoketpk38"; + std::string appid = "smoketpk38.RecoveryMode_ForMountUpdate"; + std::string exec = "smoketpk38"; + bf::path recovery_file = FindRecoveryFile("/tpk-recovery", + params.test_user.uid); + + // Filesystem may be mounted after crash + ScopedTzipInterface poweroff_unmount_interface(pkgid, params.test_user.uid); + poweroff_unmount_interface.Release(); + + ASSERT_FALSE(recovery_file.empty()); + ASSERT_EQ(ci::AppInstaller::Result::OK, backend.Recover(recovery_file)); + ScopedTzipInterface interface(pkgid, params.test_user.uid); + ASSERT_TRUE(ValidatePackage(pkgid, {appid, exec}, params)); + ASSERT_TRUE(ValidateFileContentInPackage(pkgid, + "res/VERSION", "1", params)); + } else { + std::cout << "mount update finished before process killed" << std::endl; + } +} + +} // namespace smoke_test + +#define BUF_SIZE 1024 + +const char *short_options = "iudmn:l:t:r"; +const struct option long_options[] = { + {"install", 0, NULL, 'i'}, + {"update", 0, NULL, 'u'}, + {"delta", 0, NULL, 'd'}, + {"mount-install", 0, NULL, 'm'}, + {"mount-update", 0, NULL, 'n'}, + {"delay", 1, NULL, 'l'}, + {"interval", 1, NULL, 't'}, + {"repeat", 1, NULL, 'r'}, + {0, 0, 0, 0} /* sentinel */ +}; + +static void __print_usage() { + printf("\n"); + printf("--install recovery test for forced termination during package installing\n"); + printf("--update recovery test for forced termination during package updating\n"); + printf("--delta recovery test for forced termination during package delta installing\n"); + printf("--mount-install recovery test for forced termination during package mount-installing\n"); + printf("--mount-update recovery test for forced termination during package mount-updating\n"); + printf("--delay fixed delay for forced termination\n"); + printf("--interval use with repeat option. as it repeat the interval is added to delay\n"); + printf("--repeat option for performing tests repeatedly\n"); +} + +int main(int argc, char** argv) { + int c = -1; + int opt_idx = 0; + int repeat_count = 10; + int req_type = -1; + bool repeat = false; + + if (argc == 1) { + __print_usage(); + return 0; + } + + while (1) { + c = getopt_long(argc, argv, short_options, long_options, &opt_idx); + if (c == -1) + break; /* Parse end */ + switch (c) { + case 'i': /* install */ + req_type = INSTALL_REQ; + break; + + case 'u': /* update */ + req_type = UPDATE_REQ; + break; + + case 'd': /* delta */ + req_type = DELTA_REQ; + break; + + case 'm': /* mount install */ + req_type = MOUNT_INSTALL_REQ; + break; + + case 'n': /* mount update */ + req_type = MOUNT_UPDATE_REQ; + break; + + case 'l': + if (optarg) + delay = atoi(optarg); + break; + + case 't': + if (optarg) + interval = atoi(optarg); + break; + + case 'r': /* repeat */ + repeat = true; + if (optarg) + repeat_count = atoi(optarg); + break; + + default: + break; + } + } + + if (req_type == -1) { + __print_usage(); + return 0; + } + + try { + char buf[BUF_SIZE]; + std::vector gtest_argv; + + gtest_argv.push_back(NULL); + gtest_argv.push_back(install_req_filter[req_type]); + if (repeat) { + snprintf(buf, sizeof(buf), "--gtest_repeat=%d", repeat_count); + gtest_argv.push_back(buf); + } + gtest_argv.push_back(NULL); + int gtest_argc = gtest_argv.size(); + + testing::InitGoogleTest(>est_argc, gtest_argv.data()); + ::env = static_cast( + testing::AddGlobalTestEnvironment( + new smoke_test::SmokeEnvironment(ci::RequestMode::GLOBAL))); + signal(SIGINT, ::signalHandler); + signal(SIGSEGV, ::signalHandler); + return RUN_ALL_TESTS(); + } catch (...) { + std::cout << "Exception occurred during testing" << std::endl; + return 1; + } +} -- 2.7.4