1 /*
2  * Copyright 2011 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "Resources.h"
9 #include "SkAutoMalloc.h"
10 #include "SkData.h"
11 #include "SkFrontBufferedStream.h"
12 #include "SkOSFile.h"
13 #include "SkOSPath.h"
14 #include "SkRandom.h"
15 #include "SkStream.h"
16 #include "SkStreamPriv.h"
17 #include "Test.h"
18 
19 #include <functional>
20 #include <limits>
21 
22 #ifndef SK_BUILD_FOR_WIN
23 #include <unistd.h>
24 #include <fcntl.h>
25 #endif
26 
27 #define MAX_SIZE    (256 * 1024)
28 
test_loop_stream(skiatest::Reporter * reporter,SkStream * stream,const void * src,size_t len,int repeat)29 static void test_loop_stream(skiatest::Reporter* reporter, SkStream* stream,
30                              const void* src, size_t len, int repeat) {
31     SkAutoSMalloc<256> storage(len);
32     void* tmp = storage.get();
33 
34     for (int i = 0; i < repeat; ++i) {
35         size_t bytes = stream->read(tmp, len);
36         REPORTER_ASSERT(reporter, bytes == len);
37         REPORTER_ASSERT(reporter, !memcmp(tmp, src, len));
38     }
39 
40     // expect EOF
41     size_t bytes = stream->read(tmp, 1);
42     REPORTER_ASSERT(reporter, 0 == bytes);
43     // isAtEnd might not return true until after the first failing read.
44     REPORTER_ASSERT(reporter, stream->isAtEnd());
45 }
46 
test_filestreams(skiatest::Reporter * reporter,const char * tmpDir)47 static void test_filestreams(skiatest::Reporter* reporter, const char* tmpDir) {
48     SkString path = SkOSPath::Join(tmpDir, "wstream_test");
49 
50     const char s[] = "abcdefghijklmnopqrstuvwxyz";
51 
52     {
53         SkFILEWStream writer(path.c_str());
54         if (!writer.isValid()) {
55             ERRORF(reporter, "Failed to create tmp file %s\n", path.c_str());
56             return;
57         }
58 
59         for (int i = 0; i < 100; ++i) {
60             writer.write(s, 26);
61         }
62     }
63 
64     {
65         SkFILEStream stream(path.c_str());
66         REPORTER_ASSERT(reporter, stream.isValid());
67         test_loop_stream(reporter, &stream, s, 26, 100);
68 
69         std::unique_ptr<SkStreamAsset> stream2(stream.duplicate());
70         test_loop_stream(reporter, stream2.get(), s, 26, 100);
71     }
72 
73     {
74         FILE* file = ::fopen(path.c_str(), "rb");
75         SkFILEStream stream(file);
76         REPORTER_ASSERT(reporter, stream.isValid());
77         test_loop_stream(reporter, &stream, s, 26, 100);
78 
79         std::unique_ptr<SkStreamAsset> stream2(stream.duplicate());
80         test_loop_stream(reporter, stream2.get(), s, 26, 100);
81     }
82 }
83 
TestWStream(skiatest::Reporter * reporter)84 static void TestWStream(skiatest::Reporter* reporter) {
85     SkDynamicMemoryWStream  ds;
86     const char s[] = "abcdefghijklmnopqrstuvwxyz";
87     int i;
88     for (i = 0; i < 100; i++) {
89         REPORTER_ASSERT(reporter, ds.write(s, 26));
90     }
91     REPORTER_ASSERT(reporter, ds.bytesWritten() == 100 * 26);
92 
93     char* dst = new char[100 * 26 + 1];
94     dst[100*26] = '*';
95     ds.copyTo(dst);
96     REPORTER_ASSERT(reporter, dst[100*26] == '*');
97     for (i = 0; i < 100; i++) {
98         REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0);
99     }
100 
101     {
102         std::unique_ptr<SkStreamAsset> stream(ds.detachAsStream());
103         REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength());
104         REPORTER_ASSERT(reporter, ds.bytesWritten() == 0);
105         test_loop_stream(reporter, stream.get(), s, 26, 100);
106 
107         std::unique_ptr<SkStreamAsset> stream2(stream->duplicate());
108         test_loop_stream(reporter, stream2.get(), s, 26, 100);
109 
110         std::unique_ptr<SkStreamAsset> stream3(stream->fork());
111         REPORTER_ASSERT(reporter, stream3->isAtEnd());
112         char tmp;
113         size_t bytes = stream->read(&tmp, 1);
114         REPORTER_ASSERT(reporter, 0 == bytes);
115         stream3->rewind();
116         test_loop_stream(reporter, stream3.get(), s, 26, 100);
117     }
118 
119     for (i = 0; i < 100; i++) {
120         REPORTER_ASSERT(reporter, ds.write(s, 26));
121     }
122     REPORTER_ASSERT(reporter, ds.bytesWritten() == 100 * 26);
123 
124     {
125         // Test that this works after a snapshot.
126         std::unique_ptr<SkStreamAsset> stream(ds.detachAsStream());
127         REPORTER_ASSERT(reporter, ds.bytesWritten() == 0);
128         test_loop_stream(reporter, stream.get(), s, 26, 100);
129 
130         std::unique_ptr<SkStreamAsset> stream2(stream->duplicate());
131         test_loop_stream(reporter, stream2.get(), s, 26, 100);
132     }
133     delete[] dst;
134 
135     SkString tmpDir = skiatest::GetTmpDir();
136     if (!tmpDir.isEmpty()) {
137         test_filestreams(reporter, tmpDir.c_str());
138     }
139 }
140 
TestPackedUInt(skiatest::Reporter * reporter)141 static void TestPackedUInt(skiatest::Reporter* reporter) {
142     // we know that packeduint tries to write 1, 2 or 4 bytes for the length,
143     // so we test values around each of those transitions (and a few others)
144     const size_t sizes[] = {
145         0, 1, 2, 0xFC, 0xFD, 0xFE, 0xFF, 0x100, 0x101, 32767, 32768, 32769,
146         0xFFFD, 0xFFFE, 0xFFFF, 0x10000, 0x10001,
147         0xFFFFFD, 0xFFFFFE, 0xFFFFFF, 0x1000000, 0x1000001,
148         0x7FFFFFFE, 0x7FFFFFFF, 0x80000000, 0x80000001, 0xFFFFFFFE, 0xFFFFFFFF
149     };
150 
151 
152     size_t i;
153     SkDynamicMemoryWStream wstream;
154 
155     for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
156         bool success = wstream.writePackedUInt(sizes[i]);
157         REPORTER_ASSERT(reporter, success);
158     }
159 
160     std::unique_ptr<SkStreamAsset> rstream(wstream.detachAsStream());
161     for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
162         size_t n = rstream->readPackedUInt();
163         if (sizes[i] != n) {
164             ERRORF(reporter, "sizes:%x != n:%x\n", i, sizes[i], n);
165         }
166     }
167 }
168 
169 // Test that setting an SkMemoryStream to a nullptr data does not result in a crash when calling
170 // methods that access fData.
TestDereferencingData(SkMemoryStream * memStream)171 static void TestDereferencingData(SkMemoryStream* memStream) {
172     memStream->read(nullptr, 0);
173     memStream->getMemoryBase();
174     (void)memStream->asData();
175 }
176 
TestNullData()177 static void TestNullData() {
178     SkMemoryStream memStream(nullptr);
179     TestDereferencingData(&memStream);
180 
181     memStream.setData(nullptr);
182     TestDereferencingData(&memStream);
183 
184 }
185 
DEF_TEST(Stream,reporter)186 DEF_TEST(Stream, reporter) {
187     TestWStream(reporter);
188     TestPackedUInt(reporter);
189     TestNullData();
190 }
191 
192 #ifndef SK_BUILD_FOR_IOS
193 /**
194  *  Tests peeking and then reading the same amount. The two should provide the
195  *  same results.
196  *  Returns the amount successfully read minus the amount successfully peeked.
197  */
compare_peek_to_read(skiatest::Reporter * reporter,SkStream * stream,size_t bytesToPeek)198 static size_t compare_peek_to_read(skiatest::Reporter* reporter,
199                                    SkStream* stream, size_t bytesToPeek) {
200     // The rest of our tests won't be very interesting if bytesToPeek is zero.
201     REPORTER_ASSERT(reporter, bytesToPeek > 0);
202     SkAutoMalloc peekStorage(bytesToPeek);
203     SkAutoMalloc readStorage(bytesToPeek);
204     void* peekPtr = peekStorage.get();
205     void* readPtr = peekStorage.get();
206 
207     const size_t bytesPeeked = stream->peek(peekPtr, bytesToPeek);
208     const size_t bytesRead = stream->read(readPtr, bytesToPeek);
209 
210     // bytesRead should only be less than attempted if the stream is at the
211     // end.
212     REPORTER_ASSERT(reporter, bytesRead == bytesToPeek || stream->isAtEnd());
213 
214     // peek and read should behave the same, except peek returned to the
215     // original position, so they read the same data.
216     REPORTER_ASSERT(reporter, !memcmp(peekPtr, readPtr, bytesPeeked));
217 
218     // A stream should never be able to peek more than it can read.
219     REPORTER_ASSERT(reporter, bytesRead >= bytesPeeked);
220 
221     return bytesRead - bytesPeeked;
222 }
223 
test_fully_peekable_stream(skiatest::Reporter * r,SkStream * stream,size_t limit)224 static void test_fully_peekable_stream(skiatest::Reporter* r, SkStream* stream, size_t limit) {
225     for (size_t i = 1; !stream->isAtEnd(); i++) {
226         REPORTER_ASSERT(r, compare_peek_to_read(r, stream, i) == 0);
227     }
228 }
229 
test_peeking_front_buffered_stream(skiatest::Reporter * r,const SkStream & original,size_t bufferSize)230 static void test_peeking_front_buffered_stream(skiatest::Reporter* r,
231                                                const SkStream& original,
232                                                size_t bufferSize) {
233     std::unique_ptr<SkStream> dupe(original.duplicate());
234     REPORTER_ASSERT(r, dupe != nullptr);
235     auto bufferedStream = SkFrontBufferedStream::Make(std::move(dupe), bufferSize);
236     REPORTER_ASSERT(r, bufferedStream != nullptr);
237 
238     size_t peeked = 0;
239     for (size_t i = 1; !bufferedStream->isAtEnd(); i++) {
240         const size_t unpeekableBytes = compare_peek_to_read(r, bufferedStream.get(), i);
241         if (unpeekableBytes > 0) {
242             // This could not have returned a number greater than i.
243             REPORTER_ASSERT(r, unpeekableBytes <= i);
244 
245             // We have reached the end of the buffer. Verify that it was at least
246             // bufferSize.
247             REPORTER_ASSERT(r, peeked + i - unpeekableBytes >= bufferSize);
248             // No more peeking is supported.
249             break;
250         }
251         peeked += i;
252     }
253 
254     // Test that attempting to peek beyond the length of the buffer does not prevent rewinding.
255     bufferedStream = SkFrontBufferedStream::Make(original.duplicate(), bufferSize);
256     REPORTER_ASSERT(r, bufferedStream != nullptr);
257 
258     const size_t bytesToPeek = bufferSize + 1;
259     SkAutoMalloc peekStorage(bytesToPeek);
260     SkAutoMalloc readStorage(bytesToPeek);
261 
262     for (size_t start = 0; start <= bufferSize; start++) {
263         // Skip to the starting point
264         REPORTER_ASSERT(r, bufferedStream->skip(start) == start);
265 
266         const size_t bytesPeeked = bufferedStream->peek(peekStorage.get(), bytesToPeek);
267         if (0 == bytesPeeked) {
268             // Peeking should only fail completely if we have read/skipped beyond the buffer.
269             REPORTER_ASSERT(r, start >= bufferSize);
270             break;
271         }
272 
273         // Only read the amount that was successfully peeked.
274         const size_t bytesRead = bufferedStream->read(readStorage.get(), bytesPeeked);
275         REPORTER_ASSERT(r, bytesRead == bytesPeeked);
276         REPORTER_ASSERT(r, !memcmp(peekStorage.get(), readStorage.get(), bytesPeeked));
277 
278         // This should be safe to rewind.
279         REPORTER_ASSERT(r, bufferedStream->rewind());
280     }
281 }
282 
283 // This test uses file system operations that don't work out of the
284 // box on iOS. It's likely that we don't need them on iOS. Ignoring for now.
285 // TODO(stephana): Re-evaluate if we need this in the future.
DEF_TEST(StreamPeek,reporter)286 DEF_TEST(StreamPeek, reporter) {
287     // Test a memory stream.
288     const char gAbcs[] = "abcdefghijklmnopqrstuvwxyz";
289     SkMemoryStream memStream(gAbcs, strlen(gAbcs), false);
290     test_fully_peekable_stream(reporter, &memStream, memStream.getLength());
291 
292     // Test an arbitrary file stream. file streams do not support peeking.
293     auto tmpdir = skiatest::GetTmpDir();
294     if (tmpdir.isEmpty()) {
295         ERRORF(reporter, "no tmp dir!");
296         return;
297     }
298     auto path = SkOSPath::Join(tmpdir.c_str(), "file");
299     {
300         SkFILEWStream wStream(path.c_str());
301         constexpr char filename[] = "images/baby_tux.webp";
302         auto data = GetResourceAsData(filename);
303         if (!data || data->size() == 0) {
304             ERRORF(reporter, "resource missing: %s\n", filename);
305             return;
306         }
307         if (!wStream.isValid() || !wStream.write(data->data(), data->size())) {
308             ERRORF(reporter, "error wrtiting to file %s", path.c_str());
309             return;
310         }
311     }
312     SkFILEStream fileStream(path.c_str());
313     REPORTER_ASSERT(reporter, fileStream.isValid());
314     if (!fileStream.isValid()) {
315         return;
316     }
317     SkAutoMalloc storage(fileStream.getLength());
318     for (size_t i = 1; i < fileStream.getLength(); i++) {
319         REPORTER_ASSERT(reporter, fileStream.peek(storage.get(), i) == 0);
320     }
321 
322     // Now test some FrontBufferedStreams
323     for (size_t i = 1; i < memStream.getLength(); i++) {
324         test_peeking_front_buffered_stream(reporter, memStream, i);
325     }
326 }
327 #endif
328 
329 // Asserts that asset == expected and is peekable.
stream_peek_test(skiatest::Reporter * rep,SkStreamAsset * asset,const SkData * expected)330 static void stream_peek_test(skiatest::Reporter* rep,
331                              SkStreamAsset* asset,
332                              const SkData* expected) {
333     if (asset->getLength() != expected->size()) {
334         ERRORF(rep, "Unexpected length.");
335         return;
336     }
337     SkRandom rand;
338     uint8_t buffer[4096];
339     const uint8_t* expect = expected->bytes();
340     for (size_t i = 0; i < asset->getLength(); ++i) {
341         uint32_t maxSize =
342                 SkToU32(SkTMin(sizeof(buffer), asset->getLength() - i));
343         size_t size = rand.nextRangeU(1, maxSize);
344         SkASSERT(size >= 1);
345         SkASSERT(size <= sizeof(buffer));
346         SkASSERT(size + i <= asset->getLength());
347         if (asset->peek(buffer, size) < size) {
348             ERRORF(rep, "Peek Failed!");
349             return;
350         }
351         if (0 != memcmp(buffer, &expect[i], size)) {
352             ERRORF(rep, "Peek returned wrong bytes!");
353             return;
354         }
355         uint8_t value;
356         REPORTER_ASSERT(rep, 1 == asset->read(&value, 1));
357         if (value != expect[i]) {
358             ERRORF(rep, "Read Failed!");
359             return;
360         }
361     }
362 }
363 
DEF_TEST(StreamPeek_BlockMemoryStream,rep)364 DEF_TEST(StreamPeek_BlockMemoryStream, rep) {
365     const static int kSeed = 1234;
366     SkRandom valueSource(kSeed);
367     SkRandom rand(kSeed << 1);
368     uint8_t buffer[4096];
369     SkDynamicMemoryWStream dynamicMemoryWStream;
370     size_t totalWritten = 0;
371     for (int i = 0; i < 32; ++i) {
372         // Randomize the length of the blocks.
373         size_t size = rand.nextRangeU(1, sizeof(buffer));
374         for (size_t j = 0; j < size; ++j) {
375             buffer[j] = valueSource.nextU() & 0xFF;
376         }
377         dynamicMemoryWStream.write(buffer, size);
378         totalWritten += size;
379         REPORTER_ASSERT(rep, totalWritten == dynamicMemoryWStream.bytesWritten());
380     }
381     std::unique_ptr<SkStreamAsset> asset(dynamicMemoryWStream.detachAsStream());
382     sk_sp<SkData> expected(SkData::MakeUninitialized(asset->getLength()));
383     uint8_t* expectedPtr = static_cast<uint8_t*>(expected->writable_data());
384     valueSource.setSeed(kSeed);  // reseed.
385     // We want the exact same same "random" string of numbers to put
386     // in expected. i.e.: don't rely on SkDynamicMemoryStream to work
387     // correctly while we are testing SkDynamicMemoryStream.
388     for (size_t i = 0; i < asset->getLength(); ++i) {
389         expectedPtr[i] = valueSource.nextU() & 0xFF;
390     }
391     stream_peek_test(rep, asset.get(), expected.get());
392 }
393 
394 namespace {
395 class DumbStream : public SkStream {
396 public:
DumbStream(const uint8_t * data,size_t n)397     DumbStream(const uint8_t* data, size_t n)
398         : fData(data), fCount(n), fIdx(0) {}
read(void * buffer,size_t size)399     size_t read(void* buffer, size_t size) override {
400         size_t copyCount = SkTMin(fCount - fIdx, size);
401         if (copyCount) {
402             memcpy(buffer, &fData[fIdx], copyCount);
403             fIdx += copyCount;
404         }
405         return copyCount;
406     }
isAtEnd() const407     bool isAtEnd() const override {
408         return fCount == fIdx;
409     }
410  private:
411     const uint8_t* fData;
412     size_t fCount, fIdx;
413 };
414 }  // namespace
415 
stream_copy_test(skiatest::Reporter * reporter,const void * srcData,size_t N,SkStream * stream)416 static void stream_copy_test(skiatest::Reporter* reporter,
417                              const void* srcData,
418                              size_t N,
419                              SkStream* stream) {
420     SkDynamicMemoryWStream tgt;
421     if (!SkStreamCopy(&tgt, stream)) {
422         ERRORF(reporter, "SkStreamCopy failed");
423         return;
424     }
425     sk_sp<SkData> data(tgt.detachAsData());
426     if (data->size() != N) {
427         ERRORF(reporter, "SkStreamCopy incorrect size");
428         return;
429     }
430     if (0 != memcmp(data->data(), srcData, N)) {
431         ERRORF(reporter, "SkStreamCopy bad copy");
432     }
433 }
434 
DEF_TEST(DynamicMemoryWStream_detachAsData,r)435 DEF_TEST(DynamicMemoryWStream_detachAsData, r) {
436     const char az[] = "abcdefghijklmnopqrstuvwxyz";
437     const unsigned N = 40000;
438     SkDynamicMemoryWStream dmws;
439     for (unsigned i = 0; i < N; ++i) {
440         dmws.writeText(az);
441     }
442     REPORTER_ASSERT(r, dmws.bytesWritten() == N * strlen(az));
443     auto data = dmws.detachAsData();
444     REPORTER_ASSERT(r, data->size() == N * strlen(az));
445     const uint8_t* ptr = data->bytes();
446     for (unsigned i = 0; i < N; ++i) {
447         if (0 != memcmp(ptr, az, strlen(az))) {
448             ERRORF(r, "detachAsData() memcmp failed");
449             return;
450         }
451         ptr += strlen(az);
452     }
453 }
454 
DEF_TEST(StreamCopy,reporter)455 DEF_TEST(StreamCopy, reporter) {
456     SkRandom random(123456);
457     static const int N = 10000;
458     SkAutoTMalloc<uint8_t> src((size_t)N);
459     for (int j = 0; j < N; ++j) {
460         src[j] = random.nextU() & 0xff;
461     }
462     // SkStreamCopy had two code paths; this test both.
463     DumbStream dumbStream(src.get(), (size_t)N);
464     stream_copy_test(reporter, src, N, &dumbStream);
465     SkMemoryStream smartStream(src.get(), (size_t)N);
466     stream_copy_test(reporter, src, N, &smartStream);
467 }
468 
DEF_TEST(StreamEmptyStreamMemoryBase,r)469 DEF_TEST(StreamEmptyStreamMemoryBase, r) {
470     SkDynamicMemoryWStream tmp;
471     std::unique_ptr<SkStreamAsset> asset(tmp.detachAsStream());
472     REPORTER_ASSERT(r, nullptr == asset->getMemoryBase());
473 }
474 
DEF_TEST(FILEStreamWithOffset,r)475 DEF_TEST(FILEStreamWithOffset, r) {
476     if (GetResourcePath().isEmpty()) {
477         return;
478     }
479 
480     SkString filename = GetResourcePath("images/baby_tux.png");
481     SkFILEStream stream1(filename.c_str());
482     if (!stream1.isValid()) {
483         ERRORF(r, "Could not create SkFILEStream from %s", filename.c_str());
484         return;
485     }
486     REPORTER_ASSERT(r, stream1.hasLength());
487     REPORTER_ASSERT(r, stream1.hasPosition());
488 
489     // Seek halfway through the file. The second SkFILEStream will be created
490     // with the same filename and offset and therefore will treat that offset as
491     // the beginning.
492     const size_t size = stream1.getLength();
493     const size_t middle = size / 2;
494     if (!stream1.seek(middle)) {
495         ERRORF(r, "Could not seek SkFILEStream to %lu out of %lu", middle, size);
496         return;
497     }
498     REPORTER_ASSERT(r, stream1.getPosition() == middle);
499 
500     FILE* file = sk_fopen(filename.c_str(), kRead_SkFILE_Flag);
501     if (!file) {
502         ERRORF(r, "Could not open %s as a FILE", filename.c_str());
503         return;
504     }
505 
506     if (fseek(file, (long) middle, SEEK_SET) != 0) {
507         ERRORF(r, "Could not fseek FILE to %lu out of %lu", middle, size);
508         return;
509     }
510     SkFILEStream stream2(file);
511 
512     const size_t remaining = size - middle;
513     SkAutoTMalloc<uint8_t> expected(remaining);
514     REPORTER_ASSERT(r, stream1.read(expected.get(), remaining) == remaining);
515 
516     auto test_full_read = [&r, &expected, remaining](SkStream* stream) {
517         SkAutoTMalloc<uint8_t> actual(remaining);
518         REPORTER_ASSERT(r, stream->read(actual.get(), remaining) == remaining);
519         REPORTER_ASSERT(r, !memcmp(expected.get(), actual.get(), remaining));
520 
521         REPORTER_ASSERT(r, stream->getPosition() == stream->getLength());
522         REPORTER_ASSERT(r, stream->isAtEnd());
523     };
524 
525     auto test_rewind = [&r, &expected, remaining](SkStream* stream) {
526         // Rewind goes back to original offset.
527         REPORTER_ASSERT(r, stream->rewind());
528         REPORTER_ASSERT(r, stream->getPosition() == 0);
529         SkAutoTMalloc<uint8_t> actual(remaining);
530         REPORTER_ASSERT(r, stream->read(actual.get(), remaining) == remaining);
531         REPORTER_ASSERT(r, !memcmp(expected.get(), actual.get(), remaining));
532     };
533 
534     auto test_move = [&r, &expected, size, remaining](SkStream* stream) {
535         // Cannot move to before the original offset.
536         REPORTER_ASSERT(r, stream->move(- (long) size));
537         REPORTER_ASSERT(r, stream->getPosition() == 0);
538 
539         REPORTER_ASSERT(r, stream->move(std::numeric_limits<long>::min()));
540         REPORTER_ASSERT(r, stream->getPosition() == 0);
541 
542         SkAutoTMalloc<uint8_t> actual(remaining);
543         REPORTER_ASSERT(r, stream->read(actual.get(), remaining) == remaining);
544         REPORTER_ASSERT(r, !memcmp(expected.get(), actual.get(), remaining));
545 
546         REPORTER_ASSERT(r, stream->isAtEnd());
547         REPORTER_ASSERT(r, stream->getPosition() == remaining);
548 
549         // Cannot move beyond the end.
550         REPORTER_ASSERT(r, stream->move(1));
551         REPORTER_ASSERT(r, stream->isAtEnd());
552         REPORTER_ASSERT(r, stream->getPosition() == remaining);
553     };
554 
555     auto test_seek = [&r, &expected, middle, remaining](SkStream* stream) {
556         // Seek to an arbitrary position.
557         const size_t arbitrary = middle / 2;
558         REPORTER_ASSERT(r, stream->seek(arbitrary));
559         REPORTER_ASSERT(r, stream->getPosition() == arbitrary);
560         const size_t miniRemaining = remaining - arbitrary;
561         SkAutoTMalloc<uint8_t> actual(miniRemaining);
562         REPORTER_ASSERT(r, stream->read(actual.get(), miniRemaining) == miniRemaining);
563         REPORTER_ASSERT(r, !memcmp(expected.get() + arbitrary, actual.get(), miniRemaining));
564     };
565 
566     auto test_seek_beginning = [&r, &expected, remaining](SkStream* stream) {
567         // Seek to the beginning.
568         REPORTER_ASSERT(r, stream->seek(0));
569         REPORTER_ASSERT(r, stream->getPosition() == 0);
570         SkAutoTMalloc<uint8_t> actual(remaining);
571         REPORTER_ASSERT(r, stream->read(actual.get(), remaining) == remaining);
572         REPORTER_ASSERT(r, !memcmp(expected.get(), actual.get(), remaining));
573     };
574 
575     auto test_seek_end = [&r, remaining](SkStream* stream) {
576         // Cannot seek past the end.
577         REPORTER_ASSERT(r, stream->isAtEnd());
578 
579         REPORTER_ASSERT(r, stream->seek(remaining + 1));
580         REPORTER_ASSERT(r, stream->isAtEnd());
581         REPORTER_ASSERT(r, stream->getPosition() == remaining);
582 
583         const size_t middle = remaining / 2;
584         REPORTER_ASSERT(r, stream->seek(middle));
585         REPORTER_ASSERT(r, !stream->isAtEnd());
586         REPORTER_ASSERT(r, stream->getPosition() == middle);
587 
588         REPORTER_ASSERT(r, stream->seek(remaining * 2));
589         REPORTER_ASSERT(r, stream->isAtEnd());
590         REPORTER_ASSERT(r, stream->getPosition() == remaining);
591 
592         REPORTER_ASSERT(r, stream->seek(std::numeric_limits<long>::max()));
593         REPORTER_ASSERT(r, stream->isAtEnd());
594         REPORTER_ASSERT(r, stream->getPosition() == remaining);
595     };
596 
597 
598     std::function<void (SkStream* stream, bool recurse)> test_all;
599     test_all = [&](SkStream* stream, bool recurse) {
600         REPORTER_ASSERT(r, stream->getLength() == remaining);
601         REPORTER_ASSERT(r, stream->getPosition() == 0);
602 
603         test_full_read(stream);
604         test_rewind(stream);
605         test_move(stream);
606         test_seek(stream);
607         test_seek_beginning(stream);
608         test_seek_end(stream);
609 
610         if (recurse) {
611             // Duplicate shares the original offset.
612             auto duplicate = stream->duplicate();
613             if (!duplicate) {
614                 ERRORF(r, "Failed to duplicate the stream!");
615             } else {
616                 test_all(duplicate.get(), false);
617             }
618 
619             // Fork shares the original offset, too.
620             auto fork = stream->fork();
621             if (!fork) {
622                 ERRORF(r, "Failed to fork the stream!");
623             } else {
624                 REPORTER_ASSERT(r, fork->isAtEnd());
625                 REPORTER_ASSERT(r, fork->getLength() == remaining);
626                 REPORTER_ASSERT(r, fork->rewind());
627 
628                 test_all(fork.get(), false);
629             }
630         }
631     };
632 
633     test_all(&stream2, true);
634 }
635 
636 #include "SkBuffer.h"
637 
DEF_TEST(RBuffer,reporter)638 DEF_TEST(RBuffer, reporter) {
639     int32_t value = 0;
640     SkRBuffer buffer(&value, 4);
641     REPORTER_ASSERT(reporter, buffer.isValid());
642 
643     int32_t tmp;
644     REPORTER_ASSERT(reporter, buffer.read(&tmp, 4));
645     REPORTER_ASSERT(reporter, buffer.isValid());
646 
647     REPORTER_ASSERT(reporter, !buffer.read(&tmp, 4));
648     REPORTER_ASSERT(reporter, !buffer.isValid());
649 }
650