📄 core/tests/CallGraphBuilderTests/BuildCallGraphShould.cs
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using GlobExpressions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text;
using Shouldly;
using Xunit;

namespace Cfa.Tests.CallGraphBuilderTests;

public sealed class BuildCallGraphShould
{
    [Fact]
    public async Task ExcludeTestProjectMembersButNotProductionFilesNamedTests()
    {
        // Arrange
        using var workspace = new AdhocWorkspace();
        var app = workspace.AddProject(
            ProjectInfo.Create(
                ProjectId.CreateNewId(),
                VersionStamp.Default,
                "App",
                "App",
                LanguageNames.CSharp,
                filePath: "/repo/App.csproj",
                compilationOptions: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
                metadataReferences: [MetadataReference.CreateFromFile(typeof(object).Assembly.Location)]
            )
        );
        var tests = workspace.AddProject(
            ProjectInfo.Create(
                ProjectId.CreateNewId(),
                VersionStamp.Default,
                "App.Tests",
                "App.Tests",
                LanguageNames.CSharp,
                filePath: "/repo/App.Tests.csproj",
                compilationOptions: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary),
                metadataReferences: [MetadataReference.CreateFromFile(typeof(object).Assembly.Location)],
                projectReferences: [new ProjectReference(app.Id)]
            )
        );
        workspace.AddDocument(
            app.Id,
            "Tests.cs",
            SourceText.From(
                """
                public class App
                {
                    public static void Target() { }
                    public static void Caller() { Target(); }
                }
                """
            )
        );
        workspace.AddDocument(
            tests.Id,
            "Production.cs",
            SourceText.From(
                """
                class Tests
                {
                    void Start() { Helper(); App.Target(); }
                    void Helper() { }
                }
                """
            )
        );
        var solution = workspace.CurrentSolution;
        var appCompilation = (await solution.GetProject(app.Id)!.GetCompilationAsync()).ShouldNotBeNull();
        var testCompilation = (await solution.GetProject(tests.Id)!.GetCompilationAsync()).ShouldNotBeNull();
        appCompilation.GetDiagnostics().ShouldBeEmpty();
        testCompilation.GetDiagnostics().ShouldBeEmpty();
        var start = testCompilation.GetTypeByMetadataName("Tests")!.GetMembers("Start").Single();
        var target = appCompilation.GetTypeByMetadataName("App")!.GetMembers("Target").Single();
        var builder = new CallGraphBuilder(
            new ScannerOptions { ExcludePatterns = [new("*Tests*")] },
            solution,
            [appCompilation, testCompilation]
        );

        // Act
        builder.FindCallees(start, CancellationToken.None);
        await builder.FindCallers(target, CancellationToken.None);

        // Assert
        builder.ReachableFromSymbols[start].ShouldHaveSingleItem().Target.Name.ShouldBe("Target");
        builder.ReachesToSymbols[target].ShouldHaveSingleItem().Target.Name.ShouldBe("Caller");
    }
}