Home | History | Annotate | Download | only in tests
      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 "SkData.h"
     10 #include "SkFrontBufferedStream.h"
     11 #include "SkOSFile.h"
     12 #include "SkRandom.h"
     13 #include "SkStream.h"
     14 #include "SkStreamPriv.h"
     15 #include "Test.h"
     16 
     17 #ifndef SK_BUILD_FOR_WIN
     18 #include <unistd.h>
     19 #include <fcntl.h>
     20 #endif
     21 
     22 #define MAX_SIZE    (256 * 1024)
     23 
     24 static void test_loop_stream(skiatest::Reporter* reporter, SkStream* stream,
     25                              const void* src, size_t len, int repeat) {
     26     SkAutoSMalloc<256> storage(len);
     27     void* tmp = storage.get();
     28 
     29     for (int i = 0; i < repeat; ++i) {
     30         size_t bytes = stream->read(tmp, len);
     31         REPORTER_ASSERT(reporter, bytes == len);
     32         REPORTER_ASSERT(reporter, !memcmp(tmp, src, len));
     33     }
     34 
     35     // expect EOF
     36     size_t bytes = stream->read(tmp, 1);
     37     REPORTER_ASSERT(reporter, 0 == bytes);
     38     // isAtEnd might not return true until after the first failing read.
     39     REPORTER_ASSERT(reporter, stream->isAtEnd());
     40 }
     41 
     42 static void test_filestreams(skiatest::Reporter* reporter, const char* tmpDir) {
     43     SkString path = SkOSPath::Join(tmpDir, "wstream_test");
     44 
     45     const char s[] = "abcdefghijklmnopqrstuvwxyz";
     46 
     47     {
     48         SkFILEWStream writer(path.c_str());
     49         if (!writer.isValid()) {
     50             ERRORF(reporter, "Failed to create tmp file %s\n", path.c_str());
     51             return;
     52         }
     53 
     54         for (int i = 0; i < 100; ++i) {
     55             writer.write(s, 26);
     56         }
     57     }
     58 
     59     {
     60         SkFILEStream stream(path.c_str());
     61         REPORTER_ASSERT(reporter, stream.isValid());
     62         test_loop_stream(reporter, &stream, s, 26, 100);
     63 
     64         SkAutoTDelete<SkStreamAsset> stream2(stream.duplicate());
     65         test_loop_stream(reporter, stream2.get(), s, 26, 100);
     66     }
     67 
     68     {
     69         FILE* file = ::fopen(path.c_str(), "rb");
     70         SkFILEStream stream(file, SkFILEStream::kCallerPasses_Ownership);
     71         REPORTER_ASSERT(reporter, stream.isValid());
     72         test_loop_stream(reporter, &stream, s, 26, 100);
     73 
     74         SkAutoTDelete<SkStreamAsset> stream2(stream.duplicate());
     75         test_loop_stream(reporter, stream2.get(), s, 26, 100);
     76     }
     77 }
     78 
     79 static void TestWStream(skiatest::Reporter* reporter) {
     80     SkDynamicMemoryWStream  ds;
     81     const char s[] = "abcdefghijklmnopqrstuvwxyz";
     82     int i;
     83     for (i = 0; i < 100; i++) {
     84         REPORTER_ASSERT(reporter, ds.write(s, 26));
     85     }
     86     REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
     87 
     88     char* dst = new char[100 * 26 + 1];
     89     dst[100*26] = '*';
     90     ds.copyTo(dst);
     91     REPORTER_ASSERT(reporter, dst[100*26] == '*');
     92     for (i = 0; i < 100; i++) {
     93         REPORTER_ASSERT(reporter, memcmp(&dst[i * 26], s, 26) == 0);
     94     }
     95 
     96     {
     97         SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream());
     98         REPORTER_ASSERT(reporter, 100 * 26 == stream->getLength());
     99         REPORTER_ASSERT(reporter, ds.getOffset() == 0);
    100         test_loop_stream(reporter, stream.get(), s, 26, 100);
    101 
    102         SkAutoTDelete<SkStreamAsset> stream2(stream->duplicate());
    103         test_loop_stream(reporter, stream2.get(), s, 26, 100);
    104 
    105         SkAutoTDelete<SkStreamAsset> stream3(stream->fork());
    106         REPORTER_ASSERT(reporter, stream3->isAtEnd());
    107         char tmp;
    108         size_t bytes = stream->read(&tmp, 1);
    109         REPORTER_ASSERT(reporter, 0 == bytes);
    110         stream3->rewind();
    111         test_loop_stream(reporter, stream3.get(), s, 26, 100);
    112     }
    113 
    114     for (i = 0; i < 100; i++) {
    115         REPORTER_ASSERT(reporter, ds.write(s, 26));
    116     }
    117     REPORTER_ASSERT(reporter, ds.getOffset() == 100 * 26);
    118 
    119     {
    120         SkAutoTUnref<SkData> data(ds.copyToData());
    121         REPORTER_ASSERT(reporter, 100 * 26 == data->size());
    122         REPORTER_ASSERT(reporter, memcmp(dst, data->data(), data->size()) == 0);
    123     }
    124 
    125     {
    126         // Test that this works after a copyToData.
    127         SkAutoTDelete<SkStreamAsset> stream(ds.detachAsStream());
    128         REPORTER_ASSERT(reporter, ds.getOffset() == 0);
    129         test_loop_stream(reporter, stream.get(), s, 26, 100);
    130 
    131         SkAutoTDelete<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 
    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     char buffer[sizeof(sizes) * 4];
    155 
    156     SkMemoryWStream wstream(buffer, sizeof(buffer));
    157     for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
    158         bool success = wstream.writePackedUInt(sizes[i]);
    159         REPORTER_ASSERT(reporter, success);
    160     }
    161     wstream.flush();
    162 
    163     SkMemoryStream rstream(buffer, sizeof(buffer));
    164     for (i = 0; i < SK_ARRAY_COUNT(sizes); ++i) {
    165         size_t n = rstream.readPackedUInt();
    166         if (sizes[i] != n) {
    167             ERRORF(reporter, "sizes:%x != n:%x\n", i, sizes[i], n);
    168         }
    169     }
    170 }
    171 
    172 // Test that setting an SkMemoryStream to a nullptr data does not result in a crash when calling
    173 // methods that access fData.
    174 static void TestDereferencingData(SkMemoryStream* memStream) {
    175     memStream->read(nullptr, 0);
    176     memStream->getMemoryBase();
    177     SkAutoDataUnref data(memStream->copyToData());
    178 }
    179 
    180 static void TestNullData() {
    181     SkData* nullData = nullptr;
    182     SkMemoryStream memStream(nullData);
    183     TestDereferencingData(&memStream);
    184 
    185     memStream.setData(nullData);
    186     TestDereferencingData(&memStream);
    187 
    188 }
    189 
    190 DEF_TEST(Stream, reporter) {
    191     TestWStream(reporter);
    192     TestPackedUInt(reporter);
    193     TestNullData();
    194 }
    195 
    196 /**
    197  *  Tests peeking and then reading the same amount. The two should provide the
    198  *  same results.
    199  *  Returns the amount successfully read minus the amount successfully peeked.
    200  */
    201 static size_t compare_peek_to_read(skiatest::Reporter* reporter,
    202                                    SkStream* stream, size_t bytesToPeek) {
    203     // The rest of our tests won't be very interesting if bytesToPeek is zero.
    204     REPORTER_ASSERT(reporter, bytesToPeek > 0);
    205     SkAutoMalloc peekStorage(bytesToPeek);
    206     SkAutoMalloc readStorage(bytesToPeek);
    207     void* peekPtr = peekStorage.get();
    208     void* readPtr = peekStorage.get();
    209 
    210     const size_t bytesPeeked = stream->peek(peekPtr, bytesToPeek);
    211     const size_t bytesRead = stream->read(readPtr, bytesToPeek);
    212 
    213     // bytesRead should only be less than attempted if the stream is at the
    214     // end.
    215     REPORTER_ASSERT(reporter, bytesRead == bytesToPeek || stream->isAtEnd());
    216 
    217     // peek and read should behave the same, except peek returned to the
    218     // original position, so they read the same data.
    219     REPORTER_ASSERT(reporter, !memcmp(peekPtr, readPtr, bytesPeeked));
    220 
    221     // A stream should never be able to peek more than it can read.
    222     REPORTER_ASSERT(reporter, bytesRead >= bytesPeeked);
    223 
    224     return bytesRead - bytesPeeked;
    225 }
    226 
    227 static void test_fully_peekable_stream(skiatest::Reporter* r, SkStream* stream, size_t limit) {
    228     for (size_t i = 1; !stream->isAtEnd(); i++) {
    229         REPORTER_ASSERT(r, compare_peek_to_read(r, stream, i) == 0);
    230     }
    231 }
    232 
    233 static void test_peeking_front_buffered_stream(skiatest::Reporter* r,
    234                                                const SkStream& original,
    235                                                size_t bufferSize) {
    236     SkStream* dupe = original.duplicate();
    237     REPORTER_ASSERT(r, dupe != nullptr);
    238     SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(dupe, bufferSize));
    239     REPORTER_ASSERT(r, bufferedStream != nullptr);
    240 
    241     size_t peeked = 0;
    242     for (size_t i = 1; !bufferedStream->isAtEnd(); i++) {
    243         const size_t unpeekableBytes = compare_peek_to_read(r, bufferedStream, i);
    244         if (unpeekableBytes > 0) {
    245             // This could not have returned a number greater than i.
    246             REPORTER_ASSERT(r, unpeekableBytes <= i);
    247 
    248             // We have reached the end of the buffer. Verify that it was at least
    249             // bufferSize.
    250             REPORTER_ASSERT(r, peeked + i - unpeekableBytes >= bufferSize);
    251             // No more peeking is supported.
    252             break;
    253         }
    254         peeked += i;
    255     }
    256 
    257     // Test that attempting to peek beyond the length of the buffer does not prevent rewinding.
    258     bufferedStream.reset(SkFrontBufferedStream::Create(original.duplicate(), bufferSize));
    259     REPORTER_ASSERT(r, bufferedStream != nullptr);
    260 
    261     const size_t bytesToPeek = bufferSize + 1;
    262     SkAutoMalloc peekStorage(bytesToPeek);
    263     SkAutoMalloc readStorage(bytesToPeek);
    264 
    265     for (size_t start = 0; start <= bufferSize; start++) {
    266         // Skip to the starting point
    267         REPORTER_ASSERT(r, bufferedStream->skip(start) == start);
    268 
    269         const size_t bytesPeeked = bufferedStream->peek(peekStorage.get(), bytesToPeek);
    270         if (0 == bytesPeeked) {
    271             // Peeking should only fail completely if we have read/skipped beyond the buffer.
    272             REPORTER_ASSERT(r, start >= bufferSize);
    273             break;
    274         }
    275 
    276         // Only read the amount that was successfully peeked.
    277         const size_t bytesRead = bufferedStream->read(readStorage.get(), bytesPeeked);
    278         REPORTER_ASSERT(r, bytesRead == bytesPeeked);
    279         REPORTER_ASSERT(r, !memcmp(peekStorage.get(), readStorage.get(), bytesPeeked));
    280 
    281         // This should be safe to rewind.
    282         REPORTER_ASSERT(r, bufferedStream->rewind());
    283     }
    284 }
    285 
    286 // This test uses file system operations that don't work out of the
    287 // box on iOS. It's likely that we don't need them on iOS. Ignoring for now.
    288 // TODO(stephana): Re-evaluate if we need this in the future.
    289 #ifndef SK_BUILD_FOR_IOS
    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     SkFILEStream fileStream(GetResourcePath("baby_tux.webp").c_str());
    298     REPORTER_ASSERT(reporter, fileStream.isValid());
    299     if (!fileStream.isValid()) {
    300         return;
    301     }
    302     SkAutoMalloc storage(fileStream.getLength());
    303     for (size_t i = 1; i < fileStream.getLength(); i++) {
    304         REPORTER_ASSERT(reporter, fileStream.peek(storage.get(), i) == 0);
    305     }
    306 
    307     // Now test some FrontBufferedStreams
    308     for (size_t i = 1; i < memStream.getLength(); i++) {
    309         test_peeking_front_buffered_stream(reporter, memStream, i);
    310     }
    311 }
    312 #endif
    313 
    314 // Asserts that asset == expected and is peekable.
    315 static void stream_peek_test(skiatest::Reporter* rep,
    316                              SkStreamAsset* asset,
    317                              const SkData* expected) {
    318     if (asset->getLength() != expected->size()) {
    319         ERRORF(rep, "Unexpected length.");
    320         return;
    321     }
    322     SkRandom rand;
    323     uint8_t buffer[4096];
    324     const uint8_t* expect = expected->bytes();
    325     for (size_t i = 0; i < asset->getLength(); ++i) {
    326         uint32_t maxSize =
    327                 SkToU32(SkTMin(sizeof(buffer), asset->getLength() - i));
    328         size_t size = rand.nextRangeU(1, maxSize);
    329         SkASSERT(size >= 1);
    330         SkASSERT(size <= sizeof(buffer));
    331         SkASSERT(size + i <= asset->getLength());
    332         if (asset->peek(buffer, size) < size) {
    333             ERRORF(rep, "Peek Failed!");
    334             return;
    335         }
    336         if (0 != memcmp(buffer, &expect[i], size)) {
    337             ERRORF(rep, "Peek returned wrong bytes!");
    338             return;
    339         }
    340         uint8_t value;
    341         REPORTER_ASSERT(rep, 1 == asset->read(&value, 1));
    342         if (value != expect[i]) {
    343             ERRORF(rep, "Read Failed!");
    344             return;
    345         }
    346     }
    347 }
    348 
    349 DEF_TEST(StreamPeek_BlockMemoryStream, rep) {
    350     const static int kSeed = 1234;
    351     SkRandom valueSource(kSeed);
    352     SkRandom rand(kSeed << 1);
    353     uint8_t buffer[4096];
    354     SkDynamicMemoryWStream dynamicMemoryWStream;
    355     for (int i = 0; i < 32; ++i) {
    356         // Randomize the length of the blocks.
    357         size_t size = rand.nextRangeU(1, sizeof(buffer));
    358         for (size_t j = 0; j < size; ++j) {
    359             buffer[j] = valueSource.nextU() & 0xFF;
    360         }
    361         dynamicMemoryWStream.write(buffer, size);
    362     }
    363     SkAutoTDelete<SkStreamAsset> asset(dynamicMemoryWStream.detachAsStream());
    364     SkAutoTUnref<SkData> expected(SkData::NewUninitialized(asset->getLength()));
    365     uint8_t* expectedPtr = static_cast<uint8_t*>(expected->writable_data());
    366     valueSource.setSeed(kSeed);  // reseed.
    367     // We want the exact same same "random" string of numbers to put
    368     // in expected. i.e.: don't rely on SkDynamicMemoryStream to work
    369     // correctly while we are testing SkDynamicMemoryStream.
    370     for (size_t i = 0; i < asset->getLength(); ++i) {
    371         expectedPtr[i] = valueSource.nextU() & 0xFF;
    372     }
    373     stream_peek_test(rep, asset, expected);
    374 }
    375 
    376 namespace {
    377 class DumbStream : public SkStream {
    378 public:
    379     DumbStream(const uint8_t* data, size_t n)
    380         : fData(data), fCount(n), fIdx(0) {}
    381     size_t read(void* buffer, size_t size) override {
    382         size_t copyCount = SkTMin(fCount - fIdx, size);
    383         if (copyCount) {
    384             memcpy(buffer, &fData[fIdx], copyCount);
    385             fIdx += copyCount;
    386         }
    387         return copyCount;
    388     }
    389     bool isAtEnd() const override {
    390         return fCount == fIdx;
    391     }
    392  private:
    393     const uint8_t* fData;
    394     size_t fCount, fIdx;
    395 };
    396 }  // namespace
    397 
    398 static void stream_copy_test(skiatest::Reporter* reporter,
    399                              const void* srcData,
    400                              size_t N,
    401                              SkStream* stream) {
    402     SkDynamicMemoryWStream tgt;
    403     if (!SkStreamCopy(&tgt, stream)) {
    404         ERRORF(reporter, "SkStreamCopy failed");
    405         return;
    406     }
    407     SkAutoTUnref<SkData> data(tgt.copyToData());
    408     tgt.reset();
    409     if (data->size() != N) {
    410         ERRORF(reporter, "SkStreamCopy incorrect size");
    411         return;
    412     }
    413     if (0 != memcmp(data->data(), srcData, N)) {
    414         ERRORF(reporter, "SkStreamCopy bad copy");
    415     }
    416 }
    417 
    418 DEF_TEST(StreamCopy, reporter) {
    419     SkRandom random(123456);
    420     static const int N = 10000;
    421     SkAutoTMalloc<uint8_t> src((size_t)N);
    422     for (int j = 0; j < N; ++j) {
    423         src[j] = random.nextU() & 0xff;
    424     }
    425     // SkStreamCopy had two code paths; this test both.
    426     DumbStream dumbStream(src.get(), (size_t)N);
    427     stream_copy_test(reporter, src, N, &dumbStream);
    428     SkMemoryStream smartStream(src.get(), (size_t)N);
    429     stream_copy_test(reporter, src, N, &smartStream);
    430 }
    431 
    432 DEF_TEST(StreamEmptyStreamMemoryBase, r) {
    433     SkDynamicMemoryWStream tmp;
    434     SkAutoTDelete<SkStreamAsset> asset(tmp.detachAsStream());
    435     REPORTER_ASSERT(r, nullptr == asset->getMemoryBase());
    436 }
    437 
    438