diff --git a/src/Stunts.UnitTests/BehaviorPipelineTests.cs b/src/Stunts.UnitTests/BehaviorPipelineTests.cs index afeadd3..b2b9765 100644 --- a/src/Stunts.UnitTests/BehaviorPipelineTests.cs +++ b/src/Stunts.UnitTests/BehaviorPipelineTests.cs @@ -389,13 +389,13 @@ public async Task WhenRunningParallel_ThenCanReplaceLocalPipelineFactory() await Task.WhenAll( Task.Run(() => { - BehaviorPipelineFactory.LocalDefault = factory1; + using var ambient = BehaviorPipelineFactory.UseAmbient(factory1); Thread.Sleep(50); Assert.Same(factory1, BehaviorPipelineFactory.Default); }), Task.Run(() => { - BehaviorPipelineFactory.LocalDefault = factory2; + using var ambient = BehaviorPipelineFactory.UseAmbient(factory2); Thread.Sleep(50); Assert.Same(factory2, BehaviorPipelineFactory.Default); }) diff --git a/src/Stunts.UnitTests/Castle/ClassProxyTests.cs b/src/Stunts.UnitTests/Castle/ClassProxyTests.cs index 697b856..04e156d 100644 --- a/src/Stunts.UnitTests/Castle/ClassProxyTests.cs +++ b/src/Stunts.UnitTests/Castle/ClassProxyTests.cs @@ -144,18 +144,10 @@ public void ParamsConstructorAcceptsAnArray() public void VirtualCallDuringConstructionUsesThePipelineFactory() { - var previous = BehaviorPipelineFactory.LocalDefault; - BehaviorPipelineFactory.LocalDefault = new CtorBehaviorFactory(); - try - { - var stunt = Stunt.Of(); + using var ambient = BehaviorPipelineFactory.UseAmbient(new CtorBehaviorFactory()); + var stunt = Stunt.Of(); - Assert.Equal("proxy", stunt.Seen); - } - finally - { - BehaviorPipelineFactory.LocalDefault = previous; - } + Assert.Equal("proxy", stunt.Seen); } public void NestedClassAndCharReturn() diff --git a/src/Stunts.UnitTests/DynamicStuntFactoryTests.cs b/src/Stunts.UnitTests/DynamicStuntFactoryTests.cs index 469dcef..69b0226 100644 --- a/src/Stunts.UnitTests/DynamicStuntFactoryTests.cs +++ b/src/Stunts.UnitTests/DynamicStuntFactoryTests.cs @@ -78,7 +78,7 @@ public void DelegateWithoutImplementationThrows() [Fact] public void ConstructorInterceptionNotSupported() { - BehaviorPipelineFactory.LocalDefault = new RecordingBehaviorPipelineFactory(); + using var ambient = BehaviorPipelineFactory.UseAmbient(new RecordingBehaviorPipelineFactory()); StuntFactory.LocalDefault = new DynamicStuntFactory(); var calculator = Stunt.Of(); diff --git a/src/Stunts.UnitTests/Scenarios/InterfaceBase.cs b/src/Stunts.UnitTests/Scenarios/InterfaceBase.cs index 875bc1d..194ea88 100644 --- a/src/Stunts.UnitTests/Scenarios/InterfaceBase.cs +++ b/src/Stunts.UnitTests/Scenarios/InterfaceBase.cs @@ -17,7 +17,7 @@ public class Test : IRunnable { public void Run() { - BehaviorPipelineFactory.LocalDefault = new RecordingBehaviorPipelineFactory(); + using var ambient = BehaviorPipelineFactory.UseAmbient(new RecordingBehaviorPipelineFactory()); var stunt = Stunt.Of(); Assert.NotNull(stunt); diff --git a/src/Stunts/BehaviorPipelineFactory.cs b/src/Stunts/BehaviorPipelineFactory.cs index c2e9ea1..6839a46 100644 --- a/src/Stunts/BehaviorPipelineFactory.cs +++ b/src/Stunts/BehaviorPipelineFactory.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; namespace Stunts @@ -24,8 +25,8 @@ public static class BehaviorPipelineFactory /// for creating the initial pipelines used during a stunt instantiation. /// /// - /// A can override the value of this global - /// default, if assigned to a non-null value. + /// An ambient can override the value of this global + /// default, via . This is typically only needed for testing. /// public static IBehaviorPipelineFactory Default { @@ -34,14 +35,24 @@ public static IBehaviorPipelineFactory Default } /// - /// Gets or sets the to use - /// in the current (async) flow, so it does not affect other threads/flows. - /// This is typically used in tests to isolate the default pipeline configurations. + /// Sets an ambient to use for for the current execution context. /// - public static IBehaviorPipelineFactory? LocalDefault + /// The to set as the ambient . + /// An that, when disposed, restores the original execution context default. + public static IDisposable UseAmbient(IBehaviorPipelineFactory factory) => new AmbientDisposable(localFactory, factory); + + class AmbientDisposable : IDisposable { - get => localFactory.Value; - set => localFactory.Value = value; + readonly AsyncLocal storage; + readonly IBehaviorPipelineFactory? original; + + public AmbientDisposable(AsyncLocal storage, IBehaviorPipelineFactory factory) + { + this.storage = storage; + original = storage.Value; + storage.Value = factory; + } + public void Dispose() => storage.Value = original; } class DefaultBehaviorPipelineFactory : IBehaviorPipelineFactory