Skip to content

Commit

Permalink
Add tests for package (#47)
Browse files Browse the repository at this point in the history
In preparation for #46, add some test to validate the current package
behavior. This doesn't test everything; future PRs should ensure the
code they're changing is tested.

The test flow is as follows:

1. Include `DotNet.ReproducibleBuilds`'s own .props and .targets files
into the test project's output
2. Create a temp directory
3. Create a `{project}.*.props` and `{project}.*.targets` file in the
`obj/` folder so that the .NET SDK will import the props and targets at
the correct time (this is basically to do a NuGet restore of the project
without needing to create the package or deal with package resolution)
4. Create a temp project and assert the behavior
  • Loading branch information
baronfel authored Aug 27, 2024
2 parents 97f3184 + b18402c commit e0ee68a
Show file tree
Hide file tree
Showing 11 changed files with 266 additions and 0 deletions.
3 changes: 3 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
<Project>
<PropertyGroup>
<RepoRoot>$(MSBuildThisFileDirectory)</RepoRoot>

<!-- Default to not packaging a project. Override per-project where dotnet sdk nuget packaging
is desired. -->
<IsPackable Condition="'$(IsPackable)' == ''">false</IsPackable>
Expand Down Expand Up @@ -36,6 +38,7 @@

<Target
Name="PreparePackageReleaseNotesFromFiles"
Condition="'$(IsPackable)' == 'true'"
BeforeTargets="GenerateNuspec">
<PropertyGroup>
<!-- This path will be relative to each executing project -->
Expand Down
6 changes: 6 additions & 0 deletions DotNet.ReproducibleBuilds.sln
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNet.ReproducibleBuilds.Isolated", "src\DotNet.ReproducibleBuilds.Isolated\DotNet.ReproducibleBuilds.Isolated.csproj", "{BD88D2CB-4342-47A3-B0D1-07321E9A92C1}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DotNet.ReproducibleBuilds.Tests", "tests\DotNet.ReproducibleBuilds.Tests\DotNet.ReproducibleBuilds.Tests.csproj", "{72BE4FA4-D190-44AF-B056-23AA79D1553A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -31,6 +33,10 @@ Global
{BD88D2CB-4342-47A3-B0D1-07321E9A92C1}.Debug|Any CPU.Build.0 = Debug|Any CPU
{BD88D2CB-4342-47A3-B0D1-07321E9A92C1}.Release|Any CPU.ActiveCfg = Release|Any CPU
{BD88D2CB-4342-47A3-B0D1-07321E9A92C1}.Release|Any CPU.Build.0 = Release|Any CPU
{72BE4FA4-D190-44AF-B056-23AA79D1553A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{72BE4FA4-D190-44AF-B056-23AA79D1553A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{72BE4FA4-D190-44AF-B056-23AA79D1553A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{72BE4FA4-D190-44AF-B056-23AA79D1553A}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down
3 changes: 3 additions & 0 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,9 @@ stages:
- script: dotnet nbgv cloud
displayName: Set Version

- script: dotnet test dirs.proj
displayName: Test

- script: dotnet pack dirs.proj
displayName: Create package(s)

Expand Down
9 changes: 9 additions & 0 deletions tests/DotNet.ReproducibleBuilds.Tests/BooleanExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using System.Diagnostics.CodeAnalysis;

namespace DotNet.ReproducibleBuilds.Tests;

internal static class BooleanExtensions
{
public static string ToLowerInvariant(this bool value) => value.ToString().ToLowerInvariant();
public static string? ToLowerInvariant([NotNullIfNotNull(nameof(value))] this bool? value) => value?.ToString().ToLowerInvariant();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>

<IsTestProject>true</IsTestProject>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.11.0" />
<PackageReference Include="MSBuild.ProjectCreation" Version="12.0.1" />
<PackageReference Include="xunit" Version="2.9.0" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.8.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>

<ItemGroup>
<None Include="$(RepoRoot)/src/DotNet.ReproducibleBuilds/*.props" CopyToOutputDirectory="PreserveNewest" />
<None Include="$(RepoRoot)/src/DotNet.ReproducibleBuilds/*.targets" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<ItemGroup>
<Using Include="Xunit" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace DotNet.ReproducibleBuilds.Tests;

internal sealed class EnvironmentVariableSuppressor : IDisposable
{
private readonly string? _value;
private readonly string _name;

public EnvironmentVariableSuppressor(string name)
{
_name = name;
_value = Environment.GetEnvironmentVariable(name);
Environment.SetEnvironmentVariable(name, null);
}

public void Dispose()
{
Environment.SetEnvironmentVariable(_name, _value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace DotNet.ReproducibleBuilds.Tests;

internal static class FileSystemInfoExtensions
{
public static string Combine(this FileSystemInfo info, params string[] paths)
{
return Path.Combine([info.FullName, ..paths]);
}
}
13 changes: 13 additions & 0 deletions tests/DotNet.ReproducibleBuilds.Tests/MSBuildModuleInitializer.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
using Microsoft.Build.Utilities.ProjectCreation;
using System.Runtime.CompilerServices;

namespace DotNet.ReproducibleBuilds.Tests;

internal static class MSBuildModuleInitializer
{
[ModuleInitializer]
internal static void InitializeMSBuild()
{
MSBuildAssemblyResolver.Register();
}
}
26 changes: 26 additions & 0 deletions tests/DotNet.ReproducibleBuilds.Tests/ProjectTemplates.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
using Microsoft.Build.Utilities.ProjectCreation;

namespace DotNet.ReproducibleBuilds.Tests;

internal static class ProjectTemplates
{
private static readonly string ThisAssemblyDirectory = Path.GetDirectoryName(typeof(ProjectTemplates).Assembly.Location)!;

public static ProjectCreator ReproducibleBuildProject(this ProjectCreatorTemplates templates, FileInfo project)
{
DirectoryInfo directory = project.Directory ?? throw new ArgumentException("Project's path does not appear to have a parent.", nameof(project));

_ = ProjectCreator
.Create(path: directory.Combine("obj", $"{project.Name}.tests.g.props"))
.Import(Path.Combine(ThisAssemblyDirectory, "DotNet.ReproducibleBuilds.props"))
.Save();

_ = ProjectCreator
.Create(path: directory.Combine("obj", $"{project.Name}.tests.g.targets"))
.Import(Path.Combine(ThisAssemblyDirectory, "DotNet.ReproducibleBuilds.targets"))
.Save();

return templates
.SdkCsproj(path: project.FullName, targetFramework: "net8.0");
}
}
105 changes: 105 additions & 0 deletions tests/DotNet.ReproducibleBuilds.Tests/SourceLinkTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
using FluentAssertions;
using Microsoft.Build.Utilities.ProjectCreation;

namespace DotNet.ReproducibleBuilds.Tests;

public class SourceLinkTests : TestBase
{
[Theory]
[InlineData(null, true)]
[InlineData(false, false)]
[InlineData(true, true)]
public void PublishRepositoryUrlIsSet(bool? publishRepositoryUrl, bool expected)
{
ProjectCreator.Templates
.ReproducibleBuildProject(GetRandomFile(".csproj"))
.PropertyGroup()
.Property("PublishRepositoryUrl", publishRepositoryUrl.ToLowerInvariant())
.Project
.GetPropertyValue("PublishRepositoryUrl")
.Should().Be(expected.ToLowerInvariant());
}

[Theory]
[InlineData(null, "embedded")]
[InlineData("embedded", "embedded")]
[InlineData("portable", "portable")]
public void DebugTypeIsSet(string? debugType, string expected)
{
ProjectCreator.Templates
.ReproducibleBuildProject(GetRandomFile(".csproj"))
.PropertyGroup()
.Property("DebugType", debugType)
.Project
.GetPropertyValue("DebugType")
.Should().Be(expected);
}

[Theory]
[InlineData(null, true)]
[InlineData(false, false)]
[InlineData(true, true)]
public void EmbedUntrackedSourcesIsSet(bool? embedUntrackedSources, bool expected)
{
ProjectCreator.Templates
.ReproducibleBuildProject(GetRandomFile(".csproj"))
.PropertyGroup()
.Property("PublishRepositoryUrl", embedUntrackedSources.ToLowerInvariant())
.Project
.GetPropertyValue("PublishRepositoryUrl")
.Should().Be(expected.ToLowerInvariant());
}

[Theory]
[InlineData("GITHUB_REF", "refs/pull/1234/merge", "pr1234")]
[InlineData("GITHUB_REF", "refs/heads/my-branch", "my-branch")]
[InlineData("GITHUB_REF", "refs/tags/v1.2.3", "v1.2.3")]

[InlineData("BUILD_SOURCEBRANCH", "refs/heads/my-branch", "my-branch")]
[InlineData("BUILD_SOURCEBRANCH", "refs/tags/v1.2.3", "v1.2.3")]

[InlineData("APPVEYOR_PULL_REQUEST_NUMBER", "1234", "pr1234")]
[InlineData("APPVEYOR_REPO_TAG_NAME", "refs/tags/v1.2.3", "refs/tags/v1.2.3")]
[InlineData("APPVEYOR_REPO_BRANCH", "refs/heads/my-branch", "refs/heads/my-branch")]

[InlineData("TEAMCITY_BUILD_BRANCH", "refs/heads/my-branch", "refs/heads/my-branch")]

[InlineData("TRAVIS_PULL_REQUEST", "1234", "pr1234")]
[InlineData("TRAVIS_BRANCH", "refs/heads/my-branch", "refs/heads/my-branch")]

[InlineData("CIRCLE_PR_NUMBER", "1234", "pr1234")]
[InlineData("CIRCLE_TAG", "refs/heads/v1.2.3", "refs/heads/v1.2.3")]
[InlineData("CIRCLE_BRANCH", "refs/heads/my-branch", "refs/heads/my-branch")]

[InlineData("CI_COMMIT_TAG", "refs/tags/v1.2.3", "refs/tags/v1.2.3")]
[InlineData("CI_MERGE_REQUEST_IID", "1234", "pr1234")]
[InlineData("CI_COMMIT_BRANCH", "refs/heads/my-branch", "refs/heads/my-branch")]

[InlineData("BUDDY_EXECUTION_PULL_REQUEST_NO", "1234", "pr1234")]
[InlineData("BUDDY_EXECUTION_TAG", "refs/tags/v1.2.3", "refs/tags/v1.2.3")]
[InlineData("BUDDY_EXECUTION_BRANCH", "refs/heads/my-branch", "refs/heads/my-branch")]
public void RepositoryBranchIsSet(string ci, string original, string expected)
{
using EnvironmentVariableSuppressor hostSuppressor = new("BUILD_SOURCEBRANCH"); // Suppress our own CI provider variables (i.e. Azure DevOps)
using EnvironmentVariableSuppressor ciSuppressor = new(ci); // Suppress the mock CI provider (just in case).

// If RepositoryBranch is set, it should take precedence over the CI provider variables
ProjectCreator.Templates
.ReproducibleBuildProject(GetRandomFile(".csproj"))
.PropertyGroup()
.Property("RepositoryBranch", "explicitly-set")
.Property(ci, original)
.Project
.GetPropertyValue("RepositoryBranch")
.Should().Be("explicitly-set", "because explicitly setting `RepositoryBranch` should always win.");

// If RepositoryBranch is not set, it should be set from the CI provider property
ProjectCreator.Templates
.ReproducibleBuildProject(GetRandomFile(".csproj"))
.PropertyGroup()
.Property(ci, original)
.Project
.GetPropertyValue("RepositoryBranch")
.Should().Be(expected);
}
}
38 changes: 38 additions & 0 deletions tests/DotNet.ReproducibleBuilds.Tests/TestBase.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
namespace DotNet.ReproducibleBuilds.Tests;

public abstract class TestBase : IDisposable
{
protected TestBase()
{
TestRootPath = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()));
}

public DirectoryInfo TestRootPath { get; }

public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}

protected virtual void Dispose(bool isDisposing)
{
TestRootPath.Refresh();
if (TestRootPath.Exists)
{
try
{
TestRootPath.Delete(recursive: true);
}
catch (Exception)
{
// Ignored
}
}
}

protected FileInfo GetRandomFile(string? extension = null)
{
return new(TestRootPath.Combine($"{Path.GetRandomFileName()}{extension ?? string.Empty}"));
}
}

0 comments on commit e0ee68a

Please sign in to comment.