blob: 48c39a1ed6499d20ec799f8d603f47ceb8a15c48 [file] [log] [blame]
/*
* Copyright (C) 2024 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "src/trace_processor/util/file_buffer.h"
#include <cstddef>
#include <cstdint>
#include <cstring>
#include <vector>
#include "perfetto/trace_processor/trace_blob.h"
#include "perfetto/trace_processor/trace_blob_view.h"
#include "test/gtest_and_gmock.h"
namespace perfetto::trace_processor::util {
namespace {
using ::testing::ElementsAreArray;
using ::testing::Eq;
using ::testing::Optional;
using ::testing::Property;
using ::testing::SizeIs;
class SameDataAsMatcher {
public:
template <typename ArgType>
class MatcherImpl : public ::testing ::MatcherInterface<const ArgType&> {
public:
explicit MatcherImpl(const TraceBlobView& expected_data)
: expected_data_(expected_data) {}
bool MatchAndExplain(const ArgType& arg,
::testing ::MatchResultListener*) const override {
if (expected_data_.size() != arg.size()) {
return false;
}
return memcmp(expected_data_.data(), arg.data(), expected_data_.size()) ==
0;
}
void DescribeTo(::std ::ostream*) const override {}
void DescribeNegationTo(::std ::ostream*) const override {}
private:
const TraceBlobView& expected_data_;
};
explicit SameDataAsMatcher(const TraceBlobView& expected_data)
: expected_data_(expected_data) {}
template <typename ArgType>
operator ::testing::Matcher<ArgType>() const {
return ::testing::Matcher<ArgType>(
new MatcherImpl<ArgType>(expected_data_));
}
private:
const TraceBlobView& expected_data_;
};
SameDataAsMatcher SameDataAs(const TraceBlobView& expected_data) {
return SameDataAsMatcher(expected_data);
}
TraceBlobView CreateExpectedData(size_t expected_size) {
TraceBlob tb = TraceBlob::Allocate(expected_size);
for (size_t i = 0; i < expected_size; ++i) {
tb.data()[i] = static_cast<uint8_t>(i);
}
return TraceBlobView(std::move(tb));
}
std::vector<TraceBlobView> Slice(const TraceBlobView& blob, size_t chunk_size) {
std::vector<TraceBlobView> chunks;
size_t size = blob.size();
for (size_t off = 0; size != 0;) {
chunk_size = std::min(chunk_size, size);
chunks.push_back(blob.slice_off(off, chunk_size));
size -= chunk_size;
off += chunk_size;
}
return chunks;
}
FileBuffer CreateFileBuffer(const std::vector<TraceBlobView>& chunks) {
FileBuffer chunked_buffer;
for (const auto& chunk : chunks) {
chunked_buffer.PushBack(chunk.copy());
}
return chunked_buffer;
}
TEST(FileBuffer, ContiguousAccessAtOffset) {
constexpr size_t kExpectedSize = 256;
constexpr size_t kChunkSize = kExpectedSize / 4;
TraceBlobView expected_data = CreateExpectedData(kExpectedSize);
FileBuffer buffer = CreateFileBuffer(Slice(expected_data, kChunkSize));
for (size_t file_offset = 0; file_offset <= kExpectedSize; ++file_offset) {
EXPECT_TRUE(buffer.PopFrontUntil(file_offset));
for (size_t off = file_offset; off <= kExpectedSize; ++off) {
auto expected = expected_data.slice_off(off, kExpectedSize - off);
std::optional<TraceBlobView> tbv = buffer.SliceOff(off, expected.size());
EXPECT_THAT(tbv, Optional(SameDataAs(expected)));
}
}
}
TEST(FileBuffer, NoCopyIfDataIsContiguous) {
constexpr size_t kExpectedSize = 256;
constexpr size_t kChunkSize = kExpectedSize / 4;
std::vector<TraceBlobView> chunks =
Slice(CreateExpectedData(kExpectedSize), kChunkSize);
FileBuffer buffer = CreateFileBuffer(chunks);
for (size_t i = 0; i < chunks.size(); ++i) {
for (size_t off = 0; off < kChunkSize; ++off) {
const size_t expected_size = kChunkSize - off;
EXPECT_THAT(
buffer.SliceOff(i * kChunkSize + off, expected_size),
Optional(Property(&TraceBlobView::data, Eq(chunks[i].data() + off))));
}
}
}
TEST(FileBuffer, PopRemovesData) {
size_t expected_size = 256;
size_t expected_file_offset = 0;
const size_t kChunkSize = expected_size / 4;
TraceBlobView expected_data = CreateExpectedData(expected_size);
FileBuffer buffer = CreateFileBuffer(Slice(expected_data, kChunkSize));
--expected_size;
++expected_file_offset;
buffer.PopFrontUntil(expected_file_offset);
EXPECT_THAT(buffer.file_offset(), Eq(expected_file_offset));
EXPECT_THAT(buffer.SliceOff(expected_file_offset, expected_size),
Optional(SameDataAs(expected_data.slice_off(
expected_data.size() - expected_size, expected_size))));
expected_size -= kChunkSize;
expected_file_offset += kChunkSize;
buffer.PopFrontUntil(expected_file_offset);
EXPECT_THAT(buffer.file_offset(), Eq(expected_file_offset));
EXPECT_THAT(buffer.SliceOff(expected_file_offset, expected_size),
Optional(SameDataAs(expected_data.slice_off(
expected_data.size() - expected_size, expected_size))));
}
} // namespace
} // namespace perfetto::trace_processor::util