blob: deace78961e2d794fe83c8021bfecdb46f743983 [file] [log] [blame] [edit]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef LIB_UI_SCENIC_CPP_TESTING_FAKE_FLATLAND_H_
#define LIB_UI_SCENIC_CPP_TESTING_FAKE_FLATLAND_H_
#include <fuchsia/math/cpp/fidl.h>
#include <fuchsia/scenic/scheduling/cpp/fidl.h>
#include <fuchsia/sysmem/cpp/fidl.h>
#include <fuchsia/sysmem2/cpp/fidl.h>
#include <fuchsia/ui/composition/cpp/fidl.h>
#include <fuchsia/ui/composition/cpp/fidl_test_base.h>
#include <fuchsia/ui/views/cpp/fidl.h>
#include <lib/async/dispatcher.h>
#include <lib/fidl/cpp/binding.h>
#include <lib/fidl/cpp/interface_request.h>
#include <lib/ui/scenic/cpp/testing/fake_flatland_types.h>
#include <cstdint>
#include <functional>
#include <string>
#include <unordered_map>
#include <vector>
namespace scenic {
// A lightweight fake implementation of the Flatland API.
//
// The fake has no side effects besides mutating its own internal state
// according to the rules of interacting with the Flatland API. It makes that
// internal state available for inspection by a test.
//
// Thus it allows tests to do a few things that would be difficult using either
// a mock implementation or the real implementation:
// + It allows the user to hook `Present` invocations and respond with
// stubbed-out `FuturePresentationTimes`, but more crucially it mimics the
// real Flatland behavior of only processing commands when a `Present` is
// invoked.
// + It allows the user to inspect a snapshot of the scene graph at any moment
// in time, via the `SceneGraph()` accessor.
// + It stores the various Flatland transforms and content generated by
// commands into a std::unordered_map, and also correctly manages the
// transform and content lifetimes via reference counting. This allows a
// given transform or content to stay alive if its parent still holds a
// reference to it, in the same way the real Flatland implementation would.
// + The resources returned by `SceneGraph()` that the test uses for
// inspection are decoupled from the resources managed internally by the
// `FakeFlatland` itself -- they are a snapshot of the scene graph at that
// moment in time, with all snapshot state being cloned from the underlying
// scene graph state. This allows the `FakeFlatland` and test to naturally
// use `shared_ptr` for reference counting and mimic the real Flatland
// behavior exactly, instead of an awkward index-based API.
//
// The fake deliberately does not attempt to handle certain aspects of the real
// Flatland implementation which are complex or burdensome to implement:
// + Rendering/interacting with Vulkan in any way
// + Cross-flatland links between Views and Viewports
// + Sysmem allocations
class FakeFlatland : public fuchsia::ui::composition::testing::Allocator_TestBase,
public fuchsia::ui::composition::testing::Flatland_TestBase {
public:
using PresentHandler = std::function<void(fuchsia::ui::composition::PresentArgs)>;
struct ParentViewportWatcher
: public fuchsia::ui::composition::testing::ParentViewportWatcher_TestBase {
public:
ParentViewportWatcher(fuchsia::ui::views::ViewCreationToken view_token,
fuchsia::ui::views::ViewIdentityOnCreation view_identity,
fuchsia::ui::composition::ViewBoundProtocols view_protocols,
fidl::InterfaceRequest<fuchsia::ui::composition::ParentViewportWatcher>
parent_viewport_watcher,
async_dispatcher_t* dispatcher);
ParentViewportWatcher(ParentViewportWatcher&& other) noexcept;
~ParentViewportWatcher() override;
// |fuchsia::ui::composition::testing::ParentViewportWatcher_TestBase|
void NotImplemented_(const std::string& name) override;
// |fuchsia::ui::composition::ParentViewportWatcher|
void GetLayout(GetLayoutCallback callback) override;
// |fuchsia::ui::composition::ParentViewportWatcher|
void GetStatus(GetStatusCallback callback) override;
fuchsia::ui::views::ViewCreationToken view_token;
fuchsia::ui::views::ViewIdentityOnCreation view_identity;
fuchsia::ui::composition::ViewBoundProtocols view_protocols;
fidl::Binding<fuchsia::ui::composition::ParentViewportWatcher> parent_viewport_watcher;
};
struct ChildViewWatcher : public fuchsia::ui::composition::testing::ChildViewWatcher_TestBase {
public:
ChildViewWatcher(
fuchsia::ui::views::ViewportCreationToken viewport_token,
fidl::InterfaceRequest<fuchsia::ui::composition::ChildViewWatcher> child_view_watcher,
async_dispatcher_t* dispatcher);
ChildViewWatcher(ChildViewWatcher&& other) noexcept;
~ChildViewWatcher() override;
// |fuchsia::ui::composition::testing::ChildViewWatcher_TestBase|
void NotImplemented_(const std::string& name) override;
// |fuchsia::ui::composition::ChildViewWatcher|
void GetStatus(GetStatusCallback callback) override;
fuchsia::ui::views::ViewportCreationToken viewport_token;
fidl::Binding<fuchsia::ui::composition::ChildViewWatcher> child_view_watcher;
};
struct BufferCollectionBinding {
fuchsia::ui::composition::BufferCollectionExportToken export_token;
#if FUCHSIA_API_LEVEL_AT_LEAST(25)
fuchsia::sysmem2::BufferCollectionTokenHandle sysmem_token;
#else
fuchsia::sysmem::BufferCollectionTokenHandle sysmem_token;
#endif
fuchsia::ui::composition::RegisterBufferCollectionUsage usage;
};
struct GraphBindings {
std::optional<std::pair<zx_koid_t, ParentViewportWatcher>> viewport_watcher;
std::unordered_map<zx_koid_t, ChildViewWatcher> view_watchers;
std::unordered_map<zx_koid_t, BufferCollectionBinding> buffer_collections;
};
FakeFlatland();
~FakeFlatland() override;
// Delete copy and assignment ctors.
FakeFlatland(const FakeFlatland&) = delete;
FakeFlatland& operator=(const FakeFlatland&) = delete;
bool is_allocator_connected() const { return allocator_binding_.is_bound(); }
bool is_flatland_connected() const { return flatland_binding_.is_bound(); }
const std::string& debug_name() const { return debug_name_; }
const FakeGraph& graph() { return current_graph_; }
GraphBindings& graph_bindings() { return graph_bindings_; }
// Bind this instance's Flatland FIDL channel to the `dispatcher` and allow
// processing of incoming FIDL requests.
//
// This can only be called once.
fuchsia::ui::composition::AllocatorHandle ConnectAllocator(
async_dispatcher_t* dispatcher = nullptr);
// Bind this instance's Allocator FIDL channel to the `dispatcher` and allow
// processing of incoming FIDL requests.
//
// This can only be called once.
fuchsia::ui::composition::FlatlandHandle ConnectFlatland(
async_dispatcher_t* dispatcher = nullptr);
// Returns a request handler that binds the incoming FIDL requests to this
// session's FIDL channels on the `dispatcher`.
//
// This can only be called once.
fidl::InterfaceRequestHandler<fuchsia::ui::composition::Flatland> GetFlatlandRequestHandler(
async_dispatcher_t* dispatcher = nullptr);
fidl::InterfaceRequestHandler<fuchsia::ui::composition::Allocator> GetAllocatorRequestHandler(
async_dispatcher_t* dispatcher = nullptr);
// Disconnect the Flatland's FIDL channel with an error.
void Disconnect(fuchsia::ui::composition::FlatlandError error);
// Set a handler for `Present`-related FIDL calls' return values.
void SetPresentHandler(PresentHandler present_handler);
// Fire an `OnNextFrameBegin` event. Call this first after a `Present` in
// order to give additional present tokens to the client and simulate scenic's
// normal event flow.
void FireOnNextFrameBeginEvent(
fuchsia::ui::composition::OnNextFrameBeginValues on_next_frame_begin_values);
// Fire an `OnFramePresented` event. Call this second after a `Present` in
// order to inform the client of returned frames and simulate scenic's normal
// event flow.
void FireOnFramePresentedEvent(
fuchsia::scenic::scheduling::FramePresentedInfo frame_presented_info);
// |fuchsia::ui::composition::testing::Allocator_TestBase|
// |fuchsia::ui::composition::testing::Flatland_TestBase|
void NotImplemented_(const std::string& name) override;
// |fuchsia::ui::composition::testing::Allocator|
void RegisterBufferCollection(fuchsia::ui::composition::RegisterBufferCollectionArgs args,
RegisterBufferCollectionCallback callback) override;
// |fuchsia::ui::composition::Flatland|
void Present(fuchsia::ui::composition::PresentArgs args) override;
// |fuchsia::ui::composition::Flatland|
void CreateView(fuchsia::ui::views::ViewCreationToken token,
fidl::InterfaceRequest<fuchsia::ui::composition::ParentViewportWatcher>
parent_viewport_watcher) override;
// |fuchsia::ui::composition::Flatland|
void CreateView2(fuchsia::ui::views::ViewCreationToken token,
fuchsia::ui::views::ViewIdentityOnCreation view_identity,
fuchsia::ui::composition::ViewBoundProtocols view_protocols,
fidl::InterfaceRequest<fuchsia::ui::composition::ParentViewportWatcher>
parent_viewport_watcher) override;
// |fuchsia::ui::composition::Flatland|
void CreateTransform(fuchsia::ui::composition::TransformId transform_id) override;
// |fuchsia::ui::composition::Flatland|
void SetTranslation(fuchsia::ui::composition::TransformId transform_id,
fuchsia::math::Vec translation) override;
// |fuchsia::ui::composition::Flatland|
void SetScale(fuchsia::ui::composition::TransformId transform_id,
fuchsia::math::VecF scale) override;
// |fuchsia::ui::composition::Flatland|
void SetOrientation(fuchsia::ui::composition::TransformId transform_id,
fuchsia::ui::composition::Orientation orientation) override;
// |fuchsia::ui::composition::Flatland|
void SetOpacity(fuchsia::ui::composition::TransformId transform_id, float value) override;
// |fuchsia::ui::composition::Flatland|
void SetClipBoundary(fuchsia::ui::composition::TransformId transform_id,
std::unique_ptr<fuchsia::math::Rect> bounds) override;
// |fuchsia::ui::composition::Flatland|
void SetImageOpacity(fuchsia::ui::composition::ContentId image_id, float opacity) override;
// |fuchsia::ui::composition::Flatland|
void AddChild(fuchsia::ui::composition::TransformId parent_transform_id,
fuchsia::ui::composition::TransformId child_transform_id) override;
// |fuchsia::ui::composition::Flatland|
void RemoveChild(fuchsia::ui::composition::TransformId parent_transform_id,
fuchsia::ui::composition::TransformId child_transform_id) override;
// |fuchsia::ui::composition::Flatland|
void SetContent(fuchsia::ui::composition::TransformId transform_id,
fuchsia::ui::composition::ContentId content_id) override;
// |fuchsia::ui::composition::Flatland|
void SetRootTransform(fuchsia::ui::composition::TransformId transform_id) override;
// |fuchsia::ui::composition::Flatland|
void CreateViewport(fuchsia::ui::composition::ContentId viewport_id,
fuchsia::ui::views::ViewportCreationToken token,
fuchsia::ui::composition::ViewportProperties properties,
fidl::InterfaceRequest<fuchsia::ui::composition::ChildViewWatcher>
child_view_watcher) override;
// |fuchsia::ui::composition::Flatland|
void CreateImage(fuchsia::ui::composition::ContentId image_id,
fuchsia::ui::composition::BufferCollectionImportToken import_token,
uint32_t vmo_index,
fuchsia::ui::composition::ImageProperties properties) override;
// |fuchsia::ui::composition::Flatland|
void SetImageSampleRegion(fuchsia::ui::composition::ContentId image_id,
fuchsia::math::RectF rect) override;
// |fuchsia::ui::composition::Flatland|
void SetImageDestinationSize(fuchsia::ui::composition::ContentId image_id,
fuchsia::math::SizeU size) override;
// |fuchsia::ui::composition::Flatland|
void SetImageBlendingFunction(fuchsia::ui::composition::ContentId image_id,
fuchsia::ui::composition::BlendMode blend_mode) override;
// |fuchsia::ui::composition::Flatland|
void SetImageFlip(fuchsia::ui::composition::ContentId image_id,
fuchsia::ui::composition::ImageFlip flip) override;
// |fuchsia::ui::composition::Flatland|
void SetViewportProperties(fuchsia::ui::composition::ContentId viewport_id,
fuchsia::ui::composition::ViewportProperties properties) override;
// |fuchsia::ui::composition::Flatland|
void ReleaseTransform(fuchsia::ui::composition::TransformId transform_id) override;
// |fuchsia::ui::composition::Flatland|
void ReleaseViewport(fuchsia::ui::composition::ContentId viewport_id,
ReleaseViewportCallback callback) override;
// |fuchsia::ui::composition::Flatland|
void ReleaseImage(fuchsia::ui::composition::ContentId image_id) override;
// |fuchsia::ui::composition::Flatland|
void SetHitRegions(fuchsia::ui::composition::TransformId transform_id,
std::vector<fuchsia::ui::composition::HitRegion> regions) override;
// |fuchsia::ui::composition::Flatland|
void SetInfiniteHitRegion(fuchsia::ui::composition::TransformId transform_id,
fuchsia::ui::composition::HitTestInteraction hit_test) override;
// |fuchsia::ui::composition::Flatland|
void Clear() override;
// |fuchsia::ui::composition::Flatland|
void SetDebugName(std::string debug_name) override;
private:
void ReportBadOperationError();
// True if any function has failed since the previous call to Present(), false otherwise.
bool failure_since_previous_present_ = false;
fidl::Binding<fuchsia::ui::composition::Allocator> allocator_binding_;
fidl::Binding<fuchsia::ui::composition::Flatland> flatland_binding_;
GraphBindings graph_bindings_;
PresentHandler present_handler_;
FakeGraph pending_graph_;
FakeGraph current_graph_;
// This map is used to cache parent refs for `AddChild` and `RemoveChild`.
//
// Ideally we would like to map weak(child) -> weak(parent), but std::weak_ptr
// cannot be the Key for an associative container. Instead we key on the raw
// parent pointer and store pair(weak(parent), weak(child)) in the map.
std::unordered_map<FakeGraph::TransformIdKey,
std::pair<std::weak_ptr<FakeTransform>, std::weak_ptr<FakeTransform>>>
parents_map_;
std::string debug_name_;
};
} // namespace scenic
#endif // LIB_UI_SCENIC_CPP_TESTING_FAKE_FLATLAND_H_