// Copyright (c) 2011 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 #include #include "ppapi/c/dev/ppb_testing_dev.h" #include "ppapi/c/pp_errors.h" #include "ppapi/c/ppb_file_io.h" #include "ppapi/c/trusted/ppb_file_io_trusted.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/tests/test_utils.h" #include "ppapi/tests/testing_instance.h" 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) { TestCompletionCallback callback(instance); char buf[256]; int32_t read_offset = offset; for (;;) { int32_t rv = file_io->Read(read_offset, buf, sizeof(buf), callback); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv < 0) return rv; if (rv == 0) break; read_offset += rv; data->append(buf, rv); } return PP_OK; } int32_t WriteEntireBuffer(PP_Instance instance, pp::FileIO* file_io, int32_t offset, const std::string& data) { TestCompletionCallback callback(instance); int32_t write_offset = offset; const char* buf = data.c_str(); int32_t size = data.size(); while (write_offset < offset + size) { int32_t rv = file_io->Write(write_offset, &buf[write_offset - offset], size - write_offset + offset, callback); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv < 0) return rv; if (rv == 0) return PP_ERROR_FAILED; write_offset += rv; } return PP_OK; } } // namespace bool TestFileIO::Init() { return CheckTestingInterface() && EnsureRunningOverHTTP(); } void TestFileIO::RunTests(const std::string& filter) { RUN_TEST_FORCEASYNC_AND_NOT(Open, filter); RUN_TEST_FORCEASYNC_AND_NOT(ReadWriteSetLength, filter); RUN_TEST_FORCEASYNC_AND_NOT(TouchQuery, filter); RUN_TEST_FORCEASYNC_AND_NOT(AbortCalls, filter); RUN_TEST_FORCEASYNC_AND_NOT(ParallelReads, filter); RUN_TEST_FORCEASYNC_AND_NOT(ParallelWrites, filter); RUN_TEST_FORCEASYNC_AND_NOT(NotAllowMixedReadWrite, filter); RUN_TEST_FORCEASYNC_AND_NOT(WillWriteWillSetLength, 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(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); pp::FileRef file_ref(file_system, "/file_open"); int32_t rv = file_system.Open(1024, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); 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::TestReadWriteSetLength() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); pp::FileRef file_ref(file_system, "/file_read_write_setlength"); int32_t rv = file_system.Open(1024, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE | PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); // Write something to the file. rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0, "test_test"); if (rv != PP_OK) return ReportError("FileIO::Write", rv); // Check for failing read operation. char buf[256]; rv = file_io.Read(0, buf, -1, // negative number of bytes to read callback); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_ERROR_FAILED) return ReportError("FileIO::Read", rv); // Read the entire file. std::string read_buffer; rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer); if (rv != PP_OK) return ReportError("FileIO::Read", rv); if (read_buffer != "test_test") return ReportMismatch("FileIO::Read", read_buffer, "test_test"); // Truncate the file. rv = file_io.SetLength(4, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::SetLength force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::SetLength", rv); // Check the file contents. read_buffer.clear(); rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer); if (rv != PP_OK) return ReportError("FileIO::Read", rv); if (read_buffer != "test") return ReportMismatch("FileIO::Read", read_buffer, "test"); // Try to read past the end of the file. read_buffer.clear(); rv = ReadEntireFile(instance_->pp_instance(), &file_io, 100, &read_buffer); if (rv != PP_OK) return ReportError("FileIO::Read", rv); if (!read_buffer.empty()) return ReportMismatch("FileIO::Read", read_buffer, ""); // Write past the end of the file. The file should be zero-padded. rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 8, "test"); if (rv != PP_OK) return ReportError("FileIO::Write", rv); // Check the contents of the file. read_buffer.clear(); rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer); if (rv != PP_OK) return ReportError("FileIO::Read", rv); if (read_buffer != std::string("test\0\0\0\0test", 12)) return ReportMismatch("FileIO::Read", read_buffer, std::string("test\0\0\0\0test", 12)); // Extend the file. rv = file_io.SetLength(16, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::SetLength force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::SetLength", rv); // Check the contents of the file. read_buffer.clear(); rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer); if (rv != PP_OK) return ReportError("FileIO::Read", rv); if (read_buffer != std::string("test\0\0\0\0test\0\0\0\0", 16)) return ReportMismatch("FileIO::Read", read_buffer, std::string("test\0\0\0\0test\0\0\0\0", 16)); // Write in the middle of the file. rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 4, "test"); if (rv != PP_OK) return ReportError("FileIO::Write", rv); // Check the contents of the file. read_buffer.clear(); rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer); if (rv != PP_OK) return ReportError("FileIO::Read", rv); if (read_buffer != std::string("testtesttest\0\0\0\0", 16)) return ReportMismatch("FileIO::Read", read_buffer, std::string("testtesttest\0\0\0\0", 16)); // Read from the middle of the file. read_buffer.clear(); rv = ReadEntireFile(instance_->pp_instance(), &file_io, 4, &read_buffer); if (rv != PP_OK) return ReportError("FileIO::Read", rv); if (read_buffer != std::string("testtest\0\0\0\0", 12)) return ReportMismatch("FileIO::Read", read_buffer, std::string("testtest\0\0\0\0", 12)); PASS(); } std::string TestFileIO::TestTouchQuery() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); int32_t rv = file_system.Open(1024, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); pp::FileRef file_ref(file_system, "/file_touch"); pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE | PP_FILEOPENFLAG_WRITE, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); // Write some data to have a non-zero file size. rv = file_io.Write(0, "test", 4, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Write force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != 4) return ReportError("FileIO::Write", rv); // last_access_time's granularity is 1 day // last_modified_time's granularity is 2 seconds const PP_Time last_access_time = 123 * 24 * 3600.0; const PP_Time last_modified_time = 246.0; rv = file_io.Touch(last_access_time, last_modified_time, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Touch force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Touch", rv); PP_FileInfo info; rv = file_io.Query(&info, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Query force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Query", rv); if ((info.size != 4) || (info.type != PP_FILETYPE_REGULAR) || (info.system_type != PP_FILESYSTEMTYPE_LOCALTEMPORARY) || (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. rv = file_io.Query(&info, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Query force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Query", rv); PASS(); } std::string TestFileIO::TestAbortCalls() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); pp::FileRef file_ref(file_system, "/file_abort_calls"); int32_t rv = file_system.Open(1024, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); // First, create a file which to do ops on. { pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_WRITE, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); // N.B.: Should write at least 3 bytes. rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0, "foobarbazquux"); if (rv != PP_OK) return ReportError("FileIO::Write", rv); } // Abort |Open()|. { callback.reset_run_count(); rv = pp::FileIO(instance_) .Open(file_ref, PP_FILEOPENFLAG_READ,callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (callback.run_count() > 0) return "FileIO::Open ran callback synchronously."; if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileIO::Open not aborted."; } else if (rv != PP_OK) { return ReportError("FileIO::Open", rv); } } // Abort |Query()|. { PP_FileInfo info = { 0 }; { pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_READ, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); callback.reset_run_count(); rv = file_io.Query(&info, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Query force_async", rv); } // Destroy |file_io|. if (rv == PP_OK_COMPLETIONPENDING) { // Save a copy and make sure |info| doesn't get written to. PP_FileInfo info_copy; memcpy(&info_copy, &info, sizeof(info)); rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileIO::Query not aborted."; if (memcmp(&info_copy, &info, sizeof(info)) != 0) return "FileIO::Query wrote data after resource destruction."; } else if (rv != PP_OK) { return ReportError("FileIO::Query", rv); } } // Abort |Touch()|. { { pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_WRITE, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); callback.reset_run_count(); rv = file_io.Touch(0, 0, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Touch force_async", rv); } // Destroy |file_io|. if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileIO::Touch not aborted."; } else if (rv != PP_OK) { return ReportError("FileIO::Touch", rv); } } // Abort |Read()|. { char buf[3] = { 0 }; { pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_READ, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); callback.reset_run_count(); rv = file_io.Read(0, buf, sizeof(buf), callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Read force_async", rv); } // Destroy |file_io|. if (rv == PP_OK_COMPLETIONPENDING) { // Save a copy and make sure |buf| doesn't get written to. char buf_copy[3]; memcpy(&buf_copy, &buf, sizeof(buf)); rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileIO::Read not aborted."; if (memcmp(&buf_copy, &buf, sizeof(buf)) != 0) return "FileIO::Read wrote data after resource destruction."; } else if (rv != PP_OK) { return ReportError("FileIO::Read", rv); } } // Abort |Write()|. { char buf[3] = { 0 }; { pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_READ, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); callback.reset_run_count(); rv = file_io.Write(0, buf, sizeof(buf), callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Write force_async", rv); } // Destroy |file_io|. if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileIO::Write not aborted."; } else if (rv != PP_OK) { return ReportError("FileIO::Write", rv); } } // Abort |SetLength()|. { { pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_READ, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); callback.reset_run_count(); rv = file_io.SetLength(3, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::SetLength force_async", rv); } // Destroy |file_io|. if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileIO::SetLength not aborted."; } else if (rv != PP_OK) { return ReportError("FileIO::SetLength", rv); } } // Abort |Flush()|. { { pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_READ, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); callback.reset_run_count(); rv = file_io.Flush(callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Flush force_async", rv); } // Destroy |file_io|. if (rv == PP_OK_COMPLETIONPENDING) { rv = callback.WaitForResult(); if (rv != PP_ERROR_ABORTED) return "FileIO::Flush not aborted."; } else if (rv != PP_OK) { return ReportError("FileIO::Flush", rv); } } // TODO(viettrungluu): Also test that Close() aborts callbacks. // crbug.com/69457 PASS(); } std::string TestFileIO::TestParallelReads() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); pp::FileRef file_ref(file_system, "/file_parallel_reads"); int32_t rv = file_system.Open(1024, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE | PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); // Set up testing contents. rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0, "abcdefghijkl"); if (rv != PP_OK) return ReportError("FileIO::Write", rv); // Parallel read operations. const char* border = "__border__"; const int32_t border_size = strlen(border); TestCompletionCallback callback_1(instance_->pp_instance(), force_async_); int32_t read_offset_1 = 0; int32_t size_1 = 3; std::vector 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(), force_async_); int32_t read_offset_2 = size_1; int32_t size_2 = 9; std::vector 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); if (rv_1 != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Read", rv_1); } if (size_2 > 0) { rv_2 = file_io.Read(read_offset_2, buf_2, size_2, callback_2); if (rv_2 != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Read", rv_2); } if (size_1 > 0) { rv_1 = callback_1.WaitForResult(); if (rv_1 <= 0) return ReportError("FileIO::Read", rv_1); read_offset_1 += rv_1; buf_1 += rv_1; size_1 -= rv_1; } if (size_2 > 0) { rv_2 = callback_2.WaitForResult(); if (rv_2 <= 0) return ReportError("FileIO::Read", rv_2); read_offset_2 += rv_2; buf_2 += rv_2; size_2 -= rv_2; } } // If |size_1| or |size_2| is less than 0, we have invoked wrong // callback(s). if (size_1 < 0 || size_2 < 0) { return std::string( "Parallel FileIO::Read operations have invoked wrong callbacks."); } // 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__"; if (strncmp(&extended_buf_1[0], expected_result_1, strlen(expected_result_1)) != 0 || strncmp(&extended_buf_2[0], expected_result_2, strlen(expected_result_2)) != 0) { return std::string( "Parallel FileIO::Read operations have written into wrong buffers."); } PASS(); } std::string TestFileIO::TestParallelWrites() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); pp::FileRef file_ref(file_system, "/file_parallel_writes"); int32_t rv = file_system.Open(1024, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE | PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); // Parallel write operations. TestCompletionCallback callback_1(instance_->pp_instance(), force_async_); int32_t write_offset_1 = 0; const char* buf_1 = "abc"; int32_t size_1 = strlen(buf_1); TestCompletionCallback callback_2(instance_->pp_instance(), force_async_); int32_t write_offset_2 = size_1; const char* buf_2 = "defghijkl"; int32_t size_2 = 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) { rv_1 = file_io.Write(write_offset_1, buf_1, size_1, callback_1); if (rv_1 != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Write", rv_1); } if (size_2 > 0) { rv_2 = file_io.Write(write_offset_2, buf_2, size_2, callback_2); if (rv_2 != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Write", rv_2); } if (size_1 > 0) { rv_1 = callback_1.WaitForResult(); if (rv_1 <= 0) return ReportError("FileIO::Write", rv_1); write_offset_1 += rv_1; buf_1 += rv_1; size_1 -= rv_1; } if (size_2 > 0) { rv_2 = callback_2.WaitForResult(); if (rv_2 <= 0) return ReportError("FileIO::Write", rv_2); write_offset_2 += rv_2; buf_2 += rv_2; size_2 -= rv_2; } } // If |size_1| or |size_2| is less than 0, we have invoked wrong // callback(s). if (size_1 < 0 || size_2 < 0) { return std::string( "Parallel FileIO::Write operations have invoked wrong callbacks."); } // Check the file contents. std::string read_buffer; rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer); if (rv != PP_OK) return ReportError("FileIO::Read", rv); if (read_buffer != "abcdefghijkl") return ReportMismatch("FileIO::Read", read_buffer, "abcdefghijkl"); PASS(); } std::string TestFileIO::TestNotAllowMixedReadWrite() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); pp::FileRef file_ref(file_system, "/file_not_allow_mixed_read_write"); int32_t rv = file_system.Open(1024, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE | PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); // Cannot read and write in parallel. TestCompletionCallback callback_1(instance_->pp_instance(), force_async_); int32_t write_offset_1 = 0; const char* buf_1 = "mnopqrstuvw"; int32_t rv_1 = file_io.Write(write_offset_1, buf_1, strlen(buf_1), callback_1); if (rv_1 != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Write", rv_1); TestCompletionCallback callback_2(instance_->pp_instance(), force_async_); int32_t read_offset_2 = 4; char buf_2[3]; int32_t rv_2 = file_io.Read(read_offset_2, buf_2, sizeof(buf_2), callback_2); if (force_async_ && rv_2 != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Read force_async", rv_2); if (rv_2 == PP_OK_COMPLETIONPENDING) rv_2 = callback_2.WaitForResult(); if (rv_2 != PP_ERROR_INPROGRESS) return ReportError("FileIO::Read", rv_2); // Cannot query while the write is pending. TestCompletionCallback callback_3(instance_->pp_instance(), force_async_); PP_FileInfo info; int32_t rv_3 = file_io.Query(&info, callback_3); if (rv_3 == PP_OK_COMPLETIONPENDING) rv_3 = callback_3.WaitForResult(); if (rv_3 != PP_ERROR_INPROGRESS) return ReportError("FileIO::Query", rv_3); // Cannot touch while the write is pending. TestCompletionCallback callback_4(instance_->pp_instance(), force_async_); int32_t rv_4 = file_io.Touch(1234.0, 5678.0, callback_4); if (rv_4 == PP_OK_COMPLETIONPENDING) rv_4 = callback_4.WaitForResult(); if (rv_4 != PP_ERROR_INPROGRESS) return ReportError("FileIO::Touch", rv_4); // Cannot set length while the write is pending. TestCompletionCallback callback_5(instance_->pp_instance(), force_async_); int32_t rv_5 = file_io.SetLength(123, callback_5); if (rv_5 == PP_OK_COMPLETIONPENDING) rv_5 = callback_5.WaitForResult(); if (rv_5 != PP_ERROR_INPROGRESS) return ReportError("FileIO::SetLength", rv_5); callback_1.WaitForResult(); PASS(); } std::string TestFileIO::TestWillWriteWillSetLength() { TestCompletionCallback callback(instance_->pp_instance(), force_async_); pp::FileSystem file_system(instance_, PP_FILESYSTEMTYPE_LOCALTEMPORARY); pp::FileRef file_ref(file_system, "/file_will_write"); int32_t rv = file_system.Open(1024, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileSystem::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileSystem::Open", rv); pp::FileIO file_io(instance_); rv = file_io.Open(file_ref, PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_TRUNCATE | PP_FILEOPENFLAG_READ | PP_FILEOPENFLAG_WRITE, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); const PPB_FileIOTrusted* trusted = static_cast( pp::Module::Get()->GetBrowserInterface(PPB_FILEIOTRUSTED_INTERFACE)); if (!trusted) return ReportError("FileIOTrusted", PP_ERROR_FAILED); // Get file descriptor. This is only supported in-process for now, so don't // test out of process. const PPB_Testing_Dev* testing_interface = GetTestingInterface(); if (testing_interface && !testing_interface->IsOutOfProcess()) { int32_t fd = trusted->GetOSFileDescriptor(file_io.pp_resource()); if (fd < 0) return "FileIO::GetOSFileDescriptor() returned a bad file descriptor."; } // Calling WillWrite. rv = trusted->WillWrite( file_io.pp_resource(), 0, 9, static_cast(callback).pp_completion_callback()); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != 9) return ReportError("WillWrite", rv); // Writing the actual data. rv = WriteEntireBuffer(instance_->pp_instance(), &file_io, 0, "test_test"); if (rv != PP_OK) return ReportError("FileIO::Write", rv); std::string read_buffer; rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer); if (rv != PP_OK) return ReportError("FileIO::Read", rv); if (read_buffer != "test_test") return ReportMismatch("FileIO::Read", read_buffer, "test_test"); // Calling WillSetLength. rv = trusted->WillSetLength( file_io.pp_resource(), 4, static_cast(callback).pp_completion_callback()); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("WillSetLength", rv); // Calling actual SetLength. rv = file_io.SetLength(4, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::SetLength force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::SetLength", rv); read_buffer.clear(); rv = ReadEntireFile(instance_->pp_instance(), &file_io, 0, &read_buffer); if (rv != PP_OK) return ReportError("FileIO::Read", rv); if (read_buffer != "test") return ReportMismatch("FileIO::Read", read_buffer, "test"); PASS(); } std::string TestFileIO::MatchOpenExpectations(pp::FileSystem* file_system, size_t open_flags, size_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 and // DONT_ 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(), force_async_); 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. { int32_t rv = existent_file_ref.Delete(callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Delete force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND) return ReportError("FileRef::Delete", rv); rv = nonexistent_file_ref.Delete(callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileRef::Delete force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK && rv != PP_ERROR_FILENOTFOUND) return ReportError("FileRef::Delete", rv); pp::FileIO existent_file_io(instance_); rv = existent_file_io.Open(existent_file_ref, PP_FILEOPENFLAG_CREATE | PP_FILEOPENFLAG_WRITE, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Open", rv); rv = WriteEntireBuffer(instance_->pp_instance(), &existent_file_io, 0, "foobar"); if (rv != PP_OK) return ReportError("FileIO::Write", rv); } pp::FileIO existent_file_io(instance_); int32_t rv = existent_file_io.Open(existent_file_ref, open_flags, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if ((invalid_combination && rv == PP_OK) || (!invalid_combination && ((rv == PP_OK) != open_if_exists))) { return ReportOpenError(open_flags); } if (!invalid_combination && open_if_exists) { PP_FileInfo info; rv = existent_file_io.Query(&info, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Query force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if (rv != PP_OK) return ReportError("FileIO::Query", rv); if (truncate_if_exists != (info.size == 0)) return ReportOpenError(open_flags); } pp::FileIO nonexistent_file_io(instance_); rv = nonexistent_file_io.Open(nonexistent_file_ref, open_flags, callback); if (force_async_ && rv != PP_OK_COMPLETIONPENDING) return ReportError("FileIO::Open force_async", rv); if (rv == PP_OK_COMPLETIONPENDING) rv = callback.WaitForResult(); if ((invalid_combination && rv == PP_OK) || (!invalid_combination && ((rv == PP_OK) != create_if_doesnt_exist))) { return ReportOpenError(open_flags); } return std::string(); } // TODO(viettrungluu): Test Close(). crbug.com/69457