Skip to content

Commit

Permalink
Compile time mode (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
coenm authored Nov 24, 2021
1 parent a88b812 commit 4364fa4
Show file tree
Hide file tree
Showing 47 changed files with 792 additions and 32 deletions.
39 changes: 25 additions & 14 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<ItemGroup>
<!-- xunit -->
<PackageReference Update="xunit" Version="2.4.1" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.analyzers" Version="0.10.0" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.categories" Version="2.0.5" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.extensibility.core" Version="2.4.1" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.runner.console" Version="2.4.1" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.runner.reporters" Version="2.4.1" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.3" IsImplicitlyDefined="true"/>
<ItemGroup>
<PackageReference Update="Nullable" Version="1.3.0" IsImplicitlyDefined="true" />
<PackageReference Update="Newtonsoft.Json" Version="13.0.1" IsImplicitlyDefined="true" />

<!--Unittesting-->
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.0.0" IsImplicitlyDefined="true"/>
<PackageReference Update="FluentAssertions" Version="6.2.0" IsImplicitlyDefined="true"/>
<PackageReference Update="FluentAssertions.Analyzers" Version="0.12.1" IsImplicitlyDefined="true"/>
</ItemGroup>
<!-- xunit -->
<PackageReference Update="xunit" Version="2.4.1" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.analyzers" Version="0.10.0" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.categories" Version="2.0.5" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.extensibility.core" Version="2.4.1" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.runner.console" Version="2.4.1" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.runner.reporters" Version="2.4.1" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.runner.visualstudio" Version="2.4.3" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.assert" Version="2.4.1" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.abstractions" Version="2.0.3" IsImplicitlyDefined="true"/>
<PackageReference Update="xunit.extensibility.execution" Version="2.4.1" IsImplicitlyDefined="true"/>

<PackageReference Update="NUnit" Version="3.13.2" IsImplicitlyDefined="true"/>
<PackageReference Update="NUnit3TestAdapter" Version="4.0.0" IsImplicitlyDefined="true"/>

<!--Unittesting-->
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.0.0" IsImplicitlyDefined="true"/>
<PackageReference Update="FluentAssertions" Version="6.2.0" IsImplicitlyDefined="true"/>
<PackageReference Update="FluentAssertions.Analyzers" Version="0.12.1" IsImplicitlyDefined="true"/>
<PackageReference Update="Verify.Xunit" Version="14.0.1" IsImplicitlyDefined="true"/>
<PackageReference Update="Verify.NUnit" Version="14.0.1" IsImplicitlyDefined="true"/>
</ItemGroup>
</Project>
29 changes: 29 additions & 0 deletions EasyTestFile.sln
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{7F514257-78A1-47F0-B75C-B9832A5496FA}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
README.md = README.md
version.json = version.json
EndProjectSection
EndProject
Expand All @@ -37,6 +38,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyTestFile.Xunit.Resource
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyTestFile.Tests", "tests\EasyTestFile.Tests\EasyTestFile.Tests.csproj", "{1C89726B-517D-40AC-A2C2-7BB1DC477EE7}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyTestFile.Xunit.Embed.Tests", "tests\EasyTestFile.Xunit.Embed.Tests\EasyTestFile.Xunit.Embed.Tests.csproj", "{521F5548-DE4E-4A1F-9DA1-EE2743F1864C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyTestFile.Xunit.CopyAlways.Tests", "tests\EasyTestFile.Xunit.CopyAlways.Tests\EasyTestFile.Xunit.CopyAlways.Tests.csproj", "{14927A45-6D1C-4B15-A18C-06AB7C7E97FB}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyTestFile.Xunit.CopyPreserveNewest.Tests", "tests\EasyTestFile.Xunit.CopyPreserveNewest.Tests\EasyTestFile.Xunit.CopyPreserveNewest.Tests.csproj", "{B72DBB51-A84C-4E1A-A70E-3677971A04E5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyTestFile.Xunit.ModeNone.Tests", "tests\EasyTestFile.Xunit.ModeNone.Tests\EasyTestFile.Xunit.ModeNone.Tests.csproj", "{42805355-871E-4B35-9BB7-D168417D8860}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -75,6 +84,22 @@ Global
{1C89726B-517D-40AC-A2C2-7BB1DC477EE7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1C89726B-517D-40AC-A2C2-7BB1DC477EE7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1C89726B-517D-40AC-A2C2-7BB1DC477EE7}.Release|Any CPU.Build.0 = Release|Any CPU
{521F5548-DE4E-4A1F-9DA1-EE2743F1864C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{521F5548-DE4E-4A1F-9DA1-EE2743F1864C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{521F5548-DE4E-4A1F-9DA1-EE2743F1864C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{521F5548-DE4E-4A1F-9DA1-EE2743F1864C}.Release|Any CPU.Build.0 = Release|Any CPU
{14927A45-6D1C-4B15-A18C-06AB7C7E97FB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{14927A45-6D1C-4B15-A18C-06AB7C7E97FB}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14927A45-6D1C-4B15-A18C-06AB7C7E97FB}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14927A45-6D1C-4B15-A18C-06AB7C7E97FB}.Release|Any CPU.Build.0 = Release|Any CPU
{B72DBB51-A84C-4E1A-A70E-3677971A04E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B72DBB51-A84C-4E1A-A70E-3677971A04E5}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B72DBB51-A84C-4E1A-A70E-3677971A04E5}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B72DBB51-A84C-4E1A-A70E-3677971A04E5}.Release|Any CPU.Build.0 = Release|Any CPU
{42805355-871E-4B35-9BB7-D168417D8860}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{42805355-871E-4B35-9BB7-D168417D8860}.Debug|Any CPU.Build.0 = Debug|Any CPU
{42805355-871E-4B35-9BB7-D168417D8860}.Release|Any CPU.ActiveCfg = Release|Any CPU
{42805355-871E-4B35-9BB7-D168417D8860}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -88,6 +113,10 @@ Global
{10B5E000-BA89-40B2-96C6-3628AF08F089} = {FA1F5DF0-A7AC-4E68-B875-E7F1D71329B6}
{82529167-644B-4BAF-9105-3132F7FE8CE4} = {3A849D71-FEE8-4494-A8A6-C303AAB18A42}
{1C89726B-517D-40AC-A2C2-7BB1DC477EE7} = {3A849D71-FEE8-4494-A8A6-C303AAB18A42}
{521F5548-DE4E-4A1F-9DA1-EE2743F1864C} = {3A849D71-FEE8-4494-A8A6-C303AAB18A42}
{14927A45-6D1C-4B15-A18C-06AB7C7E97FB} = {3A849D71-FEE8-4494-A8A6-C303AAB18A42}
{B72DBB51-A84C-4E1A-A70E-3677971A04E5} = {3A849D71-FEE8-4494-A8A6-C303AAB18A42}
{42805355-871E-4B35-9BB7-D168417D8860} = {3A849D71-FEE8-4494-A8A6-C303AAB18A42}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {E0B06F3E-E0CF-486F-9251-DB617317FE44}
Expand Down
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# EasyTestFile

[![Nuget Status](https://img.shields.io/nuget/v/EasyTestFile.svg?label=EasyTestFile&style=flat-square)](https://www.nuget.org/packages/EasyTestFile/)
[![Nuget Status](https://img.shields.io/nuget/v/EasyTestFile.XUnit.svg?label=EasyTestFile.XUnit&style=flat-square)](https://www.nuget.org/packages/EasyTestFile.XUnit/)
[![Nuget Status](https://img.shields.io/nuget/v/EasyTestFile.NUnit.svg?label=EasyTestFile.NUnit&style=flat-square)](https://www.nuget.org/packages/EasyTestFile.NUnit/)
[![Nuget Status](https://img.shields.io/nuget/v/EasyTestFile.NewtonsoftJson.svg?label=EasyTestFile.NewtonsoftJson&style=flat-square)](https://www.nuget.org/packages/EasyTestFile.NewtonsoftJson/)


EasyTestFile is a library that simplifies the creation and usage of testfiles in unittests.
Testfiles (like text, json, xml, binary, jpg, etc. etc.) are named based on the class and method name, are created if not exist, and are embedded as resource making sure the execution of the test is deterministic and do not rely on untracked files etc.

Expand Down Expand Up @@ -84,6 +90,30 @@ These three test methods produce the following testfiles according to the name c

![Solution Explorer TestFiles](/docs/images/SolutionExplorerTestFiles.png)

# Compile time Configuration

There is an optional option to control how testfiles are included in your artifacts. This can be controlled using the property `EasyTestFileMode`.
The options are:
- `None` TestFiles are not copied or embedded on compilation. EasyTestFile will load the files from the original source. This will speedup the compilation process but might be less reliable as files can be altered or deleted after compilation and before executing tests. Creating artifacts on buildservers in order to run the test in other environments might also be problematic as the testfiles are not included as artifact.
- `Embed` The testfiles are embedded as resource in the `dll` file. This will produce larger binaries, takes a little bit more time to compile but makes the test deterministic as testfiles cannot be altered or deleted after compilation and before testing.
- `CopyAlways` This will always copy the testfile to the artifact/build directory.
- `CopyPreserveNewest` This will copy the testfile to the build directory when the file is newer.

When no (valid) value is provided, the `Embed` mode will be used.

Configuration is done like:

<!-- snippet: CompiletimeConfigurationEasyTestFileMode -->
<a id='snippet-compiletimeconfigurationeasytestfilemode'></a>
```csproj
<PropertyGroup>
<!-- Embed;CopyAlways;CopyPreserveNewest;None -->
<EasyTestFileMode>CopyAlways</EasyTestFileMode>
</PropertyGroup>
```
<sup><a href='/tests/EasyTestFile.Xunit.CopyAlways.Tests/EasyTestFile.Xunit.CopyAlways.Tests.csproj#L26-L31' title='Snippet source file'>snippet source</a> | <a href='#snippet-compiletimeconfigurationeasytestfilemode' title='Start of snippet'>anchor</a></sup>
<!-- endSnippet -->

# Credits

## VerifyTest
Expand Down
2 changes: 1 addition & 1 deletion src/EasyTestFile.Json/EasyTestFile.Json.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="Newtonsoft.Json" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion src/EasyTestFile.Nunit/EasyTestFile.Nunit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit" />
</ItemGroup>

<ItemGroup>
Expand Down
6 changes: 3 additions & 3 deletions src/EasyTestFile.Xunit/EasyTestFile.Xunit.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="xunit.assert" Version="2.4.1" />
<PackageReference Include="xunit.abstractions" Version="2.0.3" />
<PackageReference Include="xunit.extensibility.execution" Version="2.4.1" />
<PackageReference Include="xunit.assert" />
<PackageReference Include="xunit.abstractions" />
<PackageReference Include="xunit.extensibility.execution" />
</ItemGroup>

<ItemGroup>
Expand Down
70 changes: 65 additions & 5 deletions src/EasyTestFile/DerivedPaths/AttributeReader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@ namespace EasyTestFile.DerivedPaths;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using EasyTestFile.Internals;

internal static class AttributeReader
{
private const string PROJECT_DIRECTORY = "EasyTestFile.ProjectDirectory";
private const string SOLUTION_DIRECTORY = "EasyTestFile.SolutionDirectory";
private const string EASY_TEST_FILE_MODE = "EasyTestFile.EasyTestFileMode";

/// <exception cref="AssemblyMetadataAttributeNotFoundException">Thrown when the `CallingAssembly` doesn't contain an <seealso cref="AssemblyMetadataAttribute"/> with the ProjectDirectory.</exception>
public static string GetProjectDirectory()
Expand All @@ -19,7 +21,7 @@ public static string GetProjectDirectory()
/// <exception cref="AssemblyMetadataAttributeNotFoundException">Thrown when the `CallingAssembly` doesn't contain an <seealso cref="AssemblyMetadataAttribute"/> with the ProjectDirectory.</exception>
public static string GetProjectDirectory(Assembly assembly)
{
return GetValue(assembly, PROJECT_DIRECTORY).Replace('/', '\\');
return GetEscapedPathValue(assembly, PROJECT_DIRECTORY).Replace('/', '\\');
}

public static bool TryGetProjectDirectory([NotNullWhen(true)] out string? projectDirectory)
Expand All @@ -29,7 +31,7 @@ public static bool TryGetProjectDirectory([NotNullWhen(true)] out string? projec

public static bool TryGetProjectDirectory(Assembly assembly, [NotNullWhen(true)] out string? projectDirectory)
{
return TryGetValue(assembly, PROJECT_DIRECTORY, out projectDirectory);
return TryGetEscapedPathValue(assembly, PROJECT_DIRECTORY, out projectDirectory);
}

/// <exception cref="AssemblyMetadataAttributeNotFoundException">Thrown when the `CallingAssembly` doesn't contain an <seealso cref="AssemblyMetadataAttribute"/> with the SolutionDirectory.</exception>
Expand All @@ -41,7 +43,7 @@ public static string GetSolutionDirectory()
/// <exception cref="AssemblyMetadataAttributeNotFoundException">Thrown when the <paramref name="assembly"/> doesn't contain an <seealso cref="AssemblyMetadataAttribute"/> with the SolutionDirectory.</exception>
public static string GetSolutionDirectory(Assembly assembly)
{
return GetValue(assembly, SOLUTION_DIRECTORY);
return GetEscapedPathValue(assembly, SOLUTION_DIRECTORY);
}

public static bool TryGetSolutionDirectory([NotNullWhen(true)] out string? solutionDirectory)
Expand All @@ -51,15 +53,73 @@ public static bool TryGetSolutionDirectory([NotNullWhen(true)] out string? solut

public static bool TryGetSolutionDirectory(Assembly assembly, [NotNullWhen(true)] out string? solutionDirectory)
{
return TryGetValue(assembly, SOLUTION_DIRECTORY, out solutionDirectory);
return TryGetEscapedPathValue(assembly, SOLUTION_DIRECTORY, out solutionDirectory);
}

public static bool TryGetEasyTestFileMode(Assembly assembly, [NotNullWhen(true)] out EasyTestFileMode? mode)
{
if (!TryGetValue(assembly, EASY_TEST_FILE_MODE, out var easyTestFileModeString))
{
mode = null;
return false;
}

if (easyTestFileModeString.Equals("Embed", StringComparison.InvariantCulture))
{
mode = EasyTestFileMode.Embed;
return true;
}

if (easyTestFileModeString.Equals("CopyAlways", StringComparison.InvariantCulture))
{
mode = EasyTestFileMode.CopyAlways;
return true;
}

if (easyTestFileModeString.Equals("CopyPreserveNewest", StringComparison.InvariantCulture))
{
mode = EasyTestFileMode.CopyPreserveNewest;
return true;
}

if (easyTestFileModeString.Equals("None", StringComparison.InvariantCulture))
{
mode = EasyTestFileMode.None;
return true;
}

mode = null;
return false;
}

private static bool TryGetEscapedPathValue(Assembly assembly, string key, [NotNullWhen(true)] out string? value)
{
if (TryGetValue(assembly, key, out value))
{
value = value.Replace('/', '\\');
return true;
}

value = null;
return false;
}

/// <exception cref="AssemblyMetadataAttributeNotFoundException">Thrown when an expected AssemblyMetadataAttribute was not found.</exception>
private static string GetEscapedPathValue(Assembly assembly, string key)
{
if (TryGetEscapedPathValue(assembly, key, out var value))
{
return value;
}

throw new AssemblyMetadataAttributeNotFoundException(assembly.GetName().FullName, key);
}

private static bool TryGetValue(Assembly assembly, string key, [NotNullWhen(true)] out string? value)
{
value = assembly.GetCustomAttributes<AssemblyMetadataAttribute>()
.SingleOrDefault(x => x.Key == key)
?.Value;
value = value?.Replace('/', '\\');
return value != null;
}

Expand Down
2 changes: 1 addition & 1 deletion src/EasyTestFile/EasyTestFile.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

<Otherwise>
<ItemGroup>
<PackageReference Include="Nullable" Version="1.3.0" PrivateAssets="all" />
<PackageReference Include="Nullable" PrivateAssets="all" />
</ItemGroup>
</Otherwise>
</Choose>
Expand Down
27 changes: 27 additions & 0 deletions src/EasyTestFile/Internals/EasyTestFileMode.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
namespace EasyTestFile.Internals;

/// <summary>
/// Mode how testfiles are handled during compilation.
/// </summary>
internal enum EasyTestFileMode
{
/// <summary>
/// Testfiles are embedded as resource. The project dll will contain the files and will be bigger. This mode is the `safest` because testfiles cannot be altered or deleted.
/// </summary>
Embed,

/// <summary>
/// Testfiles are always copied to the output directory.
/// </summary>
CopyAlways,

/// <summary>
/// Testfiles are only copied to the output directory when they are newer.
/// </summary>
CopyPreserveNewest,

/// <summary>
/// Testfiles are never copied nor are they embedded as resource. This mode speeds up compilation.
/// </summary>
None,
}
4 changes: 4 additions & 0 deletions src/EasyTestFile/InternalsVisibleTo.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
using System.Runtime.CompilerServices;

[assembly:InternalsVisibleTo("EasyTestFile.Xunit")]
[assembly:InternalsVisibleTo("EasyTestFile.Xunit.CopyAlways.Tests")]
[assembly:InternalsVisibleTo("EasyTestFile.Xunit.CopyPreserveNewest.Tests")]
[assembly:InternalsVisibleTo("EasyTestFile.Xunit.Embed.Tests")]
[assembly:InternalsVisibleTo("EasyTestFile.Xunit.ModeNone.Tests")]
[assembly:InternalsVisibleTo("EasyTestFile.Nunit")]
[assembly: InternalsVisibleTo("EasyTestFile.Tests")]
8 changes: 6 additions & 2 deletions src/EasyTestFile/buildTransitive/EasyTestFile.props
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<DeterministicSourcePaths>false</DeterministicSourcePaths>
<EasyTestFileAttributesFile>EasyTestFile.Attributes$(MSBuildProjectExtension.Replace('proj', ''))</EasyTestFileAttributesFile>

<!-- Valid values are 'Embed', 'CopyAlways', 'CopyPreseverNewest', 'None' -->
<!-- Valid values are 'Embed', 'CopyAlways', 'CopyPreserveNewest', 'None' -->
<EasyTestFileMode>Embed</EasyTestFileMode>
</PropertyGroup>

Expand All @@ -19,14 +19,18 @@
<EasyTestFileAttributesFilePath>$(IntermediateOutputPath)$(EasyTestFileAttributesFile)</EasyTestFileAttributesFilePath>
</PropertyGroup>
<ItemGroup>
<Attributes Include="System.Reflection.AssemblyMetadata">
<Attributes Include="System.Reflection.AssemblyMetadata" Condition="'$(ProjectDir)' != '' And '$(ProjectDir)' != '*Undefined*'">
<_Parameter1>EasyTestFile.ProjectDirectory</_Parameter1>
<_Parameter2>$(ProjectDir)</_Parameter2>
</Attributes>
<Attributes Include="System.Reflection.AssemblyMetadata" Condition="'$(SolutionDir)' != '' And '$(SolutionDir)' != '*Undefined*'">
<_Parameter1>EasyTestFile.SolutionDirectory</_Parameter1>
<_Parameter2>$(SolutionDir)</_Parameter2>
</Attributes>
<Attributes Include="System.Reflection.AssemblyMetadata" Condition="'$(EasyTestFileMode)' != '' And '$(EasyTestFileMode)' != '*Undefined*'">
<_Parameter1>EasyTestFile.EasyTestFileMode</_Parameter1>
<_Parameter2>$(EasyTestFileMode)</_Parameter2>
</Attributes>

<!-- Ensure not part of Compile, as a workaround for /~https://github.com/dotnet/sdk/issues/114 -->
<Compile Remove="$(EasyTestFileAttributesFilePath)" />
Expand Down
Loading

0 comments on commit 4364fa4

Please sign in to comment.