| /* |
| * Copyright (C) 2025 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. |
| */ |
| |
| #ifndef SRC_TRACE_PROCESSOR_DATAFRAME_DATAFRAME_TEST_UTILS_H_ |
| #define SRC_TRACE_PROCESSOR_DATAFRAME_DATAFRAME_TEST_UTILS_H_ |
| |
| #include <cstddef> |
| #include <cstdint> |
| #include <optional> |
| #include <variant> |
| #include <vector> |
| |
| #include "perfetto/base/compiler.h" |
| #include "perfetto/base/logging.h" |
| #include "src/trace_processor/containers/null_term_string_view.h" |
| #include "src/trace_processor/dataframe/cursor.h" |
| #include "src/trace_processor/dataframe/cursor_impl.h" // IWYU pragma: keep |
| #include "src/trace_processor/dataframe/value_fetcher.h" |
| |
| namespace perfetto::trace_processor::dataframe { |
| |
| struct TestRowFetcher : ValueFetcher { |
| using Value = std::variant<std::nullopt_t, int64_t, double, const char*>; |
| enum Type : uint8_t { |
| kNull, |
| kInt64, |
| kDouble, |
| kString, |
| }; |
| |
| void SetRow(const std::vector<Value>& row_data) { current_row_ = &row_data; } |
| |
| Type GetValueType(uint32_t index) { |
| PERFETTO_CHECK(current_row_ && index < current_row_->size()); |
| const auto& var = (*current_row_)[index]; |
| if (std::holds_alternative<std::nullopt_t>(var)) |
| return Type::kNull; |
| if (std::holds_alternative<int64_t>(var)) |
| return Type::kInt64; |
| if (std::holds_alternative<double>(var)) |
| return Type::kDouble; |
| if (std::holds_alternative<const char*>(var)) |
| return Type::kString; |
| PERFETTO_FATAL("Invalid variant state"); |
| } |
| |
| int64_t GetInt64Value(uint32_t index) { |
| PERFETTO_CHECK(current_row_ && index < current_row_->size()); |
| return std::get<int64_t>((*current_row_)[index]); |
| } |
| |
| double GetDoubleValue(uint32_t index) { |
| PERFETTO_CHECK(current_row_ && index < current_row_->size()); |
| return std::get<double>((*current_row_)[index]); |
| } |
| |
| const char* GetStringValue(uint32_t index) { |
| PERFETTO_CHECK(current_row_ && index < current_row_->size()); |
| return std::get<const char*>((*current_row_)[index]); |
| } |
| static bool IteratorInit(uint32_t) { PERFETTO_FATAL("Unsupported"); } |
| static bool IteratorNext(uint32_t) { PERFETTO_FATAL("Unsupported"); } |
| |
| private: |
| const std::vector<Value>* current_row_ = nullptr; |
| }; |
| |
| // Callback for verifying cell values from the cursor. |
| // Stores visited values in variants for later checking. |
| struct ValueVerifier : CellCallback { |
| using ValueVariant = std::variant<uint32_t, |
| int32_t, |
| int64_t, |
| double, |
| NullTermStringView, |
| std::nullptr_t>; |
| |
| template <typename Fetcher> |
| void Fetch(Cursor<Fetcher>* cursor, uint32_t col_count) { |
| for (uint32_t i = 0; i < col_count; ++i) { |
| cursor->Cell(i, *this); |
| } |
| } |
| void OnCell(int64_t value) { values.emplace_back(value); } |
| void OnCell(double value) { values.emplace_back(value); } |
| void OnCell(NullTermStringView value) { values.emplace_back(value); } |
| void OnCell(std::nullptr_t) { values.emplace_back(nullptr); } |
| void OnCell(int32_t value) { values.emplace_back(value); } |
| void OnCell(uint32_t value) { values.emplace_back(value); } |
| |
| std::vector<ValueVariant> values; |
| }; |
| |
| inline void VerifyData( |
| Dataframe& df, |
| uint64_t cols_bitmap, |
| const std::vector<std::vector<ValueVerifier::ValueVariant>>& expected) { |
| std::vector<FilterSpec> filter_specs; |
| auto num_cols_selected = |
| static_cast<uint32_t>(PERFETTO_POPCOUNT(cols_bitmap)); |
| ASSERT_OK_AND_ASSIGN(auto plan, |
| df.PlanQuery(filter_specs, {}, {}, {}, cols_bitmap)); |
| |
| // Heap allocate to avoid potential stack overflows due to large cursor |
| // object. |
| auto cursor = std::make_unique<Cursor<TestRowFetcher>>(); |
| df.PrepareCursor(std::move(plan), *cursor); |
| |
| TestRowFetcher fetcher; |
| cursor->Execute(fetcher); |
| |
| size_t row_index = 0; |
| for (const auto& row : expected) { |
| ValueVerifier verifier; |
| ASSERT_FALSE(cursor->Eof()) << "Cursor finished early at row " << row_index; |
| verifier.Fetch(&*cursor, num_cols_selected); |
| EXPECT_THAT(verifier.values, testing::ElementsAreArray(row)) |
| << "Mismatch in data for row " << row_index; |
| cursor->Next(); |
| row_index++; |
| } |
| ASSERT_TRUE(cursor->Eof()) << "Cursor has more rows than expected. Expected " |
| << expected.size() << " rows."; |
| ASSERT_EQ(row_index, expected.size()) |
| << "Mismatch in number of rows processed."; |
| } |
| |
| template <typename... Args> |
| std::vector<ValueVerifier::ValueVariant> Row(Args&&... args) { |
| return std::vector<ValueVerifier::ValueVariant>{ |
| ValueVerifier::ValueVariant(std::forward<Args>(args))...}; |
| } |
| |
| template <typename... Args> |
| std::vector<std::vector<ValueVerifier::ValueVariant>> Rows(Args&&... rows) { |
| return std::vector<std::vector<ValueVerifier::ValueVariant>>{ |
| std::forward<Args>(rows)...}; |
| } |
| |
| } // namespace perfetto::trace_processor::dataframe |
| |
| #endif // SRC_TRACE_PROCESSOR_DATAFRAME_DATAFRAME_TEST_UTILS_H_ |