Skip to content

Commit 15252df

Browse files
fix: 0 wildcard matches for --required-files is not an error
Agent-Logs-Url: https://github.com/304NotModified/SLNX-validator/sessions/df57f4d4-bd94-489f-94d0-3f0476a70eee Co-authored-by: 304NotModified <5808377+304NotModified@users.noreply.github.com>
1 parent 84c80ed commit 15252df

5 files changed

Lines changed: 10 additions & 19 deletions

File tree

README.md

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -104,8 +104,7 @@ Always exits with code `0`, even when validation errors are found. Useful in CI
104104

105105
Verify that a set of files or directories matching glob patterns exist on disk **and** are referenced as `<File>` entries in the solution file(s) being validated. Any failure is reported as a normal validation error (exit code `1`) that also appears in SonarQube reports.
106106

107-
- **Disk check** — if no files match the glob patterns, a `SLNX020` (`RequiredFileDoesntExistOnSystem`) error is added to the solution result.
108-
- **Reference check** — for each matched file that is not referenced as `<File Path="...">` in the `.slnx`, a `SLNX021` (`RequiredFileNotReferencedInSolution`) error is added. The error message shows the exact `<File>` element that should be added.
107+
- **Reference check** — for each matched file that is not referenced as `<File Path="...">` in the `.slnx`, a `SLNX021` (`RequiredFileNotReferencedInSolution`) error is added. The error message shows the exact `<File>` element that should be added. If the pattern matches no files, the check is skipped silently (not an error).
109108

110109
Relative paths in the `.slnx` are resolved relative to the solution file's location.
111110

@@ -150,8 +149,8 @@ slnx-validator MySolution.slnx --required-files "appsettings.json;docs/"
150149

151150
| Code | Description |
152151
|------|-------------|
153-
| `0` | All patterns matched and all matched files are referenced in the solution. |
154-
| `1` | Any validation error — including required files not existing or not referenced. |
152+
| `0` | All matched files are referenced in the solution (or no files matched the pattern). |
153+
| `1` | Any validation error — including required files not referenced. |
155154

156155
### Severity override flags
157156

@@ -403,7 +402,6 @@ The following are **intentionally out of scope** because the toolchain already h
403402
| `SLNX011` | `ReferencedFileNotFound` | A file referenced in `<File Path="...">` does not exist on disk. |
404403
| `SLNX012` | `InvalidWildcardUsage` | A `<File Path="...">` contains a wildcard pattern (see [`examples/invalid-wildcard.slnx`](examples/invalid-wildcard.slnx)). |
405404
| `SLNX013` | `XsdViolation` | The XML structure violates the schema, e.g. `<Folder>` inside `<Folder>` (see [`examples/invalid-xsd.slnx`](examples/invalid-xsd.slnx)). |
406-
| `SLNX020` | `RequiredFileDoesntExistOnSystem` | A `--required-files` pattern matched no files on the file system. |
407405
| `SLNX021` | `RequiredFileNotReferencedInSolution` | A `--required-files` matched file exists on disk but is not referenced as a `<File>` element in the solution. |
408406

409407
## XSD Schema

src/SLNX-validator/RequiredFilesOptions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ namespace JulianVerdurmen.SlnxValidator;
66
/// </summary>
77
/// <param name="MatchedPaths">
88
/// Absolute disk paths that were matched by <see cref="Pattern"/>.
9-
/// An empty list means the pattern matched no files.
9+
/// An empty list means the pattern matched no files — this is not an error (no files to check).
1010
/// <see langword="null"/> means the <c>--required-files</c> option was not used.
1111
/// </param>
1212
/// <param name="Pattern">The raw semicolon-separated pattern string supplied by the user.</param>

src/SLNX-validator/SlnxCollector.cs

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,7 @@ public async Task<IReadOnlyList<FileValidationResult>> CollectAsync(
6161
if (requiredFilesOptions is not null)
6262
{
6363
var matched = requiredFilesOptions.MatchedPaths;
64-
if (matched is null || matched.Count == 0)
65-
{
66-
allErrors.Add(new ValidationError(
67-
ValidationErrorCode.RequiredFileDoesntExistOnSystem,
68-
$"Required file does not exist on the system. No files matched: {requiredFilesOptions.Pattern}"));
69-
}
70-
else
64+
if (matched is not null && matched.Count > 0)
7165
{
7266
var hasXsdErrors = allErrors.Any(e => e.Code == ValidationErrorCode.XsdViolation);
7367
if (!hasXsdErrors)

tests/SLNX-validator.Tests/SlnxCollectorTests.cs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ private static (SlnxCollector collector, IRequiredFilesChecker checker) CreateCo
3131
#region CollectAsync
3232

3333
[Test]
34-
public async Task CollectAsync_RequiredFilesPatternNoMatch_AddsRequiredFileDoesntExistOnSystemError()
34+
public async Task CollectAsync_RequiredFilesPatternNoMatch_NoError()
3535
{
3636
// Arrange
3737
var (collector, _) = CreateCollector();
@@ -42,8 +42,7 @@ public async Task CollectAsync_RequiredFilesPatternNoMatch_AddsRequiredFileDoesn
4242

4343
// Assert
4444
results.Should().HaveCount(1);
45-
results[0].HasErrors.Should().BeTrue();
46-
results[0].Errors.Should().ContainSingle(e => e.Code == ValidationErrorCode.RequiredFileDoesntExistOnSystem);
45+
results[0].HasErrors.Should().BeFalse();
4746
}
4847

4948
[Test]

tests/SLNX-validator.Tests/ValidatorRunnerTests.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,14 +125,14 @@ public async Task RunAsync_RequiredFiles_AllMatchedAndReferenced_ReturnsZero()
125125
}
126126

127127
[Test]
128-
public async Task RunAsync_RequiredFiles_NoMatchOnDisk_ReturnsOne()
128+
public async Task RunAsync_RequiredFiles_NoMatchOnDisk_ReturnsZero()
129129
{
130130
// Arrange
131131
var slnxPath = Path.GetFullPath("test.slnx");
132132

133133
var checker = Substitute.For<IRequiredFilesChecker>();
134134
checker.ResolveMatchedPaths(Arg.Any<string>(), Arg.Any<string>())
135-
.Returns([]); // nothing matched on disk
135+
.Returns([]); // nothing matched on disk — no error expected
136136

137137
var runner = CreateRunnerWithSlnx(slnxPath, "<Solution />", checker);
138138

@@ -141,7 +141,7 @@ public async Task RunAsync_RequiredFiles_NoMatchOnDisk_ReturnsOne()
141141
Options(slnxPath, requiredFilesPattern: "nonexistent/**/*.md"), CancellationToken.None);
142142

143143
// Assert
144-
exitCode.Should().Be(1);
144+
exitCode.Should().Be(0);
145145
}
146146

147147
#endregion

0 commit comments

Comments
 (0)