Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/Snitch.Tests.Fixtures/Snitch.Tests.Fixtures.slnx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<Solution>
<Project Path="Foo\Foo.csproj" />
<Project Path="Bar\Bar.csproj" />
<Project Path="Baz\Baz.csproj" />
<Project Path="Qux\Qux.csproj" />
<Project Path="Zap\Zap.csproj" />
<Project Path="Quux\Quux.csproj" />
<Project Path="Quuux\Quuux.csproj" />
<Project Path="Thud\Thud.csproj" />
<Project Path="Thuuud\Thuuud.csproj" />
<Project Path="FSharp\FSharp.fsproj" />
</Solution>
50 changes: 50 additions & 0 deletions src/Snitch.Tests/Expectations/Slnx.Default.verified.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
Analyzing...
Analyzing Snitch.Tests.Fixtures.slnx
Analyzing Foo...
Analyzing Bar...
Analyzing Baz...
Analyzing Qux...
Analyzing Zap...
Analyzing Quux...
Analyzing Quuux...
Analyzing Thud...
Analyzing Thuuud...
Analyzing FSharp...

╭─────────────────────────────────────────────────────────────────╮
│ Packages that can be removed from Bar: │
│ ┌──────────────────────┬──────────────────────────────────────┐ │
│ │ Package │ Referenced by │ │
│ ├──────────────────────┼──────────────────────────────────────┤ │
│ │ Autofac │ Foo │ │
│ └──────────────────────┴──────────────────────────────────────┘ │
│ │
│ Packages that can be removed from Baz: │
│ ┌──────────────────────┬──────────────────────────────────────┐ │
│ │ Package │ Referenced by │ │
│ ├──────────────────────┼──────────────────────────────────────┤ │
│ │ Autofac │ Foo │ │
│ └──────────────────────┴──────────────────────────────────────┘ │
│ │
│ Packages that might be removed from Qux: │
│ ┌───────────┬───────────┬─────────────────────────────────────┐ │
│ │ Package │ Version │ Reason │ │
│ ├───────────┼───────────┼─────────────────────────────────────┤ │
│ │ Autofac │ 4.9.3 │ Downgraded from 4.9.4 in Foo │ │
│ └───────────┴───────────┴─────────────────────────────────────┘ │
│ │
│ Packages that might be removed from Zap: │
│ ┌──────────────────┬──────────┬───────────────────────────────┐ │
│ │ Package │ Version │ Reason │ │
│ ├──────────────────┼──────────┼───────────────────────────────┤ │
│ │ Newtonsoft.Json │ 12.0.3 │ Updated from 12.0.1 in Foo │ │
│ │ Autofac │ 4.9.3 │ Downgraded from 4.9.4 in Foo │ │
│ └──────────────────┴──────────┴───────────────────────────────┘ │
│ │
│ Packages that might be removed from Thuuud: │
│ ┌─────────────────┬──────────────┬────────────────────────────┐ │
│ │ Package │ Version │ Reason │ │
│ ├─────────────────┼──────────────┼────────────────────────────┤ │
│ │ Newtonsoft.Json │ 13.0.2-beta2 │ Updated from 12.0.1 in Foo │ │
│ └─────────────────┴──────────────┴────────────────────────────┘ │
╰─────────────────────────────────────────────────────────────────╯
16 changes: 16 additions & 0 deletions src/Snitch.Tests/ProgramTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,5 +222,21 @@ public async Task Should_Return_Expected_Result_For_FSharp_Not_Specifying_Framew
exitCode.ShouldBe(0);
await Verifier.Verify(output);
}

[Fact]
[Expectation("Slnx", "Default")]
public async Task Should_Return_Expected_Result_For_Slnx_Solution()
{
// Given
var fixture = new Fixture();
var solution = Fixture.GetPath("Snitch.Tests.Fixtures.slnx");

// When
var (exitCode, output) = await Fixture.Run(solution);

// Then
exitCode.ShouldBe(0);
await Verifier.Verify(output);
}
}
}
16 changes: 12 additions & 4 deletions src/Snitch/Analysis/Utilities/PathUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,11 @@ private static List<string> GetProjectsFromFile(string path)
return GetProjectsFromSolution(path);
}

if (path.EndsWith(".slnx", StringComparison.InvariantCulture))
{
return SlnxParser.GetProjectsFromSlnx(path);
}

throw new InvalidOperationException("Project or solution file do not exist.");
}

Expand All @@ -61,8 +66,11 @@ private static List<string> FindProjects(string? root, out string entry)
root ??= Environment.CurrentDirectory;

var slns = Directory.GetFiles(root, "*.sln");
var slnxs = Directory.GetFiles(root, "*.slnx");
var allSolutions = new List<string>(slns);
allSolutions.AddRange(slnxs);

if (slns.Length == 0)
if (allSolutions.Count == 0)
{
var subProjects = Directory.GetFiles(root, "*.csproj");
if (subProjects.Length == 0)
Expand All @@ -77,14 +85,14 @@ private static List<string> FindProjects(string? root, out string entry)
entry = subProjects[0];
return new List<string>(new[] { subProjects[0] });
}
else if (slns.Length > 1)
else if (allSolutions.Count > 1)
{
throw new InvalidOperationException("More than one solution file found.");
}
else
{
entry = slns[0];
return GetProjectsFromSolution(slns[0]);
entry = allSolutions[0];
return GetProjectsFromFile(allSolutions[0]);
}
}

Expand Down
71 changes: 71 additions & 0 deletions src/Snitch/Analysis/Utilities/SlnxParser.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Xml.Linq;

namespace Snitch.Analysis.Utilities
{
internal static class SlnxParser
{
public static List<string> GetProjectsFromSlnx(string slnxPath)
{
if (!File.Exists(slnxPath))
{
throw new FileNotFoundException($"Solution file not found: {slnxPath}");
}

var slnxDirectory = Path.GetDirectoryName(slnxPath)
?? throw new InvalidOperationException("Could not determine solution directory.");

var doc = XDocument.Load(slnxPath);
var solution = doc.Root;

if (solution == null || solution.Name.LocalName != "Solution")
{
throw new InvalidOperationException("Invalid slnx file: missing Solution root element.");
}

var projects = new List<string>();
CollectProjects(solution, slnxDirectory, projects);

return projects;
}

private static void CollectProjects(XElement element, string slnxDirectory, List<string> projects)
{
foreach (var child in element.Elements())
{
if (child.Name.LocalName == "Project")
{
var pathAttr = child.Attribute("Path");
if (pathAttr != null && !string.IsNullOrWhiteSpace(pathAttr.Value))
{
var projectPath = pathAttr.Value;

// Only include MSBuild project files (.csproj, .fsproj, .vbproj)
if (IsMSBuildProject(projectPath))
{
var absolutePath = Path.GetFullPath(Path.Combine(slnxDirectory, projectPath));
if (!projects.Contains(absolutePath))
{
projects.Add(absolutePath);
}
}
}
}
else if (child.Name.LocalName == "Folder")
{
// Recursively collect projects from folders
CollectProjects(child, slnxDirectory, projects);
}
}
}

private static bool IsMSBuildProject(string path)
{
return path.EndsWith(".csproj", StringComparison.OrdinalIgnoreCase)
|| path.EndsWith(".fsproj", StringComparison.OrdinalIgnoreCase)
|| path.EndsWith(".vbproj", StringComparison.OrdinalIgnoreCase);
}
}
}
Loading