1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <stddef.h>
6 #include <stdint.h>
7
8 #include <map>
9 #include <string>
10
11 #include "base/macros.h"
12 #include "base/stl_util.h"
13 #include "base/test/task_environment.h"
14 #include "components/services/filesystem/directory_test_helper.h"
15 #include "components/services/filesystem/public/mojom/directory.mojom.h"
16 #include "mojo/public/cpp/bindings/pending_receiver.h"
17 #include "mojo/public/cpp/bindings/remote.h"
18 #include "testing/gtest/include/gtest/gtest.h"
19
20 namespace filesystem {
21 namespace {
22
23 class DirectoryImplTest : public testing::Test {
24 public:
25 DirectoryImplTest() = default;
26
CreateTempDir()27 mojo::Remote<mojom::Directory> CreateTempDir() {
28 return test_helper_.CreateTempDir();
29 }
30
31 private:
32 base::test::TaskEnvironment task_environment_;
33 DirectoryTestHelper test_helper_;
34
35 DISALLOW_COPY_AND_ASSIGN(DirectoryImplTest);
36 };
37
38 constexpr char kData[] = "one two three";
39
TEST_F(DirectoryImplTest,Read)40 TEST_F(DirectoryImplTest, Read) {
41 mojo::Remote<mojom::Directory> directory = CreateTempDir();
42 base::File::Error error;
43
44 // Make some files.
45 const struct {
46 const char* name;
47 uint32_t open_flags;
48 } files_to_create[] = {
49 {"my_file1", mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate},
50 {"my_file2", mojom::kFlagWrite | mojom::kFlagCreate},
51 {"my_file3", mojom::kFlagAppend | mojom::kFlagCreate}};
52 for (size_t i = 0; i < base::size(files_to_create); i++) {
53 error = base::File::Error::FILE_ERROR_FAILED;
54 bool handled =
55 directory->OpenFile(files_to_create[i].name, mojo::NullReceiver(),
56 files_to_create[i].open_flags, &error);
57 ASSERT_TRUE(handled);
58 EXPECT_EQ(base::File::Error::FILE_OK, error);
59 }
60 // Make a directory.
61 error = base::File::Error::FILE_ERROR_FAILED;
62 bool handled = directory->OpenDirectory(
63 "my_dir", mojo::NullReceiver(),
64 mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
65 ASSERT_TRUE(handled);
66 EXPECT_EQ(base::File::Error::FILE_OK, error);
67
68 error = base::File::Error::FILE_ERROR_FAILED;
69 base::Optional<std::vector<mojom::DirectoryEntryPtr>> directory_contents;
70 handled = directory->Read(&error, &directory_contents);
71 ASSERT_TRUE(handled);
72 EXPECT_EQ(base::File::Error::FILE_OK, error);
73 ASSERT_TRUE(directory_contents.has_value());
74
75 // Expected contents of the directory.
76 std::map<std::string, mojom::FsFileType> expected_contents;
77 expected_contents["my_file1"] = mojom::FsFileType::REGULAR_FILE;
78 expected_contents["my_file2"] = mojom::FsFileType::REGULAR_FILE;
79 expected_contents["my_file3"] = mojom::FsFileType::REGULAR_FILE;
80 expected_contents["my_dir"] = mojom::FsFileType::DIRECTORY;
81 // Note: We don't expose ".." or ".".
82
83 EXPECT_EQ(expected_contents.size(), directory_contents->size());
84 for (size_t i = 0; i < directory_contents->size(); i++) {
85 auto& item = directory_contents.value()[i];
86 ASSERT_TRUE(item);
87 auto it = expected_contents.find(item->name.AsUTF8Unsafe());
88 ASSERT_TRUE(it != expected_contents.end());
89 EXPECT_EQ(it->second, item->type);
90 expected_contents.erase(it);
91 }
92 }
93
94 // TODO(vtl): Properly test OpenFile() and OpenDirectory() (including flags).
95
TEST_F(DirectoryImplTest,BasicRenameDelete)96 TEST_F(DirectoryImplTest, BasicRenameDelete) {
97 mojo::Remote<mojom::Directory> directory = CreateTempDir();
98 base::File::Error error;
99
100 // Create my_file.
101 error = base::File::Error::FILE_ERROR_FAILED;
102 bool handled =
103 directory->OpenFile("my_file", mojo::NullReceiver(),
104 mojom::kFlagWrite | mojom::kFlagCreate, &error);
105 ASSERT_TRUE(handled);
106 EXPECT_EQ(base::File::Error::FILE_OK, error);
107
108 // Opening my_file should succeed.
109 error = base::File::Error::FILE_ERROR_FAILED;
110 handled = directory->OpenFile("my_file", mojo::NullReceiver(),
111 mojom::kFlagRead | mojom::kFlagOpen, &error);
112 ASSERT_TRUE(handled);
113 EXPECT_EQ(base::File::Error::FILE_OK, error);
114
115 // Rename my_file to my_new_file.
116 handled = directory->Rename("my_file", "my_new_file", &error);
117 ASSERT_TRUE(handled);
118 EXPECT_EQ(base::File::Error::FILE_OK, error);
119
120 // Opening my_file should fail.
121
122 error = base::File::Error::FILE_ERROR_FAILED;
123 handled = directory->OpenFile("my_file", mojo::NullReceiver(),
124 mojom::kFlagRead | mojom::kFlagOpen, &error);
125 ASSERT_TRUE(handled);
126 EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_FOUND, error);
127
128 // Opening my_new_file should succeed.
129 error = base::File::Error::FILE_ERROR_FAILED;
130 handled = directory->OpenFile("my_new_file", mojo::NullReceiver(),
131 mojom::kFlagRead | mojom::kFlagOpen, &error);
132 ASSERT_TRUE(handled);
133 EXPECT_EQ(base::File::Error::FILE_OK, error);
134
135 // Delete my_new_file (no flags).
136 handled = directory->Delete("my_new_file", 0, &error);
137 ASSERT_TRUE(handled);
138 EXPECT_EQ(base::File::Error::FILE_OK, error);
139
140 // Opening my_new_file should fail.
141 error = base::File::Error::FILE_ERROR_FAILED;
142 handled = directory->OpenFile("my_new_file", mojo::NullReceiver(),
143 mojom::kFlagRead | mojom::kFlagOpen, &error);
144 ASSERT_TRUE(handled);
145 EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_FOUND, error);
146 }
147
TEST_F(DirectoryImplTest,CantOpenDirectoriesAsFiles)148 TEST_F(DirectoryImplTest, CantOpenDirectoriesAsFiles) {
149 mojo::Remote<mojom::Directory> directory = CreateTempDir();
150 base::File::Error error;
151
152 {
153 // Create a directory called 'my_file'
154 mojo::Remote<mojom::Directory> my_file_directory;
155 error = base::File::Error::FILE_ERROR_FAILED;
156 bool handled = directory->OpenDirectory(
157 "my_file", my_file_directory.BindNewPipeAndPassReceiver(),
158 mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
159 ASSERT_TRUE(handled);
160 EXPECT_EQ(base::File::Error::FILE_OK, error);
161 }
162
163 {
164 // Attempt to open that directory as a file. This must fail!
165 mojo::Remote<mojom::File> file;
166 error = base::File::Error::FILE_ERROR_FAILED;
167 bool handled =
168 directory->OpenFile("my_file", file.BindNewPipeAndPassReceiver(),
169 mojom::kFlagRead | mojom::kFlagOpen, &error);
170 ASSERT_TRUE(handled);
171 EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_A_FILE, error);
172 }
173 }
174
TEST_F(DirectoryImplTest,Clone)175 TEST_F(DirectoryImplTest, Clone) {
176 mojo::Remote<mojom::Directory> clone_one;
177 mojo::Remote<mojom::Directory> clone_two;
178 base::File::Error error;
179
180 {
181 mojo::Remote<mojom::Directory> directory = CreateTempDir();
182 directory->Clone(clone_one.BindNewPipeAndPassReceiver());
183 directory->Clone(clone_two.BindNewPipeAndPassReceiver());
184
185 // Original temporary directory goes out of scope here; shouldn't be
186 // deleted since it has clones.
187 }
188
189 std::vector<uint8_t> data(kData, kData + strlen(kData));
190 {
191 bool handled = clone_one->WriteFile("data", data, &error);
192 ASSERT_TRUE(handled);
193 EXPECT_EQ(base::File::Error::FILE_OK, error);
194 }
195
196 {
197 std::vector<uint8_t> file_contents;
198 bool handled = clone_two->ReadEntireFile("data", &error, &file_contents);
199 ASSERT_TRUE(handled);
200 EXPECT_EQ(base::File::Error::FILE_OK, error);
201
202 EXPECT_EQ(data, file_contents);
203 }
204 }
205
TEST_F(DirectoryImplTest,WriteFileReadFile)206 TEST_F(DirectoryImplTest, WriteFileReadFile) {
207 mojo::Remote<mojom::Directory> directory = CreateTempDir();
208 base::File::Error error;
209
210 std::vector<uint8_t> data(kData, kData + strlen(kData));
211 {
212 bool handled = directory->WriteFile("data", data, &error);
213 ASSERT_TRUE(handled);
214 EXPECT_EQ(base::File::Error::FILE_OK, error);
215 }
216
217 {
218 std::vector<uint8_t> file_contents;
219 bool handled = directory->ReadEntireFile("data", &error, &file_contents);
220 ASSERT_TRUE(handled);
221 EXPECT_EQ(base::File::Error::FILE_OK, error);
222
223 EXPECT_EQ(data, file_contents);
224 }
225 }
226
TEST_F(DirectoryImplTest,ReadEmptyFileIsNotFoundError)227 TEST_F(DirectoryImplTest, ReadEmptyFileIsNotFoundError) {
228 mojo::Remote<mojom::Directory> directory = CreateTempDir();
229 base::File::Error error;
230
231 {
232 std::vector<uint8_t> file_contents;
233 bool handled =
234 directory->ReadEntireFile("doesnt_exist", &error, &file_contents);
235 ASSERT_TRUE(handled);
236 EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_FOUND, error);
237 }
238 }
239
TEST_F(DirectoryImplTest,CantReadEntireFileOnADirectory)240 TEST_F(DirectoryImplTest, CantReadEntireFileOnADirectory) {
241 mojo::Remote<mojom::Directory> directory = CreateTempDir();
242 base::File::Error error;
243
244 // Create a directory
245 {
246 mojo::Remote<mojom::Directory> my_file_directory;
247 error = base::File::Error::FILE_ERROR_FAILED;
248 bool handled = directory->OpenDirectory(
249 "my_dir", my_file_directory.BindNewPipeAndPassReceiver(),
250 mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
251 ASSERT_TRUE(handled);
252 EXPECT_EQ(base::File::Error::FILE_OK, error);
253 }
254
255 // Try to read it as a file
256 {
257 std::vector<uint8_t> file_contents;
258 bool handled = directory->ReadEntireFile("my_dir", &error, &file_contents);
259 ASSERT_TRUE(handled);
260 EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_A_FILE, error);
261 }
262 }
263
TEST_F(DirectoryImplTest,CantWriteFileOnADirectory)264 TEST_F(DirectoryImplTest, CantWriteFileOnADirectory) {
265 mojo::Remote<mojom::Directory> directory = CreateTempDir();
266 base::File::Error error;
267
268 // Create a directory
269 {
270 mojo::Remote<mojom::Directory> my_file_directory;
271 error = base::File::Error::FILE_ERROR_FAILED;
272 bool handled = directory->OpenDirectory(
273 "my_dir", my_file_directory.BindNewPipeAndPassReceiver(),
274 mojom::kFlagRead | mojom::kFlagWrite | mojom::kFlagCreate, &error);
275 ASSERT_TRUE(handled);
276 EXPECT_EQ(base::File::Error::FILE_OK, error);
277 }
278
279 {
280 std::vector<uint8_t> data(kData, kData + strlen(kData));
281 bool handled = directory->WriteFile("my_dir", data, &error);
282 ASSERT_TRUE(handled);
283 EXPECT_EQ(base::File::Error::FILE_ERROR_NOT_A_FILE, error);
284 }
285 }
286
TEST_F(DirectoryImplTest,Flush)287 TEST_F(DirectoryImplTest, Flush) {
288 mojo::Remote<mojom::Directory> directory = CreateTempDir();
289 base::File::Error error;
290
291 {
292 bool handled = directory->Flush(&error);
293 ASSERT_TRUE(handled);
294 EXPECT_EQ(base::File::Error::FILE_OK, error);
295 }
296 }
297
298 } // namespace
299 } // namespace filesystem
300