Track last call and spec in thread-safe manner #264

Rename ICallStack to ICallCollection. That is because the Pop()
method is not required more (and was deleted), and Delete()
method was added instead. That is much more collection that stack.

IPendingSpecification now holds both information about previous
call and pending specification. That is encapsulated to the
PendingSpecificationInfo. The reasons why I added one more
responsibility to this class are following:
- We always either use pending specification or information about
previous call. This data is mutually exclusive (we delete other
one when store current one). Therefore, I decided that information
about previous call is also a specification to some degree.
- It's simpler to track sources of specifications because now we
have a single place only. In my previous implementation we needed
to clear pending specification and information about last call - more
chances to fail somewhere.
- Data is stored in SubstitutionContext, so we have single field
instead of two (one for pending specification, second for last
call information).

PendingSpecificationInfo stores thread-local on SubstitutionContext
(i.e. per substitute).

GetCallSpec now uses IPendingSpecification only to resolve
specification. Therefore, I renamed the FromLastCall method
to the FromPendingSpecification because it better reflects the
method implementation.

TrackLastCallHandler introduced to set `IPendingSpecification`.
This commit is contained in:
Alex Povar 2016-11-18 18:37:38 +02:00 коммит произвёл David Tchepak
Родитель ed904cabd7
Коммит bb06d33338
39 изменённых файлов: 679 добавлений и 304 удалений

1
.gitignore поставляемый
Просмотреть файл

@ -16,3 +16,4 @@ _ReSharper*
project.lock.json
.vs/
.fake/
.idea/

Просмотреть файл

@ -112,6 +112,9 @@
<Compile Include="..\NSubstitute.Acceptance.Specs\ClearSubstitute.cs">
<Link>ClearSubstitute.cs</Link>
</Compile>
<Compile Include="..\NSubstitute.Acceptance.Specs\ConcurrencyTests.cs">
<Link>ConcurrencyTests.cs</Link>
</Compile>
<Compile Include="..\NSubstitute.Acceptance.Specs\CustomHandlersSpecs.cs">
<Link>CustomHandlersSpecs.cs</Link>
</Compile>

Просмотреть файл

@ -0,0 +1,123 @@
using System;
using System.Threading;
using NSubstitute.Acceptance.Specs.Infrastructure;
using NUnit.Framework;
namespace NSubstitute.Acceptance.Specs
{
public class ConcurrencyTests
{
[Test]
public void Call_between_invocation_and_received_doesnt_cause_issue()
{
//arrange
var subs = Substitute.For<ISomething>();
var backgroundReady = new AutoResetEvent(false);
//act
var dummy = subs.Say("ping");
RunInOtherThread(() =>
{
subs.Echo(42);
backgroundReady.Set();
});
backgroundReady.WaitOne();
dummy.Returns("pong");
//assert
var actualResult = subs.Say("ping");
Assert.That(actualResult, Is.EqualTo("pong"));
}
[Test]
public void Background_invocation_doesnt_delete_specification()
{
//arrange
var subs = Substitute.For<ISomething>();
var backgroundReady = new AutoResetEvent(false);
//act
var dummy = subs.Say(Arg.Any<string>());
RunInOtherThread(() =>
{
subs.Say("hello");
backgroundReady.Set();
});
backgroundReady.WaitOne();
dummy.Returns("42");
//assert
Assert.That(subs.Say("Alex"), Is.EqualTo("42"));
}
[Test]
public void Both_threads_can_configure_returns_concurrently()
{
//arrange
var subs = Substitute.For<ISomething>();
var foregroundReady = new AutoResetEvent(false);
var backgroundReady = new AutoResetEvent(false);
//act
//1
var dummy = subs.Say("ping");
RunInOtherThread(() =>
{
//2
var d = subs.Echo(42);
SignalAndWait(backgroundReady, foregroundReady);
//4
d.Returns("42");
backgroundReady.Set();
});
backgroundReady.WaitOne();
//3
dummy.Returns("pong");
SignalAndWait(foregroundReady, backgroundReady);
//assert
Assert.That(subs.Say("ping"), Is.EqualTo("pong"));
Assert.That(subs.Echo(42), Is.EqualTo("42"));
}
#if (NET45 || NET4 || NETSTANDARD1_5)
[Test]
public void Configuration_works_fine_for_async_methods()
{
//arrange
var subs = Substitute.For<ISomething>();
//act
subs.EchoAsync(42).Returns("42");
//assert
var result = subs.EchoAsync(42).Result;
Assert.That(result, Is.EqualTo("42"));
}
#endif
private static void RunInOtherThread(Action action)
{
new Thread(action.Invoke) {IsBackground = true}.Start();
}
private static void SignalAndWait(EventWaitHandle toSignal, EventWaitHandle toWait)
{
toSignal.Set();
toWait.WaitOne();
}
}
}

Просмотреть файл

@ -11,9 +11,11 @@
<BaseIntermediateOutputPath Condition="'$(BaseIntermediateOutputPath)'=='' ">.\obj</BaseIntermediateOutputPath>
<OutputPath Condition="'$(OutputPath)'=='' ">.\bin\</OutputPath>
</PropertyGroup>
<PropertyGroup>
<SchemaVersion>2.0</SchemaVersion>
</PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DotNet\Microsoft.DotNet.targets" Condition="'$(VSToolsPath)' != ''" />
</Project>

Просмотреть файл

@ -1,5 +1,6 @@
using System;
using System.Diagnostics;
using System.Threading;
using NUnit.Framework;
namespace NSubstitute.Acceptance.Specs
@ -54,7 +55,39 @@ namespace NSubstitute.Acceptance.Specs
Console.WriteLine("{0}", watch.ElapsedMilliseconds);
}
[Test]
[Ignore("It's a stress test. It might take a lot of time and it not optimized for frequent execution.")]
public void Multiple_return_configuration_dont_leak_memory_for_any_args()
{
const int bufferSize = 100000000; //100 MB
var subs = Substitute.For<IByteArrayConsumer>();
//1000 chunks each 100 MB will require 100GB. If leak is present - OOM should be thrown.
for (var i = 0; i < 1000; i++)
{
subs.ConsumeArray(new byte[bufferSize]).ReturnsForAnyArgs(true);
}
}
[Test]
[Ignore("FAILS because of CallResults leak")]
public void Muiltiple_return_configurations_dont_lead_to_memory_leak()
{
const int bufferSize = 100000000; //100 MB
var subs = Substitute.For<IByteArraySource>();
//1000 chunks each 100 MB will require 100GB. If leak is present - OOM should be thrown.
for (int i = 0; i < 1000; i++)
{
subs.GetArray().Returns(new byte[bufferSize]);
}
}
public interface IFoo { int GetInt(string s); }
public interface IBar { int GetInt<T>(T t); }
public interface IByteArraySource { byte[] GetArray(); }
public interface IByteArrayConsumer { bool ConsumeArray(byte[] array); }
}
}

Просмотреть файл

@ -249,6 +249,9 @@
<Compile Include="..\NSubstitute\Core\CallBaseExclusions.cs">
<Link>Core\CallBaseExclusions.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Core\CallCollection.cs">
<Link>Core\CallCollection.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Core\CallFactory.cs">
<Link>Core\CallFactory.cs</Link>
</Compile>
@ -285,9 +288,6 @@
<Compile Include="..\NSubstitute\Core\CallSpecificationFactoryFactoryYesThatsRight.cs">
<Link>Core\CallSpecificationFactoryFactoryYesThatsRight.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Core\CallStack.cs">
<Link>Core\CallStack.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Core\ConfigureCall.cs">
<Link>Core\ConfigureCall.cs</Link>
</Compile>
@ -330,6 +330,9 @@
<Compile Include="..\NSubstitute\Core\ICallBaseExclusions.cs">
<Link>Core\ICallBaseExclusions.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Core\ICallCollection.cs">
<Link>Core\ICallCollection.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Core\ICallHandler.cs">
<Link>Core\ICallHandler.cs</Link>
</Compile>
@ -357,9 +360,6 @@
<Compile Include="..\NSubstitute\Core\ICallSpecificationFactory.cs">
<Link>Core\ICallSpecificationFactory.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Core\ICallStack.cs">
<Link>Core\ICallStack.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Core\IConfigureCall.cs">
<Link>Core\IConfigureCall.cs</Link>
</Compile>
@ -435,6 +435,9 @@
<Compile Include="..\NSubstitute\Core\PendingSpecification.cs">
<Link>Core\PendingSpecification.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Core\PendingSpecificationInfo.cs">
<Link>Core\PendingSpecificationInfo.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Core\PropertyCallFormatter.cs">
<Link>Core\PropertyCallFormatter.cs</Link>
</Compile>
@ -666,6 +669,9 @@
<Compile Include="..\NSubstitute\Routing\Handlers\SetActionForCallHandler.cs">
<Link>Routing\Handlers\SetActionForCallHandler.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Routing\Handlers\TrackLastCallHandler.cs">
<Link>Routing\Handlers\TrackLastCallHandler.cs</Link>
</Compile>
<Compile Include="..\NSubstitute\Routing\IRoute.cs">
<Link>Routing\IRoute.cs</Link>
</Compile>
@ -689,7 +695,6 @@
</None>
<None Include="ilmerge.exclude" />
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" Condition="($(MSBuildTargets) == '') Or ($(MSBuildTargets) == 'CSharp')" />
<Target Name="AfterBuild" Condition="(($(MSBuildTargets) == '') Or ($(MSBuildTargets) == 'CSharp')) And '$(OS)' == 'Windows_NT'">
<CreateItem Include="@(ReferenceCopyLocalPaths)" Condition="'%(Extension)'=='.dll'">

Просмотреть файл

@ -0,0 +1,82 @@
using System;
using NSubstitute.Core;
using NSubstitute.Specs.Infrastructure;
using NUnit.Framework;
namespace NSubstitute.Specs
{
public class CallCollectionSpecs : ConcernFor<CallCollection>
{
public override CallCollection CreateSubjectUnderTest() => new CallCollection();
[Test]
public void Should_add_call()
{
//arrange
var call = mock<ICall>();
//act
sut.Add(call);
//assert
CollectionAssert.Contains(sut.AllCalls(), call);
}
[Test]
public void Should_delete_call_when_deleted()
{
//arrange
var call = mock<ICall>();
//act
sut.Add(call);
sut.Delete(call);
//assert
CollectionAssert.DoesNotContain(sut.AllCalls(), call);
}
[Test]
public void Should_fail_when_delete_nonexisting_call()
{
//arrange
var call = mock<ICall>();
//act/assert
var exception = Assert.Throws<InvalidOperationException>(() => sut.Delete(call));
Assert.That(exception.Message, Is.StringContaining("Collection doesn't contain the call."));
}
[Test]
public void Should_delete_all_calls_on_clear()
{
//arrange
var call1 = mock<ICall>();
var call2 = mock<ICall>();
//act
sut.Add(call1);
sut.Add(call2);
sut.Clear();
//assert
CollectionAssert.IsEmpty(sut.AllCalls());
}
[Test]
public void Should_return_all_calls_in_the_order_they_were_received()
{
//arrange
var firstCall = mock<ICall>();
var secondCall = mock<ICall>();
//act
sut.Add(firstCall);
sut.Add(secondCall);
//assert
CollectionAssert.AreEqual(sut.AllCalls(), new[] { firstCall, secondCall });
}
}
}

Просмотреть файл

@ -1,94 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
using NSubstitute.Core;
using NSubstitute.Specs.Infrastructure;
using NUnit.Framework;
namespace NSubstitute.Specs
{
public class CallStackSpecs
{
public abstract class Concern : ConcernFor<CallStack>
{
public override CallStack CreateSubjectUnderTest()
{
return new CallStack();
}
}
public class When_a_call_has_been_pushed : Concern
{
ICall _call;
[Test]
public void Should_pop_to_get_that_call_back()
{
Assert.That(sut.Pop(), Is.SameAs(_call));
}
public override void Because()
{
sut.Push(_call);
}
public override void Context()
{
base.Context();
_call = mock<ICall>();
}
}
public class When_the_call_stack_is_empty : Concern
{
[Test]
public void Should_get_a_stack_empty_exception_when_trying_to_pop()
{
var exception = Assert.Throws<InvalidOperationException>(() => sut.Pop());
Assert.That(exception.Message, Is.StringContaining("Stack empty"));
}
}
public class When_calls_are_cleared : Concern
{
[Test]
public void Should_not_have_any_calls_to_pop()
{
Assert.Throws<InvalidOperationException>(() => sut.Pop());
}
public override void Because()
{
sut.Push(mock<ICall>());
sut.Clear();
}
}
public class When_finding_all_calls : Concern
{
private IEnumerable<ICall> _result;
private ICall _firstCall;
private ICall _secondCall;
[Test]
public void Should_return_all_calls_in_the_order_they_were_received()
{
Assert.That(_result.ToArray(), Is.EqualTo(new[] { _firstCall, _secondCall }));
}
public override void Because()
{
sut.Push(_firstCall);
sut.Push(_secondCall);
_result = sut.AllCalls();
}
public override void Context()
{
base.Context();
_firstCall = mock<ICall>();
_secondCall = mock<ICall>();
}
}
}
}

Просмотреть файл

@ -37,7 +37,7 @@ namespace NSubstitute.Specs
public void Configure_result_for_last_specified_call()
{
var lastCallSpec = mock<ICallSpecification>();
_getCallSpec.stub(x => x.FromLastCall(MatchArgs.AsSpecifiedInCall)).Return(lastCallSpec);
_getCallSpec.stub(x => x.FromPendingSpecification(MatchArgs.AsSpecifiedInCall)).Return(lastCallSpec);
sut.SetResultForLastCall(_compatibleReturnValue, MatchArgs.AsSpecifiedInCall);
@ -68,7 +68,7 @@ namespace NSubstitute.Specs
Action<CallInfo> firstAction = x => { };
Action<CallInfo> secondAction = x => { };
var lastCallSpec = mock<ICallSpecification>();
_getCallSpec.stub(x => x.FromLastCall(MatchArgs.AsSpecifiedInCall)).Return(lastCallSpec);
_getCallSpec.stub(x => x.FromPendingSpecification(MatchArgs.AsSpecifiedInCall)).Return(lastCallSpec);
var config = sut.SetResultForLastCall(_compatibleReturnValue, MatchArgs.AsSpecifiedInCall);
config
@ -86,7 +86,7 @@ namespace NSubstitute.Specs
public void Configure_result_for_last_specified_call()
{
var lastCallSpec = mock<ICallSpecification>();
_getCallSpec.stub(x => x.FromLastCall(MatchArgs.AsSpecifiedInCall)).Return(lastCallSpec);
_getCallSpec.stub(x => x.FromPendingSpecification(MatchArgs.AsSpecifiedInCall)).Return(lastCallSpec);
lastCallSpec.stub(x => x.GetMethodInfo()).Return(ReflectionHelper.GetMethod(() => SomeType.SampleMethod()));
var incompatibleReturn = mock<IReturn>();

Просмотреть файл

@ -1,4 +1,6 @@
using NSubstitute.Core;
using System;
using NSubstitute.Core;
using NSubstitute.ReturnsExtensions;
using NSubstitute.Specs.Infrastructure;
using NUnit.Framework;
@ -8,14 +10,14 @@ namespace NSubstitute.Specs
{
public abstract class Concern : ConcernFor<GetCallSpec>
{
protected ICallStack _callStack;
protected ICallCollection _callCollection;
protected IPendingSpecification _pendingSpecification;
protected ICallActions _callActions;
protected ICallSpecificationFactory _callSpecificationFactory;
public override void Context()
{
_callStack = mock<ICallStack>();
_callCollection = mock<ICallCollection>();
_pendingSpecification = mock<IPendingSpecification>();
_callActions = mock<ICallActions>();
_callSpecificationFactory = mock<ICallSpecificationFactory>();
@ -23,11 +25,11 @@ namespace NSubstitute.Specs
public override GetCallSpec CreateSubjectUnderTest()
{
return new GetCallSpec(_callStack, _pendingSpecification, _callSpecificationFactory, _callActions);
return new GetCallSpec(_callCollection, _pendingSpecification, _callSpecificationFactory, _callActions);
}
}
public class When_getting_for_last_call_with_pending_spec_and_matching_args : Concern
public class When_getting_for_pending_call_spec_with_pending_spec_and_matching_args : Concern
{
ICallSpecification _lastCallSpecification;
ICallSpecification _result;
@ -45,26 +47,28 @@ namespace NSubstitute.Specs
}
[Test]
public void Should_not_touch_call_stack()
public void Should_not_touch_call_collection()
{
_callStack.did_not_receive(x => x.Pop());
_callCollection.did_not_receive(x => x.Delete(It.IsAny<ICall>()));
}
public override void Because()
{
_result = sut.FromLastCall(MatchArgs.AsSpecifiedInCall);
_result = sut.FromPendingSpecification(MatchArgs.AsSpecifiedInCall);
}
public override void Context()
{
base.Context();
_lastCallSpecification = mock<ICallSpecification>();
_pendingSpecification.stub(x => x.HasPendingCallSpec()).Return(true);
_pendingSpecification.stub(x => x.UseCallSpec()).Return(_lastCallSpecification);
_pendingSpecification.stub(x => x.HasPendingCallSpecInfo()).Return(true);
var specificationInfo = PendingSpecificationInfo.FromCallSpecification(_lastCallSpecification);
_pendingSpecification.stub(x => x.UseCallSpecInfo()).Return(specificationInfo);
}
}
public class When_getting_for_last_call_with_pending_spec_and_setting_for_any_args : Concern
public class When_getting_for_pending_spec_with_pending_spec_and_setting_for_any_args : Concern
{
ICallSpecification _callSpecUpdatedForAnyArgs;
ICallSpecification _lastCallSpecification;
@ -85,12 +89,12 @@ namespace NSubstitute.Specs
[Test]
public void Should_not_touch_call_stack()
{
_callStack.did_not_receive(x => x.Pop());
_callCollection.did_not_receive(x => x.Delete(It.IsAny<ICall>()));
}
public override void Because()
{
_result = sut.FromLastCall(MatchArgs.Any);
_result = sut.FromPendingSpecification(MatchArgs.Any);
}
public override void Context()
@ -98,13 +102,16 @@ namespace NSubstitute.Specs
base.Context();
_callSpecUpdatedForAnyArgs = mock<ICallSpecification>();
_lastCallSpecification = mock<ICallSpecification>();
_pendingSpecification.stub(x => x.HasPendingCallSpec()).Return(true);
_pendingSpecification.stub(x => x.UseCallSpec()).Return(_lastCallSpecification);
var specificationInfo = PendingSpecificationInfo.FromCallSpecification(_lastCallSpecification);
_pendingSpecification.stub(x => x.HasPendingCallSpecInfo()).Return(true);
_pendingSpecification.stub(x => x.UseCallSpecInfo()).Return(specificationInfo);
_lastCallSpecification.stub(x => x.CreateCopyThatMatchesAnyArguments()).Return(_callSpecUpdatedForAnyArgs);
}
}
public class When_getting_for_last_call_with_no_pending_call_spec : Concern
public class When_getting_for_pending_spec_with_last_call_info : Concern
{
readonly MatchArgs _argMatchStrategy = MatchArgs.AsSpecifiedInCall;
ICall _call;
@ -112,27 +119,52 @@ namespace NSubstitute.Specs
ICallSpecification _result;
[Test]
public void Should_remove_the_call_from_those_recorded_and_add_it_to_configured_results()
public void Should_take_last_call_from_pending_specification_info()
{
Assert.That(_result, Is.EqualTo(_callSpecification));
}
[Test]
public void Should_delete_last_call_from_call_collection()
{
_callCollection.received(c => c.Delete(_call));
}
public override void Because()
{
_result = sut.FromLastCall(_argMatchStrategy);
_result = sut.FromPendingSpecification(_argMatchStrategy);
}
public override void Context()
{
base.Context();
_call = mock<ICall>();
_callStack.stub(x => x.Pop()).Return(_call);
_pendingSpecification.stub(x => x.HasPendingCallSpec()).Return(false);
var specificationInfo = PendingSpecificationInfo.FromLastCall(_call);
_pendingSpecification.stub(x => x.HasPendingCallSpecInfo()).Return(true);
_pendingSpecification.stub(x => x.UseCallSpecInfo()).Return(specificationInfo);
_callSpecification = mock<ICallSpecification>();
_callSpecificationFactory.stub(x => x.CreateFrom(_call, _argMatchStrategy)).Return(_callSpecification);
}
}
public class When_no_pending_specification : Concern
{
[Test]
public void Should_fail_with_exception()
{
Assert.That(() => sut.FromPendingSpecification(MatchArgs.AsSpecifiedInCall), Throws.InvalidOperationException);
}
public override void Context()
{
base.Context();
_pendingSpecification.stub(s => s.HasPendingCallSpecInfo()).Return(false);
}
}
}
}

Просмотреть файл

@ -7,6 +7,8 @@ namespace NSubstitute.Specs.Infrastructure
{
public class TestCallRouter : ICallRouter
{
public bool IsLastCallInfoPresent() => true;
public ConfiguredCall LastCallShouldReturn(IReturn returnValue, MatchArgs matchArgs)
{
return new ConfiguredCall(x => { });

Просмотреть файл

@ -168,7 +168,6 @@
<Compile Include="PropertyHelperSpecs.cs" />
<Compile Include="ReceivedExtensionSpec.cs" />
<Compile Include="Routing\Handlers\RecordCallHandlerSpecs.cs" />
<Compile Include="Routing\Handlers\RecordCallSpecificationHandlerSpecs.cs" />
<Compile Include="Routing\Handlers\ReturnAutoValueSpecs.cs" />
<Compile Include="Routing\Handlers\ReturnResultForTypeHandlerSpecs.cs" />
<Compile Include="Routing\Handlers\ReturnConfiguredResultHandlerSpecs.cs" />
@ -183,7 +182,7 @@
<Compile Include="SubstitutionContextSpecs.cs" />
<Compile Include="SubstituteFactorySpecs.cs" />
<Compile Include="Infrastructure\BaseConcern.cs" />
<Compile Include="CallStackSpecs.cs" />
<Compile Include="CallCollectionSpecs.cs" />
<Compile Include="Infrastructure\ConcernFor.cs" />
<Compile Include="Infrastructure\ITemporaryChange.cs" />
<Compile Include="Infrastructure\MockingAdaptor.cs" />

Просмотреть файл

@ -8,7 +8,14 @@ namespace NSubstitute.Specs
{
public abstract class Concern : ConcernFor<PendingSpecification>
{
public override PendingSpecification CreateSubjectUnderTest() { return new PendingSpecification(); }
protected ISubstitutionContext _substitutionContext;
public override void Context()
{
_substitutionContext = mock<ISubstitutionContext>();
}
public override PendingSpecification CreateSubjectUnderTest() { return new PendingSpecification(_substitutionContext); }
}
public class When_no_pending_specification : Concern
@ -16,13 +23,19 @@ namespace NSubstitute.Specs
[Test]
public void Should_not_have_a_spec()
{
Assert.That(sut.HasPendingCallSpec(), Is.False);
Assert.That(sut.HasPendingCallSpecInfo(), Is.False);
}
[Test]
public void Should_return_null_when_trying_to_use_spec()
{
Assert.That(sut.UseCallSpec(), Is.Null);
Assert.That(sut.UseCallSpecInfo(), Is.Null);
}
public override void Because()
{
base.Because();
_substitutionContext.PendingSpecificationInfo = null;
}
}
@ -33,38 +46,73 @@ namespace NSubstitute.Specs
[Test]
public void Should_have_a_spec()
{
Assert.That(sut.HasPendingCallSpec(), Is.True);
Assert.That(sut.HasPendingCallSpecInfo(), Is.True);
}
[Test]
public void Should_return_the_spec_when_using_it()
{
Assert.That(sut.UseCallSpec(), Is.SameAs(_callSpec));
var specInfo = sut.UseCallSpecInfo();
Assert.That(specInfo.CallSpecification, Is.SameAs(_callSpec));
Assert.That(specInfo.LastCall, Is.Null);
}
[Test]
public void Should_not_have_spec_after_it_is_used()
{
sut.UseCallSpec();
Assert.That(sut.HasPendingCallSpec(), Is.False);
sut.UseCallSpecInfo();
Assert.That(sut.HasPendingCallSpecInfo(), Is.False);
}
[Test]
public void Should_not_have_a_spec_after_it_is_cleared()
{
sut.Clear();
Assert.That(sut.HasPendingCallSpec(), Is.False);
Assert.That(sut.HasPendingCallSpecInfo(), Is.False);
}
public override void Because()
{
sut.Set(_callSpec);
base.Because();
sut.SetCallSpecification(_callSpec);
}
public override void Context()
{
base.Context();
_callSpec = mock<ICallSpecification>();
}
}
public class When_a_last_call_info_has_been_set : Concern
{
ICall _call;
[Test]
public void Should_have_a_spec()
{
Assert.That(sut.HasPendingCallSpecInfo(), Is.True);
}
[Test]
public void Should_return_the_call_when_using_it()
{
var specInfo = sut.UseCallSpecInfo();
Assert.That(specInfo.LastCall, Is.SameAs(_call));
Assert.That(specInfo.CallSpecification, Is.Null);
}
public override void Because()
{
base.Because();
sut.SetLastCall(_call);
}
public override void Context()
{
base.Context();
_call = mock<ICall>();
}
}
}
}

Просмотреть файл

@ -10,14 +10,14 @@ namespace NSubstitute.Specs.Routing.Handlers
public class When_handling_call_to_a_member : ConcernFor<RecordCallHandler>
{
ICall _call;
ICallStack _callStack;
ICallCollection _callCollection;
SequenceNumberGenerator _sequenceNumberGenerator;
RouteAction _result;
[Test]
public void Should_record_call()
{
_callStack.received(x => x.Push(_call));
_callCollection.received(x => x.Add(_call));
}
[Test]
@ -39,7 +39,7 @@ namespace NSubstitute.Specs.Routing.Handlers
public override void Context()
{
_callStack = mock<ICallStack>();
_callCollection = mock<ICallCollection>();
_call = mock<ICall>();
_sequenceNumberGenerator = mock<SequenceNumberGenerator>();
_sequenceNumberGenerator.stub(x => x.Next()).Return(42);
@ -47,7 +47,7 @@ namespace NSubstitute.Specs.Routing.Handlers
public override RecordCallHandler CreateSubjectUnderTest()
{
return new RecordCallHandler(_callStack, _sequenceNumberGenerator);
return new RecordCallHandler(_callCollection, _sequenceNumberGenerator);
}
}
}

Просмотреть файл

@ -1,58 +0,0 @@
using NSubstitute.Core;
using NSubstitute.Specs.Infrastructure;
using NSubstitute.Routing.Handlers;
using NUnit.Framework;
namespace NSubstitute.Specs.Routing.Handlers
{
public class RecordCallSpecificationHandlerSpecs
{
public class When_handling_call : ConcernFor<RecordCallSpecificationHandler>
{
IPendingSpecification _pendingSpec;
ICallActions _callActions;
ICallSpecificationFactory _callSpecFactory;
ICall _call;
ICallSpecification _callSpec;
RouteAction _result;
[Test]
public void Should_set_pending_call_spec()
{
_pendingSpec.received(x => x.Set(_callSpec));
}
[Test]
public void Should_add_any_specified_actions()
{
_callActions.received(x => x.Add(_callSpec));
}
[Test]
public void Should_continue_route()
{
Assert.That(_result, Is.EqualTo(RouteAction.Continue()));
}
public override void Because()
{
_result = sut.Handle(_call);
}
public override void Context()
{
_call = mock<ICall>();
_callSpec = mock<ICallSpecification>();
_callActions = mock<ICallActions>();
_pendingSpec = mock<IPendingSpecification>();
_callSpecFactory = mock<ICallSpecificationFactory>();
_callSpecFactory.stub(x => x.CreateFrom(_call, MatchArgs.AsSpecifiedInCall)).Return(_callSpec);
}
public override RecordCallSpecificationHandler CreateSubjectUnderTest()
{
return new RecordCallSpecificationHandler(_pendingSpec, _callSpecFactory, _callActions);
}
}
}
}

Просмотреть файл

@ -1,3 +1,4 @@
using System.Threading;
using NSubstitute.Core;
using NSubstitute.Core.Arguments;
using NSubstitute.Exceptions;
@ -51,6 +52,8 @@ namespace NSubstitute.Specs
{
base.Context();
_callRouter = mock<ICallRouter>();
_callRouter.stub(c => c.IsLastCallInfoPresent()).Return(true);
_valueToReturn = mock<IReturn>();
}
}
@ -64,6 +67,32 @@ namespace NSubstitute.Specs
}
}
public class When_trying_to_set_a_return_value_when_router_cannot_configure_last_call : Concern
{
private ICallRouter _callRouter;
[Test]
public void Should_throw_that_last_call_cannot_be_configured()
{
Assert.Throws<CouldNotSetReturnDueToMissingInfoAboutLastCallException>(() => sut.LastCallShouldReturn(mock<IReturn>(), MatchArgs.AsSpecifiedInCall));
}
public override void Because()
{
base.Because();
_callRouter.stub(c => c.IsLastCallInfoPresent()).Return(false);
sut.LastCallRouter(_callRouter);
}
public override void Context()
{
base.Context();
_callRouter = mock<ICallRouter>();
}
}
public class When_getting_call_router_for_a_substitute : Concern
{
private ICallRouter _routerForSubstitute;
@ -132,6 +161,39 @@ namespace NSubstitute.Specs
}
}
public class When_working_with_thread_static_pending_spec_info : Concern
{
PendingSpecificationInfo _specInfo;
[Test]
public void Storage_is_bound_to_thread()
{
var thread = new Thread(() => { sut.PendingSpecificationInfo = _specInfo; })
{
IsBackground = true
};
thread.Start();
thread.Join();
Assert.That(sut.PendingSpecificationInfo, Is.Null);
}
[Test]
public void Set_value_is_read()
{
sut.PendingSpecificationInfo = _specInfo;
Assert.That(sut.PendingSpecificationInfo, Is.EqualTo(_specInfo));
}
public override void Context()
{
base.Context();
_specInfo = PendingSpecificationInfo.FromLastCall(mock<ICall>());
}
}
public class When_accessing_current_instance : StaticConcern
{
[Test]

Просмотреть файл

@ -47,7 +47,12 @@ namespace NSubstitute.Core.Arguments
public IArgumentSpecification CreateCopyMatchingAnyArgOfType(Type requiredType)
{
return new ArgumentSpecification(requiredType, new AnyArgumentMatcher(requiredType), RunActionIfTypeIsCompatible);
//Don't pass RunActionIfTypeIsCompatible method if no action is present.
//Otherwise, unnecessary closure will keep reference to this and will keep it alive.
return new ArgumentSpecification(
requiredType,
new AnyArgumentMatcher(requiredType),
_action == NoOpAction ? NoOpAction : RunActionIfTypeIsCompatible);
}
public void RunAction(object argument)

Просмотреть файл

@ -0,0 +1,62 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
namespace NSubstitute.Core
{
public class CallCollection : ICallCollection, IReceivedCalls
{
ConcurrentQueue<CallWrapper> _callWrappers = new ConcurrentQueue<CallWrapper>();
public void Add(ICall call)
{
if (call == null) throw new ArgumentNullException(nameof(call));
_callWrappers.Enqueue(new CallWrapper(call));
}
public void Delete(ICall call)
{
if (call == null) throw new ArgumentNullException(nameof(call));
var callWrapper = _callWrappers.FirstOrDefault(w => !w.IsDeleted && call.Equals(w.Call));
if (callWrapper == null) throw new InvalidOperationException("Collection doesn't contain the call.");
callWrapper.Delete();
}
public IEnumerable<ICall> AllCalls()
{
return _callWrappers.ToArray()
.Where(w => !w.IsDeleted)
.Select(w => w.Call);
}
public void Clear()
{
//Queue doesn't have a Clear method.
_callWrappers = new ConcurrentQueue<CallWrapper>();
}
/// <summary>
/// Wrapper to track that particular entry was deleted.
/// That is needed because concurrent collections don't have a Delete method.
/// We null the hold value to not leak memory.
/// </summary>
private class CallWrapper
{
public ICall Call { get; private set; }
public bool IsDeleted => Call == null;
public CallWrapper(ICall call)
{
Call = call;
}
public void Delete()
{
Call = null;
}
}
}
}

Просмотреть файл

@ -64,6 +64,11 @@ namespace NSubstitute.Core
return routeToUseForThisCall.Handle(call);
}
public bool IsLastCallInfoPresent()
{
return _substituteState.PendingSpecification.HasPendingCallSpecInfo();
}
public ConfiguredCall LastCallShouldReturn(IReturn returnValue, MatchArgs matchArgs)
{
return _substituteState.ConfigureCall.SetResultForLastCall(returnValue, matchArgs);

Просмотреть файл

@ -1,34 +0,0 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace NSubstitute.Core
{
public class CallStack : ICallStack, IReceivedCalls
{
readonly System.Collections.Concurrent.ConcurrentStack<ICall> _stack
= new System.Collections.Concurrent.ConcurrentStack<ICall>();
public void Push(ICall call)
{
_stack.Push(call);
}
public ICall Pop()
{
ICall call;
if (!_stack.TryPop(out call)) throw new InvalidOperationException("Stack empty");
return call;
}
public IEnumerable<ICall> AllCalls()
{
return _stack.ToArray().Reverse();
}
public void Clear()
{
_stack.Clear();
}
}
}

Просмотреть файл

@ -17,7 +17,7 @@ namespace NSubstitute.Core
public ConfiguredCall SetResultForLastCall(IReturn valueToReturn, MatchArgs matchArgs)
{
var spec = _getCallSpec.FromLastCall(matchArgs);
var spec = _getCallSpec.FromPendingSpecification(matchArgs);
CheckResultIsCompatibleWithCall(valueToReturn, spec);
_configuredResults.SetResult(spec, valueToReturn);
return new ConfiguredCall(action => _callActions.Add(spec, action));

Просмотреть файл

@ -1,26 +1,40 @@
namespace NSubstitute.Core
using System;
namespace NSubstitute.Core
{
public class GetCallSpec : IGetCallSpec
{
private readonly ICallStack _callStack;
private readonly ICallCollection _callCollection;
private readonly IPendingSpecification _pendingSpecification;
private readonly ICallSpecificationFactory _callSpecificationFactory;
private readonly ICallActions _callActions;
public GetCallSpec(ICallStack callStack, IPendingSpecification pendingSpecification,
public GetCallSpec(ICallCollection callCollection, IPendingSpecification pendingSpecification,
ICallSpecificationFactory callSpecificationFactory, ICallActions callActions)
{
_callStack = callStack;
_callCollection = callCollection;
_pendingSpecification = pendingSpecification;
_callSpecificationFactory = callSpecificationFactory;
_callActions = callActions;
}
public ICallSpecification FromLastCall(MatchArgs matchArgs)
public ICallSpecification FromPendingSpecification(MatchArgs matchArgs)
{
return _pendingSpecification.HasPendingCallSpec()
? FromExistingSpec(_pendingSpecification.UseCallSpec(), matchArgs)
: FromCall(_callStack.Pop(), matchArgs);
if (!_pendingSpecification.HasPendingCallSpecInfo())
{
throw new InvalidOperationException("No pending specification or previous call info.");
}
var pendingSpecInfo = _pendingSpecification.UseCallSpecInfo();
if (pendingSpecInfo.CallSpecification != null)
{
return FromExistingSpec(pendingSpecInfo.CallSpecification, matchArgs);
}
var lastCall = pendingSpecInfo.LastCall;
_callCollection.Delete(lastCall);
return FromCall(lastCall, matchArgs);
}
public ICallSpecification FromCall(ICall call, MatchArgs matchArgs)

Просмотреть файл

@ -0,0 +1,8 @@
namespace NSubstitute.Core
{
public interface ICallCollection
{
void Add(ICall call);
void Delete(ICall call);
}
}

Просмотреть файл

@ -6,6 +6,7 @@ namespace NSubstitute.Core
{
public interface ICallRouter
{
bool IsLastCallInfoPresent();
ConfiguredCall LastCallShouldReturn(IReturn returnValue, MatchArgs matchArgs);
object Route(ICall call);
IEnumerable<ICall> ReceivedCalls();

Просмотреть файл

@ -1,8 +0,0 @@
namespace NSubstitute.Core
{
public interface ICallStack
{
void Push(ICall call);
ICall Pop();
}
}

Просмотреть файл

@ -2,7 +2,7 @@
{
public interface IGetCallSpec
{
ICallSpecification FromLastCall(MatchArgs matchArgs);
ICallSpecification FromPendingSpecification(MatchArgs matchArgs);
ICallSpecification FromExistingSpec(ICallSpecification spec, MatchArgs matchArgs);
ICallSpecification FromCall(ICall call, MatchArgs matchArgs);
}

Просмотреть файл

@ -2,9 +2,10 @@
{
public interface IPendingSpecification
{
bool HasPendingCallSpec();
ICallSpecification UseCallSpec();
void Set(ICallSpecification callSpecification);
bool HasPendingCallSpecInfo();
PendingSpecificationInfo UseCallSpecInfo();
void SetCallSpecification(ICallSpecification callSpecification);
void SetLastCall(ICall lastCall);
void Clear();
}
}

Просмотреть файл

@ -5,7 +5,7 @@ namespace NSubstitute.Core
public interface ISubstituteState
{
ISubstitutionContext SubstitutionContext { get; }
ICallStack CallStack { get; }
ICallCollection CallCollection { get; }
IReceivedCalls ReceivedCalls { get; }
IPendingSpecification PendingSpecification { get; }
ICallResults CallResults { get; }
@ -20,6 +20,5 @@ namespace NSubstitute.Core
ICallBaseExclusions CallBaseExclusions { get; }
IResultsForType ResultsForType { get; }
ICustomHandlers CustomHandlers { get; }
void ClearUnusedCallSpecs();
}
}

Просмотреть файл

@ -9,6 +9,7 @@ namespace NSubstitute.Core
{
ISubstituteFactory SubstituteFactory { get; }
SequenceNumberGenerator SequenceNumberGenerator { get; }
PendingSpecificationInfo PendingSpecificationInfo { get; set; }
ConfiguredCall LastCallShouldReturn(IReturn value, MatchArgs matchArgs);
void LastCallRouter(ICallRouter callRouter);
ICallRouter GetCallRouterFor(object substitute);

Просмотреть файл

@ -1,29 +1,41 @@
using System;
namespace NSubstitute.Core
{
public class PendingSpecification : IPendingSpecification
{
ICallSpecification _pendingSpec;
private readonly ISubstitutionContext _substitutionContext;
public bool HasPendingCallSpec()
public PendingSpecification(ISubstitutionContext substitutionContext)
{
return _pendingSpec != null;
_substitutionContext = substitutionContext;
}
public ICallSpecification UseCallSpec()
public bool HasPendingCallSpecInfo()
{
var specToUse = _pendingSpec;
_pendingSpec = null;
return specToUse;
return _substitutionContext.PendingSpecificationInfo != null;
}
public PendingSpecificationInfo UseCallSpecInfo()
{
var info = _substitutionContext.PendingSpecificationInfo;
Clear();
return info;
}
public void SetCallSpecification(ICallSpecification callSpecification)
{
_substitutionContext.PendingSpecificationInfo = PendingSpecificationInfo.FromCallSpecification(callSpecification);
}
public void SetLastCall(ICall lastCall)
{
_substitutionContext.PendingSpecificationInfo = PendingSpecificationInfo.FromLastCall(lastCall);
}
public void Clear()
{
UseCallSpec();
}
public void Set(ICallSpecification callSpecification)
{
_pendingSpec = callSpecification;
_substitutionContext.PendingSpecificationInfo = null;
}
}
}

Просмотреть файл

@ -0,0 +1,24 @@
namespace NSubstitute.Core
{
public class PendingSpecificationInfo
{
public ICallSpecification CallSpecification { get; }
public ICall LastCall { get; }
private PendingSpecificationInfo(ICallSpecification callSpecification, ICall lastCall)
{
CallSpecification = callSpecification;
LastCall = lastCall;
}
public static PendingSpecificationInfo FromLastCall(ICall lastCall)
{
return new PendingSpecificationInfo(null, lastCall);
}
public static PendingSpecificationInfo FromCallSpecification(ICallSpecification callSpecification)
{
return new PendingSpecificationInfo(callSpecification, null);
}
}
}

Просмотреть файл

@ -5,9 +5,9 @@ namespace NSubstitute.Core
public class SubstituteState : ISubstituteState
{
public ISubstitutionContext SubstitutionContext { get; private set; }
public ICallStack CallStack { get; private set; }
public ICallCollection CallCollection { get; }
public IReceivedCalls ReceivedCalls { get; private set; }
public IPendingSpecification PendingSpecification { get; private set; }
public IPendingSpecification PendingSpecification { get; }
public ICallResults CallResults { get; private set; }
public ICallSpecificationFactory CallSpecificationFactory { get; private set; }
public ICallActions CallActions { get; private set; }
@ -28,10 +28,12 @@ namespace NSubstitute.Core
SequenceNumberGenerator = substitutionContext.SequenceNumberGenerator;
var substituteFactory = substitutionContext.SubstituteFactory;
var callInfoFactory = new CallInfoFactory();
var callStack = new CallStack();
CallStack = callStack;
ReceivedCalls = callStack;
PendingSpecification = new PendingSpecification();
var callCollection = new CallCollection();
CallCollection = callCollection;
ReceivedCalls = callCollection;
PendingSpecification = new PendingSpecification(substitutionContext);
CallResults = new CallResults(callInfoFactory);
AutoValuesCallResults = new CallResults(callInfoFactory);
CallSpecificationFactory = CallSpecificationFactoryFactoryYesThatsRight.CreateCallSpecFactory();
@ -39,9 +41,10 @@ namespace NSubstitute.Core
CallBaseExclusions = new CallBaseExclusions();
ResultsForType = new ResultsForType(callInfoFactory);
CustomHandlers = new CustomHandlers(this);
var getCallSpec = new GetCallSpec(callStack, PendingSpecification, CallSpecificationFactory, CallActions);
var getCallSpec = new GetCallSpec(callCollection, PendingSpecification, CallSpecificationFactory, CallActions);
ConfigureCall = new ConfigureCall(CallResults, CallActions, getCallSpec);
EventHandlerRegistry = new EventHandlerRegistry();
AutoValueProviders = new IAutoValueProvider[] {
#if NET45 || NETSTANDARD1_5
new AutoObservableProvider(() => AutoValueProviders),
@ -55,10 +58,5 @@ namespace NSubstitute.Core
#endif
};
}
public void ClearUnusedCallSpecs()
{
PendingSpecification.Clear();
}
}
}

Просмотреть файл

@ -20,6 +20,7 @@ namespace NSubstitute.Core
readonly RobustThreadLocal<IList<IArgumentSpecification>> _argumentSpecifications = new RobustThreadLocal<IList<IArgumentSpecification>>(() => new List<IArgumentSpecification>());
readonly RobustThreadLocal<Func<ICall, object[]>> _getArgumentsForRaisingEvent = new RobustThreadLocal<Func<ICall, object[]>>();
readonly RobustThreadLocal<Query> _currentQuery = new RobustThreadLocal<Query>();
readonly RobustThreadLocal<PendingSpecificationInfo> _pendingSpecificationInfo = new RobustThreadLocal<PendingSpecificationInfo>();
static SubstitutionContext()
{
@ -45,16 +46,25 @@ namespace NSubstitute.Core
public SequenceNumberGenerator SequenceNumberGenerator { get { return _sequenceNumberGenerator; } }
public bool IsQuerying { get { return _currentQuery.Value != null; } }
public PendingSpecificationInfo PendingSpecificationInfo
{
get { return _pendingSpecificationInfo.Value; }
set { _pendingSpecificationInfo.Value = value; }
}
public ConfiguredCall LastCallShouldReturn(IReturn value, MatchArgs matchArgs)
{
if (_lastCallRouter.Value == null) throw new CouldNotSetReturnDueToNoLastCallException();
var lastCallRouter = _lastCallRouter.Value;
if (lastCallRouter == null) throw new CouldNotSetReturnDueToNoLastCallException();
if (!lastCallRouter.IsLastCallInfoPresent()) throw new CouldNotSetReturnDueToMissingInfoAboutLastCallException();
if (_argumentSpecifications.Value.Any())
{
//Clear invalid arg specs so they will not affect other tests
_argumentSpecifications.Value.Clear();
throw new UnexpectedArgumentMatcherException();
}
var configuredCall = _lastCallRouter.Value.LastCallShouldReturn(value, matchArgs);
var configuredCall = lastCallRouter.LastCallShouldReturn(value, matchArgs);
ClearLastCallRouter();
return configuredCall;
}

Просмотреть файл

@ -42,6 +42,22 @@ namespace NSubstitute.Exceptions
#endif
}
#if NET35 || NET4 || NET45
[Serializable]
#endif
public class CouldNotSetReturnDueToMissingInfoAboutLastCallException : CouldNotSetReturnException
{
public CouldNotSetReturnDueToMissingInfoAboutLastCallException() : base("Could not find information about the last call to return from.")
{
}
#if NET35 || NET4 || NET45
protected CouldNotSetReturnDueToMissingInfoAboutLastCallException(SerializationInfo info, StreamingContext context) : base(info, context)
{
}
#endif
}
#if NET35 || NET4 || NET45
[Serializable]
#endif

Просмотреть файл

@ -4,16 +4,16 @@ namespace NSubstitute.Routing.Handlers
{
public class ClearUnusedCallSpecHandler : ICallHandler
{
private readonly ISubstituteState _state;
private readonly IPendingSpecification _pendingSpecification;
public ClearUnusedCallSpecHandler(ISubstituteState state)
public ClearUnusedCallSpecHandler(IPendingSpecification pendingSpecification)
{
_state = state;
_pendingSpecification = pendingSpecification;
}
public RouteAction Handle(ICall call)
{
_state.ClearUnusedCallSpecs();
_pendingSpecification.Clear();
return RouteAction.Continue();
}
}

Просмотреть файл

@ -4,19 +4,19 @@ namespace NSubstitute.Routing.Handlers
{
public class RecordCallHandler : ICallHandler
{
private readonly ICallStack _callStack;
private readonly ICallCollection _callCollection;
private readonly SequenceNumberGenerator _generator;
public RecordCallHandler(ICallStack callStack, SequenceNumberGenerator generator)
public RecordCallHandler(ICallCollection callCollection, SequenceNumberGenerator generator)
{
_callStack = callStack;
_callCollection = callCollection;
_generator = generator;
}
public RouteAction Handle(ICall call)
{
call.AssignSequenceNumber(_generator.Next());
_callStack.Push(call);
_callCollection.Add(call);
return RouteAction.Continue();
}
}

Просмотреть файл

@ -18,7 +18,7 @@ namespace NSubstitute.Routing.Handlers
public RouteAction Handle(ICall call)
{
var callSpec = _callSpecificationFactory.CreateFrom(call, MatchArgs.AsSpecifiedInCall);
_pendingCallSpecification.Set(callSpec);
_pendingCallSpecification.SetCallSpecification(callSpec);
_callActions.Add(callSpec);
return RouteAction.Continue();
}

Просмотреть файл

@ -0,0 +1,20 @@
using NSubstitute.Core;
namespace NSubstitute.Routing.Handlers
{
public class TrackLastCallHandler : ICallHandler
{
private readonly IPendingSpecification _pendingSpecification;
public TrackLastCallHandler(IPendingSpecification pendingSpecification)
{
_pendingSpecification = pendingSpecification;
}
public RouteAction Handle(ICall call)
{
_pendingSpecification.SetLastCall(call);
return RouteAction.Continue();
}
}
}

Просмотреть файл

@ -9,7 +9,7 @@ namespace NSubstitute.Routing
public IRoute CallQuery(ISubstituteState state)
{
return new Route(new ICallHandler[] {
new ClearUnusedCallSpecHandler(state)
new ClearUnusedCallSpecHandler(state.PendingSpecification)
, new AddCallToQueryResultHandler(state.SubstitutionContext, state.CallSpecificationFactory)
, new ReturnConfiguredResultHandler(state.CallResults)
, new ReturnAutoValue(AutoValueBehaviour.UseValueForSubsequentCalls, state.AutoValueProviders, state.AutoValuesCallResults, state.CallSpecificationFactory)
@ -20,7 +20,7 @@ namespace NSubstitute.Routing
{
return new Route(new ICallHandler[] {
new ClearLastCallRouterHandler(state.SubstitutionContext)
, new ClearUnusedCallSpecHandler(state)
, new ClearUnusedCallSpecHandler(state.PendingSpecification)
, new CheckReceivedCallsHandler(state.ReceivedCalls, state.CallSpecificationFactory, new ReceivedCallsExceptionThrower(), matchArgs, requiredQuantity)
, new ReturnAutoValue(AutoValueBehaviour.ReturnAndForgetValue, state.AutoValueProviders, state.AutoValuesCallResults, state.CallSpecificationFactory)
, ReturnDefaultForReturnTypeHandler()
@ -30,7 +30,7 @@ namespace NSubstitute.Routing
{
return new Route(new ICallHandler[] {
new ClearLastCallRouterHandler(state.SubstitutionContext)
, new ClearUnusedCallSpecHandler(state)
, new ClearUnusedCallSpecHandler(state.PendingSpecification)
, new SetActionForCallHandler(state.CallSpecificationFactory, state.CallActions, doAction, matchArgs)
, ReturnDefaultForReturnTypeHandler()
});
@ -39,7 +39,7 @@ namespace NSubstitute.Routing
{
return new Route(new ICallHandler[] {
new ClearLastCallRouterHandler(state.SubstitutionContext)
, new ClearUnusedCallSpecHandler(state)
, new ClearUnusedCallSpecHandler(state.PendingSpecification)
, new DoNotCallBaseForCallHandler(state.CallSpecificationFactory, state.CallBaseExclusions, matchArgs)
, ReturnDefaultForReturnTypeHandler()
});
@ -48,7 +48,7 @@ namespace NSubstitute.Routing
{
return new Route(new ICallHandler[] {
new ClearLastCallRouterHandler(state.SubstitutionContext)
, new ClearUnusedCallSpecHandler(state)
, new ClearUnusedCallSpecHandler(state.PendingSpecification)
, new RaiseEventHandler(state.EventHandlerRegistry, getEventArguments)
, ReturnDefaultForReturnTypeHandler()
});
@ -67,8 +67,9 @@ namespace NSubstitute.Routing
public IRoute RecordReplay(ISubstituteState state)
{
return new Route(RouteType.RecordReplay, new ICallHandler[] {
new ClearUnusedCallSpecHandler(state)
, new RecordCallHandler(state.CallStack, state.SequenceNumberGenerator)
new ClearUnusedCallSpecHandler(state.PendingSpecification)
, new TrackLastCallHandler(state.PendingSpecification)
, new RecordCallHandler(state.CallCollection, state.SequenceNumberGenerator)
, new EventSubscriptionHandler(state.EventHandlerRegistry)
, new PropertySetterHandler(new PropertyHelper(), state.ConfigureCall)
, new DoActionsCallHandler(state.CallActions)