From 2ce4607366a05633598fb2a9a75881ba92ad658c Mon Sep 17 00:00:00 2001 From: Patrick Stewart Date: Fri, 27 Dec 2024 09:19:24 -0700 Subject: [PATCH] refactor: add flush() for container reset 167 pass 4 fail --- .../container/lib/src/container.dart | 27 +++ .../container/container/test/flush_test.dart | 222 ++++++++++++++++++ 2 files changed, 249 insertions(+) create mode 100644 incubation/container/container/test/flush_test.dart diff --git a/incubation/container/container/lib/src/container.dart b/incubation/container/container/lib/src/container.dart index 1e513b1..988e461 100644 --- a/incubation/container/container/lib/src/container.dart +++ b/incubation/container/container/lib/src/container.dart @@ -1248,6 +1248,33 @@ class Container { bindIf(concrete, singleton: true); } + /// Reset the container's state + /// + /// This method clears all bindings, aliases, extenders, and callbacks, + /// effectively resetting the container to its initial state. + /// + /// ```dart + /// container.flush(); + /// ``` + void flush() { + _singletons.clear(); + _factories.clear(); + _namedSingletons.clear(); + _aliases.clear(); + _extenders.clear(); + _reboundCallbacks.clear(); + _refreshing.clear(); + _parameterStack.clear(); + _contextual.clear(); + _methodBindings.clear(); + _tags.clear(); + _scopedInstances.clear(); + _beforeResolvingCallbacks.clear(); + _resolvingCallbacks.clear(); + _afterResolvingCallbacks.clear(); + _buildStack.clear(); + } + /// Make an instance with parameters /// /// This is an alias for making an instance while providing parameters. diff --git a/incubation/container/container/test/flush_test.dart b/incubation/container/container/test/flush_test.dart new file mode 100644 index 0000000..326b65a --- /dev/null +++ b/incubation/container/container/test/flush_test.dart @@ -0,0 +1,222 @@ +import 'package:platformed_container/container.dart'; +import 'package:test/test.dart'; + +abstract class Logger { + void log(String message); +} + +class ConsoleLogger implements Logger { + @override + void log(String message) => print('Console: $message'); +} + +void main() { + late Container container; + + setUp(() { + container = Container(MockReflector()); + }); + + group('Flush Tests', () { + test('flush clears all bindings', () { + container.factory(() => ConsoleLogger()); + container.registerSingleton('test'); + container.registerNamedSingleton('logger', ConsoleLogger()); + container.alias(ConsoleLogger); + container.tag([Logger], 'logging'); + + container.flush(); + + expect(container.has(), isFalse); + expect(container.has(), isFalse); + expect(container.hasNamed('logger'), isFalse); + expect(container.isAlias(Logger), isFalse); + expect(container.tagged('logging'), isEmpty); + }); + + test('flush clears callbacks', () { + var beforeResolvingCalled = false; + var resolvingCalled = false; + var afterResolvingCalled = false; + var reboundCalled = false; + + container.beforeResolving((type, args, container) { + beforeResolvingCalled = true; + }); + + container.resolving((instance, container) { + resolvingCalled = true; + }); + + container.afterResolving((instance, container) { + afterResolvingCalled = true; + }); + + container.rebinding((instance, container) { + reboundCalled = true; + }); + + container.flush(); + container.factory(() => ConsoleLogger()); + container.make(); + container.refresh(); + + expect(beforeResolvingCalled, isFalse); + expect(resolvingCalled, isFalse); + expect(afterResolvingCalled, isFalse); + expect(reboundCalled, isFalse); + }); + + test('flush clears contextual bindings', () { + container.bind(Logger).to(ConsoleLogger); + container.flush(); + + expect(() => container.make(), + throwsA(isA())); + }); + + test('flush preserves parent bindings', () { + var parent = Container(MockReflector()); + parent.registerSingleton('parent'); + var child = parent.createChild(); + child.registerSingleton(ConsoleLogger()); + + child.flush(); + + expect(child.has(), isTrue); + expect(child.has(), isFalse); + }); + }); +} + +class MockReflector extends Reflector { + @override + String? getName(Symbol symbol) => null; + + @override + ReflectedClass? reflectClass(Type clazz) { + if (clazz == ConsoleLogger) { + return MockReflectedClass( + 'ConsoleLogger', [], [], [MockConstructor('', [])], ConsoleLogger); + } + return null; + } + + @override + ReflectedType? reflectType(Type type) { + return reflectClass(type); + } + + @override + ReflectedInstance? reflectInstance(Object? instance) => null; + + @override + ReflectedFunction? reflectFunction(Function function) => null; + + @override + ReflectedType reflectFutureOf(Type type) => throw UnimplementedError(); + + @override + Type? findTypeByName(String name) => null; + + @override + ReflectedFunction? findInstanceMethod(Object instance, String methodName) => + null; + + @override + List getAnnotations(Type type) => []; + + @override + List getParameterAnnotations( + Type type, String constructorName, String parameterName) => + []; +} + +class MockConstructor implements ReflectedFunction { + final String constructorName; + final List constructorParameters; + + MockConstructor(this.constructorName, this.constructorParameters); + + @override + List get annotations => []; + + @override + bool get isGetter => false; + + @override + bool get isSetter => false; + + @override + String get name => constructorName; + + @override + List get parameters => constructorParameters; + + @override + ReflectedType? get returnType => null; + + @override + List get typeParameters => []; + + @override + ReflectedInstance invoke(Invocation invocation) => throw UnimplementedError(); +} + +class MockReflectedClass extends ReflectedType implements ReflectedClass { + @override + final List annotations; + @override + final List constructors; + @override + final List declarations; + + MockReflectedClass( + String name, + List typeParameters, + this.annotations, + this.constructors, + Type reflectedType, + ) : declarations = [], + super(reflectedType.toString(), typeParameters, reflectedType); + + @override + ReflectedInstance newInstance( + String constructorName, List positionalArguments, + [Map namedArguments = const {}, + List typeArguments = const []]) { + if (reflectedType == ConsoleLogger) { + return MockReflectedInstance(ConsoleLogger()); + } + throw UnsupportedError('Unknown type: $reflectedType'); + } + + @override + bool isAssignableTo(ReflectedType? other) { + if (reflectedType == other?.reflectedType) { + return true; + } + if (reflectedType == ConsoleLogger && other?.reflectedType == Logger) { + return true; + } + return false; + } +} + +class MockReflectedInstance implements ReflectedInstance { + final dynamic value; + + MockReflectedInstance(this.value); + + @override + ReflectedClass get clazz => throw UnimplementedError(); + + @override + ReflectedInstance getField(String name) => throw UnimplementedError(); + + @override + dynamic get reflectee => value; + + @override + ReflectedType get type => throw UnimplementedError(); +}