TNG / ArchUnitNET

A C# architecture test library to specify and assert architecture rules in C# for automated testing.
Apache License 2.0
826 stars 55 forks source link

Analyse dependencies of async lambdas #230

Open penenkel opened 3 months ago

penenkel commented 3 months ago

Summary

It seems as if the dependencies of async lambda functions are not enumerated correctly.

Scenario

Consider a class C2 that calls a method on class C1 and passes a lambda function which calls a method on class C3.

    class C1 { 
        public void Register(Func<Task> action) { } 
    }
    class C2 { 
        void Foo(C1 b1) { 
            b1.Register(() => { C3.Bar1(); return Task.CompletedTask; });
            b1.Register(async () => { C3.Bar2(); await Task.CompletedTask; });
        } 
    }
    class C3 { 
        public static void Bar1() { }
        public static void Bar2() { }
    }

I encountered this in the context of AspNetCore MinimalApi endpoint registrations thus the convoluted example. It is probable that the problem could be reproduced with a simpler example.

Expectation

Then I would expect the following test to successfully verify that both methods C3.Bar1 and C3.Bar2 are called from within C2.

    [Fact]
    public void ShouldDetectCallsInLambdas()
    {
        var architecture = new ArchLoader().LoadAssemblies([GetType().Assembly]).Build();

        var rule1 = Types().That().Are(typeof(C2)).Should().CallAny("C3::Bar1", true);
        var rule2 = Types().That().Are(typeof(C2)).Should().CallAny("C3::Bar2", true);
        var rule = rule1.And(rule2);

        rule.Check(architecture);
    }

Actual result

But it fails to do so for Bar2 which is called from an async lambda. This probably due to the compiler generated state-machine, similar to #138.