1 // Copyright (c) 2011 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 "ppapi/tests/test_file_ref.h"
6
7 #include <stdio.h>
8
9 #include <sstream>
10 #include <vector>
11
12 #include "ppapi/c/pp_errors.h"
13 #include "ppapi/c/ppb_file_io.h"
14 #include "ppapi/c/private/ppb_testing_private.h"
15 #include "ppapi/cpp/directory_entry.h"
16 #include "ppapi/cpp/file_io.h"
17 #include "ppapi/cpp/file_ref.h"
18 #include "ppapi/cpp/file_system.h"
19 #include "ppapi/cpp/instance.h"
20 #include "ppapi/cpp/module.h"
21 #include "ppapi/cpp/url_loader.h"
22 #include "ppapi/cpp/url_request_info.h"
23 #include "ppapi/cpp/url_response_info.h"
24 #include "ppapi/tests/test_utils.h"
25 #include "ppapi/tests/testing_instance.h"
26
27 REGISTER_TEST_CASE(FileRef);
28
29 namespace {
30
31 const char* kPersFileName = "persistent";
32 const char* kTempFileName = "temporary";
33 const char* kParentPath = "/foo/bar";
34 const char* kPersFilePath = "/foo/bar/persistent";
35 const char* kTempFilePath = "/foo/bar/temporary";
36 const char* kTerribleName = "!@#$%^&*()-_=+{}[] ;:'\"|`~\t\n\r\b?";
37
38 typedef std::vector<pp::DirectoryEntry> DirEntries;
39
ReportMismatch(const std::string & method_name,const std::string & returned_result,const std::string & expected_result)40 std::string ReportMismatch(const std::string& method_name,
41 const std::string& returned_result,
42 const std::string& expected_result) {
43 return method_name + " returned '" + returned_result + "'; '" +
44 expected_result + "' expected.";
45 }
46
47 } // namespace
48
Init()49 bool TestFileRef::Init() {
50 return CheckTestingInterface() && EnsureRunningOverHTTP();
51 }
52
MakeExternalFileRef(pp::FileRef * file_ref_ext)53 std::string TestFileRef::MakeExternalFileRef(pp::FileRef* file_ref_ext) {
54 pp::URLRequestInfo request(instance_);
55 request.SetURL("test_url_loader_data/hello.txt");
56 request.SetStreamToFile(true);
57
58 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
59
60 pp::URLLoader loader(instance_);
61 callback.WaitForResult(loader.Open(request, callback.GetCallback()));
62 CHECK_CALLBACK_BEHAVIOR(callback);
63 ASSERT_EQ(PP_OK, callback.result());
64
65 pp::URLResponseInfo response_info(loader.GetResponseInfo());
66 ASSERT_FALSE(response_info.is_null());
67 ASSERT_EQ(200, response_info.GetStatusCode());
68
69 *file_ref_ext = pp::FileRef(response_info.GetBodyAsFileRef());
70 ASSERT_EQ(PP_FILESYSTEMTYPE_EXTERNAL, file_ref_ext->GetFileSystemType());
71 PASS();
72 }
73
DeleteDirectoryRecursively(pp::FileRef * dir)74 int32_t TestFileRef::DeleteDirectoryRecursively(pp::FileRef* dir) {
75 if (!dir)
76 return PP_ERROR_BADARGUMENT;
77
78 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
79 TestCompletionCallbackWithOutput<DirEntries> output_callback(
80 instance_->pp_instance(), callback_type());
81
82 output_callback.WaitForResult(
83 dir->ReadDirectoryEntries(output_callback.GetCallback()));
84 int32_t rv = output_callback.result();
85 if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND)
86 return rv;
87
88 DirEntries entries = output_callback.output();
89 for (DirEntries::const_iterator it = entries.begin();
90 it != entries.end();
91 ++it) {
92 pp::FileRef file_ref = it->file_ref();
93 if (it->file_type() == PP_FILETYPE_DIRECTORY) {
94 rv = DeleteDirectoryRecursively(&file_ref);
95 if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND)
96 return rv;
97 } else {
98 callback.WaitForResult(file_ref.Delete(callback.GetCallback()));
99 rv = callback.result();
100 if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND)
101 return rv;
102 }
103 }
104 callback.WaitForResult(dir->Delete(callback.GetCallback()));
105 return callback.result();
106 }
107
RunTests(const std::string & filter)108 void TestFileRef::RunTests(const std::string& filter) {
109 RUN_CALLBACK_TEST(TestFileRef, Create, filter);
110 RUN_CALLBACK_TEST(TestFileRef, GetFileSystemType, filter);
111 RUN_CALLBACK_TEST(TestFileRef, GetName, filter);
112 RUN_CALLBACK_TEST(TestFileRef, GetPath, filter);
113 RUN_CALLBACK_TEST(TestFileRef, GetParent, filter);
114 RUN_CALLBACK_TEST(TestFileRef, MakeDirectory, filter);
115 RUN_CALLBACK_TEST(TestFileRef, QueryAndTouchFile, filter);
116 RUN_CALLBACK_TEST(TestFileRef, DeleteFileAndDirectory, filter);
117 RUN_CALLBACK_TEST(TestFileRef, RenameFileAndDirectory, filter);
118 RUN_CALLBACK_TEST(TestFileRef, Query, filter);
119 RUN_CALLBACK_TEST(TestFileRef, FileNameEscaping, filter);
120 RUN_CALLBACK_TEST(TestFileRef, ReadDirectoryEntries, filter);
121 }
122
TestCreate()123 std::string TestFileRef::TestCreate() {
124 std::vector<std::string> invalid_paths;
125 invalid_paths.push_back("invalid_path"); // no '/' at the first character
126 invalid_paths.push_back(std::string()); // empty path
127 // The following are directory traversal checks
128 invalid_paths.push_back("..");
129 invalid_paths.push_back("/../invalid_path");
130 invalid_paths.push_back("/../../invalid_path");
131 invalid_paths.push_back("/invalid/../../path");
132 const size_t num_invalid_paths = invalid_paths.size();
133
134 pp::FileSystem file_system_pers(
135 instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
136 pp::FileSystem file_system_temp(
137 instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
138 for (size_t j = 0; j < num_invalid_paths; ++j) {
139 pp::FileRef file_ref_pers(file_system_pers, invalid_paths[j].c_str());
140 if (file_ref_pers.pp_resource() != 0) {
141 return "file_ref_pers expected to be invalid for path: " +
142 invalid_paths[j];
143 }
144 pp::FileRef file_ref_temp(file_system_temp, invalid_paths[j].c_str());
145 if (file_ref_temp.pp_resource() != 0) {
146 return "file_ref_temp expected to be invalid for path: " +
147 invalid_paths[j];
148 }
149 }
150 PASS();
151 }
152
TestGetFileSystemType()153 std::string TestFileRef::TestGetFileSystemType() {
154 pp::FileSystem file_system_pers(
155 instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
156 pp::FileSystem file_system_temp(
157 instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
158
159 pp::FileRef file_ref_pers(file_system_pers, kPersFilePath);
160 if (file_ref_pers.GetFileSystemType() != PP_FILESYSTEMTYPE_LOCALPERSISTENT)
161 return "file_ref_pers expected to be persistent.";
162
163 pp::FileRef file_ref_temp(file_system_temp, kTempFilePath);
164 if (file_ref_temp.GetFileSystemType() != PP_FILESYSTEMTYPE_LOCALTEMPORARY)
165 return "file_ref_temp expected to be temporary.";
166
167 pp::FileRef file_ref_ext;
168 std::string result = MakeExternalFileRef(&file_ref_ext);
169 if (!result.empty())
170 return result;
171 PASS();
172 }
173
TestGetName()174 std::string TestFileRef::TestGetName() {
175 pp::FileSystem file_system_pers(
176 instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
177 pp::FileSystem file_system_temp(
178 instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
179
180 pp::FileRef file_ref_pers(file_system_pers, kPersFilePath);
181 std::string name = file_ref_pers.GetName().AsString();
182 if (name != kPersFileName)
183 return ReportMismatch("FileRef::GetName", name, kPersFileName);
184
185 pp::FileRef file_ref_temp(file_system_temp, kTempFilePath);
186 name = file_ref_temp.GetName().AsString();
187 if (name != kTempFileName)
188 return ReportMismatch("FileRef::GetName", name, kTempFileName);
189
190 // Test the "/" case.
191 pp::FileRef file_ref_slash(file_system_temp, "/");
192 name = file_ref_slash.GetName().AsString();
193 if (name != "/")
194 return ReportMismatch("FileRef::GetName", name, "/");
195
196 pp::URLRequestInfo request(instance_);
197 request.SetURL("test_url_loader_data/hello.txt");
198 request.SetStreamToFile(true);
199
200 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
201
202 pp::URLLoader loader(instance_);
203 callback.WaitForResult(loader.Open(request, callback.GetCallback()));
204 CHECK_CALLBACK_BEHAVIOR(callback);
205 ASSERT_EQ(PP_OK, callback.result());
206
207 pp::URLResponseInfo response_info(loader.GetResponseInfo());
208 ASSERT_FALSE(response_info.is_null());
209 ASSERT_EQ(200, response_info.GetStatusCode());
210
211 pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef());
212 name = file_ref_ext.GetName().AsString();
213 ASSERT_FALSE(name.empty());
214
215 PASS();
216 }
217
TestGetPath()218 std::string TestFileRef::TestGetPath() {
219 pp::FileSystem file_system_pers(
220 instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
221 pp::FileSystem file_system_temp(
222 instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
223
224 pp::FileRef file_ref_pers(file_system_pers, kPersFilePath);
225 ASSERT_EQ(kPersFilePath, file_ref_pers.GetPath().AsString());
226
227 pp::FileRef file_ref_temp(file_system_temp, kTempFilePath);
228 ASSERT_EQ(kTempFilePath, file_ref_temp.GetPath().AsString());
229
230 pp::URLRequestInfo request(instance_);
231 request.SetURL("test_url_loader_data/hello.txt");
232 request.SetStreamToFile(true);
233
234 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
235
236 pp::URLLoader loader(instance_);
237 callback.WaitForResult(loader.Open(request, callback.GetCallback()));
238 CHECK_CALLBACK_BEHAVIOR(callback);
239 ASSERT_EQ(PP_OK, callback.result());
240
241 pp::URLResponseInfo response_info(loader.GetResponseInfo());
242 ASSERT_FALSE(response_info.is_null());
243 ASSERT_EQ(200, response_info.GetStatusCode());
244
245 pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef());
246 ASSERT_TRUE(file_ref_ext.GetPath().is_undefined());
247
248 PASS();
249 }
250
TestGetParent()251 std::string TestFileRef::TestGetParent() {
252 pp::FileSystem file_system_pers(
253 instance_, PP_FILESYSTEMTYPE_LOCALPERSISTENT);
254 pp::FileSystem file_system_temp(
255 instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
256
257 pp::FileRef file_ref_pers(file_system_pers, kPersFilePath);
258 ASSERT_EQ(kParentPath, file_ref_pers.GetParent().GetPath().AsString());
259
260 pp::FileRef file_ref_temp(file_system_temp, kTempFilePath);
261 ASSERT_EQ(kParentPath, file_ref_temp.GetParent().GetPath().AsString());
262
263 // Test the "/" case.
264 pp::FileRef file_ref_slash(file_system_temp, "/");
265 ASSERT_EQ("/", file_ref_slash.GetParent().GetPath().AsString());
266
267 // Test the "/foo" case (the parent is "/").
268 pp::FileRef file_ref_with_root_parent(file_system_temp, "/foo");
269 ASSERT_EQ("/", file_ref_with_root_parent.GetParent().GetPath().AsString());
270
271 pp::URLRequestInfo request(instance_);
272 request.SetURL("test_url_loader_data/hello.txt");
273 request.SetStreamToFile(true);
274
275 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
276
277 pp::URLLoader loader(instance_);
278 callback.WaitForResult(loader.Open(request, callback.GetCallback()));
279 CHECK_CALLBACK_BEHAVIOR(callback);
280 ASSERT_EQ(PP_OK, callback.result());
281
282 pp::URLResponseInfo response_info(loader.GetResponseInfo());
283 ASSERT_FALSE(response_info.is_null());
284 ASSERT_EQ(200, response_info.GetStatusCode());
285
286 pp::FileRef file_ref_ext(response_info.GetBodyAsFileRef());
287 ASSERT_TRUE(file_ref_ext.GetParent().is_null());
288
289 PASS();
290 }
291
TestMakeDirectory()292 std::string TestFileRef::TestMakeDirectory() {
293 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
294
295 // Open.
296 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
297 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
298 CHECK_CALLBACK_BEHAVIOR(callback);
299 ASSERT_EQ(PP_OK, callback.result());
300
301 // Make a directory.
302 pp::FileRef dir_ref(file_system, "/dir_make_dir");
303 callback.WaitForResult(
304 dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
305 CHECK_CALLBACK_BEHAVIOR(callback);
306 ASSERT_EQ(PP_OK, callback.result());
307
308 // Make a directory on the existing path without exclusive flag.
309 callback.WaitForResult(
310 dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
311 CHECK_CALLBACK_BEHAVIOR(callback);
312 ASSERT_EQ(PP_OK, callback.result());
313
314 // Making a directory should be aborted.
315 int32_t rv = PP_ERROR_FAILED;
316 {
317 rv = pp::FileRef(file_system, "/dir_make_dir_abort")
318 .MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback());
319 }
320 callback.WaitForAbortResult(rv);
321 CHECK_CALLBACK_BEHAVIOR(callback);
322
323 // Make nested directories.
324 dir_ref = pp::FileRef(file_system, "/dir_make_nested_dir_1/dir");
325 callback.WaitForResult(
326 dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS,
327 callback.GetCallback()));
328 CHECK_CALLBACK_BEHAVIOR(callback);
329 ASSERT_EQ(PP_OK, callback.result());
330
331 dir_ref = pp::FileRef(file_system, "/dir_make_nested_dir_2/dir");
332 callback.WaitForResult(
333 dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
334 CHECK_CALLBACK_BEHAVIOR(callback);
335 ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result());
336
337 // Ensure there is no directory on the path to test exclusive cases.
338 dir_ref = pp::FileRef(file_system, "/dir_make_dir_exclusive");
339 rv = DeleteDirectoryRecursively(&dir_ref);
340 ASSERT_TRUE(rv == PP_OK || rv == PP_ERROR_FILENOTFOUND);
341
342 // Make a directory exclusively.
343 callback.WaitForResult(
344 dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_EXCLUSIVE,
345 callback.GetCallback()));
346 CHECK_CALLBACK_BEHAVIOR(callback);
347 ASSERT_EQ(PP_OK, callback.result());
348
349 callback.WaitForResult(
350 dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_EXCLUSIVE,
351 callback.GetCallback()));
352 CHECK_CALLBACK_BEHAVIOR(callback);
353 ASSERT_EQ(PP_ERROR_FILEEXISTS, callback.result());
354
355 PASS();
356 }
357
TestQueryAndTouchFile()358 std::string TestFileRef::TestQueryAndTouchFile() {
359 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
360 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
361 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
362 CHECK_CALLBACK_BEHAVIOR(callback);
363 ASSERT_EQ(PP_OK, callback.result());
364
365 pp::FileRef file_ref(file_system, "/file_touch");
366 pp::FileIO file_io(instance_);
367 callback.WaitForResult(
368 file_io.Open(file_ref,
369 PP_FILEOPENFLAG_CREATE |
370 PP_FILEOPENFLAG_TRUNCATE |
371 PP_FILEOPENFLAG_WRITE,
372 callback.GetCallback()));
373 CHECK_CALLBACK_BEHAVIOR(callback);
374 ASSERT_EQ(PP_OK, callback.result());
375
376 // Write some data to have a non-zero file size.
377 callback.WaitForResult(file_io.Write(0, "test", 4, callback.GetCallback()));
378 CHECK_CALLBACK_BEHAVIOR(callback);
379 ASSERT_EQ(4, callback.result());
380
381 // Touch.
382 const PP_Time last_access_time = 123 * 24 * 3600.0;
383 // last_modified_time's granularity is 2 seconds
384 // See note in test_file_io.cc for why we use this time.
385 const PP_Time last_modified_time = 100 * 24 * 3600.0;
386 callback.WaitForResult(file_ref.Touch(last_access_time, last_modified_time,
387 callback.GetCallback()));
388 CHECK_CALLBACK_BEHAVIOR(callback);
389 ASSERT_EQ(PP_OK, callback.result());
390
391 // Touch aborted.
392 int32_t rv = PP_ERROR_FAILED;
393 {
394 rv = pp::FileRef(file_system, "/file_touch_abort")
395 .Touch(last_access_time, last_modified_time, callback.GetCallback());
396 }
397 callback.WaitForResult(rv);
398 CHECK_CALLBACK_BEHAVIOR(callback);
399 if (rv == PP_OK_COMPLETIONPENDING) {
400 // Touch tried to run asynchronously and should have been aborted.
401 ASSERT_EQ(PP_ERROR_ABORTED, callback.result());
402 } else {
403 // Touch ran synchronously and should have failed because the file does not
404 // exist.
405 ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result());
406 }
407
408 // Query.
409 PP_FileInfo info;
410 callback.WaitForResult(file_io.Query(&info, callback.GetCallback()));
411 CHECK_CALLBACK_BEHAVIOR(callback);
412 ASSERT_EQ(PP_OK, callback.result());
413 ASSERT_EQ(4, info.size);
414 ASSERT_EQ(PP_FILETYPE_REGULAR, info.type);
415 ASSERT_EQ(PP_FILESYSTEMTYPE_LOCALTEMPORARY, info.system_type);
416
417 // Disabled due to DST-related failure: crbug.com/314579
418 // ASSERT_EQ(last_access_time, info.last_access_time);
419 // ASSERT_EQ(last_modified_time, info.last_modified_time);
420
421 // Cancellation test.
422 // TODO(viettrungluu): this test causes a bunch of LOG(WARNING)s; investigate.
423 // TODO(viettrungluu): check |info| for late writes.
424 {
425 rv = pp::FileRef(file_system, "/file_touch").Touch(
426 last_access_time, last_modified_time, callback.GetCallback());
427 }
428 callback.WaitForAbortResult(rv);
429 CHECK_CALLBACK_BEHAVIOR(callback);
430
431 PASS();
432 }
433
TestDeleteFileAndDirectory()434 std::string TestFileRef::TestDeleteFileAndDirectory() {
435 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
436 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
437 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
438 CHECK_CALLBACK_BEHAVIOR(callback);
439 ASSERT_EQ(PP_OK, callback.result());
440
441 pp::FileRef file_ref(file_system, "/file_delete");
442 pp::FileIO file_io(instance_);
443 callback.WaitForResult(
444 file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
445 CHECK_CALLBACK_BEHAVIOR(callback);
446 ASSERT_EQ(PP_OK, callback.result());
447
448 callback.WaitForResult(file_ref.Delete(callback.GetCallback()));
449 CHECK_CALLBACK_BEHAVIOR(callback);
450 ASSERT_EQ(PP_OK, callback.result());
451
452 pp::FileRef dir_ref(file_system, "/dir_delete");
453 callback.WaitForResult(dir_ref.MakeDirectory(
454 PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
455 CHECK_CALLBACK_BEHAVIOR(callback);
456 ASSERT_EQ(PP_OK, callback.result());
457
458 callback.WaitForResult(dir_ref.Delete(callback.GetCallback()));
459 CHECK_CALLBACK_BEHAVIOR(callback);
460 ASSERT_EQ(PP_OK, callback.result());
461
462 pp::FileRef nested_dir_ref(file_system, "/dir_delete_1/dir_delete_2");
463 callback.WaitForResult(
464 nested_dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS,
465 callback.GetCallback()));
466 CHECK_CALLBACK_BEHAVIOR(callback);
467 ASSERT_EQ(PP_OK, callback.result());
468
469 // Attempt to delete the parent directory (should fail; it's non-empty).
470 pp::FileRef parent_dir_ref = nested_dir_ref.GetParent();
471 callback.WaitForResult(parent_dir_ref.Delete(callback.GetCallback()));
472 CHECK_CALLBACK_BEHAVIOR(callback);
473 ASSERT_EQ(PP_ERROR_FAILED, callback.result());
474
475 pp::FileRef nonexistent_file_ref(file_system, "/nonexistent_file_delete");
476 callback.WaitForResult(nonexistent_file_ref.Delete(callback.GetCallback()));
477 CHECK_CALLBACK_BEHAVIOR(callback);
478 ASSERT_EQ(PP_ERROR_FILENOTFOUND, callback.result());
479
480 // Delete aborted.
481 int32_t rv = PP_ERROR_FAILED;
482 {
483 pp::FileRef file_ref_abort(file_system, "/file_delete_abort");
484 pp::FileIO file_io_abort(instance_);
485 callback.WaitForResult(
486 file_io_abort.Open(file_ref_abort, PP_FILEOPENFLAG_CREATE,
487 callback.GetCallback()));
488 CHECK_CALLBACK_BEHAVIOR(callback);
489 ASSERT_EQ(PP_OK, callback.result());
490 rv = file_ref_abort.Delete(callback.GetCallback());
491 }
492 callback.WaitForAbortResult(rv);
493 CHECK_CALLBACK_BEHAVIOR(callback);
494
495 PASS();
496 }
497
TestRenameFileAndDirectory()498 std::string TestFileRef::TestRenameFileAndDirectory() {
499 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
500 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
501 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
502 CHECK_CALLBACK_BEHAVIOR(callback);
503 ASSERT_EQ(PP_OK, callback.result());
504
505 pp::FileRef file_ref(file_system, "/file_rename");
506 pp::FileIO file_io(instance_);
507 callback.WaitForResult(
508 file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
509 CHECK_CALLBACK_BEHAVIOR(callback);
510 ASSERT_EQ(PP_OK, callback.result());
511
512 pp::FileRef target_file_ref(file_system, "/target_file_rename");
513 callback.WaitForResult(
514 file_ref.Rename(target_file_ref, callback.GetCallback()));
515 CHECK_CALLBACK_BEHAVIOR(callback);
516 ASSERT_EQ(PP_OK, callback.result());
517
518 pp::FileRef dir_ref(file_system, "/dir_rename");
519 callback.WaitForResult(dir_ref.MakeDirectory(
520 PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
521 CHECK_CALLBACK_BEHAVIOR(callback);
522 ASSERT_EQ(PP_OK, callback.result());
523
524 pp::FileRef target_dir_ref(file_system, "/target_dir_rename");
525 callback.WaitForResult(
526 dir_ref.Rename(target_dir_ref, callback.GetCallback()));
527 CHECK_CALLBACK_BEHAVIOR(callback);
528 ASSERT_EQ(PP_OK, callback.result());
529
530 pp::FileRef nested_dir_ref(file_system, "/dir_rename_1/dir_rename_2");
531 callback.WaitForResult(
532 nested_dir_ref.MakeDirectory(PP_MAKEDIRECTORYFLAG_WITH_ANCESTORS,
533 callback.GetCallback()));
534 CHECK_CALLBACK_BEHAVIOR(callback);
535 ASSERT_EQ(PP_OK, callback.result());
536
537 // Try to rename nested directory to the parent name. Should fail.
538 pp::FileRef target_nested_dir_ref(file_system, "/dir_rename_1");
539 callback.WaitForResult(
540 nested_dir_ref.Rename(target_nested_dir_ref, callback.GetCallback()));
541 CHECK_CALLBACK_BEHAVIOR(callback);
542 ASSERT_EQ(PP_ERROR_FAILED, callback.result());
543
544 // Rename aborted.
545 // TODO(viettrungluu): Figure out what we want to do if the target file
546 // resource is destroyed before completion.
547 int32_t rv = PP_ERROR_FAILED;
548 pp::FileRef target_file_ref_abort(file_system,
549 "/target_file_rename_abort");
550 {
551 pp::FileRef file_ref_abort(file_system, "/file_rename_abort");
552 pp::FileIO file_io_abort(instance_);
553 callback.WaitForResult(
554 file_io_abort.Open(file_ref_abort, PP_FILEOPENFLAG_CREATE,
555 callback.GetCallback()));
556 CHECK_CALLBACK_BEHAVIOR(callback);
557 ASSERT_EQ(PP_OK, callback.result());
558
559 rv = file_ref_abort.Rename(target_file_ref_abort, callback.GetCallback());
560 }
561 callback.WaitForAbortResult(rv);
562 CHECK_CALLBACK_BEHAVIOR(callback);
563
564 PASS();
565 }
566
TestQuery()567 std::string TestFileRef::TestQuery() {
568 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
569
570 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
571 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
572 CHECK_CALLBACK_BEHAVIOR(callback);
573 ASSERT_EQ(PP_OK, callback.result());
574
575 pp::FileRef file_ref(file_system, "/file");
576 pp::FileIO file_io(instance_);
577 callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE,
578 callback.GetCallback()));
579 CHECK_CALLBACK_BEHAVIOR(callback);
580 ASSERT_EQ(PP_OK, callback.result());
581
582 // We touch the file so we can easily check access and modified time.
583 callback.WaitForResult(file_io.Touch(0, 0, callback.GetCallback()));
584 CHECK_CALLBACK_BEHAVIOR(callback);
585 ASSERT_EQ(PP_OK, callback.result());
586
587 TestCompletionCallbackWithOutput<PP_FileInfo> out_callback(
588 instance_->pp_instance(), callback_type());
589 out_callback.WaitForResult(file_ref.Query(out_callback.GetCallback()));
590 CHECK_CALLBACK_BEHAVIOR(out_callback);
591 ASSERT_EQ(PP_OK, out_callback.result());
592
593 PP_FileInfo info = out_callback.output();
594 ASSERT_EQ(0, info.size);
595 ASSERT_EQ(PP_FILETYPE_REGULAR, info.type);
596 ASSERT_EQ(PP_FILESYSTEMTYPE_LOCALTEMPORARY, info.system_type);
597 ASSERT_DOUBLE_EQ(0.0, info.last_access_time);
598 ASSERT_DOUBLE_EQ(0.0, info.last_modified_time);
599
600 // Query a file ref on an external filesystem.
601 pp::FileRef file_ref_ext;
602 std::string result = MakeExternalFileRef(&file_ref_ext);
603 if (!result.empty())
604 return result;
605 out_callback.WaitForResult(file_ref_ext.Query(out_callback.GetCallback()));
606 CHECK_CALLBACK_BEHAVIOR(out_callback);
607 if (out_callback.result() != PP_OK)
608 return ReportError("Query() result", out_callback.result());
609 ASSERT_EQ(PP_OK, out_callback.result());
610
611 info = out_callback.output();
612 ASSERT_EQ(PP_FILETYPE_REGULAR, info.type);
613 ASSERT_EQ(PP_FILESYSTEMTYPE_EXTERNAL, info.system_type);
614
615 // We can't touch the file, so just sanity check the times.
616 ASSERT_TRUE(info.creation_time >= 0.0);
617 ASSERT_TRUE(info.last_modified_time >= 0.0);
618 ASSERT_TRUE(info.last_access_time >= 0.0);
619
620 // Query a file ref for a file that doesn't exist.
621 pp::FileRef missing_file_ref(file_system, "/missing_file");
622 out_callback.WaitForResult(missing_file_ref.Query(
623 out_callback.GetCallback()));
624 CHECK_CALLBACK_BEHAVIOR(out_callback);
625 ASSERT_EQ(PP_ERROR_FILENOTFOUND, out_callback.result());
626
627 PASS();
628 }
629
TestFileNameEscaping()630 std::string TestFileRef::TestFileNameEscaping() {
631 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
632 pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
633 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
634 CHECK_CALLBACK_BEHAVIOR(callback);
635 ASSERT_EQ(PP_OK, callback.result());
636
637 std::string test_dir_path = "/dir_for_escaping_test";
638 // Create a directory in which to test.
639 pp::FileRef test_dir_ref(file_system, test_dir_path.c_str());
640 callback.WaitForResult(test_dir_ref.MakeDirectory(
641 PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
642 CHECK_CALLBACK_BEHAVIOR(callback);
643 ASSERT_EQ(PP_OK, callback.result());
644
645 // Create the file with the terrible name.
646 std::string full_file_path = test_dir_path + "/" + kTerribleName;
647 pp::FileRef file_ref(file_system, full_file_path.c_str());
648 pp::FileIO file_io(instance_);
649 callback.WaitForResult(
650 file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
651 CHECK_CALLBACK_BEHAVIOR(callback);
652 ASSERT_EQ(PP_OK, callback.result());
653
654 // FileRef::ReadDirectoryEntries only works out-of-process.
655 if (testing_interface_->IsOutOfProcess()) {
656 TestCompletionCallbackWithOutput<DirEntries>
657 output_callback(instance_->pp_instance(), callback_type());
658
659 output_callback.WaitForResult(
660 test_dir_ref.ReadDirectoryEntries(output_callback.GetCallback()));
661 CHECK_CALLBACK_BEHAVIOR(output_callback);
662 ASSERT_EQ(PP_OK, output_callback.result());
663
664 DirEntries entries = output_callback.output();
665 ASSERT_EQ(1, entries.size());
666 ASSERT_EQ(kTerribleName, entries.front().file_ref().GetName().AsString());
667 }
668
669 PASS();
670 }
671
TestReadDirectoryEntries()672 std::string TestFileRef::TestReadDirectoryEntries() {
673 TestCompletionCallback callback(instance_->pp_instance(), callback_type());
674 pp::FileSystem file_system(
675 instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY);
676 callback.WaitForResult(file_system.Open(1024, callback.GetCallback()));
677 CHECK_CALLBACK_BEHAVIOR(callback);
678 ASSERT_EQ(PP_OK, callback.result());
679
680 // Setup testing directories and files.
681 const char* test_dir_name = "/test_get_next_file";
682 const char* file_prefix = "file_";
683 const char* dir_prefix = "dir_";
684
685 pp::FileRef test_dir(file_system, test_dir_name);
686 int32_t rv = DeleteDirectoryRecursively(&test_dir);
687 ASSERT_TRUE(rv == PP_OK || rv == PP_ERROR_FILENOTFOUND);
688
689 callback.WaitForResult(test_dir.MakeDirectory(
690 PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
691 CHECK_CALLBACK_BEHAVIOR(callback);
692 ASSERT_EQ(PP_OK, callback.result());
693
694 static const int kNumFiles = 3;
695 std::set<std::string> expected_file_names;
696 for (int i = 1; i <= kNumFiles; ++i) {
697 std::ostringstream buffer;
698 buffer << test_dir_name << '/' << file_prefix << i;
699 pp::FileRef file_ref(file_system, buffer.str().c_str());
700
701 pp::FileIO file_io(instance_);
702 callback.WaitForResult(
703 file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE, callback.GetCallback()));
704 CHECK_CALLBACK_BEHAVIOR(callback);
705 ASSERT_EQ(PP_OK, callback.result());
706
707 expected_file_names.insert(buffer.str());
708 }
709
710 static const int kNumDirectories = 3;
711 std::set<std::string> expected_dir_names;
712 for (int i = 1; i <= kNumDirectories; ++i) {
713 std::ostringstream buffer;
714 buffer << test_dir_name << '/' << dir_prefix << i;
715 pp::FileRef file_ref(file_system, buffer.str().c_str());
716
717 callback.WaitForResult(file_ref.MakeDirectory(
718 PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback()));
719 CHECK_CALLBACK_BEHAVIOR(callback);
720 ASSERT_EQ(PP_OK, callback.result());
721
722 expected_dir_names.insert(buffer.str());
723 }
724
725 // Test that |ReadDirectoryEntries()| is able to fetch all
726 // directories and files that we created.
727 {
728 TestCompletionCallbackWithOutput<DirEntries> output_callback(
729 instance_->pp_instance(), callback_type());
730
731 output_callback.WaitForResult(
732 test_dir.ReadDirectoryEntries(output_callback.GetCallback()));
733 CHECK_CALLBACK_BEHAVIOR(output_callback);
734 ASSERT_EQ(PP_OK, output_callback.result());
735
736 DirEntries entries = output_callback.output();
737 size_t sum = expected_file_names.size() + expected_dir_names.size();
738 ASSERT_EQ(sum, entries.size());
739
740 for (DirEntries::const_iterator it = entries.begin();
741 it != entries.end(); ++it) {
742 pp::FileRef file_ref = it->file_ref();
743 std::string file_path = file_ref.GetPath().AsString();
744 std::set<std::string>::iterator found =
745 expected_file_names.find(file_path);
746 if (found != expected_file_names.end()) {
747 if (it->file_type() != PP_FILETYPE_REGULAR)
748 return file_path + " should have been a regular file.";
749 expected_file_names.erase(found);
750 } else {
751 found = expected_dir_names.find(file_path);
752 if (found == expected_dir_names.end())
753 return "Unexpected file path: " + file_path;
754 if (it->file_type() != PP_FILETYPE_DIRECTORY)
755 return file_path + " should have been a directory.";
756 expected_dir_names.erase(found);
757 }
758 }
759 ASSERT_TRUE(expected_file_names.empty());
760 ASSERT_TRUE(expected_dir_names.empty());
761 }
762
763 // Test cancellation of asynchronous |ReadDirectoryEntries()|.
764 TestCompletionCallbackWithOutput<DirEntries> output_callback(
765 instance_->pp_instance(), callback_type());
766 {
767 rv = pp::FileRef(file_system, test_dir_name)
768 .ReadDirectoryEntries(output_callback.GetCallback());
769 }
770 output_callback.WaitForAbortResult(rv);
771 CHECK_CALLBACK_BEHAVIOR(output_callback);
772
773
774 PASS();
775 }
776