Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(31)

Side by Side Diff: content/browser/renderer_host/websocket_blob_sender_unittest.cc

Issue 2119973002: Port WebSockets to Mojo IPC (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Fix compile error Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2016 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 "content/browser/renderer_host/websocket_blob_sender.h"
6
7 #include <string.h>
8
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/callback.h"
12 #include "base/files/file.h"
13 #include "base/files/file_path.h"
14 #include "base/files/file_util.h"
15 #include "base/files/scoped_temp_dir.h"
16 #include "base/location.h"
17 #include "base/memory/ptr_util.h"
18 #include "base/memory/weak_ptr.h"
19 #include "base/message_loop/message_loop.h"
20 #include "base/run_loop.h"
21 #include "base/single_thread_task_runner.h"
22 #include "base/task_runner.h"
23 #include "base/threading/thread_task_runner_handle.h"
24 #include "base/time/time.h"
25 #include "content/browser/blob_storage/chrome_blob_storage_context.h"
26 #include "content/public/browser/blob_handle.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/storage_partition.h"
29 #include "content/public/test/test_browser_context.h"
30 #include "content/public/test/test_browser_thread_bundle.h"
31 #include "net/base/completion_callback.h"
32 #include "net/base/net_errors.h"
33 #include "net/base/test_completion_callback.h"
34 #include "storage/common/fileapi/file_system_types.h"
35 #include "testing/gtest/include/gtest/gtest.h"
36 #include "url/gurl.h"
37
38 namespace content {
39
40 namespace {
41
42 const char kDummyUrl[] = "http://www.example.com/";
43 const char kBanana[] = "banana";
44
45 // This is small so that the tests do not waste too much time just copying bytes
46 // around. But it has to be larger than kMinimumNonFinalFrameSize defined in
47 // websocket_blob_sender.cc.
48 const size_t kInitialQuota = 16 * 1024;
49
50 using net::TestCompletionCallback;
51
52 // A fake channel for testing. Records the contents of the message that was sent
53 // through it. Quota is restricted, and is refreshed asynchronously in response
54 // to calls to SendFrame().
55 class FakeChannel : public WebSocketBlobSender::Channel {
56 public:
57 // |notify_new_quota| will be run asynchronously on the current MessageLoop
58 // every time GetSendQuota() increases.
59 FakeChannel() : weak_factory_(this) {}
60
61 // This method must be called before SendFrame() is.
62 void set_notify_new_quota(const base::Closure& notify_new_quota) {
63 notify_new_quota_ = notify_new_quota;
64 }
65
66 size_t GetSendQuota() const override { return current_send_quota_; }
67
68 ChannelState SendFrame(bool fin, const std::vector<char>& data) override {
69 ++frames_sent_;
70 EXPECT_FALSE(got_fin_);
71 if (fin)
72 got_fin_ = true;
73 EXPECT_LE(data.size(), current_send_quota_);
74 message_.insert(message_.end(), data.begin(), data.end());
75 current_send_quota_ -= data.size();
76 base::ThreadTaskRunnerHandle::Get()->PostTask(
77 FROM_HERE,
78 base::Bind(&FakeChannel::RefreshQuota, weak_factory_.GetWeakPtr()));
79 return net::WebSocketEventInterface::CHANNEL_ALIVE;
80 }
81
82 bool got_fin() const { return got_fin_; }
83
84 int frames_sent() const { return frames_sent_; }
85
86 const std::vector<char>& message() const { return message_; }
87
88 private:
89 void RefreshQuota() {
90 if (current_send_quota_ == kInitialQuota)
91 return;
92 current_send_quota_ = kInitialQuota;
93 DCHECK(!notify_new_quota_.is_null());
94 notify_new_quota_.Run();
95 }
96
97 base::Closure notify_new_quota_;
98 size_t current_send_quota_ = kInitialQuota;
99 int frames_sent_ = 0;
100 bool got_fin_ = false;
101 std::vector<char> message_;
102 base::WeakPtrFactory<FakeChannel> weak_factory_;
103 };
104
105 class WebSocketBlobSenderTest : public ::testing::Test {
106 protected:
107 // The Windows implementation of net::FileStream::Context requires a real IO
108 // MessageLoop.
109 WebSocketBlobSenderTest()
110 : threads_(TestBrowserThreadBundle::IO_MAINLOOP),
111 chrome_blob_storage_context_(
112 ChromeBlobStorageContext::GetFor(&browser_context_)),
113 fake_channel_(nullptr),
114 sender_() {}
115 ~WebSocketBlobSenderTest() override {}
116
117 void SetUp() override {
118 // ChromeBlobStorageContext::GetFor() does some work asynchronously.
119 base::RunLoop().RunUntilIdle();
120 SetUpSender();
121 }
122
123 // This method can be overriden to use a different channel implementation.
124 virtual void SetUpSender() {
125 fake_channel_ = new FakeChannel;
126 sender_.reset(new WebSocketBlobSender(base::WrapUnique(fake_channel_)));
127 fake_channel_->set_notify_new_quota(base::Bind(
128 &WebSocketBlobSender::OnNewSendQuota, base::Unretained(sender_.get())));
129 }
130
131 storage::BlobStorageContext* context() {
132 return chrome_blob_storage_context_->context();
133 }
134
135 storage::FileSystemContext* GetFileSystemContext() {
136 StoragePartition* partition = BrowserContext::GetStoragePartitionForSite(
137 &browser_context_, GURL(kDummyUrl));
138 return partition->GetFileSystemContext();
139 }
140
141 // |string| is copied.
142 std::unique_ptr<BlobHandle> CreateMemoryBackedBlob(const char* string) {
143 std::unique_ptr<BlobHandle> handle =
144 chrome_blob_storage_context_->CreateMemoryBackedBlob(string,
145 strlen(string));
146 EXPECT_TRUE(handle);
147 return handle;
148 }
149
150 // Call sender_.Start() with the other parameters filled in appropriately for
151 // this test fixture.
152 int Start(const std::string& uuid,
153 uint64_t expected_size,
154 const net::CompletionCallback& callback) {
155 net::WebSocketEventInterface::ChannelState channel_state =
156 net::WebSocketEventInterface::CHANNEL_ALIVE;
157 return sender_->Start(
158 uuid, expected_size, context(), GetFileSystemContext(),
159 BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE).get(),
160 &channel_state, callback);
161 }
162
163 void NotCalledCallbackImpl(int rv) {
164 ADD_FAILURE()
165 << "Callback that should not be called was called with argument " << rv;
166 }
167
168 net::CompletionCallback NotCalled() {
169 return base::Bind(&WebSocketBlobSenderTest::NotCalledCallbackImpl,
170 base::Unretained(this));
171 }
172
173 void ExpectOkAndQuit(base::RunLoop* run_loop, int result) {
174 EXPECT_EQ(net::OK, result);
175 run_loop->Quit();
176 }
177
178 net::CompletionCallback ExpectOkAndQuitCallback(base::RunLoop* run_loop) {
179 return base::Bind(&WebSocketBlobSenderTest::ExpectOkAndQuit,
180 base::Unretained(this), run_loop);
181 }
182
183 TestBrowserThreadBundle threads_;
184 TestBrowserContext browser_context_;
185 scoped_refptr<ChromeBlobStorageContext> chrome_blob_storage_context_;
186 // |fake_channel_| is owned by |sender_|.
187 FakeChannel* fake_channel_;
188 std::unique_ptr<WebSocketBlobSender> sender_;
189 };
190
191 TEST_F(WebSocketBlobSenderTest, Construction) {}
192
193 TEST_F(WebSocketBlobSenderTest, EmptyBlob) {
194 std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob("");
195
196 // The APIs allow for this to be asynchronous but that is unlikely in
197 // practice.
198 int result = Start(handle->GetUUID(), UINT64_C(0), NotCalled());
199 // If this fails with result == -1, someone has changed the code to be
200 // asynchronous and this test should be adapted to match.
201 EXPECT_EQ(net::OK, result);
202 EXPECT_TRUE(fake_channel_->got_fin());
203 EXPECT_EQ(0U, fake_channel_->message().size());
204 }
205
206 TEST_F(WebSocketBlobSenderTest, SmallBlob) {
207 std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(kBanana);
208
209 EXPECT_EQ(net::OK, Start(handle->GetUUID(), UINT64_C(6), NotCalled()));
210 EXPECT_TRUE(fake_channel_->got_fin());
211 EXPECT_EQ(1, fake_channel_->frames_sent());
212 EXPECT_EQ(std::vector<char>(kBanana, kBanana + 6), fake_channel_->message());
213 }
214
215 TEST_F(WebSocketBlobSenderTest, SizeMismatch) {
216 std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(kBanana);
217
218 EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED,
219 Start(handle->GetUUID(), UINT64_C(5), NotCalled()));
220 EXPECT_EQ(0, fake_channel_->frames_sent());
221 }
222
223 TEST_F(WebSocketBlobSenderTest, InvalidUUID) {
224 EXPECT_EQ(net::ERR_INVALID_HANDLE,
225 Start("sandwich", UINT64_C(0), NotCalled()));
226 }
227
228 TEST_F(WebSocketBlobSenderTest, LargeMessage) {
229 std::string message(kInitialQuota + 10, 'a');
230 std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(message.c_str());
231
232 base::RunLoop run_loop;
233 int rv = Start(handle->GetUUID(), message.size(),
234 ExpectOkAndQuitCallback(&run_loop));
235 EXPECT_EQ(net::ERR_IO_PENDING, rv);
236 EXPECT_EQ(1, fake_channel_->frames_sent());
237 run_loop.Run();
238 EXPECT_EQ(2, fake_channel_->frames_sent());
239 EXPECT_TRUE(fake_channel_->got_fin());
240 std::vector<char> expected_message(message.begin(), message.end());
241 EXPECT_EQ(expected_message, fake_channel_->message());
242 }
243
244 // A message exactly equal to the available quota should be sent in one frame.
245 TEST_F(WebSocketBlobSenderTest, ExactSizeMessage) {
246 std::string message(kInitialQuota, 'a');
247 std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(message.c_str());
248
249 EXPECT_EQ(net::OK, Start(handle->GetUUID(), message.size(), NotCalled()));
250 EXPECT_EQ(1, fake_channel_->frames_sent());
251 EXPECT_TRUE(fake_channel_->got_fin());
252 std::vector<char> expected_message(message.begin(), message.end());
253 EXPECT_EQ(expected_message, fake_channel_->message());
254 }
255
256 // If the connection is closed while sending a message, the WebSocketBlobSender
257 // object will be destroyed. It needs to handle this case without error.
258 TEST_F(WebSocketBlobSenderTest, AbortedSend) {
259 std::string message(kInitialQuota + 10, 'a');
260 std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(message.c_str());
261
262 int rv = Start(handle->GetUUID(), message.size(), NotCalled());
263 EXPECT_EQ(net::ERR_IO_PENDING, rv);
264 sender_.reset();
265 }
266
267 // Invalid file-backed blob.
268 TEST_F(WebSocketBlobSenderTest, InvalidFileBackedBlob) {
269 base::FilePath path(FILE_PATH_LITERAL(
270 "WebSocketBlobSentTest.InvalidFileBackedBlob.NonExistentFile"));
271 std::unique_ptr<BlobHandle> handle =
272 chrome_blob_storage_context_->CreateFileBackedBlob(path, 0u, 32u,
273 base::Time::Now());
274 EXPECT_TRUE(handle);
275
276 TestCompletionCallback callback;
277 int rv =
278 callback.GetResult(Start(handle->GetUUID(), 5u, callback.callback()));
279 EXPECT_EQ(net::ERR_FILE_NOT_FOUND, rv);
280 }
281
282 // A test fixture that does the additional work necessary to create working
283 // file-backed blobs.
284 class WebSocketFileBackedBlobSenderTest : public WebSocketBlobSenderTest {
285 protected:
286 void SetUp() override {
287 WebSocketBlobSenderTest::SetUp();
288 // temp_dir_ is recursively deleted on destruction.
289 ASSERT_TRUE(temp_dir_.CreateUniqueTempDir());
290 }
291
292 void CreateFile(const std::string& contents,
293 const base::FilePath& path,
294 base::File::Info* info) {
295 ASSERT_EQ(contents.size(), static_cast<size_t>(base::WriteFile(
296 path, contents.data(), contents.size())));
297 ASSERT_TRUE(base::GetFileInfo(path, info));
298 }
299
300 std::unique_ptr<BlobHandle> CreateFileBackedBlob(
301 const std::string& contents) {
302 base::FilePath path = temp_dir_.path().AppendASCII("blob.dat");
303 base::File::Info info;
304 CreateFile(contents, path, &info);
305 if (HasFatalFailure())
306 return nullptr;
307 return chrome_blob_storage_context_->CreateFileBackedBlob(
308 path, 0u, contents.size(), info.last_modified);
309 }
310
311 base::ScopedTempDir temp_dir_;
312 };
313
314 TEST_F(WebSocketFileBackedBlobSenderTest, EmptyBlob) {
315 std::unique_ptr<BlobHandle> handle = CreateFileBackedBlob("");
316 ASSERT_TRUE(handle);
317
318 TestCompletionCallback callback;
319 int result = callback.GetResult(
320 Start(handle->GetUUID(), UINT64_C(0), callback.callback()));
321 EXPECT_EQ(net::OK, result);
322 EXPECT_TRUE(fake_channel_->got_fin());
323 EXPECT_EQ(0U, fake_channel_->message().size());
324 }
325
326 TEST_F(WebSocketFileBackedBlobSenderTest, SizeMismatch) {
327 std::unique_ptr<BlobHandle> handle = CreateFileBackedBlob(kBanana);
328 ASSERT_TRUE(handle);
329
330 TestCompletionCallback callback;
331 int result = Start(handle->GetUUID(), UINT64_C(8), callback.callback());
332 // This test explicitly aims to test the asynchronous code path, otherwise it
333 // would be identical to the other SizeMismatch test above.
334 EXPECT_EQ(net::ERR_IO_PENDING, result);
335 EXPECT_EQ(net::ERR_UPLOAD_FILE_CHANGED, callback.WaitForResult());
336 EXPECT_EQ(0, fake_channel_->frames_sent());
337 }
338
339 TEST_F(WebSocketFileBackedBlobSenderTest, LargeMessage) {
340 std::string message = "the green potato had lunch with the angry cat. ";
341 while (message.size() <= kInitialQuota) {
342 message = message + message;
343 }
344 std::unique_ptr<BlobHandle> handle = CreateFileBackedBlob(message);
345 ASSERT_TRUE(handle);
346
347 TestCompletionCallback callback;
348 int result = Start(handle->GetUUID(), message.size(), callback.callback());
349 EXPECT_EQ(net::OK, callback.GetResult(result));
350 std::vector<char> expected_message(message.begin(), message.end());
351 EXPECT_EQ(expected_message, fake_channel_->message());
352 }
353
354 // The WebSocketBlobSender needs to handle a connection close while doing file
355 // IO cleanly.
356 TEST_F(WebSocketFileBackedBlobSenderTest, Aborted) {
357 std::unique_ptr<BlobHandle> handle = CreateFileBackedBlob(kBanana);
358
359 int rv = Start(handle->GetUUID(), UINT64_C(6), NotCalled());
360 EXPECT_EQ(net::ERR_IO_PENDING, rv);
361 sender_.reset();
362 }
363
364 class DeletingFakeChannel : public WebSocketBlobSender::Channel {
365 public:
366 explicit DeletingFakeChannel(
367 std::unique_ptr<WebSocketBlobSender>* sender_to_delete)
368 : sender_(sender_to_delete) {}
369
370 size_t GetSendQuota() const override { return kInitialQuota; }
371
372 ChannelState SendFrame(bool fin, const std::vector<char>& data) override {
373 sender_->reset();
374 // |this| is deleted here.
375 return net::WebSocketEventInterface::CHANNEL_DELETED;
376 }
377
378 private:
379 std::unique_ptr<WebSocketBlobSender>* sender_;
380 };
381
382 class WebSocketBlobSenderDeletingTest : public WebSocketBlobSenderTest {
383 protected:
384 void SetUpSender() override {
385 sender_.reset(new WebSocketBlobSender(
386 base::WrapUnique(new DeletingFakeChannel(&sender_))));
387 }
388 };
389
390 // This test only does something useful when run under AddressSanitizer or a
391 // similar tool that can detect use-after-free bugs.
392 TEST_F(WebSocketBlobSenderDeletingTest, SenderDeleted) {
393 std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(kBanana);
394
395 EXPECT_EQ(net::ERR_CONNECTION_RESET,
396 Start(handle->GetUUID(), UINT64_C(6), NotCalled()));
397 EXPECT_FALSE(sender_);
398 }
399
400 // SendFrame() calls OnSendNewQuota() synchronously while filling the operating
401 // system's socket write buffer. The purpose of this Channel implementation is
402 // to verify that the synchronous case works correctly.
403 class SynchronousFakeChannel : public WebSocketBlobSender::Channel {
404 public:
405 // This method must be called before SendFrame() is.
406 void set_notify_new_quota(const base::Closure& notify_new_quota) {
407 notify_new_quota_ = notify_new_quota;
408 }
409
410 size_t GetSendQuota() const override { return kInitialQuota; }
411
412 ChannelState SendFrame(bool fin, const std::vector<char>& data) override {
413 message_.insert(message_.end(), data.begin(), data.end());
414 notify_new_quota_.Run();
415 return net::WebSocketEventInterface::CHANNEL_ALIVE;
416 }
417
418 const std::vector<char>& message() const { return message_; }
419
420 private:
421 base::Closure notify_new_quota_;
422 std::vector<char> message_;
423 };
424
425 class WebSocketBlobSenderSynchronousTest : public WebSocketBlobSenderTest {
426 protected:
427 void SetUpSender() override {
428 synchronous_fake_channel_ = new SynchronousFakeChannel;
429 sender_.reset(
430 new WebSocketBlobSender(base::WrapUnique(synchronous_fake_channel_)));
431 synchronous_fake_channel_->set_notify_new_quota(base::Bind(
432 &WebSocketBlobSender::OnNewSendQuota, base::Unretained(sender_.get())));
433 }
434
435 SynchronousFakeChannel* synchronous_fake_channel_ = nullptr;
436 };
437
438 TEST_F(WebSocketBlobSenderSynchronousTest, LargeMessage) {
439 std::string message(kInitialQuota + 10, 'a');
440 std::unique_ptr<BlobHandle> handle = CreateMemoryBackedBlob(message.c_str());
441
442 int rv = Start(handle->GetUUID(), message.size(), NotCalled());
443 EXPECT_EQ(net::OK, rv);
444 std::vector<char> expected_message(message.begin(), message.end());
445 EXPECT_EQ(expected_message, synchronous_fake_channel_->message());
446 }
447
448 } // namespace
449
450 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698