blob: b2c47e1d61e38ffbc34dad14f870612a48e923ea [file] [log] [blame]
// Copyright 2018 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.
#include "garnet/lib/ui/gfx/resources/view.h"
#include <lib/async/cpp/task.h>
#include <lib/ui/scenic/cpp/commands.h>
#include <lib/ui/scenic/cpp/view_token_pair.h>
#include <lib/zx/eventpair.h>
#include "garnet/lib/ui/gfx/resources/nodes/entity_node.h"
#include "garnet/lib/ui/gfx/resources/nodes/view_node.h"
#include "garnet/lib/ui/gfx/resources/view_holder.h"
#include "garnet/lib/ui/gfx/tests/session_test.h"
#include "garnet/lib/ui/gfx/tests/util.h"
namespace scenic_impl {
namespace gfx {
namespace test {
void VerifyViewState(const fuchsia::ui::scenic::Event& event,
bool is_rendering_expected) {
EXPECT_EQ(fuchsia::ui::scenic::Event::Tag::kGfx, event.Which());
EXPECT_EQ(::fuchsia::ui::gfx::Event::Tag::kViewStateChanged,
event.gfx().Which());
const ::fuchsia::ui::gfx::ViewState& view_state =
event.gfx().view_state_changed().state;
EXPECT_EQ(is_rendering_expected, view_state.is_rendering);
}
class ViewTest : public SessionTest {
public:
ViewTest() {}
std::unique_ptr<SessionForTest> CreateSession() override {
SessionContext session_context = CreateBarebonesSessionContext();
view_linker_ = std::make_unique<ViewLinker>();
session_context.view_linker = view_linker_.get();
return std::make_unique<SessionForTest>(1, std::move(session_context), this,
error_reporter());
}
std::unique_ptr<ViewLinker> view_linker_;
};
// TODO(ES-179): Only seems to die in debug builds.
TEST_F(ViewTest, DISABLED_CreateViewWithBadTokenDies) {
EXPECT_DEATH_IF_SUPPORTED(
Apply(scenic::NewCreateViewCmd(1, fuchsia::ui::views::ViewToken(), "")),
"");
EXPECT_DEATH_IF_SUPPORTED(Apply(scenic::NewCreateViewHolderCmd(
2, fuchsia::ui::views::ViewHolderToken(), "")),
"");
}
TEST_F(ViewTest, ChildrenCanBeAddedToViewWithoutViewHolder) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_id = 1;
EXPECT_TRUE(
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test")));
EXPECT_ERROR_COUNT(0);
const ResourceId node1_id = 2;
EXPECT_TRUE(Apply(scenic::NewCreateEntityNodeCmd(node1_id)));
EXPECT_ERROR_COUNT(0);
const ResourceId node2_id = 3;
EXPECT_TRUE(Apply(scenic::NewCreateEntityNodeCmd(node2_id)));
EXPECT_ERROR_COUNT(0);
auto view = FindResource<View>(view_id);
auto node1 = FindResource<Node>(node1_id);
auto node2 = FindResource<Node>(node2_id);
EXPECT_TRUE(view);
EXPECT_TRUE(node1);
EXPECT_TRUE(node2);
EXPECT_TRUE(Apply(scenic::NewAddChildCmd(view_id, node1_id)));
EXPECT_TRUE(Apply(scenic::NewAddChildCmd(view_id, node2_id)));
EXPECT_ERROR_COUNT(0);
}
TEST_F(ViewTest, ExportsViewHolderViaCmd) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1;
EXPECT_TRUE(Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Test")));
EXPECT_ERROR_COUNT(0);
auto view_holder = FindResource<ViewHolder>(view_holder_id);
EXPECT_TRUE(view_holder);
EXPECT_EQ(nullptr, view_holder->view());
EXPECT_EQ(1u, session_->GetMappedResourceCount());
EXPECT_EQ(1u, view_linker_->ExportCount());
EXPECT_EQ(1u, view_linker_->UnresolvedExportCount());
EXPECT_EQ(0u, view_linker_->ImportCount());
EXPECT_EQ(0u, view_linker_->UnresolvedImportCount());
}
TEST_F(ViewTest, ImportsViewViaCmd) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_id = 1;
EXPECT_TRUE(
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test")));
EXPECT_ERROR_COUNT(0);
auto view = FindResource<View>(view_id);
EXPECT_TRUE(view);
EXPECT_EQ(nullptr, view->view_holder());
EXPECT_EQ(1u, session_->GetMappedResourceCount());
EXPECT_EQ(0u, view_linker_->ExportCount());
EXPECT_EQ(0u, view_linker_->UnresolvedExportCount());
EXPECT_EQ(1u, view_linker_->ImportCount());
EXPECT_EQ(1u, view_linker_->UnresolvedImportCount());
}
TEST_F(ViewTest, PairedViewAndHolderAreLinked) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
EXPECT_TRUE(Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]")));
EXPECT_ERROR_COUNT(0);
auto view_holder = FindResource<ViewHolder>(view_holder_id);
EXPECT_TRUE(view_holder);
EXPECT_EQ(nullptr, view_holder->view());
EXPECT_EQ(1u, session_->GetMappedResourceCount());
EXPECT_EQ(1u, view_linker_->ExportCount());
EXPECT_EQ(1u, view_linker_->UnresolvedExportCount());
EXPECT_EQ(0u, view_linker_->ImportCount());
EXPECT_EQ(0u, view_linker_->UnresolvedImportCount());
const ResourceId view_id = 2u;
EXPECT_TRUE(
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test")));
EXPECT_ERROR_COUNT(0);
auto view = FindResource<View>(view_id);
EXPECT_TRUE(view);
EXPECT_EQ(view.get(), view_holder->view());
EXPECT_EQ(view_holder.get(), view->view_holder());
EXPECT_EQ(2u, session_->GetMappedResourceCount());
EXPECT_EQ(1u, view_linker_->ExportCount());
EXPECT_EQ(0u, view_linker_->UnresolvedExportCount());
EXPECT_EQ(1u, view_linker_->ImportCount());
EXPECT_EQ(0u, view_linker_->UnresolvedImportCount());
EXPECT_NE(0u, events_.size());
const fuchsia::ui::scenic::Event& event = events_[0];
EXPECT_EQ(::fuchsia::ui::gfx::Event::Tag::kViewConnected,
event.gfx().Which());
}
TEST_F(ViewTest, ExportViewHolderWithDeadHandleFails) {
fuchsia::ui::views::ViewHolderToken view_holder_token_out;
{
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
view_holder_token_out.value = zx::eventpair(view_holder_token.value.get());
// view_holder_token dies now.
}
const ResourceId view_holder_id = 1;
EXPECT_FALSE(Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token_out), "Test")));
EXPECT_ERROR_COUNT(1); // Dead handles cause a session error.
auto view_holder = FindResource<ViewHolder>(view_holder_id);
EXPECT_FALSE(view_holder);
EXPECT_EQ(0u, session_->GetMappedResourceCount());
EXPECT_EQ(0u, view_linker_->ExportCount());
EXPECT_EQ(0u, view_linker_->UnresolvedExportCount());
EXPECT_EQ(0u, view_linker_->ImportCount());
EXPECT_EQ(0u, view_linker_->UnresolvedImportCount());
}
TEST_F(ViewTest, ViewHolderDestroyedBeforeView) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
events_.clear();
// Destroy the ViewHolder and disconnect the link.
Apply(scenic::NewReleaseResourceCmd(view_holder_id));
EXPECT_ERROR_COUNT(0);
const fuchsia::ui::scenic::Event& event = events_[0];
EXPECT_EQ(fuchsia::ui::gfx::Event::Tag::kViewHolderDisconnected,
event.gfx().Which());
}
TEST_F(ViewTest, ViewDestroyedBeforeViewHolder) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
events_.clear();
// Destroy the ViewHolder and disconnect the link.
Apply(scenic::NewReleaseResourceCmd(view_id));
EXPECT_ERROR_COUNT(0);
EXPECT_EQ(1u, events_.size());
const fuchsia::ui::scenic::Event& event = events_[0];
EXPECT_EQ(fuchsia::ui::gfx::Event::Tag::kViewDisconnected,
event.gfx().Which());
}
TEST_F(ViewTest, ViewAndViewHolderConnectedEvents) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
bool view_holder_connected_event = false;
bool view_connected_event = false;
for (fuchsia::ui::scenic::Event& event : events_) {
if (event.gfx().Which() ==
fuchsia::ui::gfx::Event::Tag::kViewHolderConnected) {
view_holder_connected_event = true;
} else if (event.gfx().Which() ==
fuchsia::ui::gfx::Event::Tag::kViewConnected) {
view_connected_event = true;
}
}
EXPECT_TRUE(view_holder_connected_event);
EXPECT_TRUE(view_connected_event);
events_.clear();
}
TEST_F(ViewTest, ViewHolderConnectsToScene) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
auto view_holder = FindResource<ViewHolder>(view_holder_id);
auto view = FindResource<View>(view_id);
events_.clear();
// Create a Scene and connect the ViewHolder to the Scene.
const ResourceId scene_id = 3u;
Apply(scenic::NewCreateSceneCmd(scene_id));
auto scene = FindResource<Scene>(scene_id);
EXPECT_TRUE(scene);
Apply(scenic::NewAddChildCmd(scene_id, view_holder_id));
// Verify the scene was successfully set.
EXPECT_EQ(1u, events_.size());
const fuchsia::ui::scenic::Event& event = events_[0];
EXPECT_EQ(::fuchsia::ui::gfx::Event::Tag::kViewAttachedToScene,
event.gfx().Which());
}
TEST_F(ViewTest, ViewHolderDetachedAndReleased) {
// Create ViewHolder and View.
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
auto view = FindResource<View>(view_id);
events_.clear();
// Create a Scene and connect the ViewHolder to the Scene.
const ResourceId scene_id = 3u;
Apply(scenic::NewCreateSceneCmd(scene_id));
auto scene = FindResource<Scene>(scene_id);
EXPECT_TRUE(scene);
EXPECT_TRUE(Apply(scenic::NewAddChildCmd(scene_id, view_holder_id)));
// Create child node for the View.
const ResourceId node1_id = 4u;
Apply(scenic::NewCreateEntityNodeCmd(node1_id));
EXPECT_TRUE(Apply(scenic::NewAddChildCmd(view_id, node1_id)));
auto node1 = FindResource<Node>(node1_id);
EXPECT_TRUE(node1);
auto view_node = view->GetViewNode();
EXPECT_EQ(1u, view_node->children().size());
EXPECT_ERROR_COUNT(0);
// Detach the ViewHolder from the scene graph.
EXPECT_TRUE(Apply(scenic::NewDetachCmd(view_holder_id)));
{
auto view_holder = FindResource<ViewHolder>(view_holder_id);
// The view holder is still in the ResourceMap so it should still be
// connected to the view.
EXPECT_EQ(1u, view_holder->children().size());
// The view is detached from the scene but still attached to the ViewHolder.
bool detached_from_scene_event = false;
for (fuchsia::ui::scenic::Event& event : events_) {
detached_from_scene_event |=
(event.gfx().Which() ==
fuchsia::ui::gfx::Event::Tag::kViewDetachedFromScene);
}
EXPECT_TRUE(detached_from_scene_event);
events_.clear();
} // view_holder out of scope, release reference.
// Now, release the ViewHolder resource. Its link should be destroyed.
EXPECT_TRUE(Apply(scenic::NewReleaseResourceCmd(view_holder_id)));
EXPECT_ERROR_COUNT(0);
bool view_holder_disconnected_event = false;
for (fuchsia::ui::scenic::Event& event : events_) {
if (event.gfx().Which() ==
fuchsia::ui::gfx::Event::Tag::kViewHolderDisconnected) {
view_holder_disconnected_event = true;
break;
}
}
EXPECT_TRUE(view_holder_disconnected_event);
// The View's subtree should still be attached to the ViewNode.
EXPECT_EQ(1u, view_node->children().size());
EXPECT_FALSE(view_node->parent());
}
TEST_F(ViewTest, ViewHolderChildrenReleasedFromSceneGraphWhenViewDestroyed) {
// Create ViewHolder and View.
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
auto view_holder = FindResource<ViewHolder>(view_holder_id);
auto view = FindResource<View>(view_id);
events_.clear();
// Create child nodes for the View.
const ResourceId node1_id = 3u;
EXPECT_TRUE(Apply(scenic::NewCreateEntityNodeCmd(node1_id)));
const ResourceId node2_id = 4u;
EXPECT_TRUE(Apply(scenic::NewCreateEntityNodeCmd(node2_id)));
// Add children
EXPECT_TRUE(Apply(scenic::NewAddChildCmd(view_id, node1_id)));
EXPECT_TRUE(Apply(scenic::NewAddChildCmd(view_id, node2_id)));
view = FindResource<View>(view_id);
auto node1 = FindResource<Node>(node1_id);
auto node2 = FindResource<Node>(node2_id);
EXPECT_TRUE(view);
EXPECT_TRUE(node1);
EXPECT_TRUE(node2);
// Release the View
Apply(scenic::NewReleaseResourceCmd(view_id));
view = FindResource<View>(view_id);
node1 = FindResource<Node>(node1_id);
node2 = FindResource<Node>(node2_id);
EXPECT_FALSE(view);
// The child nodes are still part of the ResourcMap, and should not be
// destroyed.
EXPECT_TRUE(node1);
EXPECT_TRUE(node2);
// The nodes should not be parented.
EXPECT_FALSE(node1->parent());
EXPECT_FALSE(node1->scene());
EXPECT_FALSE(node2->parent());
// The view holder should not have any children.
EXPECT_EQ(0u, view_holder->children().size());
}
TEST_F(ViewTest, ViewNodeChildAddedToViewHolder) {
// Create ViewHolder and View.
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
auto view_holder = FindResource<ViewHolder>(view_holder_id);
auto view = FindResource<View>(view_id);
events_.clear();
auto view_node = view->GetViewNode();
EXPECT_TRUE(view->GetViewNode());
EXPECT_EQ(1u, view_holder->children().size());
EXPECT_EQ(view_node->global_id(), view_holder->children()[0]->global_id());
}
TEST_F(ViewTest, ViewHolderCannotAddArbitraryChildNodes) {
// Create ViewHolder.
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
// Create an EntityNode.
const ResourceId node_id = 2u;
EXPECT_TRUE(Apply(scenic::NewCreateEntityNodeCmd(node_id)));
EXPECT_ERROR_COUNT(0);
// Attempt to add the node as a child of the ViewHolder.
EXPECT_FALSE(Apply(scenic::NewAddChildCmd(view_holder_id, node_id)));
EXPECT_ERROR_COUNT(1);
}
TEST_F(ViewTest, ViewNodePairedToView) {
// Create View.
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
auto view = FindResource<View>(view_id);
events_.clear();
auto view_node = view->GetViewNode();
EXPECT_NE(nullptr, view_node);
EXPECT_EQ(view->global_id(), view_node->GetView()->global_id());
EXPECT_EQ(view->id(), view_node->GetView()->id());
EXPECT_EQ(view->global_id(), view_node->FindOwningView()->global_id());
}
TEST_F(ViewTest, ViewNodeNotInResourceMap) {
// Create ViewHolder and View.
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
auto view_holder = FindResource<ViewHolder>(view_holder_id);
auto view = FindResource<View>(view_id);
events_.clear();
EXPECT_NE(nullptr, view->GetViewNode());
EXPECT_EQ(nullptr, FindResource<ViewNode>(view->GetViewNode()->id()).get());
EXPECT_ERROR_COUNT(1);
}
TEST_F(ViewTest, ViewHolderGrandchildGetsSceneRefreshed) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId kViewHolderId = 1u;
Apply(scenic::NewCreateViewHolderCmd(
kViewHolderId, std::move(view_holder_token), "ViewHolder"));
const ResourceId kViewId = 2u;
Apply(scenic::NewCreateViewCmd(kViewId, std::move(view_token), "View"));
// Create a parent node for the ViewHolder.
const ResourceId kEntityNodeId = 3u;
Apply(scenic::NewCreateEntityNodeCmd(kEntityNodeId));
Apply(scenic::NewAddChildCmd(kEntityNodeId, kViewHolderId));
// Create a scene node.
const ResourceId kSceneId = 4u;
Apply(scenic::NewCreateSceneCmd(kSceneId));
auto scene = FindResource<Scene>(kSceneId);
EXPECT_ERROR_COUNT(0);
// Set the ViewHolder's parent as the child of the scene.
Apply(scenic::NewAddChildCmd(kSceneId, kEntityNodeId));
// Verify scene was set on ViewHolder
const fuchsia::ui::scenic::Event& event = events_.back();
EXPECT_EQ(::fuchsia::ui::gfx::Event::Tag::kViewAttachedToScene,
event.gfx().Which());
}
TEST_F(ViewTest, ViewLinksAfterViewHolderConnectsToScene) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
auto view_holder = FindResource<ViewHolder>(view_holder_id);
// Create a Scene and connect the ViewHolder to the Scene.
const ResourceId scene_id = 3u;
Apply(scenic::NewCreateSceneCmd(scene_id));
auto scene = FindResource<Scene>(scene_id);
EXPECT_TRUE(scene);
Apply(scenic::NewAddChildCmd(scene_id, view_holder_id));
EXPECT_EQ(0u, events_.size());
// Link the View to the ViewHolder.
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
auto view = FindResource<View>(view_id);
EXPECT_ERROR_COUNT(0);
// Verify the connect event was emitted before the scene attached event.
EXPECT_EQ(4u, events_.size());
EXPECT_ERROR_COUNT(0);
const fuchsia::ui::scenic::Event& event = events_[0];
EXPECT_EQ(::fuchsia::ui::gfx::Event::Tag::kViewConnected,
event.gfx().Which());
bool view_attached_to_scene_event = false;
for (fuchsia::ui::scenic::Event& event : events_) {
if (event.gfx().Which() ==
fuchsia::ui::gfx::Event::Tag::kViewAttachedToScene) {
view_attached_to_scene_event = true;
}
}
EXPECT_TRUE(view_attached_to_scene_event);
}
TEST_F(ViewTest, ViewStateChangeNotifiesViewHolder) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
// Verify View and ViewHolder are linked.
auto view_holder = FindResource<ViewHolder>(view_holder_id);
auto view = FindResource<View>(view_id);
EXPECT_EQ(view.get(), view_holder->view());
// Clear View/ViewHolder connected events from the session.
events_.clear();
// Trigger a change in the ViewState. Mark as rendering.
view->SignalRender();
// Verify that one ViewState change event was enqueued.
RunLoopUntilIdle();
EXPECT_EQ(1u, events_.size());
const fuchsia::ui::scenic::Event& event = events_[0];
VerifyViewState(event, true);
}
TEST_F(ViewTest, RenderStateAcrossManyFrames) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
// Verify View and ViewHolder are linked.
auto view_holder = FindResource<ViewHolder>(view_holder_id);
auto view = FindResource<View>(view_id);
EXPECT_EQ(view.get(), view_holder->view());
// Clear View/ViewHolder connected events from the session.
events_.clear();
// Trigger a change in the ViewState. Mark as rendering.
view->SignalRender();
RunLoopUntilIdle();
// Signal render for subsequent frames. No change in rendering state,
// should not enqueue another event.
view->SignalRender();
view->SignalRender();
RunLoopUntilIdle();
// Verify that one ViewState change event was enqueued.
EXPECT_EQ(1u, events_.size());
const fuchsia::ui::scenic::Event& event = events_[0];
VerifyViewState(event, true);
}
TEST_F(ViewTest, RenderStateFalseWhenViewDisconnects) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
auto view_holder = FindResource<ViewHolder>(view_holder_id);
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
{
auto view = FindResource<View>(view_id);
// Verify resources are mapped and linked.
EXPECT_EQ(2u, session_->GetMappedResourceCount());
// Mark the view as rendering.
view->SignalRender();
RunLoopUntilIdle();
events_.clear();
} // Exit scope should destroy the view and disconnect the link.
Apply(scenic::NewReleaseResourceCmd(view_id));
EXPECT_EQ(2u, events_.size());
const fuchsia::ui::scenic::Event& event = events_[0];
VerifyViewState(event, false);
const fuchsia::ui::scenic::Event& event2 = events_.back();
EXPECT_EQ(fuchsia::ui::scenic::Event::Tag::kGfx, event2.Which());
EXPECT_EQ(::fuchsia::ui::gfx::Event::Tag::kViewDisconnected,
event2.gfx().Which());
}
TEST_F(ViewTest, ViewHolderRenderWaitClearedWhenViewDestroyed) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
auto view_holder = FindResource<ViewHolder>(view_holder_id);
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
// Verify resources are mapped and linked.
EXPECT_EQ(2u, session_->GetMappedResourceCount());
events_.clear();
EXPECT_ERROR_COUNT(0);
// Destroy the view. The link between View and ViewHolder should be
// disconnected.
Apply(scenic::NewReleaseResourceCmd(view_id));
EXPECT_EQ(1u, session_->GetMappedResourceCount());
EXPECT_EQ(1u, events_.size());
const fuchsia::ui::scenic::Event& event = events_.back();
EXPECT_EQ(fuchsia::ui::scenic::Event::Tag::kGfx, event.Which());
EXPECT_EQ(::fuchsia::ui::gfx::Event::Tag::kViewDisconnected,
event.gfx().Which());
}
TEST_F(ViewTest, RenderSignalDoesntCrashWhenViewHolderDestroyed) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 1u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 2u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
// Destroy the ViewHolder and disconnect the link.
Apply(scenic::NewReleaseResourceCmd(view_holder_id));
events_.clear();
// Mark the view as rendering.
auto view = FindResource<View>(view_id);
view->SignalRender();
RunLoopUntilIdle();
EXPECT_ERROR_COUNT(0);
// No additional render state events should have been posted.
EXPECT_EQ(0u, events_.size());
}
TEST_F(ViewTest, RenderStateFalseWhenViewHolderDisconnectsFromScene) {
auto [view_token, view_holder_token] = scenic::ViewTokenPair::New();
const ResourceId view_holder_id = 2u;
Apply(scenic::NewCreateViewHolderCmd(
view_holder_id, std::move(view_holder_token), "Holder [Test]"));
const ResourceId view_id = 1u;
Apply(scenic::NewCreateViewCmd(view_id, std::move(view_token), "Test"));
EXPECT_ERROR_COUNT(0);
auto view_holder = FindResource<ViewHolder>(view_holder_id);
auto view = FindResource<View>(view_id);
events_.clear();
// Make sure that the ViewHolder is connected to the Scene and the View is
// rendering.
const ResourceId scene_id = 3u;
Apply(scenic::NewCreateSceneCmd(scene_id));
auto scene = FindResource<Scene>(scene_id);
Apply(scenic::NewAddChildCmd(scene_id, view_holder_id));
view->SignalRender();
RunLoopUntilIdle();
events_.clear();
// Detach ViewHolder from the scene.
view_holder->Detach();
// The "stopped rendering" event should have emitted before the "detached from
// scene" event.
EXPECT_EQ(2u, events_.size());
const fuchsia::ui::scenic::Event& event = events_[0];
VerifyViewState(event, false);
const fuchsia::ui::scenic::Event& event2 = events_.back();
EXPECT_EQ(::fuchsia::ui::gfx::Event::Tag::kViewDetachedFromScene,
event2.gfx().Which());
}
} // namespace test
} // namespace gfx
} // namespace scenic_impl