OLD | NEW |
---|---|
(Empty) | |
1 // Copyright 2015 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 <deque> | |
6 #include <string> | |
7 #include <vector> | |
8 | |
9 #include "base/bind.h" | |
10 #include "base/callback_helpers.h" | |
11 #include "base/message_loop/message_loop.h" | |
12 #include "media/blink/multibuffer.h" | |
13 #include "media/blink/multibuffer_reader.h" | |
14 #include "media/blink/test_random.h" | |
15 #include "testing/gtest/include/gtest/gtest.h" | |
16 | |
17 const int kBlockSizeShift = 8; | |
18 const size_t kBlockSize = 1UL << kBlockSizeShift; | |
19 | |
20 namespace media { | |
21 class TestMultiBufferDataProvider; | |
22 | |
23 std::vector<TestMultiBufferDataProvider*> writers; | |
24 | |
25 class TestMultiBufferDataProvider : public media::MultiBuffer::DataProvider { | |
26 public: | |
27 TestMultiBufferDataProvider(MultiBufferBlockId pos, | |
28 size_t file_size, | |
29 int max_blocks_after_defer, | |
30 bool must_read_whole_file) | |
31 : pos_(pos), | |
32 blocks_until_deferred_(1 << 30), | |
33 max_blocks_after_defer_(max_blocks_after_defer), | |
34 file_size_(file_size), | |
35 must_read_whole_file_(must_read_whole_file) { | |
36 writers.push_back(this); | |
37 } | |
38 | |
39 ~TestMultiBufferDataProvider() override { | |
40 if (must_read_whole_file_) { | |
41 CHECK_GE(pos_ * kBlockSize, file_size_); | |
42 } | |
43 for (size_t i = 0; i < writers.size(); i++) { | |
44 if (writers[i] == this) { | |
45 writers[i] = writers.back(); | |
46 writers.pop_back(); | |
47 return; | |
48 } | |
49 } | |
50 LOG(FATAL) << "Couldn't find myself in writers!"; | |
51 } | |
52 | |
53 MultiBufferBlockId Tell() const override { return pos_; } | |
54 | |
55 bool Available() const override { return !fifo_.empty(); } | |
56 | |
57 scoped_refptr<DataBuffer> Read() override { | |
58 DCHECK(Available()); | |
59 scoped_refptr<DataBuffer> ret = fifo_.front(); | |
60 fifo_.pop_front(); | |
61 ++pos_; | |
62 return ret; | |
63 } | |
64 | |
65 void SetAvailableCallback(const base::Closure& cb) override { | |
66 DCHECK(!Available()); | |
67 cb_ = cb; | |
68 } | |
69 | |
70 void SetDeferred(bool deferred) override { | |
71 if (deferred) { | |
72 if (max_blocks_after_defer_ > 0) { | |
73 blocks_until_deferred_ = rand() % max_blocks_after_defer_; | |
DaleCurtis
2015/11/17 06:56:06
rnd_.Rand() ?
hubbe
2015/11/17 23:40:17
Done.
| |
74 } else if (max_blocks_after_defer_ < 0) { | |
75 blocks_until_deferred_ = -max_blocks_after_defer_; | |
76 } else { | |
77 blocks_until_deferred_ = 0; | |
78 } | |
79 } else { | |
80 blocks_until_deferred_ = 1 << 30; | |
81 } | |
82 } | |
83 | |
84 bool Advance() { | |
85 if (blocks_until_deferred_ == 0) | |
86 return false; | |
87 --blocks_until_deferred_; | |
88 | |
89 bool ret = true; | |
90 scoped_refptr<media::DataBuffer> block = new media::DataBuffer(kBlockSize); | |
91 size_t x = 0; | |
92 size_t byte_pos = (fifo_.size() + pos_) * kBlockSize; | |
93 for (x = 0; x < kBlockSize; x++, byte_pos++) { | |
94 if (byte_pos >= file_size_) | |
95 break; | |
96 block->writable_data()[x] = | |
97 static_cast<uint8_t>((byte_pos * 15485863) >> 16); | |
98 } | |
99 block->set_data_size(static_cast<int>(x)); | |
100 fifo_.push_back(block); | |
101 if (byte_pos == file_size_) { | |
102 fifo_.push_back(DataBuffer::CreateEOSBuffer()); | |
103 ret = false; | |
104 } | |
105 cb_.Run(); | |
106 return ret; | |
107 } | |
108 | |
109 private: | |
110 std::deque<scoped_refptr<media::DataBuffer>> fifo_; | |
111 MultiBufferBlockId pos_; | |
112 int32_t blocks_until_deferred_; | |
113 int32_t max_blocks_after_defer_; | |
114 size_t file_size_; | |
115 bool must_read_whole_file_; | |
116 base::Closure cb_; | |
117 }; | |
118 | |
119 class TestMultiBuffer : public media::MultiBuffer { | |
120 public: | |
121 explicit TestMultiBuffer( | |
122 int32_t shift, | |
123 const scoped_refptr<media::MultiBuffer::GlobalLRU>& lru) | |
124 : media::MultiBuffer(shift, lru), | |
125 range_supported_(false), | |
126 create_ok_(true), | |
127 max_writers_(10000), | |
128 file_size_(1 << 30), | |
129 max_blocks_after_defer_(0), | |
130 must_read_whole_file_(false), | |
131 writers_created_(0) {} | |
132 | |
133 void SetMaxWriters(size_t max_writers) { max_writers_ = max_writers; } | |
134 | |
135 void CheckPresentState() { | |
136 IntervalMap<MultiBufferBlockId, int32_t> tmp; | |
137 for (DataMap::iterator i = data_.begin(); i != data_.end(); ++i) { | |
138 CHECK(i->second); // Null poineters are not allowed in data_ | |
139 CHECK_NE(!!pinned_[i->first], lru_->Contains(this, i->first)) | |
140 << " i->first = " << i->first; | |
141 tmp.IncrementInterval(i->first, i->first + 1, 1); | |
142 } | |
143 IntervalMap<MultiBufferBlockId, int32_t>::const_iterator tmp_iterator = | |
144 tmp.begin(); | |
145 IntervalMap<MultiBufferBlockId, int32_t>::const_iterator present_iterator = | |
146 present_.begin(); | |
147 while (tmp_iterator != tmp.end() && present_iterator != tmp.end()) { | |
148 EXPECT_EQ(tmp_iterator.interval_begin(), | |
149 present_iterator.interval_begin()); | |
150 EXPECT_EQ(tmp_iterator.interval_end(), present_iterator.interval_end()); | |
151 EXPECT_EQ(tmp_iterator.value(), present_iterator.value()); | |
152 ++tmp_iterator; | |
153 ++present_iterator; | |
154 } | |
155 EXPECT_TRUE(tmp_iterator == tmp.end()); | |
156 EXPECT_TRUE(present_iterator == present_.end()); | |
157 } | |
158 | |
159 void CheckLRUState() { | |
160 for (DataMap::iterator i = data_.begin(); i != data_.end(); ++i) { | |
161 CHECK(i->second); // Null poineters are not allowed in data_ | |
162 CHECK_NE(!!pinned_[i->first], lru_->Contains(this, i->first)) | |
163 << " i->first = " << i->first; | |
164 CHECK_EQ(1, present_[i->first]) << " i->first = " << i->first; | |
165 } | |
166 } | |
167 | |
168 void SetFileSize(size_t file_size) { file_size_ = file_size; } | |
169 | |
170 void SetMaxBlocksAfterDefer(int32_t max_blocks_after_defer) { | |
171 max_blocks_after_defer_ = max_blocks_after_defer; | |
172 } | |
173 | |
174 void SetMustReadWholeFile(bool must_read_whole_file) { | |
175 must_read_whole_file_ = must_read_whole_file; | |
176 } | |
177 | |
178 int32_t writers_created() const { return writers_created_; } | |
179 | |
180 void SetRangeSupported(bool supported) { range_supported_ = supported; } | |
181 | |
182 protected: | |
183 DataProvider* CreateWriter(const MultiBufferBlockId& pos) override { | |
184 DCHECK(create_ok_); | |
185 writers_created_++; | |
186 CHECK_LT(writers.size(), max_writers_); | |
187 return new TestMultiBufferDataProvider( | |
188 pos, file_size_, max_blocks_after_defer_, must_read_whole_file_); | |
189 } | |
190 void Prune(size_t max_to_free) override { | |
191 // Prune should not cause additional writers to be spawned. | |
192 create_ok_ = false; | |
193 MultiBuffer::Prune(max_to_free); | |
194 create_ok_ = true; | |
195 } | |
196 | |
197 bool RangeSupported() const override { return range_supported_; } | |
198 | |
199 private: | |
200 bool range_supported_; | |
201 bool create_ok_; | |
202 size_t max_writers_; | |
203 size_t file_size_; | |
204 int32_t max_blocks_after_defer_; | |
205 bool must_read_whole_file_; | |
206 int32_t writers_created_; | |
207 }; | |
208 } | |
209 | |
210 class MultiBufferTest : public testing::Test { | |
211 public: | |
212 MultiBufferTest() | |
213 : lru_(new media::MultiBuffer::GlobalLRU()), | |
214 multibuffer_(kBlockSizeShift, lru_), | |
215 rnd_(42) {} | |
216 | |
217 void Advance() { | |
218 CHECK(media::writers.size()); | |
219 media::writers[rand() % media::writers.size()]->Advance(); | |
DaleCurtis
2015/11/17 06:56:06
rnd_.Rand()?
hubbe
2015/11/17 23:40:18
Done.
| |
220 } | |
221 | |
222 bool AdvanceAll() { | |
223 bool advanced = false; | |
224 for (size_t i = 0; i < media::writers.size(); i++) { | |
225 advanced |= media::writers[i]->Advance(); | |
226 } | |
227 multibuffer_.CheckLRUState(); | |
228 return advanced; | |
229 } | |
230 | |
231 protected: | |
232 scoped_refptr<media::MultiBuffer::GlobalLRU> lru_; | |
233 media::TestMultiBuffer multibuffer_; | |
234 base::MessageLoop message_loop_; | |
235 media::TestRandom rnd_; | |
236 }; | |
237 | |
238 TEST_F(MultiBufferTest, ReadAll) { | |
239 multibuffer_.SetMaxWriters(1); | |
240 size_t pos = 0; | |
241 size_t end = 10000; | |
242 multibuffer_.SetFileSize(10000); | |
243 multibuffer_.SetMustReadWholeFile(true); | |
244 media::MultiBufferReader reader(&multibuffer_, pos, end, | |
245 base::Callback<void(int64_t, int64_t)>()); | |
246 reader.SetMaxBuffer(2000, 5000); | |
247 reader.SetPreload(1000, 1000); | |
248 while (pos < end) { | |
249 unsigned char buffer[27]; | |
250 buffer[17] = 17; | |
251 size_t to_read = std::min<size_t>(end - pos, 17); | |
252 int64_t bytes_read = reader.TryRead(buffer, to_read); | |
253 if (bytes_read) { | |
254 EXPECT_EQ(buffer[17], 17); | |
255 for (int64_t i = 0; i < bytes_read; i++) { | |
256 uint8_t expected = static_cast<uint8_t>((pos * 15485863) >> 16); | |
257 EXPECT_EQ(expected, buffer[i]) << " pos = " << pos; | |
258 pos++; | |
259 } | |
260 } else { | |
261 Advance(); | |
262 } | |
263 } | |
264 } | |
265 | |
266 TEST_F(MultiBufferTest, ReadAllAdvanceFirst) { | |
267 multibuffer_.SetMaxWriters(1); | |
268 size_t pos = 0; | |
269 size_t end = 10000; | |
270 multibuffer_.SetFileSize(10000); | |
271 multibuffer_.SetMustReadWholeFile(true); | |
272 media::MultiBufferReader reader(&multibuffer_, pos, end, | |
273 base::Callback<void(int64_t, int64_t)>()); | |
274 reader.SetMaxBuffer(2000, 5000); | |
275 reader.SetPreload(1000, 1000); | |
276 while (pos < end) { | |
277 unsigned char buffer[27]; | |
278 buffer[17] = 17; | |
279 size_t to_read = std::min<size_t>(end - pos, 17); | |
280 while (AdvanceAll()) | |
281 ; | |
282 int64_t bytes = reader.TryRead(buffer, to_read); | |
283 EXPECT_GT(bytes, 0); | |
284 EXPECT_EQ(buffer[17], 17); | |
285 for (int64_t i = 0; i < bytes; i++) { | |
286 uint8_t expected = static_cast<uint8_t>((pos * 15485863) >> 16); | |
287 EXPECT_EQ(expected, buffer[i]) << " pos = " << pos; | |
288 pos++; | |
289 } | |
290 } | |
291 } | |
292 | |
293 // Checks that if the data provider provides too much data after we told it | |
294 // to defer, we kill it. | |
295 TEST_F(MultiBufferTest, ReadAllAdvanceFirst_NeverDefer) { | |
296 multibuffer_.SetMaxWriters(1); | |
297 size_t pos = 0; | |
298 size_t end = 10000; | |
299 multibuffer_.SetFileSize(10000); | |
300 multibuffer_.SetMaxBlocksAfterDefer(-10000); | |
301 multibuffer_.SetRangeSupported(true); | |
302 media::MultiBufferReader reader(&multibuffer_, pos, end, | |
303 base::Callback<void(int64_t, int64_t)>()); | |
304 reader.SetMaxBuffer(2000, 5000); | |
305 reader.SetPreload(1000, 1000); | |
306 while (pos < end) { | |
307 unsigned char buffer[27]; | |
308 buffer[17] = 17; | |
309 size_t to_read = std::min<size_t>(end - pos, 17); | |
310 while (AdvanceAll()) | |
311 ; | |
312 int64_t bytes = reader.TryRead(buffer, to_read); | |
313 EXPECT_GT(bytes, 0); | |
314 EXPECT_EQ(buffer[17], 17); | |
315 for (int64_t i = 0; i < bytes; i++) { | |
316 uint8_t expected = static_cast<uint8_t>((pos * 15485863) >> 16); | |
317 EXPECT_EQ(expected, buffer[i]) << " pos = " << pos; | |
318 pos++; | |
319 } | |
320 } | |
321 EXPECT_GT(multibuffer_.writers_created(), 1); | |
322 } | |
323 | |
324 // Same as ReadAllAdvanceFirst_NeverDefer, but the url doesn't support | |
325 // ranges, so we don't destroy it no matter how much data it provides. | |
326 TEST_F(MultiBufferTest, ReadAllAdvanceFirst_NeverDefer2) { | |
327 multibuffer_.SetMaxWriters(1); | |
328 size_t pos = 0; | |
329 size_t end = 10000; | |
330 multibuffer_.SetFileSize(10000); | |
331 multibuffer_.SetMustReadWholeFile(true); | |
332 multibuffer_.SetMaxBlocksAfterDefer(-10000); | |
333 media::MultiBufferReader reader(&multibuffer_, pos, end, | |
334 base::Callback<void(int64_t, int64_t)>()); | |
335 reader.SetMaxBuffer(2000, 5000); | |
336 reader.SetPreload(1000, 1000); | |
337 while (pos < end) { | |
338 unsigned char buffer[27]; | |
339 buffer[17] = 17; | |
340 size_t to_read = std::min<size_t>(end - pos, 17); | |
341 while (AdvanceAll()) | |
342 ; | |
343 int64_t bytes = reader.TryRead(buffer, to_read); | |
344 EXPECT_GT(bytes, 0); | |
345 EXPECT_EQ(buffer[17], 17); | |
346 for (int64_t i = 0; i < bytes; i++) { | |
347 uint8_t expected = static_cast<uint8_t>((pos * 15485863) >> 16); | |
348 EXPECT_EQ(expected, buffer[i]) << " pos = " << pos; | |
349 pos++; | |
350 } | |
351 } | |
352 } | |
353 | |
354 TEST_F(MultiBufferTest, LRUTest) { | |
355 int64_t max_size = 17; | |
356 int64_t current_size = 0; | |
357 lru_->IncrementMaxSize(max_size); | |
358 | |
359 multibuffer_.SetMaxWriters(1); | |
360 size_t pos = 0; | |
361 size_t end = 10000; | |
362 multibuffer_.SetFileSize(10000); | |
363 media::MultiBufferReader reader(&multibuffer_, pos, end, | |
364 base::Callback<void(int64_t, int64_t)>()); | |
365 reader.SetPreload(10000, 10000); | |
366 // Note, no pinning, all data should end up in LRU. | |
367 EXPECT_EQ(current_size, lru_->Size()); | |
368 current_size += max_size; | |
369 while (AdvanceAll()) | |
370 ; | |
371 EXPECT_EQ(current_size, lru_->Size()); | |
372 lru_->IncrementMaxSize(-max_size); | |
373 lru_->Prune(3); | |
374 current_size -= 3; | |
375 EXPECT_EQ(current_size, lru_->Size()); | |
376 lru_->Prune(3); | |
377 current_size -= 3; | |
378 EXPECT_EQ(current_size, lru_->Size()); | |
379 lru_->Prune(1000); | |
380 EXPECT_EQ(0, lru_->Size()); | |
381 } | |
382 | |
383 class ReadHelper { | |
384 public: | |
385 ReadHelper(size_t end, | |
386 size_t max_read_size, | |
387 media::MultiBuffer* multibuffer, | |
388 media::TestRandom* rnd) | |
389 : pos_(0), | |
390 end_(end), | |
391 max_read_size_(max_read_size), | |
392 read_size_(0), | |
393 rnd_(rnd), | |
394 reader_(multibuffer, | |
395 pos_, | |
396 end_, | |
397 base::Callback<void(int64_t, int64_t)>()) { | |
398 reader_.SetMaxBuffer(2000, 5000); | |
399 reader_.SetPreload(1000, 1000); | |
400 } | |
401 | |
402 bool Read() { | |
403 if (read_size_ == 0) | |
404 return true; | |
405 unsigned char buffer[4096]; | |
406 CHECK_LE(read_size_, static_cast<int64_t>(sizeof(buffer))); | |
407 CHECK_EQ(pos_, reader_.Tell()); | |
408 int64_t bytes_read = reader_.TryRead(buffer, read_size_); | |
409 if (bytes_read) { | |
410 for (int64_t i = 0; i < bytes_read; i++) { | |
411 unsigned char expected = (pos_ * 15485863) >> 16; | |
412 EXPECT_EQ(expected, buffer[i]) << " pos = " << pos_; | |
413 pos_++; | |
414 } | |
415 CHECK_EQ(pos_, reader_.Tell()); | |
416 return true; | |
417 } | |
418 return false; | |
419 } | |
420 | |
421 void StartRead() { | |
422 CHECK_EQ(pos_, reader_.Tell()); | |
423 read_size_ = std::min(1 + rnd_->Rand() % (max_read_size_ - 1), end_ - pos_); | |
424 if (!Read()) { | |
425 reader_.Wait(read_size_, | |
426 base::Bind(&ReadHelper::WaitCB, base::Unretained(this))); | |
427 } | |
428 } | |
429 | |
430 void WaitCB() { CHECK(Read()); } | |
431 | |
432 void Seek() { | |
433 pos_ = rnd_->Rand() % end_; | |
434 reader_.Seek(pos_); | |
435 CHECK_EQ(pos_, reader_.Tell()); | |
436 } | |
437 | |
438 private: | |
439 int64_t pos_; | |
440 int64_t end_; | |
441 int64_t max_read_size_; | |
442 int64_t read_size_; | |
443 media::TestRandom* rnd_; | |
444 media::MultiBufferReader reader_; | |
445 }; | |
446 | |
447 TEST_F(MultiBufferTest, RandomTest) { | |
448 size_t file_size = 1000000; | |
449 multibuffer_.SetFileSize(file_size); | |
450 multibuffer_.SetMaxBlocksAfterDefer(10); | |
451 std::vector<ReadHelper*> read_helpers; | |
452 for (size_t i = 0; i < 20; i++) { | |
453 read_helpers.push_back( | |
454 new ReadHelper(file_size, 1000, &multibuffer_, &rnd_)); | |
455 } | |
456 for (int i = 0; i < 10000; i++) { | |
457 if (rnd_.Rand() & 1) { | |
458 if (!media::writers.empty()) | |
459 Advance(); | |
460 } else { | |
461 size_t j = rnd_.Rand() % read_helpers.size(); | |
462 if (rnd_.Rand() % 100 < 3) | |
463 read_helpers[j]->Seek(); | |
464 read_helpers[j]->StartRead(); | |
465 multibuffer_.CheckLRUState(); | |
466 } | |
467 } | |
468 multibuffer_.CheckPresentState(); | |
469 while (!read_helpers.empty()) { | |
470 delete read_helpers.back(); | |
471 read_helpers.pop_back(); | |
472 } | |
473 } | |
474 | |
475 TEST_F(MultiBufferTest, RandomTest_RangeSupported) { | |
476 size_t file_size = 1000000; | |
477 multibuffer_.SetFileSize(file_size); | |
478 multibuffer_.SetMaxBlocksAfterDefer(10); | |
479 std::vector<ReadHelper*> read_helpers; | |
480 multibuffer_.SetRangeSupported(true); | |
481 for (size_t i = 0; i < 20; i++) { | |
482 read_helpers.push_back( | |
483 new ReadHelper(file_size, 1000, &multibuffer_, &rnd_)); | |
484 } | |
485 for (int i = 0; i < 10000; i++) { | |
486 if (rnd_.Rand() & 1) { | |
487 if (!media::writers.empty()) | |
488 Advance(); | |
489 } else { | |
490 size_t j = rnd_.Rand() % read_helpers.size(); | |
491 if (rnd_.Rand() % 100 < 3) | |
492 read_helpers[j]->Seek(); | |
493 read_helpers[j]->StartRead(); | |
494 multibuffer_.CheckLRUState(); | |
495 } | |
496 } | |
497 multibuffer_.CheckPresentState(); | |
498 while (!read_helpers.empty()) { | |
499 delete read_helpers.back(); | |
500 read_helpers.pop_back(); | |
501 } | |
502 } | |
OLD | NEW |