1// Copyright 2014 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 "storage/browser/fileapi/local_file_stream_writer.h" 6 7#include <string> 8 9#include "base/callback.h" 10#include "base/files/file_util.h" 11#include "base/files/scoped_temp_dir.h" 12#include "base/logging.h" 13#include "base/memory/scoped_ptr.h" 14#include "base/message_loop/message_loop.h" 15#include "base/run_loop.h" 16#include "base/threading/thread.h" 17#include "net/base/io_buffer.h" 18#include "net/base/test_completion_callback.h" 19#include "testing/gtest/include/gtest/gtest.h" 20 21using storage::FileStreamWriter; 22using storage::LocalFileStreamWriter; 23 24namespace content { 25 26class LocalFileStreamWriterTest : public testing::Test { 27 public: 28 LocalFileStreamWriterTest() 29 : file_thread_("FileUtilProxyTestFileThread") {} 30 31 virtual void SetUp() OVERRIDE { 32 ASSERT_TRUE(file_thread_.Start()); 33 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); 34 } 35 36 virtual void TearDown() OVERRIDE { 37 // Give another chance for deleted streams to perform Close. 38 base::RunLoop().RunUntilIdle(); 39 file_thread_.Stop(); 40 base::RunLoop().RunUntilIdle(); 41 } 42 43 protected: 44 base::FilePath Path(const std::string& name) { 45 return temp_dir_.path().AppendASCII(name); 46 } 47 48 int WriteStringToWriter(LocalFileStreamWriter* writer, 49 const std::string& data) { 50 scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer(data)); 51 scoped_refptr<net::DrainableIOBuffer> drainable( 52 new net::DrainableIOBuffer(buffer.get(), buffer->size())); 53 54 while (drainable->BytesRemaining() > 0) { 55 net::TestCompletionCallback callback; 56 int result = writer->Write( 57 drainable.get(), drainable->BytesRemaining(), callback.callback()); 58 if (result == net::ERR_IO_PENDING) 59 result = callback.WaitForResult(); 60 if (result <= 0) 61 return result; 62 drainable->DidConsume(result); 63 } 64 return net::OK; 65 } 66 67 std::string GetFileContent(const base::FilePath& path) { 68 std::string content; 69 base::ReadFileToString(path, &content); 70 return content; 71 } 72 73 base::FilePath CreateFileWithContent(const std::string& name, 74 const std::string& data) { 75 base::FilePath path = Path(name); 76 base::WriteFile(path, data.c_str(), data.size()); 77 return path; 78 } 79 80 base::MessageLoopProxy* file_task_runner() const { 81 return file_thread_.message_loop_proxy().get(); 82 } 83 84 LocalFileStreamWriter* CreateWriter(const base::FilePath& path, 85 int64 offset) { 86 return new LocalFileStreamWriter(file_task_runner(), path, offset, 87 FileStreamWriter::OPEN_EXISTING_FILE); 88 } 89 90 private: 91 base::MessageLoopForIO message_loop_; 92 base::Thread file_thread_; 93 base::ScopedTempDir temp_dir_; 94}; 95 96void NeverCalled(int unused) { 97 ADD_FAILURE(); 98} 99 100TEST_F(LocalFileStreamWriterTest, Write) { 101 base::FilePath path = CreateFileWithContent("file_a", std::string()); 102 scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 0)); 103 EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo")); 104 EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "bar")); 105 writer.reset(); 106 base::RunLoop().RunUntilIdle(); 107 EXPECT_TRUE(base::PathExists(path)); 108 EXPECT_EQ("foobar", GetFileContent(path)); 109} 110 111TEST_F(LocalFileStreamWriterTest, WriteMiddle) { 112 base::FilePath path = CreateFileWithContent("file_a", "foobar"); 113 scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 2)); 114 EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx")); 115 writer.reset(); 116 base::RunLoop().RunUntilIdle(); 117 EXPECT_TRUE(base::PathExists(path)); 118 EXPECT_EQ("foxxxr", GetFileContent(path)); 119} 120 121TEST_F(LocalFileStreamWriterTest, WriteEnd) { 122 base::FilePath path = CreateFileWithContent("file_a", "foobar"); 123 scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 6)); 124 EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "xxx")); 125 writer.reset(); 126 base::RunLoop().RunUntilIdle(); 127 EXPECT_TRUE(base::PathExists(path)); 128 EXPECT_EQ("foobarxxx", GetFileContent(path)); 129} 130 131TEST_F(LocalFileStreamWriterTest, WriteFailForNonexistingFile) { 132 base::FilePath path = Path("file_a"); 133 ASSERT_FALSE(base::PathExists(path)); 134 scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 0)); 135 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, WriteStringToWriter(writer.get(), "foo")); 136 writer.reset(); 137 base::RunLoop().RunUntilIdle(); 138 EXPECT_FALSE(base::PathExists(path)); 139} 140 141TEST_F(LocalFileStreamWriterTest, CancelBeforeOperation) { 142 base::FilePath path = Path("file_a"); 143 scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 0)); 144 // Cancel immediately fails when there's no in-flight operation. 145 int cancel_result = writer->Cancel(base::Bind(&NeverCalled)); 146 EXPECT_EQ(net::ERR_UNEXPECTED, cancel_result); 147} 148 149TEST_F(LocalFileStreamWriterTest, CancelAfterFinishedOperation) { 150 base::FilePath path = CreateFileWithContent("file_a", std::string()); 151 scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 0)); 152 EXPECT_EQ(net::OK, WriteStringToWriter(writer.get(), "foo")); 153 154 // Cancel immediately fails when there's no in-flight operation. 155 int cancel_result = writer->Cancel(base::Bind(&NeverCalled)); 156 EXPECT_EQ(net::ERR_UNEXPECTED, cancel_result); 157 158 writer.reset(); 159 base::RunLoop().RunUntilIdle(); 160 // Write operation is already completed. 161 EXPECT_TRUE(base::PathExists(path)); 162 EXPECT_EQ("foo", GetFileContent(path)); 163} 164 165TEST_F(LocalFileStreamWriterTest, CancelWrite) { 166 base::FilePath path = CreateFileWithContent("file_a", "foobar"); 167 scoped_ptr<LocalFileStreamWriter> writer(CreateWriter(path, 0)); 168 169 scoped_refptr<net::StringIOBuffer> buffer(new net::StringIOBuffer("xxx")); 170 int result = 171 writer->Write(buffer.get(), buffer->size(), base::Bind(&NeverCalled)); 172 ASSERT_EQ(net::ERR_IO_PENDING, result); 173 174 net::TestCompletionCallback callback; 175 writer->Cancel(callback.callback()); 176 int cancel_result = callback.WaitForResult(); 177 EXPECT_EQ(net::OK, cancel_result); 178} 179 180} // namespace content 181