|  | /* | 
|  | * Copyright 2013 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | // Make sure SkUserConfig.h is included so #defines are available on | 
|  | // Android. | 
|  | #include "include/core/SkTypes.h" | 
|  | #ifdef SK_ENABLE_ANDROID_UTILS | 
|  | #include "client_utils/android/FrontBufferedStream.h" | 
|  | #include "include/codec/SkCodec.h" | 
|  | #include "include/core/SkBitmap.h" | 
|  | #include "include/core/SkRefCnt.h" | 
|  | #include "include/core/SkStream.h" | 
|  | #include "src/base/SkAutoMalloc.h" | 
|  | #include "tests/Test.h" | 
|  |  | 
|  | static void test_read(skiatest::Reporter* reporter, SkStream* bufferedStream, | 
|  | const void* expectations, size_t bytesToRead) { | 
|  | // output for reading bufferedStream. | 
|  | SkAutoMalloc storage(bytesToRead); | 
|  |  | 
|  | const size_t bytesRead = bufferedStream->read(storage.get(), bytesToRead); | 
|  | REPORTER_ASSERT(reporter, bytesRead == bytesToRead || bufferedStream->isAtEnd()); | 
|  | REPORTER_ASSERT(reporter, memcmp(storage.get(), expectations, bytesRead) == 0); | 
|  | } | 
|  |  | 
|  | static void test_rewind(skiatest::Reporter* reporter, | 
|  | SkStream* bufferedStream, bool shouldSucceed) { | 
|  | const bool success = bufferedStream->rewind(); | 
|  | REPORTER_ASSERT(reporter, success == shouldSucceed); | 
|  | } | 
|  |  | 
|  | // Test that hasLength() returns the correct value, based on the stream | 
|  | // being wrapped. A length can only be known if the wrapped stream has a | 
|  | // length and it has a position (so its initial position can be taken into | 
|  | // account when computing the length). | 
|  | static void test_hasLength(skiatest::Reporter* reporter, | 
|  | const SkStream& bufferedStream, | 
|  | const SkStream& streamBeingBuffered) { | 
|  | if (streamBeingBuffered.hasLength() && streamBeingBuffered.hasPosition()) { | 
|  | REPORTER_ASSERT(reporter, bufferedStream.hasLength()); | 
|  | } else { | 
|  | REPORTER_ASSERT(reporter, !bufferedStream.hasLength()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // All tests will buffer this string, and compare output to the original. | 
|  | // The string is long to ensure that all of our lengths being tested are | 
|  | // smaller than the string length. | 
|  | const char gAbcs[] = "abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwx"; | 
|  |  | 
|  | // Tests reading the stream across boundaries of what has been buffered so far and what | 
|  | // the total buffer size is. | 
|  | static void test_incremental_buffering(skiatest::Reporter* reporter, size_t bufferSize) { | 
|  | // NOTE: For this and other tests in this file, we cheat and continue to refer to the | 
|  | // wrapped stream, but that's okay because we know the wrapping stream has not been | 
|  | // deleted yet (and we only call const methods in it). | 
|  | SkMemoryStream* memStream = SkMemoryStream::MakeDirect(gAbcs, strlen(gAbcs)).release(); | 
|  |  | 
|  | auto bufferedStream = android::skia::FrontBufferedStream::Make( | 
|  | std::unique_ptr<SkStream>(memStream), bufferSize); | 
|  |  | 
|  | test_hasLength(reporter, *bufferedStream, *memStream); | 
|  |  | 
|  | // First, test reading less than the max buffer size. | 
|  | test_read(reporter, bufferedStream.get(), gAbcs, bufferSize / 2); | 
|  |  | 
|  | // Now test rewinding back to the beginning and reading less than what was | 
|  | // already buffered. | 
|  | test_rewind(reporter, bufferedStream.get(), true); | 
|  | test_read(reporter, bufferedStream.get(), gAbcs, bufferSize / 4); | 
|  |  | 
|  | // Now test reading part of what was buffered, and buffering new data. | 
|  | test_read(reporter, bufferedStream.get(), gAbcs + bufferSize / 4, bufferSize / 2); | 
|  |  | 
|  | // Now test reading what was buffered, buffering new data, and | 
|  | // reading directly from the stream. | 
|  | test_rewind(reporter, bufferedStream.get(), true); | 
|  | test_read(reporter, bufferedStream.get(), gAbcs, bufferSize << 1); | 
|  |  | 
|  | // We have reached the end of the buffer, so rewinding will fail. | 
|  | // This test assumes that the stream is larger than the buffer; otherwise the | 
|  | // result of rewind should be true. | 
|  | test_rewind(reporter, bufferedStream.get(), false); | 
|  | } | 
|  |  | 
|  | static void test_perfectly_sized_buffer(skiatest::Reporter* reporter, size_t bufferSize) { | 
|  | SkMemoryStream* memStream = SkMemoryStream::MakeDirect(gAbcs, strlen(gAbcs)).release(); | 
|  | auto bufferedStream = android::skia::FrontBufferedStream::Make( | 
|  | std::unique_ptr<SkStream>(memStream), bufferSize); | 
|  | test_hasLength(reporter, *bufferedStream, *memStream); | 
|  |  | 
|  | // Read exactly the amount that fits in the buffer. | 
|  | test_read(reporter, bufferedStream.get(), gAbcs, bufferSize); | 
|  |  | 
|  | // Rewinding should succeed. | 
|  | test_rewind(reporter, bufferedStream.get(), true); | 
|  |  | 
|  | // Once again reading buffered info should succeed | 
|  | test_read(reporter, bufferedStream.get(), gAbcs, bufferSize); | 
|  |  | 
|  | // Read past the size of the buffer. At this point, we cannot return. | 
|  | test_read(reporter, bufferedStream.get(), gAbcs + memStream->getPosition(), 1); | 
|  | test_rewind(reporter, bufferedStream.get(), false); | 
|  | } | 
|  |  | 
|  | static void test_skipping(skiatest::Reporter* reporter, size_t bufferSize) { | 
|  | SkMemoryStream* memStream = SkMemoryStream::MakeDirect(gAbcs, strlen(gAbcs)).release(); | 
|  | auto bufferedStream = android::skia::FrontBufferedStream::Make( | 
|  | std::unique_ptr<SkStream>(memStream), bufferSize); | 
|  | test_hasLength(reporter, *bufferedStream, *memStream); | 
|  |  | 
|  | // Skip half the buffer. | 
|  | bufferedStream->skip(bufferSize / 2); | 
|  |  | 
|  | // Rewind, then read part of the buffer, which should have been read. | 
|  | test_rewind(reporter, bufferedStream.get(), true); | 
|  | test_read(reporter, bufferedStream.get(), gAbcs, bufferSize / 4); | 
|  |  | 
|  | // Now skip beyond the buffered piece, but still within the total buffer. | 
|  | bufferedStream->skip(bufferSize / 2); | 
|  |  | 
|  | // Test that reading will still work. | 
|  | test_read(reporter, bufferedStream.get(), gAbcs + memStream->getPosition(), bufferSize / 4); | 
|  |  | 
|  | test_rewind(reporter, bufferedStream.get(), true); | 
|  | test_read(reporter, bufferedStream.get(), gAbcs, bufferSize); | 
|  | } | 
|  |  | 
|  | // A custom class whose isAtEnd behaves the way Android's stream does - since it is an adaptor to a | 
|  | // Java InputStream, it does not know that it is at the end until it has attempted to read beyond | 
|  | // the end and failed. Used by test_read_beyond_buffer. | 
|  | class AndroidLikeMemoryStream : public SkMemoryStream { | 
|  | public: | 
|  | AndroidLikeMemoryStream(void* data, size_t size, bool ownMemory) | 
|  | : INHERITED(data, size, ownMemory) | 
|  | , fIsAtEnd(false) {} | 
|  |  | 
|  | size_t read(void* dst, size_t requested) override { | 
|  | size_t bytesRead = this->INHERITED::read(dst, requested); | 
|  | if (bytesRead < requested) { | 
|  | fIsAtEnd = true; | 
|  | } | 
|  | return bytesRead; | 
|  | } | 
|  |  | 
|  | bool isAtEnd() const override { | 
|  | return fIsAtEnd; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool fIsAtEnd; | 
|  | using INHERITED = SkMemoryStream; | 
|  | }; | 
|  |  | 
|  | // This test ensures that buffering the exact length of the stream and attempting to read beyond it | 
|  | // does not invalidate the buffer. | 
|  | static void test_read_beyond_buffer(skiatest::Reporter* reporter, size_t bufferSize) { | 
|  | // Use a stream that behaves like Android's stream. | 
|  | AndroidLikeMemoryStream* memStream = | 
|  | new AndroidLikeMemoryStream((void*)gAbcs, bufferSize, false); | 
|  |  | 
|  | // Create a buffer that matches the length of the stream. | 
|  | auto bufferedStream = android::skia::FrontBufferedStream::Make( | 
|  | std::unique_ptr<SkStream>(memStream), bufferSize); | 
|  | test_hasLength(reporter, *bufferedStream, *memStream); | 
|  |  | 
|  | // Attempt to read one more than the bufferSize | 
|  | test_read(reporter, bufferedStream.get(), gAbcs, bufferSize + 1); | 
|  | test_rewind(reporter, bufferedStream.get(), true); | 
|  |  | 
|  | // Ensure that the initial read did not invalidate the buffer. | 
|  | test_read(reporter, bufferedStream.get(), gAbcs, bufferSize); | 
|  | } | 
|  |  | 
|  | // Mock stream that optionally has a length and/or position. Tests that FrontBufferedStream's | 
|  | // length depends on the stream it's buffering having a length and position. | 
|  | class LengthOptionalStream : public SkStream { | 
|  | public: | 
|  | LengthOptionalStream(bool hasLength, bool hasPosition) | 
|  | : fHasLength(hasLength) | 
|  | , fHasPosition(hasPosition) | 
|  | {} | 
|  |  | 
|  | bool hasLength() const override { | 
|  | return fHasLength; | 
|  | } | 
|  |  | 
|  | bool hasPosition() const override { | 
|  | return fHasPosition; | 
|  | } | 
|  |  | 
|  | size_t read(void*, size_t) override { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | bool isAtEnd() const override { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | private: | 
|  | const bool fHasLength; | 
|  | const bool fHasPosition; | 
|  | }; | 
|  |  | 
|  | // Test all possible combinations of the wrapped stream having a length and a position. | 
|  | static void test_length_combos(skiatest::Reporter* reporter, size_t bufferSize) { | 
|  | for (int hasLen = 0; hasLen <= 1; hasLen++) { | 
|  | for (int hasPos = 0; hasPos <= 1; hasPos++) { | 
|  | LengthOptionalStream* stream = | 
|  | new LengthOptionalStream(SkToBool(hasLen), SkToBool(hasPos)); | 
|  | auto buffered = android::skia::FrontBufferedStream::Make( | 
|  | std::unique_ptr<SkStream>(stream), bufferSize); | 
|  | test_hasLength(reporter, *buffered, *stream); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Test using a stream with an initial offset. | 
|  | static void test_initial_offset(skiatest::Reporter* reporter, size_t bufferSize) { | 
|  | SkMemoryStream* memStream = new SkMemoryStream(gAbcs, strlen(gAbcs), false); | 
|  |  | 
|  | // Skip a few characters into the memStream, so that bufferedStream represents an offset into | 
|  | // the stream it wraps. | 
|  | const size_t arbitraryOffset = 17; | 
|  | memStream->skip(arbitraryOffset); | 
|  | auto bufferedStream = android::skia::FrontBufferedStream::Make( | 
|  | std::unique_ptr<SkStream>(memStream), bufferSize); | 
|  |  | 
|  | // Since SkMemoryStream has a length, bufferedStream must also. | 
|  | REPORTER_ASSERT(reporter, bufferedStream->hasLength()); | 
|  |  | 
|  | const size_t amountToRead = 10; | 
|  | const size_t bufferedLength = bufferedStream->getLength(); | 
|  | size_t currentPosition = 0; | 
|  |  | 
|  | // Read the stream in chunks. After each read, the position must match currentPosition, | 
|  | // which sums the amount attempted to read, unless the end of the stream has been reached. | 
|  | // Importantly, the end should not have been reached until currentPosition == bufferedLength. | 
|  | while (currentPosition < bufferedLength) { | 
|  | REPORTER_ASSERT(reporter, !bufferedStream->isAtEnd()); | 
|  | test_read(reporter, bufferedStream.get(), gAbcs + arbitraryOffset + currentPosition, | 
|  | amountToRead); | 
|  | currentPosition = std::min(currentPosition + amountToRead, bufferedLength); | 
|  | REPORTER_ASSERT(reporter, memStream->getPosition() - arbitraryOffset == currentPosition); | 
|  | } | 
|  | REPORTER_ASSERT(reporter, bufferedStream->isAtEnd()); | 
|  | REPORTER_ASSERT(reporter, bufferedLength == currentPosition); | 
|  | } | 
|  |  | 
|  | static void test_buffers(skiatest::Reporter* reporter, size_t bufferSize) { | 
|  | test_incremental_buffering(reporter, bufferSize); | 
|  | test_perfectly_sized_buffer(reporter, bufferSize); | 
|  | test_skipping(reporter, bufferSize); | 
|  | test_read_beyond_buffer(reporter, bufferSize); | 
|  | test_length_combos(reporter, bufferSize); | 
|  | test_initial_offset(reporter, bufferSize); | 
|  | } | 
|  |  | 
|  | DEF_TEST(FrontBufferedStream, reporter) { | 
|  | // Test 6 and 64, which are used by Android, as well as another arbitrary length. | 
|  | test_buffers(reporter, 6); | 
|  | test_buffers(reporter, 15); | 
|  | test_buffers(reporter, 64); | 
|  | } | 
|  |  | 
|  | // Test that a FrontBufferedStream does not allow reading after the end of a stream. | 
|  | // This class is a mock SkStream which reports that it is at the end on the first | 
|  | // read (simulating a failure). Then it tracks whether someone calls read() again. | 
|  | class FailingStream : public SkStream { | 
|  | public: | 
|  | FailingStream() | 
|  | : fAtEnd(false) | 
|  | {} | 
|  |  | 
|  | size_t read(void* buffer, size_t size) override { | 
|  | SkASSERT(!fAtEnd); | 
|  | fAtEnd = true; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | bool isAtEnd() const override { | 
|  | return fAtEnd; | 
|  | } | 
|  |  | 
|  | private: | 
|  | bool fAtEnd; | 
|  | }; | 
|  |  | 
|  | DEF_TEST(ShortFrontBufferedStream, reporter) { | 
|  | FailingStream* failingStream = new FailingStream; | 
|  | auto stream = android::skia::FrontBufferedStream::Make( | 
|  | std::unique_ptr<SkStream>(failingStream), 64); | 
|  |  | 
|  | // This will fail to create a codec.  However, what we really want to test is that we | 
|  | // won't read past the end of the stream. | 
|  | std::unique_ptr<SkCodec> codec(SkCodec::MakeFromStream(std::move(stream))); | 
|  | } | 
|  | #endif // SK_ENABLE_ANDROID_UTILS |