| /* |
| * Copyright 2016 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "include/core/SkData.h" |
| #include "include/core/SkStream.h" |
| #include "src/codec/SkStreamBuffer.h" |
| #include "src/utils/SkOSPath.h" |
| |
| #include "tests/FakeStreams.h" |
| #include "tests/Test.h" |
| |
| static const char* gText = "Four score and seven years ago"; |
| |
| static void test_get_data_at_position(skiatest::Reporter* r, SkStreamBuffer* buffer, size_t position, |
| size_t length) { |
| sk_sp<SkData> data = buffer->getDataAtPosition(position, length); |
| REPORTER_ASSERT(r, data); |
| if (data) { |
| REPORTER_ASSERT(r, !memcmp(data->data(), gText + position, length)); |
| } |
| } |
| |
| // Test buffering from the beginning, by different amounts. |
| static void test_buffer_from_beginning(skiatest::Reporter* r, std::unique_ptr<SkStream> stream, |
| size_t length) { |
| if (!stream) { |
| return; |
| } |
| SkStreamBuffer buffer(std::move(stream)); |
| |
| // Buffer an arbitrary amount: |
| size_t buffered = length / 2; |
| REPORTER_ASSERT(r, buffer.buffer(buffered)); |
| REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, buffered)); |
| |
| // Buffering less is free: |
| REPORTER_ASSERT(r, buffer.buffer(buffered / 2)); |
| |
| // Buffer more should succeed: |
| REPORTER_ASSERT(r, buffer.buffer(length)); |
| REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, length)); |
| } |
| |
| // Test flushing the stream as we read. |
| static void test_flushing(skiatest::Reporter* r, std::unique_ptr<SkStream> stream, size_t length, |
| bool getDataAtPosition) { |
| if (!stream) { |
| return; |
| } |
| SkStreamBuffer buffer(std::move(stream)); |
| const size_t step = 5; |
| for (size_t position = 0; position + step <= length; position += step) { |
| REPORTER_ASSERT(r, buffer.buffer(step)); |
| REPORTER_ASSERT(r, buffer.markPosition() == position); |
| |
| if (!getDataAtPosition) { |
| REPORTER_ASSERT(r, !memcmp(buffer.get(), gText + position, step)); |
| } |
| buffer.flush(); |
| } |
| |
| REPORTER_ASSERT(r, !buffer.buffer(step)); |
| |
| if (getDataAtPosition) { |
| for (size_t position = 0; position + step <= length; position += step) { |
| test_get_data_at_position(r, &buffer, position, step); |
| } |
| } |
| } |
| |
| DEF_TEST(StreamBuffer, r) { |
| const size_t size = strlen(gText); |
| sk_sp<SkData> data(SkData::MakeWithoutCopy(gText, size)); |
| |
| SkString tmpDir = skiatest::GetTmpDir(); |
| const char* subdir = "streamBuffer.txt"; |
| SkString path; |
| |
| if (!tmpDir.isEmpty()) { |
| path = SkOSPath::Join(tmpDir.c_str(), subdir); |
| SkFILEWStream writer(path.c_str()); |
| if (!writer.isValid()) { |
| ERRORF(r, "unable to write to '%s'\n", path.c_str()); |
| return; |
| } |
| writer.write(gText, size); |
| } |
| |
| struct Factory { |
| std::function<std::unique_ptr<SkStream>()> createStream; |
| bool skipIfNoTmpDir; |
| }; |
| |
| Factory factories[] = { |
| { [&data]() { return std::make_unique<SkMemoryStream>(data); }, false }, |
| { [&data]() { return std::make_unique<NotAssetMemStream>(data); }, false }, |
| { [&path]() { return path.isEmpty() |
| ? nullptr |
| : std::make_unique<SkFILEStream>(path.c_str()); }, true }, |
| }; |
| |
| for (const Factory& f : factories) { |
| if (tmpDir.isEmpty() && f.skipIfNoTmpDir) { |
| continue; |
| } |
| test_buffer_from_beginning(r, f.createStream(), size); |
| test_flushing(r, f.createStream(), size, false); |
| test_flushing(r, f.createStream(), size, true); |
| } |
| |
| // Stream that will receive more data. Will be owned by the SkStreamBuffer. |
| auto halting = std::make_unique<HaltingStream>(data, 6); |
| HaltingStream* peekHalting = halting.get(); |
| SkStreamBuffer buffer(std::move(halting)); |
| |
| // Can only buffer less than what's available (6). |
| REPORTER_ASSERT(r, !buffer.buffer(7)); |
| REPORTER_ASSERT(r, buffer.buffer(5)); |
| REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, 5)); |
| |
| // Add some more data. We can buffer and read all of it. |
| peekHalting->addNewData(8); |
| REPORTER_ASSERT(r, buffer.buffer(14)); |
| REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, 14)); |
| |
| // Flush the buffer, which moves the position. |
| buffer.flush(); |
| |
| // Add some data, and try to read more. Can only read what is |
| // available. |
| peekHalting->addNewData(9); |
| REPORTER_ASSERT(r, !buffer.buffer(13)); |
| peekHalting->addNewData(4); |
| REPORTER_ASSERT(r, buffer.buffer(13)); |
| |
| // Do not call get on this data. We'll come back to this data after adding |
| // more. |
| buffer.flush(); |
| const size_t remaining = size - 27; |
| REPORTER_ASSERT(r, remaining > 0); |
| peekHalting->addNewData(remaining); |
| REPORTER_ASSERT(r, buffer.buffer(remaining)); |
| REPORTER_ASSERT(r, !memcmp(buffer.get(), gText + 27, remaining)); |
| |
| // Now go back to the data we skipped. |
| test_get_data_at_position(r, &buffer, 14, 13); |
| } |