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