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