diff --git a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md index 591f36eb1ae8..cc88c07eb00d 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md +++ b/packages/google_sign_in/google_sign_in_platform_interface/CHANGELOG.md @@ -1,3 +1,8 @@ +## 2.3.0 + +* Adopts `plugin_platform_interface`. As a result, `isMock` is deprecated in + favor of the now-standard `MockPlatformInterfaceMixin`. + ## 2.2.0 * Adds support for the `serverClientId` parameter. diff --git a/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart b/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart index 69d8455b6bd2..64fc88d4866f 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/lib/google_sign_in_platform_interface.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart' show visibleForTesting; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'src/method_channel_google_sign_in.dart'; import 'src/types.dart'; @@ -20,13 +21,19 @@ export 'src/types.dart'; /// ensures that the subclass will get the default implementation, while /// platform implementations that `implements` this interface will be broken by /// newly added [GoogleSignInPlatform] methods. -abstract class GoogleSignInPlatform { +abstract class GoogleSignInPlatform extends PlatformInterface { + /// Constructs a GoogleSignInPlatform. + GoogleSignInPlatform() : super(token: _token); + + static final Object _token = Object(); + /// Only mock implementations should set this to `true`. /// /// Mockito mocks implement this class with `implements` which is forbidden /// (see class docs). This property provides a backdoor for mocks to skip the /// verification that the class isn't implemented with `implements`. @visibleForTesting + @Deprecated('Use MockPlatformInterfaceMixin instead') bool get isMock => false; /// The default instance of [GoogleSignInPlatform] to use. @@ -44,25 +51,11 @@ abstract class GoogleSignInPlatform { // https://github.com/flutter/flutter/issues/43368 static set instance(GoogleSignInPlatform instance) { if (!instance.isMock) { - try { - instance._verifyProvidesDefaultImplementations(); - } on NoSuchMethodError catch (_) { - throw AssertionError( - 'Platform interfaces must not be implemented with `implements`'); - } + PlatformInterface.verify(instance, _token); } _instance = instance; } - /// This method ensures that [GoogleSignInPlatform] isn't implemented with `implements`. - /// - /// See class docs for more details on why using `implements` to implement - /// [GoogleSignInPlatform] is forbidden. - /// - /// This private method is called by the [instance] setter, which should fail - /// if the provided instance is a class implemented with `implements`. - void _verifyProvidesDefaultImplementations() {} - /// Initializes the plugin. Deprecated: call [initWithParams] instead. /// /// The [hostedDomain] argument specifies a hosted domain restriction. By diff --git a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml index 9ad3e1cf005b..aa9c8f6ff520 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml +++ b/packages/google_sign_in/google_sign_in_platform_interface/pubspec.yaml @@ -4,7 +4,7 @@ repository: https://github.com/flutter/plugins/tree/main/packages/google_sign_in issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+google_sign_in%22 # NOTE: We strongly prefer non-breaking changes, even at the expense of a # less-clean API. See https://flutter.dev/go/platform-interface-breaking-changes -version: 2.2.0 +version: 2.3.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -13,6 +13,7 @@ environment: dependencies: flutter: sdk: flutter + plugin_platform_interface: ^2.1.0 quiver: ^3.0.0 dev_dependencies: diff --git a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart index 6ffa85fa1e4b..057f13cb26f5 100644 --- a/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart +++ b/packages/google_sign_in/google_sign_in_platform_interface/test/google_sign_in_platform_interface_test.dart @@ -5,6 +5,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:google_sign_in_platform_interface/google_sign_in_platform_interface.dart'; import 'package:mockito/mockito.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; void main() { // Store the initial instance before any tests change it. @@ -33,7 +34,11 @@ void main() { }); test('Can be mocked with `implements`', () { - GoogleSignInPlatform.instance = ImplementsWithIsMock(); + GoogleSignInPlatform.instance = ModernMockImplementation(); + }); + + test('still supports legacy isMock', () { + GoogleSignInPlatform.instance = LegacyIsMockImplementation(); }); }); @@ -76,11 +81,18 @@ void main() { }); } -class ImplementsWithIsMock extends Mock implements GoogleSignInPlatform { +class LegacyIsMockImplementation extends Mock implements GoogleSignInPlatform { @override bool get isMock => true; } +class ModernMockImplementation extends Mock + with MockPlatformInterfaceMixin + implements GoogleSignInPlatform { + @override + bool get isMock => false; +} + class ImplementsGoogleSignInPlatform extends Mock implements GoogleSignInPlatform {} diff --git a/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md b/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md index 51b59651b49a..12d61329171f 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md +++ b/packages/shared_preferences/shared_preferences_platform_interface/CHANGELOG.md @@ -1,6 +1,7 @@ -## NEXT +## 2.1.0 -* Fixes newly enabled analyzer options. +* Adopts `plugin_platform_interface`. As a result, `isMock` is deprecated in + favor of the now-standard `MockPlatformInterfaceMixin`. ## 2.0.0 diff --git a/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart b/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart index 8023c864a399..ced6aa5c389f 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart +++ b/packages/shared_preferences/shared_preferences_platform_interface/lib/shared_preferences_platform_interface.dart @@ -5,6 +5,7 @@ import 'dart:async'; import 'package:flutter/foundation.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'method_channel_shared_preferences.dart'; @@ -15,7 +16,12 @@ import 'method_channel_shared_preferences.dart'; /// (using `extends`) ensures that the subclass will get the default implementation, while /// platform implementations that `implements` this interface will be broken by newly added /// [SharedPreferencesStorePlatform] methods. -abstract class SharedPreferencesStorePlatform { +abstract class SharedPreferencesStorePlatform extends PlatformInterface { + /// Constructs a SharedPreferencesStorePlatform. + SharedPreferencesStorePlatform() : super(token: _token); + + static final Object _token = Object(); + /// The default instance of [SharedPreferencesStorePlatform] to use. /// /// Defaults to [MethodChannelSharedPreferencesStore]. @@ -23,16 +29,11 @@ abstract class SharedPreferencesStorePlatform { /// Platform-specific plugins should set this with their own platform-specific /// class that extends [SharedPreferencesStorePlatform] when they register themselves. - static set instance(SharedPreferencesStorePlatform value) { - if (!value.isMock) { - try { - value._verifyProvidesDefaultImplementations(); - } on NoSuchMethodError catch (_) { - throw AssertionError( - 'Platform interfaces must not be implemented with `implements`'); - } + static set instance(SharedPreferencesStorePlatform instance) { + if (!instance.isMock) { + PlatformInterface.verify(instance, _token); } - _instance = value; + _instance = instance; } static SharedPreferencesStorePlatform _instance = @@ -44,6 +45,7 @@ abstract class SharedPreferencesStorePlatform { /// other than mocks (see class docs). This property provides a backdoor for mockito mocks to /// skip the verification that the class isn't implemented with `implements`. @visibleForTesting + @Deprecated('Use MockPlatformInterfaceMixin instead') bool get isMock => false; /// Removes the value associated with the [key]. @@ -65,14 +67,6 @@ abstract class SharedPreferencesStorePlatform { /// Returns all key/value pairs persisted in this store. Future> getAll(); - - // This method makes sure that SharedPreferencesStorePlatform isn't implemented with `implements`. - // - // See class doc for more details on why implementing this class is forbidden. - // - // This private method is called by the instance setter, which fails if the class is - // implemented with `implements`. - void _verifyProvidesDefaultImplementations() {} } /// Stores data in memory. diff --git a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml index 9dce94c616c4..7e4b9f0699fc 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml +++ b/packages/shared_preferences/shared_preferences_platform_interface/pubspec.yaml @@ -2,7 +2,7 @@ name: shared_preferences_platform_interface description: A common platform interface for the shared_preferences plugin. repository: https://github.com/flutter/plugins/tree/main/packages/shared_preferences/shared_preferences_platform_interface issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+label%3A%22p%3A+shared_preferences%22 -version: 2.0.0 +version: 2.1.0 environment: sdk: ">=2.12.0 <3.0.0" @@ -11,6 +11,7 @@ environment: dependencies: flutter: sdk: flutter + plugin_platform_interface: ^2.1.0 dev_dependencies: flutter_test: diff --git a/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart b/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart index 8efe885c122c..ed078e87909e 100644 --- a/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart +++ b/packages/shared_preferences/shared_preferences_platform_interface/test/shared_preferences_platform_interface_test.dart @@ -3,6 +3,7 @@ // found in the LICENSE file. import 'package:flutter_test/flutter_test.dart'; +import 'package:plugin_platform_interface/plugin_platform_interface.dart'; import 'package:shared_preferences_platform_interface/shared_preferences_platform_interface.dart'; void main() { @@ -10,16 +11,30 @@ void main() { group(SharedPreferencesStorePlatform, () { test('disallows implementing interface', () { - expect( - () { - SharedPreferencesStorePlatform.instance = IllegalImplementation(); - }, - throwsAssertionError, - ); + expect(() { + SharedPreferencesStorePlatform.instance = IllegalImplementation(); + }, + // In versions of `package:plugin_platform_interface` prior to fixing + // https://github.com/flutter/flutter/issues/109339, an attempt to + // implement a platform interface using `implements` would sometimes + // throw a `NoSuchMethodError` and other times throw an + // `AssertionError`. After the issue is fixed, an `AssertionError` will + // always be thrown. For the purpose of this test, we don't really care + // what exception is thrown, so just allow any exception. + throwsA(anything)); + }); + + test('supports MockPlatformInterfaceMixin', () { + SharedPreferencesStorePlatform.instance = ModernMockImplementation(); + }); + + test('still supports legacy isMock', () { + SharedPreferencesStorePlatform.instance = LegacyIsMockImplementation(); }); }); } +/// An implementation using `implements` that isn't a mock, which isn't allowed. class IllegalImplementation implements SharedPreferencesStorePlatform { // Intentionally declare self as not a mock to trigger the // compliance check. @@ -46,3 +61,55 @@ class IllegalImplementation implements SharedPreferencesStorePlatform { throw UnimplementedError(); } } + +class LegacyIsMockImplementation implements SharedPreferencesStorePlatform { + @override + bool get isMock => true; + + @override + Future clear() { + throw UnimplementedError(); + } + + @override + Future> getAll() { + throw UnimplementedError(); + } + + @override + Future remove(String key) { + throw UnimplementedError(); + } + + @override + Future setValue(String valueType, String key, Object value) { + throw UnimplementedError(); + } +} + +class ModernMockImplementation + with MockPlatformInterfaceMixin + implements SharedPreferencesStorePlatform { + @override + bool get isMock => false; + + @override + Future clear() { + throw UnimplementedError(); + } + + @override + Future> getAll() { + throw UnimplementedError(); + } + + @override + Future remove(String key) { + throw UnimplementedError(); + } + + @override + Future setValue(String valueType, String key, Object value) { + throw UnimplementedError(); + } +}