From 27a5cbfb2702e7bc57a7dba2f39ccfef83f98d0f Mon Sep 17 00:00:00 2001 From: Andrew Morger Date: Thu, 17 Aug 2023 06:47:12 -0500 Subject: [PATCH 1/3] Adds Excluded projects to the cli options --- src/LivingDocumentation.Analyzer/Options.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/LivingDocumentation.Analyzer/Options.cs b/src/LivingDocumentation.Analyzer/Options.cs index 76315f0..eb80c52 100644 --- a/src/LivingDocumentation.Analyzer/Options.cs +++ b/src/LivingDocumentation.Analyzer/Options.cs @@ -10,6 +10,9 @@ public class Options [Option("project", Required = true, SetName = "project", HelpText = "The project to analyze.")] public string? ProjectPath { get; set; } + [Option("exclude", Required = false, SetName = "solution", Separator = ',', HelpText = "Any projects to exclude from analysis.")] + public IEnumerable ExcludedProjectPaths { get; set; } = Enumerable.Empty(); + [Option("output", Required = true, HelpText = "The location of the output.")] public string? OutputPath { get; set; } From f8a10b0f09a8734478c4b6f07a7e26a5579dd5e6 Mon Sep 17 00:00:00 2001 From: Andrew Morger Date: Thu, 17 Aug 2023 06:50:53 -0500 Subject: [PATCH 2/3] Incorporates excluded projects into the analysis logic --- src/LivingDocumentation.Analyzer/Program.cs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/LivingDocumentation.Analyzer/Program.cs b/src/LivingDocumentation.Analyzer/Program.cs index 8c2a19b..ce29c5f 100644 --- a/src/LivingDocumentation.Analyzer/Program.cs +++ b/src/LivingDocumentation.Analyzer/Program.cs @@ -30,7 +30,8 @@ private static async Task RunApplicationAsync(Options options) var stopwatch = Stopwatch.StartNew(); if (options.SolutionPath is not null) { - await AnalyzeSolutionFileAsync(types, options.SolutionPath).ConfigureAwait(false); + var excludedProjects = new HashSet(options.ExcludedProjectPaths, StringComparer.OrdinalIgnoreCase); + await AnalyzeSolutionFileAsync(types, options.SolutionPath, excludedProjects).ConfigureAwait(false); } else { @@ -52,7 +53,7 @@ private static async Task RunApplicationAsync(Options options) } } - private static async Task AnalyzeSolutionFileAsync(List types, string solutionFile) + private static async Task AnalyzeSolutionFileAsync(List types, string solutionFile, HashSet excludedProjects) { var manager = new AnalyzerManager(solutionFile); var workspace = manager.GetWorkspace(); @@ -64,6 +65,11 @@ private static async Task AnalyzeSolutionFileAsync(List types, foreach (var project in projects) { + if (!string.IsNullOrEmpty(project.FilePath) && excludedProjects.Contains(project.FilePath)) + { + continue; + } + await AnalyzeProjectAsyc(types, project).ConfigureAwait(false); } From d9175a3e44ea84cbccd8243fbb384a65bdcfd3c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Micha=C3=ABl=20Hompus?= Date: Thu, 17 Aug 2023 18:17:49 +0200 Subject: [PATCH 3/3] Separate project selection from analyzer to make it testable --- .../AnalyzerSetup.cs | 51 ++++++++++ src/LivingDocumentation.Analyzer/Program.cs | 47 ++------- .../AnotherProject/AnotherProject.csproj | 9 ++ .../AnotherProject/Class1.cs | 7 ++ .../OtherProject/Class1.cs | 7 ++ .../OtherProject/OtherProject.csproj | 9 ++ .../Project/Class1.cs | 7 ++ .../Project/Project.csproj | 9 ++ .../SolutionWithTests.sln | 43 ++++++++ .../SolutionWithoutTests.sln | 37 +++++++ .../TestProject/TestProject.csproj | 22 +++++ .../TestProject/UnitTest1.cs | 11 +++ .../TestProject/Usings.cs | 1 + .../AnalyzerSetup/AnalyzerSetupTests.cs | 97 +++++++++++++++++++ 14 files changed, 318 insertions(+), 39 deletions(-) create mode 100644 src/LivingDocumentation.Analyzer/AnalyzerSetup.cs create mode 100644 tests/AnalyzerSetupVerification/AnotherProject/AnotherProject.csproj create mode 100644 tests/AnalyzerSetupVerification/AnotherProject/Class1.cs create mode 100644 tests/AnalyzerSetupVerification/OtherProject/Class1.cs create mode 100644 tests/AnalyzerSetupVerification/OtherProject/OtherProject.csproj create mode 100644 tests/AnalyzerSetupVerification/Project/Class1.cs create mode 100644 tests/AnalyzerSetupVerification/Project/Project.csproj create mode 100644 tests/AnalyzerSetupVerification/SolutionWithTests.sln create mode 100644 tests/AnalyzerSetupVerification/SolutionWithoutTests.sln create mode 100644 tests/AnalyzerSetupVerification/TestProject/TestProject.csproj create mode 100644 tests/AnalyzerSetupVerification/TestProject/UnitTest1.cs create mode 100644 tests/AnalyzerSetupVerification/TestProject/Usings.cs create mode 100644 tests/LivingDocumentation.Analyzer.Tests/AnalyzerSetup/AnalyzerSetupTests.cs diff --git a/src/LivingDocumentation.Analyzer/AnalyzerSetup.cs b/src/LivingDocumentation.Analyzer/AnalyzerSetup.cs new file mode 100644 index 0000000..e146337 --- /dev/null +++ b/src/LivingDocumentation.Analyzer/AnalyzerSetup.cs @@ -0,0 +1,51 @@ +using Buildalyzer; +using Buildalyzer.Workspaces; + +namespace LivingDocumentation; + +public sealed class AnalyzerSetup : IDisposable +{ + public IEnumerable Projects; + public readonly Workspace Workspace; + + private AnalyzerSetup(AnalyzerManager Manager) + { + this.Workspace = Manager.GetWorkspace(); + this.Projects = this.Workspace.CurrentSolution.Projects; + } + + public void Dispose() + { + this.Workspace.Dispose(); + } + + public static AnalyzerSetup BuildSolutionAnalyzer(string solutionFile, IEnumerable excludedProjects = default!) + { + var excludedSet = excludedProjects is not null ? new HashSet(excludedProjects, StringComparer.OrdinalIgnoreCase) : new(0); + + var manager = new AnalyzerManager(solutionFile); + var analysis = new AnalyzerSetup(manager); + + var assembliesInSolution = analysis.Workspace.CurrentSolution.Projects.Select(p => p.AssemblyName).ToList(); + + // Every project in the solution, except unit test projects + analysis.Projects = analysis.Projects + .Where(p => !ProjectContainsTestPackageReference(manager, p)) + .Where(p => string.IsNullOrEmpty(p.FilePath) || !excludedSet.Contains(p.FilePath)); + + return analysis; + } + + public static AnalyzerSetup BuildProjectAnalyzer(string projectFile) + { + var manager = new AnalyzerManager(); + manager.GetProject(projectFile); + + return new AnalyzerSetup(manager); + } + + private static bool ProjectContainsTestPackageReference(AnalyzerManager manager, Project p) + { + return manager.Projects.First(mp => p.Id.Id == mp.Value.ProjectGuid).Value.ProjectFile.PackageReferences.Any(pr => pr.Name.Contains("Test", StringComparison.Ordinal)); + } +} diff --git a/src/LivingDocumentation.Analyzer/Program.cs b/src/LivingDocumentation.Analyzer/Program.cs index ce29c5f..ec8b3cb 100644 --- a/src/LivingDocumentation.Analyzer/Program.cs +++ b/src/LivingDocumentation.Analyzer/Program.cs @@ -1,6 +1,4 @@ using System.Diagnostics; -using Buildalyzer; -using Buildalyzer.Workspaces; using Newtonsoft.Json; namespace LivingDocumentation; @@ -28,15 +26,14 @@ private static async Task RunApplicationAsync(Options options) var types = new List(); var stopwatch = Stopwatch.StartNew(); - if (options.SolutionPath is not null) - { - var excludedProjects = new HashSet(options.ExcludedProjectPaths, StringComparer.OrdinalIgnoreCase); - await AnalyzeSolutionFileAsync(types, options.SolutionPath, excludedProjects).ConfigureAwait(false); - } - else + + using (var analyzer = options.SolutionPath is not null + ? AnalyzerSetup.BuildSolutionAnalyzer(options.SolutionPath, options.ExcludedProjectPaths) + : AnalyzerSetup.BuildProjectAnalyzer(options.ProjectPath!)) { - await AnalyzeProjectFileAsync(types, options.ProjectPath!).ConfigureAwait(false); + await AnalyzeWorkspace(types, analyzer).ConfigureAwait(false); } + stopwatch.Stop(); // Write analysis @@ -53,40 +50,12 @@ private static async Task RunApplicationAsync(Options options) } } - private static async Task AnalyzeSolutionFileAsync(List types, string solutionFile, HashSet excludedProjects) + private static async Task AnalyzeWorkspace(List types, AnalyzerSetup analysis) { - var manager = new AnalyzerManager(solutionFile); - var workspace = manager.GetWorkspace(); - var assembliesInSolution = workspace.CurrentSolution.Projects.Select(p => p.AssemblyName).ToList(); - - // Every project in the solution, except unit test projects - var projects = workspace.CurrentSolution.Projects - .Where(p => !manager.Projects.First(mp => p.Id.Id == mp.Value.ProjectGuid).Value.ProjectFile.PackageReferences.Any(pr => pr.Name.Contains("Test", StringComparison.Ordinal))); - - foreach (var project in projects) + foreach (var project in analysis.Projects) { - if (!string.IsNullOrEmpty(project.FilePath) && excludedProjects.Contains(project.FilePath)) - { - continue; - } - await AnalyzeProjectAsyc(types, project).ConfigureAwait(false); } - - workspace.Dispose(); - } - - private static async Task AnalyzeProjectFileAsync(List types, string projectFile) - { - var manager = new AnalyzerManager(); - manager.GetProject(projectFile); - var workspace = manager.GetWorkspace(); - - var project = workspace.CurrentSolution.Projects.First(); - - await AnalyzeProjectAsyc(types, project).ConfigureAwait(false); - - workspace.Dispose(); } private static async Task AnalyzeProjectAsyc(List types, Project project) diff --git a/tests/AnalyzerSetupVerification/AnotherProject/AnotherProject.csproj b/tests/AnalyzerSetupVerification/AnotherProject/AnotherProject.csproj new file mode 100644 index 0000000..cfadb03 --- /dev/null +++ b/tests/AnalyzerSetupVerification/AnotherProject/AnotherProject.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/tests/AnalyzerSetupVerification/AnotherProject/Class1.cs b/tests/AnalyzerSetupVerification/AnotherProject/Class1.cs new file mode 100644 index 0000000..7621a25 --- /dev/null +++ b/tests/AnalyzerSetupVerification/AnotherProject/Class1.cs @@ -0,0 +1,7 @@ +namespace AnotherProject +{ + public class Class1 + { + + } +} \ No newline at end of file diff --git a/tests/AnalyzerSetupVerification/OtherProject/Class1.cs b/tests/AnalyzerSetupVerification/OtherProject/Class1.cs new file mode 100644 index 0000000..dc92a0f --- /dev/null +++ b/tests/AnalyzerSetupVerification/OtherProject/Class1.cs @@ -0,0 +1,7 @@ +namespace OtherProject +{ + public class Class1 + { + + } +} diff --git a/tests/AnalyzerSetupVerification/OtherProject/OtherProject.csproj b/tests/AnalyzerSetupVerification/OtherProject/OtherProject.csproj new file mode 100644 index 0000000..cfadb03 --- /dev/null +++ b/tests/AnalyzerSetupVerification/OtherProject/OtherProject.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/tests/AnalyzerSetupVerification/Project/Class1.cs b/tests/AnalyzerSetupVerification/Project/Class1.cs new file mode 100644 index 0000000..d1bf83d --- /dev/null +++ b/tests/AnalyzerSetupVerification/Project/Class1.cs @@ -0,0 +1,7 @@ +namespace Project +{ + public class Class1 + { + + } +} diff --git a/tests/AnalyzerSetupVerification/Project/Project.csproj b/tests/AnalyzerSetupVerification/Project/Project.csproj new file mode 100644 index 0000000..cfadb03 --- /dev/null +++ b/tests/AnalyzerSetupVerification/Project/Project.csproj @@ -0,0 +1,9 @@ + + + + net7.0 + enable + enable + + + diff --git a/tests/AnalyzerSetupVerification/SolutionWithTests.sln b/tests/AnalyzerSetupVerification/SolutionWithTests.sln new file mode 100644 index 0000000..9b5f9c9 --- /dev/null +++ b/tests/AnalyzerSetupVerification/SolutionWithTests.sln @@ -0,0 +1,43 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33213.308 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project", "Project\Project.csproj", "{3394B9C3-A5D3-4145-B70D-3D54B8852596}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OtherProject", "OtherProject\OtherProject.csproj", "{586E3262-BB21-4F37-BA2D-3B56706D4746}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestProject", "TestProject\TestProject.csproj", "{E9E8CDAD-2B87-4C53-A1E0-7BC625375ADA}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnotherProject", "AnotherProject\AnotherProject.csproj", "{91EA6563-C625-43F4-9C37-23B7DB54FD73}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {3394B9C3-A5D3-4145-B70D-3D54B8852596}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3394B9C3-A5D3-4145-B70D-3D54B8852596}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3394B9C3-A5D3-4145-B70D-3D54B8852596}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3394B9C3-A5D3-4145-B70D-3D54B8852596}.Release|Any CPU.Build.0 = Release|Any CPU + {586E3262-BB21-4F37-BA2D-3B56706D4746}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {586E3262-BB21-4F37-BA2D-3B56706D4746}.Debug|Any CPU.Build.0 = Debug|Any CPU + {586E3262-BB21-4F37-BA2D-3B56706D4746}.Release|Any CPU.ActiveCfg = Release|Any CPU + {586E3262-BB21-4F37-BA2D-3B56706D4746}.Release|Any CPU.Build.0 = Release|Any CPU + {E9E8CDAD-2B87-4C53-A1E0-7BC625375ADA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9E8CDAD-2B87-4C53-A1E0-7BC625375ADA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9E8CDAD-2B87-4C53-A1E0-7BC625375ADA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9E8CDAD-2B87-4C53-A1E0-7BC625375ADA}.Release|Any CPU.Build.0 = Release|Any CPU + {91EA6563-C625-43F4-9C37-23B7DB54FD73}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {91EA6563-C625-43F4-9C37-23B7DB54FD73}.Debug|Any CPU.Build.0 = Debug|Any CPU + {91EA6563-C625-43F4-9C37-23B7DB54FD73}.Release|Any CPU.ActiveCfg = Release|Any CPU + {91EA6563-C625-43F4-9C37-23B7DB54FD73}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {BD82645A-5556-4DFF-8F8A-AEF5EAEE04A9} + EndGlobalSection +EndGlobal diff --git a/tests/AnalyzerSetupVerification/SolutionWithoutTests.sln b/tests/AnalyzerSetupVerification/SolutionWithoutTests.sln new file mode 100644 index 0000000..20be97e --- /dev/null +++ b/tests/AnalyzerSetupVerification/SolutionWithoutTests.sln @@ -0,0 +1,37 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 17 +VisualStudioVersion = 17.4.33213.308 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Project", "Project\Project.csproj", "{5CA13AC2-756B-45BB-9EC8-9F71C31A8F35}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OtherProject", "OtherProject\OtherProject.csproj", "{50FB5E30-AB8D-4D9F-BCDE-98F0BACC084F}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AnotherProject", "AnotherProject\AnotherProject.csproj", "{6412962A-D13A-4B91-B2A0-6F592BCB5CAF}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {5CA13AC2-756B-45BB-9EC8-9F71C31A8F35}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5CA13AC2-756B-45BB-9EC8-9F71C31A8F35}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5CA13AC2-756B-45BB-9EC8-9F71C31A8F35}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5CA13AC2-756B-45BB-9EC8-9F71C31A8F35}.Release|Any CPU.Build.0 = Release|Any CPU + {50FB5E30-AB8D-4D9F-BCDE-98F0BACC084F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {50FB5E30-AB8D-4D9F-BCDE-98F0BACC084F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {50FB5E30-AB8D-4D9F-BCDE-98F0BACC084F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {50FB5E30-AB8D-4D9F-BCDE-98F0BACC084F}.Release|Any CPU.Build.0 = Release|Any CPU + {6412962A-D13A-4B91-B2A0-6F592BCB5CAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6412962A-D13A-4B91-B2A0-6F592BCB5CAF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6412962A-D13A-4B91-B2A0-6F592BCB5CAF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6412962A-D13A-4B91-B2A0-6F592BCB5CAF}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {FF21EC90-3752-4496-B630-3FD0CEB652E1} + EndGlobalSection +EndGlobal diff --git a/tests/AnalyzerSetupVerification/TestProject/TestProject.csproj b/tests/AnalyzerSetupVerification/TestProject/TestProject.csproj new file mode 100644 index 0000000..ea10bac --- /dev/null +++ b/tests/AnalyzerSetupVerification/TestProject/TestProject.csproj @@ -0,0 +1,22 @@ + + + + net7.0 + enable + enable + + false + + + + + + + + + + + + + + diff --git a/tests/AnalyzerSetupVerification/TestProject/UnitTest1.cs b/tests/AnalyzerSetupVerification/TestProject/UnitTest1.cs new file mode 100644 index 0000000..a0732c5 --- /dev/null +++ b/tests/AnalyzerSetupVerification/TestProject/UnitTest1.cs @@ -0,0 +1,11 @@ +namespace TestProject +{ + [TestClass] + public class UnitTest1 + { + [TestMethod] + public void TestMethod1() + { + } + } +} \ No newline at end of file diff --git a/tests/AnalyzerSetupVerification/TestProject/Usings.cs b/tests/AnalyzerSetupVerification/TestProject/Usings.cs new file mode 100644 index 0000000..ab67c7e --- /dev/null +++ b/tests/AnalyzerSetupVerification/TestProject/Usings.cs @@ -0,0 +1 @@ +global using Microsoft.VisualStudio.TestTools.UnitTesting; \ No newline at end of file diff --git a/tests/LivingDocumentation.Analyzer.Tests/AnalyzerSetup/AnalyzerSetupTests.cs b/tests/LivingDocumentation.Analyzer.Tests/AnalyzerSetup/AnalyzerSetupTests.cs new file mode 100644 index 0000000..5fc9098 --- /dev/null +++ b/tests/LivingDocumentation.Analyzer.Tests/AnalyzerSetup/AnalyzerSetupTests.cs @@ -0,0 +1,97 @@ +namespace LivingDocumentation.Analyzer.Tests; + +[TestClass] +public class AnalyzerSetupTests +{ + private static readonly string solutionPath = GetSolutionPath(); + + [TestMethod] + public void SolutionShouldLoadAllProjects() + { + // Arrange + var solutionFile = Path.Combine(solutionPath, "SolutionWithoutTests.sln"); + + // Act + using var analyzerSetup = AnalyzerSetup.BuildSolutionAnalyzer(solutionFile); + + // Assert + analyzerSetup.Projects.Should().HaveCount(3); + analyzerSetup.Projects.Should().Satisfy( + p => p.FilePath.EndsWith("Project.csproj"), + p => p.FilePath.EndsWith("OtherProject.csproj"), + p => p.FilePath.EndsWith("AnotherProject.csproj")); + } + + [TestMethod] + public void SolutionShouldFilterTestProjects() + { + // Arrange + var solutionFile = Path.Combine(solutionPath, "SolutionWithTests.sln"); + + // Act + using var analyzerSetup = AnalyzerSetup.BuildSolutionAnalyzer(solutionFile); + + // Assert + analyzerSetup.Projects.Should().HaveCount(3); + analyzerSetup.Projects.Should().Satisfy( + p => p.FilePath.EndsWith("Project.csproj"), + p => p.FilePath.EndsWith("OtherProject.csproj"), + p => p.FilePath.EndsWith("AnotherProject.csproj")); + } + + [TestMethod] + public void SolutionShouldFilterExcludedProject() + { + // Arrange + var solutionFile = Path.Combine(solutionPath, "SolutionWithoutTests.sln"); + var excludeProjectFile = Path.Combine(solutionPath, "OtherProject", "OtherProject.csproj"); + + // Act + using var analyzerSetup = AnalyzerSetup.BuildSolutionAnalyzer(solutionFile, new[] { excludeProjectFile }); + + // Assert + analyzerSetup.Projects.Should().HaveCount(2); + analyzerSetup.Projects.Should().Satisfy( + p => p.FilePath.EndsWith("Project.csproj"), + p => p.FilePath.EndsWith("AnotherProject.csproj")); + } + + [TestMethod] + public void SolutionShouldFilterExcludedProjects() + { + // Arrange + var solutionFile = Path.Combine(solutionPath, "SolutionWithoutTests.sln"); + var excludeProjectFile1 = Path.Combine(solutionPath, "OtherProject", "OtherProject.csproj"); + var excludeProjectFile2 = Path.Combine(solutionPath, "AnotherProject", "AnotherProject.csproj"); + + // Act + using var analyzerSetup = AnalyzerSetup.BuildSolutionAnalyzer(solutionFile, new[] { excludeProjectFile1, excludeProjectFile2 }); + + // Assert + analyzerSetup.Projects.Should().HaveCount(1); + analyzerSetup.Projects.Should().Satisfy(p => p.FilePath.EndsWith("Project.csproj")); + } + + [TestMethod] + public void SolutionShouldLoadProject() + { + // Arrange + var projectFile = Path.Combine(solutionPath, "Project", "Project.csproj"); + + // Act + using var analyzerSetup = AnalyzerSetup.BuildProjectAnalyzer(projectFile); + + // Assert + analyzerSetup.Projects.Should().HaveCount(1); + analyzerSetup.Projects.Should().Satisfy(p => p.FilePath.EndsWith("Project.csproj")); + } + + private static string GetSolutionPath() + { + var currentDirectory = Directory.GetCurrentDirectory().AsSpan(); + + var path = currentDirectory[..(currentDirectory.IndexOf("tests") + 6)]; + + return Path.Combine(path.ToString(), "AnalyzerSetupVerification"); + } +}