blob: 8148dd902db4d3539aa342774865a25da0f7b7e5 [file] [log] [blame]
// Copyright 2013 The Flutter 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 "flutter/lib/ui/compositing/scene_builder.h"
#include <memory>
#include "flutter/common/task_runners.h"
#include "flutter/fml/synchronization/waitable_event.h"
#include "flutter/runtime/dart_vm.h"
#include "flutter/shell/common/shell_test.h"
#include "flutter/shell/common/thread_host.h"
#include "flutter/testing/testing.h"
// CREATE_NATIVE_ENTRY is leaky by design
// NOLINTBEGIN(clang-analyzer-core.StackAddressEscape)
namespace flutter {
namespace testing {
TEST_F(ShellTest, SceneBuilderBuildAndSceneDisposeReleasesLayerStack) {
auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
// prevent ClearDartWrapper() from deleting the scene builder.
SceneBuilder* retained_scene_builder;
Scene* retained_scene;
auto validate_builder_has_layers =
[&retained_scene_builder](Dart_NativeArguments args) {
auto handle = Dart_GetNativeArgument(args, 0);
intptr_t peer = 0;
Dart_Handle result = Dart_GetNativeInstanceField(
handle, tonic::DartWrappable::kPeerIndex, &peer);
ASSERT_FALSE(Dart_IsError(result));
retained_scene_builder = reinterpret_cast<SceneBuilder*>(peer);
retained_scene_builder->AddRef();
ASSERT_TRUE(retained_scene_builder);
ASSERT_EQ(retained_scene_builder->layer_stack().size(), 2ul);
};
auto validate_builder_has_no_layers =
[&retained_scene_builder](Dart_NativeArguments args) {
ASSERT_EQ(retained_scene_builder->layer_stack().size(), 0ul);
retained_scene_builder->Release();
retained_scene_builder = nullptr;
};
auto capture_scene = [&retained_scene](Dart_NativeArguments args) {
auto handle = Dart_GetNativeArgument(args, 0);
intptr_t peer = 0;
Dart_Handle result = Dart_GetNativeInstanceField(
handle, tonic::DartWrappable::kPeerIndex, &peer);
ASSERT_FALSE(Dart_IsError(result));
retained_scene = reinterpret_cast<Scene*>(peer);
retained_scene->AddRef();
ASSERT_TRUE(retained_scene);
};
auto validate_scene_has_no_layers =
[message_latch, &retained_scene](Dart_NativeArguments args) {
EXPECT_FALSE(retained_scene->takeLayerTree());
retained_scene->Release();
retained_scene = nullptr;
message_latch->Signal();
};
Settings settings = CreateSettingsForFixture();
TaskRunners task_runners("test", // label
GetCurrentTaskRunner(), // platform
CreateNewThread(), // raster
CreateNewThread(), // ui
CreateNewThread() // io
);
AddNativeCallback("ValidateBuilderHasLayers",
CREATE_NATIVE_ENTRY(validate_builder_has_layers));
AddNativeCallback("ValidateBuilderHasNoLayers",
CREATE_NATIVE_ENTRY(validate_builder_has_no_layers));
AddNativeCallback("CaptureScene", CREATE_NATIVE_ENTRY(capture_scene));
AddNativeCallback("ValidateSceneHasNoLayers",
CREATE_NATIVE_ENTRY(validate_scene_has_no_layers));
std::unique_ptr<Shell> shell =
CreateShell(std::move(settings), std::move(task_runners));
ASSERT_TRUE(shell->IsSetup());
auto configuration = RunConfiguration::InferFromSettings(settings);
configuration.SetEntrypoint("validateSceneBuilderAndScene");
shell->RunEngine(std::move(configuration), [](auto result) {
ASSERT_EQ(result, Engine::RunStatus::Success);
});
message_latch->Wait();
DestroyShell(std::move(shell), std::move(task_runners));
}
TEST_F(ShellTest, EngineLayerDisposeReleasesReference) {
auto message_latch = std::make_shared<fml::AutoResetWaitableEvent>();
std::shared_ptr<ContainerLayer> root_layer;
auto capture_root_layer = [&root_layer](Dart_NativeArguments args) {
auto handle = Dart_GetNativeArgument(args, 0);
intptr_t peer = 0;
Dart_Handle result = Dart_GetNativeInstanceField(
handle, tonic::DartWrappable::kPeerIndex, &peer);
ASSERT_FALSE(Dart_IsError(result));
SceneBuilder* scene_builder = reinterpret_cast<SceneBuilder*>(peer);
ASSERT_TRUE(scene_builder);
root_layer = scene_builder->layer_stack()[0];
ASSERT_TRUE(root_layer);
};
auto validate_layer_tree_counts = [&root_layer](Dart_NativeArguments args) {
// One for the EngineLayer, one for our pointer in the test.
EXPECT_EQ(root_layer->layers().front().use_count(), 2);
};
auto validate_engine_layer_dispose =
[message_latch, &root_layer](Dart_NativeArguments args) {
// Just one for our pointer now.
EXPECT_EQ(root_layer->layers().front().use_count(), 1);
root_layer.reset();
message_latch->Signal();
};
Settings settings = CreateSettingsForFixture();
TaskRunners task_runners("test", // label
GetCurrentTaskRunner(), // platform
CreateNewThread(), // raster
CreateNewThread(), // ui
CreateNewThread() // io
);
AddNativeCallback("CaptureRootLayer",
CREATE_NATIVE_ENTRY(capture_root_layer));
AddNativeCallback("ValidateLayerTreeCounts",
CREATE_NATIVE_ENTRY(validate_layer_tree_counts));
AddNativeCallback("ValidateEngineLayerDispose",
CREATE_NATIVE_ENTRY(validate_engine_layer_dispose));
std::unique_ptr<Shell> shell =
CreateShell(std::move(settings), std::move(task_runners));
ASSERT_TRUE(shell->IsSetup());
auto configuration = RunConfiguration::InferFromSettings(settings);
configuration.SetEntrypoint("validateEngineLayerDispose");
shell->RunEngine(std::move(configuration), [](auto result) {
ASSERT_EQ(result, Engine::RunStatus::Success);
});
message_latch->Wait();
DestroyShell(std::move(shell), std::move(task_runners));
}
} // namespace testing
} // namespace flutter
// NOLINTEND(clang-analyzer-core.StackAddressEscape)