--- /dev/null
+// 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 <gum/gum-user.h>
+#include <gum/gum-user-service.h>
+#include <gum/common/gum-user-types.h>
+#include <manifest_parser/utils/version_number.h>
+
+#include <boost/filesystem/path.hpp>
+#include <gtest/gtest.h>
+
+#include <common/app_installer.h>
+#include <common/paths.h>
+#include <common/pkgmgr_interface.h>
+#include <common/pkgmgr_query.h>
+#include <common/tzip_interface.h>
+
+#include <memory>
+#include <string>
+#include <vector>
+
+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<std::string> 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<std::string>(), "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<std::string>() == "global") {
+ std::cout << "Request mode was set to global." << std::endl;
+ return ci::RequestMode::GLOBAL;
+ }
+ if (vm["request-mode"].as<std::string>() == "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::string>() << 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_t> uid = ci::GetUidByUserName(kNormalUserName)) {
+ test_user->uid = *uid;
+ std::cout << "User created properly: uid=" << *uid;
+ if (boost::optional<gid_t> 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_t> 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<char, 200> 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<gid_t> 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<std::string>& 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<bool()> validator) const {
+ TestPkgmgrInstaller pkgmgr_installer;
+ std::unique_ptr<ci::AppQueryInterface> query_interface =
+ CreateQueryInterface();
+ auto pkgmgr =
+ ci::PkgMgrInterface::Create(argc, const_cast<char**>(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<ci::configuration::StepFail>(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<std::string>* args,
+ std::function<bool(int iter)> validator, PackageType type) const {
+ std::unique_ptr<const char*[]> 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<char**>(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<ci::AppInstaller> installer = CreateInstaller(pkgmgr);
+ switch (mode_) {
+ case RequestResult::FAIL:
+ installer->AddStep<ci::configuration::StepFail>();
+ 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<char**>(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<RequestResult&>(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<RequestResult&>(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<RequestResult&>(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<RequestResult&>(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<bf::path> SetupBackupDirectories(uid_t test_uid) {
+ std::vector<bf::path> 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
--- /dev/null
+// 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 <pkgmgr-info.h>
+#include <signal.h>
+#include <unistd.h>
+#include <tzplatform_config.h>
+#include <vconf.h>
+#include <vconf-internal-keys.h>
+
+#include <boost/filesystem/operations.hpp>
+#include <boost/range/iterator_range.hpp>
+#include <boost/format.hpp>
+#include <boost/program_options.hpp>
+#include <boost/system/error_code.hpp>
+
+#include <gtest/gtest.h>
+#include <gtest/gtest-death-test.h>
+
+#include <manifest_parser/utils/version_number.h>
+
+#include <common/utils/subprocess.h>
+#include <common/step/configuration/step_fail.h>
+#include <common/utils/user_util.h>
+#include <common/utils/file_util.h>
+#include <common/request.h>
+#include <common/tzip_interface.h>
+#include <common/app_installer.h>
+#include <common/paths.h>
+
+#include <array>
+#include <cstdio>
+#include <cstdlib>
+#include <regex>
+#include <string>
+#include <vector>
+
+#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<std::string>& appids,
+ const TestParameters& params);
+
+bool ValidateDataFiles(const std::string& pkgid, uid_t uid);
+
+bool ValidateExternalPackage(const std::string& pkgid,
+ const std::vector<std::string>& appids,
+ const TestParameters& params);
+bool ValidateExtendedPackage(const std::string& pkgid,
+ const std::vector<std::string>& 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<bool()> validator) const;
+ void CrashAfterEachStep(std::vector<std::string>* args,
+ std::function<bool(int iter)> 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<common_installer::AppQueryInterface>;
+ using AppInstallerPtr = std::unique_ptr<common_installer::AppInstaller>;
+ 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<boost::filesystem::path> 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_