/* * 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 #include #include #include #include #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 class MatcherImpl : public ::testing ::MatcherInterface { public: explicit MatcherImpl(const TraceBlobView& expected_data) : expected_data_(expected_data) {} bool MatchAndExplain(const ArgType& arg, ::testing ::MatchResultListener*) const override { return std::equal(expected_data_.data(), expected_data_.data() + expected_data_.size(), arg.data(), arg.data() + arg.size()); } 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 operator ::testing::Matcher() const { return ::testing::Matcher( new MatcherImpl(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(i); } return TraceBlobView(std::move(tb)); } std::vector Slice(const TraceBlobView& blob, size_t chunk_size) { std::vector 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& 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 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 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