initial commit

This commit is contained in:
Niels Gorter 2022-10-07 12:16:17 +02:00
commit e348c921d5
9 changed files with 360 additions and 0 deletions

30
.gitignore vendored Normal file
View 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
View 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
View file

@ -0,0 +1,3 @@
## 1.0.0
- Initial release

21
LICENSE Normal file
View 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
View file

@ -0,0 +1,15 @@
[![style: effective dart](https://img.shields.io/badge/style-effective_dart-40c4ff.svg)](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
View 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

View 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
View 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:

View 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();
});
});
}