Skip to content
This repository was archived by the owner on Feb 22, 2023. It is now read-only.

Commit 72634e0

Browse files
[plugin_platform_interface] Add a new method verify that prevents use of const Object() as token. (#4640)
- Introduce `verify`, a more future-proof name. It throws `AssertionError` if `const Object()` used as the instance's token. - Soft-deprecate `verifyToken` with a comment. It will actually deprecated in a future release, to avoid breaking tests with this minor change. - Update documentation for `PlatformInterface` to show new usage of `verify` and other cosmetic fixes. - Add a test for the new assertion. Fixes flutter/flutter#96178.
1 parent cb381ce commit 72634e0

File tree

4 files changed

+109
-25
lines changed

4 files changed

+109
-25
lines changed

packages/plugin_platform_interface/CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
## 2.1.0
2+
3+
* Introduce `verify`, which prevents use of `const Object()` as instance token.
4+
* Add a comment indicating that `verifyToken` will be deprecated in a future release.
5+
16
## 2.0.2
27

38
* Update package description.

packages/plugin_platform_interface/lib/plugin_platform_interface.dart

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ import 'package:meta/meta.dart';
1212
/// implemented using `extends` instead of `implements`.
1313
///
1414
/// Platform interface classes are expected to have a private static token object which will be
15-
/// be passed to [verifyToken] along with a platform interface object for verification.
15+
/// be passed to [verify] along with a platform interface object for verification.
1616
///
1717
/// Sample usage:
1818
///
@@ -22,14 +22,14 @@ import 'package:meta/meta.dart';
2222
///
2323
/// static UrlLauncherPlatform _instance = MethodChannelUrlLauncher();
2424
///
25-
/// static const Object _token = Object();
25+
/// static final Object _token = Object();
2626
///
2727
/// static UrlLauncherPlatform get instance => _instance;
2828
///
2929
/// /// Platform-specific plugins should set this with their own platform-specific
3030
/// /// class that extends [UrlLauncherPlatform] when they register themselves.
3131
/// static set instance(UrlLauncherPlatform instance) {
32-
/// PlatformInterface.verifyToken(instance, _token);
32+
/// PlatformInterface.verify(instance, _token);
3333
/// _instance = instance;
3434
/// }
3535
///
@@ -40,13 +40,16 @@ import 'package:meta/meta.dart';
4040
/// to include the [MockPlatformInterfaceMixin] for the verification to be temporarily disabled. See
4141
/// [MockPlatformInterfaceMixin] for a sample of using Mockito to mock a platform interface.
4242
abstract class PlatformInterface {
43-
/// Pass a private, class-specific `const Object()` as the `token`.
43+
/// Constructs a PlatformInterface, for use only in constructors of abstract
44+
/// derived classes.
45+
///
46+
/// @param token The same, non-`const` `Object` that will be passed to `verify`.
4447
PlatformInterface({required Object token}) : _instanceToken = token;
4548

4649
final Object? _instanceToken;
4750

48-
/// Ensures that the platform instance has a token that matches the
49-
/// provided token and throws [AssertionError] if not.
51+
/// Ensures that the platform instance was constructed with a non-`const` token
52+
/// that matches the provided token and throws [AssertionError] if not.
5053
///
5154
/// This is used to ensure that implementers are using `extends` rather than
5255
/// `implements`.
@@ -56,7 +59,22 @@ abstract class PlatformInterface {
5659
///
5760
/// This is implemented as a static method so that it cannot be overridden
5861
/// with `noSuchMethod`.
62+
static void verify(PlatformInterface instance, Object token) {
63+
if (identical(instance._instanceToken, const Object())) {
64+
throw AssertionError('`const Object()` cannot be used as the token.');
65+
}
66+
_verify(instance, token);
67+
}
68+
69+
/// Performs the same checks as `verify` but without throwing an
70+
/// [AssertionError] if `const Object()` is used as the instance token.
71+
///
72+
/// This method will be deprecated in a future release.
5973
static void verifyToken(PlatformInterface instance, Object token) {
74+
_verify(instance, token);
75+
}
76+
77+
static void _verify(PlatformInterface instance, Object token) {
6078
if (instance is MockPlatformInterfaceMixin) {
6179
bool assertionsEnabled = false;
6280
assert(() {
@@ -78,12 +96,12 @@ abstract class PlatformInterface {
7896

7997
/// A [PlatformInterface] mixin that can be combined with mockito's `Mock`.
8098
///
81-
/// It passes the [PlatformInterface.verifyToken] check even though it isn't
99+
/// It passes the [PlatformInterface.verify] check even though it isn't
82100
/// using `extends`.
83101
///
84102
/// This class is intended for use in tests only.
85103
///
86-
/// Sample usage (assuming UrlLauncherPlatform extends [PlatformInterface]:
104+
/// Sample usage (assuming `UrlLauncherPlatform` extends [PlatformInterface]):
87105
///
88106
/// ```dart
89107
/// class UrlLauncherPlatformMock extends Mock

packages/plugin_platform_interface/pubspec.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ issue_tracker: https://github.com/flutter/flutter/issues?q=is%3Aissue+is%3Aopen+
99
#
1010
# This package is used as a second level dependency for many plugins, a major version bump here
1111
# is guaranteed to lead the ecosystem to a version lock (the first plugin that upgrades to version
12-
# 2 of this package cannot be used with any other plugin that have not yet migrated).
12+
# 3 of this package cannot be used with any other plugin that have not yet migrated).
1313
#
1414
# Please consider carefully before bumping the major version of this package, ideally it should only
15-
# be done when absolutely necessary and after the ecosystem has already migrated to 1.X.Y version
16-
# that is forward compatible with 2.0.0 (ideally the ecosystem have migrated to depend on:
17-
# `plugin_platform_interface: >=1.X.Y <3.0.0`).
18-
version: 2.0.2
15+
# be done when absolutely necessary and after the ecosystem has already migrated to 2.X.Y version
16+
# that is forward compatible with 3.0.0 (ideally the ecosystem have migrated to depend on:
17+
# `plugin_platform_interface: >=2.X.Y <4.0.0`).
18+
version: 2.1.0
1919

2020
environment:
2121
sdk: ">=2.12.0 <3.0.0"

packages/plugin_platform_interface/test/plugin_platform_interface_test.dart

Lines changed: 73 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ class SamplePluginPlatform extends PlatformInterface {
1212
static final Object _token = Object();
1313

1414
static set instance(SamplePluginPlatform instance) {
15-
PlatformInterface.verifyToken(instance, _token);
15+
PlatformInterface.verify(instance, _token);
1616
// A real implementation would set a static instance field here.
1717
}
1818
}
@@ -26,20 +26,81 @@ class ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin extends Mock
2626

2727
class ExtendsSamplePluginPlatform extends SamplePluginPlatform {}
2828

29+
class ConstTokenPluginPlatform extends PlatformInterface {
30+
ConstTokenPluginPlatform() : super(token: _token);
31+
32+
static const Object _token = Object(); // invalid
33+
34+
static set instance(ConstTokenPluginPlatform instance) {
35+
PlatformInterface.verify(instance, _token);
36+
}
37+
}
38+
39+
class ExtendsConstTokenPluginPlatform extends ConstTokenPluginPlatform {}
40+
41+
class VerifyTokenPluginPlatform extends PlatformInterface {
42+
VerifyTokenPluginPlatform() : super(token: _token);
43+
44+
static final Object _token = Object();
45+
46+
static set instance(VerifyTokenPluginPlatform instance) {
47+
PlatformInterface.verifyToken(instance, _token);
48+
// A real implementation would set a static instance field here.
49+
}
50+
}
51+
52+
class ImplementsVerifyTokenPluginPlatform extends Mock
53+
implements VerifyTokenPluginPlatform {}
54+
55+
class ImplementsVerifyTokenPluginPlatformUsingMockPlatformInterfaceMixin
56+
extends Mock
57+
with MockPlatformInterfaceMixin
58+
implements VerifyTokenPluginPlatform {}
59+
60+
class ExtendsVerifyTokenPluginPlatform extends VerifyTokenPluginPlatform {}
61+
2962
void main() {
30-
test('Cannot be implemented with `implements`', () {
31-
expect(() {
32-
SamplePluginPlatform.instance = ImplementsSamplePluginPlatform();
33-
}, throwsA(isA<AssertionError>()));
34-
});
63+
group('`verify`', () {
64+
test('prevents implementation with `implements`', () {
65+
expect(() {
66+
SamplePluginPlatform.instance = ImplementsSamplePluginPlatform();
67+
}, throwsA(isA<AssertionError>()));
68+
});
69+
70+
test('allows mocking with `implements`', () {
71+
final SamplePluginPlatform mock =
72+
ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin();
73+
SamplePluginPlatform.instance = mock;
74+
});
3575

36-
test('Can be mocked with `implements`', () {
37-
final SamplePluginPlatform mock =
38-
ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin();
39-
SamplePluginPlatform.instance = mock;
76+
test('allows extending', () {
77+
SamplePluginPlatform.instance = ExtendsSamplePluginPlatform();
78+
});
79+
80+
test('prevents `const Object()` token', () {
81+
expect(() {
82+
ConstTokenPluginPlatform.instance = ExtendsConstTokenPluginPlatform();
83+
}, throwsA(isA<AssertionError>()));
84+
});
4085
});
4186

42-
test('Can be extended', () {
43-
SamplePluginPlatform.instance = ExtendsSamplePluginPlatform();
87+
// Tests of the earlier, to-be-deprecated `verifyToken` method
88+
group('`verifyToken`', () {
89+
test('prevents implementation with `implements`', () {
90+
expect(() {
91+
VerifyTokenPluginPlatform.instance =
92+
ImplementsVerifyTokenPluginPlatform();
93+
}, throwsA(isA<AssertionError>()));
94+
});
95+
96+
test('allows mocking with `implements`', () {
97+
final VerifyTokenPluginPlatform mock =
98+
ImplementsVerifyTokenPluginPlatformUsingMockPlatformInterfaceMixin();
99+
VerifyTokenPluginPlatform.instance = mock;
100+
});
101+
102+
test('allows extending', () {
103+
VerifyTokenPluginPlatform.instance = ExtendsVerifyTokenPluginPlatform();
104+
});
44105
});
45106
}

0 commit comments

Comments
 (0)