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
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -218,10 +218,12 @@ ModelManifest.xml
# TargetFrameworks.props files
**/TargetFrameworks.props

# Local SourceCodeTransformation settings
/Code/Light.GuardClauses.SourceCodeTransformation/settings.local.json

# JetBrains Rider
*.sln.iml
.idea/

# The source code redistributable of Light.GuardClauses
/Code/Light.GuardClauses.Source/Light.GuardClauses.cs
/Code/Light.GuardClauses.SourceCodeTransformation/settings.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions Code/AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Root AGENTS.md

Light.GuardClauses is a lightweight library for writing GuardClauses in C#/.NET

## Implementation rules

Plans typically have acceptance criteria with check boxes. Check each box when you are finished with the corresponding criterion.

## Plan Rules

Read ./ai-plans/AGENTS.md for details on how to write plans.

## Here is Your Space

If you encounter something worth noting while you are working on this code base, write it down here in this section. Once you are finished, I will discuss it with you, and we can decide where to put your notes.
1 change: 1 addition & 0 deletions Code/CLAUDE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Please read AGENTS.md
5 changes: 0 additions & 5 deletions Code/Light.GuardClauses.AllProjects.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.Source",
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.SourceCodeTransformation.Tests", "Light.GuardClauses.SourceCodeTransformation.Tests\Light.GuardClauses.SourceCodeTransformation.Tests.csproj", "{A2067796-F167-47BE-B367-191152DCE230}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plans", "Plans", "{664661A0-3861-486B-87B5-A35A5CC5F7B7}"
ProjectSection(SolutionItems) = preProject
Plans\issue-114-must-not-be-default-or-empty-for-immutable-array.md = Plans\issue-114-must-not-be-default-or-empty-for-immutable-array.md
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{8A42A536-E597-454B-BE18-4F62311FA158}"
ProjectSection(SolutionItems) = preProject
..\CONTRIBUTING.md = ..\CONTRIBUTING.md
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
using System;
using System.IO;
using FluentAssertions;
using Xunit;

namespace Light.GuardClauses.SourceCodeTransformation.Tests;

public static class GeneratedFileBuildValidatorTests
{
[Fact]
public static void ValidateBuildsSingleProjectNextToGeneratedFile()
{
using var temporaryDirectory = new TemporaryDirectory();
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "Generated.cs");
File.WriteAllText(targetFile, "namespace ValidationSample; public static class Generated { }");
File.WriteAllText(
Path.Combine(temporaryDirectory.DirectoryPath, "ValidationSample.csproj"),
"""
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
"""
);

GeneratedFileBuildValidator.Validate(targetFile).Should().Be(0);
}

[Fact]
public static void ValidateReturnsNonZeroExitCodeAndLeavesFileOnBuildFailure()
{
using var temporaryDirectory = new TemporaryDirectory();
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "Generated.cs");
File.WriteAllText(targetFile, "namespace ValidationSample; public static class Generated { this is invalid }");
File.WriteAllText(
Path.Combine(temporaryDirectory.DirectoryPath, "ValidationSample.csproj"),
"""
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
</Project>
"""
);

GeneratedFileBuildValidator.Validate(targetFile).Should().NotBe(0);
File.Exists(targetFile).Should().BeTrue();
}

[Fact]
public static void ValidateSkipsWhenNoProjectFileExists()
{
using var temporaryDirectory = new TemporaryDirectory();
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "Generated.cs");
File.WriteAllText(targetFile, "namespace ValidationSample; public static class Generated { }");

GeneratedFileBuildValidator.Validate(targetFile).Should().Be(0);
}

[Fact]
public static void ValidateSkipsWhenMultipleProjectFilesExist()
{
using var temporaryDirectory = new TemporaryDirectory();
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "Generated.cs");
File.WriteAllText(targetFile, "namespace ValidationSample; public static class Generated { }");
File.WriteAllText(
Path.Combine(temporaryDirectory.DirectoryPath, "First.csproj"),
"<Project Sdk=\"Microsoft.NET.Sdk\" />"
);
File.WriteAllText(
Path.Combine(temporaryDirectory.DirectoryPath, "Second.csproj"),
"<Project Sdk=\"Microsoft.NET.Sdk\" />"
);

GeneratedFileBuildValidator.Validate(targetFile).Should().Be(0);
}

[Fact]
public static void ProgramRunsNonWhitelistTransformationAndSkipsValidationWithoutProject()
{
using var temporaryDirectory = new TemporaryDirectory();
var codeDirectory = FindCodeDirectory();
var sourceFolder = Path.Combine(codeDirectory.FullName, "Light.GuardClauses");
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "Generated.cs");

Program.Main(
[
$"SourceFolder={sourceFolder}",
$"TargetFile={targetFile}",
"IncludeVersionComment=false",
"AssertionWhitelist:IsEnabled=false",
]
).Should().Be(0);

File.Exists(targetFile).Should().BeTrue();
}

private static DirectoryInfo FindCodeDirectory()
{
var currentDirectory = new DirectoryInfo(".");
do
{
if (currentDirectory.Name == "Code")
{
return currentDirectory;
}

currentDirectory = currentDirectory.Parent;
} while (currentDirectory != null);

throw new InvalidOperationException(
"This test project does not reside in a folder called \"Code\" (directly or indirectly)."
);
}

private sealed class TemporaryDirectory : IDisposable
{
public TemporaryDirectory()
{
DirectoryPath = Path.Combine(Path.GetTempPath(), Guid.NewGuid().ToString("N"));
Directory.CreateDirectory(DirectoryPath);
}

public string DirectoryPath { get; }

public void Dispose()
{
if (Directory.Exists(DirectoryPath))
{
Directory.Delete(DirectoryPath, true);
}
}
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<IsPackable>false</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
<PackageReference Include="FluentAssertions" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.NET.Test.Sdk" />
<PackageReference Include="xunit" />
<PackageReference Include="xunit.runner.visualstudio" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" />
<PackageReference Include="FluentAssertions" />
</ItemGroup>

</Project>
<ItemGroup>
<ProjectReference Include="..\Light.GuardClauses.SourceCodeTransformation\Light.GuardClauses.SourceCodeTransformation.csproj" />
</ItemGroup>

</Project>
Loading
Loading