#include <list>
#include <memory>
+#include <sstream>
#include <string>
#include <vector>
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) {
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();
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
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()),
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);
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_