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/