| /* | |
| * 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); | |
| } |