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

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

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

Powered by Google App Engine
This is Rietveld 408576698