|  | // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "ppapi/tests/test_file_io.h" | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <stddef.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <sys/stat.h> | 
|  | #include <sys/types.h> | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <vector> | 
|  |  | 
|  | #include "ppapi/c/pp_errors.h" | 
|  | #include "ppapi/c/ppb_file_io.h" | 
|  | #include "ppapi/c/private/pp_file_handle.h" | 
|  | #include "ppapi/c/private/ppb_testing_private.h" | 
|  | #include "ppapi/cpp/file_io.h" | 
|  | #include "ppapi/cpp/file_ref.h" | 
|  | #include "ppapi/cpp/file_system.h" | 
|  | #include "ppapi/cpp/instance.h" | 
|  | #include "ppapi/cpp/module.h" | 
|  | #include "ppapi/cpp/private/file_io_private.h" | 
|  | #include "ppapi/cpp/private/pass_file_handle.h" | 
|  | #include "ppapi/tests/test_utils.h" | 
|  | #include "ppapi/tests/testing_instance.h" | 
|  |  | 
|  | #if defined(PPAPI_OS_WIN) | 
|  | # include <io.h> | 
|  | # include <windows.h> | 
|  | // TODO(hamaji): Use standard windows APIs instead of compatibility layer? | 
|  | # define lseek _lseek | 
|  | # define read _read | 
|  | # define write _write | 
|  | # define ssize_t int | 
|  | #else | 
|  | # include <sys/mman.h> | 
|  | # include <unistd.h> | 
|  | #endif | 
|  |  | 
|  | REGISTER_TEST_CASE(FileIO); | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | std::string ReportMismatch(const std::string& method_name, | 
|  | const std::string& returned_result, | 
|  | const std::string& expected_result) { | 
|  | return method_name + " returned '" + returned_result + "'; '" + | 
|  | expected_result + "' expected."; | 
|  | } | 
|  |  | 
|  | std::string ReportOpenError(int32_t open_flags) { | 
|  | static const char* kFlagNames[] = { | 
|  | "PP_FILEOPENFLAG_READ", | 
|  | "PP_FILEOPENFLAG_WRITE", | 
|  | "PP_FILEOPENFLAG_CREATE", | 
|  | "PP_FILEOPENFLAG_TRUNCATE", | 
|  | "PP_FILEOPENFLAG_EXCLUSIVE" | 
|  | }; | 
|  |  | 
|  | std::string result = "FileIO:Open had unexpected behavior with flags: "; | 
|  | bool first_flag = true; | 
|  | for (int32_t mask = 1, index = 0; mask <= PP_FILEOPENFLAG_EXCLUSIVE; | 
|  | mask <<= 1, ++index) { | 
|  | if (mask & open_flags) { | 
|  | if (first_flag) { | 
|  | first_flag = false; | 
|  | } else { | 
|  | result += " | "; | 
|  | } | 
|  | result += kFlagNames[index]; | 
|  | } | 
|  | } | 
|  | if (first_flag) | 
|  | result += "[None]"; | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | int32_t ReadEntireFile(PP_Instance instance, | 
|  | pp::FileIO* file_io, | 
|  | int32_t offset, | 
|  | std::string* data, | 
|  | CallbackType callback_type) { | 
|  | TestCompletionCallback callback(instance, callback_type); | 
|  | char buf[256]; | 
|  | int32_t read_offset = offset; | 
|  |  | 
|  | for (;;) { | 
|  | callback.WaitForResult( | 
|  | file_io->Read(read_offset, buf, sizeof(buf), callback.GetCallback())); | 
|  | if (callback.result() < 0) | 
|  | return callback.result(); | 
|  | if (callback.result() == 0) | 
|  | break; | 
|  | read_offset += callback.result(); | 
|  | data->append(buf, callback.result()); | 
|  | } | 
|  |  | 
|  | return PP_OK; | 
|  | } | 
|  |  | 
|  | int32_t ReadToArrayEntireFile(PP_Instance instance, | 
|  | pp::FileIO* file_io, | 
|  | int32_t offset, | 
|  | std::string* data, | 
|  | CallbackType callback_type) { | 
|  | TestCompletionCallbackWithOutput< std::vector<char> > callback( | 
|  | instance, callback_type); | 
|  |  | 
|  | for (;;) { | 
|  | callback.WaitForResult(file_io->Read(offset, 256, callback.GetCallback())); | 
|  | int32_t rv = callback.result(); | 
|  | if (rv < 0) | 
|  | return rv; | 
|  | if (rv == 0) | 
|  | break; | 
|  | const std::vector<char>& output = callback.output(); | 
|  | assert(rv == static_cast<int32_t>(output.size())); | 
|  | offset += rv; | 
|  | data->append(output.begin(), output.end()); | 
|  | } | 
|  |  | 
|  | return PP_OK; | 
|  | } | 
|  |  | 
|  | #if !defined(PPAPI_OS_WIN) | 
|  | bool ReadEntireFileFromFileHandle(int fd, std::string* data) { | 
|  | if (lseek(fd, 0, SEEK_SET) < 0) | 
|  | return false; | 
|  | data->clear(); | 
|  |  | 
|  | int ret; | 
|  | do { | 
|  | char buf[8192]; | 
|  | ret = read(fd, buf, sizeof(buf)); | 
|  | if (ret > 0) | 
|  | data->append(buf, ret); | 
|  | } while (ret > 0); | 
|  | return ret == 0; | 
|  | } | 
|  | #endif  // !defined(PPAPI_OS_WIN) | 
|  |  | 
|  | int32_t WriteEntireBuffer(PP_Instance instance, | 
|  | pp::FileIO* file_io, | 
|  | int32_t offset, | 
|  | const std::string& data, | 
|  | CallbackType callback_type) { | 
|  | TestCompletionCallback callback(instance, callback_type); | 
|  | int32_t write_offset = offset; | 
|  | const char* buf = data.c_str(); | 
|  | int32_t size = static_cast<int32_t>(data.size()); | 
|  |  | 
|  | while (write_offset < offset + size) { | 
|  | callback.WaitForResult(file_io->Write(write_offset, | 
|  | &buf[write_offset - offset], | 
|  | size - write_offset + offset, | 
|  | callback.GetCallback())); | 
|  | if (callback.result() < 0) | 
|  | return callback.result(); | 
|  | if (callback.result() == 0) | 
|  | return PP_ERROR_FAILED; | 
|  | write_offset += callback.result(); | 
|  | } | 
|  |  | 
|  | return PP_OK; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | bool TestFileIO::Init() { | 
|  | return CheckTestingInterface() && EnsureRunningOverHTTP(); | 
|  | } | 
|  |  | 
|  | void TestFileIO::RunTests(const std::string& filter) { | 
|  | RUN_CALLBACK_TEST(TestFileIO, Open, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, OpenDirectory, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, ReadWriteSetLength, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, ReadToArrayWriteSetLength, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, TouchQuery, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, AbortCalls, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, ParallelReads, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, ParallelWrites, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, NotAllowMixedReadWrite, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, RequestOSFileHandle, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, RequestOSFileHandleWithOpenExclusive, filter); | 
|  | RUN_CALLBACK_TEST(TestFileIO, Mmap, filter); | 
|  |  | 
|  | // TODO(viettrungluu): add tests: | 
|  | //  - that PP_ERROR_PENDING is correctly returned | 
|  | //  - that operations respect the file open modes (flags) | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::TestOpen() { | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  |  | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | pp::FileRef file_ref(file_system, "/file_open"); | 
|  |  | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | std::string result; | 
|  | result = MatchOpenExpectations( | 
|  | &file_system, | 
|  | PP_FILEOPENFLAG_READ, | 
|  | DONT_CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS); | 
|  | if (!result.empty()) | 
|  | return result; | 
|  |  | 
|  | // Test the behavior of the power set of | 
|  | //   { PP_FILEOPENFLAG_CREATE, | 
|  | //     PP_FILEOPENFLAG_TRUNCATE, | 
|  | //     PP_FILEOPENFLAG_EXCLUSIVE }. | 
|  |  | 
|  | // First of all, none of them are specified. | 
|  | result = MatchOpenExpectations( | 
|  | &file_system, | 
|  | PP_FILEOPENFLAG_WRITE, | 
|  | DONT_CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS); | 
|  | if (!result.empty()) | 
|  | return result; | 
|  |  | 
|  | result = MatchOpenExpectations( | 
|  | &file_system, | 
|  | PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE, | 
|  | CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS); | 
|  | if (!result.empty()) | 
|  | return result; | 
|  |  | 
|  | result = MatchOpenExpectations( | 
|  | &file_system, | 
|  | PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_EXCLUSIVE, | 
|  | DONT_CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS); | 
|  | if (!result.empty()) | 
|  | return result; | 
|  |  | 
|  | result = MatchOpenExpectations( | 
|  | &file_system, | 
|  | PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_TRUNCATE, | 
|  | DONT_CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | TRUNCATE_IF_EXISTS); | 
|  | if (!result.empty()) | 
|  | return result; | 
|  |  | 
|  | result = MatchOpenExpectations( | 
|  | &file_system, | 
|  | PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_EXCLUSIVE, | 
|  | CREATE_IF_DOESNT_EXIST | DONT_OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS); | 
|  | if (!result.empty()) | 
|  | return result; | 
|  |  | 
|  | result = MatchOpenExpectations( | 
|  | &file_system, | 
|  | PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE, | 
|  | CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | TRUNCATE_IF_EXISTS); | 
|  | if (!result.empty()) | 
|  | return result; | 
|  |  | 
|  | result = MatchOpenExpectations( | 
|  | &file_system, | 
|  | PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_EXCLUSIVE | | 
|  | PP_FILEOPENFLAG_TRUNCATE, | 
|  | DONT_CREATE_IF_DOESNT_EXIST | OPEN_IF_EXISTS | TRUNCATE_IF_EXISTS); | 
|  | if (!result.empty()) | 
|  | return result; | 
|  |  | 
|  | result = MatchOpenExpectations( | 
|  | &file_system, | 
|  | PP_FILEOPENFLAG_WRITE | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_EXCLUSIVE | PP_FILEOPENFLAG_TRUNCATE, | 
|  | CREATE_IF_DOESNT_EXIST | DONT_OPEN_IF_EXISTS | DONT_TRUNCATE_IF_EXISTS); | 
|  | if (!result.empty()) | 
|  | return result; | 
|  |  | 
|  | // Invalid combination: PP_FILEOPENFLAG_TRUNCATE without | 
|  | // PP_FILEOPENFLAG_WRITE. | 
|  | result = MatchOpenExpectations( | 
|  | &file_system, | 
|  | PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_TRUNCATE, | 
|  | INVALID_FLAG_COMBINATION); | 
|  | if (!result.empty()) | 
|  | return result; | 
|  |  | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::TestOpenDirectory() { | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  |  | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Make a directory. | 
|  | pp::FileRef dir_ref(file_system, "/test_dir_open_directory"); | 
|  | callback.WaitForResult(dir_ref.MakeDirectory( | 
|  | PP_MAKEDIRECTORYFLAG_NONE, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Open the directory. This is expected to fail since directories cannot be | 
|  | // opened. | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(dir_ref, PP_FILEOPENFLAG_READ, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_ERROR_NOTAFILE, callback.result()); | 
|  |  | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::TestReadWriteSetLength() { | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  |  | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | pp::FileRef file_ref(file_system, "/file_read_write_setlength"); | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_TRUNCATE | | 
|  | PP_FILEOPENFLAG_READ | | 
|  | PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Write something to the file. | 
|  | int32_t rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0, | 
|  | "test_test", callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  |  | 
|  | // Attempt to read a negative number of bytes; it should fail. | 
|  | char buf[256]; | 
|  | callback.WaitForResult(file_io.Read(0, | 
|  | buf, | 
|  | -1, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_ERROR_FAILED, callback.result()); | 
|  |  | 
|  | // Read the entire file. | 
|  | std::string read_buffer; | 
|  | rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer, | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("test_test"), read_buffer); | 
|  |  | 
|  | // Truncate the file. | 
|  | callback.WaitForResult(file_io.SetLength(4, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Check the file contents. | 
|  | read_buffer.clear(); | 
|  | rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer, | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("test"), read_buffer); | 
|  |  | 
|  | // Try to read past the end of the file. | 
|  | read_buffer.clear(); | 
|  | rv = ReadEntireFile(instance_->pp_instance(), &file_io, 100, &read_buffer, | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_TRUE(read_buffer.empty()); | 
|  |  | 
|  | // Write past the end of the file. The file should be zero-padded. | 
|  | rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 8, "test", | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  |  | 
|  | // Check the contents of the file. | 
|  | read_buffer.clear(); | 
|  | rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer, | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("test\0\0\0\0test", 12), read_buffer); | 
|  |  | 
|  | // Extend the file. | 
|  | callback.WaitForResult(file_io.SetLength(16, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Check the contents of the file. | 
|  | read_buffer.clear(); | 
|  | rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer, | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("test\0\0\0\0test\0\0\0\0", 16), read_buffer); | 
|  |  | 
|  | // Write in the middle of the file. | 
|  | rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 4, "test", | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  |  | 
|  | // Check the contents of the file. | 
|  | read_buffer.clear(); | 
|  | rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer, | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("testtesttest\0\0\0\0", 16), read_buffer); | 
|  |  | 
|  | // Read from the middle of the file. | 
|  | read_buffer.clear(); | 
|  | rv = ReadEntireFile(instance_->pp_instance(), &file_io, 4, &read_buffer, | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("testtest\0\0\0\0", 12), read_buffer); | 
|  |  | 
|  | // Append to the end of the file. | 
|  | pp::FileIO file_io2(instance_); | 
|  | callback.WaitForResult(file_io2.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_READ | | 
|  | PP_FILEOPENFLAG_APPEND, | 
|  | callback.GetCallback())); | 
|  | rv = WriteEntireBuffer(instance_->pp_instance(), &file_io2, 0, "appended", | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | read_buffer.clear(); | 
|  | rv = ReadEntireFile(instance_->pp_instance(), &file_io2, 0, &read_buffer, | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("testtesttest\0\0\0\0appended", 24), read_buffer); | 
|  |  | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | // This is basically a copy of TestReadWriteSetLength, but with the new Read | 
|  | // API.  With this test case, we can make sure the two Read's have the same | 
|  | // behavior. | 
|  | std::string TestFileIO::TestReadToArrayWriteSetLength() { | 
|  | if (callback_type() == PP_BLOCKING) { | 
|  | // This test does not make sense for blocking callbacks. | 
|  | PASS(); | 
|  | } | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  |  | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | pp::FileRef file_ref(file_system, "/file_read_write_setlength"); | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_TRUNCATE | | 
|  | PP_FILEOPENFLAG_READ | | 
|  | PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Write something to the file. | 
|  | int32_t rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0, | 
|  | "test_test", callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  |  | 
|  | TestCompletionCallbackWithOutput< std::vector<char> > callback2( | 
|  | instance_->pp_instance(), callback_type()); | 
|  | // Attempt to read a negative number of bytes; it should fail. | 
|  | callback2.WaitForResult(file_io.Read(0, -1, callback2.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback2); | 
|  | ASSERT_EQ(PP_ERROR_FAILED, callback2.result()); | 
|  |  | 
|  | // Read the entire file. | 
|  | std::string read_buffer; | 
|  | read_buffer.reserve(10); | 
|  | rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0, | 
|  | &read_buffer, callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("test_test"), read_buffer); | 
|  |  | 
|  | // Truncate the file. | 
|  | callback.WaitForResult(file_io.SetLength(4, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  |  | 
|  | // Check the file contents. | 
|  | read_buffer.clear(); | 
|  | rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0, | 
|  | &read_buffer, callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("test"), read_buffer); | 
|  |  | 
|  | // Try to read past the end of the file. | 
|  | read_buffer.clear(); | 
|  | rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 100, | 
|  | &read_buffer, callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_TRUE(read_buffer.empty()); | 
|  |  | 
|  | // Write past the end of the file. The file should be zero-padded. | 
|  | rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 8, "test", | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  |  | 
|  | // Check the contents of the file. | 
|  | read_buffer.clear(); | 
|  | rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0, | 
|  | &read_buffer, callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("test\0\0\0\0test", 12), read_buffer); | 
|  |  | 
|  | // Extend the file. | 
|  | callback.WaitForResult(file_io.SetLength(16, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Check the contents of the file. | 
|  | read_buffer.clear(); | 
|  | rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0, | 
|  | &read_buffer, callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("test\0\0\0\0test\0\0\0\0", 16), read_buffer); | 
|  |  | 
|  | // Write in the middle of the file. | 
|  | rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 4, "test", | 
|  | callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  |  | 
|  | // Check the contents of the file. | 
|  | read_buffer.clear(); | 
|  | rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 0, | 
|  | &read_buffer, callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("testtesttest\0\0\0\0", 16), read_buffer); | 
|  |  | 
|  | // Read from the middle of the file. | 
|  | read_buffer.clear(); | 
|  | rv = ReadToArrayEntireFile(instance_->pp_instance(), &file_io, 4, | 
|  | &read_buffer, callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("testtest\0\0\0\0", 12), read_buffer); | 
|  |  | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::TestTouchQuery() { | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  |  | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | pp::FileRef file_ref(file_system, "/file_touch"); | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_TRUNCATE | | 
|  | PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Write some data to have a non-zero file size. | 
|  | callback.WaitForResult(file_io.Write(0, "test", 4, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(4, callback.result()); | 
|  |  | 
|  | const PP_Time last_access_time = 123 * 24 * 3600.0; | 
|  | // last_modified_time's granularity is 2 seconds | 
|  | // NOTE: In NaCl on Windows, NaClDescIO uses _fstat64 to retrieve file info. | 
|  | // This function returns strange values for very small time values (near the | 
|  | // Unix Epoch). For a value like 246.0, it returns -1. For larger values, it | 
|  | // returns values that are exactly an hour less. The value below is handled | 
|  | // correctly, and is only 100 days after the start of Unix time. | 
|  | const PP_Time last_modified_time = 100 * 24 * 3600.0; | 
|  | callback.WaitForResult(file_io.Touch(last_access_time, last_modified_time, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | PP_FileInfo info; | 
|  | callback.WaitForResult(file_io.Query(&info, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | if ((info.size != 4) || | 
|  | (info.type != PP_FILETYPE_REGULAR) || | 
|  | (info.system_type != PP_FILESYSTEMTYPE_LOCALTEMPORARY)) | 
|  | // Disabled due to DST-related failure: crbug.com/314579 | 
|  | //(info.last_access_time != last_access_time) || | 
|  | //(info.last_modified_time != last_modified_time)) | 
|  | return "FileIO::Query() has returned bad data."; | 
|  |  | 
|  | // Call |Query()| again, to make sure it works a second time. | 
|  | callback.WaitForResult(file_io.Query(&info, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::TestAbortCalls() { | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  |  | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | pp::FileRef file_ref(file_system, "/file_abort_calls"); | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | int32_t rv = PP_ERROR_FAILED; | 
|  | // First, create a file on which to do ops. | 
|  | { | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult( | 
|  | file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // N.B.: Should write at least 3 bytes. | 
|  | rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0, | 
|  | "foobarbazquux", callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | } | 
|  |  | 
|  | // Abort |Open()|. | 
|  | { | 
|  | rv = pp::FileIO(instance_) | 
|  | .Open(file_ref, PP_FILEOPENFLAG_READ, callback.GetCallback()); | 
|  | } | 
|  | callback.WaitForAbortResult(rv); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  |  | 
|  | // Abort |Query()|. | 
|  | { | 
|  | PP_FileInfo info = { 0 }; | 
|  | // Save a copy and make sure |info| doesn't get written to if it is aborted. | 
|  | PP_FileInfo info_copy; | 
|  | memcpy(&info_copy, &info, sizeof(info)); | 
|  | { | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_READ, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | rv = file_io.Query(&info, callback.GetCallback()); | 
|  | }  // Destroy |file_io|. | 
|  | callback.WaitForResult(rv); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | if (callback_type() == PP_BLOCKING) { | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  | // The operation completed synchronously, so |info| should have changed. | 
|  | ASSERT_NE(0, memcmp(&info_copy, &info, sizeof(info))); | 
|  | } else { | 
|  | ASSERT_EQ(PP_ERROR_ABORTED, callback.result()); | 
|  | ASSERT_EQ(0, memcmp(&info_copy, &info, sizeof(info))); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Abort |Touch()|. | 
|  | { | 
|  | { | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | rv = file_io.Touch(0, 0, callback.GetCallback()); | 
|  | }  // Destroy |file_io|. | 
|  | callback.WaitForAbortResult(rv); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | } | 
|  |  | 
|  | // Abort |Read()|. | 
|  | { | 
|  | char buf[3] = { 0 }; | 
|  | { | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_READ, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | rv = file_io.Read(0, buf, sizeof(buf), callback.GetCallback()); | 
|  | }  // Destroy |file_io|. | 
|  | // Save a copy to make sure buf isn't written to in the async case. | 
|  | char buf_copy[3]; | 
|  | memcpy(&buf_copy, &buf, sizeof(buf)); | 
|  | callback.WaitForResult(rv); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | if (callback_type() == PP_BLOCKING) { | 
|  | ASSERT_EQ(callback.result(), sizeof(buf)); | 
|  | } else { | 
|  | ASSERT_EQ(PP_ERROR_ABORTED, callback.result()); | 
|  | ASSERT_EQ(0, memcmp(&buf_copy, &buf, sizeof(buf))); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Abort |Write()|. | 
|  | { | 
|  | char buf[3] = { 0 }; | 
|  | { | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | rv = file_io.Write(0, buf, sizeof(buf), callback.GetCallback()); | 
|  | }  // Destroy |file_io|. | 
|  | callback.WaitForResult(rv); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | if (callback_type() == PP_BLOCKING) | 
|  | ASSERT_EQ(callback.result(), sizeof(buf)); | 
|  | else | 
|  | ASSERT_EQ(PP_ERROR_ABORTED, callback.result()); | 
|  | } | 
|  |  | 
|  | // Abort |SetLength()|. | 
|  | { | 
|  | { | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | rv = file_io.SetLength(3, callback.GetCallback()); | 
|  | }  // Destroy |file_io|. | 
|  | callback.WaitForAbortResult(rv); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | } | 
|  |  | 
|  | // Abort |Flush|. | 
|  | { | 
|  | { | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | rv = file_io.Flush(callback.GetCallback()); | 
|  | }  // Destroy |file_io|. | 
|  | callback.WaitForAbortResult(rv); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | } | 
|  |  | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::TestParallelReads() { | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | pp::FileRef file_ref(file_system, "/file_parallel_reads"); | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_TRUNCATE | | 
|  | PP_FILEOPENFLAG_READ | | 
|  | PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Set up testing contents. | 
|  | int32_t rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0, | 
|  | "abcdefghijkl", callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  |  | 
|  | // Parallel read operations. | 
|  | const char* border = "__border__"; | 
|  | const int32_t border_size = static_cast<int32_t>(strlen(border)); | 
|  |  | 
|  | TestCompletionCallback callback_1(instance_->pp_instance(), callback_type()); | 
|  | int32_t read_offset_1 = 0; | 
|  | int32_t size_1 = 3; | 
|  | std::vector<char> extended_buf_1(border_size * 2 + size_1); | 
|  | char* buf_1 = &extended_buf_1[border_size]; | 
|  | memcpy(&extended_buf_1[0], border, border_size); | 
|  | memcpy(buf_1 + size_1, border, border_size); | 
|  |  | 
|  | TestCompletionCallback callback_2(instance_->pp_instance(), callback_type()); | 
|  | int32_t read_offset_2 = size_1; | 
|  | int32_t size_2 = 9; | 
|  | std::vector<char> extended_buf_2(border_size * 2 + size_2); | 
|  | char* buf_2 = &extended_buf_2[border_size]; | 
|  | memcpy(&extended_buf_2[0], border, border_size); | 
|  | memcpy(buf_2 + size_2, border, border_size); | 
|  |  | 
|  | int32_t rv_1 = PP_OK; | 
|  | int32_t rv_2 = PP_OK; | 
|  | while (size_1 >= 0 && size_2 >= 0 && size_1 + size_2 > 0) { | 
|  | if (size_1 > 0) { | 
|  | rv_1 = file_io.Read(read_offset_1, buf_1, size_1, | 
|  | callback_1.GetCallback()); | 
|  | } | 
|  | if (size_2 > 0) { | 
|  | rv_2 = file_io.Read(read_offset_2, buf_2, size_2, | 
|  | callback_2.GetCallback()); | 
|  | } | 
|  | if (size_1 > 0) { | 
|  | callback_1.WaitForResult(rv_1); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_1); | 
|  | ASSERT_TRUE(callback_1.result() > 0); | 
|  | read_offset_1 += callback_1.result(); | 
|  | buf_1 += callback_1.result(); | 
|  | size_1 -= callback_1.result(); | 
|  | } | 
|  |  | 
|  | if (size_2 > 0) { | 
|  | callback_2.WaitForResult(rv_2); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_2); | 
|  | ASSERT_TRUE(callback_2.result() > 0); | 
|  | read_offset_2 += callback_2.result(); | 
|  | buf_2 += callback_2.result(); | 
|  | size_2 -= callback_2.result(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // If |size_1| or |size_2| is not 0, we have invoked wrong callback(s). | 
|  | ASSERT_EQ(0, size_1); | 
|  | ASSERT_EQ(0, size_2); | 
|  |  | 
|  | // Make sure every read operation writes into the correct buffer. | 
|  | const char expected_result_1[] = "__border__abc__border__"; | 
|  | const char expected_result_2[] = "__border__defghijkl__border__"; | 
|  | ASSERT_TRUE(strncmp(&extended_buf_1[0], expected_result_1, | 
|  | strlen(expected_result_1)) == 0); | 
|  | ASSERT_TRUE(strncmp(&extended_buf_2[0], expected_result_2, | 
|  | strlen(expected_result_2)) == 0); | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::TestParallelWrites() { | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | pp::FileRef file_ref(file_system, "/file_parallel_writes"); | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_TRUNCATE | | 
|  | PP_FILEOPENFLAG_READ | | 
|  | PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Parallel write operations. | 
|  | TestCompletionCallback callback_1(instance_->pp_instance(), callback_type()); | 
|  | int32_t write_offset_1 = 0; | 
|  | const char* buf_1 = "abc"; | 
|  | int32_t size_1 = static_cast<int32_t>(strlen(buf_1)); | 
|  |  | 
|  | TestCompletionCallback callback_2(instance_->pp_instance(), callback_type()); | 
|  | int32_t write_offset_2 = size_1; | 
|  | const char* buf_2 = "defghijkl"; | 
|  | int32_t size_2 = static_cast<int32_t>(strlen(buf_2)); | 
|  |  | 
|  | int32_t rv_1 = PP_OK; | 
|  | int32_t rv_2 = PP_OK; | 
|  | while (size_1 >= 0 && size_2 >= 0 && size_1 + size_2 > 0) { | 
|  | if (size_1 > 0) { | 
|  | // Copy the buffer so we can erase it below. | 
|  | std::string str_1(buf_1); | 
|  | rv_1 = file_io.Write( | 
|  | write_offset_1, &str_1[0], static_cast<int32_t>(str_1.size()), | 
|  | callback_1.GetCallback()); | 
|  | // Erase the buffer to test that async writes copy it. | 
|  | std::fill(str_1.begin(), str_1.end(), 0); | 
|  | } | 
|  | if (size_2 > 0) { | 
|  | // Copy the buffer so we can erase it below. | 
|  | std::string str_2(buf_2); | 
|  | rv_2 = file_io.Write( | 
|  | write_offset_2, &str_2[0], static_cast<int32_t>(str_2.size()), | 
|  | callback_2.GetCallback()); | 
|  | // Erase the buffer to test that async writes copy it. | 
|  | std::fill(str_2.begin(), str_2.end(), 0); | 
|  | } | 
|  |  | 
|  | if (size_1 > 0) { | 
|  | callback_1.WaitForResult(rv_1); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_1); | 
|  | ASSERT_TRUE(callback_1.result() > 0); | 
|  | write_offset_1 += callback_1.result(); | 
|  | buf_1 += callback_1.result(); | 
|  | size_1 -= callback_1.result(); | 
|  | } | 
|  |  | 
|  | if (size_2 > 0) { | 
|  | callback_2.WaitForResult(rv_2); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_2); | 
|  | ASSERT_TRUE(callback_2.result() > 0); | 
|  | write_offset_2 += callback_2.result(); | 
|  | buf_2 += callback_2.result(); | 
|  | size_2 -= callback_2.result(); | 
|  | } | 
|  | } | 
|  |  | 
|  | // If |size_1| or |size_2| is not 0, we have invoked wrong callback(s). | 
|  | ASSERT_EQ(0, size_1); | 
|  | ASSERT_EQ(0, size_2); | 
|  |  | 
|  | // Check the file contents. | 
|  | std::string read_buffer; | 
|  | int32_t rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, | 
|  | &read_buffer, callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | ASSERT_EQ(std::string("abcdefghijkl"), read_buffer); | 
|  |  | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::TestNotAllowMixedReadWrite() { | 
|  | if (callback_type() == PP_BLOCKING) { | 
|  | // This test does not make sense for blocking callbacks. | 
|  | PASS(); | 
|  | } | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  |  | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | pp::FileRef file_ref(file_system, "/file_not_allow_mixed_read_write"); | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | pp::FileIO file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_TRUNCATE | | 
|  | PP_FILEOPENFLAG_READ | | 
|  | PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | TestCompletionCallback callback_1(instance_->pp_instance(), PP_REQUIRED); | 
|  | int32_t write_offset_1 = 0; | 
|  | const char* buf_1 = "mnopqrstuvw"; | 
|  | int32_t rv_1 = file_io.Write(write_offset_1, buf_1, | 
|  | static_cast<int32_t>(strlen(buf_1)), | 
|  | callback_1.GetCallback()); | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, rv_1); | 
|  |  | 
|  | TestCompletionCallback callback_2(instance_->pp_instance(), callback_type()); | 
|  | int32_t read_offset_2 = 4; | 
|  | char buf_2[3]; | 
|  | callback_2.WaitForResult(file_io.Read(read_offset_2, buf_2, sizeof(buf_2), | 
|  | callback_2.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_2); | 
|  | ASSERT_EQ(PP_ERROR_INPROGRESS, callback_2.result()); | 
|  | callback_1.WaitForResult(rv_1); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_1); | 
|  |  | 
|  | // Cannot query while a write is pending. | 
|  | rv_1 = file_io.Write(write_offset_1, buf_1, | 
|  | static_cast<int32_t>(strlen(buf_1)), | 
|  | callback_1.GetCallback()); | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, rv_1); | 
|  | PP_FileInfo info; | 
|  | callback_2.WaitForResult(file_io.Query(&info, callback_2.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_2); | 
|  | ASSERT_EQ(PP_ERROR_INPROGRESS, callback_2.result()); | 
|  | callback_1.WaitForResult(rv_1); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_1); | 
|  |  | 
|  | // Cannot touch while a write is pending. | 
|  | rv_1 = file_io.Write(write_offset_1, buf_1, | 
|  | static_cast<int32_t>(strlen(buf_1)), | 
|  | callback_1.GetCallback()); | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, rv_1); | 
|  | callback_2.WaitForResult(file_io.Touch(1234.0, 5678.0, | 
|  | callback_2.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_2); | 
|  | ASSERT_EQ(PP_ERROR_INPROGRESS, callback_2.result()); | 
|  | callback_1.WaitForResult(rv_1); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_1); | 
|  |  | 
|  | // Cannot set length while a write is pending. | 
|  | rv_1 = file_io.Write(write_offset_1, buf_1, | 
|  | static_cast<int32_t>(strlen(buf_1)), | 
|  | callback_1.GetCallback()); | 
|  | ASSERT_EQ(PP_OK_COMPLETIONPENDING, rv_1); | 
|  | callback_2.WaitForResult(file_io.SetLength(123, callback_2.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_2); | 
|  | ASSERT_EQ(PP_ERROR_INPROGRESS, callback_2.result()); | 
|  | callback_1.WaitForResult(rv_1); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback_1); | 
|  |  | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::TestRequestOSFileHandle() { | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  |  | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | pp::FileRef file_ref(file_system, "/file_os_fd"); | 
|  |  | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | pp::FileIO_Private file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_TRUNCATE | | 
|  | PP_FILEOPENFLAG_READ | | 
|  | PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | TestCompletionCallbackWithOutput<pp::PassFileHandle> output_callback( | 
|  | instance_->pp_instance(), callback_type()); | 
|  | output_callback.WaitForResult( | 
|  | file_io.RequestOSFileHandle(output_callback.GetCallback())); | 
|  | PP_FileHandle handle = output_callback.output().Release(); | 
|  | ASSERT_EQ(PP_OK, output_callback.result()); | 
|  |  | 
|  | if (handle == PP_kInvalidFileHandle) | 
|  | return "FileIO::RequestOSFileHandle() returned a bad file handle."; | 
|  | #if defined(PPAPI_OS_WIN) | 
|  | int fd = _open_osfhandle(reinterpret_cast<intptr_t>(handle), | 
|  | _O_RDWR | _O_BINARY); | 
|  | #else | 
|  | int fd = handle; | 
|  | #endif | 
|  | if (fd < 0) | 
|  | return "FileIO::RequestOSFileHandle() returned a bad file descriptor."; | 
|  |  | 
|  | // Check write(2) for the native FD. | 
|  | const std::string msg = "foobar"; | 
|  | ssize_t cnt = write(fd, msg.data(), static_cast<unsigned>(msg.size())); | 
|  | if (cnt < 0) | 
|  | return ReportError("write for native FD returned error", errno); | 
|  | if (cnt != static_cast<ssize_t>(msg.size())) | 
|  | return ReportError("write for native FD count mismatch", cnt); | 
|  |  | 
|  | // Check lseek(2) for the native FD. | 
|  | off_t off = lseek(fd, 0, SEEK_CUR); | 
|  | if (off == static_cast<off_t>(-1)) | 
|  | return ReportError("lseek for native FD returned error", errno); | 
|  | if (off != static_cast<off_t>(msg.size())) | 
|  | return ReportError("lseek for native FD offset mismatch", off); | 
|  |  | 
|  | off = lseek(fd, 0, SEEK_SET); | 
|  | if (off == static_cast<off_t>(-1)) | 
|  | return ReportError("lseek for native FD returned error", errno); | 
|  | if (off != 0) | 
|  | return ReportError("lseek for native FD offset mismatch", off); | 
|  |  | 
|  | // Check read(2) for the native FD. | 
|  | std::string buf(msg.size(), '\0'); | 
|  | cnt = read(fd, &buf[0], static_cast<unsigned>(msg.size())); | 
|  | if (cnt < 0) | 
|  | return ReportError("read for native FD returned error", errno); | 
|  | if (cnt != static_cast<ssize_t>(msg.size())) | 
|  | return ReportError("read for native FD count mismatch", cnt); | 
|  | if (msg != buf) | 
|  | return ReportMismatch("read for native FD", buf, msg); | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | // Calling RequestOSFileHandle with the FileIO that is opened with | 
|  | // PP_FILEOPENFLAG_EXCLUSIVE used to cause NaCl module to crash while loading. | 
|  | // This is a regression test for crbug.com/243241. | 
|  | std::string TestFileIO::TestRequestOSFileHandleWithOpenExclusive() { | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  |  | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | pp::FileRef file_ref(file_system, "/file_os_fd2"); | 
|  |  | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | // Open with PP_FILEOPENFLAG_CREATE and PP_FILEOPENFLAG_EXCLUSIVE will fail | 
|  | // if the file already exists. Delete it here to make sure it does not. | 
|  | callback.WaitForResult(file_ref.Delete(callback.GetCallback())); | 
|  |  | 
|  | pp::FileIO_Private file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_READ | | 
|  | PP_FILEOPENFLAG_WRITE | | 
|  | PP_FILEOPENFLAG_EXCLUSIVE, | 
|  | callback.GetCallback())); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | TestCompletionCallbackWithOutput<pp::PassFileHandle> output_callback( | 
|  | instance_->pp_instance(), callback_type()); | 
|  | output_callback.WaitForResult( | 
|  | file_io.RequestOSFileHandle(output_callback.GetCallback())); | 
|  | PP_FileHandle handle = output_callback.output().Release(); | 
|  | if (handle == PP_kInvalidFileHandle) | 
|  | return "FileIO::RequestOSFileHandle() returned a bad file handle."; | 
|  | ASSERT_EQ(PP_OK, output_callback.result()); | 
|  |  | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::TestMmap() { | 
|  | #if !defined(PPAPI_OS_WIN) | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  |  | 
|  | pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); | 
|  | pp::FileRef file_ref(file_system, "/file_os_fd"); | 
|  |  | 
|  | callback.WaitForResult(file_system.Open(1024, callback.GetCallback())); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | pp::FileIO_Private file_io(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | | 
|  | PP_FILEOPENFLAG_TRUNCATE | | 
|  | PP_FILEOPENFLAG_READ | | 
|  | PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | TestCompletionCallbackWithOutput<pp::PassFileHandle> output_callback( | 
|  | instance_->pp_instance(), callback_type()); | 
|  | output_callback.WaitForResult( | 
|  | file_io.RequestOSFileHandle(output_callback.GetCallback())); | 
|  | PP_FileHandle handle = output_callback.output().Release(); | 
|  | ASSERT_EQ(PP_OK, output_callback.result()); | 
|  |  | 
|  | if (handle == PP_kInvalidFileHandle) | 
|  | return "FileIO::RequestOSFileHandle() returned a bad file handle."; | 
|  | int fd = handle; | 
|  | if (fd < 0) | 
|  | return "FileIO::RequestOSFileHandle() returned a bad file descriptor."; | 
|  |  | 
|  | // Check write(2) for the native FD. | 
|  | const std::string msg = "foobar"; | 
|  | ssize_t cnt = write(fd, msg.data(), msg.size()); | 
|  | if (cnt < 0) | 
|  | return ReportError("write for native FD returned error", errno); | 
|  | if (cnt != static_cast<ssize_t>(msg.size())) | 
|  | return ReportError("write for native FD count mismatch", cnt); | 
|  |  | 
|  | // BEGIN mmap(2) test with a file handle opened in READ-WRITE mode. | 
|  | // Check mmap(2) for read. | 
|  | { | 
|  | char* mapped = reinterpret_cast<char*>( | 
|  | mmap(NULL, msg.size(), PROT_READ, MAP_PRIVATE, fd, 0)); | 
|  | if (mapped == MAP_FAILED) | 
|  | return ReportError("mmap(r) for native FD returned errno", errno); | 
|  | // Make sure the buffer is cleared. | 
|  | std::string buf = std::string(msg.size(), '\0'); | 
|  | memcpy(&buf[0], mapped, msg.size()); | 
|  | if (msg != buf) | 
|  | return ReportMismatch("mmap(r) for native FD", buf, msg); | 
|  | int r = munmap(mapped, msg.size()); | 
|  | if (r < 0) | 
|  | return ReportError("munmap for native FD returned error", errno); | 
|  | } | 
|  |  | 
|  | // Check mmap(2) for write with MAP_PRIVATE | 
|  | { | 
|  | char* mapped = reinterpret_cast<char*>( | 
|  | mmap(NULL, msg.size(), PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)); | 
|  | if (mapped == MAP_FAILED) | 
|  | return ReportError("mmap(r) for native FD returned errno", errno); | 
|  | // Make sure the file is not polluted by writing to privage mmap. | 
|  | strncpy(mapped, "baz", 3); | 
|  | std::string read_buffer; | 
|  | ASSERT_TRUE(ReadEntireFileFromFileHandle(fd, &read_buffer)); | 
|  | if (msg != read_buffer) | 
|  | return ReportMismatch("file content != msg", read_buffer, msg); | 
|  | int r = munmap(mapped, msg.size()); | 
|  | if (r < 0) | 
|  | return ReportError("munmap for native FD returned error", errno); | 
|  | } | 
|  |  | 
|  | // Check mmap(2) for write with MAP_SHARED. | 
|  | { | 
|  | char* mapped = reinterpret_cast<char*>( | 
|  | mmap(NULL, msg.size(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); | 
|  | if (mapped == MAP_FAILED) | 
|  | return ReportError("mmap(w) for native FD returned errno", errno); | 
|  | // s/foo/baz/ | 
|  | strncpy(mapped, "baz", 3); | 
|  | std::string read_buffer; | 
|  | ASSERT_TRUE(ReadEntireFileFromFileHandle(fd, &read_buffer)); | 
|  | if (read_buffer != "bazbar") | 
|  | return ReportMismatch("file content != msg", read_buffer, "bazbar"); | 
|  | int r = munmap(mapped, msg.size()); | 
|  | if (r < 0) | 
|  | return ReportError("munmap for native FD returned error", errno); | 
|  | } | 
|  | // END mmap(2) test with a file handle opened in READ-WRITE mode. | 
|  |  | 
|  | if (close(fd) < 0) | 
|  | return ReportError("close for native FD returned error", errno); | 
|  |  | 
|  | // BEGIN mmap(2) test with a file handle opened in READONLY mode. | 
|  | file_io = pp::FileIO_Private(instance_); | 
|  | callback.WaitForResult(file_io.Open(file_ref, | 
|  | PP_FILEOPENFLAG_READ, | 
|  | callback.GetCallback())); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  |  | 
|  | output_callback = TestCompletionCallbackWithOutput<pp::PassFileHandle>( | 
|  | instance_->pp_instance(), callback_type()); | 
|  | output_callback.WaitForResult( | 
|  | file_io.RequestOSFileHandle(output_callback.GetCallback())); | 
|  | handle = output_callback.output().Release(); | 
|  | ASSERT_EQ(PP_OK, output_callback.result()); | 
|  |  | 
|  | if (handle == PP_kInvalidFileHandle) | 
|  | return "FileIO::RequestOSFileHandle() returned a bad file handle."; | 
|  | fd = handle; | 
|  | if (fd < 0) | 
|  | return "FileIO::RequestOSFileHandle() returned a bad file descriptor."; | 
|  |  | 
|  | const std::string msg2 = "bazbar"; | 
|  |  | 
|  | // Check mmap(2) for read. | 
|  | { | 
|  | char* mapped = reinterpret_cast<char*>( | 
|  | mmap(NULL, msg2.size(), PROT_READ, MAP_PRIVATE, fd, 0)); | 
|  | if (mapped == MAP_FAILED) | 
|  | return ReportError("mmap(r) for native FD returned errno", errno); | 
|  | // Make sure the buffer is cleared. | 
|  | std::string buf = std::string(msg2.size(), '\0'); | 
|  | memcpy(&buf[0], mapped, msg2.size()); | 
|  | if (msg2 != buf) | 
|  | return ReportMismatch("mmap(r) for native FD", buf, msg2); | 
|  | int r = munmap(mapped, msg2.size()); | 
|  | if (r < 0) | 
|  | return ReportError("munmap for native FD returned error", errno); | 
|  | } | 
|  |  | 
|  | // Check mmap(2) for write with MAP_PRIVATE | 
|  | { | 
|  | char* mapped = reinterpret_cast<char*>( | 
|  | mmap(NULL, msg2.size(), PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)); | 
|  | if (mapped == MAP_FAILED) | 
|  | return ReportError("mmap(r) for native FD returned errno", errno); | 
|  | // Make sure the file is not polluted by writing to privage mmap. | 
|  | strncpy(mapped, "baz", 3); | 
|  | std::string read_buffer; | 
|  | ASSERT_TRUE(ReadEntireFileFromFileHandle(fd, &read_buffer)); | 
|  | if (msg2 != read_buffer) | 
|  | return ReportMismatch("file content != msg2", read_buffer, msg2); | 
|  | int r = munmap(mapped, msg2.size()); | 
|  | if (r < 0) | 
|  | return ReportError("munmap for native FD returned error", errno); | 
|  | } | 
|  |  | 
|  | // Check mmap(2) for write with MAP_SHARED. | 
|  | { | 
|  | char* mapped = reinterpret_cast<char*>( | 
|  | mmap(NULL, msg2.size(), PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); | 
|  | if (mapped != MAP_FAILED) | 
|  | return ReportError("mmap(w) for native FD must fail when opened readonly", | 
|  | -1); | 
|  | } | 
|  | // END mmap(2) test with a file handle opened in READONLY mode. | 
|  |  | 
|  | if (close(fd) < 0) | 
|  | return ReportError("close for native FD returned error", errno); | 
|  | #endif  // !defined(PPAPI_OS_WIN) | 
|  |  | 
|  | PASS(); | 
|  | } | 
|  |  | 
|  | std::string TestFileIO::MatchOpenExpectations(pp::FileSystem* file_system, | 
|  | int32_t open_flags, | 
|  | int32_t expectations) { | 
|  | std::string bad_argument = | 
|  | "TestFileIO::MatchOpenExpectations has invalid input arguments."; | 
|  | bool invalid_combination = !!(expectations & INVALID_FLAG_COMBINATION); | 
|  | if (invalid_combination) { | 
|  | if (expectations != INVALID_FLAG_COMBINATION) | 
|  | return bad_argument; | 
|  | } else { | 
|  | // Validate that one and only one of <some_expectation> and | 
|  | // DONT_<some_expectation> is specified. | 
|  | for (size_t remains = expectations, end = END_OF_OPEN_EXPECATION_PAIRS; | 
|  | end != 0; remains >>= 2, end >>= 2) { | 
|  | if (!!(remains & 1) == !!(remains & 2)) | 
|  | return bad_argument; | 
|  | } | 
|  | } | 
|  | bool create_if_doesnt_exist = !!(expectations & CREATE_IF_DOESNT_EXIST); | 
|  | bool open_if_exists = !!(expectations & OPEN_IF_EXISTS); | 
|  | bool truncate_if_exists = !!(expectations & TRUNCATE_IF_EXISTS); | 
|  |  | 
|  | TestCompletionCallback callback(instance_->pp_instance(), callback_type()); | 
|  | pp::FileRef existent_file_ref( | 
|  | *file_system, "/match_open_expectation_existent_non_empty_file"); | 
|  | pp::FileRef nonexistent_file_ref( | 
|  | *file_system, "/match_open_expectation_nonexistent_file"); | 
|  |  | 
|  | // Setup files for test. | 
|  | { | 
|  | callback.WaitForResult(existent_file_ref.Delete(callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_TRUE(callback.result() == PP_OK || | 
|  | callback.result() == PP_ERROR_FILENOTFOUND); | 
|  | callback.WaitForResult(nonexistent_file_ref.Delete(callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_TRUE(callback.result() == PP_OK || | 
|  | callback.result() == PP_ERROR_FILENOTFOUND); | 
|  |  | 
|  | pp::FileIO existent_file_io(instance_); | 
|  | callback.WaitForResult(existent_file_io.Open( | 
|  | existent_file_ref, | 
|  | PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_WRITE, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  | int32_t rv = WriteEntireBuffer(instance_->pp_instance(), &existent_file_io, | 
|  | 0, "foobar", callback_type()); | 
|  | ASSERT_EQ(PP_OK, rv); | 
|  | } | 
|  |  | 
|  | pp::FileIO existent_file_io(instance_); | 
|  | callback.WaitForResult(existent_file_io.Open(existent_file_ref, open_flags, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | if ((invalid_combination && callback.result() == PP_OK) || | 
|  | (!invalid_combination && | 
|  | ((callback.result() == PP_OK) != open_if_exists))) { | 
|  | return ReportOpenError(open_flags); | 
|  | } | 
|  |  | 
|  | if (!invalid_combination && open_if_exists) { | 
|  | PP_FileInfo info; | 
|  | callback.WaitForResult(existent_file_io.Query(&info, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | ASSERT_EQ(PP_OK, callback.result()); | 
|  | if (truncate_if_exists != (info.size == 0)) | 
|  | return ReportOpenError(open_flags); | 
|  | } | 
|  |  | 
|  | pp::FileIO nonexistent_file_io(instance_); | 
|  | callback.WaitForResult(nonexistent_file_io.Open(nonexistent_file_ref, | 
|  | open_flags, | 
|  | callback.GetCallback())); | 
|  | CHECK_CALLBACK_BEHAVIOR(callback); | 
|  | if ((invalid_combination && callback.result() == PP_OK) || | 
|  | (!invalid_combination && | 
|  | ((callback.result() == PP_OK) != create_if_doesnt_exist))) { | 
|  | return ReportOpenError(open_flags); | 
|  | } | 
|  |  | 
|  | return std::string(); | 
|  | } | 
|  |  | 
|  | // TODO(viettrungluu): Test Close(). crbug.com/69457 |