| /* | 
 |  * 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/core/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 |