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 <sys/stat.h>
20 #include <memory>
21 #include <string>
22
23 #include "perfetto/base/logging.h"
24 #include "protos/perfetto/trace/filesystem/inode_file_map.pbzero.h"
25 #include "src/base/test/test_task_runner.h"
26 #include "src/base/test/utils.h"
27 #include "test/gtest_and_gmock.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 &,InodeFileMap_Entry_Type)> callback,std::function<void ()> done_callback)38 TestDelegate(std::function<bool(BlockDeviceID,
39 Inode,
40 const std::string&,
41 InodeFileMap_Entry_Type)> callback,
42 std::function<void()> done_callback)
43 : callback_(std::move(callback)),
44 done_callback_(std::move(done_callback)) {}
OnInodeFound(BlockDeviceID block_device_id,Inode inode,const std::string & path,InodeFileMap_Entry_Type type)45 bool OnInodeFound(BlockDeviceID block_device_id,
46 Inode inode,
47 const std::string& path,
48 InodeFileMap_Entry_Type type) override {
49 return callback_(block_device_id, inode, path, type);
50 }
51
OnInodeScanDone()52 void OnInodeScanDone() { return done_callback_(); }
53
54 private:
55 std::function<
56 bool(BlockDeviceID, Inode, const std::string&, InodeFileMap_Entry_Type)>
57 callback_;
58 std::function<void()> done_callback_;
59 };
60
61 struct FileEntry {
FileEntryperfetto::__anon87225d370111::FileEntry62 FileEntry(BlockDeviceID block_device_id,
63 Inode inode,
64 std::string path,
65 InodeFileMap_Entry_Type type)
66 : block_device_id_(block_device_id),
67 inode_(inode),
68 path_(std::move(path)),
69 type_(type) {}
70
operator ==perfetto::__anon87225d370111::FileEntry71 bool operator==(const FileEntry& other) const {
72 return block_device_id_ == other.block_device_id_ &&
73 inode_ == other.inode_ && path_ == other.path_ &&
74 type_ == other.type_;
75 }
76
77 BlockDeviceID block_device_id_;
78 Inode inode_;
79 std::string path_;
80 InodeFileMap_Entry_Type type_;
81 };
82
CheckStat(const std::string & path)83 struct stat CheckStat(const std::string& path) {
84 struct stat buf;
85 PERFETTO_CHECK(lstat(path.c_str(), &buf) != -1);
86 return buf;
87 }
88
StatFileEntry(const std::string & path,InodeFileMap_Entry_Type type)89 FileEntry StatFileEntry(const std::string& path, InodeFileMap_Entry_Type type) {
90 struct stat buf = CheckStat(path);
91 return FileEntry(buf.st_dev, buf.st_ino, path, type);
92 }
93
TEST(FileScannerTest,TestSynchronousStop)94 TEST(FileScannerTest, TestSynchronousStop) {
95 uint64_t seen = 0;
96 bool done = false;
97 TestDelegate delegate(
98 [&seen](BlockDeviceID, Inode, const std::string&,
99 InodeFileMap_Entry_Type) {
100 ++seen;
101 return false;
102 },
103 [&done] { done = true; });
104
105 FileScanner fs(
106 {base::GetTestDataPath("src/traced/probes/filesystem/testdata")},
107 &delegate);
108 fs.Scan();
109
110 EXPECT_EQ(seen, 1u);
111 EXPECT_TRUE(done);
112 }
113
TEST(FileScannerTest,TestAsynchronousStop)114 TEST(FileScannerTest, TestAsynchronousStop) {
115 uint64_t seen = 0;
116 base::TestTaskRunner task_runner;
117 TestDelegate delegate(
118 [&seen](BlockDeviceID, Inode, const std::string&,
119 InodeFileMap_Entry_Type) {
120 ++seen;
121 return false;
122 },
123 task_runner.CreateCheckpoint("done"));
124
125 FileScanner fs(
126 {base::GetTestDataPath("src/traced/probes/filesystem/testdata")},
127 &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, InodeFileMap_Entry_Type type) {
140 file_entries.emplace_back(block_device_id, inode, path, type);
141 return true;
142 },
143 [] {});
144
145 FileScanner fs(
146 {base::GetTestDataPath("src/traced/probes/filesystem/testdata")},
147 &delegate);
148 fs.Scan();
149
150 EXPECT_THAT(
151 file_entries,
152 UnorderedElementsAre(
153 Eq(StatFileEntry(
154 base::GetTestDataPath(
155 "src/traced/probes/filesystem/testdata/dir1/file1"),
156 protos::pbzero::InodeFileMap_Entry_Type_FILE)),
157 Eq(StatFileEntry(base::GetTestDataPath(
158 "src/traced/probes/filesystem/testdata/file2"),
159 protos::pbzero::InodeFileMap_Entry_Type_FILE)),
160 Eq(StatFileEntry(
161 base::GetTestDataPath(
162 "src/traced/probes/filesystem/testdata/dir1"),
163 protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY))));
164 }
165
TEST(FileScannerTest,TestAsynchronousFindFiles)166 TEST(FileScannerTest, TestAsynchronousFindFiles) {
167 base::TestTaskRunner task_runner;
168 std::vector<FileEntry> file_entries;
169 TestDelegate delegate(
170 [&file_entries](BlockDeviceID block_device_id, Inode inode,
171 const std::string& path, InodeFileMap_Entry_Type type) {
172 file_entries.emplace_back(block_device_id, inode, path, type);
173 return true;
174 },
175 task_runner.CreateCheckpoint("done"));
176
177 FileScanner fs(
178 {base::GetTestDataPath("src/traced/probes/filesystem/testdata")},
179 &delegate, 1, 1);
180 fs.Scan(&task_runner);
181
182 task_runner.RunUntilCheckpoint("done");
183
184 EXPECT_THAT(
185 file_entries,
186 UnorderedElementsAre(
187 Eq(StatFileEntry(
188 base::GetTestDataPath(
189 "src/traced/probes/filesystem/testdata/dir1/file1"),
190 protos::pbzero::InodeFileMap_Entry_Type_FILE)),
191 Eq(StatFileEntry(base::GetTestDataPath(
192 "src/traced/probes/filesystem/testdata/file2"),
193 protos::pbzero::InodeFileMap_Entry_Type_FILE)),
194 Eq(StatFileEntry(
195 base::GetTestDataPath(
196 "src/traced/probes/filesystem/testdata/dir1"),
197 protos::pbzero::InodeFileMap_Entry_Type_DIRECTORY))));
198 }
199
200 } // namespace
201 } // namespace perfetto
202