1/* 2 * Copyright 2016 Google Inc. 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8#include "include/core/SkData.h" 9#include "include/core/SkStream.h" 10#include "src/codec/SkStreamBuffer.h" 11#include "src/utils/SkOSPath.h" 12 13#include "tests/FakeStreams.h" 14#include "tests/Test.h" 15 16static const char* gText = "Four score and seven years ago"; 17 18static void test_get_data_at_position(skiatest::Reporter* r, SkStreamBuffer* buffer, size_t position, 19 size_t length) { 20 sk_sp<SkData> data = buffer->getDataAtPosition(position, length); 21 REPORTER_ASSERT(r, data); 22 if (data) { 23 REPORTER_ASSERT(r, !memcmp(data->data(), gText + position, length)); 24 } 25} 26 27// Test buffering from the beginning, by different amounts. 28static void test_buffer_from_beginning(skiatest::Reporter* r, std::unique_ptr<SkStream> stream, 29 size_t length) { 30 if (!stream) { 31 return; 32 } 33 SkStreamBuffer buffer(std::move(stream)); 34 35 // Buffer an arbitrary amount: 36 size_t buffered = length / 2; 37 REPORTER_ASSERT(r, buffer.buffer(buffered)); 38 REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, buffered)); 39 40 // Buffering less is free: 41 REPORTER_ASSERT(r, buffer.buffer(buffered / 2)); 42 43 // Buffer more should succeed: 44 REPORTER_ASSERT(r, buffer.buffer(length)); 45 REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, length)); 46} 47 48// Test flushing the stream as we read. 49static void test_flushing(skiatest::Reporter* r, std::unique_ptr<SkStream> stream, size_t length, 50 bool getDataAtPosition) { 51 if (!stream) { 52 return; 53 } 54 SkStreamBuffer buffer(std::move(stream)); 55 const size_t step = 5; 56 for (size_t position = 0; position + step <= length; position += step) { 57 REPORTER_ASSERT(r, buffer.buffer(step)); 58 REPORTER_ASSERT(r, buffer.markPosition() == position); 59 60 if (!getDataAtPosition) { 61 REPORTER_ASSERT(r, !memcmp(buffer.get(), gText + position, step)); 62 } 63 buffer.flush(); 64 } 65 66 REPORTER_ASSERT(r, !buffer.buffer(step)); 67 68 if (getDataAtPosition) { 69 for (size_t position = 0; position + step <= length; position += step) { 70 test_get_data_at_position(r, &buffer, position, step); 71 } 72 } 73} 74 75DEF_TEST(StreamBuffer, r) { 76 const size_t size = strlen(gText); 77 sk_sp<SkData> data(SkData::MakeWithoutCopy(gText, size)); 78 79 SkString tmpDir = skiatest::GetTmpDir(); 80 const char* subdir = "streamBuffer.txt"; 81 SkString path; 82 83 if (!tmpDir.isEmpty()) { 84 path = SkOSPath::Join(tmpDir.c_str(), subdir); 85 SkFILEWStream writer(path.c_str()); 86 if (!writer.isValid()) { 87 ERRORF(r, "unable to write to '%s'\n", path.c_str()); 88 return; 89 } 90 writer.write(gText, size); 91 } 92 93 struct Factory { 94 std::function<std::unique_ptr<SkStream>()> createStream; 95 bool skipIfNoTmpDir; 96 }; 97 98 Factory factories[] = { 99 { [&data]() { return std::make_unique<SkMemoryStream>(data); }, false }, 100 { [&data]() { return std::make_unique<NotAssetMemStream>(data); }, false }, 101 { [&path]() { return path.isEmpty() 102 ? nullptr 103 : std::make_unique<SkFILEStream>(path.c_str()); }, true }, 104 }; 105 106 for (const Factory& f : factories) { 107 if (tmpDir.isEmpty() && f.skipIfNoTmpDir) { 108 continue; 109 } 110 test_buffer_from_beginning(r, f.createStream(), size); 111 test_flushing(r, f.createStream(), size, false); 112 test_flushing(r, f.createStream(), size, true); 113 } 114 115 // Stream that will receive more data. Will be owned by the SkStreamBuffer. 116 auto halting = std::make_unique<HaltingStream>(data, 6); 117 HaltingStream* peekHalting = halting.get(); 118 SkStreamBuffer buffer(std::move(halting)); 119 120 // Can only buffer less than what's available (6). 121 REPORTER_ASSERT(r, !buffer.buffer(7)); 122 REPORTER_ASSERT(r, buffer.buffer(5)); 123 REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, 5)); 124 125 // Add some more data. We can buffer and read all of it. 126 peekHalting->addNewData(8); 127 REPORTER_ASSERT(r, buffer.buffer(14)); 128 REPORTER_ASSERT(r, !memcmp(buffer.get(), gText, 14)); 129 130 // Flush the buffer, which moves the position. 131 buffer.flush(); 132 133 // Add some data, and try to read more. Can only read what is 134 // available. 135 peekHalting->addNewData(9); 136 REPORTER_ASSERT(r, !buffer.buffer(13)); 137 peekHalting->addNewData(4); 138 REPORTER_ASSERT(r, buffer.buffer(13)); 139 140 // Do not call get on this data. We'll come back to this data after adding 141 // more. 142 buffer.flush(); 143 const size_t remaining = size - 27; 144 REPORTER_ASSERT(r, remaining > 0); 145 peekHalting->addNewData(remaining); 146 REPORTER_ASSERT(r, buffer.buffer(remaining)); 147 REPORTER_ASSERT(r, !memcmp(buffer.get(), gText + 27, remaining)); 148 149 // Now go back to the data we skipped. 150 test_get_data_at_position(r, &buffer, 14, 13); 151} 152