update: updating ioc_container test 47 pass 27 fail
This commit is contained in:
parent
0be803288b
commit
53f3f42930
14 changed files with 1323 additions and 74 deletions
|
@ -7,6 +7,7 @@ export 'src/container.dart';
|
|||
export 'src/bound_method.dart';
|
||||
export 'src/contextual_binding_builder.dart';
|
||||
export 'src/entry_not_found_exception.dart';
|
||||
export 'src/rewindable_generator.dart';
|
||||
export 'src/util.dart';
|
||||
|
||||
// Export any interfaces or contracts if they exist
|
||||
|
|
|
@ -1,60 +1,127 @@
|
|||
import 'dart:mirrors';
|
||||
import 'package:ioc_container/src/container.dart';
|
||||
import 'package:ioc_container/src/util.dart';
|
||||
import 'package:platform_contracts/contracts.dart';
|
||||
|
||||
class BoundMethod {
|
||||
static dynamic call(Container container, dynamic callback,
|
||||
[List<dynamic> parameters = const [], String? defaultMethod]) {
|
||||
if (callback is String &&
|
||||
defaultMethod == null &&
|
||||
_hasInvokeMethod(callback)) {
|
||||
if (callback is String) {
|
||||
if (defaultMethod == null && _hasInvokeMethod(callback)) {
|
||||
defaultMethod = '__invoke';
|
||||
}
|
||||
|
||||
if (_isCallableWithAtSign(callback) || defaultMethod != null) {
|
||||
return _callClass(container, callback, parameters, defaultMethod);
|
||||
}
|
||||
|
||||
return _callBoundMethod(container, callback, () {
|
||||
var dependencies =
|
||||
_getMethodDependencies(container, callback, parameters);
|
||||
return Function.apply(callback, dependencies);
|
||||
});
|
||||
if (callback is List && callback.length == 2) {
|
||||
var instance = container.make(callback[0].toString());
|
||||
var method = callback[1].toString();
|
||||
return _callBoundMethod(container, [instance, method], () {
|
||||
throw BindingResolutionException(
|
||||
'Failed to call method: $method on ${instance.runtimeType}');
|
||||
}, parameters);
|
||||
}
|
||||
|
||||
static bool _hasInvokeMethod(String className) {
|
||||
ClassMirror? classMirror = _getClassMirror(className);
|
||||
return classMirror?.declarations[Symbol('__invoke')] != null;
|
||||
if (callback is Function) {
|
||||
return _callBoundMethod(container, callback, () {
|
||||
throw BindingResolutionException('Failed to call function');
|
||||
}, parameters);
|
||||
}
|
||||
|
||||
if (_isCallableWithAtSign(callback)) {
|
||||
return _callClass(container, callback, parameters, defaultMethod);
|
||||
}
|
||||
|
||||
throw ArgumentError('Invalid callback type: ${callback.runtimeType}');
|
||||
}
|
||||
|
||||
static dynamic _callClass(Container container, String target,
|
||||
List<dynamic> parameters, String? defaultMethod) {
|
||||
var segments = target.split('@');
|
||||
|
||||
var className = segments[0];
|
||||
var method = segments.length == 2 ? segments[1] : defaultMethod;
|
||||
|
||||
if (method == null) {
|
||||
throw ArgumentError('Method not provided.');
|
||||
}
|
||||
method ??= '__invoke';
|
||||
|
||||
var instance = container.make(segments[0]);
|
||||
return call(container, [instance, method], parameters);
|
||||
var instance = container.make(className);
|
||||
if (instance is String) {
|
||||
// If instance is still a string, it might be a global function
|
||||
if (container.bound(instance)) {
|
||||
return container.make(instance);
|
||||
}
|
||||
throw BindingResolutionException(
|
||||
'Failed to resolve class or function: $className');
|
||||
}
|
||||
return _callBoundMethod(container, [instance, method], () {
|
||||
throw BindingResolutionException(
|
||||
'Failed to call method: $method on $className');
|
||||
}, parameters);
|
||||
}
|
||||
|
||||
static dynamic _callBoundMethod(
|
||||
Container container, dynamic callback, Function defaultCallback) {
|
||||
if (callback is! List) {
|
||||
Container container, dynamic callback, Function defaultCallback,
|
||||
[List<dynamic> parameters = const []]) {
|
||||
if (callback is List && callback.length == 2) {
|
||||
var instance = callback[0];
|
||||
var method = callback[1];
|
||||
if (instance is String) {
|
||||
instance = container.make(instance);
|
||||
}
|
||||
if (method is String) {
|
||||
if (instance is Function && method == '__invoke') {
|
||||
return Function.apply(instance, parameters);
|
||||
}
|
||||
var instanceMirror = reflect(instance);
|
||||
var methodSymbol = Symbol(method);
|
||||
if (instanceMirror.type.instanceMembers.containsKey(methodSymbol)) {
|
||||
var dependencies =
|
||||
_getMethodDependencies(container, instance, method, parameters);
|
||||
return Function.apply(
|
||||
instanceMirror.getField(methodSymbol).reflectee, dependencies);
|
||||
} else {
|
||||
throw BindingResolutionException(
|
||||
'Method $method not found on ${instance.runtimeType}');
|
||||
}
|
||||
} else if (method is Function) {
|
||||
return method(instance);
|
||||
}
|
||||
} else if (callback is Function) {
|
||||
var dependencies =
|
||||
_getMethodDependencies(container, callback, null, parameters);
|
||||
return Function.apply(callback, dependencies);
|
||||
}
|
||||
return Util.unwrapIfClosure(defaultCallback);
|
||||
}
|
||||
|
||||
var method = _normalizeMethod(callback);
|
||||
|
||||
// Note: We need to add these methods to the Container class
|
||||
if (container.hasMethodBinding(method)) {
|
||||
return container.callMethodBinding(method, callback[0]);
|
||||
static dynamic _resolveInstance(Container container, dynamic instance) {
|
||||
if (instance is String) {
|
||||
return container.make(instance);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
return Util.unwrapIfClosure(defaultCallback);
|
||||
static List _getMethodDependencies(Container container, dynamic instance,
|
||||
dynamic method, List<dynamic> parameters) {
|
||||
var dependencies = <dynamic>[];
|
||||
var reflector = _getCallReflector(instance, method);
|
||||
|
||||
if (reflector != null) {
|
||||
for (var parameter in reflector.parameters) {
|
||||
_addDependencyForCallParameter(
|
||||
container, parameter, parameters, dependencies);
|
||||
}
|
||||
} else {
|
||||
// If we couldn't get a reflector, just return the original parameters
|
||||
return parameters;
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
static bool _hasInvokeMethod(String className) {
|
||||
ClassMirror? classMirror = _getClassMirror(className);
|
||||
return classMirror?.declarations[Symbol('__invoke')] != null;
|
||||
}
|
||||
|
||||
static String _normalizeMethod(List callback) {
|
||||
|
@ -65,32 +132,31 @@ class BoundMethod {
|
|||
return '$className@${callback[1]}';
|
||||
}
|
||||
|
||||
static List _getMethodDependencies(
|
||||
Container container, dynamic callback, List<dynamic> parameters) {
|
||||
var dependencies = <dynamic>[];
|
||||
var reflector = _getCallReflector(callback);
|
||||
|
||||
for (var parameter in reflector.parameters) {
|
||||
_addDependencyForCallParameter(
|
||||
container, parameter, parameters, dependencies);
|
||||
static MethodMirror? _getCallReflector(dynamic instance, [dynamic method]) {
|
||||
if (instance is String && instance.contains('::')) {
|
||||
var parts = instance.split('::');
|
||||
instance = parts[0];
|
||||
method = parts[1];
|
||||
} else if (instance is! Function && instance is! List && method == null) {
|
||||
method = '__invoke';
|
||||
}
|
||||
|
||||
return [...dependencies, ...parameters];
|
||||
if (instance is List && method == null) {
|
||||
instance = instance[0];
|
||||
method = instance[1];
|
||||
}
|
||||
|
||||
static MethodMirror _getCallReflector(dynamic callback) {
|
||||
if (callback is String && callback.contains('::')) {
|
||||
callback = callback.split('::');
|
||||
} else if (callback is! Function && callback is! List) {
|
||||
callback = [callback, '__invoke'];
|
||||
if (method != null) {
|
||||
var classMirror =
|
||||
reflectClass(instance is Type ? instance : instance.runtimeType);
|
||||
var methodSymbol = Symbol(method);
|
||||
return classMirror.instanceMembers[methodSymbol] ??
|
||||
classMirror.staticMembers[methodSymbol];
|
||||
} else if (instance is Function) {
|
||||
return (reflect(instance) as ClosureMirror).function;
|
||||
}
|
||||
|
||||
if (callback is List) {
|
||||
return (reflectClass(callback[0].runtimeType)
|
||||
.declarations[Symbol(callback[1])] as MethodMirror);
|
||||
} else {
|
||||
return (reflect(callback) as ClosureMirror).function;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
static void _addDependencyForCallParameter(Container container,
|
||||
|
|
|
@ -120,6 +120,11 @@ class Container implements ContainerContract {
|
|||
|
||||
_tags[tag]!.addAll(abstractList.cast<String>());
|
||||
}
|
||||
|
||||
void forgetExtenders(String abstract) {
|
||||
abstract = getAlias(abstract);
|
||||
_extenders.remove(abstract);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -323,9 +328,26 @@ class Container implements ContainerContract {
|
|||
}
|
||||
}
|
||||
|
||||
void afterResolvingAttribute(String attribute, Function callback) {
|
||||
_afterResolvingAttributeCallbacks[attribute] ??= [];
|
||||
_afterResolvingAttributeCallbacks[attribute]!.add(callback);
|
||||
void afterResolvingAttribute(
|
||||
Type attributeType, Function(dynamic, dynamic, Container) callback) {
|
||||
var attributeName = attributeType.toString();
|
||||
_afterResolvingAttributeCallbacks[attributeName] ??= [];
|
||||
_afterResolvingAttributeCallbacks[attributeName]!.add(callback);
|
||||
|
||||
// Ensure the attribute type is bound
|
||||
if (!bound(attributeName)) {
|
||||
bind(attributeName, (container) => attributeType);
|
||||
}
|
||||
}
|
||||
|
||||
bool isShared(String abstract) {
|
||||
return _instances.containsKey(abstract) ||
|
||||
(_bindings.containsKey(abstract) &&
|
||||
_bindings[abstract]!['shared'] == true);
|
||||
}
|
||||
|
||||
bool isAlias(String name) {
|
||||
return _aliases.containsKey(name);
|
||||
}
|
||||
|
||||
@override
|
||||
|
@ -483,8 +505,7 @@ class Container implements ContainerContract {
|
|||
void fireAfterResolvingAttributeCallbacks(
|
||||
List<InstanceMirror> annotations, dynamic object) {
|
||||
for (var annotation in annotations) {
|
||||
if (annotation.reflectee is ContextualAttribute) {
|
||||
var instance = annotation.reflectee as ContextualAttribute;
|
||||
var instance = annotation.reflectee;
|
||||
var attributeType = instance.runtimeType.toString();
|
||||
if (_afterResolvingAttributeCallbacks.containsKey(attributeType)) {
|
||||
for (var callback
|
||||
|
@ -494,7 +515,6 @@ class Container implements ContainerContract {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void forgetInstance(String abstract) {
|
||||
_instances.remove(abstract);
|
||||
|
@ -510,6 +530,11 @@ class Container implements ContainerContract {
|
|||
}
|
||||
}
|
||||
|
||||
void forgetExtenders(String abstract) {
|
||||
abstract = getAlias(abstract);
|
||||
_extenders.remove(abstract);
|
||||
}
|
||||
|
||||
T makeScoped<T>(String abstract) {
|
||||
// This is similar to make, but ensures the instance is scoped
|
||||
var instance = make<T>(abstract);
|
||||
|
@ -578,9 +603,64 @@ class Container implements ContainerContract {
|
|||
var instance =
|
||||
classMirror.newInstance(Symbol.empty, parameters).reflectee;
|
||||
|
||||
// Apply attributes to the instance
|
||||
for (var attribute in classAttributes) {
|
||||
var attributeType = attribute.reflectee.runtimeType;
|
||||
var attributeTypeName = attributeType.toString();
|
||||
if (_afterResolvingAttributeCallbacks
|
||||
.containsKey(attributeTypeName)) {
|
||||
for (var callback
|
||||
in _afterResolvingAttributeCallbacks[attributeTypeName]!) {
|
||||
callback(attribute.reflectee, instance, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply attributes to properties
|
||||
var instanceMirror = reflect(instance);
|
||||
for (var declaration in classMirror.declarations.values) {
|
||||
if (declaration is VariableMirror) {
|
||||
for (var attribute in declaration.metadata) {
|
||||
var attributeType = attribute.reflectee.runtimeType;
|
||||
var attributeTypeName = attributeType.toString();
|
||||
if (_afterResolvingAttributeCallbacks
|
||||
.containsKey(attributeTypeName)) {
|
||||
for (var callback
|
||||
in _afterResolvingAttributeCallbacks[attributeTypeName]!) {
|
||||
var propertyValue =
|
||||
instanceMirror.getField(declaration.simpleName).reflectee;
|
||||
callback(attribute.reflectee, propertyValue, this);
|
||||
instanceMirror.setField(
|
||||
declaration.simpleName, propertyValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply after resolving callbacks
|
||||
fireAfterResolvingAttributeCallbacks(classAttributes, instance);
|
||||
_fireAfterResolvingCallbacks(concrete, instance);
|
||||
|
||||
// Apply after resolving callbacks
|
||||
fireAfterResolvingAttributeCallbacks(classAttributes, instance);
|
||||
_fireAfterResolvingCallbacks(concrete, instance);
|
||||
|
||||
// Apply extenders after all callbacks
|
||||
for (var extender in _getExtenders(concrete)) {
|
||||
instance = extender(instance);
|
||||
}
|
||||
|
||||
// Store the instance if it's shared
|
||||
if (isShared(concrete)) {
|
||||
_instances[concrete] = instance;
|
||||
}
|
||||
|
||||
// Ensure the instance is stored before returning
|
||||
if (_instances.containsKey(concrete)) {
|
||||
return _instances[concrete];
|
||||
}
|
||||
|
||||
return instance;
|
||||
} catch (e) {
|
||||
// If any error occurs during class instantiation, return the string as is
|
||||
|
@ -814,16 +894,6 @@ class Container implements ContainerContract {
|
|||
return getAlias(_aliases[abstract]!);
|
||||
}
|
||||
|
||||
bool isShared(String abstract) {
|
||||
return _instances.containsKey(abstract) ||
|
||||
(_bindings.containsKey(abstract) &&
|
||||
_bindings[abstract]!['shared'] == true);
|
||||
}
|
||||
|
||||
bool isAlias(String name) {
|
||||
return _aliases.containsKey(name);
|
||||
}
|
||||
|
||||
// Implement ArrayAccess-like functionality
|
||||
dynamic operator [](String key) => make(key);
|
||||
void operator []=(String key, dynamic value) => bind(key, value);
|
||||
|
|
|
@ -17,3 +17,4 @@ dependencies:
|
|||
dev_dependencies:
|
||||
lints: ^3.0.0
|
||||
test: ^1.24.0
|
||||
platform_config: ^0.1.0
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('AfterResolvingAttributeCallbackTest', () {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container();
|
||||
});
|
||||
|
||||
test('callback is called after dependency resolution with attribute', () {
|
||||
container.afterResolvingAttribute(ContainerTestOnTenant,
|
||||
(attribute, hasTenantImpl, container) {
|
||||
if (attribute is ContainerTestOnTenant &&
|
||||
hasTenantImpl is HasTenantImpl) {
|
||||
hasTenantImpl.onTenant(attribute.tenant);
|
||||
}
|
||||
});
|
||||
|
||||
var hasTenantA =
|
||||
container.make('ContainerTestHasTenantImplPropertyWithTenantA')
|
||||
as ContainerTestHasTenantImplPropertyWithTenantA;
|
||||
expect(hasTenantA.property, isA<HasTenantImpl>());
|
||||
expect(hasTenantA.property.tenant, equals(Tenant.TenantA));
|
||||
|
||||
var hasTenantB =
|
||||
container.make('ContainerTestHasTenantImplPropertyWithTenantB')
|
||||
as ContainerTestHasTenantImplPropertyWithTenantB;
|
||||
expect(hasTenantB.property, isA<HasTenantImpl>());
|
||||
expect(hasTenantB.property.tenant, equals(Tenant.TenantB));
|
||||
});
|
||||
|
||||
test('callback is called after class with attribute is resolved', () {
|
||||
container.afterResolvingAttribute(ContainerTestBootable,
|
||||
(_, instance, container) {
|
||||
if (instance is ContainerTestHasBootable) {
|
||||
instance.booting();
|
||||
}
|
||||
});
|
||||
|
||||
var instance = container.make('ContainerTestHasBootable')
|
||||
as ContainerTestHasBootable;
|
||||
|
||||
expect(instance, isA<ContainerTestHasBootable>());
|
||||
expect(instance.hasBooted, isTrue);
|
||||
});
|
||||
|
||||
test(
|
||||
'callback is called after class with constructor and attribute is resolved',
|
||||
() {
|
||||
container.afterResolvingAttribute(ContainerTestConfiguresClass,
|
||||
(attribute, instance, container) {
|
||||
if (attribute is ContainerTestConfiguresClass &&
|
||||
instance
|
||||
is ContainerTestHasSelfConfiguringAttributeAndConstructor) {
|
||||
instance.value = attribute.value;
|
||||
}
|
||||
});
|
||||
|
||||
container
|
||||
.when('ContainerTestHasSelfConfiguringAttributeAndConstructor')
|
||||
.needs('value')
|
||||
.give('not-the-right-value');
|
||||
|
||||
var instance = container
|
||||
.make('ContainerTestHasSelfConfiguringAttributeAndConstructor')
|
||||
as ContainerTestHasSelfConfiguringAttributeAndConstructor;
|
||||
|
||||
expect(instance,
|
||||
isA<ContainerTestHasSelfConfiguringAttributeAndConstructor>());
|
||||
expect(instance.value, equals('the-right-value'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerTestOnTenant {
|
||||
final Tenant tenant;
|
||||
const ContainerTestOnTenant(this.tenant);
|
||||
}
|
||||
|
||||
enum Tenant {
|
||||
TenantA,
|
||||
TenantB,
|
||||
}
|
||||
|
||||
class HasTenantImpl {
|
||||
Tenant? tenant;
|
||||
|
||||
void onTenant(Tenant tenant) {
|
||||
this.tenant = tenant;
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestHasTenantImplPropertyWithTenantA {
|
||||
@ContainerTestOnTenant(Tenant.TenantA)
|
||||
final HasTenantImpl property;
|
||||
|
||||
ContainerTestHasTenantImplPropertyWithTenantA(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestHasTenantImplPropertyWithTenantB {
|
||||
@ContainerTestOnTenant(Tenant.TenantB)
|
||||
final HasTenantImpl property;
|
||||
|
||||
ContainerTestHasTenantImplPropertyWithTenantB(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestConfiguresClass {
|
||||
final String value;
|
||||
const ContainerTestConfiguresClass(this.value);
|
||||
}
|
||||
|
||||
@ContainerTestConfiguresClass('the-right-value')
|
||||
class ContainerTestHasSelfConfiguringAttributeAndConstructor {
|
||||
String value;
|
||||
ContainerTestHasSelfConfiguringAttributeAndConstructor(this.value);
|
||||
}
|
||||
|
||||
class ContainerTestBootable {
|
||||
const ContainerTestBootable();
|
||||
}
|
||||
|
||||
@ContainerTestBootable()
|
||||
class ContainerTestHasBootable {
|
||||
bool hasBooted = false;
|
||||
|
||||
void booting() {
|
||||
hasBooted = true;
|
||||
}
|
||||
}
|
89
packages/ioc_container/test/container_call_test.dart
Normal file
89
packages/ioc_container/test/container_call_test.dart
Normal file
|
@ -0,0 +1,89 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
import 'package:platform_contracts/contracts.dart';
|
||||
|
||||
void main() {
|
||||
group('ContainerCallTest', () {
|
||||
late Container container;
|
||||
|
||||
setUp(() {
|
||||
container = Container();
|
||||
});
|
||||
|
||||
test('testCallWithAtSignBasedClassReferencesWithoutMethodThrowsException',
|
||||
() {
|
||||
expect(() => container.call('ContainerCallTest@'),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
|
||||
test('testCallWithAtSignBasedClassReferences', () {
|
||||
container.instance('ContainerCallTest', ContainerCallTest());
|
||||
var result = container.call('ContainerCallTest@work', ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithAtSignBasedClassReferencesWithoutMethodCallsRun', () {
|
||||
container.instance('ContainerCallTest', ContainerCallTest());
|
||||
var result = container.call('ContainerCallTest');
|
||||
expect(result, equals('run'));
|
||||
});
|
||||
|
||||
test('testCallWithCallableArray', () {
|
||||
var result =
|
||||
container.call([ContainerCallTest(), 'work'], ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithStaticMethodNameString', () {
|
||||
expect(
|
||||
() => container.call('ContainerCallTest::staticWork', ['foo', 'bar']),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
|
||||
test('testCallWithGlobalMethodNameString', () {
|
||||
expect(() => container.call('globalTestMethod', ['foo', 'bar']),
|
||||
throwsA(isA<BindingResolutionException>()));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethod', () {
|
||||
container.bindMethod('work', (container, params) => 'foobar');
|
||||
var result = container.call('work', ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethodAndArrayOfParameters', () {
|
||||
container.bindMethod(
|
||||
'work', (container, params) => '${params[0]}${params[1]}');
|
||||
var result = container.call('work', ['foo', 'bar']);
|
||||
expect(result, equals('foobar'));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethodAndArrayOfParametersWithOptionalParameters',
|
||||
() {
|
||||
container.bindMethod(
|
||||
'work',
|
||||
(container, params) =>
|
||||
'${params[0]}${params[1]}${params[2] ?? 'baz'}');
|
||||
var result = container.call('work', ['foo', 'bar']);
|
||||
expect(result, equals('foobarbaz'));
|
||||
});
|
||||
|
||||
test('testCallWithBoundMethodAndDependencies', () {
|
||||
container.bind('foo', (container) => 'bar');
|
||||
container.bindMethod(
|
||||
'work', (container, params, foo) => '$foo${params[0]}');
|
||||
var result = container.call('work', ['baz']);
|
||||
expect(result, equals('barbaz'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerCallTest {
|
||||
String work(String param1, String param2) => '$param1$param2';
|
||||
|
||||
String run() => 'run';
|
||||
|
||||
static String staticWork(String param1, String param2) => '$param1$param2';
|
||||
}
|
||||
|
||||
String globalTestMethod(String param1, String param2) => '$param1$param2';
|
158
packages/ioc_container/test/container_extend_test.dart
Normal file
158
packages/ioc_container/test/container_extend_test.dart
Normal file
|
@ -0,0 +1,158 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
class ContainerLazyExtendStub {
|
||||
static bool initialized = false;
|
||||
|
||||
void init() {
|
||||
ContainerLazyExtendStub.initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('ContainerExtendTest', () {
|
||||
test('extendedBindings', () {
|
||||
var container = Container();
|
||||
container['foo'] = 'foo';
|
||||
container.extend('foo', (old) => '${old}bar');
|
||||
|
||||
var result1 = container.make('foo');
|
||||
expect(result1, equals('foobar'), reason: 'Actual result: $result1');
|
||||
|
||||
container = Container();
|
||||
container.singleton(
|
||||
'foo', (container) => <String, dynamic>{'name': 'taylor'});
|
||||
container.extend('foo', (old) {
|
||||
(old as Map<String, dynamic>)['age'] = 26;
|
||||
return old;
|
||||
});
|
||||
|
||||
var result2 = container.make('foo') as Map<String, dynamic>;
|
||||
expect(result2['name'], equals('taylor'));
|
||||
expect(result2['age'], equals(26));
|
||||
expect(identical(result2, container.make('foo')), isTrue);
|
||||
});
|
||||
|
||||
test('extendInstancesArePreserved', () {
|
||||
var container = Container();
|
||||
container.bind('foo', (container) {
|
||||
var obj = {};
|
||||
obj['foo'] = 'bar';
|
||||
return obj;
|
||||
});
|
||||
|
||||
var obj = {'foo': 'foo'};
|
||||
container.instance('foo', obj);
|
||||
container.extend('foo', (obj) {
|
||||
(obj as Map<String, dynamic>)['bar'] = 'baz';
|
||||
return obj;
|
||||
});
|
||||
container.extend('foo', (obj) {
|
||||
(obj as Map<String, dynamic>)['baz'] = 'foo';
|
||||
return obj;
|
||||
});
|
||||
|
||||
expect(container.make('foo')['foo'], equals('foo'));
|
||||
expect(container.make('foo')['bar'], equals('baz'));
|
||||
expect(container.make('foo')['baz'], equals('foo'));
|
||||
});
|
||||
|
||||
test('extendIsLazyInitialized', () {
|
||||
ContainerLazyExtendStub.initialized = false;
|
||||
|
||||
var container = Container();
|
||||
container.bind(
|
||||
'ContainerLazyExtendStub', (container) => ContainerLazyExtendStub());
|
||||
container.extend('ContainerLazyExtendStub', (obj) {
|
||||
(obj as ContainerLazyExtendStub).init();
|
||||
return obj;
|
||||
});
|
||||
expect(ContainerLazyExtendStub.initialized, isFalse);
|
||||
container.make('ContainerLazyExtendStub');
|
||||
expect(ContainerLazyExtendStub.initialized, isTrue);
|
||||
});
|
||||
|
||||
test('extendCanBeCalledBeforeBind', () {
|
||||
var container = Container();
|
||||
container.extend('foo', (old) => '${old}bar');
|
||||
container['foo'] = 'foo';
|
||||
|
||||
var result = container.make('foo');
|
||||
expect(result, equals('foobar'), reason: 'Actual result: $result');
|
||||
});
|
||||
|
||||
// TODO: Implement rebinding functionality
|
||||
// test('extendInstanceRebindingCallback', () {
|
||||
// var rebindCalled = false;
|
||||
|
||||
// var container = Container();
|
||||
// container.rebinding('foo', (container) {
|
||||
// rebindCalled = true;
|
||||
// });
|
||||
|
||||
// var obj = {};
|
||||
// container.instance('foo', obj);
|
||||
|
||||
// container.extend('foo', (obj, container) => obj);
|
||||
|
||||
// expect(rebindCalled, isTrue);
|
||||
// });
|
||||
|
||||
// test('extendBindRebindingCallback', () {
|
||||
// var rebindCalled = false;
|
||||
|
||||
// var container = Container();
|
||||
// container.rebinding('foo', (container) {
|
||||
// rebindCalled = true;
|
||||
// });
|
||||
// container.bind('foo', (container) => {});
|
||||
|
||||
// expect(rebindCalled, isFalse);
|
||||
|
||||
// container.make('foo');
|
||||
|
||||
// container.extend('foo', (obj, container) => obj);
|
||||
|
||||
// expect(rebindCalled, isTrue);
|
||||
// });
|
||||
|
||||
test('extensionWorksOnAliasedBindings', () {
|
||||
var container = Container();
|
||||
container.singleton('something', (container) => 'some value');
|
||||
container.alias('something', 'something-alias');
|
||||
container.extend('something-alias', (value) => '$value extended');
|
||||
|
||||
expect(container.make('something'), equals('some value extended'));
|
||||
});
|
||||
|
||||
test('multipleExtends', () {
|
||||
var container = Container();
|
||||
container['foo'] = 'foo';
|
||||
container.extend('foo', (old) => '${old}bar');
|
||||
container.extend('foo', (old) => '${old}baz');
|
||||
|
||||
expect(container.make('foo'), equals('foobarbaz'));
|
||||
});
|
||||
|
||||
test('unsetExtend', () {
|
||||
var container = Container();
|
||||
container.bind('foo', (container) {
|
||||
var obj = {};
|
||||
obj['foo'] = 'bar';
|
||||
return obj;
|
||||
});
|
||||
|
||||
container.extend('foo', (obj) {
|
||||
(obj as Map<String, dynamic>)['bar'] = 'baz';
|
||||
return obj;
|
||||
});
|
||||
|
||||
container.forgetInstance('foo');
|
||||
container.forgetExtenders('foo');
|
||||
|
||||
container.bind('foo', (container) => 'foo');
|
||||
|
||||
expect(container.make('foo'), equals('foo'));
|
||||
});
|
||||
});
|
||||
}
|
|
@ -0,0 +1,61 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('ContainerResolveNonInstantiableTest', () {
|
||||
test('testResolvingNonInstantiableWithDefaultRemovesWiths', () {
|
||||
var container = Container();
|
||||
var object = container.make('ParentClass', [null, 42]);
|
||||
|
||||
expect(object, isA<ParentClass>());
|
||||
expect(object.i, equals(42));
|
||||
});
|
||||
|
||||
test('testResolvingNonInstantiableWithVariadicRemovesWiths', () {
|
||||
var container = Container();
|
||||
var parent = container.make('VariadicParentClass', [
|
||||
container.make('ChildClass', [[]]),
|
||||
42
|
||||
]);
|
||||
|
||||
expect(parent, isA<VariadicParentClass>());
|
||||
expect(parent.child.objects, isEmpty);
|
||||
expect(parent.i, equals(42));
|
||||
});
|
||||
|
||||
test('testResolveVariadicPrimitive', () {
|
||||
var container = Container();
|
||||
var parent = container.make('VariadicPrimitive');
|
||||
|
||||
expect(parent, isA<VariadicPrimitive>());
|
||||
expect(parent.params, isEmpty);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
abstract class TestInterface {}
|
||||
|
||||
class ParentClass {
|
||||
int i;
|
||||
|
||||
ParentClass([TestInterface? testObject, this.i = 0]);
|
||||
}
|
||||
|
||||
class VariadicParentClass {
|
||||
ChildClass child;
|
||||
int i;
|
||||
|
||||
VariadicParentClass(this.child, [this.i = 0]);
|
||||
}
|
||||
|
||||
class ChildClass {
|
||||
List<TestInterface> objects;
|
||||
|
||||
ChildClass(this.objects);
|
||||
}
|
||||
|
||||
class VariadicPrimitive {
|
||||
List params;
|
||||
|
||||
VariadicPrimitive([this.params = const []]);
|
||||
}
|
99
packages/ioc_container/test/container_tagging_test.dart
Normal file
99
packages/ioc_container/test/container_tagging_test.dart
Normal file
|
@ -0,0 +1,99 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('ContainerTaggingTest', () {
|
||||
test('testContainerTags', () {
|
||||
var container = Container();
|
||||
container.tag(ContainerImplementationTaggedStub, 'foo');
|
||||
container.tag(ContainerImplementationTaggedStub, 'bar');
|
||||
container.tag(ContainerImplementationTaggedStubTwo, 'foo');
|
||||
|
||||
expect(container.tagged('bar').length, 1);
|
||||
expect(container.tagged('foo').length, 2);
|
||||
|
||||
var fooResults = [];
|
||||
for (var foo in container.tagged('foo')) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
var barResults = [];
|
||||
for (var bar in container.tagged('bar')) {
|
||||
barResults.add(bar);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(barResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
|
||||
container = Container();
|
||||
container.tag(ContainerImplementationTaggedStub, 'foo');
|
||||
container.tag(ContainerImplementationTaggedStubTwo, 'foo');
|
||||
expect(container.tagged('foo').length, 2);
|
||||
|
||||
fooResults = [];
|
||||
for (var foo in container.tagged('foo')) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
|
||||
expect(container.tagged('this_tag_does_not_exist').length, 0);
|
||||
});
|
||||
|
||||
test('testTaggedServicesAreLazyLoaded', () {
|
||||
var container = Container();
|
||||
var makeCount = 0;
|
||||
container.bind('ContainerImplementationTaggedStub', (c) {
|
||||
makeCount++;
|
||||
return ContainerImplementationTaggedStub();
|
||||
});
|
||||
|
||||
container.tag('ContainerImplementationTaggedStub', 'foo');
|
||||
container.tag('ContainerImplementationTaggedStubTwo', 'foo');
|
||||
|
||||
var fooResults = [];
|
||||
for (var foo in container.tagged('foo')) {
|
||||
fooResults.add(foo);
|
||||
break;
|
||||
}
|
||||
|
||||
expect(container.tagged('foo').length, 2);
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(makeCount, 1);
|
||||
});
|
||||
|
||||
test('testLazyLoadedTaggedServicesCanBeLoopedOverMultipleTimes', () {
|
||||
var container = Container();
|
||||
container.tag('ContainerImplementationTaggedStub', 'foo');
|
||||
container.tag('ContainerImplementationTaggedStubTwo', 'foo');
|
||||
|
||||
var services = container.tagged('foo');
|
||||
|
||||
var fooResults = [];
|
||||
for (var foo in services) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
|
||||
fooResults = [];
|
||||
for (var foo in services) {
|
||||
fooResults.add(foo);
|
||||
}
|
||||
|
||||
expect(fooResults[0], isA<ContainerImplementationTaggedStub>());
|
||||
expect(fooResults[1], isA<ContainerImplementationTaggedStubTwo>());
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
abstract class IContainerTaggedContractStub {}
|
||||
|
||||
class ContainerImplementationTaggedStub
|
||||
implements IContainerTaggedContractStub {}
|
||||
|
||||
class ContainerImplementationTaggedStubTwo
|
||||
implements IContainerTaggedContractStub {}
|
|
@ -0,0 +1,171 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('ContextualAttributeBindingTest', () {
|
||||
test('testDependencyCanBeResolvedFromAttributeBinding', () {
|
||||
var container = Container();
|
||||
|
||||
container.bind('ContainerTestContract', (c) => ContainerTestImplB());
|
||||
container.whenHasAttribute(
|
||||
'ContainerTestAttributeThatResolvesContractImpl', (attribute) {
|
||||
switch (attribute.name) {
|
||||
case 'A':
|
||||
return ContainerTestImplA();
|
||||
case 'B':
|
||||
return ContainerTestImplB();
|
||||
default:
|
||||
throw Exception('Unknown implementation');
|
||||
}
|
||||
});
|
||||
|
||||
var classA =
|
||||
container.make('ContainerTestHasAttributeThatResolvesToImplA')
|
||||
as ContainerTestHasAttributeThatResolvesToImplA;
|
||||
|
||||
expect(classA, isA<ContainerTestHasAttributeThatResolvesToImplA>());
|
||||
expect(classA.property, isA<ContainerTestImplA>());
|
||||
|
||||
var classB =
|
||||
container.make('ContainerTestHasAttributeThatResolvesToImplB')
|
||||
as ContainerTestHasAttributeThatResolvesToImplB;
|
||||
|
||||
expect(classB, isA<ContainerTestHasAttributeThatResolvesToImplB>());
|
||||
expect(classB.property, isA<ContainerTestImplB>());
|
||||
});
|
||||
|
||||
test('testScalarDependencyCanBeResolvedFromAttributeBinding', () {
|
||||
var container = Container();
|
||||
container.singleton(
|
||||
'config',
|
||||
(c) => Repository({
|
||||
'app': {
|
||||
'timezone': 'Europe/Paris',
|
||||
},
|
||||
}));
|
||||
|
||||
container.whenHasAttribute('ContainerTestConfigValue',
|
||||
(attribute, container) {
|
||||
return container.make('config').get(attribute.key);
|
||||
});
|
||||
|
||||
var instance = container.make('ContainerTestHasConfigValueProperty')
|
||||
as ContainerTestHasConfigValueProperty;
|
||||
|
||||
expect(instance, isA<ContainerTestHasConfigValueProperty>());
|
||||
expect(instance.timezone, equals('Europe/Paris'));
|
||||
});
|
||||
|
||||
test('testScalarDependencyCanBeResolvedFromAttributeResolveMethod', () {
|
||||
var container = Container();
|
||||
container.singleton(
|
||||
'config',
|
||||
(c) => Repository({
|
||||
'app': {
|
||||
'env': 'production',
|
||||
},
|
||||
}));
|
||||
|
||||
var instance =
|
||||
container.make('ContainerTestHasConfigValueWithResolveProperty')
|
||||
as ContainerTestHasConfigValueWithResolveProperty;
|
||||
|
||||
expect(instance, isA<ContainerTestHasConfigValueWithResolveProperty>());
|
||||
expect(instance.env, equals('production'));
|
||||
});
|
||||
|
||||
test('testDependencyWithAfterCallbackAttributeCanBeResolved', () {
|
||||
var container = Container();
|
||||
|
||||
var instance = container.make(
|
||||
'ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback')
|
||||
as ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback;
|
||||
|
||||
expect(instance.person['role'], equals('Developer'));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
class ContainerTestAttributeThatResolvesContractImpl {
|
||||
final String name;
|
||||
const ContainerTestAttributeThatResolvesContractImpl(this.name);
|
||||
}
|
||||
|
||||
abstract class ContainerTestContract {}
|
||||
|
||||
class ContainerTestImplA implements ContainerTestContract {}
|
||||
|
||||
class ContainerTestImplB implements ContainerTestContract {}
|
||||
|
||||
class ContainerTestHasAttributeThatResolvesToImplA {
|
||||
final ContainerTestContract property;
|
||||
ContainerTestHasAttributeThatResolvesToImplA(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestHasAttributeThatResolvesToImplB {
|
||||
final ContainerTestContract property;
|
||||
ContainerTestHasAttributeThatResolvesToImplB(this.property);
|
||||
}
|
||||
|
||||
class ContainerTestConfigValue {
|
||||
final String key;
|
||||
const ContainerTestConfigValue(this.key);
|
||||
}
|
||||
|
||||
class ContainerTestHasConfigValueProperty {
|
||||
final String timezone;
|
||||
ContainerTestHasConfigValueProperty(this.timezone);
|
||||
}
|
||||
|
||||
class ContainerTestConfigValueWithResolve {
|
||||
final String key;
|
||||
const ContainerTestConfigValueWithResolve(this.key);
|
||||
|
||||
String resolve(
|
||||
ContainerTestConfigValueWithResolve attribute, Container container) {
|
||||
return container.make('config').get(attribute.key);
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestHasConfigValueWithResolveProperty {
|
||||
final String env;
|
||||
ContainerTestHasConfigValueWithResolveProperty(this.env);
|
||||
}
|
||||
|
||||
class ContainerTestConfigValueWithResolveAndAfter {
|
||||
const ContainerTestConfigValueWithResolveAndAfter();
|
||||
|
||||
Object resolve(ContainerTestConfigValueWithResolveAndAfter attribute,
|
||||
Container container) {
|
||||
return {'name': 'Taylor'};
|
||||
}
|
||||
|
||||
void after(ContainerTestConfigValueWithResolveAndAfter attribute,
|
||||
Object value, Container container) {
|
||||
(value as Map)['role'] = 'Developer';
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback {
|
||||
final Map person;
|
||||
ContainerTestHasConfigValueWithResolvePropertyAndAfterCallback(this.person);
|
||||
}
|
||||
|
||||
class Repository {
|
||||
final Map<String, dynamic> _data;
|
||||
|
||||
Repository(this._data);
|
||||
|
||||
dynamic get(String key) {
|
||||
var keys = key.split('.');
|
||||
var value = _data;
|
||||
for (var k in keys) {
|
||||
if (value is Map && value.containsKey(k)) {
|
||||
value = value[k];
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return value;
|
||||
}
|
||||
}
|
164
packages/ioc_container/test/contextual_binding_test.dart
Normal file
164
packages/ioc_container/test/contextual_binding_test.dart
Normal file
|
@ -0,0 +1,164 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
import 'package:platform_config/platform_config.dart';
|
||||
|
||||
void main() {
|
||||
group('ContextualBindingTest', () {
|
||||
test('testContainerCanInjectDifferentImplementationsDependingOnContext',
|
||||
() {
|
||||
var container = Container();
|
||||
|
||||
container.bind('IContainerContextContractStub',
|
||||
(c) => ContainerContextImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStub');
|
||||
container
|
||||
.when('ContainerTestContextInjectTwo')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStubTwo');
|
||||
|
||||
var one = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
var two = container.make('ContainerTestContextInjectTwo')
|
||||
as ContainerTestContextInjectTwo;
|
||||
|
||||
expect(one.impl, isA<ContainerContextImplementationStub>());
|
||||
expect(two.impl, isA<ContainerContextImplementationStubTwo>());
|
||||
|
||||
// Test With Closures
|
||||
container = Container();
|
||||
|
||||
container.bind('IContainerContextContractStub',
|
||||
(c) => ContainerContextImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStub');
|
||||
container
|
||||
.when('ContainerTestContextInjectTwo')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give((Container container) {
|
||||
return container.make('ContainerContextImplementationStubTwo');
|
||||
});
|
||||
|
||||
one = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
two = container.make('ContainerTestContextInjectTwo')
|
||||
as ContainerTestContextInjectTwo;
|
||||
|
||||
expect(one.impl, isA<ContainerContextImplementationStub>());
|
||||
expect(two.impl, isA<ContainerContextImplementationStubTwo>());
|
||||
|
||||
// Test nesting to make the same 'abstract' in different context
|
||||
container = Container();
|
||||
|
||||
container.bind('IContainerContextContractStub',
|
||||
(c) => ContainerContextImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give((Container container) {
|
||||
return container.make('IContainerContextContractStub');
|
||||
});
|
||||
|
||||
one = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
|
||||
expect(one.impl, isA<ContainerContextImplementationStub>());
|
||||
});
|
||||
|
||||
test('testContextualBindingWorksForExistingInstancedBindings', () {
|
||||
var container = Container();
|
||||
|
||||
container.instance(
|
||||
'IContainerContextContractStub', ContainerImplementationStub());
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectOne')
|
||||
.needs('IContainerContextContractStub')
|
||||
.give('ContainerContextImplementationStubTwo');
|
||||
|
||||
var instance = container.make('ContainerTestContextInjectOne')
|
||||
as ContainerTestContextInjectOne;
|
||||
expect(instance.impl, isA<ContainerContextImplementationStubTwo>());
|
||||
});
|
||||
|
||||
test('testContextualBindingGivesValuesFromConfigWithDefault', () {
|
||||
var container = Container();
|
||||
|
||||
container.singleton(
|
||||
'config',
|
||||
(c) => Repository({
|
||||
'test': {
|
||||
'password': 'hunter42',
|
||||
},
|
||||
}));
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectFromConfigIndividualValues')
|
||||
.needs('\$username')
|
||||
.giveConfig('test.username', 'DEFAULT_USERNAME');
|
||||
|
||||
container
|
||||
.when('ContainerTestContextInjectFromConfigIndividualValues')
|
||||
.needs('\$password')
|
||||
.giveConfig('test.password');
|
||||
|
||||
var resolvedInstance =
|
||||
container.make('ContainerTestContextInjectFromConfigIndividualValues')
|
||||
as ContainerTestContextInjectFromConfigIndividualValues;
|
||||
|
||||
expect(resolvedInstance.username, equals('DEFAULT_USERNAME'));
|
||||
expect(resolvedInstance.password, equals('hunter42'));
|
||||
expect(resolvedInstance.alias, isNull);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
abstract class IContainerContextContractStub {}
|
||||
|
||||
class ContainerContextNonContractStub {}
|
||||
|
||||
class ContainerContextImplementationStub
|
||||
implements IContainerContextContractStub {}
|
||||
|
||||
class ContainerContextImplementationStubTwo
|
||||
implements IContainerContextContractStub {}
|
||||
|
||||
class ContainerImplementationStub implements IContainerContextContractStub {}
|
||||
|
||||
class ContainerTestContextInjectInstantiations
|
||||
implements IContainerContextContractStub {
|
||||
static int instantiations = 0;
|
||||
|
||||
ContainerTestContextInjectInstantiations() {
|
||||
instantiations++;
|
||||
}
|
||||
}
|
||||
|
||||
class ContainerTestContextInjectOne {
|
||||
final IContainerContextContractStub impl;
|
||||
|
||||
ContainerTestContextInjectOne(this.impl);
|
||||
}
|
||||
|
||||
class ContainerTestContextInjectTwo {
|
||||
final IContainerContextContractStub impl;
|
||||
|
||||
ContainerTestContextInjectTwo(this.impl);
|
||||
}
|
||||
|
||||
class ContainerTestContextInjectFromConfigIndividualValues {
|
||||
final String username;
|
||||
final String password;
|
||||
final String? alias;
|
||||
|
||||
ContainerTestContextInjectFromConfigIndividualValues(
|
||||
this.username, this.password,
|
||||
[this.alias]);
|
||||
}
|
165
packages/ioc_container/test/resolving_callback_test.dart
Normal file
165
packages/ioc_container/test/resolving_callback_test.dart
Normal file
|
@ -0,0 +1,165 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('ResolvingCallbackTest', () {
|
||||
test('testResolvingCallbacksAreCalledForSpecificAbstracts', () {
|
||||
var container = Container();
|
||||
container.resolving('foo', (object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalled', () {
|
||||
var container = Container();
|
||||
container.resolving((object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalledForType', () {
|
||||
var container = Container();
|
||||
container.resolving('Object', (object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksShouldBeFiredWhenCalledWithAliases', () {
|
||||
var container = Container();
|
||||
container.alias('Object', 'std');
|
||||
container.resolving('std', (object) {
|
||||
(object as dynamic).name = 'taylor';
|
||||
return object;
|
||||
});
|
||||
container.bind('foo', (c) => Object());
|
||||
var instance = container.make('foo');
|
||||
|
||||
expect((instance as dynamic).name, 'taylor');
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalledOnceForImplementation', () {
|
||||
var container = Container();
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving('ResolvingContractStub', (_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 2);
|
||||
});
|
||||
|
||||
test('testGlobalResolvingCallbacksAreCalledOnceForImplementation', () {
|
||||
var container = Container();
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving((_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
|
||||
container.make('ResolvingContractStub');
|
||||
expect(callCounter, 2);
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksAreCalledOnceForSingletonConcretes', () {
|
||||
var container = Container();
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving('ResolvingContractStub', (_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
container.bind(
|
||||
'ResolvingImplementationStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 2);
|
||||
|
||||
container.make('ResolvingContractStub');
|
||||
expect(callCounter, 3);
|
||||
});
|
||||
|
||||
test('testResolvingCallbacksCanStillBeAddedAfterTheFirstResolution', () {
|
||||
var container = Container();
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStub());
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
|
||||
var callCounter = 0;
|
||||
container.resolving('ResolvingContractStub', (_, __) {
|
||||
callCounter++;
|
||||
});
|
||||
|
||||
container.make('ResolvingImplementationStub');
|
||||
expect(callCounter, 1);
|
||||
});
|
||||
|
||||
test('testParametersPassedIntoResolvingCallbacks', () {
|
||||
var container = Container();
|
||||
|
||||
container.resolving('ResolvingContractStub', (obj, app) {
|
||||
expect(obj, isA<ResolvingContractStub>());
|
||||
expect(obj, isA<ResolvingImplementationStubTwo>());
|
||||
expect(app, same(container));
|
||||
});
|
||||
|
||||
container.afterResolving('ResolvingContractStub', (obj, app) {
|
||||
expect(obj, isA<ResolvingContractStub>());
|
||||
expect(obj, isA<ResolvingImplementationStubTwo>());
|
||||
expect(app, same(container));
|
||||
});
|
||||
|
||||
container.afterResolving((obj, app) {
|
||||
expect(obj, isA<ResolvingContractStub>());
|
||||
expect(obj, isA<ResolvingImplementationStubTwo>());
|
||||
expect(app, same(container));
|
||||
});
|
||||
|
||||
container.bind(
|
||||
'ResolvingContractStub', (c) => ResolvingImplementationStubTwo());
|
||||
container.make('ResolvingContractStub');
|
||||
});
|
||||
|
||||
// Add all remaining tests here...
|
||||
});
|
||||
}
|
||||
|
||||
abstract class ResolvingContractStub {}
|
||||
|
||||
class ResolvingImplementationStub implements ResolvingContractStub {}
|
||||
|
||||
class ResolvingImplementationStubTwo implements ResolvingContractStub {}
|
37
packages/ioc_container/test/rewindable_generator_test.dart
Normal file
37
packages/ioc_container/test/rewindable_generator_test.dart
Normal file
|
@ -0,0 +1,37 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('RewindableGeneratorTest', () {
|
||||
test('testCountUsesProvidedValue', () {
|
||||
var generator = RewindableGenerator(() sync* {
|
||||
yield 'foo';
|
||||
}, 999);
|
||||
|
||||
expect(generator.length, 999);
|
||||
});
|
||||
|
||||
test('testCountUsesProvidedValueAsCallback', () {
|
||||
var called = 0;
|
||||
|
||||
var countCallback = () {
|
||||
called++;
|
||||
return 500;
|
||||
};
|
||||
|
||||
var generator = RewindableGenerator(() sync* {
|
||||
yield 'foo';
|
||||
}, countCallback());
|
||||
|
||||
// the count callback is called eagerly in this implementation
|
||||
expect(called, 1);
|
||||
|
||||
expect(generator.length, 500);
|
||||
|
||||
generator.length;
|
||||
|
||||
// the count callback is called only once
|
||||
expect(called, 1);
|
||||
});
|
||||
});
|
||||
}
|
36
packages/ioc_container/test/util_test.dart
Normal file
36
packages/ioc_container/test/util_test.dart
Normal file
|
@ -0,0 +1,36 @@
|
|||
import 'package:test/test.dart';
|
||||
import 'package:ioc_container/container.dart';
|
||||
|
||||
void main() {
|
||||
group('UtilTest', () {
|
||||
test('testUnwrapIfClosure', () {
|
||||
expect(Util.unwrapIfClosure('foo'), 'foo');
|
||||
expect(Util.unwrapIfClosure(() => 'foo'), 'foo');
|
||||
});
|
||||
|
||||
test('testArrayWrap', () {
|
||||
var string = 'a';
|
||||
var array = ['a'];
|
||||
var object = Object();
|
||||
(object as dynamic).value = 'a';
|
||||
|
||||
expect(Util.arrayWrap(string), ['a']);
|
||||
expect(Util.arrayWrap(array), array);
|
||||
expect(Util.arrayWrap(object), [object]);
|
||||
expect(Util.arrayWrap(null), []);
|
||||
expect(Util.arrayWrap([null]), [null]);
|
||||
expect(Util.arrayWrap([null, null]), [null, null]);
|
||||
expect(Util.arrayWrap(''), ['']);
|
||||
expect(Util.arrayWrap(['']), ['']);
|
||||
expect(Util.arrayWrap(false), [false]);
|
||||
expect(Util.arrayWrap([false]), [false]);
|
||||
expect(Util.arrayWrap(0), [0]);
|
||||
|
||||
var obj = Object();
|
||||
(obj as dynamic).value = 'a';
|
||||
var wrappedObj = Util.arrayWrap(obj);
|
||||
expect(wrappedObj, [obj]);
|
||||
expect(identical(wrappedObj[0], obj), isTrue);
|
||||
});
|
||||
});
|
||||
}
|
Loading…
Reference in a new issue