|  | // 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/runtime/dart_isolate.h" | 
|  |  | 
|  | #include <cstdlib> | 
|  | #include "flutter/fml/paths.h" | 
|  | #include "flutter/runtime/dart_plugin_registrant.h" | 
|  | #include "flutter/runtime/dart_vm.h" | 
|  | #include "flutter/runtime/dart_vm_lifecycle.h" | 
|  | #include "flutter/testing/dart_isolate_runner.h" | 
|  | #include "flutter/testing/fixture_test.h" | 
|  | #include "flutter/testing/testing.h" | 
|  |  | 
|  | // CREATE_NATIVE_ENTRY is leaky by design | 
|  | // NOLINTBEGIN(clang-analyzer-core.StackAddressEscape) | 
|  |  | 
|  | namespace flutter { | 
|  | namespace testing { | 
|  |  | 
|  | const std::string kKernelFileName = "plugin_registrant_kernel_blob.bin"; | 
|  | const std::string kElfFileName = "plugin_registrant_app_elf_snapshot.so"; | 
|  |  | 
|  | class DartIsolateTest : public FixtureTest { | 
|  | public: | 
|  | DartIsolateTest() : FixtureTest(kKernelFileName, kElfFileName, "") {} | 
|  |  | 
|  | void OverrideDartPluginRegistrant(const std::string& override_value) { | 
|  | dart_plugin_registrant_library_ = override_value; | 
|  | dart_plugin_registrant_library_override = | 
|  | dart_plugin_registrant_library_.c_str(); | 
|  | } | 
|  |  | 
|  | void SetUp() override { | 
|  | std::string source_path = GetSourcePath(); | 
|  | if (source_path[0] != '/') { | 
|  | // On windows we need an extra '/' prefix. | 
|  | source_path = "/" + source_path; | 
|  | } | 
|  | std::string registrant_uri = std::string("file://") + source_path + | 
|  | "flutter/runtime/fixtures/dart_tool/" | 
|  | "flutter_build/dart_plugin_registrant.dart"; | 
|  | OverrideDartPluginRegistrant(registrant_uri); | 
|  | } | 
|  |  | 
|  | void TearDown() override { | 
|  | dart_plugin_registrant_library_override = nullptr; | 
|  | } | 
|  |  | 
|  | std::string dart_plugin_registrant_library_; | 
|  | }; | 
|  |  | 
|  | TEST_F(DartIsolateTest, DartPluginRegistrantIsPresent) { | 
|  | ASSERT_FALSE(DartVMRef::IsInstanceRunning()); | 
|  |  | 
|  | std::vector<std::string> messages; | 
|  | fml::AutoResetWaitableEvent latch; | 
|  |  | 
|  | AddNativeCallback( | 
|  | "PassMessage", | 
|  | CREATE_NATIVE_ENTRY(([&latch, &messages](Dart_NativeArguments args) { | 
|  | auto message = tonic::DartConverter<std::string>::FromDart( | 
|  | Dart_GetNativeArgument(args, 0)); | 
|  | messages.push_back(message); | 
|  | latch.Signal(); | 
|  | }))); | 
|  |  | 
|  | auto settings = CreateSettingsForFixture(); | 
|  | auto did_throw_exception = false; | 
|  | settings.unhandled_exception_callback = [&](const std::string& error, | 
|  | const std::string& stack_trace) { | 
|  | did_throw_exception = true; | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | auto vm_ref = DartVMRef::Create(settings); | 
|  | auto thread = CreateNewThread(); | 
|  | TaskRunners task_runners(GetCurrentTestName(),  // | 
|  | thread,                // | 
|  | thread,                // | 
|  | thread,                // | 
|  | thread                 // | 
|  | ); | 
|  |  | 
|  | auto kernel_path = | 
|  | fml::paths::JoinPaths({GetFixturesPath(), kKernelFileName}); | 
|  | auto isolate = | 
|  | RunDartCodeInIsolate(vm_ref, settings, task_runners, | 
|  | "mainForPluginRegistrantTest", {}, kernel_path); | 
|  |  | 
|  | ASSERT_TRUE(isolate); | 
|  | ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); | 
|  |  | 
|  | latch.Wait(); | 
|  |  | 
|  | ASSERT_EQ(messages.size(), 1u); | 
|  | ASSERT_EQ(messages[0], "_PluginRegistrant.register() was called"); | 
|  | } | 
|  |  | 
|  | TEST_F(DartIsolateTest, DartPluginRegistrantFromBackgroundIsolate) { | 
|  | ASSERT_FALSE(DartVMRef::IsInstanceRunning()); | 
|  |  | 
|  | std::vector<std::string> messages; | 
|  | fml::AutoResetWaitableEvent latch; | 
|  |  | 
|  | AddNativeCallback( | 
|  | "PassMessage", | 
|  | CREATE_NATIVE_ENTRY(([&latch, &messages](Dart_NativeArguments args) { | 
|  | auto message = tonic::DartConverter<std::string>::FromDart( | 
|  | Dart_GetNativeArgument(args, 0)); | 
|  | messages.push_back(message); | 
|  | latch.Signal(); | 
|  | }))); | 
|  |  | 
|  | auto settings = CreateSettingsForFixture(); | 
|  | auto did_throw_exception = false; | 
|  | settings.unhandled_exception_callback = [&](const std::string& error, | 
|  | const std::string& stack_trace) { | 
|  | did_throw_exception = true; | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | auto vm_ref = DartVMRef::Create(settings); | 
|  | auto thread = CreateNewThread(); | 
|  | TaskRunners task_runners(GetCurrentTestName(),  // | 
|  | thread,                // | 
|  | thread,                // | 
|  | thread,                // | 
|  | thread                 // | 
|  | ); | 
|  |  | 
|  | auto kernel_path = | 
|  | fml::paths::JoinPaths({GetFixturesPath(), kKernelFileName}); | 
|  | auto isolate = RunDartCodeInIsolate( | 
|  | vm_ref, settings, task_runners, | 
|  | "callDartPluginRegistrantFromBackgroundIsolate", {}, kernel_path); | 
|  |  | 
|  | ASSERT_TRUE(isolate); | 
|  | ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); | 
|  |  | 
|  | latch.Wait(); | 
|  |  | 
|  | ASSERT_EQ(messages.size(), 1u); | 
|  | ASSERT_EQ(messages[0], | 
|  | "_PluginRegistrant.register() was called on background isolate"); | 
|  | } | 
|  |  | 
|  | TEST_F(DartIsolateTest, DartPluginRegistrantNotFromBackgroundIsolate) { | 
|  | ASSERT_FALSE(DartVMRef::IsInstanceRunning()); | 
|  |  | 
|  | std::vector<std::string> messages; | 
|  | fml::AutoResetWaitableEvent latch; | 
|  |  | 
|  | AddNativeCallback( | 
|  | "PassMessage", | 
|  | CREATE_NATIVE_ENTRY(([&latch, &messages](Dart_NativeArguments args) { | 
|  | auto message = tonic::DartConverter<std::string>::FromDart( | 
|  | Dart_GetNativeArgument(args, 0)); | 
|  | messages.push_back(message); | 
|  | latch.Signal(); | 
|  | }))); | 
|  |  | 
|  | auto settings = CreateSettingsForFixture(); | 
|  | auto did_throw_exception = false; | 
|  | settings.unhandled_exception_callback = [&](const std::string& error, | 
|  | const std::string& stack_trace) { | 
|  | did_throw_exception = true; | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | auto vm_ref = DartVMRef::Create(settings); | 
|  | auto thread = CreateNewThread(); | 
|  | TaskRunners task_runners(GetCurrentTestName(),  // | 
|  | thread,                // | 
|  | thread,                // | 
|  | thread,                // | 
|  | thread                 // | 
|  | ); | 
|  |  | 
|  | auto kernel_path = | 
|  | fml::paths::JoinPaths({GetFixturesPath(), kKernelFileName}); | 
|  | auto isolate = RunDartCodeInIsolate( | 
|  | vm_ref, settings, task_runners, | 
|  | "dontCallDartPluginRegistrantFromBackgroundIsolate", {}, kernel_path); | 
|  |  | 
|  | ASSERT_TRUE(isolate); | 
|  | ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); | 
|  |  | 
|  | latch.Wait(); | 
|  |  | 
|  | ASSERT_EQ(messages.size(), 1u); | 
|  | ASSERT_EQ( | 
|  | messages[0], | 
|  | "_PluginRegistrant.register() was not called on background isolate"); | 
|  | } | 
|  |  | 
|  | TEST_F(DartIsolateTest, DartPluginRegistrantWhenRegisteringBackgroundIsolate) { | 
|  | ASSERT_FALSE(DartVMRef::IsInstanceRunning()); | 
|  |  | 
|  | std::vector<std::string> messages; | 
|  | fml::AutoResetWaitableEvent latch; | 
|  |  | 
|  | AddNativeCallback( | 
|  | "PassMessage", | 
|  | CREATE_NATIVE_ENTRY(([&latch, &messages](Dart_NativeArguments args) { | 
|  | auto message = tonic::DartConverter<std::string>::FromDart( | 
|  | Dart_GetNativeArgument(args, 0)); | 
|  | messages.push_back(message); | 
|  | latch.Signal(); | 
|  | }))); | 
|  |  | 
|  | auto settings = CreateSettingsForFixture(); | 
|  | auto did_throw_exception = false; | 
|  | settings.unhandled_exception_callback = [&](const std::string& error, | 
|  | const std::string& stack_trace) { | 
|  | did_throw_exception = true; | 
|  | return true; | 
|  | }; | 
|  |  | 
|  | auto vm_ref = DartVMRef::Create(settings); | 
|  | auto thread = CreateNewThread(); | 
|  | TaskRunners task_runners(GetCurrentTestName(),  // | 
|  | thread,                // | 
|  | thread,                // | 
|  | thread,                // | 
|  | thread                 // | 
|  | ); | 
|  |  | 
|  | auto kernel_path = | 
|  | fml::paths::JoinPaths({GetFixturesPath(), kKernelFileName}); | 
|  | auto isolate = RunDartCodeInIsolate( | 
|  | vm_ref, settings, task_runners, | 
|  | "registerBackgroundIsolateCallsDartPluginRegistrant", {}, kernel_path); | 
|  |  | 
|  | ASSERT_TRUE(isolate); | 
|  | ASSERT_EQ(isolate->get()->GetPhase(), DartIsolate::Phase::Running); | 
|  |  | 
|  | latch.Wait(); | 
|  |  | 
|  | ASSERT_EQ(messages.size(), 1u); | 
|  | ASSERT_EQ(messages[0], | 
|  | "_PluginRegistrant.register() was called on background isolate"); | 
|  | } | 
|  |  | 
|  | }  // namespace testing | 
|  | }  // namespace flutter | 
|  |  | 
|  | // NOLINTEND(clang-analyzer-core.StackAddressEscape) |