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

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

Issue 1664743002: [OBSOLETE][DO NOT SUBMIT][DO NOT COMMIT]] Browser-side implementation of WebSocket Blob receive. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@websocket_blob_send_sender
Patch Set: Now actually works. Created 4 years, 10 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 "websocket_blob_receiver.h"
6
7 #include <string>
8 #include <utility>
9
10 #include "base/run_loop.h"
11 #include "base/strings/string_piece.h"
12 #include "content/browser/fileapi/chrome_blob_storage_context.h"
13 #include "content/public/browser/browser_context.h"
14 #include "content/public/browser/browser_thread.h"
15 #include "content/public/browser/storage_partition.h"
16 #include "content/public/test/test_browser_context.h"
17 #include "content/public/test/test_browser_thread_bundle.h"
18 #include "net/base/completion_callback.h"
19 #include "net/base/io_buffer.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/test_completion_callback.h"
22 #include "storage/browser/blob/blob_data_handle.h"
23 #include "storage/browser/blob/blob_reader.h"
24 #include "storage/browser/fileapi/file_system_context.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 #include "url/gurl.h"
27
28 namespace content {
29
30 namespace {
31
32 using base::RunLoop;
33 using base::WeakPtr;
34 using net::TestCompletionCallback;
35 using storage::BlobDataHandle;
36
37 class FakeClient : public WebSocketBlobReceiver::Client {
38 public:
39 FakeClient() : weak_factory_(this) {}
40
41 void BlobCreated(scoped_ptr<BlobDataHandle> blob_data_handle,
42 uint64_t size) override {
43 blob_data_handle_ = std::move(blob_data_handle);
44 size_ = size;
45 done_run_loop_.Quit();
46 }
47
48 void BlobFailed(int net_error_code) override {
49 failure_code_ = net_error_code;
50 done_run_loop_.Quit();
51 }
52
53 void AddFlowControlQuota(size_t quota) override {
54 total_quota_ += quota;
55 if (flow_control_run_loop_)
56 flow_control_run_loop_->Quit();
57 }
58
59 WeakPtr<FakeClient> GetWeakPtr() { return weak_factory_.GetWeakPtr(); }
60
61 const BlobDataHandle* blob_data_handle() const {
62 return blob_data_handle_.get();
63 }
64
65 uint64_t size() const { return size_; }
66
67 int failure_code() const { return failure_code_; }
68
69 void WaitUntilDone() { done_run_loop_.Run(); }
70
71 size_t total_quota() const { return total_quota_; }
72
73 // The point of this class is that it starts waiting at the point when it is
74 // initialised. This means Wait() will always return, even if the flow control
75 // is supplied before Wait() is called.
76 class FlowControlWaiter {
77 public:
78 FlowControlWaiter(WeakPtr<FakeClient> fake_client)
79 : fake_client_(fake_client) {
80 fake_client->flow_control_run_loop_ = &run_loop_;
81 }
82
83 ~FlowControlWaiter() {
84 if (fake_client_)
85 fake_client_->flow_control_run_loop_ = nullptr;
86 }
87
88 void Wait() {
89 run_loop_.Run();
90 if (fake_client_)
91 fake_client_->flow_control_run_loop_ = nullptr;
92 }
93
94 private:
95 WeakPtr<FakeClient> fake_client_;
96 RunLoop run_loop_;
97
98 DISALLOW_COPY_AND_ASSIGN(FlowControlWaiter);
99 };
100
101 private:
102 RunLoop done_run_loop_;
103 RunLoop* flow_control_run_loop_;
104 scoped_ptr<BlobDataHandle> blob_data_handle_;
105 uint64_t size_ = 0;
106 int failure_code_ = net::OK;
107 size_t total_quota_ = 0;
108 base::WeakPtrFactory<FakeClient> weak_factory_;
109
110 DISALLOW_COPY_AND_ASSIGN(FakeClient);
111 };
112
113 class WebSocketBlobReceiverTest : public ::testing::Test {
114 protected:
115 // The Windows implementation of net::FileStream::Context requires a real IO
116 // MessageLoop.
117 WebSocketBlobReceiverTest()
118 : thread_bundle_(TestBrowserThreadBundle::IO_MAINLOOP),
119 browser_context_(),
120 chrome_blob_storage_context_(
121 ChromeBlobStorageContext::GetFor(&browser_context_)) {}
122
123 void SetUp() override {
124 // Allow ChromeBlobStorageContext to initialise the BlobStorageContext on
125 // the "IO thread".
126 RunLoop().RunUntilIdle();
127 FakeClient* fake_client = new FakeClient;
128 fake_client_ = fake_client->GetWeakPtr();
129 receiver_.reset(new WebSocketBlobReceiver(
130 make_scoped_ptr(fake_client), chrome_blob_storage_context_->context()));
131 }
132
133 std::string BlobToString(const BlobDataHandle* blob_data_handle) {
134 static const char kDummyUrl[] = "http://www.example.com/";
135 using storage::BlobReader;
136
137 StoragePartition* partition = BrowserContext::GetStoragePartitionForSite(
138 &browser_context_, GURL(kDummyUrl));
139 storage::FileSystemContext* file_system_context =
140 partition->GetFileSystemContext();
141 scoped_ptr<BlobReader> blob_reader = blob_data_handle->CreateReader(
142 file_system_context,
143 BrowserThread::GetMessageLoopProxyForThread(BrowserThread::FILE).get());
144
145 TestCompletionCallback size_callback;
146 switch (blob_reader->CalculateSize(size_callback.callback())) {
147 case BlobReader::Status::DONE:
148 break;
149
150 case BlobReader::Status::IO_PENDING:
151 EXPECT_EQ(net::OK, size_callback.WaitForResult());
152 break;
153
154 case BlobReader::Status::NET_ERROR:
155 ADD_FAILURE() << "BlobReader::CalculateSize returned error: "
156 << net::ErrorToString(blob_reader->net_error());
157 break;
158 }
159
160 TestCompletionCallback read_callback;
161 size_t dest_size = blob_reader->total_size();
162 scoped_refptr<net::IOBuffer> buffer(new net::IOBuffer(dest_size));
163 int bytes_read = 0;
164 switch (blob_reader->Read(buffer.get(), dest_size, &bytes_read,
165 read_callback.callback())) {
166 case BlobReader::Status::DONE:
167 EXPECT_EQ(dest_size, static_cast<size_t>(bytes_read));
168 break;
169
170 case BlobReader::Status::IO_PENDING:
171 EXPECT_EQ(static_cast<int>(dest_size), read_callback.WaitForResult());
172 break;
173
174 case BlobReader::Status::NET_ERROR:
175 ADD_FAILURE() << "BlobReader::Read returned error: "
176 << net::ErrorToString(blob_reader->net_error());
177 break;
178 }
179
180 return std::string(buffer->data(), buffer->data() + dest_size);
181 }
182
183 // Test for success, and provide a useful diagnostic if we don't have it.
184 void ExpectSuccess() {
185 EXPECT_EQ(net::OK, fake_client_->failure_code())
186 << net::ErrorToString(fake_client_->failure_code());
187 }
188
189 // Start the WebSocketBlobReceiver, and wait for the temporary file to be
190 // opened (indicated by quota being supplied).
191 void StartAndWaitForOpen() {
192 FakeClient::FlowControlWaiter await_open(fake_client_);
193 receiver_->Start();
194 await_open.Wait();
195 }
196
197 // Convert |data| to a vector and append it. Expect success. |data| must be
198 // non-empty.
199 void AppendDataSuccessfully(base::StringPiece data) {
200 CHECK(!data.empty());
201 EXPECT_EQ(net::ERR_IO_PENDING, receiver_->AppendData(std::vector<char>(
202 data.begin(), data.end())));
203 }
204
205 // Finish, wait until completion, and expect success.
206 void FinishAndWaitForSuccess() {
207 EXPECT_EQ(net::ERR_IO_PENDING, receiver_->Finish());
208 fake_client_->WaitUntilDone();
209 ExpectSuccess();
210 }
211
212 TestBrowserThreadBundle thread_bundle_;
213 TestBrowserContext browser_context_;
214 scoped_refptr<ChromeBlobStorageContext> chrome_blob_storage_context_;
215 WeakPtr<FakeClient> fake_client_;
216 scoped_ptr<WebSocketBlobReceiver> receiver_;
217 };
218
219 TEST_F(WebSocketBlobReceiverTest, Construct) {}
220
221 TEST_F(WebSocketBlobReceiverTest, SendEmptyBlob) {
222 receiver_->Start();
223
224 EXPECT_EQ(net::OK, receiver_->AppendData(std::vector<char>()));
225
226 EXPECT_EQ(net::ERR_IO_PENDING, receiver_->Finish());
227 fake_client_->WaitUntilDone();
228
229 ExpectSuccess();
230 EXPECT_EQ("", BlobToString(fake_client_->blob_data_handle()));
231 }
232
233 TEST_F(WebSocketBlobReceiverTest, SendNonEmptyBlob) {
234 receiver_->Start();
235
236 // This violates the interface contract since no quota has been provided yet,
237 // but WebSocketBlobReceiver doesn't strictly enforce its quota invariants
238 // since the WebSocket wire implementation does that.
239 EXPECT_EQ(net::ERR_IO_PENDING,
240 receiver_->AppendData(std::vector<char>(4, 'a')));
241
242 FinishAndWaitForSuccess();
243
244 EXPECT_EQ("aaaa", BlobToString(fake_client_->blob_data_handle()));
245 }
246
247 TEST_F(WebSocketBlobReceiverTest, SendAfterOpenComplete) {
248 StartAndWaitForOpen();
249
250 EXPECT_GT(fake_client_->total_quota(), 0u);
251
252 AppendDataSuccessfully("bbbb");
253
254 FinishAndWaitForSuccess();
255 EXPECT_EQ("bbbb", BlobToString(fake_client_->blob_data_handle()));
256 }
257
258 TEST_F(WebSocketBlobReceiverTest, SendDuringWrite) {
259 StartAndWaitForOpen();
260
261 AppendDataSuccessfully("cc");
262 AppendDataSuccessfully("dd");
263
264 FinishAndWaitForSuccess();
265 EXPECT_EQ("ccdd", BlobToString(fake_client_->blob_data_handle()));
266 }
267
268 TEST_F(WebSocketBlobReceiverTest, UsedQuotaIsReturned) {
269 const size_t kBytesToUse = 8;
270 StartAndWaitForOpen();
271
272 size_t initial_quota = fake_client_->total_quota();
273 std::string data(kBytesToUse, 'e');
274
275 // It isn't actually guaranteed that all the quota will be returned in one
276 // call, but this should work in practice.
277 FakeClient::FlowControlWaiter await_more_quota(fake_client_);
278 AppendDataSuccessfully(data);
279 await_more_quota.Wait();
280
281 size_t new_quota = fake_client_->total_quota() - initial_quota;
282 EXPECT_EQ(kBytesToUse, new_quota);
283
284 FinishAndWaitForSuccess();
285 }
286
287 TEST_F(WebSocketBlobReceiverTest, WaitForQuota) {
288 StartAndWaitForOpen();
289
290 size_t initial_quota = fake_client_->total_quota();
291 std::string data1(initial_quota, 'w');
292
293 FakeClient::FlowControlWaiter await_more_quota(fake_client_);
294 AppendDataSuccessfully(data1);
295 await_more_quota.Wait();
296
297 size_t new_quota = fake_client_->total_quota() - initial_quota;
298 std::string data2(new_quota, 'f');
299 AppendDataSuccessfully(data2);
300
301 FinishAndWaitForSuccess();
302 EXPECT_EQ(data1 + data2, BlobToString(fake_client_->blob_data_handle()));
303 }
304
305 TEST_F(WebSocketBlobReceiverTest, SizeIsCorrect) {
306 const size_t kTotallyRandomNumber = 42;
307
308 StartAndWaitForOpen();
309
310 std::string data(kTotallyRandomNumber, 'q');
311 AppendDataSuccessfully(data);
312
313 FinishAndWaitForSuccess();
314 EXPECT_EQ(kTotallyRandomNumber, fake_client_->size());
315 }
316
317 // Deleting the WebSocketBlobReceiver should cleanly abort all operations and
318 // leak no memory or temporary files.
319 TEST_F(WebSocketBlobReceiverTest, AbortAfterStart) {
320 receiver_->Start();
321 receiver_.reset();
322 }
323
324 TEST_F(WebSocketBlobReceiverTest, AbortAfterOpen) {
325 StartAndWaitForOpen();
326 receiver_.reset();
327 }
328
329 TEST_F(WebSocketBlobReceiverTest, AbortAfterAppend) {
330 StartAndWaitForOpen();
331 AppendDataSuccessfully("banana");
332 receiver_.reset();
333 }
334
335 TEST_F(WebSocketBlobReceiverTest, AbortDuringLargeWrite) {
336 StartAndWaitForOpen();
337 size_t initial_quota = fake_client_->total_quota();
338 receiver_->AppendData(std::vector<char>(initial_quota, 'g'));
339 receiver_.reset();
340 }
341
342 TEST_F(WebSocketBlobReceiverTest, AbortAfterSendQuota) {
343 StartAndWaitForOpen();
344 FakeClient::FlowControlWaiter await_more_quota(fake_client_);
345 AppendDataSuccessfully("orange");
346 await_more_quota.Wait();
347 receiver_.reset();
348 }
349
350 TEST_F(WebSocketBlobReceiverTest, AbortAfterAppendAndFinish) {
351 StartAndWaitForOpen();
352 AppendDataSuccessfully("apple");
353 receiver_->Finish();
354 receiver_.reset();
355 }
356
357 // The difference of this test from AbortAfterAppendAndFinish is that it waits
358 // for the write to complete before calling Finish().
359 TEST_F(WebSocketBlobReceiverTest, AbortDuringFinish) {
360 StartAndWaitForOpen();
361 FakeClient::FlowControlWaiter await_more_quota(fake_client_);
362 AppendDataSuccessfully("kiwi");
363 await_more_quota.Wait();
364 receiver_->Finish();
365 receiver_.reset();
366 }
367
368 } // namespace
369
370 } // namespace content
OLDNEW
« no previous file with comments | « content/browser/renderer_host/websocket_blob_receiver.cc ('k') | content/browser/renderer_host/websocket_dispatcher_host.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698