Index: content/browser/renderer_host/websocket_blob_sender_unittest.cc |
diff --git a/content/browser/renderer_host/websocket_blob_sender_unittest.cc b/content/browser/renderer_host/websocket_blob_sender_unittest.cc |
deleted file mode 100644 |
index 590c914bdc032033ffc40354ad012af60282fe70..0000000000000000000000000000000000000000 |
--- a/content/browser/renderer_host/websocket_blob_sender_unittest.cc |
+++ /dev/null |
@@ -1,450 +0,0 @@ |
-// Copyright 2016 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 "content/browser/renderer_host/websocket_blob_sender.h" |
- |
-#include <string.h> |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/callback.h" |
-#include "base/files/file.h" |
-#include "base/files/file_path.h" |
-#include "base/files/file_util.h" |
-#include "base/files/scoped_temp_dir.h" |
-#include "base/location.h" |
-#include "base/memory/ptr_util.h" |
-#include "base/memory/weak_ptr.h" |
-#include "base/message_loop/message_loop.h" |
-#include "base/run_loop.h" |
-#include "base/single_thread_task_runner.h" |
-#include "base/task_runner.h" |
-#include "base/threading/thread_task_runner_handle.h" |
-#include "base/time/time.h" |
-#include "content/browser/blob_storage/chrome_blob_storage_context.h" |
-#include "content/public/browser/blob_handle.h" |
-#include "content/public/browser/browser_thread.h" |
-#include "content/public/browser/storage_partition.h" |
-#include "content/public/test/test_browser_context.h" |
-#include "content/public/test/test_browser_thread_bundle.h" |
-#include "net/base/completion_callback.h" |
-#include "net/base/net_errors.h" |
-#include "net/base/test_completion_callback.h" |
-#include "storage/common/fileapi/file_system_types.h" |
-#include "testing/gtest/include/gtest/gtest.h" |
-#include "url/gurl.h" |
- |
-namespace content { |
- |
-namespace { |
- |
-const char kDummyUrl[] = "http://www.example.com/"; |
-const char kBanana[] = "banana"; |
- |
-// This is small so that the tests do not waste too much time just copying bytes |
-// around. But it has to be larger than kMinimumNonFinalFrameSize defined in |
-// websocket_blob_sender.cc. |
-const size_t kInitialQuota = 16 * 1024; |
- |
-using net::TestCompletionCallback; |
- |
-// A fake channel for testing. Records the contents of the message that was sent |
-// through it. Quota is restricted, and is refreshed asynchronously in response |
-// to calls to SendFrame(). |
-class FakeChannel : public WebSocketBlobSender::Channel { |
- public: |
- // |notify_new_quota| will be run asynchronously on the current MessageLoop |
- // every time GetSendQuota() increases. |
- FakeChannel() : weak_factory_(this) {} |
- |
- // This method must be called before SendFrame() is. |
- void set_notify_new_quota(const base::Closure& notify_new_quota) { |
- notify_new_quota_ = notify_new_quota; |
- } |
- |
- size_t GetSendQuota() const override { return current_send_quota_; } |
- |
- ChannelState SendFrame(bool fin, const std::vector<char>& data) override { |
- ++frames_sent_; |
- EXPECT_FALSE(got_fin_); |
- if (fin) |
- got_fin_ = true; |
- EXPECT_LE(data.size(), current_send_quota_); |
- message_.insert(message_.end(), data.begin(), data.end()); |
- current_send_quota_ -= data.size(); |
- base::ThreadTaskRunnerHandle::Get()->PostTask( |
- FROM_HERE, |
- base::Bind(&FakeChannel::RefreshQuota, weak_factory_.GetWeakPtr())); |
- return net::WebSocketEventInterface::CHANNEL_ALIVE; |
- } |
- |
- bool got_fin() const { return got_fin_; } |
- |
- int frames_sent() const { return frames_sent_; } |
- |
- const std::vector<char>& message() const { return message_; } |
- |
- private: |
- void RefreshQuota() { |
- if (current_send_quota_ == kInitialQuota) |
- return; |
- current_send_quota_ = kInitialQuota; |
- DCHECK(!notify_new_quota_.is_null()); |
- notify_new_quota_.Run(); |
- } |
- |
- base::Closure notify_new_quota_; |
- size_t current_send_quota_ = kInitialQuota; |
- int frames_sent_ = 0; |
- bool got_fin_ = false; |
- std::vector<char> message_; |
- base::WeakPtrFactory<FakeChannel> weak_factory_; |
-}; |
- |
-class WebSocketBlobSenderTest : public ::testing::Test { |
- protected: |
- // The Windows implementation of net::FileStream::Context requires a real IO |
- // MessageLoop. |
- WebSocketBlobSenderTest() |
- : threads_(TestBrowserThreadBundle::IO_MAINLOOP), |
- chrome_blob_storage_context_( |
- ChromeBlobStorageContext::GetFor(&browser_context_)), |
- fake_channel_(nullptr), |
- sender_() {} |
- ~WebSocketBlobSenderTest() override {} |
- |
- void SetUp() override { |
- // ChromeBlobStorageContext::GetFor() does some work asynchronously. |
- base::RunLoop().RunUntilIdle(); |
- SetUpSender(); |
- } |
- |
- // This method can be overriden to use a different channel implementation. |
- virtual void SetUpSender() { |
- fake_channel_ = new FakeChannel; |
- sender_.reset(new WebSocketBlobSender(base::WrapUnique(fake_channel_))); |
- fake_channel_->set_notify_new_quota(base::Bind( |
- &WebSocketBlobSender::OnNewSendQuota, base::Unretained(sender_.get()))); |
- } |
- |
- storage::BlobStorageContext* context() { |
- return chrome_blob_storage_context_->context(); |
- } |
- |
- storage::FileSystemContext* GetFileSystemContext() { |
- StoragePartition* partition = BrowserContext::GetStoragePartitionForSite( |
- &browser_context_, GURL(kDummyUrl)); |
- return partition->GetFileSystemContext(); |
- } |
- |
- // |string| is copied. |
- std::unique_ptr<BlobHandle> CreateMemoryBackedBlob(const char* string) { |
- std::unique_ptr<BlobHandle> handle = |
- chrome_blob_storage_context_->CreateMemoryBackedBlob(string, |
- strlen(string)); |
- EXPECT_TRUE(handle); |
- return handle; |
- } |
- |
- // Call sender_.Start() with the other parameters filled in appropriately for |
- // this test fixture. |
- int Start(const std::string& uuid, |
- uint64_t expected_size, |
- const net::CompletionCallback& callback) { |
- net::WebSocketEventInterface::ChannelState channel_state = |
- net::WebSocketEventInterface::CHANNEL_ALIVE; |
- return sender_->Start( |
- uuid, expected_size, context(), GetFileSystemContext(), |
- BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get(), |
- &channel_state, callback); |
- } |
- |
- void NotCalledCallbackImpl(int rv) { |
- ADD_FAILURE() |
- << "Callback that should not be called was called with argument " << rv; |
- } |
- |
- net::CompletionCallback NotCalled() { |
- return base::Bind(&WebSocketBlobSenderTest::NotCalledCallbackImpl, |
- base::Unretained(this)); |
- } |
- |
- void ExpectOkAndQuit(base::RunLoop* run_loop, int result) { |
- EXPECT_EQ(net::OK, result); |
- run_loop->Quit(); |
- } |
- |
- net::CompletionCallback ExpectOkAndQuitCallback(base::RunLoop* run_loop) { |
- return base::Bind(&WebSocketBlobSenderTest::ExpectOkAndQuit, |
- base::Unretained(this), run_loop); |
- } |
- |
- TestBrowserThreadBundle threads_; |
- TestBrowserContext browser_context_; |
- scoped_refptr<ChromeBlobStorageContext> chrome_blob_storage_context_; |
- // |fake_channel_| is owned by |sender_|. |
- FakeChannel* fake_channel_; |
- std::unique_ptr<WebSocketBlobSender> sender_; |
-}; |
- |
-TEST_F(WebSocketBlobSenderTest, Construction) {} |
- |
-TEST_F(WebSocketBlobSenderTest, EmptyBlob) { |
- std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(""); |
- |
- // The APIs allow for this to be asynchronous but that is unlikely in |
- // practice. |
- int result = Start(handle->GetUUID(), UINT64_C(0), NotCalled()); |
- // If this fails with result == -1, someone has changed the code to be |
- // asynchronous and this test should be adapted to match. |
- EXPECT_EQ(net::OK, result); |
- EXPECT_TRUE(fake_channel_->got_fin()); |
- EXPECT_EQ(0U, fake_channel_->message().size()); |
-} |
- |
-TEST_F(WebSocketBlobSenderTest, SmallBlob) { |
- std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(kBanana); |
- |
- EXPECT_EQ(net::OK, Start(handle->GetUUID(), UINT64_C(6), NotCalled())); |
- EXPECT_TRUE(fake_channel_->got_fin()); |
- EXPECT_EQ(1, fake_channel_->frames_sent()); |
- EXPECT_EQ(std::vector<char>(kBanana, kBanana + 6), fake_channel_->message()); |
-} |
- |
-TEST_F(WebSocketBlobSenderTest, SizeMismatch) { |
- std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(kBanana); |
- |
- EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, |
- Start(handle->GetUUID(), UINT64_C(5), NotCalled())); |
- EXPECT_EQ(0, fake_channel_->frames_sent()); |
-} |
- |
-TEST_F(WebSocketBlobSenderTest, InvalidUUID) { |
- EXPECT_EQ(net::ERR_INVALID_HANDLE, |
- Start("sandwich", UINT64_C(0), NotCalled())); |
-} |
- |
-TEST_F(WebSocketBlobSenderTest, LargeMessage) { |
- std::string message(kInitialQuota + 10, 'a'); |
- std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(message.c_str()); |
- |
- base::RunLoop run_loop; |
- int rv = Start(handle->GetUUID(), message.size(), |
- ExpectOkAndQuitCallback(&run_loop)); |
- EXPECT_EQ(net::ERR_IO_PENDING, rv); |
- EXPECT_EQ(1, fake_channel_->frames_sent()); |
- run_loop.Run(); |
- EXPECT_EQ(2, fake_channel_->frames_sent()); |
- EXPECT_TRUE(fake_channel_->got_fin()); |
- std::vector<char> expected_message(message.begin(), message.end()); |
- EXPECT_EQ(expected_message, fake_channel_->message()); |
-} |
- |
-// A message exactly equal to the available quota should be sent in one frame. |
-TEST_F(WebSocketBlobSenderTest, ExactSizeMessage) { |
- std::string message(kInitialQuota, 'a'); |
- std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(message.c_str()); |
- |
- EXPECT_EQ(net::OK, Start(handle->GetUUID(), message.size(), NotCalled())); |
- EXPECT_EQ(1, fake_channel_->frames_sent()); |
- EXPECT_TRUE(fake_channel_->got_fin()); |
- std::vector<char> expected_message(message.begin(), message.end()); |
- EXPECT_EQ(expected_message, fake_channel_->message()); |
-} |
- |
-// If the connection is closed while sending a message, the WebSocketBlobSender |
-// object will be destroyed. It needs to handle this case without error. |
-TEST_F(WebSocketBlobSenderTest, AbortedSend) { |
- std::string message(kInitialQuota + 10, 'a'); |
- std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(message.c_str()); |
- |
- int rv = Start(handle->GetUUID(), message.size(), NotCalled()); |
- EXPECT_EQ(net::ERR_IO_PENDING, rv); |
- sender_.reset(); |
-} |
- |
-// Invalid file-backed blob. |
-TEST_F(WebSocketBlobSenderTest, InvalidFileBackedBlob) { |
- base::FilePath path(FILE_PATH_LITERAL( |
- "WebSocketBlobSentTest.InvalidFileBackedBlob.NonExistentFile")); |
- std::unique_ptr<BlobHandle> handle = |
- chrome_blob_storage_context_->CreateFileBackedBlob(path, 0u, 32u, |
- base::Time::Now()); |
- EXPECT_TRUE(handle); |
- |
- TestCompletionCallback callback; |
- int rv = |
- callback.GetResult(Start(handle->GetUUID(), 5u, callback.callback())); |
- EXPECT_EQ(net::ERR_FILE_NOT_FOUND, rv); |
-} |
- |
-// A test fixture that does the additional work necessary to create working |
-// file-backed blobs. |
-class WebSocketFileBackedBlobSenderTest : public WebSocketBlobSenderTest { |
- protected: |
- void SetUp() override { |
- WebSocketBlobSenderTest::SetUp(); |
- // temp_dir_ is recursively deleted on destruction. |
- ASSERT_TRUE(temp_dir_.CreateUniqueTempDir()); |
- } |
- |
- void CreateFile(const std::string& contents, |
- const base::FilePath& path, |
- base::File::Info* info) { |
- ASSERT_EQ(contents.size(), static_cast<size_t>(base::WriteFile( |
- path, contents.data(), contents.size()))); |
- ASSERT_TRUE(base::GetFileInfo(path, info)); |
- } |
- |
- std::unique_ptr<BlobHandle> CreateFileBackedBlob( |
- const std::string& contents) { |
- base::FilePath path = temp_dir_.path().AppendASCII("blob.dat"); |
- base::File::Info info; |
- CreateFile(contents, path, &info); |
- if (HasFatalFailure()) |
- return nullptr; |
- return chrome_blob_storage_context_->CreateFileBackedBlob( |
- path, 0u, contents.size(), info.last_modified); |
- } |
- |
- base::ScopedTempDir temp_dir_; |
-}; |
- |
-TEST_F(WebSocketFileBackedBlobSenderTest, EmptyBlob) { |
- std::unique_ptr<BlobHandle> handle = CreateFileBackedBlob(""); |
- ASSERT_TRUE(handle); |
- |
- TestCompletionCallback callback; |
- int result = callback.GetResult( |
- Start(handle->GetUUID(), UINT64_C(0), callback.callback())); |
- EXPECT_EQ(net::OK, result); |
- EXPECT_TRUE(fake_channel_->got_fin()); |
- EXPECT_EQ(0U, fake_channel_->message().size()); |
-} |
- |
-TEST_F(WebSocketFileBackedBlobSenderTest, SizeMismatch) { |
- std::unique_ptr<BlobHandle> handle = CreateFileBackedBlob(kBanana); |
- ASSERT_TRUE(handle); |
- |
- TestCompletionCallback callback; |
- int result = Start(handle->GetUUID(), UINT64_C(8), callback.callback()); |
- // This test explicitly aims to test the asynchronous code path, otherwise it |
- // would be identical to the other SizeMismatch test above. |
- EXPECT_EQ(net::ERR_IO_PENDING, result); |
- EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, callback.WaitForResult()); |
- EXPECT_EQ(0, fake_channel_->frames_sent()); |
-} |
- |
-TEST_F(WebSocketFileBackedBlobSenderTest, LargeMessage) { |
- std::string message = "the green potato had lunch with the angry cat. "; |
- while (message.size() <= kInitialQuota) { |
- message = message + message; |
- } |
- std::unique_ptr<BlobHandle> handle = CreateFileBackedBlob(message); |
- ASSERT_TRUE(handle); |
- |
- TestCompletionCallback callback; |
- int result = Start(handle->GetUUID(), message.size(), callback.callback()); |
- EXPECT_EQ(net::OK, callback.GetResult(result)); |
- std::vector<char> expected_message(message.begin(), message.end()); |
- EXPECT_EQ(expected_message, fake_channel_->message()); |
-} |
- |
-// The WebSocketBlobSender needs to handle a connection close while doing file |
-// IO cleanly. |
-TEST_F(WebSocketFileBackedBlobSenderTest, Aborted) { |
- std::unique_ptr<BlobHandle> handle = CreateFileBackedBlob(kBanana); |
- |
- int rv = Start(handle->GetUUID(), UINT64_C(6), NotCalled()); |
- EXPECT_EQ(net::ERR_IO_PENDING, rv); |
- sender_.reset(); |
-} |
- |
-class DeletingFakeChannel : public WebSocketBlobSender::Channel { |
- public: |
- explicit DeletingFakeChannel( |
- std::unique_ptr<WebSocketBlobSender>* sender_to_delete) |
- : sender_(sender_to_delete) {} |
- |
- size_t GetSendQuota() const override { return kInitialQuota; } |
- |
- ChannelState SendFrame(bool fin, const std::vector<char>& data) override { |
- sender_->reset(); |
- // |this| is deleted here. |
- return net::WebSocketEventInterface::CHANNEL_DELETED; |
- } |
- |
- private: |
- std::unique_ptr<WebSocketBlobSender>* sender_; |
-}; |
- |
-class WebSocketBlobSenderDeletingTest : public WebSocketBlobSenderTest { |
- protected: |
- void SetUpSender() override { |
- sender_.reset(new WebSocketBlobSender( |
- base::WrapUnique(new DeletingFakeChannel(&sender_)))); |
- } |
-}; |
- |
-// This test only does something useful when run under AddressSanitizer or a |
-// similar tool that can detect use-after-free bugs. |
-TEST_F(WebSocketBlobSenderDeletingTest, SenderDeleted) { |
- std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(kBanana); |
- |
- EXPECT_EQ(net::ERR_CONNECTION_RESET, |
- Start(handle->GetUUID(), UINT64_C(6), NotCalled())); |
- EXPECT_FALSE(sender_); |
-} |
- |
-// SendFrame() calls OnSendNewQuota() synchronously while filling the operating |
-// system's socket write buffer. The purpose of this Channel implementation is |
-// to verify that the synchronous case works correctly. |
-class SynchronousFakeChannel : public WebSocketBlobSender::Channel { |
- public: |
- // This method must be called before SendFrame() is. |
- void set_notify_new_quota(const base::Closure& notify_new_quota) { |
- notify_new_quota_ = notify_new_quota; |
- } |
- |
- size_t GetSendQuota() const override { return kInitialQuota; } |
- |
- ChannelState SendFrame(bool fin, const std::vector<char>& data) override { |
- message_.insert(message_.end(), data.begin(), data.end()); |
- notify_new_quota_.Run(); |
- return net::WebSocketEventInterface::CHANNEL_ALIVE; |
- } |
- |
- const std::vector<char>& message() const { return message_; } |
- |
- private: |
- base::Closure notify_new_quota_; |
- std::vector<char> message_; |
-}; |
- |
-class WebSocketBlobSenderSynchronousTest : public WebSocketBlobSenderTest { |
- protected: |
- void SetUpSender() override { |
- synchronous_fake_channel_ = new SynchronousFakeChannel; |
- sender_.reset( |
- new WebSocketBlobSender(base::WrapUnique(synchronous_fake_channel_))); |
- synchronous_fake_channel_->set_notify_new_quota(base::Bind( |
- &WebSocketBlobSender::OnNewSendQuota, base::Unretained(sender_.get()))); |
- } |
- |
- SynchronousFakeChannel* synchronous_fake_channel_ = nullptr; |
-}; |
- |
-TEST_F(WebSocketBlobSenderSynchronousTest, LargeMessage) { |
- std::string message(kInitialQuota + 10, 'a'); |
- std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(message.c_str()); |
- |
- int rv = Start(handle->GetUUID(), message.size(), NotCalled()); |
- EXPECT_EQ(net::OK, rv); |
- std::vector<char> expected_message(message.begin(), message.end()); |
- EXPECT_EQ(expected_message, synchronous_fake_channel_->message()); |
-} |
- |
-} // namespace |
- |
-} // namespace content |