Skip to content

Commit e956c34

Browse files
Rename SlnxFileRefs→SlnxFile, remove try-catch, add #regions/AAA, split integration tests
Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com> Agent-Logs-Url: https://github.com/304NotModified/SLNX-validator/sessions/ceaf8ba9-6628-42a8-b534-1dfec61aafc3
1 parent ec1f478 commit e956c34

9 files changed

Lines changed: 275 additions & 145 deletions

File tree

src/SLNX-validator.Core/Validation/IRequiredFilesChecker.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ public interface IRequiredFilesChecker
1212

1313
/// <summary>
1414
/// Checks which of the <paramref name="requiredAbsolutePaths"/> are NOT present in
15-
/// <paramref name="slnxFileRefs"/>.
15+
/// <paramref name="slnxFile"/>.
1616
/// Returns a <see cref="ValidationError"/> for each missing file.
1717
/// </summary>
1818
IReadOnlyList<ValidationError> CheckInSlnx(
1919
IReadOnlyList<string> requiredAbsolutePaths,
20-
SlnxFileRefs slnxFileRefs);
20+
SlnxFile slnxFile);
2121
}
2222

src/SLNX-validator.Core/Validation/RequiredFilesChecker.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@ public IReadOnlyList<string> ResolveMatchedPaths(string patternsRaw, string root
3232
/// <inheritdoc />
3333
public IReadOnlyList<ValidationError> CheckInSlnx(
3434
IReadOnlyList<string> requiredAbsolutePaths,
35-
SlnxFileRefs slnxFileRefs)
35+
SlnxFile slnxFile)
3636
{
3737
var errors = new List<ValidationError>();
3838
foreach (var requiredPath in requiredAbsolutePaths)
3939
{
40-
if (!slnxFileRefs.AbsoluteFilePaths.Contains(requiredPath, StringComparer.OrdinalIgnoreCase))
40+
if (!slnxFile.Files.Contains(requiredPath, StringComparer.OrdinalIgnoreCase))
4141
{
42-
var relativePath = Path.GetRelativePath(slnxFileRefs.SlnxDirectory, requiredPath).Replace('\\', '/');
42+
var relativePath = Path.GetRelativePath(slnxFile.SlnxDirectory, requiredPath).Replace('\\', '/');
4343
errors.Add(new ValidationError(
4444
ValidationErrorCode.RequiredFileNotReferencedInSolution,
4545
$"Required file is not referenced in the solution: {requiredPath}" +
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
using System.Xml.Linq;
2+
3+
namespace JulianVerdurmen.SlnxValidator.Core.Validation;
4+
5+
/// <summary>
6+
/// Represents the set of absolute file paths that are referenced as
7+
/// <c>&lt;File Path="..."&gt;</c> elements inside a .slnx solution file.
8+
/// </summary>
9+
public sealed class SlnxFile
10+
{
11+
/// <summary>The directory that contains the .slnx file.</summary>
12+
public string SlnxDirectory { get; }
13+
14+
/// <summary>Absolute, normalised paths for every <c>&lt;File&gt;</c> entry in the solution.</summary>
15+
public IReadOnlyList<string> Files { get; }
16+
17+
private SlnxFile(string slnxDirectory, IReadOnlyList<string> files)
18+
{
19+
SlnxDirectory = slnxDirectory;
20+
Files = files;
21+
}
22+
23+
/// <summary>
24+
/// Parses <paramref name="slnxContent"/> and returns the resolved absolute paths of all
25+
/// <c>&lt;File Path="..."&gt;</c> elements. Relative paths are resolved against
26+
/// <paramref name="slnxDirectory"/>.
27+
/// </summary>
28+
/// <returns>The parsed <see cref="SlnxFile"/>, or <see langword="null"/> when the XML is malformed.</returns>
29+
public static SlnxFile? Parse(string slnxContent, string slnxDirectory)
30+
{
31+
XDocument doc;
32+
try
33+
{
34+
doc = XDocument.Parse(slnxContent);
35+
}
36+
catch (Exception)
37+
{
38+
return null;
39+
}
40+
41+
var refs = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
42+
foreach (var fileElement in doc.Descendants("File"))
43+
{
44+
var path = fileElement.Attribute("Path")?.Value;
45+
if (path is null)
46+
continue;
47+
48+
var fullPath = Path.IsPathRooted(path)
49+
? Path.GetFullPath(path)
50+
: Path.GetFullPath(Path.Combine(slnxDirectory, path));
51+
52+
refs.Add(fullPath);
53+
}
54+
55+
return new SlnxFile(slnxDirectory, [.. refs]);
56+
}
57+
}

src/SLNX-validator.Core/Validation/SlnxFileRefs.cs

Lines changed: 0 additions & 55 deletions
This file was deleted.

src/SLNX-validator/ValidationCollector.cs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,9 @@ public async Task<IReadOnlyList<FileValidationResult>> CollectAsync(
5252
}
5353
else
5454
{
55-
var slnxFileRefs = SlnxFileRefs.Parse(content, directory);
56-
allErrors.AddRange(requiredFilesChecker.CheckInSlnx(matched, slnxFileRefs));
55+
var slnxFile = SlnxFile.Parse(content, directory);
56+
if (slnxFile is not null)
57+
allErrors.AddRange(requiredFilesChecker.CheckInSlnx(matched, slnxFile));
5758
}
5859
}
5960

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
using AwesomeAssertions;
2+
using JulianVerdurmen.SlnxValidator.Core.Validation;
3+
4+
namespace JulianVerdurmen.SlnxValidator.Core.Tests;
5+
6+
public class RequiredFilesCheckerIntegrationTests
7+
{
8+
private static string CreateTempDir()
9+
{
10+
var tempDir = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName());
11+
Directory.CreateDirectory(tempDir);
12+
return tempDir;
13+
}
14+
15+
private static RequiredFilesChecker CreateChecker() => new();
16+
17+
#region ResolveMatchedPaths
18+
19+
[Test]
20+
public void ResolveMatchedPaths_SingleInclude_MatchesFiles_ReturnsNonEmpty()
21+
{
22+
// Arrange
23+
var tempDir = CreateTempDir();
24+
try
25+
{
26+
var docDir = Path.Combine(tempDir, "doc");
27+
Directory.CreateDirectory(docDir);
28+
File.WriteAllText(Path.Combine(docDir, "readme.md"), "# Readme");
29+
File.WriteAllText(Path.Combine(docDir, "contributing.md"), "# Contributing");
30+
31+
// Act
32+
var matched = CreateChecker().ResolveMatchedPaths("doc/*.md", tempDir);
33+
34+
// Assert
35+
matched.Should().HaveCount(2);
36+
}
37+
finally
38+
{
39+
Directory.Delete(tempDir, recursive: true);
40+
}
41+
}
42+
43+
[Test]
44+
public void ResolveMatchedPaths_IncludeFollowedByExclude_ExcludesFile()
45+
{
46+
// Arrange
47+
var tempDir = CreateTempDir();
48+
try
49+
{
50+
var docDir = Path.Combine(tempDir, "doc");
51+
Directory.CreateDirectory(docDir);
52+
File.WriteAllText(Path.Combine(docDir, "readme.md"), "# Readme");
53+
File.WriteAllText(Path.Combine(docDir, "contributing.md"), "# Contributing");
54+
55+
// Act
56+
var matched = CreateChecker().ResolveMatchedPaths("doc/*.md;!doc/contributing.md", tempDir);
57+
58+
// Assert
59+
matched.Should().HaveCount(1);
60+
matched[0].Should().EndWith("readme.md");
61+
}
62+
finally
63+
{
64+
Directory.Delete(tempDir, recursive: true);
65+
}
66+
}
67+
68+
#endregion
69+
}

0 commit comments

Comments
 (0)