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