From aac7c07bbddd15845b2291020292944895fcc7e7 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 1 Jul 2025 07:23:58 +0000
Subject: [PATCH 1/4] Initial plan
From b5b0844e543f9585d481c92a21bffd32539807d8 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 1 Jul 2025 07:39:36 +0000
Subject: [PATCH 2/4] Implement folder support for project selection
Co-authored-by: eNeRGy164 <10671831+eNeRGy164@users.noreply.github.com>
---
Directory.Packages.props | 4 +-
src/DendroDocs.Tool/AnalyzerSetup.cs | 95 +++++++++++++++++++
src/DendroDocs.Tool/DendroDocs.Tool.csproj | 2 +
src/DendroDocs.Tool/Options.cs | 5 +-
src/DendroDocs.Tool/Program.cs | 4 +-
src/DendroDocs.Tool/packages.lock.json | 6 ++
.../AnalyzerSetup/AnalyzerSetupTests.cs | 59 ++++++++++++
.../DendroDocs.Tool.Tests/packages.lock.json | 7 ++
8 files changed, 178 insertions(+), 4 deletions(-)
diff --git a/Directory.Packages.props b/Directory.Packages.props
index 3c3cc91..781f948 100644
--- a/Directory.Packages.props
+++ b/Directory.Packages.props
@@ -3,11 +3,11 @@
true
true
-
+
@@ -23,4 +23,4 @@
-
+
\ No newline at end of file
diff --git a/src/DendroDocs.Tool/AnalyzerSetup.cs b/src/DendroDocs.Tool/AnalyzerSetup.cs
index 110df73..2d1d927 100644
--- a/src/DendroDocs.Tool/AnalyzerSetup.cs
+++ b/src/DendroDocs.Tool/AnalyzerSetup.cs
@@ -1,5 +1,7 @@
using Buildalyzer;
using Buildalyzer.Workspaces;
+using Microsoft.Extensions.FileSystemGlobbing;
+using Microsoft.Extensions.FileSystemGlobbing.Abstractions;
namespace DendroDocs.Tool;
@@ -42,8 +44,101 @@ public static AnalyzerSetup BuildProjectAnalyzer(string projectFile)
return new AnalyzerSetup(manager);
}
+ public static AnalyzerSetup BuildFolderAnalyzer(string folderPathOrPattern, IEnumerable excludedProjects = default!)
+ {
+ var excludedSet = excludedProjects is not null ? new HashSet(excludedProjects, StringComparer.OrdinalIgnoreCase) : [];
+ var projectFiles = DiscoverProjectFiles(folderPathOrPattern);
+
+ if (!projectFiles.Any())
+ {
+ throw new InvalidOperationException($"No project files found in folder or pattern: {folderPathOrPattern}");
+ }
+
+ var manager = new AnalyzerManager();
+ foreach (var projectFile in projectFiles)
+ {
+ if (!excludedSet.Contains(projectFile))
+ {
+ manager.GetProject(projectFile);
+ }
+ }
+
+ var analysis = new AnalyzerSetup(manager);
+
+ // Filter out test projects and excluded projects
+ analysis.Projects = analysis.Projects
+ .Where(p => !ProjectContainsTestPackageReference(manager, p))
+ .Where(p => string.IsNullOrEmpty(p.FilePath) || !excludedSet.Contains(p.FilePath));
+
+ return analysis;
+ }
+
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));
}
+
+ private static IEnumerable DiscoverProjectFiles(string folderPathOrPattern)
+ {
+ // Check if it's a direct path to a folder
+ if (Directory.Exists(folderPathOrPattern))
+ {
+ return Directory.GetFiles(folderPathOrPattern, "*.csproj", SearchOption.AllDirectories);
+ }
+
+ // Treat as a glob pattern
+ var matcher = new Matcher();
+
+ // If the pattern doesn't contain wildcards, assume it's a folder and add /**/*.csproj
+ if (!folderPathOrPattern.Contains('*') && !folderPathOrPattern.Contains('?'))
+ {
+ var basePath = folderPathOrPattern;
+ if (!basePath.EndsWith(Path.DirectorySeparatorChar))
+ {
+ basePath += Path.DirectorySeparatorChar;
+ }
+ matcher.AddInclude($"**/*.csproj");
+
+ // Use current directory as base if path doesn't exist as a directory
+ var baseDirectory = Directory.Exists(folderPathOrPattern) ? folderPathOrPattern : Directory.GetCurrentDirectory();
+ var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(baseDirectory)));
+ return result.Files.Select(f => Path.Combine(baseDirectory, f.Path));
+ }
+ else
+ {
+ // Handle as a true glob pattern
+ matcher.AddInclude(folderPathOrPattern);
+
+ // Determine base directory from the pattern
+ var baseDirectory = GetBasDirectoryFromPattern(folderPathOrPattern);
+ var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(baseDirectory)));
+ return result.Files.Select(f => Path.Combine(baseDirectory, f.Path));
+ }
+ }
+
+ private static string GetBasDirectoryFromPattern(string pattern)
+ {
+ // Find the first occurrence of wildcards and take the directory part before it
+ var wildcardIndex = Math.Min(
+ pattern.IndexOf('*') >= 0 ? pattern.IndexOf('*') : int.MaxValue,
+ pattern.IndexOf('?') >= 0 ? pattern.IndexOf('?') : int.MaxValue
+ );
+
+ if (wildcardIndex == int.MaxValue)
+ {
+ // No wildcards, use the pattern as is if it's a directory
+ return Directory.Exists(pattern) ? pattern : Directory.GetCurrentDirectory();
+ }
+
+ var basePart = pattern.Substring(0, wildcardIndex);
+ var lastSeparator = basePart.LastIndexOf(Path.DirectorySeparatorChar);
+
+ if (lastSeparator >= 0)
+ {
+ var baseDir = basePart.Substring(0, lastSeparator);
+ return Directory.Exists(baseDir) ? baseDir : Directory.GetCurrentDirectory();
+ }
+
+ return Directory.GetCurrentDirectory();
+ }
}
diff --git a/src/DendroDocs.Tool/DendroDocs.Tool.csproj b/src/DendroDocs.Tool/DendroDocs.Tool.csproj
index 5032590..27babeb 100644
--- a/src/DendroDocs.Tool/DendroDocs.Tool.csproj
+++ b/src/DendroDocs.Tool/DendroDocs.Tool.csproj
@@ -36,12 +36,14 @@
+
+
diff --git a/src/DendroDocs.Tool/Options.cs b/src/DendroDocs.Tool/Options.cs
index 3ba4f00..3068e40 100644
--- a/src/DendroDocs.Tool/Options.cs
+++ b/src/DendroDocs.Tool/Options.cs
@@ -10,7 +10,10 @@ 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.")]
+ [Option("folder", Required = true, SetName = "folder", HelpText = "The folder to search for projects, or a glob pattern to match project files.")]
+ public string? FolderPath { get; set; }
+
+ [Option("exclude", Required = false, Separator = ',', HelpText = "Any projects to exclude from analysis.")]
public IEnumerable ExcludedProjectPaths { get; set; } = [];
[Option("output", Required = true, HelpText = "The location of the output.")]
diff --git a/src/DendroDocs.Tool/Program.cs b/src/DendroDocs.Tool/Program.cs
index 93ed339..89cbc7d 100644
--- a/src/DendroDocs.Tool/Program.cs
+++ b/src/DendroDocs.Tool/Program.cs
@@ -31,7 +31,9 @@ private static async Task RunApplicationAsync(Options options)
using (var analyzer = options.SolutionPath is not null
? AnalyzerSetup.BuildSolutionAnalyzer(options.SolutionPath, options.ExcludedProjectPaths)
- : AnalyzerSetup.BuildProjectAnalyzer(options.ProjectPath!))
+ : options.ProjectPath is not null
+ ? AnalyzerSetup.BuildProjectAnalyzer(options.ProjectPath!)
+ : AnalyzerSetup.BuildFolderAnalyzer(options.FolderPath!, options.ExcludedProjectPaths))
{
await AnalyzeWorkspace(types, analyzer).ConfigureAwait(false);
}
diff --git a/src/DendroDocs.Tool/packages.lock.json b/src/DendroDocs.Tool/packages.lock.json
index 761a3a0..56260b1 100644
--- a/src/DendroDocs.Tool/packages.lock.json
+++ b/src/DendroDocs.Tool/packages.lock.json
@@ -64,6 +64,12 @@
"System.Threading.Channels": "7.0.0"
}
},
+ "Microsoft.Extensions.FileSystemGlobbing": {
+ "type": "Direct",
+ "requested": "[8.0.0, )",
+ "resolved": "8.0.0",
+ "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
+ },
"Microsoft.SourceLink.GitHub": {
"type": "Direct",
"requested": "[8.0.0, )",
diff --git a/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs b/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs
index 1b09d38..0e4e03f 100644
--- a/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs
+++ b/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs
@@ -95,6 +95,58 @@ public void SolutionShouldLoadProject()
analyzerSetup.Projects.ShouldAllBe(p => p.FilePath != null && p.FilePath.EndsWith("Project.csproj"));
}
+ [TestMethod]
+ public void FolderShouldLoadAllProjects()
+ {
+ // Arrange
+ var folderPath = SolutionPath;
+
+ // Act
+ using var analyzerSetup = AnalyzerSetup.BuildFolderAnalyzer(folderPath);
+
+ // Assert
+ analyzerSetup.Projects.Count().ShouldBe(3);
+
+ var projectPaths = analyzerSetup.Projects.Select(p => p.FilePath).ToList();
+ projectPaths.ShouldContain(path => path != null && path.EndsWith("Project.csproj"));
+ projectPaths.ShouldContain(path => path != null && path.EndsWith("OtherProject.csproj"));
+ projectPaths.ShouldContain(path => path != null && path.EndsWith("AnotherProject.csproj"));
+ }
+
+ [TestMethod]
+ public void FolderShouldFilterTestProjects()
+ {
+ // Arrange
+ var basePath = GetBasePath();
+ var folderPath = Path.Combine(basePath, "AnalyzerSetupVerification");
+
+ // Act
+ using var analyzerSetup = AnalyzerSetup.BuildFolderAnalyzer(folderPath);
+
+ // Assert
+ // Should have 3 projects (excluding TestProject which has test packages)
+ analyzerSetup.Projects.Count().ShouldBe(3);
+
+ var projects = analyzerSetup.Projects.ToList();
+ projects.ShouldAllBe(p => p.FilePath != null && !p.FilePath.Contains("TestProject"));
+ }
+
+ [TestMethod]
+ public void FolderShouldFilterExcludedProjects()
+ {
+ // Arrange
+ var folderPath = SolutionPath;
+ var excludeProjectFile1 = Path.Combine(SolutionPath, "OtherProject", "OtherProject.csproj");
+ var excludeProjectFile2 = Path.Combine(SolutionPath, "AnotherProject", "AnotherProject.csproj");
+
+ // Act
+ using var analyzerSetup = AnalyzerSetup.BuildFolderAnalyzer(folderPath, [excludeProjectFile1, excludeProjectFile2]);
+
+ // Assert
+ analyzerSetup.Projects.ShouldHaveSingleItem();
+ analyzerSetup.Projects.ShouldAllBe(p => p.FilePath != null && p.FilePath.EndsWith("Project.csproj"));
+ }
+
private static string GetSolutionPath()
{
var currentDirectory = Directory.GetCurrentDirectory().AsSpan();
@@ -103,4 +155,11 @@ private static string GetSolutionPath()
return Path.Combine(path.ToString(), "AnalyzerSetupVerification");
}
+
+ private static string GetBasePath()
+ {
+ var currentDirectory = Directory.GetCurrentDirectory().AsSpan();
+
+ return currentDirectory[..(currentDirectory.IndexOf("tests") + 6)].ToString();
+ }
}
diff --git a/tests/DendroDocs.Tool.Tests/packages.lock.json b/tests/DendroDocs.Tool.Tests/packages.lock.json
index baf776c..d28da46 100644
--- a/tests/DendroDocs.Tool.Tests/packages.lock.json
+++ b/tests/DendroDocs.Tool.Tests/packages.lock.json
@@ -530,6 +530,7 @@
"DendroDocs.Shared": "[0.4.2, )",
"Microsoft.CodeAnalysis.CSharp.Workspaces": "[4.14.0, )",
"Microsoft.CodeAnalysis.VisualBasic.Workspaces": "[4.14.0, )",
+ "Microsoft.Extensions.FileSystemGlobbing": "[8.0.0, )",
"Newtonsoft.Json.Schema": "[4.0.1, )"
}
},
@@ -633,6 +634,12 @@
"System.Threading.Channels": "7.0.0"
}
},
+ "Microsoft.Extensions.FileSystemGlobbing": {
+ "type": "CentralTransitive",
+ "requested": "[8.0.0, )",
+ "resolved": "8.0.0",
+ "contentHash": "OK+670i7esqlQrPjdIKRbsyMCe9g5kSLpRRQGSr4Q58AOYEe/hCnfLZprh7viNisSUUQZmMrbbuDaIrP+V1ebQ=="
+ },
"Newtonsoft.Json.Schema": {
"type": "CentralTransitive",
"requested": "[4.0.1, )",
From 50acbe2adf7d846c632c7699c840d74ed7c57ba5 Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 1 Jul 2025 07:45:13 +0000
Subject: [PATCH 3/4] Complete folder and glob pattern support with
documentation
Co-authored-by: eNeRGy164 <10671831+eNeRGy164@users.noreply.github.com>
---
README.md | 13 ++++
src/DendroDocs.Tool/AnalyzerSetup.cs | 69 +++++++++++--------
src/DendroDocs.Tool/Options.cs | 2 +-
src/DendroDocs.Tool/README.md | 13 ++++
.../AnalyzerSetup/AnalyzerSetupTests.cs | 31 +++++++++
5 files changed, 98 insertions(+), 30 deletions(-)
diff --git a/README.md b/README.md
index b1a67be..1c68563 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,20 @@ dotnet tool install --global DendroDocs.Tool
Example usage:
```shell
+# Analyze a solution file
dendrodocs-analyze --solution G:\DendroDocs\dotnet-shared-lib\DendroDocs.Shared.sln --output shared.json --pretty --verbose --exclude G:\DendroDocs\dotnet-shared-lib\build\_build.csproj
+
+# Analyze a single project file
+dendrodocs-analyze --project MyProject.csproj --output project.json --pretty
+
+# Analyze all projects in a folder
+dendrodocs-analyze --folder /path/to/projects --output folder.json --pretty
+
+# Use glob patterns to select specific projects
+dendrodocs-analyze --folder "src/**/*.csproj" --output matched.json --pretty
+
+# Exclude specific projects when analyzing a folder
+dendrodocs-analyze --folder /path/to/projects --exclude /path/to/unwanted.csproj,/path/to/test.csproj --output filtered.json
```
## Output
diff --git a/src/DendroDocs.Tool/AnalyzerSetup.cs b/src/DendroDocs.Tool/AnalyzerSetup.cs
index 2d1d927..f6edb2a 100644
--- a/src/DendroDocs.Tool/AnalyzerSetup.cs
+++ b/src/DendroDocs.Tool/AnalyzerSetup.cs
@@ -86,59 +86,70 @@ private static IEnumerable DiscoverProjectFiles(string folderPathOrPatte
return Directory.GetFiles(folderPathOrPattern, "*.csproj", SearchOption.AllDirectories);
}
- // Treat as a glob pattern
+ // Handle glob patterns
var matcher = new Matcher();
- // If the pattern doesn't contain wildcards, assume it's a folder and add /**/*.csproj
+ // If the pattern doesn't contain wildcards, assume it's a folder that doesn't exist
if (!folderPathOrPattern.Contains('*') && !folderPathOrPattern.Contains('?'))
{
- var basePath = folderPathOrPattern;
- if (!basePath.EndsWith(Path.DirectorySeparatorChar))
- {
- basePath += Path.DirectorySeparatorChar;
- }
- matcher.AddInclude($"**/*.csproj");
-
- // Use current directory as base if path doesn't exist as a directory
- var baseDirectory = Directory.Exists(folderPathOrPattern) ? folderPathOrPattern : Directory.GetCurrentDirectory();
- var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(baseDirectory)));
- return result.Files.Select(f => Path.Combine(baseDirectory, f.Path));
+ throw new DirectoryNotFoundException($"Folder not found: {folderPathOrPattern}");
+ }
+
+ // Handle as a glob pattern
+ string baseDirectory;
+ string pattern;
+
+ // Check if pattern is absolute or relative
+ if (Path.IsPathRooted(folderPathOrPattern))
+ {
+ // Absolute path - extract base directory and relative pattern
+ var parts = SplitAbsolutePattern(folderPathOrPattern);
+ baseDirectory = parts.BaseDirectory;
+ pattern = parts.Pattern;
}
else
{
- // Handle as a true glob pattern
- matcher.AddInclude(folderPathOrPattern);
-
- // Determine base directory from the pattern
- var baseDirectory = GetBasDirectoryFromPattern(folderPathOrPattern);
- var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(baseDirectory)));
- return result.Files.Select(f => Path.Combine(baseDirectory, f.Path));
+ // Relative path
+ baseDirectory = Directory.GetCurrentDirectory();
+ pattern = folderPathOrPattern;
}
+
+ matcher.AddInclude(pattern);
+ var result = matcher.Execute(new DirectoryInfoWrapper(new DirectoryInfo(baseDirectory)));
+ return result.Files.Select(f => Path.Combine(baseDirectory, f.Path));
}
- private static string GetBasDirectoryFromPattern(string pattern)
+ private static (string BaseDirectory, string Pattern) SplitAbsolutePattern(string absolutePattern)
{
- // Find the first occurrence of wildcards and take the directory part before it
+ // Find the first wildcard
var wildcardIndex = Math.Min(
- pattern.IndexOf('*') >= 0 ? pattern.IndexOf('*') : int.MaxValue,
- pattern.IndexOf('?') >= 0 ? pattern.IndexOf('?') : int.MaxValue
+ absolutePattern.IndexOf('*') >= 0 ? absolutePattern.IndexOf('*') : int.MaxValue,
+ absolutePattern.IndexOf('?') >= 0 ? absolutePattern.IndexOf('?') : int.MaxValue
);
if (wildcardIndex == int.MaxValue)
{
- // No wildcards, use the pattern as is if it's a directory
- return Directory.Exists(pattern) ? pattern : Directory.GetCurrentDirectory();
+ // No wildcards found, treat as directory
+ return (absolutePattern, "**/*.csproj");
}
- var basePart = pattern.Substring(0, wildcardIndex);
+ // Find the last directory separator before the wildcard
+ var basePart = absolutePattern.Substring(0, wildcardIndex);
var lastSeparator = basePart.LastIndexOf(Path.DirectorySeparatorChar);
if (lastSeparator >= 0)
{
var baseDir = basePart.Substring(0, lastSeparator);
- return Directory.Exists(baseDir) ? baseDir : Directory.GetCurrentDirectory();
+ var relativePattern = absolutePattern.Substring(lastSeparator + 1);
+
+ // Ensure base directory exists
+ if (Directory.Exists(baseDir))
+ {
+ return (baseDir, relativePattern);
+ }
}
- return Directory.GetCurrentDirectory();
+ // Fallback to current directory
+ return (Directory.GetCurrentDirectory(), absolutePattern);
}
}
diff --git a/src/DendroDocs.Tool/Options.cs b/src/DendroDocs.Tool/Options.cs
index 3068e40..9763bdc 100644
--- a/src/DendroDocs.Tool/Options.cs
+++ b/src/DendroDocs.Tool/Options.cs
@@ -10,7 +10,7 @@ public class Options
[Option("project", Required = true, SetName = "project", HelpText = "The project to analyze.")]
public string? ProjectPath { get; set; }
- [Option("folder", Required = true, SetName = "folder", HelpText = "The folder to search for projects, or a glob pattern to match project files.")]
+ [Option("folder", Required = true, SetName = "folder", HelpText = "The folder to search for projects recursively, or a glob pattern to match specific project files (e.g., 'src/**/*.csproj').")]
public string? FolderPath { get; set; }
[Option("exclude", Required = false, Separator = ',', HelpText = "Any projects to exclude from analysis.")]
diff --git a/src/DendroDocs.Tool/README.md b/src/DendroDocs.Tool/README.md
index 71903d9..1313590 100644
--- a/src/DendroDocs.Tool/README.md
+++ b/src/DendroDocs.Tool/README.md
@@ -22,7 +22,20 @@ dotnet tool install --global DendroDocs.Tool
## Example usage
```shell
+# Analyze a solution file
dendrodocs-analyze --solution G:\DendroDocs\dotnet-shared-lib\DendroDocs.Shared.sln --output shared.json --pretty --verbose --exclude G:\DendroDocs\dotnet-shared-lib\build\_build.csproj
+
+# Analyze a single project file
+dendrodocs-analyze --project MyProject.csproj --output project.json --pretty
+
+# Analyze all projects in a folder
+dendrodocs-analyze --folder /path/to/projects --output folder.json --pretty
+
+# Use glob patterns to select specific projects
+dendrodocs-analyze --folder "src/**/*.csproj" --output matched.json --pretty
+
+# Exclude specific projects when analyzing a folder
+dendrodocs-analyze --folder /path/to/projects --exclude /path/to/unwanted.csproj,/path/to/test.csproj --output filtered.json
```
## Output
diff --git a/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs b/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs
index 0e4e03f..f0460a5 100644
--- a/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs
+++ b/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs
@@ -147,6 +147,37 @@ public void FolderShouldFilterExcludedProjects()
analyzerSetup.Projects.ShouldAllBe(p => p.FilePath != null && p.FilePath.EndsWith("Project.csproj"));
}
+ [TestMethod]
+ public void GlobPatternShouldFindMatchingProjects()
+ {
+ // Arrange
+ var basePath = GetBasePath();
+ var originalDir = Directory.GetCurrentDirectory();
+
+ try
+ {
+ // Change to the test base directory to make relative patterns work
+ Directory.SetCurrentDirectory(basePath);
+ var globPattern = "AnalyzerSetupVerification/**/*.csproj";
+
+ // Act
+ using var analyzerSetup = AnalyzerSetup.BuildFolderAnalyzer(globPattern);
+
+ // Assert
+ // Should find the 3 non-test projects that match the pattern (excludes TestProject due to test references)
+ analyzerSetup.Projects.Count().ShouldBe(3);
+
+ var projectPaths = analyzerSetup.Projects.Select(p => p.FilePath).ToList();
+ projectPaths.ShouldContain(path => path != null && path.EndsWith("Project.csproj"));
+ projectPaths.ShouldContain(path => path != null && path.EndsWith("OtherProject.csproj"));
+ projectPaths.ShouldContain(path => path != null && path.EndsWith("AnotherProject.csproj"));
+ }
+ finally
+ {
+ Directory.SetCurrentDirectory(originalDir);
+ }
+ }
+
private static string GetSolutionPath()
{
var currentDirectory = Directory.GetCurrentDirectory().AsSpan();
From 63e9da05813fb7da2a82686fb2ad5e666cff994a Mon Sep 17 00:00:00 2001
From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com>
Date: Tue, 1 Jul 2025 08:10:55 +0000
Subject: [PATCH 4/4] Separate null checks from functional assertions
Co-authored-by: eNeRGy164 <10671831+eNeRGy164@users.noreply.github.com>
---
.../AnalyzerSetup/AnalyzerSetupTests.cs | 26 ++++++++++++-------
1 file changed, 16 insertions(+), 10 deletions(-)
diff --git a/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs b/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs
index f0460a5..1e547a8 100644
--- a/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs
+++ b/tests/DendroDocs.Tool.Tests/AnalyzerSetup/AnalyzerSetupTests.cs
@@ -78,7 +78,8 @@ public void SolutionShouldFilterExcludedProjects()
// Assert
analyzerSetup.Projects.ShouldHaveSingleItem();
- analyzerSetup.Projects.ShouldAllBe(p => p.FilePath != null && p.FilePath.EndsWith("Project.csproj"));
+ analyzerSetup.Projects.ShouldAllBe(p => p.FilePath != null);
+ analyzerSetup.Projects.ShouldAllBe(p => p.FilePath!.EndsWith("Project.csproj"));
}
[TestMethod]
@@ -92,7 +93,8 @@ public void SolutionShouldLoadProject()
// Assert
analyzerSetup.Projects.ShouldHaveSingleItem();
- analyzerSetup.Projects.ShouldAllBe(p => p.FilePath != null && p.FilePath.EndsWith("Project.csproj"));
+ analyzerSetup.Projects.ShouldAllBe(p => p.FilePath != null);
+ analyzerSetup.Projects.ShouldAllBe(p => p.FilePath!.EndsWith("Project.csproj"));
}
[TestMethod]
@@ -108,9 +110,10 @@ public void FolderShouldLoadAllProjects()
analyzerSetup.Projects.Count().ShouldBe(3);
var projectPaths = analyzerSetup.Projects.Select(p => p.FilePath).ToList();
- projectPaths.ShouldContain(path => path != null && path.EndsWith("Project.csproj"));
- projectPaths.ShouldContain(path => path != null && path.EndsWith("OtherProject.csproj"));
- projectPaths.ShouldContain(path => path != null && path.EndsWith("AnotherProject.csproj"));
+ projectPaths.ShouldAllBe(path => path != null);
+ projectPaths.ShouldContain(path => path!.EndsWith("Project.csproj"));
+ projectPaths.ShouldContain(path => path!.EndsWith("OtherProject.csproj"));
+ projectPaths.ShouldContain(path => path!.EndsWith("AnotherProject.csproj"));
}
[TestMethod]
@@ -128,7 +131,8 @@ public void FolderShouldFilterTestProjects()
analyzerSetup.Projects.Count().ShouldBe(3);
var projects = analyzerSetup.Projects.ToList();
- projects.ShouldAllBe(p => p.FilePath != null && !p.FilePath.Contains("TestProject"));
+ projects.ShouldAllBe(p => p.FilePath != null);
+ projects.ShouldAllBe(p => !p.FilePath!.Contains("TestProject"));
}
[TestMethod]
@@ -144,7 +148,8 @@ public void FolderShouldFilterExcludedProjects()
// Assert
analyzerSetup.Projects.ShouldHaveSingleItem();
- analyzerSetup.Projects.ShouldAllBe(p => p.FilePath != null && p.FilePath.EndsWith("Project.csproj"));
+ analyzerSetup.Projects.ShouldAllBe(p => p.FilePath != null);
+ analyzerSetup.Projects.ShouldAllBe(p => p.FilePath!.EndsWith("Project.csproj"));
}
[TestMethod]
@@ -168,9 +173,10 @@ public void GlobPatternShouldFindMatchingProjects()
analyzerSetup.Projects.Count().ShouldBe(3);
var projectPaths = analyzerSetup.Projects.Select(p => p.FilePath).ToList();
- projectPaths.ShouldContain(path => path != null && path.EndsWith("Project.csproj"));
- projectPaths.ShouldContain(path => path != null && path.EndsWith("OtherProject.csproj"));
- projectPaths.ShouldContain(path => path != null && path.EndsWith("AnotherProject.csproj"));
+ projectPaths.ShouldAllBe(path => path != null);
+ projectPaths.ShouldContain(path => path!.EndsWith("Project.csproj"));
+ projectPaths.ShouldContain(path => path!.EndsWith("OtherProject.csproj"));
+ projectPaths.ShouldContain(path => path!.EndsWith("AnotherProject.csproj"));
}
finally
{