1 /*
2 * Copyright (C) 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "src/traced/probes/filesystem/file_scanner.h"
18
19 #include "gmock/gmock.h"
20 #include "gtest/gtest.h"
21
22 #include <sys/stat.h>
23 #include <memory>
24 #include <string>
25
26 #include "perfetto/base/logging.h"
27 #include "src/base/test/test_task_runner.h"
28
29 namespace perfetto {
30 namespace {
31
32 using ::testing::Eq;
33 using ::testing::Contains;
34 using ::testing::UnorderedElementsAre;
35
36 class TestDelegate : public FileScanner::Delegate {
37 public:
TestDelegate(std::function<bool (BlockDeviceID,Inode,const std::string &,protos::pbzero::InodeFileMap_Entry_Type)> callback,std::function<void ()> done_callback)38 TestDelegate(
39 std::function<bool(BlockDeviceID,
40 Inode,
41 const std::string&,
42 protos::pbzero::InodeFileMap_Entry_Type)> callback,
43 std::function<void()> done_callback)
44 : callback_(std::move(callback)),
45 done_callback_(std::move(done_callback)) {}
OnInodeFound(BlockDeviceID block_device_id,Inode inode,const std::string & path,protos::pbzero::InodeFileMap_Entry_Type type)46 bool OnInodeFound(BlockDeviceID block_device_id,
47 Inode inode,
48 const std::string& path,
49 protos::pbzero::InodeFileMap_Entry_Type type) override {
50 return callback_(block_device_id, inode, path, type);
51 }
52
OnInodeScanDone()53 void OnInodeScanDone() { return done_callback_(); }
54
55 private:
56 std::function<bool(BlockDeviceID,
57 Inode,
58 const std::string&,
59 protos::pbzero::InodeFileMap_Entry_Type)>
60 callback_;
61 std::function<void()> done_callback_;
62 };
63
64 struct FileEntry {
FileEntryperfetto::__anona0bb470c0111::FileEntry65 FileEntry(BlockDeviceID block_device_id,
66 Inode inode,
67 std::string path,
68 protos::pbzero::InodeFileMap_Entry_Type type)
69 : block_device_id_(block_device_id),
70 inode_(inode),
71 path_(std::move(path)),
72 type_(type) {}
73
operator ==perfetto::__anona0bb470c0111::FileEntry74 bool operator==(const FileEntry& other) const {
75 return block_device_id_ == other.block_device_id_ &&
76 inode_ == other.inode_ && path_ == other.path_ &&
77 type_ == other.type_;
78 }
79
80 BlockDeviceID block_device_id_;
81 Inode inode_;
82 std::string path_;
83 protos::pbzero::InodeFileMap_Entry_Type type_;
84 };
85
CheckStat(const std::string & path)86 struct stat CheckStat(const std::string& path) {
87 struct stat buf;
88 PERFETTO_CHECK(lstat(path.c_str(), &buf) != -1);
89 return buf;
90 }
91
StatFileEntry(const std::string & path,protos::pbzero::InodeFileMap_Entry_Type type)92 FileEntry StatFileEntry(const std::string& path,
93 protos::pbzero::InodeFileMap_Entry_Type type) {
94 struct stat buf = CheckStat(path);
95 return FileEntry(buf.st_dev, buf.st_ino, path, type);
96 }
97
TEST(FileScannerTest,TestSynchronousStop)98 TEST(FileScannerTest, TestSynchronousStop) {
99 uint64_t seen = 0;
100 bool done = false;
101 TestDelegate delegate(
102 [&seen](BlockDeviceID, Inode, const std::string&,
103 protos::pbzero::InodeFileMap_Entry_Type) {
104 ++seen;
105 return false;
106 },
107 [&done] { done = true; });
108
109 FileScanner fs({"src/traced/probes/filesystem/testdata"}, &delegate);
110 fs.Scan();
111
112 EXPECT_EQ(seen, 1u);
113 EXPECT_TRUE(done);
114 }
115
TEST(FileScannerTest,TestAsynchronousStop)116 TEST(FileScannerTest, TestAsynchronousStop) {
117 uint64_t seen = 0;
118 base::TestTaskRunner task_runner;
119 TestDelegate delegate(
120 [&seen](BlockDeviceID, Inode, const std::string&,
121 protos::pbzero::InodeFileMap_Entry_Type) {
122 ++seen;
123 return false;
124 },
125 task_runner.CreateCheckpoint("done"));
126
127 FileScanner fs({"src/traced/probes/filesystem/testdata"}, &delegate, 1, 1);
128 fs.Scan(&task_runner);
129
130 task_runner.RunUntilCheckpoint("done");
131
132 EXPECT_EQ(seen, 1u);
133 }
134
TEST(FileScannerTest,TestSynchronousFindFiles)135 TEST(FileScannerTest, TestSynchronousFindFiles) {
136 std::vector<FileEntry> file_entries;
137 TestDelegate delegate(
138 [&file_entries](BlockDeviceID block_device_id, Inode inode,
139 const std::string& path,
140 protos::pbzero::InodeFileMap_Entry_Type type) {
141 file_entries.emplace_back(block_device_id, inode, path, type);
142 return true;
143 },
144 [] {});
145
146 FileScanner fs({"src/traced/probes/filesystem/testdata"}, &delegate);
147 fs.Scan();
148
149 EXPECT_THAT(
150 file_entries,
151 UnorderedElementsAre(
152 Eq(StatFileEntry("src/traced/probes/filesystem/testdata/dir1/file1",
153 protos::pbzero::InodeFileMap_Entry_Type_FILE)),
154 Eq(StatFileEntry("src/traced/probes/filesystem/testdata/file2",
155 protos::pbzero::InodeFileMap_Entry_Type_FILE)),
156 Eq(StatFileEntry(
157 "src/traced/probes/filesystem/testdata/dir1",
158 protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY))));
159 }
160
TEST(FileScannerTest,TestAsynchronousFindFiles)161 TEST(FileScannerTest, TestAsynchronousFindFiles) {
162 base::TestTaskRunner task_runner;
163 std::vector<FileEntry> file_entries;
164 TestDelegate delegate(
165 [&file_entries](BlockDeviceID block_device_id, Inode inode,
166 const std::string& path,
167 protos::pbzero::InodeFileMap_Entry_Type type) {
168 file_entries.emplace_back(block_device_id, inode, path, type);
169 return true;
170 },
171 task_runner.CreateCheckpoint("done"));
172
173 FileScanner fs({"src/traced/probes/filesystem/testdata"}, &delegate, 1, 1);
174 fs.Scan(&task_runner);
175
176 task_runner.RunUntilCheckpoint("done");
177
178 EXPECT_THAT(
179 file_entries,
180 UnorderedElementsAre(
181 Eq(StatFileEntry("src/traced/probes/filesystem/testdata/dir1/file1",
182 protos::pbzero::InodeFileMap_Entry_Type_FILE)),
183 Eq(StatFileEntry("src/traced/probes/filesystem/testdata/file2",
184 protos::pbzero::InodeFileMap_Entry_Type_FILE)),
185 Eq(StatFileEntry(
186 "src/traced/probes/filesystem/testdata/dir1",
187 protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY))));
188 }
189
190 } // namespace
191 } // namespace perfetto
192