--- /dev/null
+// 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(>est_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;
+ }
+}