-
Notifications
You must be signed in to change notification settings - Fork 53
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
ad0c6ac
commit d3a1afe
Showing
11 changed files
with
271 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,144 @@ | ||
using System; | ||
using System.Collections.Generic; | ||
using FluentAssertions; | ||
using NUnit.Framework; | ||
using Reflex.Core; | ||
using UnityEditor; | ||
|
||
namespace Reflex.EditModeTests | ||
{ | ||
public class ScopedTests | ||
{ | ||
private class Service : IDisposable | ||
{ | ||
public bool IsDisposed { get; private set; } | ||
|
||
public void Dispose() | ||
{ | ||
IsDisposed = true; | ||
} | ||
} | ||
|
||
[Test] | ||
public void ScopedFromType_ShouldReturnAlwaysSameInstance_WhenCalledFromSameContainer() | ||
{ | ||
var parentContainer = new ContainerBuilder().AddScoped(typeof(Service)).Build(); | ||
var childContainer = parentContainer.Scope(); | ||
parentContainer.Resolve<Service>().Should().Be(parentContainer.Resolve<Service>()); | ||
childContainer.Resolve<Service>().Should().Be(childContainer.Resolve<Service>()); | ||
} | ||
|
||
[Test] | ||
public void ScopedFromFactory_ShouldReturnAlwaysSameInstance_WhenCalledFromSameContainer() | ||
{ | ||
var parentContainer = new ContainerBuilder().AddScoped(_ => new Service()).Build(); | ||
var childContainer = parentContainer.Scope(); | ||
parentContainer.Resolve<Service>().Should().Be(parentContainer.Resolve<Service>()); | ||
childContainer.Resolve<Service>().Should().Be(childContainer.Resolve<Service>()); | ||
} | ||
|
||
[Test] | ||
public void ScopedFromType_NewInstanceShouldBeConstructed_ForEveryNewContainer() | ||
{ | ||
var instances = new HashSet<Service>(); | ||
var parentContainer = new ContainerBuilder().AddScoped(typeof(Service)).Build(); | ||
var childContainer = parentContainer.Scope(); | ||
instances.Add(parentContainer.Resolve<Service>()); | ||
instances.Add(childContainer.Resolve<Service>()); | ||
instances.Add(parentContainer.Resolve<Service>()); | ||
instances.Add(childContainer.Resolve<Service>()); | ||
instances.Count.Should().Be(2); | ||
} | ||
|
||
[Test] | ||
public void ScopedFromFactory_NewInstanceShouldBeConstructed_ForEveryNewContainer() | ||
{ | ||
var instances = new HashSet<Service>(); | ||
var parentContainer = new ContainerBuilder().AddScoped(_ => new Service()).Build(); | ||
var childContainer = parentContainer.Scope(); | ||
instances.Add(parentContainer.Resolve<Service>()); | ||
instances.Add(childContainer.Resolve<Service>()); | ||
instances.Add(parentContainer.Resolve<Service>()); | ||
instances.Add(childContainer.Resolve<Service>()); | ||
instances.Count.Should().Be(2); | ||
} | ||
|
||
[Test] | ||
public void ScopedFromType_ConstructedInstances_ShouldBeDisposed_WithinConstructingContainer() | ||
{ | ||
var parentContainer = new ContainerBuilder().AddScoped(typeof(Service)).Build(); | ||
var childContainer = parentContainer.Scope(); | ||
|
||
var instanceConstructedByChild = childContainer.Resolve<Service>(); | ||
var instanceConstructedByParent = parentContainer.Resolve<Service>(); | ||
|
||
childContainer.Dispose(); | ||
|
||
instanceConstructedByChild.IsDisposed.Should().BeTrue(); | ||
instanceConstructedByParent.IsDisposed.Should().BeFalse(); | ||
} | ||
|
||
[Test] | ||
public void ScopedFromFactory_ConstructedInstances_ShouldBeDisposed_WithinConstructingContainer() | ||
{ | ||
var parentContainer = new ContainerBuilder().AddScoped(_ => new Service()).Build(); | ||
var childContainer = parentContainer.Scope(); | ||
|
||
var instanceConstructedByChild = childContainer.Resolve<Service>(); | ||
var instanceConstructedByParent = parentContainer.Resolve<Service>(); | ||
|
||
childContainer.Dispose(); | ||
|
||
instanceConstructedByChild.IsDisposed.Should().BeTrue(); | ||
instanceConstructedByParent.IsDisposed.Should().BeFalse(); | ||
} | ||
|
||
[Test, Retry(3)] | ||
public void ScopedFromType_ConstructedInstances_ShouldBeCollected_WhenConstructingContainerIsDisposed() | ||
{ | ||
GarbageCollectionTests.MarkAsInconclusiveWhenReflexDebugIsEnabled(); | ||
|
||
WeakReference instanceConstructedByChild; | ||
WeakReference instanceConstructedByParent; | ||
var parentContainer = new ContainerBuilder().AddScoped(typeof(Service)).Build(); | ||
|
||
void Act() | ||
{ | ||
using (var childContainer = parentContainer.Scope()) | ||
{ | ||
instanceConstructedByChild = new WeakReference(childContainer.Resolve<Service>()); | ||
instanceConstructedByParent = new WeakReference(parentContainer.Resolve<Service>()); | ||
} | ||
} | ||
|
||
Act(); | ||
GarbageCollectionTests.ForceGarbageCollection(); | ||
instanceConstructedByChild.IsAlive.Should().BeFalse(); | ||
instanceConstructedByParent.IsAlive.Should().BeTrue(); | ||
} | ||
|
||
[Test, Retry(3)] | ||
public void ScopedFromFactory_ConstructedInstances_ShouldBeCollected_WhenConstructingContainerIsDisposed() | ||
{ | ||
GarbageCollectionTests.MarkAsInconclusiveWhenReflexDebugIsEnabled(); | ||
|
||
WeakReference instanceConstructedByChild; | ||
WeakReference instanceConstructedByParent; | ||
var parentContainer = new ContainerBuilder().AddScoped(_ => new Service()).Build(); | ||
|
||
void Act() | ||
{ | ||
using (var childContainer = parentContainer.Scope()) | ||
{ | ||
instanceConstructedByChild = new WeakReference(childContainer.Resolve<Service>()); | ||
instanceConstructedByParent = new WeakReference(parentContainer.Resolve<Service>()); | ||
} | ||
} | ||
|
||
Act(); | ||
GarbageCollectionTests.ForceGarbageCollection(); | ||
instanceConstructedByChild.IsAlive.Should().BeFalse(); | ||
instanceConstructedByParent.IsAlive.Should().BeTrue(); | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -4,5 +4,6 @@ public enum Lifetime | |
{ | ||
Singleton, | ||
Transient, | ||
Scoped, | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using System; | ||
using System.Runtime.CompilerServices; | ||
using Reflex.Core; | ||
using Reflex.Enums; | ||
|
||
namespace Reflex.Resolvers | ||
{ | ||
internal sealed class ScopedFactoryResolver : IResolver | ||
{ | ||
private readonly Func<Container, object> _factory; | ||
private readonly ConditionalWeakTable<Container, object> _instances = new(); | ||
public Lifetime Lifetime => Lifetime.Scoped; | ||
|
||
public ScopedFactoryResolver(Func<Container, object> factory) | ||
{ | ||
Diagnosis.RegisterCallSite(this); | ||
_factory = factory; | ||
} | ||
|
||
public object Resolve(Container container) | ||
{ | ||
Diagnosis.IncrementResolutions(this); | ||
|
||
if (!_instances.TryGetValue(container, out var instance)) | ||
{ | ||
instance = _factory.Invoke(container); | ||
_instances.Add(container, instance); | ||
container.Disposables.TryAdd(instance); | ||
Diagnosis.RegisterInstance(this, instance); | ||
} | ||
|
||
return instance; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,39 @@ | ||
using System; | ||
using System.Runtime.CompilerServices; | ||
using Reflex.Core; | ||
using Reflex.Enums; | ||
|
||
namespace Reflex.Resolvers | ||
{ | ||
internal sealed class ScopedTypeResolver : IResolver | ||
{ | ||
private readonly Type _concreteType; | ||
private readonly ConditionalWeakTable<Container, object> _instances = new(); | ||
public Lifetime Lifetime => Lifetime.Scoped; | ||
|
||
public ScopedTypeResolver(Type concreteType) | ||
{ | ||
Diagnosis.RegisterCallSite(this); | ||
_concreteType = concreteType; | ||
} | ||
|
||
public object Resolve(Container container) | ||
{ | ||
Diagnosis.IncrementResolutions(this); | ||
|
||
if (!_instances.TryGetValue(container, out var instance)) | ||
{ | ||
instance = container.Construct(_concreteType); | ||
_instances.Add(container, instance); | ||
container.Disposables.TryAdd(instance); | ||
Diagnosis.RegisterInstance(this, instance); | ||
} | ||
|
||
return instance; | ||
} | ||
|
||
public void Dispose() | ||
{ | ||
} | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Oops, something went wrong.