| /* | 
 |  * Copyright (C) 2022 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/db/view.h" | 
 | #include "src/trace_processor/tables/macros.h" | 
 | #include "src/trace_processor/views/macros.h" | 
 |  | 
 | #include "test/gtest_and_gmock.h" | 
 |  | 
 | namespace perfetto { | 
 | namespace trace_processor { | 
 | namespace { | 
 |  | 
 | #define PERFETTO_TP_TEST_THREAD_TABLE_DEF(NAME, PARENT, C) \ | 
 |   NAME(TestThreadTable, "thread_table")                    \ | 
 |   PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C)             \ | 
 |   C(StringPool::Id, name)                                  \ | 
 |   C(uint32_t, tid) | 
 | PERFETTO_TP_TABLE(PERFETTO_TP_TEST_THREAD_TABLE_DEF); | 
 |  | 
 | #define PERFETTO_TP_TEST_TRACK_TABLE_DEF(NAME, PARENT, C) \ | 
 |   NAME(TestTrackTable, "track_table")                     \ | 
 |   PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C)            \ | 
 |   C(StringPool::Id, name) | 
 | PERFETTO_TP_TABLE(PERFETTO_TP_TEST_TRACK_TABLE_DEF); | 
 |  | 
 | #define PERFETTO_TP_TEST_THREAD_TRACK_TABLE_DEF(NAME, PARENT, C) \ | 
 |   NAME(TestThreadTrackTable, "thread_track_table")               \ | 
 |   PARENT(PERFETTO_TP_TEST_TRACK_TABLE_DEF, C)                    \ | 
 |   C(TestThreadTable::Id, utid) | 
 | PERFETTO_TP_TABLE(PERFETTO_TP_TEST_THREAD_TRACK_TABLE_DEF); | 
 |  | 
 | #define PERFETTO_TP_TEST_EVENT_TABLE_DEF(NAME, PARENT, C) \ | 
 |   NAME(TestEventTable, "event_table")                     \ | 
 |   PARENT(PERFETTO_TP_ROOT_TABLE_PARENT_DEF, C)            \ | 
 |   C(int64_t, ts, Column::Flag::kSorted)                   \ | 
 |   C(TestTrackTable::Id, track_id) | 
 | PERFETTO_TP_TABLE(PERFETTO_TP_TEST_EVENT_TABLE_DEF); | 
 |  | 
 | #define PERFETTO_TP_TEST_SLICE_TABLE_DEF(NAME, PARENT, C) \ | 
 |   NAME(TestSliceTable, "slice_table")                     \ | 
 |   PARENT(PERFETTO_TP_TEST_EVENT_TABLE_DEF, C)             \ | 
 |   C(StringPool::Id, name) | 
 | PERFETTO_TP_TABLE(PERFETTO_TP_TEST_SLICE_TABLE_DEF); | 
 |  | 
 | TestThreadTable::~TestThreadTable() = default; | 
 | TestTrackTable::~TestTrackTable() = default; | 
 | TestThreadTrackTable::~TestThreadTrackTable() = default; | 
 | TestEventTable::~TestEventTable() = default; | 
 | TestSliceTable::~TestSliceTable() = default; | 
 |  | 
 | template <typename ViewSubclass> | 
 | class AbstractViewTest : public ::testing::Test { | 
 |  protected: | 
 |   using ColIdx = typename ViewSubclass::ColumnIndex; | 
 |   using QueryResult = typename ViewSubclass::QueryResult; | 
 |  | 
 |   virtual ~AbstractViewTest() = default; | 
 |  | 
 |   QueryResult Query(const std::vector<Constraint>& cs = {}, | 
 |                     const std::vector<Order>& ob = {}) { | 
 |     return Query(cs, ob, AllColsUsed(view())); | 
 |   } | 
 |   QueryResult QueryUsingCols(const std::vector<uint32_t>& cols_used) { | 
 |     return Query({}, {}, cols_used); | 
 |   } | 
 |   QueryResult Query(const std::vector<Constraint>& cs, | 
 |                     const std::vector<Order>& ob, | 
 |                     const std::vector<uint32_t>& cols_used) { | 
 |     return view().Query(cs, ob, IvToBv(view(), cols_used)); | 
 |   } | 
 |  | 
 |   StringPool::Id Intern(const char* ptr) { return pool_.InternString(ptr); } | 
 |  | 
 |   virtual ViewSubclass& view() = 0; | 
 |  | 
 |   StringPool pool_; | 
 |  | 
 |  private: | 
 |   std::vector<uint32_t> AllColsUsed(const View& v) { | 
 |     std::vector<uint32_t> used(v.GetColumnCount()); | 
 |     std::iota(used.begin(), used.end(), 0); | 
 |     return used; | 
 |   } | 
 |  | 
 |   BitVector IvToBv(const View& v, const std::vector<uint32_t>& cols_used) { | 
 |     BitVector bv(v.GetColumnCount()); | 
 |     for (uint32_t col : cols_used) { | 
 |       bv.Set(col); | 
 |     } | 
 |     return bv; | 
 |   } | 
 | }; | 
 |  | 
 | #define PERFETTO_TP_EVENT_VIEW_DEF(NAME, FROM, JOIN, COL, _)               \ | 
 |   NAME(TestEventView, "event_view")                                        \ | 
 |   FROM(TestEventTable, event)                                              \ | 
 |   JOIN(TestTrackTable, track, id, event, track_id, View::kIdAlwaysPresent) \ | 
 |   COL(id, event, id)                                                       \ | 
 |   COL(ts, event, ts)                                                       \ | 
 |   COL(track_id, event, track_id)                                           \ | 
 |   COL(track_name, track, name) | 
 | PERFETTO_TP_DECLARE_VIEW(PERFETTO_TP_EVENT_VIEW_DEF); | 
 | PERFETTO_TP_DEFINE_VIEW(TestEventView); | 
 |  | 
 | class EventViewTest : public AbstractViewTest<TestEventView> { | 
 |  protected: | 
 |   EventViewTest() { | 
 |     t1_id_ = track_.Insert({/* name */ Intern("foo")}).id; | 
 |     t2_id_ = track_.Insert({/* name */ Intern("bar")}).id; | 
 |  | 
 |     event_table_.Insert({/* ts */ 100, t1_id_}); | 
 |     event_table_.Insert({/* ts */ 101, t2_id_}); | 
 |     event_table_.Insert({/* ts */ 102, t1_id_}); | 
 |   } | 
 |  | 
 |   virtual TestEventView& view() override { return event_view_; } | 
 |  | 
 |   TestTrackTable::Id t1_id_; | 
 |   TestTrackTable::Id t2_id_; | 
 |  | 
 |  private: | 
 |   TestEventTable event_table_{&pool_, nullptr}; | 
 |   TestTrackTable track_{&pool_, nullptr}; | 
 |   TestEventView event_view_{&event_table_, &track_}; | 
 | }; | 
 |  | 
 | TEST_F(EventViewTest, UnusedColumnsAreDummy) { | 
 |   TestEventView::QueryResult result = QueryUsingCols({ColIdx::track_name}); | 
 |   ASSERT_TRUE(result.columns()[ColIdx::id].IsDummy()); | 
 |   ASSERT_TRUE(result.columns()[ColIdx::ts].IsDummy()); | 
 |   ASSERT_FALSE(result.columns()[ColIdx::track_name].IsDummy()); | 
 | } | 
 |  | 
 | TEST_F(EventViewTest, Iterate) { | 
 |   TestEventView::QueryResult result = Query(); | 
 |   auto it = result.IterateRows(); | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.row_number().row_number(), 0u); | 
 |   ASSERT_EQ(it.ts(), 100); | 
 |   ASSERT_EQ(it.track_name(), Intern("foo")); | 
 |   ASSERT_EQ(it.track_id(), t1_id_); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.row_number().row_number(), 1u); | 
 |   ASSERT_EQ(it.ts(), 101); | 
 |   ASSERT_EQ(it.track_name(), Intern("bar")); | 
 |   ASSERT_EQ(it.track_id(), t2_id_); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.row_number().row_number(), 2u); | 
 |   ASSERT_EQ(it.ts(), 102); | 
 |   ASSERT_EQ(it.track_name(), Intern("foo")); | 
 |   ASSERT_EQ(it.track_id(), t1_id_); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(EventViewTest, FilterEventEmpty) { | 
 |   TestEventView::QueryResult result = Query({view().ts().eq(0)}); | 
 |   auto it = result.IterateRows(); | 
 |   ASSERT_FALSE(it); | 
 | } | 
 |  | 
 | TEST_F(EventViewTest, FilterEventNoUseTrack) { | 
 |   TestEventView::QueryResult result = | 
 |       Query({view().ts().eq(100)}, {}, {ColIdx::ts}); | 
 |   auto it = result.IterateRows(); | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 100); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(EventViewTest, FilterEventUseTrack) { | 
 |   TestEventView::QueryResult result = | 
 |       Query({view().ts().eq(100)}, {}, | 
 |             {ColIdx::ts, ColIdx::track_name, ColIdx::track_id}); | 
 |   auto it = result.IterateRows(); | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 100); | 
 |   ASSERT_EQ(it.track_name(), Intern("foo")); | 
 |   ASSERT_EQ(it.track_id(), t1_id_); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(EventViewTest, FilterTrackEmpty) { | 
 |   TestEventView::QueryResult result = Query({view().track_id().eq(102398)}); | 
 |   auto it = result.IterateRows(); | 
 |   ASSERT_FALSE(it); | 
 | } | 
 |  | 
 | TEST_F(EventViewTest, FilterTrackNoUseEvent) { | 
 |   TestEventView::QueryResult result = | 
 |       Query({view().track_name().eq("foo")}, {}, | 
 |             {ColIdx::track_name, ColIdx::track_id}); | 
 |   auto it = result.IterateRows(); | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.track_id(), t1_id_); | 
 |   ASSERT_EQ(it.track_name(), Intern("foo")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.track_id(), t1_id_); | 
 |   ASSERT_EQ(it.track_name(), Intern("foo")); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(EventViewTest, FilterTrackUseEvent) { | 
 |   TestEventView::QueryResult result = | 
 |       Query({view().track_id().eq(t1_id_.value)}, {}, | 
 |             {ColIdx::ts, ColIdx::track_name, ColIdx::track_id}); | 
 |   auto it = result.IterateRows(); | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 100); | 
 |   ASSERT_EQ(it.track_name(), Intern("foo")); | 
 |   ASSERT_EQ(it.track_id(), t1_id_); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 102); | 
 |   ASSERT_EQ(it.track_name(), Intern("foo")); | 
 |   ASSERT_EQ(it.track_id(), t1_id_); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | #define PERFETTO_TP_THREAD_EVENT_VIEW_DEF(NAME, FROM, JOIN, COL, _)      \ | 
 |   NAME(TestThreadEventView, "thread_event_view")                         \ | 
 |   FROM(TestEventTable, event)                                            \ | 
 |   JOIN(TestThreadTrackTable, track, id, event, track_id, View::kNoFlag)  \ | 
 |   JOIN(TestThreadTable, thread, id, track, utid, View::kIdAlwaysPresent) \ | 
 |   COL(id, event, id)                                                     \ | 
 |   COL(ts, event, ts)                                                     \ | 
 |   COL(track_id, track, id)                                               \ | 
 |   COL(track_name, track, name)                                           \ | 
 |   COL(utid, track, utid)                                                 \ | 
 |   COL(thread_name, thread, name) | 
 | PERFETTO_TP_DECLARE_VIEW(PERFETTO_TP_THREAD_EVENT_VIEW_DEF); | 
 | PERFETTO_TP_DEFINE_VIEW(TestThreadEventView); | 
 |  | 
 | class ThreadEventViewTest : public AbstractViewTest<TestThreadEventView> { | 
 |  protected: | 
 |   ThreadEventViewTest() { | 
 |     th1_id_ = thread_.Insert({Intern("th1"), 1}).id; | 
 |     th2_id_ = thread_.Insert({Intern("th2"), 2}).id; | 
 |  | 
 |     t1_id_ = track_.Insert({/* name */ Intern("t1")}).id; | 
 |     t2_id_ = track_.Insert({/* name */ Intern("t2")}).id; | 
 |     t3_id_ = thread_track_.Insert({/* name */ Intern("t3"), th2_id_}).id; | 
 |     t4_id_ = thread_track_.Insert({/* name */ Intern("t4"), th1_id_}).id; | 
 |     t5_id_ = thread_track_.Insert({/* name */ Intern("t5"), th2_id_}).id; | 
 |     t6_id_ = track_.Insert({/* name */ Intern("t6")}).id; | 
 |  | 
 |     event_table_.Insert({/* ts */ 100, t1_id_}); | 
 |     event_table_.Insert({/* ts */ 101, t2_id_}); | 
 |     event_table_.Insert({/* ts */ 102, t3_id_}); | 
 |     event_table_.Insert({/* ts */ 103, t5_id_}); | 
 |     event_table_.Insert({/* ts */ 104, t4_id_}); | 
 |     event_table_.Insert({/* ts */ 105, t5_id_}); | 
 |     event_table_.Insert({/* ts */ 106, t1_id_}); | 
 |     event_table_.Insert({/* ts */ 107, t4_id_}); | 
 |   } | 
 |  | 
 |   virtual TestThreadEventView& view() override { return event_view_; } | 
 |  | 
 |   TestThreadTable::Id th1_id_; | 
 |   TestThreadTable::Id th2_id_; | 
 |  | 
 |   TestTrackTable::Id t1_id_; | 
 |   TestTrackTable::Id t2_id_; | 
 |   TestTrackTable::Id t3_id_; | 
 |   TestTrackTable::Id t4_id_; | 
 |   TestTrackTable::Id t5_id_; | 
 |   TestTrackTable::Id t6_id_; | 
 |  | 
 |  private: | 
 |   TestEventTable event_table_{&pool_, nullptr}; | 
 |   TestTrackTable track_{&pool_, nullptr}; | 
 |   TestThreadTrackTable thread_track_{&pool_, &track_}; | 
 |   TestThreadTable thread_{&pool_, nullptr}; | 
 |   TestThreadEventView event_view_{&event_table_, &thread_track_, &thread_}; | 
 | }; | 
 |  | 
 | TEST_F(ThreadEventViewTest, Iterate) { | 
 |   auto result = Query(); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 102); | 
 |   ASSERT_EQ(it.track_id(), t3_id_); | 
 |   ASSERT_EQ(it.track_name(), Intern("t3")); | 
 |   ASSERT_EQ(it.utid(), th2_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th2")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 103); | 
 |   ASSERT_EQ(it.track_name(), Intern("t5")); | 
 |   ASSERT_EQ(it.track_id(), t5_id_); | 
 |   ASSERT_EQ(it.utid(), th2_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th2")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 104); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |   ASSERT_EQ(it.track_name(), Intern("t4")); | 
 |   ASSERT_EQ(it.utid(), th1_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th1")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 105); | 
 |   ASSERT_EQ(it.track_id(), t5_id_); | 
 |   ASSERT_EQ(it.track_name(), Intern("t5")); | 
 |   ASSERT_EQ(it.utid(), th2_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th2")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 107); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |   ASSERT_EQ(it.track_name(), Intern("t4")); | 
 |   ASSERT_EQ(it.utid(), th1_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th1")); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(ThreadEventViewTest, FilterEventUseTrackAndThread) { | 
 |   auto result = | 
 |       Query({view().ts().ge(105)}, {}, | 
 |             {ColIdx::ts, ColIdx::track_id, ColIdx::utid, ColIdx::thread_name}); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 105); | 
 |   ASSERT_EQ(it.track_id(), t5_id_); | 
 |   ASSERT_EQ(it.utid(), th2_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th2")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 107); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |   ASSERT_EQ(it.utid(), th1_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th1")); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(ThreadEventViewTest, FilterEventUseThreadNoUseTrack) { | 
 |   auto result = Query({view().ts().ge(103), view().ts().le(105)}, {}, | 
 |                       {ColIdx::ts, ColIdx::thread_name}); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 103); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th2")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 104); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th1")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 105); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th2")); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(ThreadEventViewTest, FilterTrackUseEventNoUseThread) { | 
 |   auto result = Query({view().track_id().eq(t4_id_.value)}, {}, | 
 |                       {ColIdx::ts, ColIdx::track_id}); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 104); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 107); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(ThreadEventViewTest, FilterEventAndTrack) { | 
 |   auto result = Query({view().ts().ge(103), view().track_name().eq("t5")}, {}, | 
 |                       {ColIdx::ts, ColIdx::track_name}); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 103); | 
 |   ASSERT_EQ(it.track_name(), Intern("t5")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 105); | 
 |   ASSERT_EQ(it.track_name(), Intern("t5")); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(ThreadEventViewTest, FilterEventAndThread) { | 
 |   auto result = Query({view().ts().ge(103), view().thread_name().eq("th1")}, {}, | 
 |                       {ColIdx::ts, ColIdx::thread_name}); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 104); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th1")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 107); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th1")); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | #define PERFETTO_TP_THREAD_SLICE_VIEW_DEF(NAME, FROM, JOIN, COL, _)     \ | 
 |   NAME(TestThreadSliceView, "thread_slice_view")                        \ | 
 |   COL(id, slice, id)                                                    \ | 
 |   COL(ts, slice, ts)                                                    \ | 
 |   COL(name, slice, name)                                                \ | 
 |   COL(track_id, slice, track_id)                                        \ | 
 |   COL(track_name, track, name)                                          \ | 
 |   COL(utid, thread, id)                                                 \ | 
 |   COL(thread_name, thread, name)                                        \ | 
 |   FROM(TestSliceTable, slice)                                           \ | 
 |   JOIN(TestThreadTrackTable, track, id, slice, track_id, View::kNoFlag) \ | 
 |   JOIN(TestThreadTable, thread, id, track, utid, View::kIdAlwaysPresent) | 
 | PERFETTO_TP_DECLARE_VIEW(PERFETTO_TP_THREAD_SLICE_VIEW_DEF); | 
 | PERFETTO_TP_DEFINE_VIEW(TestThreadSliceView); | 
 |  | 
 | class ThreadSliceViewTest : public AbstractViewTest<TestThreadSliceView> { | 
 |  protected: | 
 |   ThreadSliceViewTest() { | 
 |     th1_id_ = thread_.Insert({Intern("th1"), 1}).id; | 
 |     th2_id_ = thread_.Insert({Intern("th2"), 2}).id; | 
 |  | 
 |     t1_id_ = track_.Insert({/* name */ Intern("t1")}).id; | 
 |     t2_id_ = track_.Insert({/* name */ Intern("t2")}).id; | 
 |     t3_id_ = thread_track_.Insert({/* name */ Intern("t3"), th2_id_}).id; | 
 |     t4_id_ = thread_track_.Insert({/* name */ Intern("t4"), th1_id_}).id; | 
 |     t5_id_ = thread_track_.Insert({/* name */ Intern("t5"), th2_id_}).id; | 
 |     t6_id_ = track_.Insert({/* name */ Intern("t6")}).id; | 
 |  | 
 |     event_.Insert({/* ts */ 100, t1_id_}); | 
 |     event_.Insert({/* ts */ 101, t2_id_}); | 
 |     slice_table_.Insert({/* ts */ 102, t3_id_, Intern("ts102")}); | 
 |     slice_table_.Insert({/* ts */ 103, t5_id_, Intern("ts103")}); | 
 |     slice_table_.Insert({/* ts */ 104, t4_id_, Intern("ts104")}); | 
 |     event_.Insert({/* ts */ 105, t5_id_}); | 
 |     slice_table_.Insert({/* ts */ 106, t1_id_, Intern("ts106")}); | 
 |     slice_table_.Insert({/* ts */ 107, t4_id_, Intern("ts107")}); | 
 |   } | 
 |  | 
 |   TestThreadSliceView& view() override { return slice_view_; } | 
 |  | 
 |   TestThreadTable::Id th1_id_; | 
 |   TestThreadTable::Id th2_id_; | 
 |  | 
 |   TestTrackTable::Id t1_id_; | 
 |   TestTrackTable::Id t2_id_; | 
 |   TestTrackTable::Id t3_id_; | 
 |   TestTrackTable::Id t4_id_; | 
 |   TestTrackTable::Id t5_id_; | 
 |   TestTrackTable::Id t6_id_; | 
 |  | 
 |  private: | 
 |   TestEventTable event_{&pool_, nullptr}; | 
 |   TestSliceTable slice_table_{&pool_, &event_}; | 
 |   TestTrackTable track_{&pool_, nullptr}; | 
 |   TestThreadTrackTable thread_track_{&pool_, &track_}; | 
 |   TestThreadTable thread_{&pool_, nullptr}; | 
 |   TestThreadSliceView slice_view_{&slice_table_, &thread_track_, &thread_}; | 
 | }; | 
 |  | 
 | TEST_F(ThreadSliceViewTest, Iterate) { | 
 |   auto result = Query(); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 102); | 
 |   ASSERT_EQ(it.track_id(), t3_id_); | 
 |   ASSERT_EQ(it.track_name(), Intern("t3")); | 
 |   ASSERT_EQ(it.utid(), th2_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th2")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 103); | 
 |   ASSERT_EQ(it.track_name(), Intern("t5")); | 
 |   ASSERT_EQ(it.track_id(), t5_id_); | 
 |   ASSERT_EQ(it.utid(), th2_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th2")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 104); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |   ASSERT_EQ(it.track_name(), Intern("t4")); | 
 |   ASSERT_EQ(it.utid(), th1_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th1")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 107); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |   ASSERT_EQ(it.track_name(), Intern("t4")); | 
 |   ASSERT_EQ(it.utid(), th1_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th1")); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(ThreadSliceViewTest, FilterAll) { | 
 |   auto result = Query({view().ts().le(106), view().track_id().le(t4_id_.value), | 
 |                        view().thread_name().eq("th2")}, | 
 |                       {}, {ColIdx::ts, ColIdx::track_id, ColIdx::thread_name}); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 102); | 
 |   ASSERT_EQ(it.track_id(), t3_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th2")); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(ThreadSliceViewTest, FilterEventAndTrack) { | 
 |   auto result = Query({view().ts().le(106), view().track_id().le(t4_id_.value)}, | 
 |                       {}, {ColIdx::ts, ColIdx::track_id}); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 102); | 
 |   ASSERT_EQ(it.track_id(), t3_id_); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 104); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(ThreadSliceViewTest, Sort) { | 
 |   auto result = | 
 |       Query({}, {view().track_id().ascending(), view().ts().descending()}, | 
 |             {ColIdx::track_id, ColIdx::ts}); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 102); | 
 |   ASSERT_EQ(it.track_id(), t3_id_); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 107); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 104); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 103); | 
 |   ASSERT_EQ(it.track_id(), t5_id_); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | TEST_F(ThreadSliceViewTest, FilterAndSort) { | 
 |   auto result = Query({view().track_id().lt(t5_id_.value)}, | 
 |                       {view().track_id().ascending(), view().ts().descending()}, | 
 |                       {ColIdx::track_id, ColIdx::ts, ColIdx::thread_name}); | 
 |   auto it = result.IterateRows(); | 
 |  | 
 |   ASSERT_TRUE(it); | 
 |   ASSERT_EQ(it.ts(), 102); | 
 |   ASSERT_EQ(it.track_id(), t3_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th2")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 107); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th1")); | 
 |  | 
 |   ASSERT_TRUE(++it); | 
 |   ASSERT_EQ(it.ts(), 104); | 
 |   ASSERT_EQ(it.track_id(), t4_id_); | 
 |   ASSERT_EQ(it.thread_name(), Intern("th1")); | 
 |  | 
 |   ASSERT_FALSE(++it); | 
 | } | 
 |  | 
 | }  // namespace | 
 | }  // namespace trace_processor | 
 | }  // namespace perfetto |