1 /* 2 * Copyright 2013 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 "SkBitmap.h" 9 #include "SkCodec.h" 10 #include "SkFrontBufferedStream.h" 11 #include "SkRefCnt.h" 12 #include "SkStream.h" 13 #include "SkTypes.h" 14 #include "Test.h" 15 16 static void test_read(skiatest::Reporter* reporter, SkStream* bufferedStream, 17 const void* expectations, size_t bytesToRead) { 18 // output for reading bufferedStream. 19 SkAutoMalloc storage(bytesToRead); 20 21 const size_t bytesRead = bufferedStream->read(storage.get(), bytesToRead); 22 REPORTER_ASSERT(reporter, bytesRead == bytesToRead || bufferedStream->isAtEnd()); 23 REPORTER_ASSERT(reporter, memcmp(storage.get(), expectations, bytesRead) == 0); 24 } 25 26 static void test_rewind(skiatest::Reporter* reporter, 27 SkStream* bufferedStream, bool shouldSucceed) { 28 const bool success = bufferedStream->rewind(); 29 REPORTER_ASSERT(reporter, success == shouldSucceed); 30 } 31 32 // Test that hasLength() returns the correct value, based on the stream 33 // being wrapped. A length can only be known if the wrapped stream has a 34 // length and it has a position (so its initial position can be taken into 35 // account when computing the length). 36 static void test_hasLength(skiatest::Reporter* reporter, 37 const SkStream& bufferedStream, 38 const SkStream& streamBeingBuffered) { 39 if (streamBeingBuffered.hasLength() && streamBeingBuffered.hasPosition()) { 40 REPORTER_ASSERT(reporter, bufferedStream.hasLength()); 41 } else { 42 REPORTER_ASSERT(reporter, !bufferedStream.hasLength()); 43 } 44 } 45 46 // All tests will buffer this string, and compare output to the original. 47 // The string is long to ensure that all of our lengths being tested are 48 // smaller than the string length. 49 const char gAbcs[] = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx"; 50 51 // Tests reading the stream across boundaries of what has been buffered so far and what 52 // the total buffer size is. 53 static void test_incremental_buffering(skiatest::Reporter* reporter, size_t bufferSize) { 54 // NOTE: For this and other tests in this file, we cheat and continue to refer to the 55 // wrapped stream, but that's okay because we know the wrapping stream has not been 56 // deleted yet (and we only call const methods in it). 57 SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false); 58 59 SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize)); 60 test_hasLength(reporter, *bufferedStream.get(), *memStream); 61 62 // First, test reading less than the max buffer size. 63 test_read(reporter, bufferedStream, gAbcs, bufferSize / 2); 64 65 // Now test rewinding back to the beginning and reading less than what was 66 // already buffered. 67 test_rewind(reporter, bufferedStream, true); 68 test_read(reporter, bufferedStream, gAbcs, bufferSize / 4); 69 70 // Now test reading part of what was buffered, and buffering new data. 71 test_read(reporter, bufferedStream, gAbcs + bufferSize / 4, bufferSize / 2); 72 73 // Now test reading what was buffered, buffering new data, and 74 // reading directly from the stream. 75 test_rewind(reporter, bufferedStream, true); 76 test_read(reporter, bufferedStream, gAbcs, bufferSize << 1); 77 78 // We have reached the end of the buffer, so rewinding will fail. 79 // This test assumes that the stream is larger than the buffer; otherwise the 80 // result of rewind should be true. 81 test_rewind(reporter, bufferedStream, false); 82 } 83 84 static void test_perfectly_sized_buffer(skiatest::Reporter* reporter, size_t bufferSize) { 85 SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false); 86 SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize)); 87 test_hasLength(reporter, *bufferedStream.get(), *memStream); 88 89 // Read exactly the amount that fits in the buffer. 90 test_read(reporter, bufferedStream, gAbcs, bufferSize); 91 92 // Rewinding should succeed. 93 test_rewind(reporter, bufferedStream, true); 94 95 // Once again reading buffered info should succeed 96 test_read(reporter, bufferedStream, gAbcs, bufferSize); 97 98 // Read past the size of the buffer. At this point, we cannot return. 99 test_read(reporter, bufferedStream, gAbcs + memStream->getPosition(), 1); 100 test_rewind(reporter, bufferedStream, false); 101 } 102 103 static void test_skipping(skiatest::Reporter* reporter, size_t bufferSize) { 104 SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false); 105 SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize)); 106 test_hasLength(reporter, *bufferedStream.get(), *memStream); 107 108 // Skip half the buffer. 109 bufferedStream->skip(bufferSize / 2); 110 111 // Rewind, then read part of the buffer, which should have been read. 112 test_rewind(reporter, bufferedStream, true); 113 test_read(reporter, bufferedStream, gAbcs, bufferSize / 4); 114 115 // Now skip beyond the buffered piece, but still within the total buffer. 116 bufferedStream->skip(bufferSize / 2); 117 118 // Test that reading will still work. 119 test_read(reporter, bufferedStream, gAbcs + memStream->getPosition(), bufferSize / 4); 120 121 test_rewind(reporter, bufferedStream, true); 122 test_read(reporter, bufferedStream, gAbcs, bufferSize); 123 } 124 125 // A custom class whose isAtEnd behaves the way Android's stream does - since it is an adaptor to a 126 // Java InputStream, it does not know that it is at the end until it has attempted to read beyond 127 // the end and failed. Used by test_read_beyond_buffer. 128 class AndroidLikeMemoryStream : public SkMemoryStream { 129 public: 130 AndroidLikeMemoryStream(void* data, size_t size, bool ownMemory) 131 : INHERITED(data, size, ownMemory) 132 , fIsAtEnd(false) {} 133 134 size_t read(void* dst, size_t requested) override { 135 size_t bytesRead = this->INHERITED::read(dst, requested); 136 if (bytesRead < requested) { 137 fIsAtEnd = true; 138 } 139 return bytesRead; 140 } 141 142 bool isAtEnd() const override { 143 return fIsAtEnd; 144 } 145 146 private: 147 bool fIsAtEnd; 148 typedef SkMemoryStream INHERITED; 149 }; 150 151 // This test ensures that buffering the exact length of the stream and attempting to read beyond it 152 // does not invalidate the buffer. 153 static void test_read_beyond_buffer(skiatest::Reporter* reporter, size_t bufferSize) { 154 // Use a stream that behaves like Android's stream. 155 AndroidLikeMemoryStream* memStream = 156 new AndroidLikeMemoryStream((void*)gAbcs, bufferSize, false); 157 158 // Create a buffer that matches the length of the stream. 159 SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize)); 160 test_hasLength(reporter, *bufferedStream.get(), *memStream); 161 162 // Attempt to read one more than the bufferSize 163 test_read(reporter, bufferedStream.get(), gAbcs, bufferSize + 1); 164 test_rewind(reporter, bufferedStream.get(), true); 165 166 // Ensure that the initial read did not invalidate the buffer. 167 test_read(reporter, bufferedStream, gAbcs, bufferSize); 168 } 169 170 // Dummy stream that optionally has a length and/or position. Tests that FrontBufferedStream's 171 // length depends on the stream it's buffering having a length and position. 172 class LengthOptionalStream : public SkStream { 173 public: 174 LengthOptionalStream(bool hasLength, bool hasPosition) 175 : fHasLength(hasLength) 176 , fHasPosition(hasPosition) 177 {} 178 179 bool hasLength() const override { 180 return fHasLength; 181 } 182 183 bool hasPosition() const override { 184 return fHasPosition; 185 } 186 187 size_t read(void*, size_t) override { 188 return 0; 189 } 190 191 bool isAtEnd() const override { 192 return true; 193 } 194 195 private: 196 const bool fHasLength; 197 const bool fHasPosition; 198 }; 199 200 // Test all possible combinations of the wrapped stream having a length and a position. 201 static void test_length_combos(skiatest::Reporter* reporter, size_t bufferSize) { 202 for (int hasLen = 0; hasLen <= 1; hasLen++) { 203 for (int hasPos = 0; hasPos <= 1; hasPos++) { 204 LengthOptionalStream* stream = 205 new LengthOptionalStream(SkToBool(hasLen), SkToBool(hasPos)); 206 SkAutoTDelete<SkStream> buffered(SkFrontBufferedStream::Create(stream, bufferSize)); 207 test_hasLength(reporter, *buffered.get(), *stream); 208 } 209 } 210 } 211 212 // Test using a stream with an initial offset. 213 static void test_initial_offset(skiatest::Reporter* reporter, size_t bufferSize) { 214 SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false); 215 216 // Skip a few characters into the memStream, so that bufferedStream represents an offset into 217 // the stream it wraps. 218 const size_t arbitraryOffset = 17; 219 memStream->skip(arbitraryOffset); 220 SkAutoTDelete<SkStream> bufferedStream(SkFrontBufferedStream::Create(memStream, bufferSize)); 221 222 // Since SkMemoryStream has a length, bufferedStream must also. 223 REPORTER_ASSERT(reporter, bufferedStream->hasLength()); 224 225 const size_t amountToRead = 10; 226 const size_t bufferedLength = bufferedStream->getLength(); 227 size_t currentPosition = 0; 228 229 // Read the stream in chunks. After each read, the position must match currentPosition, 230 // which sums the amount attempted to read, unless the end of the stream has been reached. 231 // Importantly, the end should not have been reached until currentPosition == bufferedLength. 232 while (currentPosition < bufferedLength) { 233 REPORTER_ASSERT(reporter, !bufferedStream->isAtEnd()); 234 test_read(reporter, bufferedStream, gAbcs + arbitraryOffset + currentPosition, 235 amountToRead); 236 currentPosition = SkTMin(currentPosition + amountToRead, bufferedLength); 237 REPORTER_ASSERT(reporter, memStream->getPosition() - arbitraryOffset == currentPosition); 238 } 239 REPORTER_ASSERT(reporter, bufferedStream->isAtEnd()); 240 REPORTER_ASSERT(reporter, bufferedLength == currentPosition); 241 } 242 243 static void test_buffers(skiatest::Reporter* reporter, size_t bufferSize) { 244 test_incremental_buffering(reporter, bufferSize); 245 test_perfectly_sized_buffer(reporter, bufferSize); 246 test_skipping(reporter, bufferSize); 247 test_read_beyond_buffer(reporter, bufferSize); 248 test_length_combos(reporter, bufferSize); 249 test_initial_offset(reporter, bufferSize); 250 } 251 252 DEF_TEST(FrontBufferedStream, reporter) { 253 // Test 6 and 64, which are used by Android, as well as another arbitrary length. 254 test_buffers(reporter, 6); 255 test_buffers(reporter, 15); 256 test_buffers(reporter, 64); 257 } 258 259 // Test that a FrontBufferedStream does not allow reading after the end of a stream. 260 // This class is a dummy SkStream which reports that it is at the end on the first 261 // read (simulating a failure). Then it tracks whether someone calls read() again. 262 class FailingStream : public SkStream { 263 public: 264 FailingStream() 265 : fAtEnd(false) 266 {} 267 268 size_t read(void* buffer, size_t size) override { 269 SkASSERT(!fAtEnd); 270 fAtEnd = true; 271 return 0; 272 } 273 274 bool isAtEnd() const override { 275 return fAtEnd; 276 } 277 278 private: 279 bool fAtEnd; 280 }; 281 282 DEF_TEST(ShortFrontBufferedStream, reporter) { 283 FailingStream* failingStream = new FailingStream; 284 SkAutoTDelete<SkStreamRewindable> stream(SkFrontBufferedStream::Create(failingStream, 64)); 285 286 // This will fail to create a codec. However, what we really want to test is that we 287 // won't read past the end of the stream. 288 SkAutoTDelete<SkCodec> codec(SkCodec::NewFromStream(stream.detach())); 289 } 290