Add FileInfoCollector class 80/246680/11
authorIlho Kim <ilho159.kim@samsung.com>
Tue, 3 Nov 2020 02:18:12 +0000 (11:18 +0900)
committerilho kim <ilho159.kim@samsung.com>
Fri, 13 Jan 2023 07:31:14 +0000 (07:31 +0000)
This class is to check the installation result file status in the smoke test

Change-Id: I4853b4711f5d1b77bf158e791504df21dd9f6d76
Signed-off-by: Ilho Kim <ilho159.kim@samsung.com>
test/smoke_tests/common/smoke_utils.cc
test/smoke_tests/common/smoke_utils.h

index ba7fe5d..eb6eb33 100644 (file)
@@ -25,6 +25,7 @@
 
 #include <list>
 #include <memory>
+#include <sstream>
 #include <string>
 #include <vector>
 
@@ -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<bf::path>* result) {
+  std::vector<bf::path> 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<bf::path>* 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<bool()> 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
index 94f7569..9e1369b 100644 (file)
@@ -152,6 +152,31 @@ struct TestParameters {
   User test_user;
 };
 
+class FileInfoCollector {
+ public:
+  using FileInfo = std::tuple<bf::path, bf::file_type, bf::perms,
+      std::string, std::string, std::string>;
+
+  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<bf::path>* 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<bf::path> root_paths_;
+  std::vector<FileInfo> 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<bf::path>* 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_