Skip to content
Open
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
3 changes: 2 additions & 1 deletion src/BenchmarkDotNet/Running/BuildPartition.cs
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,8 @@ internal bool ForcedNoDependenciesForIntegrationTests
if (!XUnitHelper.IsIntegrationTest.Value || !RuntimeInformation.IsNetCore)
return false;

if (RepresentativeBenchmarkCase.GetToolchain().Builder is not DotNetCliBuilder)
// Publishing toolchains need the full reference-gathering and publish path.
if (RepresentativeBenchmarkCase.GetToolchain().Builder is not DotNetCliBuilder { IsPublishing: false })
return false;

return !RepresentativeBenchmarkCase.Job.HasDynamicBuildCharacteristic();
Expand Down
2 changes: 2 additions & 0 deletions src/BenchmarkDotNet/Toolchains/CsProj/CsProjBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,8 @@ protected void AddGatheredReferences(XElement project)
project.Add(new XElement("PropertyGroup",
new XAttribute("Condition", GatheringCondition),
new XElement("SkipCompilerExecution", "true"),
// A self-contained executable requires an apphost, which this pass never produces.
new XElement("SelfContained", "false"),
// Both of these copy a build product that no longer gets produced.
new XElement("UseAppHost", "false"),
new XElement("SkipCopyBuildProduct", "true")));
Expand Down
2 changes: 2 additions & 0 deletions src/BenchmarkDotNet/Toolchains/DotNetCli/DotNetCliBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ public abstract class DotNetCliBuilder(DotNetCliSettings settings) : BuilderBase
/// </summary>
protected virtual bool PublishesOutput => false;

internal bool IsPublishing => PublishesOutput;

/// <summary>
/// Extra arguments appended to every cli command.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,63 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Environments;
using BenchmarkDotNet.Helpers;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Parameters;
using BenchmarkDotNet.Portability;
using BenchmarkDotNet.Running;
using BenchmarkDotNet.Tests.Mocks;
using BenchmarkDotNet.Toolchains.CoreRun;
using BenchmarkDotNet.Toolchains.InProcess.NoEmit;
using BenchmarkDotNet.Toolchains.Mono;

namespace BenchmarkDotNet.IntegrationTests;

public class IntegrationTestSetupTests
{
[Fact]
public void IntegrationTestsAreDetected() => Assert.True(XUnitHelper.IsIntegrationTest.Value);

[Theory]
[InlineData("net10.0", "default", true)]
[InlineData("net10.0", "configuration", false)]
[InlineData("net10.0", "arguments", false)]
[InlineData("r2r10.0", "default", false)]
[InlineData("r2r10.0", "configuration", false)]
[InlineData("r2r10.0", "arguments", false)]
[InlineData("mono10.0", "default", false)]
[InlineData("nativeaot10.0", "default", false)]
[InlineData("monowasm10.0", "default", false)]
[InlineData("monowasmaot10.0", "default", false)]
[InlineData("corewasm11.0", "default", false)]
[InlineData("CoreRun", "default", false)]
[InlineData("InProcess", "default", false)]
public void OnlyNonPublishingCliBuildsUseTheNoDependenciesShortcut(string toolchain, string buildCharacteristic, bool usesShortcut)
{
var job = toolchain switch
{
"mono10.0" => Job.Dry.WithToolchain(CsProjMonoCoreToolchain.Mono10_0),
"CoreRun" => Job.Dry.WithToolchain(CoreRunToolchain.From(new CoreRunSettings
{
// This case inspects selection only; no CoreRun executable is needed or executed.
SourceCoreRun = new FileInfo(typeof(IntegrationTestSetupTests).Assembly.Location),
CreateCopy = false,
TargetFrameworkMoniker = "net10.0"
})),
"InProcess" => Job.Dry.WithToolchain(InProcessNoEmitToolchain.Default),
_ => Job.Dry.WithRuntime(Runtime.Parse(toolchain))
};
job = buildCharacteristic switch
{
"default" => job,
"configuration" => job.WithCustomBuildConfiguration(InfrastructureMode.ReleaseConfigurationName),
"arguments" => job.WithArguments([]),
_ => throw new ArgumentOutOfRangeException(nameof(buildCharacteristic))
};
var config = ManualConfig.CreateEmpty().CreateImmutableConfig();
var benchmark = BenchmarkCase.Create(new Descriptor(MockFactory.MockType, MockFactory.MockMethodInfo),
job, ParameterInstances.Empty, config);
var partition = new BuildPartition([new BenchmarkBuildInfo(benchmark, config, 0, new([]))], BenchmarkRunnerClean.DefaultResolver);

Assert.Equal(usesShortcut && RuntimeInformation.IsNetCore, partition.ForcedNoDependenciesForIntegrationTests);
}
}
61 changes: 61 additions & 0 deletions tests/BenchmarkDotNet.IntegrationTests/R2RTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,76 @@
using BenchmarkDotNet.Configs;
using BenchmarkDotNet.IntegrationTests.Xunit;
using BenchmarkDotNet.Jobs;
using BenchmarkDotNet.Loggers;
using BenchmarkDotNet.Tests.XUnit;
using BenchmarkDotNet.Toolchains.R2R;
using System.Reflection;
using System.Xml.Linq;

namespace BenchmarkDotNet.IntegrationTests
{
public class R2RTests : BenchmarkTestExecutor
{
public R2RTests(ITestOutputHelper output) : base(output) { }

[FactEnvSpecific("R2R requires .NET Core runtime", EnvRequirement.DotNetCoreOnly)]
public void R2RToolchainGathersReferencesBeforePublishing()
{
var logger = new AccumulationLogger();
var config = ManualConfig.CreateEmpty()
.AddLogger(logger)
.AddJob(Job.Dry
.WithToolchain(CsProjR2RToolchain.R2R10_0))
.WithBuildTimeout(TimeSpan.FromSeconds(540))
.WithOption(ConfigOptions.KeepBenchmarkFiles, true)
.WithOption(ConfigOptions.LogBuildOutput, true);
var summary = CanExecute<R2RBenchmark>(config, fullValidation: false);
string log = logger.GetLog();
Assert.True(summary.Reports.Length == 1, log);
var report = summary.Reports[0];
var paths = report.BuildResult.ArtifactsPaths;
Output.WriteLine($"Reference-gathering artifacts: {paths.BuildArtifactsDirectoryPath}");

string referencesList = Path.Combine(paths.BuildArtifactsDirectoryPath, "ref.txt");
string referencesProps = Path.Combine(paths.BuildArtifactsDirectoryPath, "ref.props");
Assert.True(File.Exists(referencesList) && new FileInfo(referencesList).Length > 0,
$"Reference gathering did not produce a nonempty ref.txt.{Environment.NewLine}{log}");
Assert.True(File.Exists(referencesProps),
$"Reference gathering did not complete successfully (ref.props missing).{Environment.NewLine}{log}");

var gatheredPaths = File.ReadAllLines(referencesList).Select(line => line.Split('|')[0]).ToArray();
var references = XDocument.Load(referencesProps).Descendants("Reference").ToArray();
foreach (string assemblyName in new[] { typeof(Job).Assembly.GetName().Name!, typeof(R2RBenchmark).Assembly.GetName().Name! })
{
var matchingReferences = references.Where(reference => (string?)reference.Attribute("Include") == assemblyName).ToArray();
Assert.True(matchingReferences.Length == 1, $"Expected gathered reference {assemblyName}.{Environment.NewLine}{log}");
string? hintPath = (string?)matchingReferences[0].Element("HintPath");
Assert.True(hintPath is not null && File.Exists(hintPath), $"Gathered reference {assemblyName} is missing.{Environment.NewLine}{log}");
Assert.True(AssemblyName.GetAssemblyName(hintPath!).Name == assemblyName, log);
Assert.True(gatheredPaths.Any(path => string.Equals(path, hintPath, StringComparison.OrdinalIgnoreCase)), log);
}

Assert.True(log.Contains("BenchmarkDotNetGatherReferences=true"), log);
Assert.False(log.Contains("--no-dependencies"), log);
Assert.False(log.Contains("Failed to gather assembly references"), log);
Assert.True(report.BuildResult.IsBuildSuccess, log);
Assert.True(File.Exists(paths.ExecutablePath), log);
string compositeAssembly = Path.Combine(paths.BinariesDirectoryPath, $"{paths.ProgramName}.r2r.dll");
Assert.True(File.Exists(compositeAssembly) && new FileInfo(compositeAssembly).Length > 0,
$"Expected composite assembly: {compositeAssembly}{Environment.NewLine}{log}");
Assert.True(report.ExecuteResults.Count > 0 && report.ExecuteResults.All(result => result.IsSuccess), log);
Assert.True(report.Success, log);

var project = XDocument.Load(paths.ProjectFilePath);
var r2rProperties = Assert.Single(project.Root!.Elements("PropertyGroup"),
group => group.Element("PublishReadyToRunComposite") is not null);
Assert.Equal("true", (string?)r2rProperties.Element("SelfContained"));
Assert.Equal("true", (string?)r2rProperties.Element("PublishReadyToRun"));
Assert.Equal("true", (string?)r2rProperties.Element("PublishReadyToRunComposite"));
Assert.Equal(BenchmarkDotNet.Portability.RuntimeInformation.GetPortableRuntimeIdentifier(),
(string?)r2rProperties.Element("RuntimeIdentifier"));
}

[FactEnvSpecific("R2R requires .NET Core runtime", EnvRequirement.DotNetCoreOnly, EnvRequirement.NonGitHubDraftPR)]
public void R2RToolchainCanExecuteBenchmarks()
{
Expand Down
Loading