diff options
author | hidehiko@chromium.org <hidehiko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-23 01:14:21 +0000 |
---|---|---|
committer | hidehiko@chromium.org <hidehiko@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-23 01:14:21 +0000 |
commit | 1b2fc951d33ce93f4ba12743c6a8f405a2418126 (patch) | |
tree | e1ede187fe56cfbfc7ebd3e97f9ac96747042f37 /chrome/browser/chromeos | |
parent | 0852f829239a64c744dafdd29ffdc0f555b78a69 (diff) | |
download | chromium_src-1b2fc951d33ce93f4ba12743c6a8f405a2418126.zip chromium_src-1b2fc951d33ce93f4ba12743c6a8f405a2418126.tar.gz chromium_src-1b2fc951d33ce93f4ba12743c6a8f405a2418126.tar.bz2 |
Implement DriveFileStreamReader.
Simplified the interface by getting rid of the inheritance of
webkit_blog::FileStreamReader.
BUG=127129
TEST=Ran unit_tests.
Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=195504
Review URL: https://chromiumcodereview.appspot.com/14365018
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@195681 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/chromeos')
3 files changed, 380 insertions, 22 deletions
diff --git a/chrome/browser/chromeos/drive/drive_file_stream_reader.cc b/chrome/browser/chromeos/drive/drive_file_stream_reader.cc index 66e52e3..2a20415 100644 --- a/chrome/browser/chromeos/drive/drive_file_stream_reader.cc +++ b/chrome/browser/chromeos/drive/drive_file_stream_reader.cc @@ -9,6 +9,9 @@ #include "base/callback_helpers.h" #include "base/logging.h" +#include "chrome/browser/chromeos/drive/drive.pb.h" +#include "chrome/browser/chromeos/drive/drive_file_system_interface.h" +#include "chrome/browser/google_apis/task_util.h" #include "content/public/browser/browser_thread.h" #include "net/base/file_stream.h" #include "net/base/io_buffer.h" @@ -166,25 +169,178 @@ void NetworkReaderProxy::OnError(DriveFileError error) { } // namespace internal +namespace { + +// Calls DriveFileSystemInterface::GetFileContentByPath if the file system +// is available. If not, the |completion_callback| is invoked with +// DRIVE_FILE_ERROR_FAILED. +void GetFileContentByPathOnUIThread( + const DriveFileStreamReader::DriveFileSystemGetter& file_system_getter, + const base::FilePath& drive_file_path, + const GetFileContentInitializedCallback& initialized_callback, + const google_apis::GetContentCallback& get_content_callback, + const FileOperationCallback& completion_callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); + + DriveFileSystemInterface* file_system = file_system_getter.Run(); + if (!file_system) { + completion_callback.Run(DRIVE_FILE_ERROR_FAILED); + return; + } -DriveFileStreamReader::DriveFileStreamReader() { + file_system->GetFileContentByPath(drive_file_path, + initialized_callback, + get_content_callback, + completion_callback); +} + +// Helper to run DriveFileSystemInterface::GetFileContentByPath on UI thread. +void GetFileContentByPath( + const DriveFileStreamReader::DriveFileSystemGetter& file_system_getter, + const base::FilePath& drive_file_path, + const GetFileContentInitializedCallback& initialized_callback, + const google_apis::GetContentCallback& get_content_callback, + const FileOperationCallback& completion_callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + BrowserThread::PostTask( + BrowserThread::UI, + FROM_HERE, + base::Bind(&GetFileContentByPathOnUIThread, + file_system_getter, + drive_file_path, + google_apis::CreateRelayCallback(initialized_callback), + google_apis::CreateRelayCallback(get_content_callback), + google_apis::CreateRelayCallback(completion_callback))); +} + +} // namespace + +DriveFileStreamReader::DriveFileStreamReader( + const DriveFileSystemGetter& drive_file_system_getter) + : drive_file_system_getter_(drive_file_system_getter), + ALLOW_THIS_IN_INITIALIZER_LIST(weak_ptr_factory_(this)) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); } DriveFileStreamReader::~DriveFileStreamReader() { } -int DriveFileStreamReader::Read(net::IOBuffer* buf, int buf_len, +void DriveFileStreamReader::Initialize( + const base::FilePath& drive_file_path, + const InitializeCompletionCallback& callback) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(!callback.is_null()); + + GetFileContentByPath( + drive_file_system_getter_, + drive_file_path, + base::Bind(&DriveFileStreamReader + ::InitializeAfterGetFileContentByPathInitialized, + weak_ptr_factory_.GetWeakPtr(), + callback), + base::Bind(&DriveFileStreamReader::OnGetContent, + weak_ptr_factory_.GetWeakPtr()), + base::Bind(&DriveFileStreamReader::OnGetFileContentByPathCompletion, + weak_ptr_factory_.GetWeakPtr(), + callback)); +} + +int DriveFileStreamReader::Read(net::IOBuffer* buffer, int buffer_length, const net::CompletionCallback& callback) { - // TODO(hidehiko): Implement this. - NOTIMPLEMENTED(); - return 0; + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(reader_proxy_); + DCHECK(buffer); + DCHECK(!callback.is_null()); + return reader_proxy_->Read(buffer, buffer_length, callback); +} + +void DriveFileStreamReader::InitializeAfterGetFileContentByPathInitialized( + const InitializeCompletionCallback& callback, + DriveFileError error, + scoped_ptr<DriveEntryProto> entry, + const base::FilePath& drive_file_path) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (error != DRIVE_FILE_OK) { + callback.Run(error, scoped_ptr<DriveEntryProto>()); + return; + } + DCHECK(entry); + + if (drive_file_path.empty()) { + // The file is not cached, and being downloaded. + reader_proxy_.reset( + new internal::NetworkReaderProxy(entry->file_info().size())); + callback.Run(DRIVE_FILE_OK, entry.Pass()); + return; + } + + // Otherwise, open the stream for file. + scoped_ptr<net::FileStream> file_stream(new net::FileStream(NULL)); + net::FileStream* file_stream_ptr = file_stream.get(); + net::CompletionCallback open_completion_callback = base::Bind( + &DriveFileStreamReader::InitializeAfterLocalFileOpen, + weak_ptr_factory_.GetWeakPtr(), + callback, + base::Passed(&entry), + base::Passed(&file_stream)); + int result = file_stream_ptr->Open( + drive_file_path, + base::PLATFORM_FILE_OPEN | base::PLATFORM_FILE_READ | + base::PLATFORM_FILE_ASYNC, + open_completion_callback); + + if (result == net::ERR_IO_PENDING) { + // If the result ERR_IO_PENDING, the callback will be invoked later. + // Do nothing here. + return; + } + + open_completion_callback.Run(result); } -int64 DriveFileStreamReader::GetLength( - const net::Int64CompletionCallback& callback) { - // TODO(hidehiko): Implement this. - NOTIMPLEMENTED(); - return 0; +void DriveFileStreamReader::InitializeAfterLocalFileOpen( + const InitializeCompletionCallback& callback, + scoped_ptr<DriveEntryProto> entry, + scoped_ptr<net::FileStream> file_stream, + int open_result) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (open_result != net::OK) { + callback.Run(DRIVE_FILE_ERROR_FAILED, scoped_ptr<DriveEntryProto>()); + return; + } + + reader_proxy_.reset(new internal::LocalReaderProxy(file_stream.Pass())); + callback.Run(DRIVE_FILE_OK, entry.Pass()); +} + +void DriveFileStreamReader::OnGetContent(google_apis::GDataErrorCode error_code, + scoped_ptr<std::string> data) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + DCHECK(reader_proxy_); + reader_proxy_->OnGetContent(data.Pass()); +} + +void DriveFileStreamReader::OnGetFileContentByPathCompletion( + const InitializeCompletionCallback& callback, + DriveFileError error) { + DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); + + if (error == DRIVE_FILE_OK) { + // We are interested in only errors. + return; + } + + if (reader_proxy_) { + // If the proxy object available, send the error to it. + reader_proxy_->OnError(error); + } else { + // Here, this callback is invoked in the initialization process. + // So let the client know via initialization callback. + callback.Run(error, scoped_ptr<DriveEntryProto>()); + } } } // namespace drive diff --git a/chrome/browser/chromeos/drive/drive_file_stream_reader.h b/chrome/browser/chromeos/drive/drive_file_stream_reader.h index 8100fcd..f69fdaf 100644 --- a/chrome/browser/chromeos/drive/drive_file_stream_reader.h +++ b/chrome/browser/chromeos/drive/drive_file_stream_reader.h @@ -8,11 +8,12 @@ #include <string> #include "base/basictypes.h" +#include "base/callback.h" #include "base/memory/scoped_ptr.h" #include "base/memory/scoped_vector.h" #include "chrome/browser/chromeos/drive/drive_file_error.h" +#include "chrome/browser/google_apis/gdata_errorcode.h" #include "net/base/completion_callback.h" -#include "webkit/blob/file_stream_reader.h" namespace net { class FileStream; @@ -92,23 +93,82 @@ class NetworkReaderProxy : public ReaderProxy { } // namespace internal -// TODO(hidehiko): Simplify the interface by getting rid of -// webkit_blob::FileStreamReader inheritance. -class DriveFileStreamReader : public webkit_blob::FileStreamReader { - public: - DriveFileStreamReader(); - virtual ~DriveFileStreamReader(); +class DriveFileSystemInterface; +class DriveEntryProto; - // webkit_blob::FileStreamReader overrides. - virtual int Read(net::IOBuffer* buf, int buf_len, - const net::CompletionCallback& callback) OVERRIDE; - virtual int64 GetLength( - const net::Int64CompletionCallback& callback) OVERRIDE; +// The stream reader for a file in DriveFileSystem. Instances of this class +// should live on IO thread. +class DriveFileStreamReader { + public: + // Callback to return the DriveFileSystemInterface instance. This is an + // injecting point for testing. + // Note that the callback will be copied between threads (IO and UI), and + // will be called on UI thread. + typedef base::Callback<DriveFileSystemInterface*()> DriveFileSystemGetter; + + // Callback to return the result of Initialize(). + typedef base::Callback<void(DriveFileError error, + scoped_ptr<DriveEntryProto> entry)> + InitializeCompletionCallback; + + explicit DriveFileStreamReader( + const DriveFileSystemGetter& drive_file_system_getter); + ~DriveFileStreamReader(); + + // Initializes the stream for the |drive_file_path|. + // |callback| must not be null. + // TODO(hidehiko): Support reading range (crbug.com/168258). + void Initialize(const base::FilePath& drive_file_path, + const InitializeCompletionCallback& callback); + + // Reads the data into |buffer| at most |buffer_length|, and returns + // the number of bytes. If an error happened, returns an error code. + // If no data is available yet, returns net::ERR_IO_PENDING immediately, + // and when the data is available the actual Read operation is done + // and |callback| will be run with the result. + // The Read() method must not be called before the Initialize() is completed + // successfully, or if there is pending read operation. + // Neither |buffer| nor |callback| must be null. + int Read(net::IOBuffer* buffer, int buffer_length, + const net::CompletionCallback& callback); private: + // Part of Initialize. Called after GetFileContentByPath's initialization + // is done. + void InitializeAfterGetFileContentByPathInitialized( + const InitializeCompletionCallback& callback, + DriveFileError error, + scoped_ptr<DriveEntryProto> entry, + const base::FilePath& drive_file_path); + + // Part of Initialize. Called when the local file open process is done. + void InitializeAfterLocalFileOpen( + const InitializeCompletionCallback& callback, + scoped_ptr<DriveEntryProto> entry, + scoped_ptr<net::FileStream> file_stream, + int open_result); + + // Called when the data is received from the server. + void OnGetContent(google_apis::GDataErrorCode error_code, + scoped_ptr<std::string> data); + + // Called when GetFileContentByPath is completed. + void OnGetFileContentByPathCompletion( + const InitializeCompletionCallback& callback, + DriveFileError error); + + const DriveFileSystemGetter drive_file_system_getter_; + scoped_ptr<internal::ReaderProxy> reader_proxy_; + + // This should remain the last member so it'll be destroyed first and + // invalidate its weak pointers before other members are destroyed. + base::WeakPtrFactory<DriveFileStreamReader> weak_ptr_factory_; DISALLOW_COPY_AND_ASSIGN(DriveFileStreamReader); }; +// TODO(hidehiko): Add thin wrapper class inheriting +// webkit_blob::FileStreamReader for the DriveFileStreamReader. + } // namespace drive #endif // CHROME_BROWSER_CHROMEOS_DRIVE_DRIVE_FILE_STREAM_READER_H_ diff --git a/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc b/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc index 37f36b3..c24de06 100644 --- a/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc +++ b/chrome/browser/chromeos/drive/drive_file_stream_reader_unittest.cc @@ -6,9 +6,14 @@ #include <string> +#include "base/bind.h" #include "base/file_util.h" #include "base/files/file_path.h" #include "base/message_loop.h" +#include "chrome/browser/chromeos/drive/drive_file_system_util.h" +#include "chrome/browser/chromeos/drive/fake_drive_file_system.h" +#include "chrome/browser/google_apis/fake_drive_service.h" +#include "chrome/browser/google_apis/task_util.h" #include "chrome/browser/google_apis/test_util.h" #include "content/public/test/test_browser_thread.h" #include "net/base/file_stream.h" @@ -182,4 +187,141 @@ TEST(NetworkReaderProxyTest, ErrorWithPendingData) { } } // namespace internal + +class DriveFileStreamReaderTest : public ::testing::Test { + protected: + DriveFileStreamReaderTest() + : ui_thread_(BrowserThread::UI), + io_thread_(BrowserThread::IO, &message_loop_) { + } + + virtual void SetUp() OVERRIDE { + ui_thread_.Start(); + + BrowserThread::PostTaskAndReply( + BrowserThread::UI, + FROM_HERE, + base::Bind(&DriveFileStreamReaderTest::SetUpOnUIThread, + base::Unretained(this)), + base::MessageLoop::QuitClosure()); + message_loop_.Run(); + } + + virtual void TearDown() OVERRIDE { + BrowserThread::PostTaskAndReply( + BrowserThread::UI, + FROM_HERE, + base::Bind(&DriveFileStreamReaderTest::TearDownOnUIThread, + base::Unretained(this)), + base::MessageLoop::QuitClosure()); + message_loop_.Run(); + } + + void SetUpOnUIThread() { + // Initialize FakeDriveService. + fake_drive_service_.reset(new google_apis::FakeDriveService); + fake_drive_service_->LoadResourceListForWapi( + "chromeos/gdata/root_feed.json"); + fake_drive_service_->LoadAccountMetadataForWapi( + "chromeos/gdata/account_metadata.json"); + fake_drive_service_->LoadAppListForDriveApi("chromeos/drive/applist.json"); + + // Create a testee instance. + fake_drive_file_system_.reset( + new test_util::FakeDriveFileSystem(fake_drive_service_.get())); + fake_drive_file_system_->Initialize(); + } + + void TearDownOnUIThread() { + fake_drive_file_system_.reset(); + fake_drive_service_.reset(); + } + + DriveFileSystemInterface* GetDriveFileSystem() { + return fake_drive_file_system_.get(); + } + + DriveFileStreamReader::DriveFileSystemGetter GetDriveFileSystemGetter() { + return base::Bind(&DriveFileStreamReaderTest::GetDriveFileSystem, + base::Unretained(this)); + } + + MessageLoopForIO message_loop_; + content::TestBrowserThread ui_thread_; + content::TestBrowserThread io_thread_; + + scoped_ptr<google_apis::FakeDriveService> fake_drive_service_; + scoped_ptr<test_util::FakeDriveFileSystem> fake_drive_file_system_; +}; + +TEST_F(DriveFileStreamReaderTest, Read) { + const base::FilePath kDriveFile = + util::GetDriveMyDriveRootPath().AppendASCII("File 1.txt"); + net::TestCompletionCallback callback; + const int kBufferSize = 3; + scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(kBufferSize)); + + // Create the reader, and initialize it. + // In this case, the file is not yet locally cached. + scoped_ptr<DriveFileStreamReader> reader( + new DriveFileStreamReader(GetDriveFileSystemGetter())); + + DriveFileError error = DRIVE_FILE_ERROR_FAILED; + scoped_ptr<DriveEntryProto> entry; + reader->Initialize( + kDriveFile, + google_apis::CreateComposedCallback( + base::Bind(&google_apis::test_util::RunAndQuit), + google_apis::test_util::CreateCopyResultCallback( + &error, &entry))); + message_loop_.Run(); + EXPECT_EQ(DRIVE_FILE_OK, error); + ASSERT_TRUE(entry); + + // Read data from the reader. + size_t content_size = entry->file_info().size(); + std::string first_content; + while (first_content.size() < content_size) { + int result = reader->Read(buffer.get(), kBufferSize, callback.callback()); + result = callback.GetResult(result); + ASSERT_GT(result, 0); + first_content.append(buffer->data(), result); + } + + EXPECT_EQ(content_size, first_content.size()); + + // Create second instance and initialize it. + // In this case, the file should be cached one. + reader.reset( + new DriveFileStreamReader(GetDriveFileSystemGetter())); + + error = DRIVE_FILE_ERROR_FAILED; + entry.reset(); + reader->Initialize( + kDriveFile, + google_apis::CreateComposedCallback( + base::Bind(&google_apis::test_util::RunAndQuit), + google_apis::test_util::CreateCopyResultCallback( + &error, &entry))); + message_loop_.Run(); + EXPECT_EQ(DRIVE_FILE_OK, error); + ASSERT_TRUE(entry); + + // The size should be same. + EXPECT_EQ(content_size, + static_cast<size_t>(entry->file_info().size())); + + // Read data from the reader, again. + std::string second_content; + while (second_content.size() < content_size) { + int result = reader->Read(buffer.get(), kBufferSize, callback.callback()); + result = callback.GetResult(result); + ASSERT_GT(result, 0); + second_content.append(buffer->data(), result); + } + + // The same content is expected. + EXPECT_EQ(first_content, second_content); +} + } // namespace drive |