From 8dd4845f706eea4357b1ea95c12171c885d8fd64 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Sat, 14 Jan 2023 13:10:07 -0500 Subject: [PATCH 1/8] Initial definition matching current API --- .../lib/src/messages.g.dart | 72 ++++++++++++ .../pigeons/copyright.txt | 3 + .../pigeons/messages.dart | 19 ++++ .../url_launcher_windows/pubspec.yaml | 1 + .../url_launcher_windows/test/test_api.g.dart | 62 +++++++++++ .../windows/message.cpp.out | 103 ++++++++++++++++++ .../windows/messages.h.out | 82 ++++++++++++++ 7 files changed, 342 insertions(+) create mode 100644 packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart create mode 100644 packages/url_launcher/url_launcher_windows/pigeons/copyright.txt create mode 100644 packages/url_launcher/url_launcher_windows/pigeons/messages.dart create mode 100644 packages/url_launcher/url_launcher_windows/test/test_api.g.dart create mode 100644 packages/url_launcher/url_launcher_windows/windows/message.cpp.out create mode 100644 packages/url_launcher/url_launcher_windows/windows/messages.h.out diff --git a/packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart b/packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart new file mode 100644 index 000000000000..9a4835dfce6a --- /dev/null +++ b/packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart @@ -0,0 +1,72 @@ +// 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. +// Autogenerated from Pigeon (v5.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, prefer_null_aware_operators, omit_local_variable_types, unused_shown_name, unnecessary_import +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; + +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; + + +class UrlLauncherApi { + /// Constructor for [UrlLauncherApi]. The [binaryMessenger] named argument is + /// available for dependency injection. If it is left null, the default + /// BinaryMessenger will be used which routes to the host platform. + UrlLauncherApi({BinaryMessenger? binaryMessenger}) + : _binaryMessenger = binaryMessenger; + final BinaryMessenger? _binaryMessenger; + + static const MessageCodec codec = StandardMessageCodec(); + + Future canLaunchUrl(String arg_url) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_url]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else if (replyList[0] == null) { + throw PlatformException( + code: 'null-error', + message: 'Host platform returned null value for non-null return value.', + ); + } else { + return (replyList[0] as bool?)!; + } + } + + Future launchUrl(String arg_url) async { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UrlLauncherApi.launchUrl', codec, + binaryMessenger: _binaryMessenger); + final List? replyList = + await channel.send([arg_url]) as List?; + if (replyList == null) { + throw PlatformException( + code: 'channel-error', + message: 'Unable to establish connection on channel.', + ); + } else if (replyList.length > 1) { + throw PlatformException( + code: replyList[0]! as String, + message: replyList[1] as String?, + details: replyList[2], + ); + } else { + return; + } + } +} diff --git a/packages/url_launcher/url_launcher_windows/pigeons/copyright.txt b/packages/url_launcher/url_launcher_windows/pigeons/copyright.txt new file mode 100644 index 000000000000..1236b63caf3a --- /dev/null +++ b/packages/url_launcher/url_launcher_windows/pigeons/copyright.txt @@ -0,0 +1,3 @@ +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. diff --git a/packages/url_launcher/url_launcher_windows/pigeons/messages.dart b/packages/url_launcher/url_launcher_windows/pigeons/messages.dart new file mode 100644 index 000000000000..4297d8baae64 --- /dev/null +++ b/packages/url_launcher/url_launcher_windows/pigeons/messages.dart @@ -0,0 +1,19 @@ +// 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. + +import 'package:pigeon/pigeon.dart'; + +@ConfigurePigeon(PigeonOptions( + dartOut: 'lib/src/messages.g.dart', + dartTestOut: 'test/test_api.g.dart', + cppOptions: CppOptions(namespace: 'url_launcher_windows'), + cppHeaderOut: 'windows/messages.g.h', + cppSourceOut: 'windows/messages.g.cpp', + copyrightHeader: 'pigeons/copyright.txt', +)) +@HostApi(dartHostTestHandler: 'TestUrlLauncherApi') +abstract class UrlLauncherApi { + bool canLaunchUrl(String url); + void launchUrl(String url); +} diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index b35b62e1d82e..1a18dc894ce4 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -24,4 +24,5 @@ dependencies: dev_dependencies: flutter_test: sdk: flutter + pigeon: ^5.0.1 test: ^1.16.3 diff --git a/packages/url_launcher/url_launcher_windows/test/test_api.g.dart b/packages/url_launcher/url_launcher_windows/test/test_api.g.dart new file mode 100644 index 000000000000..645a27142637 --- /dev/null +++ b/packages/url_launcher/url_launcher_windows/test/test_api.g.dart @@ -0,0 +1,62 @@ +// 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. +// Autogenerated from Pigeon (v5.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon +// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import +// ignore_for_file: avoid_relative_lib_imports +import 'dart:async'; +import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; +import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; +import 'package:flutter/services.dart'; +import 'package:flutter_test/flutter_test.dart'; + +import 'package:url_launcher_windows/src/messages.g.dart'; + + +abstract class TestUrlLauncherApi { + static const MessageCodec codec = StandardMessageCodec(); + + bool canLaunchUrl(String url); + + void launchUrl(String url); + + static void setup(TestUrlLauncherApi? api, {BinaryMessenger? binaryMessenger}) { + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl was null.'); + final List args = (message as List?)!; + final String? arg_url = (args[0] as String?); + assert(arg_url != null, 'Argument for dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl was null, expected non-null String.'); + final bool output = api.canLaunchUrl(arg_url!); + return [output]; + }); + } + } + { + final BasicMessageChannel channel = BasicMessageChannel( + 'dev.flutter.pigeon.UrlLauncherApi.launchUrl', codec, + binaryMessenger: binaryMessenger); + if (api == null) { + channel.setMockMessageHandler(null); + } else { + channel.setMockMessageHandler((Object? message) async { + assert(message != null, + 'Argument for dev.flutter.pigeon.UrlLauncherApi.launchUrl was null.'); + final List args = (message as List?)!; + final String? arg_url = (args[0] as String?); + assert(arg_url != null, 'Argument for dev.flutter.pigeon.UrlLauncherApi.launchUrl was null, expected non-null String.'); + api.launchUrl(arg_url!); + return []; + }); + } + } + } +} diff --git a/packages/url_launcher/url_launcher_windows/windows/message.cpp.out b/packages/url_launcher/url_launcher_windows/windows/message.cpp.out new file mode 100644 index 000000000000..13771dc61194 --- /dev/null +++ b/packages/url_launcher/url_launcher_windows/windows/message.cpp.out @@ -0,0 +1,103 @@ +// 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. +// Autogenerated from Pigeon (v5.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#undef _HAS_EXCEPTIONS + +#include "messages.h.out" + +#include +#include +#include +#include + +#include +#include +#include + +/// The codec used by UrlLauncherApi. +const flutter::StandardMessageCodec& UrlLauncherApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance(&flutter::StandardCodecSerializer::GetInstance()); +} + +// Sets up an instance of `UrlLauncherApi` to handle messages through the `binary_messenger`. +void UrlLauncherApi::SetUp(flutter::BinaryMessenger* binary_messenger, UrlLauncherApi* api) { + { + auto channel = std::make_unique>( + binary_messenger, "dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const flutter::EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_url_arg = args.at(0); + if (encodable_url_arg.IsNull()) { + reply(WrapError("url_arg unexpectedly null.")); + return; + } + const auto& url_arg = std::get(encodable_url_arg); + ErrorOr output = api->CanLaunchUrl(url_arg); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + flutter::EncodableList wrapped; + wrapped.push_back(flutter::EncodableValue(std::move(output).TakeValue())); + reply(flutter::EncodableValue(std::move(wrapped))); + } + catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = std::make_unique>( + binary_messenger, "dev.flutter.pigeon.UrlLauncherApi.launchUrl", &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler([api](const flutter::EncodableValue& message, const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_url_arg = args.at(0); + if (encodable_url_arg.IsNull()) { + reply(WrapError("url_arg unexpectedly null.")); + return; + } + const auto& url_arg = std::get(encodable_url_arg); + std::optional output = api->LaunchUrl(url_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + flutter::EncodableList wrapped; + wrapped.push_back(flutter::EncodableValue()); + reply(flutter::EncodableValue(std::move(wrapped))); + } + catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } +} + +flutter::EncodableValue UrlLauncherApi::WrapError(std::string_view error_message) { + return flutter::EncodableValue(flutter::EncodableList{ + flutter::EncodableValue(std::string(error_message)), + flutter::EncodableValue("Error"), + flutter::EncodableValue() + }); +} +flutter::EncodableValue UrlLauncherApi::WrapError(const FlutterError& error) { + return flutter::EncodableValue(flutter::EncodableList{ + flutter::EncodableValue(error.message()), + flutter::EncodableValue(error.code()), + error.details() + }); +} + diff --git a/packages/url_launcher/url_launcher_windows/windows/messages.h.out b/packages/url_launcher/url_launcher_windows/windows/messages.h.out new file mode 100644 index 000000000000..321e2e8baac7 --- /dev/null +++ b/packages/url_launcher/url_launcher_windows/windows/messages.h.out @@ -0,0 +1,82 @@ +// 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. +// Autogenerated from Pigeon (v5.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#ifndef PIGEON_H_ +#define PIGEON_H_ +#include +#include +#include +#include + +#include +#include +#include + + +// Generated class from Pigeon. + +class FlutterError { + public: + explicit FlutterError(const std::string& code) + : code_(code) {} + explicit FlutterError(const std::string& code, const std::string& message) + : code_(code), message_(message) {} + explicit FlutterError(const std::string& code, const std::string& message, const flutter::EncodableValue& details) + : code_(code), message_(message), details_(details) {} + + const std::string& code() const { return code_; } + const std::string& message() const { return message_; } + const flutter::EncodableValue& details() const { return details_; } + + private: + std::string code_; + std::string message_; + flutter::EncodableValue details_; +}; + +template class ErrorOr { + public: + ErrorOr(const T& rhs) { new(&v_) T(rhs); } + ErrorOr(const T&& rhs) { v_ = std::move(rhs); } + ErrorOr(const FlutterError& rhs) { + new(&v_) FlutterError(rhs); + } + ErrorOr(const FlutterError&& rhs) { v_ = std::move(rhs); } + + bool has_error() const { return std::holds_alternative(v_); } + const T& value() const { return std::get(v_); }; + const FlutterError& error() const { return std::get(v_); }; + + private: + friend class UrlLauncherApi; + ErrorOr() = default; + T TakeValue() && { return std::get(std::move(v_)); } + + std::variant v_; +}; + + +// Generated interface from Pigeon that represents a handler of messages from Flutter. +class UrlLauncherApi { + public: + UrlLauncherApi(const UrlLauncherApi&) = delete; + UrlLauncherApi& operator=(const UrlLauncherApi&) = delete; + virtual ~UrlLauncherApi() { }; + virtual ErrorOr CanLaunchUrl(const std::string& url) = 0; + virtual std::optional LaunchUrl(const std::string& url) = 0; + + // The codec used by UrlLauncherApi. + static const flutter::StandardMessageCodec& GetCodec(); + // Sets up an instance of `UrlLauncherApi` to handle messages through the `binary_messenger`. + static void SetUp(flutter::BinaryMessenger* binary_messenger, UrlLauncherApi* api); + static flutter::EncodableValue WrapError(std::string_view error_message); + static flutter::EncodableValue WrapError(const FlutterError& error); + + protected: + UrlLauncherApi() = default; + +}; +#endif // PIGEON_H_ From 66233b6ca3c7492aab9c1b123fa180f49694f1b4 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 17 Jan 2023 16:22:54 -0500 Subject: [PATCH 2/8] Rename, autoformat --- .../windows/message.cpp.out | 103 ----------------- .../windows/messages.g.cpp | 109 ++++++++++++++++++ .../windows/{messages.h.out => messages.g.h} | 33 +++--- 3 files changed, 125 insertions(+), 120 deletions(-) delete mode 100644 packages/url_launcher/url_launcher_windows/windows/message.cpp.out create mode 100644 packages/url_launcher/url_launcher_windows/windows/messages.g.cpp rename packages/url_launcher/url_launcher_windows/windows/{messages.h.out => messages.g.h} (78%) diff --git a/packages/url_launcher/url_launcher_windows/windows/message.cpp.out b/packages/url_launcher/url_launcher_windows/windows/message.cpp.out deleted file mode 100644 index 13771dc61194..000000000000 --- a/packages/url_launcher/url_launcher_windows/windows/message.cpp.out +++ /dev/null @@ -1,103 +0,0 @@ -// 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. -// Autogenerated from Pigeon (v5.0.1), do not edit directly. -// See also: https://pub.dev/packages/pigeon - -#undef _HAS_EXCEPTIONS - -#include "messages.h.out" - -#include -#include -#include -#include - -#include -#include -#include - -/// The codec used by UrlLauncherApi. -const flutter::StandardMessageCodec& UrlLauncherApi::GetCodec() { - return flutter::StandardMessageCodec::GetInstance(&flutter::StandardCodecSerializer::GetInstance()); -} - -// Sets up an instance of `UrlLauncherApi` to handle messages through the `binary_messenger`. -void UrlLauncherApi::SetUp(flutter::BinaryMessenger* binary_messenger, UrlLauncherApi* api) { - { - auto channel = std::make_unique>( - binary_messenger, "dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl", &GetCodec()); - if (api != nullptr) { - channel->SetMessageHandler([api](const flutter::EncodableValue& message, const flutter::MessageReply& reply) { - try { - const auto& args = std::get(message); - const auto& encodable_url_arg = args.at(0); - if (encodable_url_arg.IsNull()) { - reply(WrapError("url_arg unexpectedly null.")); - return; - } - const auto& url_arg = std::get(encodable_url_arg); - ErrorOr output = api->CanLaunchUrl(url_arg); - if (output.has_error()) { - reply(WrapError(output.error())); - return; - } - flutter::EncodableList wrapped; - wrapped.push_back(flutter::EncodableValue(std::move(output).TakeValue())); - reply(flutter::EncodableValue(std::move(wrapped))); - } - catch (const std::exception& exception) { - reply(WrapError(exception.what())); - } - }); - } else { - channel->SetMessageHandler(nullptr); - } - } - { - auto channel = std::make_unique>( - binary_messenger, "dev.flutter.pigeon.UrlLauncherApi.launchUrl", &GetCodec()); - if (api != nullptr) { - channel->SetMessageHandler([api](const flutter::EncodableValue& message, const flutter::MessageReply& reply) { - try { - const auto& args = std::get(message); - const auto& encodable_url_arg = args.at(0); - if (encodable_url_arg.IsNull()) { - reply(WrapError("url_arg unexpectedly null.")); - return; - } - const auto& url_arg = std::get(encodable_url_arg); - std::optional output = api->LaunchUrl(url_arg); - if (output.has_value()) { - reply(WrapError(output.value())); - return; - } - flutter::EncodableList wrapped; - wrapped.push_back(flutter::EncodableValue()); - reply(flutter::EncodableValue(std::move(wrapped))); - } - catch (const std::exception& exception) { - reply(WrapError(exception.what())); - } - }); - } else { - channel->SetMessageHandler(nullptr); - } - } -} - -flutter::EncodableValue UrlLauncherApi::WrapError(std::string_view error_message) { - return flutter::EncodableValue(flutter::EncodableList{ - flutter::EncodableValue(std::string(error_message)), - flutter::EncodableValue("Error"), - flutter::EncodableValue() - }); -} -flutter::EncodableValue UrlLauncherApi::WrapError(const FlutterError& error) { - return flutter::EncodableValue(flutter::EncodableList{ - flutter::EncodableValue(error.message()), - flutter::EncodableValue(error.code()), - error.details() - }); -} - diff --git a/packages/url_launcher/url_launcher_windows/windows/messages.g.cpp b/packages/url_launcher/url_launcher_windows/windows/messages.g.cpp new file mode 100644 index 000000000000..d48041d3dedf --- /dev/null +++ b/packages/url_launcher/url_launcher_windows/windows/messages.g.cpp @@ -0,0 +1,109 @@ +// 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. +// Autogenerated from Pigeon (v5.0.1), do not edit directly. +// See also: https://pub.dev/packages/pigeon + +#undef _HAS_EXCEPTIONS + +#include "messages.g.h" + +#include +#include +#include +#include + +#include +#include +#include + +/// The codec used by UrlLauncherApi. +const flutter::StandardMessageCodec& UrlLauncherApi::GetCodec() { + return flutter::StandardMessageCodec::GetInstance( + &flutter::StandardCodecSerializer::GetInstance()); +} + +// Sets up an instance of `UrlLauncherApi` to handle messages through the +// `binary_messenger`. +void UrlLauncherApi::SetUp(flutter::BinaryMessenger* binary_messenger, + UrlLauncherApi* api) { + { + auto channel = + std::make_unique>( + binary_messenger, "dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl", + &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler( + [api](const flutter::EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_url_arg = args.at(0); + if (encodable_url_arg.IsNull()) { + reply(WrapError("url_arg unexpectedly null.")); + return; + } + const auto& url_arg = std::get(encodable_url_arg); + ErrorOr output = api->CanLaunchUrl(url_arg); + if (output.has_error()) { + reply(WrapError(output.error())); + return; + } + flutter::EncodableList wrapped; + wrapped.push_back( + flutter::EncodableValue(std::move(output).TakeValue())); + reply(flutter::EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } + { + auto channel = + std::make_unique>( + binary_messenger, "dev.flutter.pigeon.UrlLauncherApi.launchUrl", + &GetCodec()); + if (api != nullptr) { + channel->SetMessageHandler( + [api](const flutter::EncodableValue& message, + const flutter::MessageReply& reply) { + try { + const auto& args = std::get(message); + const auto& encodable_url_arg = args.at(0); + if (encodable_url_arg.IsNull()) { + reply(WrapError("url_arg unexpectedly null.")); + return; + } + const auto& url_arg = std::get(encodable_url_arg); + std::optional output = api->LaunchUrl(url_arg); + if (output.has_value()) { + reply(WrapError(output.value())); + return; + } + flutter::EncodableList wrapped; + wrapped.push_back(flutter::EncodableValue()); + reply(flutter::EncodableValue(std::move(wrapped))); + } catch (const std::exception& exception) { + reply(WrapError(exception.what())); + } + }); + } else { + channel->SetMessageHandler(nullptr); + } + } +} + +flutter::EncodableValue UrlLauncherApi::WrapError( + std::string_view error_message) { + return flutter::EncodableValue(flutter::EncodableList{ + flutter::EncodableValue(std::string(error_message)), + flutter::EncodableValue("Error"), flutter::EncodableValue()}); +} +flutter::EncodableValue UrlLauncherApi::WrapError(const FlutterError& error) { + return flutter::EncodableValue(flutter::EncodableList{ + flutter::EncodableValue(error.message()), + flutter::EncodableValue(error.code()), error.details()}); +} diff --git a/packages/url_launcher/url_launcher_windows/windows/messages.h.out b/packages/url_launcher/url_launcher_windows/windows/messages.g.h similarity index 78% rename from packages/url_launcher/url_launcher_windows/windows/messages.h.out rename to packages/url_launcher/url_launcher_windows/windows/messages.g.h index 321e2e8baac7..5aa492f7140b 100644 --- a/packages/url_launcher/url_launcher_windows/windows/messages.h.out +++ b/packages/url_launcher/url_launcher_windows/windows/messages.g.h @@ -15,17 +15,16 @@ #include #include - // Generated class from Pigeon. class FlutterError { public: - explicit FlutterError(const std::string& code) - : code_(code) {} + explicit FlutterError(const std::string& code) : code_(code) {} explicit FlutterError(const std::string& code, const std::string& message) - : code_(code), message_(message) {} - explicit FlutterError(const std::string& code, const std::string& message, const flutter::EncodableValue& details) - : code_(code), message_(message), details_(details) {} + : code_(code), message_(message) {} + explicit FlutterError(const std::string& code, const std::string& message, + const flutter::EncodableValue& details) + : code_(code), message_(message), details_(details) {} const std::string& code() const { return code_; } const std::string& message() const { return message_; } @@ -37,13 +36,12 @@ class FlutterError { flutter::EncodableValue details_; }; -template class ErrorOr { +template +class ErrorOr { public: - ErrorOr(const T& rhs) { new(&v_) T(rhs); } + ErrorOr(const T& rhs) { new (&v_) T(rhs); } ErrorOr(const T&& rhs) { v_ = std::move(rhs); } - ErrorOr(const FlutterError& rhs) { - new(&v_) FlutterError(rhs); - } + ErrorOr(const FlutterError& rhs) { new (&v_) FlutterError(rhs); } ErrorOr(const FlutterError&& rhs) { v_ = std::move(rhs); } bool has_error() const { return std::holds_alternative(v_); } @@ -58,25 +56,26 @@ template class ErrorOr { std::variant v_; }; - -// Generated interface from Pigeon that represents a handler of messages from Flutter. +// Generated interface from Pigeon that represents a handler of messages from +// Flutter. class UrlLauncherApi { public: UrlLauncherApi(const UrlLauncherApi&) = delete; UrlLauncherApi& operator=(const UrlLauncherApi&) = delete; - virtual ~UrlLauncherApi() { }; + virtual ~UrlLauncherApi(){}; virtual ErrorOr CanLaunchUrl(const std::string& url) = 0; virtual std::optional LaunchUrl(const std::string& url) = 0; // The codec used by UrlLauncherApi. static const flutter::StandardMessageCodec& GetCodec(); - // Sets up an instance of `UrlLauncherApi` to handle messages through the `binary_messenger`. - static void SetUp(flutter::BinaryMessenger* binary_messenger, UrlLauncherApi* api); + // Sets up an instance of `UrlLauncherApi` to handle messages through the + // `binary_messenger`. + static void SetUp(flutter::BinaryMessenger* binary_messenger, + UrlLauncherApi* api); static flutter::EncodableValue WrapError(std::string_view error_message); static flutter::EncodableValue WrapError(const FlutterError& error); protected: UrlLauncherApi() = default; - }; #endif // PIGEON_H_ From f40164dc745e593848105e01fb865a0e601ba0d9 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 17 Jan 2023 19:56:59 -0500 Subject: [PATCH 3/8] Update native implementation and unit tests --- .../windows/CMakeLists.txt | 2 + .../windows/messages.g.cpp | 4 + .../url_launcher_windows/windows/messages.g.h | 5 ++ .../windows/system_apis.cpp | 4 +- .../windows/system_apis.h | 4 +- .../test/url_launcher_windows_test.cpp | 82 +++++-------------- .../windows/url_launcher_plugin.cpp | 53 ++---------- .../windows/url_launcher_plugin.h | 19 ++--- .../windows/url_launcher_windows.cpp | 2 +- 9 files changed, 53 insertions(+), 122 deletions(-) diff --git a/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt b/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt index a4185acff6a1..a34bcb3d35da 100644 --- a/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt +++ b/packages/url_launcher/url_launcher_windows/windows/CMakeLists.txt @@ -5,6 +5,8 @@ project(${PROJECT_NAME} LANGUAGES CXX) set(PLUGIN_NAME "${PROJECT_NAME}_plugin") list(APPEND PLUGIN_SOURCES + "messages.g.cpp" + "messages.g.h" "system_apis.cpp" "system_apis.h" "url_launcher_plugin.cpp" diff --git a/packages/url_launcher/url_launcher_windows/windows/messages.g.cpp b/packages/url_launcher/url_launcher_windows/windows/messages.g.cpp index d48041d3dedf..eb1cf792931f 100644 --- a/packages/url_launcher/url_launcher_windows/windows/messages.g.cpp +++ b/packages/url_launcher/url_launcher_windows/windows/messages.g.cpp @@ -17,6 +17,8 @@ #include #include +namespace url_launcher_windows { + /// The codec used by UrlLauncherApi. const flutter::StandardMessageCodec& UrlLauncherApi::GetCodec() { return flutter::StandardMessageCodec::GetInstance( @@ -107,3 +109,5 @@ flutter::EncodableValue UrlLauncherApi::WrapError(const FlutterError& error) { flutter::EncodableValue(error.message()), flutter::EncodableValue(error.code()), error.details()}); } + +} // namespace url_launcher_windows diff --git a/packages/url_launcher/url_launcher_windows/windows/messages.g.h b/packages/url_launcher/url_launcher_windows/windows/messages.g.h index 5aa492f7140b..cb8e95f8d065 100644 --- a/packages/url_launcher/url_launcher_windows/windows/messages.g.h +++ b/packages/url_launcher/url_launcher_windows/windows/messages.g.h @@ -15,6 +15,8 @@ #include #include +namespace url_launcher_windows { + // Generated class from Pigeon. class FlutterError { @@ -78,4 +80,7 @@ class UrlLauncherApi { protected: UrlLauncherApi() = default; }; + +} // namespace url_launcher_windows + #endif // PIGEON_H_ diff --git a/packages/url_launcher/url_launcher_windows/windows/system_apis.cpp b/packages/url_launcher/url_launcher_windows/windows/system_apis.cpp index abd690b6e47f..cde95ee1b399 100644 --- a/packages/url_launcher/url_launcher_windows/windows/system_apis.cpp +++ b/packages/url_launcher/url_launcher_windows/windows/system_apis.cpp @@ -5,7 +5,7 @@ #include -namespace url_launcher_plugin { +namespace url_launcher_windows { SystemApis::SystemApis() {} @@ -35,4 +35,4 @@ HINSTANCE SystemApisImpl::ShellExecuteW(HWND hwnd, LPCWSTR operation, show_flags); } -} // namespace url_launcher_plugin +} // namespace url_launcher_windows diff --git a/packages/url_launcher/url_launcher_windows/windows/system_apis.h b/packages/url_launcher/url_launcher_windows/windows/system_apis.h index 7b56704d8e04..c56c4100180b 100644 --- a/packages/url_launcher/url_launcher_windows/windows/system_apis.h +++ b/packages/url_launcher/url_launcher_windows/windows/system_apis.h @@ -3,7 +3,7 @@ // found in the LICENSE file. #include -namespace url_launcher_plugin { +namespace url_launcher_windows { // An interface wrapping system APIs used by the plugin, for mocking. class SystemApis { @@ -53,4 +53,4 @@ class SystemApisImpl : public SystemApis { int show_flags); }; -} // namespace url_launcher_plugin +} // namespace url_launcher_windows diff --git a/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp b/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp index 191d51a0caa8..9dd2be5347b5 100644 --- a/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp +++ b/packages/url_launcher/url_launcher_windows/windows/test/url_launcher_windows_test.cpp @@ -9,11 +9,13 @@ #include #include +#include #include +#include "messages.g.h" #include "url_launcher_plugin.h" -namespace url_launcher_plugin { +namespace url_launcher_windows { namespace test { namespace { @@ -42,30 +44,10 @@ class MockSystemApis : public SystemApis { (override)); }; -class MockMethodResult : public flutter::MethodResult<> { - public: - MOCK_METHOD(void, SuccessInternal, (const EncodableValue* result), - (override)); - MOCK_METHOD(void, ErrorInternal, - (const std::string& error_code, const std::string& error_message, - const EncodableValue* details), - (override)); - MOCK_METHOD(void, NotImplementedInternal, (), (override)); -}; - -std::unique_ptr CreateArgumentsWithUrl(const std::string& url) { - EncodableMap args = { - {EncodableValue("url"), EncodableValue(url)}, - }; - return std::make_unique(args); -} - } // namespace TEST(UrlLauncherPlugin, CanLaunchSuccessTrue) { std::unique_ptr system = std::make_unique(); - std::unique_ptr result = - std::make_unique(); // Return success values from the registery commands. HKEY fake_key = reinterpret_cast(1); @@ -73,20 +55,16 @@ TEST(UrlLauncherPlugin, CanLaunchSuccessTrue) { .WillOnce(DoAll(SetArgPointee<4>(fake_key), Return(ERROR_SUCCESS))); EXPECT_CALL(*system, RegQueryValueExW).WillOnce(Return(ERROR_SUCCESS)); EXPECT_CALL(*system, RegCloseKey(fake_key)).WillOnce(Return(ERROR_SUCCESS)); - // Expect a success response. - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(true)))); UrlLauncherPlugin plugin(std::move(system)); - plugin.HandleMethodCall( - flutter::MethodCall("canLaunch", - CreateArgumentsWithUrl("https://some.url.com")), - std::move(result)); + ErrorOr result = plugin.CanLaunchUrl("https://some.url.com"); + + ASSERT_FALSE(result.has_error()); + EXPECT_TRUE(result.value()); } TEST(UrlLauncherPlugin, CanLaunchQueryFailure) { std::unique_ptr system = std::make_unique(); - std::unique_ptr result = - std::make_unique(); // Return success values from the registery commands, except for the query, // to simulate a scheme that is in the registry, but has no URL handler. @@ -95,68 +73,52 @@ TEST(UrlLauncherPlugin, CanLaunchQueryFailure) { .WillOnce(DoAll(SetArgPointee<4>(fake_key), Return(ERROR_SUCCESS))); EXPECT_CALL(*system, RegQueryValueExW).WillOnce(Return(ERROR_FILE_NOT_FOUND)); EXPECT_CALL(*system, RegCloseKey(fake_key)).WillOnce(Return(ERROR_SUCCESS)); - // Expect a success response. - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(false)))); UrlLauncherPlugin plugin(std::move(system)); - plugin.HandleMethodCall( - flutter::MethodCall("canLaunch", - CreateArgumentsWithUrl("https://some.url.com")), - std::move(result)); + ErrorOr result = plugin.CanLaunchUrl("https://some.url.com"); + + ASSERT_FALSE(result.has_error()); + EXPECT_FALSE(result.value()); } TEST(UrlLauncherPlugin, CanLaunchHandlesOpenFailure) { std::unique_ptr system = std::make_unique(); - std::unique_ptr result = - std::make_unique(); // Return failure for opening. EXPECT_CALL(*system, RegOpenKeyExW).WillOnce(Return(ERROR_BAD_PATHNAME)); - // Expect a success response. - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(false)))); UrlLauncherPlugin plugin(std::move(system)); - plugin.HandleMethodCall( - flutter::MethodCall("canLaunch", - CreateArgumentsWithUrl("https://some.url.com")), - std::move(result)); + ErrorOr result = plugin.CanLaunchUrl("https://some.url.com"); + + ASSERT_FALSE(result.has_error()); + EXPECT_FALSE(result.value()); } TEST(UrlLauncherPlugin, LaunchSuccess) { std::unique_ptr system = std::make_unique(); - std::unique_ptr result = - std::make_unique(); // Return a success value (>32) from launching. EXPECT_CALL(*system, ShellExecuteW) .WillOnce(Return(reinterpret_cast(33))); - // Expect a success response. - EXPECT_CALL(*result, SuccessInternal(Pointee(EncodableValue(true)))); UrlLauncherPlugin plugin(std::move(system)); - plugin.HandleMethodCall( - flutter::MethodCall("launch", - CreateArgumentsWithUrl("https://some.url.com")), - std::move(result)); + std::optional error = plugin.LaunchUrl("https://some.url.com"); + + EXPECT_FALSE(error.has_value()); } TEST(UrlLauncherPlugin, LaunchReportsFailure) { std::unique_ptr system = std::make_unique(); - std::unique_ptr result = - std::make_unique(); // Return a faile value (<=32) from launching. EXPECT_CALL(*system, ShellExecuteW) .WillOnce(Return(reinterpret_cast(32))); - // Expect an error response. - EXPECT_CALL(*result, ErrorInternal); UrlLauncherPlugin plugin(std::move(system)); - plugin.HandleMethodCall( - flutter::MethodCall("launch", - CreateArgumentsWithUrl("https://some.url.com")), - std::move(result)); + std::optional error = plugin.LaunchUrl("https://some.url.com"); + + EXPECT_TRUE(error.has_value()); } } // namespace test -} // namespace url_launcher_plugin +} // namespace url_launcher_windows diff --git a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp index d5f201219c75..1dfee16c4445 100644 --- a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp +++ b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.cpp @@ -13,7 +13,9 @@ #include #include -namespace url_launcher_plugin { +#include "messages.g.h" + +namespace url_launcher_windows { namespace { @@ -62,18 +64,9 @@ std::string GetUrlArgument(const flutter::MethodCall<>& method_call) { // static void UrlLauncherPlugin::RegisterWithRegistrar( flutter::PluginRegistrar* registrar) { - auto channel = std::make_unique>( - registrar->messenger(), "plugins.flutter.io/url_launcher_windows", - &flutter::StandardMethodCodec::GetInstance()); - std::unique_ptr plugin = std::make_unique(); - - channel->SetMethodCallHandler( - [plugin_pointer = plugin.get()](const auto& call, auto result) { - plugin_pointer->HandleMethodCall(call, std::move(result)); - }); - + UrlLauncherApi::SetUp(registrar->messenger(), plugin.get()); registrar->AddPlugin(std::move(plugin)); } @@ -85,37 +78,7 @@ UrlLauncherPlugin::UrlLauncherPlugin(std::unique_ptr system_apis) UrlLauncherPlugin::~UrlLauncherPlugin() = default; -void UrlLauncherPlugin::HandleMethodCall( - const flutter::MethodCall<>& method_call, - std::unique_ptr> result) { - if (method_call.method_name().compare("launch") == 0) { - std::string url = GetUrlArgument(method_call); - if (url.empty()) { - result->Error("argument_error", "No URL provided"); - return; - } - - std::optional error = LaunchUrl(url); - if (error) { - result->Error("open_error", error.value()); - return; - } - result->Success(EncodableValue(true)); - } else if (method_call.method_name().compare("canLaunch") == 0) { - std::string url = GetUrlArgument(method_call); - if (url.empty()) { - result->Error("argument_error", "No URL provided"); - return; - } - - bool can_launch = CanLaunchUrl(url); - result->Success(EncodableValue(can_launch)); - } else { - result->NotImplemented(); - } -} - -bool UrlLauncherPlugin::CanLaunchUrl(const std::string& url) { +ErrorOr UrlLauncherPlugin::CanLaunchUrl(const std::string& url) { size_t separator_location = url.find(":"); if (separator_location == std::string::npos) { return false; @@ -134,7 +97,7 @@ bool UrlLauncherPlugin::CanLaunchUrl(const std::string& url) { return has_handler; } -std::optional UrlLauncherPlugin::LaunchUrl( +std::optional UrlLauncherPlugin::LaunchUrl( const std::string& url) { std::wstring url_wide = Utf16FromUtf8(url); @@ -147,9 +110,9 @@ std::optional UrlLauncherPlugin::LaunchUrl( std::ostringstream error_message; error_message << "Failed to open " << url << ": ShellExecute error code " << status; - return std::optional(error_message.str()); + return FlutterError("open_error", error_message.str()); } return std::nullopt; } -} // namespace url_launcher_plugin +} // namespace url_launcher_windows diff --git a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.h b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.h index 45e70e5fc067..e51cde67ab79 100644 --- a/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.h +++ b/packages/url_launcher/url_launcher_windows/windows/url_launcher_plugin.h @@ -10,11 +10,12 @@ #include #include +#include "messages.g.h" #include "system_apis.h" -namespace url_launcher_plugin { +namespace url_launcher_windows { -class UrlLauncherPlugin : public flutter::Plugin { +class UrlLauncherPlugin : public flutter::Plugin, public UrlLauncherApi { public: static void RegisterWithRegistrar(flutter::PluginRegistrar* registrar); @@ -31,18 +32,12 @@ class UrlLauncherPlugin : public flutter::Plugin { UrlLauncherPlugin(const UrlLauncherPlugin&) = delete; UrlLauncherPlugin& operator=(const UrlLauncherPlugin&) = delete; - // Called when a method is called on the plugin channel. - void HandleMethodCall(const flutter::MethodCall<>& method_call, - std::unique_ptr> result); + // UrlLauncherApi: + ErrorOr CanLaunchUrl(const std::string& url) override; + std::optional LaunchUrl(const std::string& url) override; private: - // Returns whether or not the given URL has a registered handler. - bool CanLaunchUrl(const std::string& url); - - // Attempts to launch the given URL. On failure, returns an error string. - std::optional LaunchUrl(const std::string& url); - std::unique_ptr system_apis_; }; -} // namespace url_launcher_plugin +} // namespace url_launcher_windows diff --git a/packages/url_launcher/url_launcher_windows/windows/url_launcher_windows.cpp b/packages/url_launcher/url_launcher_windows/windows/url_launcher_windows.cpp index 05de586d8fe0..726709386fa6 100644 --- a/packages/url_launcher/url_launcher_windows/windows/url_launcher_windows.cpp +++ b/packages/url_launcher/url_launcher_windows/windows/url_launcher_windows.cpp @@ -9,7 +9,7 @@ void UrlLauncherWindowsRegisterWithRegistrar( FlutterDesktopPluginRegistrarRef registrar) { - url_launcher_plugin::UrlLauncherPlugin::RegisterWithRegistrar( + url_launcher_windows::UrlLauncherPlugin::RegisterWithRegistrar( flutter::PluginRegistrarManager::GetInstance() ->GetRegistrar(registrar)); } From 938ac6a604a83d625c4237b54820ef0fc86055dd Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 19 Jan 2023 10:39:20 -0500 Subject: [PATCH 4/8] Update Dart; remove unnecessary Pigeon test API --- .../lib/url_launcher_windows.dart | 36 ++-- .../pigeons/messages.dart | 1 - .../url_launcher_windows/test/test_api.g.dart | 62 ------ .../test/url_launcher_windows_test.dart | 196 +++++++----------- 4 files changed, 94 insertions(+), 201 deletions(-) delete mode 100644 packages/url_launcher/url_launcher_windows/test/test_api.g.dart diff --git a/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart b/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart index b0ee8cb1a0b4..20c04b6cd948 100644 --- a/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart +++ b/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart @@ -2,31 +2,34 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. -import 'dart:async'; - -import 'package:flutter/services.dart'; +import 'package:flutter/foundation.dart'; import 'package:url_launcher_platform_interface/link.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; -const MethodChannel _channel = - MethodChannel('plugins.flutter.io/url_launcher_windows'); +import 'src/messages.g.dart'; /// An implementation of [UrlLauncherPlatform] for Windows. class UrlLauncherWindows extends UrlLauncherPlatform { + UrlLauncherApi _hostApi = UrlLauncherApi(); + /// Registers this class as the default instance of [UrlLauncherPlatform]. static void registerWith() { UrlLauncherPlatform.instance = UrlLauncherWindows(); } + /// Overrides the implementing Pigeon API for testing. + @visibleForTesting + // ignore: use_setters_to_change_properties + void setMockApi(UrlLauncherApi api) { + _hostApi = api; + } + @override final LinkDelegate? linkDelegate = null; @override Future canLaunch(String url) { - return _channel.invokeMethod( - 'canLaunch', - {'url': url}, - ).then((bool? value) => value ?? false); + return _hostApi.canLaunchUrl(url); } @override @@ -39,16 +42,9 @@ class UrlLauncherWindows extends UrlLauncherPlatform { required bool universalLinksOnly, required Map headers, String? webOnlyWindowName, - }) { - return _channel.invokeMethod( - 'launch', - { - 'url': url, - 'enableJavaScript': enableJavaScript, - 'enableDomStorage': enableDomStorage, - 'universalLinksOnly': universalLinksOnly, - 'headers': headers, - }, - ).then((bool? value) => value ?? false); + }) async { + await _hostApi.launchUrl(url); + // Failure is handled via a PlatformException from `launchUrl`. + return true; } } diff --git a/packages/url_launcher/url_launcher_windows/pigeons/messages.dart b/packages/url_launcher/url_launcher_windows/pigeons/messages.dart index 4297d8baae64..9607cdffc686 100644 --- a/packages/url_launcher/url_launcher_windows/pigeons/messages.dart +++ b/packages/url_launcher/url_launcher_windows/pigeons/messages.dart @@ -6,7 +6,6 @@ import 'package:pigeon/pigeon.dart'; @ConfigurePigeon(PigeonOptions( dartOut: 'lib/src/messages.g.dart', - dartTestOut: 'test/test_api.g.dart', cppOptions: CppOptions(namespace: 'url_launcher_windows'), cppHeaderOut: 'windows/messages.g.h', cppSourceOut: 'windows/messages.g.cpp', diff --git a/packages/url_launcher/url_launcher_windows/test/test_api.g.dart b/packages/url_launcher/url_launcher_windows/test/test_api.g.dart deleted file mode 100644 index 645a27142637..000000000000 --- a/packages/url_launcher/url_launcher_windows/test/test_api.g.dart +++ /dev/null @@ -1,62 +0,0 @@ -// 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. -// Autogenerated from Pigeon (v5.0.1), do not edit directly. -// See also: https://pub.dev/packages/pigeon -// ignore_for_file: public_member_api_docs, non_constant_identifier_names, avoid_as, unused_import, unnecessary_parenthesis, unnecessary_import -// ignore_for_file: avoid_relative_lib_imports -import 'dart:async'; -import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; -import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; -import 'package:flutter/services.dart'; -import 'package:flutter_test/flutter_test.dart'; - -import 'package:url_launcher_windows/src/messages.g.dart'; - - -abstract class TestUrlLauncherApi { - static const MessageCodec codec = StandardMessageCodec(); - - bool canLaunchUrl(String url); - - void launchUrl(String url); - - static void setup(TestUrlLauncherApi? api, {BinaryMessenger? binaryMessenger}) { - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl was null.'); - final List args = (message as List?)!; - final String? arg_url = (args[0] as String?); - assert(arg_url != null, 'Argument for dev.flutter.pigeon.UrlLauncherApi.canLaunchUrl was null, expected non-null String.'); - final bool output = api.canLaunchUrl(arg_url!); - return [output]; - }); - } - } - { - final BasicMessageChannel channel = BasicMessageChannel( - 'dev.flutter.pigeon.UrlLauncherApi.launchUrl', codec, - binaryMessenger: binaryMessenger); - if (api == null) { - channel.setMockMessageHandler(null); - } else { - channel.setMockMessageHandler((Object? message) async { - assert(message != null, - 'Argument for dev.flutter.pigeon.UrlLauncherApi.launchUrl was null.'); - final List args = (message as List?)!; - final String? arg_url = (args[0] as String?); - assert(arg_url != null, 'Argument for dev.flutter.pigeon.UrlLauncherApi.launchUrl was null, expected non-null String.'); - api.launchUrl(arg_url!); - return []; - }); - } - } - } -} diff --git a/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart b/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart index 8b55b29bb530..2241490ea7a7 100644 --- a/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart +++ b/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart @@ -5,140 +5,100 @@ import 'package:flutter/services.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:url_launcher_platform_interface/url_launcher_platform_interface.dart'; +import 'package:url_launcher_windows/src/messages.g.dart'; import 'package:url_launcher_windows/url_launcher_windows.dart'; void main() { - TestWidgetsFlutterBinding.ensureInitialized(); - - group('$UrlLauncherWindows', () { - const MethodChannel channel = - MethodChannel('plugins.flutter.io/url_launcher_windows'); - final List log = []; - channel.setMockMethodCallHandler((MethodCall methodCall) async { - log.add(methodCall); - - // Return null explicitly instead of relying on the implicit null - // returned by the method channel if no return statement is specified. - return null; - }); + late _FakeUrlLauncherApi api; + late UrlLauncherWindows plugin; - test('registers instance', () { - UrlLauncherWindows.registerWith(); - expect(UrlLauncherPlatform.instance, isA()); - }); + setUp(() { + plugin = UrlLauncherWindows(); + api = _FakeUrlLauncherApi(); + plugin.setMockApi(api); + }); - tearDown(() { - log.clear(); - }); + test('registers instance', () { + UrlLauncherWindows.registerWith(); + expect(UrlLauncherPlatform.instance, isA()); + }); - test('canLaunch', () async { - final UrlLauncherWindows launcher = UrlLauncherWindows(); - await launcher.canLaunch('http://example.com/'); - expect( - log, - [ - isMethodCall('canLaunch', arguments: { - 'url': 'http://example.com/', - }) - ], - ); - }); + group('canLaunch', () { + test('handles true', () async { + api.canLaunch = true; - test('canLaunch should return false if platform returns null', () async { - final UrlLauncherWindows launcher = UrlLauncherWindows(); - final bool canLaunch = await launcher.canLaunch('http://example.com/'); + final bool result = await plugin.canLaunch('http://example.com/'); - expect(canLaunch, false); + expect(result, isTrue); + expect(api.argument, 'http://example.com/'); }); - test('launch', () async { - final UrlLauncherWindows launcher = UrlLauncherWindows(); - await launcher.launch( - 'http://example.com/', - useSafariVC: true, - useWebView: false, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: false, - headers: const {}, - ); - expect( - log, - [ - isMethodCall('launch', arguments: { - 'url': 'http://example.com/', - 'enableJavaScript': false, - 'enableDomStorage': false, - 'universalLinksOnly': false, - 'headers': {}, - }) - ], - ); - }); + test('handles false', () async { + api.canLaunch = false; - test('launch with headers', () async { - final UrlLauncherWindows launcher = UrlLauncherWindows(); - await launcher.launch( - 'http://example.com/', - useSafariVC: true, - useWebView: false, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: false, - headers: const {'key': 'value'}, - ); - expect( - log, - [ - isMethodCall('launch', arguments: { - 'url': 'http://example.com/', - 'enableJavaScript': false, - 'enableDomStorage': false, - 'universalLinksOnly': false, - 'headers': {'key': 'value'}, - }) - ], - ); + final bool result = await plugin.canLaunch('http://example.com/'); + + expect(result, isFalse); + expect(api.argument, 'http://example.com/'); }); + }); + + group('launch', () { + test('handles success', () async { + api.canLaunch = true; - test('launch universal links only', () async { - final UrlLauncherWindows launcher = UrlLauncherWindows(); - await launcher.launch( - 'http://example.com/', - useSafariVC: false, - useWebView: false, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: true, - headers: const {}, - ); expect( - log, - [ - isMethodCall('launch', arguments: { - 'url': 'http://example.com/', - 'enableJavaScript': false, - 'enableDomStorage': false, - 'universalLinksOnly': true, - 'headers': {}, - }) - ], - ); + plugin.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ), + completes); + expect(api.argument, 'http://example.com/'); }); - test('launch should return false if platform returns null', () async { - final UrlLauncherWindows launcher = UrlLauncherWindows(); - final bool launched = await launcher.launch( - 'http://example.com/', - useSafariVC: true, - useWebView: false, - enableJavaScript: false, - enableDomStorage: false, - universalLinksOnly: false, - headers: const {}, - ); - - expect(launched, false); + test('handles failure', () async { + api.canLaunch = false; + + await expectLater( + plugin.launch( + 'http://example.com/', + useSafariVC: true, + useWebView: false, + enableJavaScript: false, + enableDomStorage: false, + universalLinksOnly: false, + headers: const {}, + ), + throwsA(isA())); + expect(api.argument, 'http://example.com/'); }); }); } + +class _FakeUrlLauncherApi implements UrlLauncherApi { + /// The argument that was passed to an API call. + String? argument; + + /// The return value for [canLaunchUrl], and whether or not [launchUrl] + /// should throw. + bool canLaunch = false; + + @override + Future canLaunchUrl(String url) async { + argument = url; + return canLaunch; + } + + @override + Future launchUrl(String url) async { + argument = url; + if (!canLaunch) { + throw PlatformException(code: 'Failed'); + } + } +} From 7056e0d83770589cc01fcf38a2bb0d81090353fa Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 19 Jan 2023 10:47:45 -0500 Subject: [PATCH 5/8] Version bump --- packages/url_launcher/url_launcher_windows/CHANGELOG.md | 3 ++- packages/url_launcher/url_launcher_windows/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/packages/url_launcher/url_launcher_windows/CHANGELOG.md b/packages/url_launcher/url_launcher_windows/CHANGELOG.md index a5952feb4978..1d59512ca8da 100644 --- a/packages/url_launcher/url_launcher_windows/CHANGELOG.md +++ b/packages/url_launcher/url_launcher_windows/CHANGELOG.md @@ -1,5 +1,6 @@ -## NEXT +## 3.0.2 +* Converts internal implentation to Pigeon. * Updates minimum Flutter version to 2.10. ## 3.0.1 diff --git a/packages/url_launcher/url_launcher_windows/pubspec.yaml b/packages/url_launcher/url_launcher_windows/pubspec.yaml index 1a18dc894ce4..1aa627284b71 100644 --- a/packages/url_launcher/url_launcher_windows/pubspec.yaml +++ b/packages/url_launcher/url_launcher_windows/pubspec.yaml @@ -2,7 +2,7 @@ name: url_launcher_windows description: Windows implementation of the url_launcher plugin. repository: https://github.com/flutter/plugins/tree/main/packages/url_launcher/url_launcher_windows issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+url_launcher%22 -version: 3.0.1 +version: 3.0.2 environment: sdk: ">=2.12.0 <3.0.0" From fbff3bb6c794b59be3218f41ad931f181482f21c Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Thu, 19 Jan 2023 10:59:54 -0500 Subject: [PATCH 6/8] autoformat --- .../url_launcher/url_launcher_windows/lib/src/messages.g.dart | 1 - 1 file changed, 1 deletion(-) diff --git a/packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart b/packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart index 9a4835dfce6a..a1d46c11267d 100644 --- a/packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart +++ b/packages/url_launcher/url_launcher_windows/lib/src/messages.g.dart @@ -10,7 +10,6 @@ import 'dart:typed_data' show Float64List, Int32List, Int64List, Uint8List; import 'package:flutter/foundation.dart' show ReadBuffer, WriteBuffer; import 'package:flutter/services.dart'; - class UrlLauncherApi { /// Constructor for [UrlLauncherApi]. The [binaryMessenger] named argument is /// available for dependency injection. If it is left null, the default From 3fbffb8830510d4946cbc42c7508f05ab5d0e129 Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Mon, 23 Jan 2023 14:30:28 -0500 Subject: [PATCH 7/8] Adjust mock API setup --- .../lib/url_launcher_windows.dart | 14 ++++++-------- .../test/url_launcher_windows_test.dart | 3 +-- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart b/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart index 20c04b6cd948..41c403e56f8e 100644 --- a/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart +++ b/packages/url_launcher/url_launcher_windows/lib/url_launcher_windows.dart @@ -10,20 +10,18 @@ import 'src/messages.g.dart'; /// An implementation of [UrlLauncherPlatform] for Windows. class UrlLauncherWindows extends UrlLauncherPlatform { - UrlLauncherApi _hostApi = UrlLauncherApi(); + /// Creates a new plugin implementation instance. + UrlLauncherWindows({ + @visibleForTesting UrlLauncherApi? api, + }) : _hostApi = api ?? UrlLauncherApi(); + + final UrlLauncherApi _hostApi; /// Registers this class as the default instance of [UrlLauncherPlatform]. static void registerWith() { UrlLauncherPlatform.instance = UrlLauncherWindows(); } - /// Overrides the implementing Pigeon API for testing. - @visibleForTesting - // ignore: use_setters_to_change_properties - void setMockApi(UrlLauncherApi api) { - _hostApi = api; - } - @override final LinkDelegate? linkDelegate = null; diff --git a/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart b/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart index 2241490ea7a7..c2cbad354a1f 100644 --- a/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart +++ b/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart @@ -13,9 +13,8 @@ void main() { late UrlLauncherWindows plugin; setUp(() { - plugin = UrlLauncherWindows(); api = _FakeUrlLauncherApi(); - plugin.setMockApi(api); + plugin = UrlLauncherWindows(api: api); }); test('registers instance', () { From b9f430bd0755d38bad05e1b90b4758dfc40ae14a Mon Sep 17 00:00:00 2001 From: Stuart Morgan Date: Tue, 24 Jan 2023 13:28:18 -0500 Subject: [PATCH 8/8] Improve comment --- .../test/url_launcher_windows_test.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart b/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart index c2cbad354a1f..7f48f64fa92c 100644 --- a/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart +++ b/packages/url_launcher/url_launcher_windows/test/url_launcher_windows_test.dart @@ -83,8 +83,10 @@ class _FakeUrlLauncherApi implements UrlLauncherApi { /// The argument that was passed to an API call. String? argument; - /// The return value for [canLaunchUrl], and whether or not [launchUrl] - /// should throw. + /// Controls the behavior of the fake implementations. + /// + /// - [canLaunchUrl] returns this value. + /// - [launchUrl] throws if this is false. bool canLaunch = false; @override