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