question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Proposal for GenerationDepthBehavior.

See original GitHub issue

Here is a generation Depth limiter behavior proposal.

fixture.Behaviors.Add(new GenerationDepthBehavior(2));

Had to re-implement ComposeIfMultiple for test because it’s internal.

public class GenerationDepthBehavior : ISpecimenBuilderTransformation
{
    private const int DefaultGenerationDepth = 1;
    private readonly int generationDepth;

    public GenerationDepthBehavior() : this(DefaultGenerationDepth)
    {
    }

    public GenerationDepthBehavior(int generationDepth)
    {
        if (generationDepth < 1)
            throw new ArgumentOutOfRangeException(nameof(generationDepth), "Generation depth must be greater than 0.");

        this.generationDepth = generationDepth;
    }


    public ISpecimenBuilderNode Transform(ISpecimenBuilder builder)
    {
        if (builder == null) throw new ArgumentNullException(nameof(builder));

        return new GenerationDepthGuard(builder, new GenerationDepthHandler(), this.generationDepth);
    }
}

public interface IGenerationDepthHandler
{

    object HandleGenerationDepthLimitRequest(object request, IEnumerable<object> recordedRequests, int depth);
}

public class DepthSeededRequest : SeededRequest
{
    public int Depth { get; }
    public DepthSeededRequest(object request, object seed, int depth) : base(request, seed)
    {
        Depth = depth;
    }
}

public class GenerationDepthGuard : ISpecimenBuilderNode
{
    private readonly ThreadLocal<Stack<DepthSeededRequest>> requestsByThread
        = new ThreadLocal<Stack<DepthSeededRequest>>(() => new Stack<DepthSeededRequest>());

    private Stack<DepthSeededRequest> GetMonitoredRequestsForCurrentThread() => this.requestsByThread.Value;


    public GenerationDepthGuard(ISpecimenBuilder builder)
        : this(builder, EqualityComparer<object>.Default)
    {
    }

    public GenerationDepthGuard(
        ISpecimenBuilder builder,
        IGenerationDepthHandler depthHandler)
        : this(
            builder,
            depthHandler,
            EqualityComparer<object>.Default,
            1)
    {
    }

    public GenerationDepthGuard(
        ISpecimenBuilder builder,
        IGenerationDepthHandler depthHandler,
        int generationDepth)
        : this(
            builder,
            depthHandler,
            EqualityComparer<object>.Default,
            generationDepth)
    {
    }


    public GenerationDepthGuard(ISpecimenBuilder builder, IEqualityComparer comparer)
    {
        this.Builder = builder ?? throw new ArgumentNullException(nameof(builder));
        this.Comparer = comparer ?? throw new ArgumentNullException(nameof(comparer));
        this.GenerationDepth = 1;
    }


    public GenerationDepthGuard(
        ISpecimenBuilder builder,
        IGenerationDepthHandler depthHandler,
        IEqualityComparer comparer)
        : this(
        builder,
        depthHandler,
        comparer,
        1)
    {
    }

    public GenerationDepthGuard(
        ISpecimenBuilder builder,
        IGenerationDepthHandler depthHandler,
        IEqualityComparer comparer,
        int generationDepth)
    {
        if (builder == null) throw new ArgumentNullException(nameof(builder));
        if (depthHandler == null) throw new ArgumentNullException(nameof(depthHandler));
        if (comparer == null) throw new ArgumentNullException(nameof(comparer));
        if (generationDepth < 1)
            throw new ArgumentOutOfRangeException(nameof(generationDepth), "Generation depth must be greater than 0.");

        this.Builder = builder;
        this.GenerationDepthHandler = depthHandler;
        this.Comparer = comparer;
        this.GenerationDepth = generationDepth;
    }


    public ISpecimenBuilder Builder { get; }

    public IGenerationDepthHandler GenerationDepthHandler { get; }

    public int GenerationDepth { get; }

    public int CurrentDepth { get; }

    public IEqualityComparer Comparer { get; }

    protected IEnumerable RecordedRequests => this.GetMonitoredRequestsForCurrentThread();

    public virtual object HandleGenerationDepthLimitRequest(object request, int currentDepth)
    {
        return this.GenerationDepthHandler.HandleGenerationDepthLimitRequest(
            request,
            this.GetMonitoredRequestsForCurrentThread(), currentDepth);
    }

    public object Create(object request, ISpecimenContext context)
    {
        if (request is SeededRequest)
        {
            int currentDepth = -1;

            var requestsForCurrentThread = this.GetMonitoredRequestsForCurrentThread();
            
            if (requestsForCurrentThread.Count > 0)
            {
                currentDepth = requestsForCurrentThread.Max(x => x.Depth) + 1;
            }

            DepthSeededRequest depthRequest = new DepthSeededRequest(((SeededRequest)request).Request, ((SeededRequest)request).Seed, currentDepth);

            if (depthRequest.Depth >= this.GenerationDepth)
            {
                return HandleGenerationDepthLimitRequest(request, depthRequest.Depth);
            }

            requestsForCurrentThread.Push(depthRequest);
            try
            {
                return this.Builder.Create(request, context);
            }
            finally
            {
                requestsForCurrentThread.Pop();
            }
        }
        else
        {
            return this.Builder.Create(request, context);
        }
    }

    public virtual ISpecimenBuilderNode Compose(
        IEnumerable<ISpecimenBuilder> builders)
    {
        var composedBuilder = ComposeIfMultiple(
            builders);
        return new GenerationDepthGuard(
            composedBuilder,
            this.GenerationDepthHandler,
            this.Comparer,
            this.GenerationDepth);
    }

    internal static ISpecimenBuilder ComposeIfMultiple(IEnumerable<ISpecimenBuilder> builders)
    {
        ISpecimenBuilder singleItem = null;
        List<ISpecimenBuilder> multipleItems = null;
        bool hasItems = false;

        using (var enumerator = builders.GetEnumerator())
        {
            if (enumerator.MoveNext())
            {
                singleItem = enumerator.Current;
                hasItems = true;

                while (enumerator.MoveNext())
                {
                    if (multipleItems == null)
                    {
                        multipleItems = new List<ISpecimenBuilder> { singleItem };
                    }

                    multipleItems.Add(enumerator.Current);
                }
            }
        }

        if (!hasItems)
        {
            return new CompositeSpecimenBuilder();
        }

        if (multipleItems == null)
        {
            return singleItem;
        }

        return new CompositeSpecimenBuilder(multipleItems);
    }

    public virtual IEnumerator<ISpecimenBuilder> GetEnumerator()
    {
        yield return this.Builder;
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return this.GetEnumerator();
    }
}

public class GenerationDepthHandler : IGenerationDepthHandler
{
    public object HandleGenerationDepthLimitRequest(
        object request,
        IEnumerable<object> recordedRequests, int depth)
    {
        return new OmitSpecimen();
    }
}

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:13 (7 by maintainers)

github_iconTop GitHub Comments

3reactions
zvirjacommented, May 10, 2018

@malylemire1 Would you be fine if we postpone this for now and implement it later only if we see a lot of requests for this feature?

I’m sorry we cannot include every desired feature to the core 😟 It’s a bit hard to keep the balance between the provided feature and and the amount of code we maintain here. It looks like it will not be a big problem for you to implement this ad-hoc feature in your solution, so you are not blocked.

Thanks again for your proposal!

1reaction
mrukascommented, Jun 7, 2023

Its sad that this has not been built into the core. I have the exact same problem and have to use the workaround for now.

Read more comments on GitHub >

github_iconTop Results From Across the Web

Developers - Proposal for GenerationDepthBehavior. -
Coming soon: A brand new website interface for an even better experience!
Read more >
Object Proposal Generation
Object proposal generation is a preprocessing technique that has been widely used in current object detection pipelines to guide the search of objects...
Read more >
Temporal Action Proposal Generation with Transformers
To capture the dependencies at different levels of granularity, this paper intuitively presents a unified temporal action proposal generation ...
Read more >
A Hybrid Approach for Object Proposal Generation
This paper presents an efficient new hybrid object proposal method, which gets the initial proposal by computing multiple hierarchical ...
Read more >
Generating Object Proposals | pdollar - WordPress.com
Each generated foreground mask serves as an object proposal, the proposals are ranked according to a learned scoring function. The algorithm is ...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found