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