• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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