Skip to content

Commit ab6733c

Browse files
committed
Extract ICallInfo interfaces (#641)
Apply review comments.
1 parent 035b939 commit ab6733c

18 files changed

+152
-116
lines changed

src/NSubstitute/Callback.cs

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ public class Callback
1818
/// </summary>
1919
/// <param name="doThis"></param>
2020
/// <returns></returns>
21-
public static ConfiguredCallback First(Action<CallInfo> doThis)
21+
public static ConfiguredCallback First(Action<ICallInfo> doThis)
2222
{
2323
return new ConfiguredCallback().Then(doThis);
2424
}
@@ -28,7 +28,7 @@ public static ConfiguredCallback First(Action<CallInfo> doThis)
2828
/// </summary>
2929
/// <param name="doThis"></param>
3030
/// <returns></returns>
31-
public static Callback Always(Action<CallInfo> doThis)
31+
public static Callback Always(Action<ICallInfo> doThis)
3232
{
3333
return new ConfiguredCallback().AndAlways(doThis);
3434
}
@@ -38,7 +38,7 @@ public static Callback Always(Action<CallInfo> doThis)
3838
/// </summary>
3939
/// <param name="throwThis"></param>
4040
/// <returns></returns>
41-
public static ConfiguredCallback FirstThrow<TException>(Func<CallInfo, TException> throwThis) where TException : Exception
41+
public static ConfiguredCallback FirstThrow<TException>(Func<ICallInfo, TException> throwThis) where TException : Exception
4242
{
4343
return new ConfiguredCallback().ThenThrow(throwThis);
4444
}
@@ -59,7 +59,7 @@ public static ConfiguredCallback FirstThrow<TException>(TException exception) wh
5959
/// <typeparam name="TException">The type of the exception.</typeparam>
6060
/// <param name="throwThis">The throw this.</param>
6161
/// <returns></returns>
62-
public static Callback AlwaysThrow<TException>(Func<CallInfo, TException> throwThis) where TException : Exception
62+
public static Callback AlwaysThrow<TException>(Func<ICallInfo, TException> throwThis) where TException : Exception
6363
{
6464
return new ConfiguredCallback().AndAlways(ToCallback(throwThis));
6565
}
@@ -75,33 +75,33 @@ public static Callback AlwaysThrow<TException>(TException exception) where TExce
7575
return AlwaysThrow(_ => exception);
7676
}
7777

78-
protected static Action<CallInfo> ToCallback<TException>(Func<CallInfo, TException> throwThis)
78+
protected static Action<ICallInfo> ToCallback<TException>(Func<ICallInfo, TException> throwThis)
7979
where TException : notnull, Exception
8080
{
8181
return ci => { if (throwThis != null) throw throwThis(ci); };
8282
}
8383

8484
internal Callback() { }
85-
private readonly ConcurrentQueue<Action<CallInfo>> callbackQueue = new ConcurrentQueue<Action<CallInfo>>();
86-
private Action<CallInfo> alwaysDo = x => { };
87-
private Action<CallInfo> keepDoing = x => { };
85+
private readonly ConcurrentQueue<Action<ICallInfo>> callbackQueue = new ConcurrentQueue<Action<ICallInfo>>();
86+
private Action<ICallInfo> alwaysDo = x => { };
87+
private Action<ICallInfo> keepDoing = x => { };
8888

89-
protected void AddCallback(Action<CallInfo> doThis)
89+
protected void AddCallback(Action<ICallInfo> doThis)
9090
{
9191
callbackQueue.Enqueue(doThis);
9292
}
9393

94-
protected void SetAlwaysDo(Action<CallInfo> always)
94+
protected void SetAlwaysDo(Action<ICallInfo> always)
9595
{
9696
alwaysDo = always ?? (_ => { });
9797
}
9898

99-
protected void SetKeepDoing(Action<CallInfo> keep)
99+
protected void SetKeepDoing(Action<ICallInfo> keep)
100100
{
101101
keepDoing = keep ?? (_ => { });
102102
}
103103

104-
public void Call(CallInfo callInfo)
104+
public void Call(ICallInfo callInfo)
105105
{
106106
try
107107
{
@@ -113,7 +113,7 @@ public void Call(CallInfo callInfo)
113113
}
114114
}
115115

116-
private void CallFromStack(CallInfo callInfo)
116+
private void CallFromStack(ICallInfo callInfo)
117117
{
118118
if (callbackQueue.TryDequeue(out var callback))
119119
{

src/NSubstitute/Callbacks/ConfiguredCallback.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ internal ConfiguredCallback() { }
1313
/// <summary>
1414
/// Perform this action once in chain of called callbacks.
1515
/// </summary>
16-
public ConfiguredCallback Then(Action<CallInfo> doThis)
16+
public ConfiguredCallback Then(Action<ICallInfo> doThis)
1717
{
1818
AddCallback(doThis);
1919
return this;
@@ -22,7 +22,7 @@ public ConfiguredCallback Then(Action<CallInfo> doThis)
2222
/// <summary>
2323
/// Keep doing this action after the other callbacks have run.
2424
/// </summary>
25-
public EndCallbackChain ThenKeepDoing(Action<CallInfo> doThis)
25+
public EndCallbackChain ThenKeepDoing(Action<ICallInfo> doThis)
2626
{
2727
SetKeepDoing(doThis);
2828
return this;
@@ -31,7 +31,7 @@ public EndCallbackChain ThenKeepDoing(Action<CallInfo> doThis)
3131
/// <summary>
3232
/// Keep throwing this exception after the other callbacks have run.
3333
/// </summary>
34-
public EndCallbackChain ThenKeepThrowing<TException>(Func<CallInfo, TException> throwThis) where TException : Exception =>
34+
public EndCallbackChain ThenKeepThrowing<TException>(Func<ICallInfo, TException> throwThis) where TException : Exception =>
3535
ThenKeepDoing(ToCallback(throwThis));
3636

3737
/// <summary>
@@ -45,7 +45,7 @@ public EndCallbackChain ThenKeepThrowing<TException>(TException throwThis) where
4545
/// </summary>
4646
/// <typeparam name="TException">The type of the exception</typeparam>
4747
/// <param name="throwThis">Produce the exception to throw for a CallInfo</param>
48-
public ConfiguredCallback ThenThrow<TException>(Func<CallInfo, TException> throwThis) where TException : Exception
48+
public ConfiguredCallback ThenThrow<TException>(Func<ICallInfo, TException> throwThis) where TException : Exception
4949
{
5050
AddCallback(ToCallback(throwThis));
5151
return this;
@@ -68,7 +68,7 @@ internal EndCallbackChain() { }
6868
/// Perform the given action for every call.
6969
/// </summary>
7070
/// <param name="doThis">The action to perform for every call</param>
71-
public Callback AndAlways(Action<CallInfo> doThis)
71+
public Callback AndAlways(Action<ICallInfo> doThis)
7272
{
7373
SetAlwaysDo(doThis);
7474
return this;

src/NSubstitute/Core/CallInfo.cs

Lines changed: 24 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,12 @@
99

1010
namespace NSubstitute.Core
1111
{
12-
public class CallInfo
12+
public class CallInfo : ICallInfo
1313
{
1414
private readonly Argument[] _callArguments;
1515
private readonly Func<Maybe<object>> _baseResult;
1616

17-
public CallInfo(Argument[] callArguments, Func<Maybe<object>> baseResult)
18-
{
17+
public CallInfo(Argument[] callArguments, Func<Maybe<object>> baseResult) {
1918
_callArguments = callArguments;
2019
_baseResult = baseResult;
2120
}
@@ -33,63 +32,41 @@ protected object GetBaseResult() {
3332
return _baseResult().ValueOr(() => throw new NoBaseImplementationException());
3433
}
3534

36-
/// <summary>
37-
/// Gets the nth argument to this call.
38-
/// </summary>
39-
/// <param name="index">Index of argument</param>
40-
/// <returns>The value of the argument at the given index</returns>
41-
public object this[int index]
42-
{
35+
/// <inheritdoc/>
36+
public object this[int index] {
4337
get => _callArguments[index].Value;
44-
set
45-
{
38+
set {
4639
var argument = _callArguments[index];
4740
EnsureArgIsSettable(argument, index, value);
4841
argument.Value = value;
4942
}
5043
}
5144

52-
private void EnsureArgIsSettable(Argument argument, int index, object value)
53-
{
54-
if (!argument.IsByRef)
55-
{
45+
private void EnsureArgIsSettable(Argument argument, int index, object value) {
46+
if (!argument.IsByRef) {
5647
throw new ArgumentIsNotOutOrRefException(index, argument.DeclaredType);
5748
}
5849

59-
if (value != null && !argument.CanSetValueWithInstanceOf(value.GetType()))
60-
{
50+
if (value != null && !argument.CanSetValueWithInstanceOf(value.GetType())) {
6151
throw new ArgumentSetWithIncompatibleValueException(index, argument.DeclaredType, value.GetType());
6252
}
6353
}
6454

65-
/// <summary>
66-
/// Get the arguments passed to this call.
67-
/// </summary>
68-
/// <returns>Array of all arguments passed to this call</returns>
55+
/// <inheritdoc/>
6956
public object[] Args() => _callArguments.Select(x => x.Value).ToArray();
7057

71-
/// <summary>
72-
/// Gets the types of all the arguments passed to this call.
73-
/// </summary>
74-
/// <returns>Array of types of all arguments passed to this call</returns>
58+
/// <inheritdoc/>
7559
public Type[] ArgTypes() => _callArguments.Select(x => x.DeclaredType).ToArray();
7660

77-
/// <summary>
78-
/// Gets the argument of type `T` passed to this call. This will throw if there are no arguments
79-
/// of this type, or if there is more than one matching argument.
80-
/// </summary>
81-
/// <typeparam name="T">The type of the argument to retrieve</typeparam>
82-
/// <returns>The argument passed to the call, or throws if there is not exactly one argument of this type</returns>
83-
public T Arg<T>()
84-
{
61+
/// <inheritdoc/>
62+
public T Arg<T>() {
8563
T arg;
8664
if (TryGetArg(x => x.IsDeclaredTypeEqualToOrByRefVersionOf(typeof(T)), out arg)) return arg;
8765
if (TryGetArg(x => x.IsValueAssignableTo(typeof(T)), out arg)) return arg;
8866
throw new ArgumentNotFoundException("Can not find an argument of type " + typeof(T).FullName + " to this call.");
8967
}
9068

91-
private bool TryGetArg<T>(Func<Argument, bool> condition, [MaybeNullWhen(false)] out T value)
92-
{
69+
private bool TryGetArg<T>(Func<Argument, bool> condition, [MaybeNullWhen(false)] out T value) {
9370
value = default;
9471

9572
var matchingArgs = _callArguments.Where(condition);
@@ -100,10 +77,8 @@ private bool TryGetArg<T>(Func<Argument, bool> condition, [MaybeNullWhen(false)]
10077
return true;
10178
}
10279

103-
private void ThrowIfMoreThanOne<T>(IEnumerable<Argument> arguments)
104-
{
105-
if (arguments.Skip(1).Any())
106-
{
80+
private void ThrowIfMoreThanOne<T>(IEnumerable<Argument> arguments) {
81+
if (arguments.Skip(1).Any()) {
10782
throw new AmbiguousArgumentsException(
10883
"There is more than one argument of type " + typeof(T).FullName + " to this call.\n" +
10984
"The call signature is (" + DisplayTypes(ArgTypes()) + ")\n" +
@@ -112,33 +87,24 @@ private void ThrowIfMoreThanOne<T>(IEnumerable<Argument> arguments)
11287
}
11388
}
11489

115-
/// <summary>
116-
/// Gets the argument passed to this call at the specified zero-based position, converted to type `T`.
117-
/// This will throw if there are no arguments, if the argument is out of range or if it
118-
/// cannot be converted to the specified type.
119-
/// </summary>
120-
/// <typeparam name="T">The type of the argument to retrieve</typeparam>
121-
/// <param name="position">The zero-based position of the argument to retrieve</param>
122-
/// <returns>The argument passed to the call, or throws if there is not exactly one argument of this type</returns>
123-
public T ArgAt<T>(int position)
124-
{
125-
if (position >= _callArguments.Length)
126-
{
90+
/// <inheritdoc/>
91+
public T ArgAt<T>(int position) {
92+
if (position >= _callArguments.Length) {
12793
throw new ArgumentOutOfRangeException(nameof(position), $"There is no argument at position {position}");
12894
}
12995

130-
try
131-
{
132-
return (T) _callArguments[position].Value!;
133-
}
134-
catch (InvalidCastException)
135-
{
96+
try {
97+
return (T)_callArguments[position].Value!;
98+
} catch (InvalidCastException) {
13699
throw new InvalidCastException(
137100
$"Couldn't convert parameter at position {position} to type {typeof(T).FullName}");
138101
}
139102
}
140103

141104
private static string DisplayTypes(IEnumerable<Type> types) =>
142105
string.Join(", ", types.Select(x => x.Name).ToArray());
106+
107+
/// <inheritdoc/>
108+
public ICallInfo<T> ForCallReturning<T>() => new CallInfo<T>(this);
143109
}
144110
}

src/NSubstitute/Core/CallInfoWithReturns.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
/// Information for a call that returns a value of type <c>T</c>.
55
/// </summary>
66
/// <typeparam name="T"></typeparam>
7-
public class CallInfo<T> : CallInfo
7+
public class CallInfo<T> : CallInfo, ICallInfo<T>
88
{
99
internal CallInfo(CallInfo info) : base(info) {
1010
}

src/NSubstitute/Core/IReturn.cs

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,13 @@ namespace NSubstitute.Core
99
{
1010
public interface IReturn
1111
{
12-
object? ReturnFor(CallInfo info);
12+
object? ReturnFor(ICallInfo info);
1313
Type? TypeOrNull();
1414
bool CanBeAssignedTo(Type t);
1515
}
1616

1717
/// <summary>
18-
/// Performance optimization. Allows to not construct <see cref="CallInfo"/> if configured result doesn't depend on it.
18+
/// Performance optimization. Allows to not construct <see cref="ICallInfo"/> if configured result doesn't depend on it.
1919
/// </summary>
2020
internal interface ICallIndependentReturn
2121
{
@@ -32,25 +32,25 @@ public ReturnValue(object? value)
3232
}
3333

3434
public object? GetReturnValue() => _value;
35-
public object? ReturnFor(CallInfo info) => GetReturnValue();
35+
public object? ReturnFor(ICallInfo info) => GetReturnValue();
3636
public Type? TypeOrNull() => _value?.GetType();
3737
public bool CanBeAssignedTo(Type t) => _value.IsCompatibleWith(t);
3838
}
3939

4040
public class ReturnValueFromFunc<T> : IReturn
4141
{
42-
private readonly Func<CallInfo<T>, T?> _funcToReturnValue;
42+
private readonly Func<ICallInfo<T>, T?> _funcToReturnValue;
4343

44-
public ReturnValueFromFunc(Func<CallInfo<T>, T?>? funcToReturnValue)
44+
public ReturnValueFromFunc(Func<ICallInfo<T>, T?>? funcToReturnValue)
4545
{
4646
_funcToReturnValue = funcToReturnValue ?? ReturnNull();
4747
}
4848

49-
public object? ReturnFor(CallInfo info) => _funcToReturnValue(new CallInfo<T>(info));
49+
public object? ReturnFor(ICallInfo info) => _funcToReturnValue(info.ForCallReturning<T>());
5050
public Type TypeOrNull() => typeof(T);
5151
public bool CanBeAssignedTo(Type t) => typeof(T).IsAssignableFrom(t);
5252

53-
private static Func<CallInfo, T?> ReturnNull()
53+
private static Func<ICallInfo, T?> ReturnNull()
5454
{
5555
if (typeof(T).GetTypeInfo().IsValueType) throw new CannotReturnNullForValueType(typeof(T));
5656
return x => default;
@@ -69,7 +69,7 @@ public ReturnMultipleValues(T?[] values)
6969
}
7070

7171
public object? GetReturnValue() => GetNext();
72-
public object? ReturnFor(CallInfo info) => GetReturnValue();
72+
public object? ReturnFor(ICallInfo info) => GetReturnValue();
7373
public Type TypeOrNull() => typeof(T);
7474
public bool CanBeAssignedTo(Type t) => typeof(T).IsAssignableFrom(t);
7575

@@ -78,20 +78,22 @@ public ReturnMultipleValues(T?[] values)
7878

7979
public class ReturnMultipleFuncsValues<T> : IReturn
8080
{
81-
private readonly ConcurrentQueue<Func<CallInfo<T>, T?>> _funcsToReturn;
82-
private readonly Func<CallInfo<T>, T?> _lastFunc;
81+
private readonly ConcurrentQueue<Func<ICallInfo<T>, T?>> _funcsToReturn;
82+
private readonly Func<ICallInfo<T>, T?> _lastFunc;
8383

84-
public ReturnMultipleFuncsValues(Func<CallInfo<T>, T?>[] funcs)
84+
public ReturnMultipleFuncsValues(Func<ICallInfo<T>, T?>[] funcs)
8585
{
86-
_funcsToReturn = new ConcurrentQueue<Func<CallInfo<T>, T?>>(funcs);
86+
_funcsToReturn = new ConcurrentQueue<Func<ICallInfo<T>, T?>>(funcs);
8787
_lastFunc = funcs.Last();
8888
}
8989

90-
public object? ReturnFor(CallInfo info) => GetNext(info);
90+
public object? ReturnFor(ICallInfo info) => GetNext(info);
9191
public Type TypeOrNull() => typeof(T);
9292
public bool CanBeAssignedTo(Type t) => typeof(T).IsAssignableFrom(t);
9393

94-
private T? GetNext(CallInfo info) =>
95-
_funcsToReturn.TryDequeue(out var nextFunc) ? nextFunc(new CallInfo<T>(info)) : _lastFunc(new CallInfo<T>(info));
94+
private T? GetNext(ICallInfo info) =>
95+
_funcsToReturn.TryDequeue(out var nextFunc)
96+
? nextFunc(info.ForCallReturning<T>())
97+
: _lastFunc(info.ForCallReturning<T>());
9698
}
9799
}

src/NSubstitute/Core/WhenCalled.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public WhenCalled(ISubstitutionContext context, T substitute, Action<T> call, Ma
2929
/// Perform this action when called.
3030
/// </summary>
3131
/// <param name="callbackWithArguments"></param>
32-
public void Do(Action<CallInfo> callbackWithArguments)
32+
public void Do(Action<ICallInfo> callbackWithArguments)
3333
{
3434
_threadContext.SetNextRoute(_callRouter, x => _routeFactory.DoWhenCalled(x, callbackWithArguments, _matchArgs));
3535
_call(_substitute);
@@ -82,7 +82,7 @@ public void Throw(Exception exception) =>
8282
/// <summary>
8383
/// Throw an exception generated by the specified function when called.
8484
/// </summary>
85-
public void Throw(Func<CallInfo, Exception> createException) =>
85+
public void Throw(Func<ICallInfo, Exception> createException) =>
8686
Do(ci => throw createException(ci));
8787
}
8888
}

0 commit comments

Comments
 (0)