/* * 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 "SkData.h" #include "SkMakeUnique.h" #include "SkOSPath.h" #include "SkStream.h" #include "SkStreamBuffer.h" #include "FakeStreams.h" #include "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 { std::function<std::unique_ptr<SkStream>()> createStream; bool skipIfNoTmpDir; } factories[] = { { [&data]() { return skstd::make_unique<SkMemoryStream>(data); }, false }, { [&data]() { return skstd::make_unique<NotAssetMemStream>(data); }, false }, { [&path]() { return path.isEmpty() ? nullptr : skstd::make_unique<SkFILEStream>(path.c_str()); }, true }, }; for (auto 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 = skstd::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); }