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
28 changes: 14 additions & 14 deletions Code/Light.GuardClauses.AllProjects.sln
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SourceCodePublishing", "Sou
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.SourceCodeTransformation", "Light.GuardClauses.SourceCodeTransformation\Light.GuardClauses.SourceCodeTransformation.csproj", "{ECE42390-E3B5-49D4-9452-3B0CC04C633A}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.Source", "Light.GuardClauses.Source\Light.GuardClauses.Source.csproj", "{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Light.GuardClauses.SourceValidation", "Light.GuardClauses.SourceValidation\Light.GuardClauses.SourceValidation.csproj", "{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}"
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
Expand Down Expand Up @@ -118,18 +118,18 @@ Global
{ECE42390-E3B5-49D4-9452-3B0CC04C633A}.Release|x64.Build.0 = Release|Any CPU
{ECE42390-E3B5-49D4-9452-3B0CC04C633A}.Release|x86.ActiveCfg = Release|Any CPU
{ECE42390-E3B5-49D4-9452-3B0CC04C633A}.Release|x86.Build.0 = Release|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Debug|x64.ActiveCfg = Debug|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Debug|x64.Build.0 = Debug|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Debug|x86.ActiveCfg = Debug|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Debug|x86.Build.0 = Debug|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Release|Any CPU.Build.0 = Release|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Release|x64.ActiveCfg = Release|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Release|x64.Build.0 = Release|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Release|x86.ActiveCfg = Release|Any CPU
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843}.Release|x86.Build.0 = Release|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Debug|x64.ActiveCfg = Debug|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Debug|x64.Build.0 = Debug|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Debug|x86.ActiveCfg = Debug|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Debug|x86.Build.0 = Debug|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Release|Any CPU.Build.0 = Release|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Release|x64.ActiveCfg = Release|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Release|x64.Build.0 = Release|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Release|x86.ActiveCfg = Release|Any CPU
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF}.Release|x86.Build.0 = Release|Any CPU
{A2067796-F167-47BE-B367-191152DCE230}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A2067796-F167-47BE-B367-191152DCE230}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A2067796-F167-47BE-B367-191152DCE230}.Debug|x64.ActiveCfg = Debug|Any CPU
Expand All @@ -150,7 +150,7 @@ Global
{DB4A3982-5312-4923-9C79-C39D18A5899C} = {A58A06DD-EEDC-4B1B-830F-0927739CFB9E}
{2FE2D52E-21B8-4C51-9983-9C1812BDEBA0} = {A58A06DD-EEDC-4B1B-830F-0927739CFB9E}
{ECE42390-E3B5-49D4-9452-3B0CC04C633A} = {0E0A86F9-C976-4FA1-8BF6-3A6A21D86BFD}
{A7B9AC78-FA4F-4BDE-9DAF-B854408AB843} = {0E0A86F9-C976-4FA1-8BF6-3A6A21D86BFD}
{B546B7B7-9CA5-456C-96CD-15C21E64B7DF} = {0E0A86F9-C976-4FA1-8BF6-3A6A21D86BFD}
{A2067796-F167-47BE-B367-191152DCE230} = {0E0A86F9-C976-4FA1-8BF6-3A6A21D86BFD}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
Expand Down
16 changes: 0 additions & 16 deletions Code/Light.GuardClauses.Source/Light.GuardClauses.Source.csproj

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.IO;
using System.IO;
using FluentAssertions;
using Xunit;

Expand All @@ -8,25 +7,16 @@ namespace Light.GuardClauses.SourceCodeTransformation.Tests;
public static class GeneratedFileBuildValidatorTests
{
[Fact]
public static void ValidateBuildsSingleProjectNextToGeneratedFile()
public static void TryFindSourceValidationProjectFindsProjectFromRepositoryRoot()
{
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>
"""
var repositoryRoot = TestEnvironment.CodeDirectory.Parent!.FullName;
var expectedProjectPath = Path.Combine(
TestEnvironment.CodeDirectory.FullName,
"Light.GuardClauses.SourceValidation",
"Light.GuardClauses.SourceValidation.csproj"
);

GeneratedFileBuildValidator.Validate(targetFile).Should().Be(0);
GeneratedFileBuildValidator.TryFindSourceValidationProject([repositoryRoot]).Should().Be(expectedProjectPath);
}

[Fact]
Expand All @@ -35,55 +25,36 @@ 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);
GeneratedFileBuildValidator.Validate(SourceTargetFramework.Net8_0, targetFile).Should().NotBe(0);
File.Exists(targetFile).Should().BeTrue();
}

[Fact]
public static void ValidateSkipsWhenNoProjectFileExists()
public static void ValidateReturnsZeroForCompilingFileOnNet8()
{
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);
GeneratedFileBuildValidator.Validate(SourceTargetFramework.Net8_0, targetFile).Should().Be(0);
}

[Fact]
public static void ValidateSkipsWhenMultipleProjectFilesExist()
public static void ValidateReturnsZeroForCompilingFileOnNetStandard()
{
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);
GeneratedFileBuildValidator.Validate(SourceTargetFramework.NetStandard2_0, targetFile).Should().Be(0);
}

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

Program.Main(
Expand All @@ -92,46 +63,10 @@ public static void ProgramRunsNonWhitelistTransformationAndSkipsValidationWithou
$"TargetFile={targetFile}",
"IncludeVersionComment=false",
"AssertionWhitelist:IsEnabled=false",
"TargetFramework=NetStandard2_0",
]
).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,4 +1,3 @@
using System;
using System.IO;
using FluentAssertions;
using Microsoft.CodeAnalysis.CSharp;
Expand All @@ -9,10 +8,6 @@ namespace Light.GuardClauses.SourceCodeTransformation.Tests;

public static class ParseCSharpWithRoslynTests
{
private static readonly DirectoryInfo CodeDirectory;

static ParseCSharpWithRoslynTests() => CodeDirectory = FindCodeDirectory();

[Fact]
public static void ParseExpressionExtensionsFile()
{
Expand All @@ -36,25 +31,7 @@ public static void ParseSpanDelegatesFile()
root.Members.Should().NotBeEmpty();
}

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 static FileInfo GetLightGuardClausesFile(string relativeFilePath) => new (
Path.Combine(CodeDirectory.FullName, "Light.GuardClauses", relativeFilePath)
Path.Combine(TestEnvironment.SourceDirectory.FullName, relativeFilePath)
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,133 @@
using System;
using System.IO;
using FluentAssertions;
using Xunit;

namespace Light.GuardClauses.SourceCodeTransformation.Tests;

public static class SourceFileMergerFrameworkTests
{
[Fact]
public static void Net8ExportContainsNet8MembersAndNoDirectives()
{
using var temporaryDirectory = new TemporaryDirectory();
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "Net8Export.cs");

SourceFileMerger.CreateSingleSourceFile(CreateOptions(targetFile, SourceTargetFramework.Net8_0));
var sourceCode = File.ReadAllText(targetFile);

sourceCode.Should().Contain("using System.Numerics;");
sourceCode.Should().Contain("INumber<T>");
sourceCode.Should().Contain("MustBeGreaterThanOrApproximately<T>");
sourceCode.Should().Contain("public static ReadOnlySpan<char> MustBeEmailAddress");
sourceCode.Should().Contain("public static Span<char> MustBeEmailAddress");
sourceCode.Should().Contain("public static Memory<char> MustBeEmailAddress");
sourceCode.Should().NotContain("#if");
}

[Fact]
public static void NetStandardExportOmitsNet8MembersAndNoDirectives()
{
using var temporaryDirectory = new TemporaryDirectory();
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "NetStandardExport.cs");

SourceFileMerger.CreateSingleSourceFile(CreateOptions(targetFile, SourceTargetFramework.NetStandard2_0));
var sourceCode = File.ReadAllText(targetFile);

sourceCode.Should().NotContain("using System.Numerics;");
sourceCode.Should().NotContain("INumber<T>");
sourceCode.Should().NotContain("ReadOnlySpan<char> MustBeEmailAddress");
sourceCode.Should().NotContain("Span<char> MustBeEmailAddress");
sourceCode.Should().NotContain("Memory<char> MustBeEmailAddress");
sourceCode.Should().NotContain("#if");
}

[Fact]
public static void Net8ExportOmitsPolyfillAttributes()
{
using var temporaryDirectory = new TemporaryDirectory();
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "Net8Polyfill.cs");

SourceFileMerger.CreateSingleSourceFile(CreateOptions(targetFile, SourceTargetFramework.Net8_0));
var sourceCode = File.ReadAllText(targetFile);

sourceCode.Should().NotContain("class AllowNullAttribute");
sourceCode.Should().NotContain("class CallerArgumentExpressionAttribute");
}

[Fact]
public static void NetStandardExportIncludesPolyfillAttributes()
{
using var temporaryDirectory = new TemporaryDirectory();
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "NetStandardPolyfill.cs");

SourceFileMerger.CreateSingleSourceFile(CreateOptions(targetFile, SourceTargetFramework.NetStandard2_0));
var sourceCode = File.ReadAllText(targetFile);

sourceCode.Should().Contain("class AllowNullAttribute");
sourceCode.Should().Contain("class CallerArgumentExpressionAttribute");
}

[Fact]
public static void Net8ExportValidatesAgainstNet8()
{
using var temporaryDirectory = new TemporaryDirectory();
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "Net8Validation.cs");

SourceFileMerger.CreateSingleSourceFile(CreateOptions(targetFile, SourceTargetFramework.Net8_0));

GeneratedFileBuildValidator.Validate(SourceTargetFramework.Net8_0, targetFile).Should().Be(0);
}

[Fact]
public static void NetStandardExportValidatesAgainstNetStandard()
{
using var temporaryDirectory = new TemporaryDirectory();
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "NetStandardValidation.cs");

SourceFileMerger.CreateSingleSourceFile(CreateOptions(targetFile, SourceTargetFramework.NetStandard2_0));

GeneratedFileBuildValidator.Validate(SourceTargetFramework.NetStandard2_0, targetFile).Should().Be(0);
}

[Fact]
public static void SyntheticAssertionFileWithNoWhitelistPropertyThrows()
{
using var temporaryDirectory = new TemporaryDirectory();
var sourceFolder = Path.Combine(temporaryDirectory.DirectoryPath, "Source");
Directory.CreateDirectory(sourceFolder);
File.WriteAllText(
Path.Combine(sourceFolder, "Check.SyntheticNotWhitelisted.cs"),
"""
namespace Light.GuardClauses;

public static partial class Check
{
public static T SyntheticNotWhitelisted<T>(T parameter) => parameter;
}
"""
);
var targetFile = Path.Combine(temporaryDirectory.DirectoryPath, "Synthetic.cs");
var options = new SourceFileMergeOptions
{
SourceFolder = sourceFolder,
TargetFile = targetFile,
TargetFramework = SourceTargetFramework.NetStandard2_0,
IncludeVersionComment = false,
AssertionWhitelist = new() { IsEnabled = true },
};

var act = () => SourceFileMerger.CreateSingleSourceFile(options);

act.Should().Throw<InvalidOperationException>().WithMessage("*SyntheticNotWhitelisted*");
}

private static SourceFileMergeOptions CreateOptions(string targetFile, SourceTargetFramework targetFramework) =>
new ()
{
SourceFolder = TestEnvironment.SourceDirectory.FullName,
TargetFile = targetFile,
TargetFramework = targetFramework,
IncludeVersionComment = false,
};
}
Loading
Loading