Add unittests for file_scanner. Fix minor bug. Bug: 73625480 Change-Id: Ice90c62ee6dd9742add9627cf7d8c9b9f2e6d741
diff --git a/Android.bp b/Android.bp index ad89c9d..46ce586 100644 --- a/Android.bp +++ b/Android.bp
@@ -3374,6 +3374,7 @@ "src/protozero/test/fake_scattered_buffer.cc", "src/protozero/test/protozero_conformance_unittest.cc", "src/traced/probes/filesystem/file_scanner.cc", + "src/traced/probes/filesystem/file_scanner_unittest.cc", "src/traced/probes/filesystem/fs_mount.cc", "src/traced/probes/filesystem/fs_mount_unittest.cc", "src/traced/probes/filesystem/inode_file_data_source.cc",
diff --git a/src/traced/probes/filesystem/BUILD.gn b/src/traced/probes/filesystem/BUILD.gn index 6beb973..78d58be 100644 --- a/src/traced/probes/filesystem/BUILD.gn +++ b/src/traced/probes/filesystem/BUILD.gn
@@ -43,8 +43,10 @@ ":filesystem", "../../../../gn:default_deps", "../../../../gn:gtest_deps", + "../../../../src/base:test_support", ] sources = [ + "file_scanner_unittest.cc", "fs_mount_unittest.cc", "lru_inode_cache_unittest.cc", "prefix_finder_unittest.cc",
diff --git a/src/traced/probes/filesystem/file_scanner.cc b/src/traced/probes/filesystem/file_scanner.cc index 8369e50..83dc767 100644 --- a/src/traced/probes/filesystem/file_scanner.cc +++ b/src/traced/probes/filesystem/file_scanner.cc
@@ -58,6 +58,7 @@ void FileScanner::Scan() { while (!Done()) Step(); + delegate_->OnInodeScanDone(); } void FileScanner::Scan(base::TaskRunner* task_runner) { PERFETTO_DCHECK(scan_interval_ms_ && scan_steps_);
diff --git a/src/traced/probes/filesystem/file_scanner_unittest.cc b/src/traced/probes/filesystem/file_scanner_unittest.cc new file mode 100644 index 0000000..44e127d --- /dev/null +++ b/src/traced/probes/filesystem/file_scanner_unittest.cc
@@ -0,0 +1,193 @@ +/* + * 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 "gmock/gmock.h" +#include "gtest/gtest.h" + +#include <sys/stat.h> +#include <memory> +#include <string> + +#include "perfetto/base/logging.h" +#include "src/base/test/test_task_runner.h" + +namespace perfetto { +namespace { + +using ::testing::Eq; +using ::testing::Contains; +using ::testing::UnorderedElementsAre; + +class TestDelegate : public FileScanner::Delegate { + public: + TestDelegate( + std::function<bool(BlockDeviceID, + Inode, + const std::string&, + protos::pbzero::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, + protos::pbzero::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&, + protos::pbzero::InodeFileMap_Entry_Type)> + callback_; + std::function<void()> done_callback_; +}; + +struct FileEntry { + FileEntry(BlockDeviceID block_device_id, + Inode inode, + std::string path, + protos::pbzero::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_; + protos::pbzero::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, + protos::pbzero::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 block_device_id, Inode inode, + const std::string& path, + protos::pbzero::InodeFileMap_Entry_Type type) { + ++seen; + return false; + }, + [&done] { done = true; }); + + FileScanner fs({"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 block_device_id, Inode inode, + const std::string& path, + protos::pbzero::InodeFileMap_Entry_Type type) { + ++seen; + return false; + }, + task_runner.CreateCheckpoint("done")); + + FileScanner fs({"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, + protos::pbzero::InodeFileMap_Entry_Type type) { + file_entries.emplace_back(block_device_id, inode, path, type); + return true; + }, + [] {}); + + FileScanner fs({"src/traced/probes/filesystem/testdata"}, &delegate); + fs.Scan(); + + EXPECT_THAT( + file_entries, + UnorderedElementsAre( + Eq(StatFileEntry("src/traced/probes/filesystem/testdata/dir1/file1", + protos::pbzero::InodeFileMap_Entry_Type_FILE)), + Eq(StatFileEntry("src/traced/probes/filesystem/testdata/file2", + protos::pbzero::InodeFileMap_Entry_Type_FILE)), + Eq(StatFileEntry( + "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, + protos::pbzero::InodeFileMap_Entry_Type type) { + file_entries.emplace_back(block_device_id, inode, path, type); + return true; + }, + task_runner.CreateCheckpoint("done")); + + FileScanner fs({"src/traced/probes/filesystem/testdata"}, &delegate, 1, 1); + fs.Scan(&task_runner); + + task_runner.RunUntilCheckpoint("done"); + + EXPECT_THAT( + file_entries, + UnorderedElementsAre( + Eq(StatFileEntry("src/traced/probes/filesystem/testdata/dir1/file1", + protos::pbzero::InodeFileMap_Entry_Type_FILE)), + Eq(StatFileEntry("src/traced/probes/filesystem/testdata/file2", + protos::pbzero::InodeFileMap_Entry_Type_FILE)), + Eq(StatFileEntry( + "src/traced/probes/filesystem/testdata/dir1", + protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY)))); +} + +} // namespace +} // namespace perfetto
diff --git a/src/traced/probes/filesystem/testdata/dir1/file1 b/src/traced/probes/filesystem/testdata/dir1/file1 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/traced/probes/filesystem/testdata/dir1/file1
diff --git a/src/traced/probes/filesystem/testdata/file2 b/src/traced/probes/filesystem/testdata/file2 new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/traced/probes/filesystem/testdata/file2
diff --git a/tools/test_data.txt b/tools/test_data.txt index c0692b0..36c9cdb 100644 --- a/tools/test_data.txt +++ b/tools/test_data.txt
@@ -1,3 +1,4 @@ # List of test deps that should be pushed on the device. Paths are relative # to the root. src/ftrace_reader/test/data/ +src/traced/probes/filesystem/testdata/