From: Ilho Kim Date: Tue, 3 Nov 2020 02:18:12 +0000 (+0900) Subject: Add FileInfoCollector class X-Git-Tag: accepted/tizen/7.0/unified/20230117.141737~1 X-Git-Url: http://review.tizen.org/git/?p=platform%2Fcore%2Fappfw%2Fapp-installers.git;a=commitdiff_plain;h=e093285525c7487daa705daf24e422331c46a3df Add FileInfoCollector class This class is to check the installation result file status in the smoke test Change-Id: I4853b4711f5d1b77bf158e791504df21dd9f6d76 Signed-off-by: Ilho Kim --- diff --git a/test/smoke_tests/common/smoke_utils.cc b/test/smoke_tests/common/smoke_utils.cc index ba7fe5d..eb6eb33 100644 --- a/test/smoke_tests/common/smoke_utils.cc +++ b/test/smoke_tests/common/smoke_utils.cc @@ -25,6 +25,7 @@ #include #include +#include #include #include @@ -185,19 +186,28 @@ bool TouchFile(const bf::path& path) { return true; } -void AddDataFiles(const std::string& pkgid, uid_t uid) { +void AddDataFiles(const std::string& pkgid, uid_t uid, + std::vector* result) { + std::vector files; + files.clear(); 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")); + files.emplace_back(pkg_path / "data" / "file1.txt"); + files.emplace_back(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")); + files.emplace_back(pkg_path / "data" / "file1.txt"); + files.emplace_back(pkg_path / "data" / "file2.txt"); } + + for (const auto& path : files) + ASSERT_TRUE(TouchFile(path)); + + if (result) + *result = std::move(files); } void RemoveAllRecoveryFiles(const std::string& prefix, uid_t uid) { @@ -593,6 +603,228 @@ bool CheckSharedDataNonExistance(const std::string& pkgid, return true; } +void FileInfoCollector::AddPath(const bf::path& path) { + root_paths_.emplace_back(path); +} + +bool FileInfoCollector::CollectFileInfoRecursive() { + for (const auto& path : root_paths_) { + if (!bf::exists(path) && !bf::is_symlink(path)) + continue; + + if (!GetFileListTraversal(path)) + return false; + } + + return true; +} + +bool FileInfoCollector::GetFileListTraversal(const bf::path& cur) { + bs::error_code error; + bf::file_status file_status = bf::symlink_status(cur, error); + if (error) { + LOG(ERROR) << "Fail to get symlink_status, " << error.message(); + return false; + } + + struct stat info; + if (lstat(cur.c_str(), &info) != 0) { + LOG(ERROR) << "Fail to lstat from [" << cur << "]"; + return false; + } + + std::string owner = ci::GetUsernameByUid(info.st_uid); + std::string group = ci::GetGroupNameByGid(info.st_gid); + + char* access_label = nullptr; + if (smack_lgetlabel(cur.c_str(), &access_label, SMACK_LABEL_ACCESS) < 0) { + LOG(ERROR) << "Fail to get access label from [" << cur << "]"; + return false; + } + + if (access_label == nullptr) { + LOG(ERROR) << "Fail to get access label from [" << cur << "]"; + return false; + } + + FileInfos_.emplace_back(cur, file_status.type(), + file_status.permissions(), owner, group, access_label); + + if (!bf::is_directory(cur) || bf::is_symlink(cur)) + return true; + + for (bf::directory_iterator file(cur); + file != bf::directory_iterator(); + ++file) { + if (!GetFileListTraversal(file->path())) { + FileInfos_.clear(); + return false; + } + } + + return true; +} + +bool FileInfoCollector::FileInfoToFile(const bf::path& path) const { + std::ofstream out(path.string()); + + for (const auto& info : FileInfos_) + out << FileInfoToString(info) << std::endl; + + out.close(); + + return true; +} + +bool FileInfoCollector::Init() { + bf::path skel_apps_rw = bf::path(kSkelDir); + bf::path root_path = + ci::GetRootAppPath(params_.is_readonly, params_.test_user.uid); + + AddPath(root_path / pkgid_); + AddPath(skel_apps_rw / pkgid_); + + if (params_.test_user.uid == tzplatform_getuid(TZ_SYS_GLOBALAPP_USER)) { + // per user dir + ci::UserList list = ci::GetUserList(); + for (auto l : list) { + bf::path apps_rw = std::get<2>(l) / "apps_rw"; + AddPath(apps_rw / pkgid_); + AddPath(apps_rw / kShared / pkgid_); + AddPath(apps_rw / kSharedTmp / pkgid_); + } + } else { + AddPath(root_path / kShared / pkgid_); + AddPath(root_path / kSharedTmp / pkgid_); + } + + if (!CollectFileInfoRecursive()) + return false; + + std::sort(FileInfos_.begin(), FileInfos_.end(), + [](const FileInfo& l, const FileInfo& r) -> bool { + return std::get<0>(l) < std::get<0>(r); + }); + + return true; +} + +bool FileInfoCollector::LoadFromFile(const bf::path& path) { + std::ifstream readFile; + readFile.open(path.c_str()); + + if (!readFile.is_open()) { + LOG(ERROR) << "Fail to read file : " << path; + return false; + } + + std::string line; + + while (std::getline(readFile, line)) { + std::istringstream iss(line); + bf::path p; + int file_permission; + int file_type; + std::string owner; + std::string group; + std::string access_label; + + iss >> p >> file_type >> std::oct >> file_permission + >> owner >> group >> access_label; + + FileInfos_.emplace_back(p, bf::file_type(file_type), + bf::perms(file_permission), owner, group, access_label); + } + + readFile.close(); + + return true; +} + +std::string FileInfoCollector::FileInfoToString( + const FileInfo& file_info) const { + bf::path p = std::get<0>(file_info); + bf::file_type file_type = std::get<1>(file_info); + std::string file_permission; + std::string owner = std::get<3>(file_info); + std::string group = std::get<4>(file_info); + std::string access_label = std::get<5>(file_info); + std::stringstream ss; + ss << std::oct << std::get<2>(file_info); + ss >> file_permission; + + std::string res; + res += p.string(); + res += " "; + res += std::to_string(file_type); + res += " "; + res += file_permission; + res += " "; + res += owner; + res += " "; + res += group; + res += " "; + res += access_label; + + return res; +} + +bool FileInfoCollector::IsEqual(const FileInfoCollector& that, + const std::vector* exception_list) const { + auto it_l = FileInfos_.begin(); + auto it_r = that.FileInfos_.begin(); + bool res = true; + + while (it_l != FileInfos_.end() && it_r != that.FileInfos_.end()) { + if (*it_l == *it_r) { + it_l++; + it_r++; + continue; + } + + bf::path path_l = std::get<0>(*it_l); + bf::path path_r = std::get<0>(*it_r); + if (exception_list && path_l == path_r && + std::find(exception_list->begin(), exception_list->end(), path_r) + != exception_list->end()) { + it_l++; + it_r++; + continue; + } + + res = false; + + if (path_l > path_r) { + LOG(ERROR) << "There is an unexpected file [" << path_r << "]"; + it_r++; + } else if (path_l < path_r) { + LOG(ERROR) << "There is not exists an expected file [" << path_l << "]"; + it_l++; + } else { + LOG(ERROR) << "There is a different status file. expected [" + << FileInfoToString(*it_l) << "], result [" + << FileInfoToString(*it_r) << "]"; + it_l++; + it_r++; + } + } + + while (it_l != FileInfos_.end()) { + LOG(ERROR) << "There is an unexpected file [" << std::get<0>(*it_l) << "]"; + it_l++; + res = false; + } + + while (it_r != that.FileInfos_.end()) { + LOG(ERROR) << "There is not exists an expected file [" + << std::get<0>(*it_r) << "]"; + it_r++; + res = false; + } + + return res; +} + void BackendInterface::TestRollbackAfterEachStep(int argc, const char* argv[], std::function validator) const { ci::Subprocess backend_helper = CreateSubprocess(); @@ -1575,4 +1807,23 @@ int GetAppInstalledTime(const char* appid, uid_t uid) { return installed_time; } +bool CompareFileInfo(const std::string& pkgid, const TestParameters& params, + const bf::path& file) { + FileInfoCollector result(pkgid, params); + if (!result.Init()) + return false; + + bf::path p = "/tmp"; + p /= file.filename(); + + if (!result.FileInfoToFile(p)) + return false; + + FileInfoCollector expected(pkgid, params); + if (!expected.LoadFromFile(file)) + return false; + + return result.IsEqual(expected); +} + } // namespace smoke_test diff --git a/test/smoke_tests/common/smoke_utils.h b/test/smoke_tests/common/smoke_utils.h index 94f7569..9e1369b 100644 --- a/test/smoke_tests/common/smoke_utils.h +++ b/test/smoke_tests/common/smoke_utils.h @@ -152,6 +152,31 @@ struct TestParameters { User test_user; }; +class FileInfoCollector { + public: + using FileInfo = std::tuple; + + FileInfoCollector(std::string pkgid, TestParameters params) : + pkgid_(pkgid), params_(params) {} + bool FileInfoToFile(const bf::path& path) const; + bool Init(); + bool IsEqual(const FileInfoCollector& collector, + const std::vector* exception_list = nullptr) const; + bool LoadFromFile(const bf::path& path); + + private: + void AddPath(const bf::path& path); + bool CollectFileInfoRecursive(); + bool GetFileListTraversal(const bf::path& cur); + std::string FileInfoToString(const FileInfo& file_info) const; + + std::string pkgid_; + TestParameters params_; + std::vector root_paths_; + std::vector FileInfos_; +}; + struct PackageAttributes { explicit PackageAttributes(common_installer::PkgQueryInterface pi) : is_global(pi.IsGlobalPackage()), is_readonly(pi.IsReadonlyPackage()), @@ -180,7 +205,8 @@ common_installer::RequestMode ParseRequestMode(int argc, char** argv); bool TouchFile(const boost::filesystem::path& path); -void AddDataFiles(const std::string& pkgid, uid_t uid); +void AddDataFiles(const std::string& pkgid, uid_t uid, + std::vector* result = nullptr); bool AddTestUser(User* test_user); @@ -402,6 +428,9 @@ class StepCrash : public common_installer::Step { CrashStepType type_; }; +bool CompareFileInfo(const std::string& pkgid, const TestParameters& params, + const bf::path& file); + } // namespace smoke_test #endif // TEST_SMOKE_TESTS_COMMON_SMOKE_UTILS_H_