Skip to content
Merged
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
6 changes: 3 additions & 3 deletions .github/workflows/dotnetcore.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@67a3573c9a986a3f9c594539f4ab511d57bb3ce9 # v4
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Build with dotnet
run: dotnet build --configuration Release
run: dotnet build ProjectsRuler.slnx --configuration Release
- name: Run tests
run: dotnet test
run: dotnet test ProjectsRuler.slnx --configuration Release --no-build
46 changes: 21 additions & 25 deletions ProjectReferencesRuler.Tests/ProjectReferencesRuler.Tests.csproj
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>

<IsPackable>false</IsPackable>

<RootNamespace>ProjectReferencesRuler</RootNamespace>

<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<DotNetCliToolReference Include="dotnet-xunit" Version="2.3.1" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\ProjectReferencesRuler\ProjectReferencesRuler.csproj" PrivateAssets="All" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="15.8.0" PrivateAssets="All" />
<PackageReference Include="Moq" Version="4.10.0" PrivateAssets="All" />
<PackageReference Include="xunit" Version="2.4.0" PrivateAssets="All" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.0" PrivateAssets="All" />
</ItemGroup>

<PropertyGroup>
<IsPackable>false</IsPackable>

<RootNamespace>ProjectReferencesRuler</RootNamespace>

<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>

<ItemGroup>
<ProjectReference
Include="..\ProjectReferencesRuler\ProjectReferencesRuler.csproj"
PrivateAssets="All"
/>
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.3.0" PrivateAssets="All" />
<PackageReference Include="Moq" Version="4.20.72" PrivateAssets="All" />
<PackageReference Include="xunit" Version="2.9.3" PrivateAssets="All" />
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5" PrivateAssets="All" />
</ItemGroup>
</Project>
83 changes: 64 additions & 19 deletions ProjectReferencesRuler.Tests/SolutionParserTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@
var path = "this does not work.png";
var solutionParser = new SolutionParser();

Assert.Throws<NotSupportedException>(() => solutionParser.ExtractSolutionProjects(path, ".csproj"));
Assert.Throws<NotSupportedException>(() =>
solutionParser.ExtractSolutionProjects(path, ".csproj")
);
}

[Fact]
Expand All @@ -45,17 +47,21 @@
var path = @"../../../TestSolutionFiles/SmallSolution.txt";
var solutionParser = new SolutionParser();

var paths = solutionParser.ExtractSolutionProjects(path, ".csproj").Select(sp => sp.ProjectPath).ToList();
var paths = solutionParser
.ExtractSolutionProjects(path, ".csproj")
.Select(sp => sp.ProjectPath)
.ToList();

Assert.NotEmpty(paths);
Assert.Equal(
new []
new[]
{
@"../../../TestSolutionFiles/devinite.PortalSystem/devinite.PortalSystem.csproj",
@"../../../TestSolutionFiles/CustomerService/Dg.CustomerService.Contracts/Dg.CustomerService.Contracts.csproj",
@"../../../TestSolutionFiles/CustomerService/Dg.CustomerService.Monolith/Dg.CustomerService.Monolith.csproj"
@"../../../TestSolutionFiles/CustomerService/Dg.CustomerService.Monolith/Dg.CustomerService.Monolith.csproj",
},
paths);
paths
);
}

[Fact]
Expand All @@ -64,17 +70,44 @@
var path = @"../../../TestSolutionFiles/SmallSolution.xml";
var solutionParser = new SolutionParser();

var paths = solutionParser.ExtractSolutionProjects(path, ".csproj").Select(sp => sp.ProjectPath).ToList();
var paths = solutionParser
.ExtractSolutionProjects(path, ".csproj")
.Select(sp => sp.ProjectPath)
.ToList();

Assert.NotEmpty(paths);
Assert.Equal(
new []
new[]
{
@"../../../TestSolutionFiles/CustomerService/Dg.CustomerService.Contracts/Dg.CustomerService.Contracts.csproj",
@"../../../TestSolutionFiles/CustomerService/Dg.CustomerService.Monolith/Dg.CustomerService.Monolith.csproj",
@"../../../TestSolutionFiles/devinite.PortalSystem/devinite.PortalSystem.csproj"
@"../../../TestSolutionFiles/devinite.PortalSystem/devinite.PortalSystem.csproj",
},
paths);
paths
);
}

[Fact]
public void ExtractProjectPaths_RealSlnxExtension_ReturnsPaths()
{
var path = @"../../../../ProjectsRuler.slnx";
var solutionParser = new SolutionParser();

var paths = solutionParser
.ExtractSolutionProjects(path, ".csproj")
.Select(sp => sp.ProjectPath)
.ToList();

Assert.NotEmpty(paths);
Assert.Equal(2, paths.Count);
Assert.Contains(
paths,
p => p.EndsWith("ProjectReferencesRuler/ProjectReferencesRuler.csproj")
);
Assert.Contains(
paths,
p => p.EndsWith("ProjectReferencesRuler.Tests/ProjectReferencesRuler.Tests.csproj")
);
}

[Fact]
Expand All @@ -83,17 +116,21 @@
var path = @"../../../TestSolutionFiles/SmallSolution.txt";
var solutionParser = new SolutionParser();

var paths = solutionParser.ExtractSolutionProjects(path, ".csproj").Select(sp => sp.ProjectGuid).ToList();
var paths = solutionParser
.ExtractSolutionProjects(path, ".csproj")
.Select(sp => sp.ProjectGuid)

Check warning on line 121 in ProjectReferencesRuler.Tests/SolutionParserTests.cs

View workflow job for this annotation

GitHub Actions / Cross platform build (windows-latest)

'SolutionProject.ProjectGuid' is obsolete: 'ProjectGuid is deprecated because it is specific to classic .sln files and will be removed in the next major migration step.'
.ToList();

Assert.NotEmpty(paths);
Assert.Equal(
new []
new[]
{
@"FAE04EC0-301F-11D3-BF4B-00C04F79EFBC",
@"FAE04EC0-301F-11D3-BF4B-00C04F79EFBC",
@"FAE04EC0-301F-11D3-BF4B-00C04F79EFBC"
@"FAE04EC0-301F-11D3-BF4B-00C04F79EFBC",
},
paths);
paths
);
}

[Fact]
Expand All @@ -102,15 +139,19 @@
var path = @"../../../TestSolutionFiles/SolutionWithFolders.txt";
var solutionParser = new SolutionParser();

var paths = solutionParser.ExtractSolutionProjects(path, ".csproj").Select(sp => sp.ProjectPath).ToList();
var paths = solutionParser
.ExtractSolutionProjects(path, ".csproj")
.Select(sp => sp.ProjectPath)
.ToList();

Assert.NotEmpty(paths);
Assert.Equal(
new []
new[]
{
@"../../../TestSolutionFiles/devinite.PortalSystem/devinite.PortalSystem.csproj",
},
paths);
paths
);
}

[Fact]
Expand All @@ -119,15 +160,19 @@
var path = @"../../../TestSolutionFiles/SolutionWithFolders.xml";
var solutionParser = new SolutionParser();

var paths = solutionParser.ExtractSolutionProjects(path, ".csproj").Select(sp => sp.ProjectPath).ToList();
var paths = solutionParser
.ExtractSolutionProjects(path, ".csproj")
.Select(sp => sp.ProjectPath)
.ToList();

Assert.NotEmpty(paths);
Assert.Equal(
new []
new[]
{
@"../../../TestSolutionFiles/devinite.PortalSystem/devinite.PortalSystem.csproj",
},
paths);
paths
);
}

[Fact]
Expand Down
57 changes: 45 additions & 12 deletions ProjectReferencesRuler/SolutionParsing/SolutionParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,30 @@ namespace ProjectReferencesRuler.SolutionParsing
{
public class SolutionParser : ISolutionParser
{
public IEnumerable<SolutionProject> ExtractSolutionProjects(string solutionPath, string projectFileExtension)
public IEnumerable<SolutionProject> ExtractSolutionProjects(
string solutionPath,
string projectFileExtension
)
{
return Path.GetExtension(solutionPath).ToLowerInvariant() switch
{
// .xml and .txt are used for tests
".slnx" or ".xml" => ExtractNewSolutionProjects(solutionPath, projectFileExtension),
".sln" or ".txt" => ExtractClassicSolutionProjects(solutionPath, projectFileExtension),
_ => throw new NotSupportedException($"Solution type not supported: {solutionPath}")
".slnx" or ".xml" => ExtractSlnxSolutionProjects(
solutionPath,
projectFileExtension
),
// Legacy support for classic .sln format during migration to .slnx.
".sln" or ".txt" => ExtractSlnSolutionProjects(solutionPath, projectFileExtension),
_ => throw new NotSupportedException(
$"Solution type not supported: {solutionPath}"
),
};
}

private IEnumerable<SolutionProject> ExtractClassicSolutionProjects(string solutionPath, string projectFileExtension)
private IEnumerable<SolutionProject> ExtractSlnSolutionProjects(
string solutionPath,
string projectFileExtension
)
{
bool atLeastOneReferenceFound = false;
var solutionDir = Path.GetDirectoryName(CleanPath(solutionPath))!;
Expand All @@ -29,24 +41,36 @@ private IEnumerable<SolutionProject> ExtractClassicSolutionProjects(string solut
{
var projectPath = ParseProjectPath(line);
var projectGuid = CleanPath(ParseProjectGuid(line));
if (projectPath != null && projectPath.EndsWith(projectFileExtension, StringComparison.InvariantCultureIgnoreCase))
if (
projectPath != null
&& projectPath.EndsWith(
projectFileExtension,
StringComparison.InvariantCultureIgnoreCase
)
)
{
atLeastOneReferenceFound = true;
yield return new SolutionProject(
projectGuid: projectGuid,
projectPath: CleanPath(Path.Combine(solutionDir, projectPath)),
isFolder: projectGuid == "2150E333-8FDC-42A3-9474-1A3956D46DE8");
isFolder: projectGuid == "2150E333-8FDC-42A3-9474-1A3956D46DE8"
);
}
}
}

if (!atLeastOneReferenceFound)
{
throw new InvalidOperationException($"No project references were found in the solution {solutionPath}.");
throw new InvalidOperationException(
$"No project references were found in the solution {solutionPath}."
);
}
}

private IEnumerable<SolutionProject> ExtractNewSolutionProjects(string solutionPath, string projectFileExtension)
private IEnumerable<SolutionProject> ExtractSlnxSolutionProjects(
string solutionPath,
string projectFileExtension
)
{
var solutionDir = Path.GetDirectoryName(CleanPath(solutionPath))!;
var doc = new XmlDocument();
Expand All @@ -55,13 +79,22 @@ private IEnumerable<SolutionProject> ExtractNewSolutionProjects(string solutionP
var projects = doc.GetElementsByTagName("Project")
.OfType<XmlNode>()
.Select(e => e.Attributes?["Path"].Value)
.Where(p => !string.IsNullOrEmpty(p) && p.EndsWith(projectFileExtension, StringComparison.InvariantCultureIgnoreCase))
.Select(p => new SolutionProject("", CleanPath(Path.Combine(solutionDir, p)), false))
.Where(p =>
!string.IsNullOrEmpty(p)
&& p.EndsWith(projectFileExtension, StringComparison.InvariantCultureIgnoreCase)
)
.Select(p => new SolutionProject(
"",
CleanPath(Path.Combine(solutionDir, p)),
false
))
.ToList();

if (!projects.Any())
{
throw new InvalidOperationException($"No project references were found in the solution {solutionPath}.");
throw new InvalidOperationException(
$"No project references were found in the solution {solutionPath}."
);
}

return projects;
Expand Down
14 changes: 9 additions & 5 deletions ProjectReferencesRuler/SolutionParsing/SolutionProject.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
using System;

namespace ProjectReferencesRuler
{
public class SolutionProject
{
[Obsolete(
"ProjectGuid is deprecated because it is specific to classic .sln files and will be removed in the next major migration step."
)]
public readonly string ProjectGuid;
public readonly string ProjectPath;
public readonly bool IsFolder;

public SolutionProject(
string projectGuid,
string projectPath,
bool isFolder)
public SolutionProject(string projectGuid, string projectPath, bool isFolder)
{
#pragma warning disable CS0618 // keep legacy field populated during migration
ProjectGuid = projectGuid;
#pragma warning restore CS0618
ProjectPath = projectPath;
IsFolder = isFolder;
}
}
}
}
Loading
Loading