Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/image/test/gtest/TestSourceBuffer.cpp
Line
Count
Source (jump to first uncovered line)
1
/* This Source Code Form is subject to the terms of the Mozilla Public
2
 * License, v. 2.0. If a copy of the MPL was not distributed with this
3
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4
5
#include "gtest/gtest.h"
6
7
#include <algorithm>
8
#include <cstdint>
9
10
#include "Common.h"
11
#include "mozilla/Move.h"
12
#include "nsIInputStream.h"
13
#include "SourceBuffer.h"
14
#include "SurfaceCache.h"
15
16
using namespace mozilla;
17
using namespace mozilla::image;
18
19
using std::min;
20
21
void
22
ExpectChunkAndByteCount(const SourceBufferIterator& aIterator,
23
                        uint32_t aChunks,
24
                        size_t aBytes)
25
0
{
26
0
  EXPECT_EQ(aChunks, aIterator.ChunkCount());
27
0
  EXPECT_EQ(aBytes, aIterator.ByteCount());
28
0
}
29
30
void
31
ExpectRemainingBytes(const SourceBufferIterator& aIterator, size_t aBytes)
32
0
{
33
0
  EXPECT_TRUE(aIterator.RemainingBytesIsNoMoreThan(aBytes));
34
0
  EXPECT_TRUE(aIterator.RemainingBytesIsNoMoreThan(aBytes + 1));
35
0
36
0
  if (aBytes > 0) {
37
0
    EXPECT_FALSE(aIterator.RemainingBytesIsNoMoreThan(0));
38
0
    EXPECT_FALSE(aIterator.RemainingBytesIsNoMoreThan(aBytes - 1));
39
0
  }
40
0
}
41
42
char
43
GenerateByte(size_t aIndex)
44
0
{
45
0
  uint8_t byte = aIndex % 256;
46
0
  return *reinterpret_cast<char*>(&byte);
47
0
}
48
49
void
50
GenerateData(char* aOutput, size_t aOffset, size_t aLength)
51
0
{
52
0
  for (size_t i = 0; i < aLength; ++i) {
53
0
    aOutput[i] = GenerateByte(aOffset + i);
54
0
  }
55
0
}
56
57
void
58
GenerateData(char* aOutput, size_t aLength)
59
0
{
60
0
  GenerateData(aOutput, 0, aLength);
61
0
}
62
63
void
64
CheckData(const char* aData, size_t aOffset, size_t aLength)
65
0
{
66
0
  for (size_t i = 0; i < aLength; ++i) {
67
0
    ASSERT_EQ(GenerateByte(aOffset + i), aData[i]);
68
0
  }
69
0
}
70
71
enum class AdvanceMode
72
{
73
  eAdvanceAsMuchAsPossible,
74
  eAdvanceByLengthExactly
75
};
76
77
class ImageSourceBuffer : public ::testing::Test
78
{
79
public:
80
  ImageSourceBuffer()
81
    : mSourceBuffer(new SourceBuffer)
82
    , mExpectNoResume(new ExpectNoResume)
83
    , mCountResumes(new CountResumes)
84
0
  {
85
0
    GenerateData(mData, sizeof(mData));
86
0
    EXPECT_FALSE(mSourceBuffer->IsComplete());
87
0
  }
88
89
protected:
90
  void CheckedAppendToBuffer(const char* aData, size_t aLength)
91
0
  {
92
0
    EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->Append(aData, aLength)));
93
0
  }
94
95
  void CheckedAppendToBufferLastByteForLength(size_t aLength)
96
0
  {
97
0
    const char lastByte = GenerateByte(aLength);
98
0
    CheckedAppendToBuffer(&lastByte, 1);
99
0
  }
100
101
  void CheckedAppendToBufferInChunks(size_t aChunkLength, size_t aTotalLength)
102
0
  {
103
0
    char* data = new char[aChunkLength];
104
0
105
0
    size_t bytesWritten = 0;
106
0
    while (bytesWritten < aTotalLength) {
107
0
      GenerateData(data, bytesWritten, aChunkLength);
108
0
      size_t toWrite = min(aChunkLength, aTotalLength - bytesWritten);
109
0
      CheckedAppendToBuffer(data, toWrite);
110
0
      bytesWritten += toWrite;
111
0
    }
112
0
113
0
    delete[] data;
114
0
  }
115
116
  void CheckedCompleteBuffer(nsresult aCompletionStatus = NS_OK)
117
0
  {
118
0
    mSourceBuffer->Complete(aCompletionStatus);
119
0
    EXPECT_TRUE(mSourceBuffer->IsComplete());
120
0
  }
121
122
  void CheckedCompleteBuffer(SourceBufferIterator& aIterator,
123
                             size_t aLength,
124
                             nsresult aCompletionStatus = NS_OK)
125
0
  {
126
0
    CheckedCompleteBuffer(aCompletionStatus);
127
0
    ExpectRemainingBytes(aIterator, aLength);
128
0
  }
129
130
  void CheckedAdvanceIteratorStateOnly(SourceBufferIterator& aIterator,
131
                                       size_t aLength,
132
                                       uint32_t aChunks,
133
                                       size_t aTotalLength,
134
                                       AdvanceMode aAdvanceMode
135
                                         = AdvanceMode::eAdvanceAsMuchAsPossible)
136
0
  {
137
0
    const size_t advanceBy = aAdvanceMode == AdvanceMode::eAdvanceAsMuchAsPossible
138
0
                           ? SIZE_MAX
139
0
                           : aLength;
140
0
141
0
    auto state = aIterator.AdvanceOrScheduleResume(advanceBy, mExpectNoResume);
142
0
    ASSERT_EQ(SourceBufferIterator::READY, state);
143
0
    EXPECT_TRUE(aIterator.Data());
144
0
    EXPECT_EQ(aLength, aIterator.Length());
145
0
146
0
    ExpectChunkAndByteCount(aIterator, aChunks, aTotalLength);
147
0
  }
148
149
  void CheckedAdvanceIteratorStateOnly(SourceBufferIterator& aIterator,
150
                                       size_t aLength)
151
0
  {
152
0
    CheckedAdvanceIteratorStateOnly(aIterator, aLength, 1, aLength);
153
0
  }
154
155
  void CheckedAdvanceIterator(SourceBufferIterator& aIterator,
156
                              size_t aLength,
157
                              uint32_t aChunks,
158
                              size_t aTotalLength,
159
                              AdvanceMode aAdvanceMode
160
                                = AdvanceMode::eAdvanceAsMuchAsPossible)
161
0
  {
162
0
    // Check that the iterator is in the expected state.
163
0
    CheckedAdvanceIteratorStateOnly(aIterator, aLength, aChunks,
164
0
                                    aTotalLength, aAdvanceMode);
165
0
166
0
    // Check that we read the expected data. To do this, we need to compute our
167
0
    // offset in the SourceBuffer, but fortunately that's pretty easy: it's the
168
0
    // total number of bytes the iterator has advanced through, minus the length
169
0
    // of the current chunk.
170
0
    const size_t offset = aIterator.ByteCount() - aIterator.Length();
171
0
    CheckData(aIterator.Data(), offset, aIterator.Length());
172
0
  }
173
174
  void CheckedAdvanceIterator(SourceBufferIterator& aIterator, size_t aLength)
175
0
  {
176
0
    CheckedAdvanceIterator(aIterator, aLength, 1, aLength);
177
0
  }
178
179
  void CheckIteratorMustWait(SourceBufferIterator& aIterator,
180
                             IResumable* aOnResume)
181
0
  {
182
0
    auto state = aIterator.AdvanceOrScheduleResume(1, aOnResume);
183
0
    EXPECT_EQ(SourceBufferIterator::WAITING, state);
184
0
  }
185
186
  void CheckIteratorIsComplete(SourceBufferIterator& aIterator,
187
                               uint32_t aChunks,
188
                               size_t aTotalLength,
189
                               nsresult aCompletionStatus = NS_OK)
190
0
  {
191
0
    ASSERT_TRUE(mSourceBuffer->IsComplete());
192
0
    auto state = aIterator.AdvanceOrScheduleResume(1, mExpectNoResume);
193
0
    ASSERT_EQ(SourceBufferIterator::COMPLETE, state);
194
0
    EXPECT_EQ(aCompletionStatus, aIterator.CompletionStatus());
195
0
    ExpectRemainingBytes(aIterator, 0);
196
0
    ExpectChunkAndByteCount(aIterator, aChunks, aTotalLength);
197
0
  }
198
199
  void CheckIteratorIsComplete(SourceBufferIterator& aIterator,
200
                               size_t aTotalLength)
201
0
  {
202
0
    CheckIteratorIsComplete(aIterator, 1, aTotalLength);
203
0
  }
204
205
  AutoInitializeImageLib mInit;
206
  char mData[9];
207
  RefPtr<SourceBuffer> mSourceBuffer;
208
  RefPtr<ExpectNoResume> mExpectNoResume;
209
  RefPtr<CountResumes> mCountResumes;
210
};
211
212
TEST_F(ImageSourceBuffer, InitialState)
213
0
{
214
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
215
0
216
0
  // RemainingBytesIsNoMoreThan() should always return false in the initial
217
0
  // state, since we can't know the answer until Complete() has been called.
218
0
  EXPECT_FALSE(iterator.RemainingBytesIsNoMoreThan(0));
219
0
  EXPECT_FALSE(iterator.RemainingBytesIsNoMoreThan(SIZE_MAX));
220
0
221
0
  // We haven't advanced our iterator at all, so its counters should be zero.
222
0
  ExpectChunkAndByteCount(iterator, 0, 0);
223
0
224
0
  // Attempt to advance; we should fail, and end up in the WAITING state. We
225
0
  // expect no resumes because we don't actually append anything to the
226
0
  // SourceBuffer in this test.
227
0
  CheckIteratorMustWait(iterator, mExpectNoResume);
228
0
}
229
230
TEST_F(ImageSourceBuffer, ZeroLengthBufferAlwaysFails)
231
0
{
232
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
233
0
234
0
  // Complete the buffer without writing to it, providing a successful
235
0
  // completion status.
236
0
  CheckedCompleteBuffer(iterator, 0);
237
0
238
0
  // Completing a buffer without writing to it results in an automatic failure;
239
0
  // make sure that the actual completion status we get from the iterator
240
0
  // reflects this.
241
0
  CheckIteratorIsComplete(iterator, 0, 0, NS_ERROR_FAILURE);
242
0
}
243
244
TEST_F(ImageSourceBuffer, CompleteSuccess)
245
0
{
246
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
247
0
248
0
  // Write a single byte to the buffer and complete the buffer. (We have to
249
0
  // write at least one byte because completing a zero length buffer always
250
0
  // fails; see the ZeroLengthBufferAlwaysFails test.)
251
0
  CheckedAppendToBuffer(mData, 1);
252
0
  CheckedCompleteBuffer(iterator, 1);
253
0
254
0
  // We should be able to advance once (to read the single byte) and then should
255
0
  // reach the COMPLETE state with a successful status.
256
0
  CheckedAdvanceIterator(iterator, 1);
257
0
  CheckIteratorIsComplete(iterator, 1);
258
0
}
259
260
TEST_F(ImageSourceBuffer, CompleteFailure)
261
0
{
262
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
263
0
264
0
  // Write a single byte to the buffer and complete the buffer. (We have to
265
0
  // write at least one byte because completing a zero length buffer always
266
0
  // fails; see the ZeroLengthBufferAlwaysFails test.)
267
0
  CheckedAppendToBuffer(mData, 1);
268
0
  CheckedCompleteBuffer(iterator, 1, NS_ERROR_FAILURE);
269
0
270
0
  // Advance the iterator. Because a failing status is propagated to the
271
0
  // iterator as soon as it advances, we won't be able to read the single byte
272
0
  // that we wrote above; we go directly into the COMPLETE state.
273
0
  CheckIteratorIsComplete(iterator, 0, 0, NS_ERROR_FAILURE);
274
0
}
275
276
TEST_F(ImageSourceBuffer, Append)
277
0
{
278
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
279
0
280
0
  // Write test data to the buffer.
281
0
  EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->ExpectLength(sizeof(mData))));
282
0
  CheckedAppendToBuffer(mData, sizeof(mData));
283
0
  CheckedCompleteBuffer(iterator, sizeof(mData));
284
0
285
0
  // Verify that we can read it back via the iterator, and that the final state
286
0
  // is what we expect.
287
0
  CheckedAdvanceIterator(iterator, sizeof(mData));
288
0
  CheckIteratorIsComplete(iterator, sizeof(mData));
289
0
}
290
291
TEST_F(ImageSourceBuffer, HugeAppendFails)
292
0
{
293
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
294
0
295
0
  // We should fail to append anything bigger than what the SurfaceCache can
296
0
  // hold, so use the SurfaceCache's maximum capacity to calculate what a
297
0
  // "massive amount of data" (see below) consists of on this platform.
298
0
  ASSERT_LT(SurfaceCache::MaximumCapacity(), SIZE_MAX);
299
0
  const size_t hugeSize = SurfaceCache::MaximumCapacity() + 1;
300
0
301
0
  // Attempt to write a massive amount of data and verify that it fails. (We'd
302
0
  // get a buffer overrun during the test if it succeeds, but if it succeeds
303
0
  // that's the least of our problems.)
304
0
  EXPECT_TRUE(NS_FAILED(mSourceBuffer->Append(mData, hugeSize)));
305
0
  EXPECT_TRUE(mSourceBuffer->IsComplete());
306
0
  CheckIteratorIsComplete(iterator, 0, 0, NS_ERROR_OUT_OF_MEMORY);
307
0
}
308
309
TEST_F(ImageSourceBuffer, AppendFromInputStream)
310
0
{
311
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
312
0
313
0
  // Construct an input stream with some arbitrary data. (We use test data from
314
0
  // one of the decoder tests.)
315
0
  nsCOMPtr<nsIInputStream> inputStream = LoadFile(GreenPNGTestCase().mPath);
316
0
  ASSERT_TRUE(inputStream != nullptr);
317
0
318
0
  // Figure out how much data we have.
319
0
  uint64_t length;
320
0
  ASSERT_TRUE(NS_SUCCEEDED(inputStream->Available(&length)));
321
0
322
0
  // Write test data to the buffer.
323
0
  EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->AppendFromInputStream(inputStream,
324
0
                                                                length)));
325
0
  CheckedCompleteBuffer(iterator, length);
326
0
327
0
  // Verify that the iterator sees the appropriate amount of data.
328
0
  CheckedAdvanceIteratorStateOnly(iterator, length);
329
0
  CheckIteratorIsComplete(iterator, length);
330
0
}
331
332
TEST_F(ImageSourceBuffer, AppendAfterComplete)
333
0
{
334
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
335
0
336
0
  // Write test data to the buffer.
337
0
  EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->ExpectLength(sizeof(mData))));
338
0
  CheckedAppendToBuffer(mData, sizeof(mData));
339
0
  CheckedCompleteBuffer(iterator, sizeof(mData));
340
0
341
0
  // Verify that we can read it back via the iterator, and that the final state
342
0
  // is what we expect.
343
0
  CheckedAdvanceIterator(iterator, sizeof(mData));
344
0
  CheckIteratorIsComplete(iterator, sizeof(mData));
345
0
346
0
  // Write more data to the completed buffer.
347
0
  EXPECT_TRUE(NS_FAILED(mSourceBuffer->Append(mData, sizeof(mData))));
348
0
349
0
  // Try to read with a new iterator and verify that the new data got ignored.
350
0
  SourceBufferIterator iterator2 = mSourceBuffer->Iterator();
351
0
  CheckedAdvanceIterator(iterator2, sizeof(mData));
352
0
  CheckIteratorIsComplete(iterator2, sizeof(mData));
353
0
}
354
355
TEST_F(ImageSourceBuffer, MinChunkCapacity)
356
0
{
357
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
358
0
359
0
  // Write test data to the buffer using many small appends. Since
360
0
  // ExpectLength() isn't being called, we should be able to write up to
361
0
  // SourceBuffer::MIN_CHUNK_CAPACITY bytes without a second chunk being
362
0
  // allocated.
363
0
  CheckedAppendToBufferInChunks(10, SourceBuffer::MIN_CHUNK_CAPACITY);
364
0
365
0
  // Verify that the iterator sees the appropriate amount of data.
366
0
  CheckedAdvanceIterator(iterator, SourceBuffer::MIN_CHUNK_CAPACITY);
367
0
368
0
  // Write one more byte; we expect to see that it triggers an allocation.
369
0
  CheckedAppendToBufferLastByteForLength(SourceBuffer::MIN_CHUNK_CAPACITY);
370
0
  CheckedCompleteBuffer(iterator, 1);
371
0
372
0
  // Verify that the iterator sees the new byte and a new chunk has been
373
0
  // allocated.
374
0
  CheckedAdvanceIterator(iterator, 1, 2, SourceBuffer::MIN_CHUNK_CAPACITY + 1);
375
0
  CheckIteratorIsComplete(iterator, 2, SourceBuffer::MIN_CHUNK_CAPACITY + 1);
376
0
}
377
378
TEST_F(ImageSourceBuffer, ExpectLengthAllocatesRequestedCapacity)
379
0
{
380
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
381
0
382
0
  // Write SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the buffer,
383
0
  // but call ExpectLength() first to make SourceBuffer expect only a single
384
0
  // byte. We expect this to still result in two chunks, because we trust the
385
0
  // initial guess of ExpectLength() but after that it will only allocate chunks
386
0
  // of at least MIN_CHUNK_CAPACITY bytes.
387
0
  EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->ExpectLength(1)));
388
0
  CheckedAppendToBufferInChunks(10, SourceBuffer::MIN_CHUNK_CAPACITY);
389
0
  CheckedCompleteBuffer(iterator, SourceBuffer::MIN_CHUNK_CAPACITY);
390
0
391
0
  // Verify that the iterator sees a first chunk with 1 byte, and a second chunk
392
0
  // with the remaining data.
393
0
  CheckedAdvanceIterator(iterator, 1, 1, 1);
394
0
  CheckedAdvanceIterator(iterator, SourceBuffer::MIN_CHUNK_CAPACITY - 1, 2,
395
0
                       SourceBuffer::MIN_CHUNK_CAPACITY);
396
0
  CheckIteratorIsComplete(iterator, 2, SourceBuffer::MIN_CHUNK_CAPACITY);
397
0
}
398
399
TEST_F(ImageSourceBuffer, ExpectLengthGrowsAboveMinCapacity)
400
0
{
401
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
402
0
403
0
  // Write two times SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the
404
0
  // buffer, calling ExpectLength() with the correct length first. We expect
405
0
  // this to result in only one chunk, because ExpectLength() allows us to
406
0
  // allocate a larger first chunk than MIN_CHUNK_CAPACITY bytes.
407
0
  const size_t length = 2 * SourceBuffer::MIN_CHUNK_CAPACITY;
408
0
  EXPECT_TRUE(NS_SUCCEEDED(mSourceBuffer->ExpectLength(length)));
409
0
  CheckedAppendToBufferInChunks(10, length);
410
0
411
0
  // Verify that the iterator sees a single chunk.
412
0
  CheckedAdvanceIterator(iterator, length);
413
0
414
0
  // Write one more byte; we expect to see that it triggers an allocation.
415
0
  CheckedAppendToBufferLastByteForLength(length);
416
0
  CheckedCompleteBuffer(iterator, 1);
417
0
418
0
  // Verify that the iterator sees the new byte and a new chunk has been
419
0
  // allocated.
420
0
  CheckedAdvanceIterator(iterator, 1, 2, length + 1);
421
0
  CheckIteratorIsComplete(iterator, 2, length + 1);
422
0
}
423
424
TEST_F(ImageSourceBuffer, HugeExpectLengthFails)
425
0
{
426
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
427
0
428
0
  // ExpectLength() should fail if the length is bigger than what the
429
0
  // SurfaceCache can hold, so use the SurfaceCache's maximum capacity to
430
0
  // calculate what a "massive amount of data" (see below) consists of on this
431
0
  // platform.
432
0
  ASSERT_LT(SurfaceCache::MaximumCapacity(), SIZE_MAX);
433
0
  const size_t hugeSize = SurfaceCache::MaximumCapacity() + 1;
434
0
435
0
  // Attempt to write a massive amount of data and verify that it fails. (We'd
436
0
  // get a buffer overrun during the test if it succeeds, but if it succeeds
437
0
  // that's the least of our problems.)
438
0
  EXPECT_TRUE(NS_FAILED(mSourceBuffer->ExpectLength(hugeSize)));
439
0
  EXPECT_TRUE(mSourceBuffer->IsComplete());
440
0
  CheckIteratorIsComplete(iterator, 0, 0, NS_ERROR_INVALID_ARG);
441
0
}
442
443
TEST_F(ImageSourceBuffer, LargeAppendsAllocateOnlyOneChunk)
444
0
{
445
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
446
0
447
0
  // Write two times SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the
448
0
  // buffer in a single Append() call. We expect this to result in only one
449
0
  // chunk even though ExpectLength() wasn't called, because we should always
450
0
  // allocate a new chunk large enough to store the data we have at hand.
451
0
  constexpr size_t length = 2 * SourceBuffer::MIN_CHUNK_CAPACITY;
452
0
  char data[length];
453
0
  GenerateData(data, sizeof(data));
454
0
  CheckedAppendToBuffer(data, length);
455
0
456
0
  // Verify that the iterator sees a single chunk.
457
0
  CheckedAdvanceIterator(iterator, length);
458
0
459
0
  // Write one more byte; we expect to see that it triggers an allocation.
460
0
  CheckedAppendToBufferLastByteForLength(length);
461
0
  CheckedCompleteBuffer(iterator, 1);
462
0
463
0
  // Verify that the iterator sees the new byte and a new chunk has been
464
0
  // allocated.
465
0
  CheckedAdvanceIterator(iterator, 1, 2, length + 1);
466
0
  CheckIteratorIsComplete(iterator, 2, length + 1);
467
0
}
468
469
TEST_F(ImageSourceBuffer, LargeAppendsAllocateAtMostOneChunk)
470
0
{
471
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
472
0
473
0
  // Allocate some data we'll use below.
474
0
  constexpr size_t firstWriteLength = SourceBuffer::MIN_CHUNK_CAPACITY / 2;
475
0
  constexpr size_t secondWriteLength = 3 * SourceBuffer::MIN_CHUNK_CAPACITY;
476
0
  constexpr size_t totalLength = firstWriteLength + secondWriteLength;
477
0
  char data[totalLength];
478
0
  GenerateData(data, sizeof(data));
479
0
480
0
  // Write half of SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the
481
0
  // buffer in a single Append() call. This should fill half of the first chunk.
482
0
  CheckedAppendToBuffer(data, firstWriteLength);
483
0
484
0
  // Write three times SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the
485
0
  // buffer in a single Append() call. We expect this to result in the first of
486
0
  // the first chunk being filled and a new chunk being allocated for the
487
0
  // remainder.
488
0
  CheckedAppendToBuffer(data + firstWriteLength, secondWriteLength);
489
0
490
0
  // Verify that the iterator sees a MIN_CHUNK_CAPACITY-length chunk.
491
0
  CheckedAdvanceIterator(iterator, SourceBuffer::MIN_CHUNK_CAPACITY);
492
0
493
0
  // Verify that the iterator sees a second chunk of the length we expect.
494
0
  const size_t expectedSecondChunkLength =
495
0
    totalLength - SourceBuffer::MIN_CHUNK_CAPACITY;
496
0
  CheckedAdvanceIterator(iterator, expectedSecondChunkLength, 2, totalLength);
497
0
498
0
  // Write one more byte; we expect to see that it triggers an allocation.
499
0
  CheckedAppendToBufferLastByteForLength(totalLength);
500
0
  CheckedCompleteBuffer(iterator, 1);
501
0
502
0
  // Verify that the iterator sees the new byte and a new chunk has been
503
0
  // allocated.
504
0
  CheckedAdvanceIterator(iterator, 1, 3, totalLength + 1);
505
0
  CheckIteratorIsComplete(iterator, 3, totalLength + 1);
506
0
}
507
508
TEST_F(ImageSourceBuffer, OversizedAppendsAllocateAtMostOneChunk)
509
0
{
510
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
511
0
512
0
  // Allocate some data we'll use below.
513
0
  constexpr size_t writeLength = SourceBuffer::MAX_CHUNK_CAPACITY + 1;
514
0
515
0
  // Write SourceBuffer::MAX_CHUNK_CAPACITY + 1 bytes of test data to the
516
0
  // buffer in a single Append() call. This should cause one chunk to be
517
0
  // allocated because we wrote it as a single block.
518
0
  CheckedAppendToBufferInChunks(writeLength, writeLength);
519
0
520
0
  // Verify that the iterator sees a MAX_CHUNK_CAPACITY+1-length chunk.
521
0
  CheckedAdvanceIterator(iterator, writeLength);
522
0
523
0
  CheckedCompleteBuffer(NS_OK);
524
0
  CheckIteratorIsComplete(iterator, 1, writeLength);
525
0
}
526
527
TEST_F(ImageSourceBuffer, CompactionHappensWhenBufferIsComplete)
528
0
{
529
0
  constexpr size_t chunkLength = SourceBuffer::MIN_CHUNK_CAPACITY;
530
0
  constexpr size_t totalLength = 2 * chunkLength;
531
0
532
0
  // Write enough data to create two chunks.
533
0
  CheckedAppendToBufferInChunks(chunkLength, totalLength);
534
0
535
0
  {
536
0
    SourceBufferIterator iterator = mSourceBuffer->Iterator();
537
0
538
0
    // Verify that the iterator sees two chunks.
539
0
    CheckedAdvanceIterator(iterator, chunkLength);
540
0
    CheckedAdvanceIterator(iterator, chunkLength, 2, totalLength);
541
0
  }
542
0
543
0
  // Complete the buffer, which should trigger compaction implicitly.
544
0
  CheckedCompleteBuffer();
545
0
546
0
  {
547
0
    SourceBufferIterator iterator = mSourceBuffer->Iterator();
548
0
549
0
    // Verify that compaction happened and there's now only one chunk.
550
0
    CheckedAdvanceIterator(iterator, totalLength);
551
0
    CheckIteratorIsComplete(iterator, 1, totalLength);
552
0
  }
553
0
}
554
555
TEST_F(ImageSourceBuffer, CompactionIsDelayedWhileIteratorsExist)
556
0
{
557
0
  constexpr size_t chunkLength = SourceBuffer::MIN_CHUNK_CAPACITY;
558
0
  constexpr size_t totalLength = 2 * chunkLength;
559
0
560
0
  {
561
0
    SourceBufferIterator outerIterator = mSourceBuffer->Iterator();
562
0
563
0
    {
564
0
      SourceBufferIterator iterator = mSourceBuffer->Iterator();
565
0
566
0
      // Write enough data to create two chunks.
567
0
      CheckedAppendToBufferInChunks(chunkLength, totalLength);
568
0
      CheckedCompleteBuffer(iterator, totalLength);
569
0
570
0
      // Verify that the iterator sees two chunks. Since there are live
571
0
      // iterators, compaction shouldn't have happened when we completed the
572
0
      // buffer.
573
0
      CheckedAdvanceIterator(iterator, chunkLength);
574
0
      CheckedAdvanceIterator(iterator, chunkLength, 2, totalLength);
575
0
      CheckIteratorIsComplete(iterator, 2, totalLength);
576
0
    }
577
0
578
0
    // Now |iterator| has been destroyed, but |outerIterator| still exists, so
579
0
    // we expect no compaction to have occurred at this point.
580
0
    CheckedAdvanceIterator(outerIterator, chunkLength);
581
0
    CheckedAdvanceIterator(outerIterator, chunkLength, 2, totalLength);
582
0
    CheckIteratorIsComplete(outerIterator, 2, totalLength);
583
0
  }
584
0
585
0
  // Now all iterators have been destroyed. Since the buffer was already
586
0
  // complete, we expect compaction to happen implicitly here.
587
0
588
0
  {
589
0
    SourceBufferIterator iterator = mSourceBuffer->Iterator();
590
0
591
0
    // Verify that compaction happened and there's now only one chunk.
592
0
    CheckedAdvanceIterator(iterator, totalLength);
593
0
    CheckIteratorIsComplete(iterator, 1, totalLength);
594
0
  }
595
0
}
596
597
TEST_F(ImageSourceBuffer, SourceBufferIteratorsCanBeMoved)
598
0
{
599
0
  constexpr size_t chunkLength = SourceBuffer::MIN_CHUNK_CAPACITY;
600
0
  constexpr size_t totalLength = 2 * chunkLength;
601
0
602
0
  // Write enough data to create two chunks. We create an iterator here to make
603
0
  // sure that compaction doesn't happen during the test.
604
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
605
0
  CheckedAppendToBufferInChunks(chunkLength, totalLength);
606
0
  CheckedCompleteBuffer(iterator, totalLength);
607
0
608
0
  auto GetIterator = [&]{
609
0
    SourceBufferIterator lambdaIterator = mSourceBuffer->Iterator();
610
0
    CheckedAdvanceIterator(lambdaIterator, chunkLength);
611
0
    return lambdaIterator;
612
0
  };
613
0
614
0
  // Move-construct |movedIterator| from the iterator returned from
615
0
  // GetIterator() and check that its state is as we expect.
616
0
  SourceBufferIterator tmpIterator = GetIterator();
617
0
  SourceBufferIterator movedIterator(std::move(tmpIterator));
618
0
  EXPECT_TRUE(movedIterator.Data());
619
0
  EXPECT_EQ(chunkLength, movedIterator.Length());
620
0
  ExpectChunkAndByteCount(movedIterator, 1, chunkLength);
621
0
622
0
  // Make sure that we can advance the iterator.
623
0
  CheckedAdvanceIterator(movedIterator, chunkLength, 2, totalLength);
624
0
625
0
  // Make sure that the iterator handles completion properly.
626
0
  CheckIteratorIsComplete(movedIterator, 2, totalLength);
627
0
628
0
  // Move-assign |movedIterator| from the iterator returned from
629
0
  // GetIterator() and check that its state is as we expect.
630
0
  tmpIterator = GetIterator();
631
0
  movedIterator = std::move(tmpIterator);
632
0
  EXPECT_TRUE(movedIterator.Data());
633
0
  EXPECT_EQ(chunkLength, movedIterator.Length());
634
0
  ExpectChunkAndByteCount(movedIterator, 1, chunkLength);
635
0
636
0
  // Make sure that we can advance the iterator.
637
0
  CheckedAdvanceIterator(movedIterator, chunkLength, 2, totalLength);
638
0
639
0
  // Make sure that the iterator handles completion properly.
640
0
  CheckIteratorIsComplete(movedIterator, 2, totalLength);
641
0
}
642
643
TEST_F(ImageSourceBuffer, SubchunkAdvance)
644
0
{
645
0
  constexpr size_t chunkLength = SourceBuffer::MIN_CHUNK_CAPACITY;
646
0
  constexpr size_t totalLength = 2 * chunkLength;
647
0
648
0
  // Write enough data to create two chunks. We create our iterator here to make
649
0
  // sure that compaction doesn't happen during the test.
650
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
651
0
  CheckedAppendToBufferInChunks(chunkLength, totalLength);
652
0
  CheckedCompleteBuffer(iterator, totalLength);
653
0
654
0
  // Advance through the first chunk. The chunk count should not increase.
655
0
  // We check that by always passing 1 for the |aChunks| parameter of
656
0
  // CheckedAdvanceIteratorStateOnly(). We have to call CheckData() manually
657
0
  // because the offset calculation in CheckedAdvanceIterator() assumes that
658
0
  // we're advancing a chunk at a time.
659
0
  size_t offset = 0;
660
0
  while (offset < chunkLength) {
661
0
    CheckedAdvanceIteratorStateOnly(iterator, 1, 1, chunkLength,
662
0
                                    AdvanceMode::eAdvanceByLengthExactly);
663
0
    CheckData(iterator.Data(), offset++, iterator.Length());
664
0
  }
665
0
666
0
  // Read the first byte of the second chunk. This is the point at which we
667
0
  // can't advance within the same chunk, so the chunk count should increase. We
668
0
  // check that by passing 2 for the |aChunks| parameter of
669
0
  // CheckedAdvanceIteratorStateOnly().
670
0
  CheckedAdvanceIteratorStateOnly(iterator, 1, 2, totalLength,
671
0
                                  AdvanceMode::eAdvanceByLengthExactly);
672
0
  CheckData(iterator.Data(), offset++, iterator.Length());
673
0
674
0
  // Read the rest of the second chunk. The chunk count should not increase.
675
0
  while (offset < totalLength) {
676
0
    CheckedAdvanceIteratorStateOnly(iterator, 1, 2, totalLength,
677
0
                                    AdvanceMode::eAdvanceByLengthExactly);
678
0
    CheckData(iterator.Data(), offset++, iterator.Length());
679
0
  }
680
0
681
0
  // Make sure we reached the end.
682
0
  CheckIteratorIsComplete(iterator, 2, totalLength);
683
0
}
684
685
TEST_F(ImageSourceBuffer, SubchunkZeroByteAdvance)
686
0
{
687
0
  constexpr size_t chunkLength = SourceBuffer::MIN_CHUNK_CAPACITY;
688
0
  constexpr size_t totalLength = 2 * chunkLength;
689
0
690
0
  // Write enough data to create two chunks. We create our iterator here to make
691
0
  // sure that compaction doesn't happen during the test.
692
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
693
0
  CheckedAppendToBufferInChunks(chunkLength, totalLength);
694
0
  CheckedCompleteBuffer(iterator, totalLength);
695
0
696
0
  // Make an initial zero-length advance. Although a zero-length advance
697
0
  // normally won't cause us to read a chunk from the SourceBuffer, we'll do so
698
0
  // if the iterator is in the initial state to keep the invariant that
699
0
  // SourceBufferIterator in the READY state always returns a non-null pointer
700
0
  // from Data().
701
0
  CheckedAdvanceIteratorStateOnly(iterator, 0, 1, chunkLength,
702
0
                                  AdvanceMode::eAdvanceByLengthExactly);
703
0
704
0
  // Advance through the first chunk. As in the |SubchunkAdvance| test, the
705
0
  // chunk count should not increase. We do a zero-length advance after each
706
0
  // normal advance to ensure that zero-length advances do not change the
707
0
  // iterator's position or cause a new chunk to be read.
708
0
  size_t offset = 0;
709
0
  while (offset < chunkLength) {
710
0
    CheckedAdvanceIteratorStateOnly(iterator, 1, 1, chunkLength,
711
0
                                    AdvanceMode::eAdvanceByLengthExactly);
712
0
    CheckData(iterator.Data(), offset++, iterator.Length());
713
0
    CheckedAdvanceIteratorStateOnly(iterator, 0, 1, chunkLength,
714
0
                                    AdvanceMode::eAdvanceByLengthExactly);
715
0
  }
716
0
717
0
  // Read the first byte of the second chunk. This is the point at which we
718
0
  // can't advance within the same chunk, so the chunk count should increase. As
719
0
  // before, we do a zero-length advance afterward.
720
0
  CheckedAdvanceIteratorStateOnly(iterator, 1, 2, totalLength,
721
0
                                  AdvanceMode::eAdvanceByLengthExactly);
722
0
  CheckData(iterator.Data(), offset++, iterator.Length());
723
0
  CheckedAdvanceIteratorStateOnly(iterator, 0, 2, totalLength,
724
0
                                  AdvanceMode::eAdvanceByLengthExactly);
725
0
726
0
  // Read the rest of the second chunk. The chunk count should not increase. As
727
0
  // before, we do a zero-length advance after each normal advance.
728
0
  while (offset < totalLength) {
729
0
    CheckedAdvanceIteratorStateOnly(iterator, 1, 2, totalLength,
730
0
                                    AdvanceMode::eAdvanceByLengthExactly);
731
0
    CheckData(iterator.Data(), offset++, iterator.Length());
732
0
    CheckedAdvanceIteratorStateOnly(iterator, 0, 2, totalLength,
733
0
                                    AdvanceMode::eAdvanceByLengthExactly);
734
0
  }
735
0
736
0
  // Make sure we reached the end.
737
0
  CheckIteratorIsComplete(iterator, 2, totalLength);
738
0
}
739
740
TEST_F(ImageSourceBuffer, SubchunkZeroByteAdvanceWithNoData)
741
0
{
742
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
743
0
744
0
  // Check that advancing by zero bytes still makes us enter the WAITING state.
745
0
  // This is because if we entered the READY state before reading any data at
746
0
  // all, we'd break the invariant that SourceBufferIterator::Data() always
747
0
  // returns a non-null pointer in the READY state.
748
0
  auto state = iterator.AdvanceOrScheduleResume(0, mCountResumes);
749
0
  EXPECT_EQ(SourceBufferIterator::WAITING, state);
750
0
751
0
  // Call Complete(). This should trigger a resume.
752
0
  CheckedCompleteBuffer();
753
0
  EXPECT_EQ(1u, mCountResumes->Count());
754
0
}
755
756
TEST_F(ImageSourceBuffer, NullIResumable)
757
0
{
758
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
759
0
760
0
  // Check that we can't advance.
761
0
  CheckIteratorMustWait(iterator, nullptr);
762
0
763
0
  // Append to the buffer, which would cause a resume if we had passed a
764
0
  // non-null IResumable.
765
0
  CheckedAppendToBuffer(mData, sizeof(mData));
766
0
  CheckedCompleteBuffer(iterator, sizeof(mData));
767
0
}
768
769
TEST_F(ImageSourceBuffer, AppendTriggersResume)
770
0
{
771
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
772
0
773
0
  // Check that we can't advance.
774
0
  CheckIteratorMustWait(iterator, mCountResumes);
775
0
776
0
  // Call Append(). This should trigger a resume.
777
0
  mSourceBuffer->Append(mData, sizeof(mData));
778
0
  EXPECT_EQ(1u, mCountResumes->Count());
779
0
}
780
781
TEST_F(ImageSourceBuffer, OnlyOneResumeTriggeredPerAppend)
782
0
{
783
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
784
0
785
0
  // Check that we can't advance.
786
0
  CheckIteratorMustWait(iterator, mCountResumes);
787
0
788
0
  // Allocate some data we'll use below.
789
0
  constexpr size_t firstWriteLength = SourceBuffer::MIN_CHUNK_CAPACITY / 2;
790
0
  constexpr size_t secondWriteLength = 3 * SourceBuffer::MIN_CHUNK_CAPACITY;
791
0
  constexpr size_t totalLength = firstWriteLength + secondWriteLength;
792
0
  char data[totalLength];
793
0
  GenerateData(data, sizeof(data));
794
0
795
0
  // Write half of SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the
796
0
  // buffer in a single Append() call. This should fill half of the first chunk.
797
0
  // This should trigger a resume.
798
0
  CheckedAppendToBuffer(data, firstWriteLength);
799
0
  EXPECT_EQ(1u, mCountResumes->Count());
800
0
801
0
  // Advance past the new data and wait again.
802
0
  CheckedAdvanceIterator(iterator, firstWriteLength);
803
0
  CheckIteratorMustWait(iterator, mCountResumes);
804
0
805
0
  // Write three times SourceBuffer::MIN_CHUNK_CAPACITY bytes of test data to the
806
0
  // buffer in a single Append() call. We expect this to result in the first of
807
0
  // the first chunk being filled and a new chunk being allocated for the
808
0
  // remainder. Even though two chunks are getting written to here, only *one*
809
0
  // resume should get triggered, for a total of two in this test.
810
0
  CheckedAppendToBuffer(data + firstWriteLength, secondWriteLength);
811
0
  EXPECT_EQ(2u, mCountResumes->Count());
812
0
}
813
814
TEST_F(ImageSourceBuffer, CompleteTriggersResume)
815
0
{
816
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
817
0
818
0
  // Check that we can't advance.
819
0
  CheckIteratorMustWait(iterator, mCountResumes);
820
0
821
0
  // Call Complete(). This should trigger a resume.
822
0
  CheckedCompleteBuffer();
823
0
  EXPECT_EQ(1u, mCountResumes->Count());
824
0
}
825
826
TEST_F(ImageSourceBuffer, ExpectLengthDoesNotTriggerResume)
827
0
{
828
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator();
829
0
830
0
  // Check that we can't advance.
831
0
  CheckIteratorMustWait(iterator, mExpectNoResume);
832
0
833
0
  // Call ExpectLength(). If this triggers a resume, |mExpectNoResume| will
834
0
  // ensure that the test fails.
835
0
  mSourceBuffer->ExpectLength(1000);
836
0
}
837
838
TEST_F(ImageSourceBuffer, CompleteSuccessWithSameReadLength)
839
0
{
840
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator(1);
841
0
842
0
  // Write a single byte to the buffer and complete the buffer. (We have to
843
0
  // write at least one byte because completing a zero length buffer always
844
0
  // fails; see the ZeroLengthBufferAlwaysFails test.)
845
0
  CheckedAppendToBuffer(mData, 1);
846
0
  CheckedCompleteBuffer(iterator, 1);
847
0
848
0
  // We should be able to advance once (to read the single byte) and then should
849
0
  // reach the COMPLETE state with a successful status.
850
0
  CheckedAdvanceIterator(iterator, 1);
851
0
  CheckIteratorIsComplete(iterator, 1);
852
0
}
853
854
TEST_F(ImageSourceBuffer, CompleteSuccessWithSmallerReadLength)
855
0
{
856
0
  // Create an iterator limited to one byte.
857
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator(1);
858
0
859
0
  // Write two bytes to the buffer and complete the buffer. (We have to
860
0
  // write at least one byte because completing a zero length buffer always
861
0
  // fails; see the ZeroLengthBufferAlwaysFails test.)
862
0
  CheckedAppendToBuffer(mData, 2);
863
0
  CheckedCompleteBuffer(iterator, 2);
864
0
865
0
  // We should be able to advance once (to read the single byte) and then should
866
0
  // reach the COMPLETE state with a successful status, because our iterator is
867
0
  // limited to a single byte, rather than the full length.
868
0
  CheckedAdvanceIterator(iterator, 1);
869
0
  CheckIteratorIsComplete(iterator, 1);
870
0
}
871
872
TEST_F(ImageSourceBuffer, CompleteSuccessWithGreaterReadLength)
873
0
{
874
0
  // Create an iterator limited to one byte.
875
0
  SourceBufferIterator iterator = mSourceBuffer->Iterator(2);
876
0
877
0
  // Write a single byte to the buffer and complete the buffer. (We have to
878
0
  // write at least one byte because completing a zero length buffer always
879
0
  // fails; see the ZeroLengthBufferAlwaysFails test.)
880
0
  CheckedAppendToBuffer(mData, 1);
881
0
  CheckedCompleteBuffer(iterator, 1);
882
0
883
0
  // We should be able to advance once (to read the single byte) and then should
884
0
  // reach the COMPLETE state with a successful status. Our iterator lets us
885
0
  // read more but the underlying buffer has been completed.
886
0
  CheckedAdvanceIterator(iterator, 1);
887
0
  CheckIteratorIsComplete(iterator, 1);
888
0
}