|  | /* | 
|  | * 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/containers/interval_tree.h" | 
|  |  | 
|  | #include <cstddef> | 
|  | #include <cstdint> | 
|  | #include <numeric> | 
|  | #include <random> | 
|  | #include <tuple> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | #include "perfetto/base/compiler.h" | 
|  | #include "test/gtest_and_gmock.h" | 
|  |  | 
|  | namespace perfetto::trace_processor { | 
|  |  | 
|  | inline bool operator==(const Interval& a, const Interval& b) { | 
|  | return std::tie(a.start, a.end, a.id) == std::tie(b.start, b.end, b.id); | 
|  | } | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using Interval = Interval; | 
|  | using testing::IsEmpty; | 
|  | using testing::UnorderedElementsAre; | 
|  |  | 
|  | std::vector<Interval> CreateIntervals( | 
|  | std::vector<std::pair<uint32_t, uint32_t>> periods) { | 
|  | std::vector<Interval> res; | 
|  | uint32_t id = 0; | 
|  | for (auto period : periods) { | 
|  | res.push_back({period.first, period.second, id++}); | 
|  | } | 
|  | return res; | 
|  | } | 
|  |  | 
|  | TEST(IntervalTree, Trivial) { | 
|  | std::vector<Interval> interval({{10, 20, 5}}); | 
|  | IntervalTree tree(interval); | 
|  | std::vector<uint32_t> overlaps; | 
|  | tree.FindOverlaps(5, 30, overlaps); | 
|  |  | 
|  | ASSERT_THAT(overlaps, UnorderedElementsAre(5)); | 
|  | } | 
|  |  | 
|  | TEST(IntervalTree, Simple) { | 
|  | auto intervals = CreateIntervals({{0, 10}, {5, 20}, {30, 40}}); | 
|  | IntervalTree tree(intervals); | 
|  | std::vector<uint32_t> overlaps; | 
|  | tree.FindOverlaps(4, 30, overlaps); | 
|  |  | 
|  | ASSERT_THAT(overlaps, UnorderedElementsAre(0, 1)); | 
|  | } | 
|  |  | 
|  | TEST(IntervalTree, SinglePointOverlap) { | 
|  | auto intervals = CreateIntervals({{10, 20}}); | 
|  | IntervalTree tree(intervals); | 
|  | std::vector<uint32_t> overlaps; | 
|  |  | 
|  | // Overlaps at the start point only | 
|  | tree.FindOverlaps(10, 10, overlaps); | 
|  | ASSERT_THAT(overlaps, IsEmpty()); | 
|  |  | 
|  | overlaps.clear(); | 
|  |  | 
|  | // Overlaps at the end point only | 
|  | tree.FindOverlaps(20, 20, overlaps); | 
|  | ASSERT_THAT(overlaps, IsEmpty()); | 
|  | } | 
|  |  | 
|  | TEST(IntervalTree, NoOverlaps) { | 
|  | auto intervals = CreateIntervals({{10, 20}, {30, 40}}); | 
|  | IntervalTree tree(intervals); | 
|  | std::vector<uint32_t> overlaps; | 
|  |  | 
|  | // Before all intervals | 
|  | tree.FindOverlaps(5, 9, overlaps); | 
|  | ASSERT_THAT(overlaps, IsEmpty()); | 
|  | overlaps.clear(); | 
|  |  | 
|  | // Between intervals | 
|  | tree.FindOverlaps(21, 29, overlaps); | 
|  | ASSERT_THAT(overlaps, IsEmpty()); | 
|  | overlaps.clear(); | 
|  |  | 
|  | // After all intervals | 
|  | tree.FindOverlaps(41, 50, overlaps); | 
|  | ASSERT_THAT(overlaps, IsEmpty()); | 
|  | } | 
|  |  | 
|  | TEST(IntervalTree, IdenticalIntervals) { | 
|  | auto intervals = CreateIntervals({{10, 20}, {10, 20}}); | 
|  | IntervalTree tree(intervals); | 
|  | std::vector<uint32_t> overlaps; | 
|  | tree.FindOverlaps(10, 20, overlaps); | 
|  | ASSERT_THAT(overlaps, UnorderedElementsAre(0, 1)); | 
|  | } | 
|  |  | 
|  | TEST(IntervalTree, MultipleOverlapsVariousPositions) { | 
|  | auto intervals = CreateIntervals({{5, 15}, {10, 20}, {12, 22}, {25, 35}}); | 
|  | IntervalTree tree(intervals); | 
|  |  | 
|  | std::vector<uint32_t> overlaps; | 
|  | /// Starts before, ends within | 
|  | tree.FindOverlaps(9, 11, overlaps); | 
|  | ASSERT_THAT(overlaps, UnorderedElementsAre(0, 1)); | 
|  |  | 
|  | overlaps.clear(); | 
|  | // Starts within, ends within | 
|  | tree.FindOverlaps(13, 21, overlaps); | 
|  | ASSERT_THAT(overlaps, UnorderedElementsAre(0, 1, 2)); | 
|  |  | 
|  | overlaps.clear(); | 
|  | // Starts within, ends after | 
|  | tree.FindOverlaps(18, 26, overlaps); | 
|  | ASSERT_THAT(overlaps, UnorderedElementsAre(1, 2, 3)); | 
|  | } | 
|  |  | 
|  | TEST(IntervalTree, OverlappingEndpoints) { | 
|  | auto intervals = CreateIntervals({{10, 20}, {20, 30}}); | 
|  | IntervalTree tree(intervals); | 
|  | std::vector<uint32_t> overlaps; | 
|  |  | 
|  | tree.FindOverlaps(19, 21, overlaps); | 
|  | ASSERT_THAT(overlaps, UnorderedElementsAre(0, 1)); | 
|  | } | 
|  |  | 
|  | TEST(IntervalTree, Stress) { | 
|  | static constexpr size_t kCount = 9249; | 
|  | std::minstd_rand0 rng(42); | 
|  |  | 
|  | std::vector<std::pair<uint32_t, uint32_t>> periods; | 
|  | uint32_t prev_max = 0; | 
|  | for (uint32_t i = 0; i < kCount; ++i) { | 
|  | prev_max += static_cast<uint32_t>(rng()) % 100; | 
|  | periods.push_back( | 
|  | {prev_max, prev_max + (static_cast<uint32_t>(rng()) % 100)}); | 
|  | } | 
|  | auto intervals = CreateIntervals(periods); | 
|  | IntervalTree tree(intervals); | 
|  | std::vector<uint32_t> overlaps; | 
|  | tree.FindOverlaps(periods.front().first, periods.back().first + 1, overlaps); | 
|  |  | 
|  | EXPECT_EQ(overlaps.size(), kCount); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  | }  // namespace perfetto::trace_processor |