blob: 6143a0156dfab69e66e7b58371c4bad721ad9186 [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "third_party/blink/renderer/platform/graphics/rw_buffer.h"
#include <array>
#include "base/threading/platform_thread.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkStream.h"
namespace blink {
namespace {
const char gABC[] = "abcdefghijklmnopqrstuvwxyz";
void check_abcs(const char buffer[], size_t size) {
ASSERT_EQ(size % 26, 0u);
for (size_t offset = 0; offset < size; offset += 26) {
EXPECT_TRUE(!memcmp(&buffer[offset], gABC, 26));
}
}
// reader should contains an integral number of copies of gABC.
void check_alphabet_buffer(const ROBuffer* reader) {
size_t size = reader->size();
ASSERT_EQ(size % 26, 0u);
std::vector<char> storage(size);
ROBuffer::Iter iter(reader);
size_t offset = 0;
do {
ASSERT_LE(offset + iter.size(), size);
memcpy(storage.data() + offset, iter.data(), iter.size());
offset += iter.size();
} while (iter.Next());
ASSERT_EQ(offset, size);
check_abcs(storage.data(), size);
}
class ROBufferTestThread : public base::PlatformThread::Delegate {
public:
ROBufferTestThread(scoped_refptr<ROBuffer> reader, size_t i)
: reader_(reader), i_(i) {}
ROBufferTestThread() = default;
ROBufferTestThread(const ROBufferTestThread&) = default;
void ThreadMain() override {
EXPECT_EQ((i_ + 1) * 26U, reader_->size());
check_alphabet_buffer(reader_.get());
}
scoped_refptr<ROBuffer> reader_;
size_t i_;
};
} // namespace
TEST(RWBufferTest, Append) {
// Knowing that the default capacity is 4096, choose N large enough so we
// force it to use multiple buffers internally.
static constexpr size_t N = 1000;
std::array<scoped_refptr<ROBuffer>, N> readers;
{
RWBuffer buffer;
for (size_t i = 0; i < N; ++i) {
buffer.Append(gABC, 26);
readers[i] = buffer.MakeROBufferSnapshot();
}
EXPECT_EQ(N * 26, buffer.size());
}
// Verify that although the RWBuffer's destructor has run, the readers are
// still valid.
for (size_t i = 0; i < N; ++i) {
EXPECT_EQ((i + 1) * 26U, readers[i]->size());
check_alphabet_buffer(readers[i].get());
}
}
TEST(RWBufferTest, Threaded) {
// Knowing that the default capacity is 4096, choose N large enough so we
// force it to use multiple buffers internally.
constexpr size_t N = 1000;
RWBuffer buffer;
std::array<ROBufferTestThread, N> threads;
std::array<base::PlatformThreadHandle, N> handlers;
for (size_t i = 0; i < N; ++i) {
buffer.Append(gABC, 26);
scoped_refptr<ROBuffer> reader = buffer.MakeROBufferSnapshot();
EXPECT_EQ(reader->size(), buffer.size());
// reader's copy constructor will ref the ROBuffer, which will be unreffed
// when the task ends.
// Ownership of stream is passed to the task, which will delete it.
threads[i] = ROBufferTestThread(reader, i);
ASSERT_TRUE(base::PlatformThread::Create(0, &threads[i], &handlers[i]));
}
EXPECT_EQ(N * 26, buffer.size());
for (size_t i = 0; i < N; ++i) {
base::PlatformThread::Join(handlers[i]);
}
}
// Tests that it is safe to call ROBuffer::Iter::size() when exhausted.
TEST(RWBufferTest, Size) {
RWBuffer buffer;
buffer.Append(gABC, 26);
scoped_refptr<ROBuffer> roBuffer(buffer.MakeROBufferSnapshot());
ROBuffer::Iter iter(roBuffer.get());
EXPECT_TRUE(iter.data());
EXPECT_EQ(iter.size(), 26u);
// There is only one block in this buffer.
EXPECT_TRUE(!iter.Next());
EXPECT_EQ(0u, iter.size());
}
// Tests that operations (including the destructor) are safe on an RWBuffer
// without any data appended.
TEST(RWBufferTest, Empty) {
RWBuffer buffer;
ASSERT_EQ(0u, buffer.size());
scoped_refptr<ROBuffer> roBuffer = buffer.MakeROBufferSnapshot();
ASSERT_TRUE(roBuffer);
if (roBuffer) {
EXPECT_EQ(roBuffer->size(), 0u);
ROBuffer::Iter iter(roBuffer.get());
EXPECT_EQ(iter.size(), 0u);
EXPECT_TRUE(!iter.data());
EXPECT_TRUE(!iter.Next());
}
}
// Tests that |HasNoSnapshots| returns the correct value when the buffer is
// empty.
// In this case, we can't tell if a snapshot has been created (in general), so
// we expect to always get back false.
TEST(RWBufferTest, HasNoSnapshotsEmpty) {
RWBuffer buffer;
ASSERT_EQ(0u, buffer.size());
EXPECT_TRUE(buffer.HasNoSnapshots());
{
scoped_refptr<ROBuffer> first = buffer.MakeROBufferSnapshot();
EXPECT_TRUE(buffer.HasNoSnapshots());
scoped_refptr<ROBuffer> second = buffer.MakeROBufferSnapshot();
EXPECT_TRUE(buffer.HasNoSnapshots());
}
EXPECT_TRUE(buffer.HasNoSnapshots());
}
// Tests that |HasNoSnapshots| returns the correct value when the buffer is
// empty.
TEST(RWBufferTest, HasNoSnapshots) {
RWBuffer buffer;
ASSERT_EQ(0u, buffer.size());
buffer.Append(gABC, 26);
EXPECT_TRUE(buffer.HasNoSnapshots());
{
scoped_refptr<ROBuffer> first = buffer.MakeROBufferSnapshot();
EXPECT_FALSE(buffer.HasNoSnapshots());
scoped_refptr<ROBuffer> second = buffer.MakeROBufferSnapshot();
EXPECT_FALSE(buffer.HasNoSnapshots());
}
EXPECT_TRUE(buffer.HasNoSnapshots());
}
} // namespace blink