Implement recovery test for forced termination 64/207664/15
authorIlho Kim <ilho159.kim@samsung.com>
Wed, 5 Jun 2019 07:45:54 +0000 (16:45 +0900)
committerIlho Kim <ilho159.kim@samsung.com>
Tue, 23 Jul 2019 01:58:06 +0000 (10:58 +0900)
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 <ilho159.kim@samsung.com>
CMakeLists.txt
src/unit_tests/CMakeLists.txt
src/unit_tests/recovery_test.cc [new file with mode: 0644]

index 41bda08ae5c10081ea4c3e7503be47842d012eb0..ce42c94e273683d6e0b7c244ae44650585c094e8 100644 (file)
@@ -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\"")
 
index 45d5c6c0fdfab51e5fcbb86112af3c24f2ca3c7b..85cadaec26390dde640b4f0ad4973f86de7dce91 100644 (file)
@@ -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 (file)
index 0000000..7161aea
--- /dev/null
@@ -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 <common/utils/subprocess.h>
+#include <unit_tests/common/smoke_utils.h>
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+#include <string>
+#include <vector>
+
+#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<bf::path> 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    <u_time>   fixed delay for forced termination\n");
+  printf("--interval <u_time>   use with repeat option. as it repeat the interval is added to delay\n");
+  printf("--repeat   <count>    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<char *> 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(&gtest_argc, gtest_argv.data());
+    ::env = static_cast<smoke_test::SmokeEnvironment*>(
+        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;
+  }
+}