mirror of
https://github.com/Iconica-Development/flutter_data_interface.git
synced 2025-05-18 18:33:45 +02:00
initial commit
This commit is contained in:
commit
e348c921d5
9 changed files with 360 additions and 0 deletions
30
.gitignore
vendored
Normal file
30
.gitignore
vendored
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
# Miscellaneous
|
||||||
|
*.class
|
||||||
|
*.log
|
||||||
|
*.pyc
|
||||||
|
*.swp
|
||||||
|
.DS_Store
|
||||||
|
.atom/
|
||||||
|
.buildlog/
|
||||||
|
.history
|
||||||
|
.svn/
|
||||||
|
migrate_working_dir/
|
||||||
|
|
||||||
|
# IntelliJ related
|
||||||
|
*.iml
|
||||||
|
*.ipr
|
||||||
|
*.iws
|
||||||
|
.idea/
|
||||||
|
|
||||||
|
# The .vscode folder contains launch configuration and tasks you configure in
|
||||||
|
# VS Code which you may wish to be included in version control, so this line
|
||||||
|
# is commented out by default.
|
||||||
|
#.vscode/
|
||||||
|
|
||||||
|
# Flutter/Dart/Pub related
|
||||||
|
# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.
|
||||||
|
/pubspec.lock
|
||||||
|
**/doc/api/
|
||||||
|
.dart_tool/
|
||||||
|
.packages
|
||||||
|
build/
|
10
.metadata
Normal file
10
.metadata
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
# This file tracks properties of this Flutter project.
|
||||||
|
# Used by Flutter tool to assess capabilities and perform upgrades etc.
|
||||||
|
#
|
||||||
|
# This file should be version controlled and should not be manually edited.
|
||||||
|
|
||||||
|
version:
|
||||||
|
revision: ffccd96b62ee8cec7740dab303538c5fc26ac543
|
||||||
|
channel: stable
|
||||||
|
|
||||||
|
project_type: package
|
3
CHANGELOG.md
Normal file
3
CHANGELOG.md
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
## 1.0.0
|
||||||
|
|
||||||
|
- Initial release
|
21
LICENSE
Normal file
21
LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2022 Iconica
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
15
README.md
Normal file
15
README.md
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
[](https://github.com/tenhobi/effective_dart)
|
||||||
|
|
||||||
|
Flutter package for creating data providers
|
||||||
|
|
||||||
|
## Issues
|
||||||
|
|
||||||
|
Please file any issues, bugs or feature request as an issue on our [GitHub](https://github.com/Iconica-Development/flutter_axis_video_viewer/issues) page. Commercial support is available if you need help with integration with your app or services. You can contact us at [support@iconica.nl](mailto:support@iconica.nl).
|
||||||
|
|
||||||
|
## Want to contribute
|
||||||
|
|
||||||
|
If you would like to contribute to the plugin (e.g. by improving the documentation, solving a bug or adding a cool new feature), please carefully review our [contribution guide](./CONTRIBUTING.md) and send us your [pull request](https://github.com/Iconica-Development/flutter_date_interface/pulls).
|
||||||
|
|
||||||
|
## Author
|
||||||
|
|
||||||
|
This [flutter_data_interface](https://github.com/Iconica-Development/flutter_data_interface) for Flutter is developed by [Iconica](https://iconica.nl). You can contact us at <support@iconica.nl>
|
4
analysis_options.yaml
Normal file
4
analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
include: package:flutter_lints/flutter.yaml
|
||||||
|
|
||||||
|
# Additional information about this file can be found at
|
||||||
|
# https://dart.dev/guides/language/analysis-options
|
99
lib/flutter_data_interface.dart
Normal file
99
lib/flutter_data_interface.dart
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
library plugin_platform_interface;
|
||||||
|
|
||||||
|
import 'package:meta/meta.dart';
|
||||||
|
|
||||||
|
/// Base class for data interfaces.
|
||||||
|
///
|
||||||
|
/// Provides a static helper method for ensuring that data interfaces are
|
||||||
|
/// implemented using `extends` instead of `implements`.
|
||||||
|
///
|
||||||
|
/// Data interface classes are expected to have a private static token object which will be
|
||||||
|
/// be passed to [verify] along with a data interface object for verification.
|
||||||
|
///
|
||||||
|
/// Sample usage:
|
||||||
|
///
|
||||||
|
///
|
||||||
|
/// Mockito mocks of data interfaces will fail the verification, in test code only it is possible
|
||||||
|
/// to include the [MockDataInterfaceMixin] for the verification to be temporarily disabled. See
|
||||||
|
/// [MockDataInterfaceMixin] for a sample of using Mockito to mock a data interface.
|
||||||
|
abstract class DataInterface {
|
||||||
|
/// Constructs a DataInterface, for use only in constructors of abstract
|
||||||
|
/// derived classes.
|
||||||
|
///
|
||||||
|
/// @param token The same, non-`const` `Object` that will be passed to `verify`.
|
||||||
|
DataInterface({required Object token}) {
|
||||||
|
_instanceTokens[this] = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Expando mapping instances of DataInterface to their associated tokens.
|
||||||
|
/// The reason this is not simply a private field of type `Object?` is because
|
||||||
|
/// as of the implementation of field promotion in Dart
|
||||||
|
/// (https://github.com/dart-lang/language/issues/2020), it is a runtime error
|
||||||
|
/// to invoke a private member that is mocked in another library. The expando
|
||||||
|
/// approach prevents [_verify] from triggering this runtime exception when
|
||||||
|
/// encountering an implementation that uses `implements` rather than
|
||||||
|
/// `extends`. This in turn allows [_verify] to throw an [AssertionError] (as
|
||||||
|
/// documented).
|
||||||
|
static final Expando<Object> _instanceTokens = Expando<Object>();
|
||||||
|
|
||||||
|
/// Ensures that the data instance was constructed with a non-`const` token
|
||||||
|
/// that matches the provided token and throws [AssertionError] if not.
|
||||||
|
///
|
||||||
|
/// This is used to ensure that implementers are using `extends` rather than
|
||||||
|
/// `implements`.
|
||||||
|
///
|
||||||
|
/// Subclasses of [MockDataInterfaceMixin] are assumed to be valid in debug
|
||||||
|
/// builds.
|
||||||
|
///
|
||||||
|
/// This is implemented as a static method so that it cannot be overridden
|
||||||
|
/// with `noSuchMethod`.
|
||||||
|
static void verify(DataInterface instance, Object token) {
|
||||||
|
_verify(instance, token, preventConstObject: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Performs the same checks as `verify` but without throwing an
|
||||||
|
/// [AssertionError] if `const Object()` is used as the instance token.
|
||||||
|
///
|
||||||
|
/// This method will be deprecated in a future release.
|
||||||
|
static void verifyToken(DataInterface instance, Object token) {
|
||||||
|
_verify(instance, token, preventConstObject: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void _verify(
|
||||||
|
DataInterface instance,
|
||||||
|
Object token, {
|
||||||
|
required bool preventConstObject,
|
||||||
|
}) {
|
||||||
|
if (instance is MockDataInterfaceMixin) {
|
||||||
|
bool assertionsEnabled = false;
|
||||||
|
assert(() {
|
||||||
|
assertionsEnabled = true;
|
||||||
|
return true;
|
||||||
|
}());
|
||||||
|
if (!assertionsEnabled) {
|
||||||
|
throw AssertionError(
|
||||||
|
'`MockDataInterfaceMixin` is not intended for use in release builds.');
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (preventConstObject &&
|
||||||
|
identical(_instanceTokens[instance], const Object())) {
|
||||||
|
throw AssertionError('`const Object()` cannot be used as the token.');
|
||||||
|
}
|
||||||
|
if (!identical(token, _instanceTokens[instance])) {
|
||||||
|
throw AssertionError(
|
||||||
|
'Data interfaces must not be implemented with `implements`');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A [DataInterface] mixin that can be combined with fake or mock objects,
|
||||||
|
/// such as test's `Fake` or mockito's `Mock`.
|
||||||
|
///
|
||||||
|
/// It passes the [DataInterface.verify] check even though it isn't
|
||||||
|
/// using `extends`.
|
||||||
|
///
|
||||||
|
/// This class is intended for use in tests only.
|
||||||
|
///
|
||||||
|
@visibleForTesting
|
||||||
|
abstract class MockDataInterfaceMixin implements DataInterface {}
|
21
pubspec.yaml
Normal file
21
pubspec.yaml
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
name: flutter_data_interface
|
||||||
|
description: Generic data interface package
|
||||||
|
version: 1.0.0
|
||||||
|
repository: https://github.com/Iconica-Development/flutter_data_interface
|
||||||
|
|
||||||
|
|
||||||
|
environment:
|
||||||
|
sdk: '>=2.18.0 <3.0.0'
|
||||||
|
flutter: ">=1.17.0"
|
||||||
|
|
||||||
|
dependencies:
|
||||||
|
flutter:
|
||||||
|
sdk: flutter
|
||||||
|
mockito: any
|
||||||
|
|
||||||
|
dev_dependencies:
|
||||||
|
flutter_test:
|
||||||
|
sdk: flutter
|
||||||
|
flutter_lints: ^2.0.0
|
||||||
|
|
||||||
|
flutter:
|
157
test/flutter_data_interface_test.dart
Normal file
157
test/flutter_data_interface_test.dart
Normal file
|
@ -0,0 +1,157 @@
|
||||||
|
import 'package:flutter_data_interface/flutter_data_interface.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
|
||||||
|
class SamplePluginPlatform extends DataInterface {
|
||||||
|
SamplePluginPlatform() : super(token: _token);
|
||||||
|
|
||||||
|
static final Object _token = Object();
|
||||||
|
|
||||||
|
// ignore: avoid_setters_without_getters
|
||||||
|
static set instance(SamplePluginPlatform instance) {
|
||||||
|
DataInterface.verify(instance, _token);
|
||||||
|
// A real implementation would set a static instance field here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImplementsSamplePluginPlatform extends Mock
|
||||||
|
implements SamplePluginPlatform {}
|
||||||
|
|
||||||
|
class ImplementsSamplePluginPlatformUsingNoSuchMethod
|
||||||
|
implements SamplePluginPlatform {
|
||||||
|
@override
|
||||||
|
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin extends Mock
|
||||||
|
with MockDataInterfaceMixin
|
||||||
|
implements SamplePluginPlatform {}
|
||||||
|
|
||||||
|
class ImplementsSamplePluginPlatformUsingFakePlatformInterfaceMixin extends Fake
|
||||||
|
with MockDataInterfaceMixin
|
||||||
|
implements SamplePluginPlatform {}
|
||||||
|
|
||||||
|
class ExtendsSamplePluginPlatform extends SamplePluginPlatform {}
|
||||||
|
|
||||||
|
class ConstTokenPluginPlatform extends DataInterface {
|
||||||
|
ConstTokenPluginPlatform() : super(token: _token);
|
||||||
|
|
||||||
|
static const Object _token = Object(); // invalid
|
||||||
|
|
||||||
|
// ignore: avoid_setters_without_getters
|
||||||
|
static set instance(ConstTokenPluginPlatform instance) {
|
||||||
|
DataInterface.verify(instance, _token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ExtendsConstTokenPluginPlatform extends ConstTokenPluginPlatform {}
|
||||||
|
|
||||||
|
class VerifyTokenPluginPlatform extends DataInterface {
|
||||||
|
VerifyTokenPluginPlatform() : super(token: _token);
|
||||||
|
|
||||||
|
static final Object _token = Object();
|
||||||
|
|
||||||
|
// ignore: avoid_setters_without_getters
|
||||||
|
static set instance(VerifyTokenPluginPlatform instance) {
|
||||||
|
DataInterface.verifyToken(instance, _token);
|
||||||
|
// A real implementation would set a static instance field here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImplementsVerifyTokenPluginPlatform extends Mock
|
||||||
|
implements VerifyTokenPluginPlatform {}
|
||||||
|
|
||||||
|
class ImplementsVerifyTokenPluginPlatformUsingMockPlatformInterfaceMixin
|
||||||
|
extends Mock
|
||||||
|
with MockDataInterfaceMixin
|
||||||
|
implements VerifyTokenPluginPlatform {}
|
||||||
|
|
||||||
|
class ExtendsVerifyTokenPluginPlatform extends VerifyTokenPluginPlatform {}
|
||||||
|
|
||||||
|
class ConstVerifyTokenPluginPlatform extends DataInterface {
|
||||||
|
ConstVerifyTokenPluginPlatform() : super(token: _token);
|
||||||
|
|
||||||
|
static const Object _token = Object(); // invalid
|
||||||
|
|
||||||
|
// ignore: avoid_setters_without_getters
|
||||||
|
static set instance(ConstVerifyTokenPluginPlatform instance) {
|
||||||
|
DataInterface.verifyToken(instance, _token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ImplementsConstVerifyTokenPluginPlatform extends DataInterface
|
||||||
|
implements ConstVerifyTokenPluginPlatform {
|
||||||
|
ImplementsConstVerifyTokenPluginPlatform() : super(token: const Object());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensures that `PlatformInterface` has no instance methods. Adding an
|
||||||
|
// instance method is discouraged and may be a breaking change if it
|
||||||
|
// conflicts with instance methods in subclasses.
|
||||||
|
class StaticMethodsOnlyPlatformInterfaceTest implements DataInterface {}
|
||||||
|
|
||||||
|
class StaticMethodsOnlyMockPlatformInterfaceMixinTest
|
||||||
|
implements MockDataInterfaceMixin {}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
group('`verify`', () {
|
||||||
|
test('prevents implementation with `implements`', () {
|
||||||
|
expect(() {
|
||||||
|
SamplePluginPlatform.instance = ImplementsSamplePluginPlatform();
|
||||||
|
}, throwsA(isA<AssertionError>()));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('prevents implmentation with `implements` and `noSuchMethod`', () {
|
||||||
|
expect(() {
|
||||||
|
SamplePluginPlatform.instance =
|
||||||
|
ImplementsSamplePluginPlatformUsingNoSuchMethod();
|
||||||
|
}, throwsA(isA<AssertionError>()));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('allows mocking with `implements`', () {
|
||||||
|
final SamplePluginPlatform mock =
|
||||||
|
ImplementsSamplePluginPlatformUsingMockPlatformInterfaceMixin();
|
||||||
|
SamplePluginPlatform.instance = mock;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('allows faking with `implements`', () {
|
||||||
|
final SamplePluginPlatform fake =
|
||||||
|
ImplementsSamplePluginPlatformUsingFakePlatformInterfaceMixin();
|
||||||
|
SamplePluginPlatform.instance = fake;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('allows extending', () {
|
||||||
|
SamplePluginPlatform.instance = ExtendsSamplePluginPlatform();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('prevents `const Object()` token', () {
|
||||||
|
expect(() {
|
||||||
|
ConstTokenPluginPlatform.instance = ExtendsConstTokenPluginPlatform();
|
||||||
|
}, throwsA(isA<AssertionError>()));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Tests of the earlier, to-be-deprecated `verifyToken` method
|
||||||
|
group('`verifyToken`', () {
|
||||||
|
test('prevents implementation with `implements`', () {
|
||||||
|
expect(() {
|
||||||
|
VerifyTokenPluginPlatform.instance =
|
||||||
|
ImplementsVerifyTokenPluginPlatform();
|
||||||
|
}, throwsA(isA<AssertionError>()));
|
||||||
|
});
|
||||||
|
|
||||||
|
test('allows mocking with `implements`', () {
|
||||||
|
final VerifyTokenPluginPlatform mock =
|
||||||
|
ImplementsVerifyTokenPluginPlatformUsingMockPlatformInterfaceMixin();
|
||||||
|
VerifyTokenPluginPlatform.instance = mock;
|
||||||
|
});
|
||||||
|
|
||||||
|
test('allows extending', () {
|
||||||
|
VerifyTokenPluginPlatform.instance = ExtendsVerifyTokenPluginPlatform();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('does not prevent `const Object()` token', () {
|
||||||
|
ConstVerifyTokenPluginPlatform.instance =
|
||||||
|
ImplementsConstVerifyTokenPluginPlatform();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Loading…
Reference in a new issue