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
27REGISTER_TEST_CASE(FileRef);
28
29namespace {
30
31const char* kPersFileName = "persistent";
32const char* kTempFileName = "temporary";
33const char* kParentPath = "/foo/bar";
34const char* kPersFilePath = "/foo/bar/persistent";
35const char* kTempFilePath = "/foo/bar/temporary";
36const char* kTerribleName = "!@#$%^&*()-_=+{}[] ;:'\"|`~\t\n\r\b?";
37
38typedef std::vector<pp::DirectoryEntry> DirEntries;
39
40std::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
49bool TestFileRef::Init() {
50  return CheckTestingInterface() && EnsureRunningOverHTTP();
51}
52
53std::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
74int32_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
108void 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
123std::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
153std::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
174std::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
218std::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
251std::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
292std::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
358std::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
434std::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
498std::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
567std::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
630std::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
672std::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