Created
May 31, 2021 18:44
-
-
Save aivascu/b3357f3875f97c80f35a3c23127d6d20 to your computer and use it in GitHub Desktop.
AutoFixture depth guard
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
using System.Collections.Generic; | |
namespace AutoFixture | |
{ | |
public interface IDepthHandler | |
{ | |
object Handle(object request, Stack<object> recordedRequests); | |
} | |
} |
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
using System; | |
using AutoFixture.Kernel; | |
namespace AutoFixture | |
{ | |
public class OmitOnRequestDepthBehavior : ISpecimenBuilderTransformation | |
{ | |
private const int DefaultRequestDepth = 10; | |
private readonly int requestDepth; | |
public OmitOnRequestDepthBehavior() | |
: this(DefaultRequestDepth) | |
{ | |
} | |
public OmitOnRequestDepthBehavior(int requestDepth) | |
{ | |
if (requestDepth < 1) | |
throw new ArgumentOutOfRangeException(nameof(requestDepth), "Recursion depth must be greater than 0."); | |
this.requestDepth = requestDepth; | |
} | |
public ISpecimenBuilderNode Transform(ISpecimenBuilder builder) | |
{ | |
if (builder == null) throw new ArgumentNullException(nameof(builder)); | |
return new RequestDepthGuard(builder, new OmitOnRequestDepthHandler(), this.requestDepth); | |
} | |
} | |
} |
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
using System.Collections.Generic; | |
using AutoFixture.Kernel; | |
namespace AutoFixture | |
{ | |
public class OmitOnRequestDepthHandler : IDepthHandler | |
{ | |
public object Handle(object request, Stack<object> recordedRequests) | |
{ | |
return new OmitSpecimen(); | |
} | |
} | |
} |
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
using System; | |
using System.Collections; | |
using System.Collections.Generic; | |
using System.Reflection; | |
using System.Threading; | |
using AutoFixture.Kernel; | |
namespace AutoFixture | |
{ | |
public class RequestDepthGuard : ISpecimenBuilderNode | |
{ | |
private readonly ThreadLocal<Stack<object>> requestsByThread | |
= new(() => new Stack<object>()); | |
private Stack<object> GetMonitoredRequestsForCurrentThread() => this.requestsByThread.Value; | |
public RequestDepthGuard(ISpecimenBuilder builder, | |
IDepthHandler depthHandler, | |
int requestDepth) | |
{ | |
if (requestDepth < 1) | |
throw new ArgumentOutOfRangeException(nameof(requestDepth), "Recursion depth must be greater than 0."); | |
this.Builder = builder ?? throw new ArgumentNullException(nameof(builder)); | |
this.DepthHandler = depthHandler ?? throw new ArgumentNullException(nameof(depthHandler)); | |
this.RequestDepth = requestDepth; | |
} | |
public int RequestDepth { get; set; } | |
public IDepthHandler DepthHandler { get; set; } | |
public ISpecimenBuilder Builder { get; set; } | |
public object Create(object request, ISpecimenContext context) | |
{ | |
var requestsForCurrentThread = this.GetMonitoredRequestsForCurrentThread(); | |
if (requestsForCurrentThread.Count >= this.RequestDepth && request is PropertyInfo) | |
{ | |
return this.DepthHandler.Handle(request, this.GetMonitoredRequestsForCurrentThread()); | |
} | |
if(request is PropertyInfo) | |
requestsForCurrentThread.Push(request); | |
try | |
{ | |
return this.Builder.Create(request, context); | |
} | |
finally | |
{ | |
if(request is PropertyInfo) | |
requestsForCurrentThread.Pop(); | |
} | |
} | |
public IEnumerator<ISpecimenBuilder> GetEnumerator() | |
{ | |
yield return this.Builder; | |
} | |
public ISpecimenBuilderNode Compose(IEnumerable<ISpecimenBuilder> builders) | |
{ | |
if (builders == null) throw new ArgumentNullException(nameof(builders)); | |
var composedBuilder = CompositeSpecimenBuilder.ComposeIfMultiple(builders); | |
return new RequestDepthGuard(composedBuilder, this.DepthHandler, this.RequestDepth); | |
} | |
IEnumerator IEnumerable.GetEnumerator() | |
{ | |
return this.GetEnumerator(); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment