SF unit test utils. Bug: 411363817 Test: n/a test suite to come
diff --git a/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_test_utils.h b/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_test_utils.h new file mode 100644 index 0000000..6c31b0e --- /dev/null +++ b/src/trace_processor/importers/proto/winscope/surfaceflinger_layers_test_utils.h
@@ -0,0 +1,240 @@ +/* + * 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_IMPORTERS_PROTO_WINSCOPE_SURFACEFLINGER_LAYERS_TEST_UTILS_H_ +#define SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SURFACEFLINGER_LAYERS_TEST_UTILS_H_ + +#include "src/trace_processor/importers/proto/winscope/surfaceflinger_layers_visibility_computation.h" + +#include <optional> +#include <unordered_map> +#include <vector> + +#include "protos/perfetto/trace/android/graphics/rect.gen.h" +#include "protos/perfetto/trace/android/surfaceflinger_common.gen.h" +#include "protos/perfetto/trace/android/surfaceflinger_layers.gen.h" +#include "src/trace_processor/importers/proto/winscope/surfaceflinger_layers_extractor.h" + +namespace perfetto::trace_processor::winscope::surfaceflinger_layers::test { + +struct Color { + float r; + float g; + float b; + float a; +}; + +struct ActiveBuffer { + uint32_t width; + uint32_t height; + uint32_t stride; + int32_t format; +}; + +namespace { +using LayerProto = protos::gen::LayerProto; +using FloatRectProto = protos::gen::FloatRectProto; +using RectProto = protos::gen::RectProto; + +void UpdateColor(protos::gen::LayerProto* layer, Color color) { + auto* color_proto = layer->mutable_color(); + color_proto->set_r(color.r); + color_proto->set_g(color.g); + color_proto->set_b(color.b); + color_proto->set_a(color.a); +} + +void UpdateActiveBuffer(protos::gen::LayerProto* layer, ActiveBuffer buffer) { + auto* buffer_proto = layer->mutable_active_buffer(); + buffer_proto->set_width(buffer.width); + buffer_proto->set_height(buffer.height); + buffer_proto->set_stride(buffer.stride); + buffer_proto->set_format(buffer.format); +} + +void UpdateRect(protos::gen::FloatRectProto* rect_proto, geometry::Rect rect) { + rect_proto->set_left(static_cast<float>(rect.x)); + rect_proto->set_top(static_cast<float>(rect.y)); + rect_proto->set_right(static_cast<float>(rect.x + rect.w)); + rect_proto->set_bottom(static_cast<float>(rect.y + rect.h)); +} + +void UpdateRect(protos::gen::RectProto* rect_proto, geometry::Rect rect) { + rect_proto->set_left(static_cast<int32_t>(rect.x)); + rect_proto->set_top(static_cast<int32_t>(rect.y)); + rect_proto->set_right(static_cast<int32_t>(rect.x + rect.w)); + rect_proto->set_bottom(static_cast<int32_t>(rect.y + rect.h)); +} +} // namespace + +class Layer { + public: + explicit Layer() = default; + + Layer& SetColor(Color value) { + color_ = value; + return *this; + } + + Layer& SetActiveBuffer(ActiveBuffer value) { + active_buffer_ = value; + return *this; + } + + Layer& SetFlags(uint32_t value) { + flags_ = value; + return *this; + } + + Layer& SetParent(int32_t value) { + parent_ = value; + return *this; + } + + Layer& SetZOrderRelativeOf(uint32_t value) { + z_order_relative_of_ = value; + return *this; + } + + Layer& SetSourceBounds(geometry::Rect value) { + source_bounds_ = value; + return *this; + } + + Layer& SetScreenBounds(geometry::Rect value) { + screen_bounds_ = value; + return *this; + } + + Layer& SetBounds(geometry::Rect value) { + bounds_ = value; + return *this; + } + + Layer& InitializeVisibleRegion() { + std::vector<geometry::Rect> rects; + visible_region_rects_ = rects; + return *this; + } + + Layer& AddVisibleRegionRect(geometry::Rect value) { + if (!visible_region_rects_.has_value()) { + InitializeVisibleRegion(); + } + visible_region_rects_->push_back(value); + return *this; + } + + Layer& SetIsOpaque(bool value) { + is_opaque_ = value; + return *this; + } + + Layer& SetLayerStack(uint32_t value) { + layer_stack_ = value; + return *this; + } + + std::optional<Color> color_; + std::optional<ActiveBuffer> active_buffer_; + std::optional<uint32_t> flags_; + std::optional<int32_t> parent_; + std::optional<int32_t> z_order_relative_of_; + std::optional<geometry::Rect> source_bounds_; + std::optional<geometry::Rect> screen_bounds_; + std::optional<geometry::Rect> bounds_; + std::optional<std::vector<geometry::Rect>> visible_region_rects_; + std::optional<bool> is_opaque_; + std::optional<uint32_t> layer_stack_; +}; + +class SnapshotProtoBuilder { + public: + explicit SnapshotProtoBuilder() = default; + + SnapshotProtoBuilder& SetExcludesCompositionState(bool value) { + excludes_composition_state_ = value; + return *this; + } + + SnapshotProtoBuilder& AddLayer(const Layer& value) { + layers_.push_back(value); + return *this; + } + + std::string Build() { + protos::gen::LayersSnapshotProto snapshot_proto; + snapshot_proto.set_excludes_composition_state(excludes_composition_state_); + auto* layers_proto = snapshot_proto.mutable_layers(); + + auto i = 1; + for (const auto& layer : layers_) { + auto* layer_proto = layers_proto->add_layers(); + layer_proto->set_id(i); + i++; + + if (layer.color_.has_value()) { + UpdateColor(layer_proto, layer.color_.value()); + } + if (layer.active_buffer_.has_value()) { + UpdateActiveBuffer(layer_proto, layer.active_buffer_.value()); + } + if (layer.source_bounds_.has_value()) { + UpdateRect(layer_proto->mutable_source_bounds(), + layer.source_bounds_.value()); + } + if (layer.screen_bounds_.has_value()) { + UpdateRect(layer_proto->mutable_screen_bounds(), + layer.screen_bounds_.value()); + } + if (layer.bounds_.has_value()) { + UpdateRect(layer_proto->mutable_bounds(), layer.bounds_.value()); + } + if (layer.flags_.has_value()) { + layer_proto->set_flags(layer.flags_.value()); + } + if (layer.parent_.has_value()) { + layer_proto->set_parent(layer.parent_.value()); + } + if (layer.z_order_relative_of_.has_value()) { + layer_proto->set_z_order_relative_of( + layer.z_order_relative_of_.value()); + } + if (layer.visible_region_rects_.has_value()) { + auto* visible_region_proto = layer_proto->mutable_visible_region(); + for (auto& rect : layer.visible_region_rects_.value()) { + UpdateRect(visible_region_proto->add_rect(), rect); + } + } + if (layer.is_opaque_.has_value()) { + layer_proto->set_is_opaque(layer.is_opaque_.value()); + } + if (layer.layer_stack_.has_value()) { + layer_proto->set_layer_stack(layer.layer_stack_.value()); + } + } + + return snapshot_proto.SerializeAsString(); + } + + private: + bool excludes_composition_state_ = false; + std::vector<Layer> layers_; +}; + +} // namespace perfetto::trace_processor::winscope::surfaceflinger_layers::test + +#endif // SRC_TRACE_PROCESSOR_IMPORTERS_PROTO_WINSCOPE_SURFACEFLINGER_LAYERS_TEST_UTILS_H_