| /* |
| * Copyright (C) 2018 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "src/traced/probes/filesystem/file_scanner.h" |
| |
| #include <sys/stat.h> |
| #include <memory> |
| #include <string> |
| |
| #include "perfetto/base/logging.h" |
| #include "protos/perfetto/trace/filesystem/inode_file_map.pbzero.h" |
| #include "src/base/test/test_task_runner.h" |
| #include "src/base/test/utils.h" |
| #include "test/gtest_and_gmock.h" |
| |
| namespace perfetto { |
| namespace { |
| |
| using ::testing::Contains; |
| using ::testing::Eq; |
| using ::testing::UnorderedElementsAre; |
| |
| class TestDelegate : public FileScanner::Delegate { |
| public: |
| TestDelegate(std::function<bool(BlockDeviceID, |
| Inode, |
| const std::string&, |
| InodeFileMap_Entry_Type)> callback, |
| std::function<void()> done_callback) |
| : callback_(std::move(callback)), |
| done_callback_(std::move(done_callback)) {} |
| bool OnInodeFound(BlockDeviceID block_device_id, |
| Inode inode, |
| const std::string& path, |
| InodeFileMap_Entry_Type type) override { |
| return callback_(block_device_id, inode, path, type); |
| } |
| |
| void OnInodeScanDone() { return done_callback_(); } |
| |
| private: |
| std::function< |
| bool(BlockDeviceID, Inode, const std::string&, InodeFileMap_Entry_Type)> |
| callback_; |
| std::function<void()> done_callback_; |
| }; |
| |
| struct FileEntry { |
| FileEntry(BlockDeviceID block_device_id, |
| Inode inode, |
| std::string path, |
| InodeFileMap_Entry_Type type) |
| : block_device_id_(block_device_id), |
| inode_(inode), |
| path_(std::move(path)), |
| type_(type) {} |
| |
| bool operator==(const FileEntry& other) const { |
| return block_device_id_ == other.block_device_id_ && |
| inode_ == other.inode_ && path_ == other.path_ && |
| type_ == other.type_; |
| } |
| |
| BlockDeviceID block_device_id_; |
| Inode inode_; |
| std::string path_; |
| InodeFileMap_Entry_Type type_; |
| }; |
| |
| struct stat CheckStat(const std::string& path) { |
| struct stat buf; |
| PERFETTO_CHECK(lstat(path.c_str(), &buf) != -1); |
| return buf; |
| } |
| |
| FileEntry StatFileEntry(const std::string& path, InodeFileMap_Entry_Type type) { |
| struct stat buf = CheckStat(path); |
| return FileEntry(buf.st_dev, buf.st_ino, path, type); |
| } |
| |
| TEST(FileScannerTest, TestSynchronousStop) { |
| uint64_t seen = 0; |
| bool done = false; |
| TestDelegate delegate( |
| [&seen](BlockDeviceID, Inode, const std::string&, |
| InodeFileMap_Entry_Type) { |
| ++seen; |
| return false; |
| }, |
| [&done] { done = true; }); |
| |
| FileScanner fs( |
| {base::GetTestDataPath("src/traced/probes/filesystem/testdata")}, |
| &delegate); |
| fs.Scan(); |
| |
| EXPECT_EQ(seen, 1u); |
| EXPECT_TRUE(done); |
| } |
| |
| TEST(FileScannerTest, TestAsynchronousStop) { |
| uint64_t seen = 0; |
| base::TestTaskRunner task_runner; |
| TestDelegate delegate( |
| [&seen](BlockDeviceID, Inode, const std::string&, |
| InodeFileMap_Entry_Type) { |
| ++seen; |
| return false; |
| }, |
| task_runner.CreateCheckpoint("done")); |
| |
| FileScanner fs( |
| {base::GetTestDataPath("src/traced/probes/filesystem/testdata")}, |
| &delegate, 1, 1); |
| fs.Scan(&task_runner); |
| |
| task_runner.RunUntilCheckpoint("done"); |
| |
| EXPECT_EQ(seen, 1u); |
| } |
| |
| TEST(FileScannerTest, TestSynchronousFindFiles) { |
| std::vector<FileEntry> file_entries; |
| TestDelegate delegate( |
| [&file_entries](BlockDeviceID block_device_id, Inode inode, |
| const std::string& path, InodeFileMap_Entry_Type type) { |
| file_entries.emplace_back(block_device_id, inode, path, type); |
| return true; |
| }, |
| [] {}); |
| |
| FileScanner fs( |
| {base::GetTestDataPath("src/traced/probes/filesystem/testdata")}, |
| &delegate); |
| fs.Scan(); |
| |
| EXPECT_THAT( |
| file_entries, |
| UnorderedElementsAre( |
| Eq(StatFileEntry( |
| base::GetTestDataPath( |
| "src/traced/probes/filesystem/testdata/dir1/file1"), |
| protos::pbzero::InodeFileMap::Entry::Type::FILE)), |
| Eq(StatFileEntry(base::GetTestDataPath( |
| "src/traced/probes/filesystem/testdata/file2"), |
| protos::pbzero::InodeFileMap::Entry::Type::FILE)), |
| Eq(StatFileEntry( |
| base::GetTestDataPath( |
| "src/traced/probes/filesystem/testdata/dir1"), |
| protos::pbzero::InodeFileMap::Entry::Type::DIRECTORY)))); |
| } |
| |
| TEST(FileScannerTest, TestAsynchronousFindFiles) { |
| base::TestTaskRunner task_runner; |
| std::vector<FileEntry> file_entries; |
| TestDelegate delegate( |
| [&file_entries](BlockDeviceID block_device_id, Inode inode, |
| const std::string& path, InodeFileMap_Entry_Type type) { |
| file_entries.emplace_back(block_device_id, inode, path, type); |
| return true; |
| }, |
| task_runner.CreateCheckpoint("done")); |
| |
| FileScanner fs( |
| {base::GetTestDataPath("src/traced/probes/filesystem/testdata")}, |
| &delegate, 1, 1); |
| fs.Scan(&task_runner); |
| |
| task_runner.RunUntilCheckpoint("done"); |
| |
| EXPECT_THAT( |
| file_entries, |
| UnorderedElementsAre( |
| Eq(StatFileEntry( |
| base::GetTestDataPath( |
| "src/traced/probes/filesystem/testdata/dir1/file1"), |
| protos::pbzero::InodeFileMap::Entry::Type::FILE)), |
| Eq(StatFileEntry(base::GetTestDataPath( |
| "src/traced/probes/filesystem/testdata/file2"), |
| protos::pbzero::InodeFileMap::Entry::Type::FILE)), |
| Eq(StatFileEntry( |
| base::GetTestDataPath( |
| "src/traced/probes/filesystem/testdata/dir1"), |
| protos::pbzero::InodeFileMap::Entry::Type::DIRECTORY)))); |
| } |
| |
| } // namespace |
| } // namespace perfetto |