diff --git a/BenchmarkDotNet.sln b/BenchmarkDotNet.sln index 1df6c0aabd..f47fde8160 100644 --- a/BenchmarkDotNet.sln +++ b/BenchmarkDotNet.sln @@ -59,6 +59,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Exporters.P EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.Exporters.Plotting.Tests", "tests\BenchmarkDotNet.Exporters.Plotting.Tests\BenchmarkDotNet.Exporters.Plotting.Tests.csproj", "{199AC83E-30BD-40CD-87CE-0C838AC0320D}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BenchmarkDotNet.IntegrationTests.FileLocators", "tests\BenchmarkDotNet.IntegrationTests.FileLocators\BenchmarkDotNet.IntegrationTests.FileLocators.csproj", "{7AD9FCF9-69B5-4984-93AC-D6E30344DADC}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -161,6 +163,10 @@ Global {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Debug|Any CPU.Build.0 = Debug|Any CPU {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Release|Any CPU.ActiveCfg = Release|Any CPU {199AC83E-30BD-40CD-87CE-0C838AC0320D}.Release|Any CPU.Build.0 = Release|Any CPU + {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7AD9FCF9-69B5-4984-93AC-D6E30344DADC}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -190,6 +196,7 @@ Global {2E2283A3-6DA6-4482-8518-99D6D9F689AB} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} {B92ECCEF-7C27-4012-9E19-679F3C40A6A6} = {D6597E3A-6892-4A68-8E14-042FC941FDA2} {199AC83E-30BD-40CD-87CE-0C838AC0320D} = {14195214-591A-45B7-851A-19D3BA2413F9} + {7AD9FCF9-69B5-4984-93AC-D6E30344DADC} = {14195214-591A-45B7-851A-19D3BA2413F9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4D9AF12B-1F7F-45A7-9E8C-E4E46ADCBD1F} diff --git a/src/BenchmarkDotNet/Configs/DebugConfig.cs b/src/BenchmarkDotNet/Configs/DebugConfig.cs index 0fdda7b08d..820fa423c4 100644 --- a/src/BenchmarkDotNet/Configs/DebugConfig.cs +++ b/src/BenchmarkDotNet/Configs/DebugConfig.cs @@ -8,6 +8,7 @@ using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -58,6 +59,7 @@ public abstract class DebugConfig : IConfig public IEnumerable GetValidators() => Array.Empty(); public IEnumerable GetColumnProviders() => DefaultColumnProviders.Instance; public IEnumerable GetExporters() => Array.Empty(); + public IEnumerable GetFileLocators() => Array.Empty(); public IEnumerable GetLoggers() => new[] { ConsoleLogger.Default }; public IEnumerable GetDiagnosers() => Array.Empty(); public IEnumerable GetAnalysers() => Array.Empty(); diff --git a/src/BenchmarkDotNet/Configs/DefaultConfig.cs b/src/BenchmarkDotNet/Configs/DefaultConfig.cs index b70333cc1d..6092c05114 100644 --- a/src/BenchmarkDotNet/Configs/DefaultConfig.cs +++ b/src/BenchmarkDotNet/Configs/DefaultConfig.cs @@ -11,6 +11,7 @@ using BenchmarkDotNet.Exporters.Csv; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Portability; @@ -40,6 +41,8 @@ public IEnumerable GetExporters() yield return HtmlExporter.Default; } + public IEnumerable GetFileLocators() => Array.Empty(); + public IEnumerable GetLoggers() { if (LinqPadLogger.IsAvailable) diff --git a/src/BenchmarkDotNet/Configs/IConfig.cs b/src/BenchmarkDotNet/Configs/IConfig.cs index b311c235f5..7335641534 100644 --- a/src/BenchmarkDotNet/Configs/IConfig.cs +++ b/src/BenchmarkDotNet/Configs/IConfig.cs @@ -8,6 +8,7 @@ using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -20,6 +21,7 @@ public interface IConfig { IEnumerable GetColumnProviders(); IEnumerable GetExporters(); + IEnumerable GetFileLocators(); IEnumerable GetLoggers(); IEnumerable GetDiagnosers(); IEnumerable GetAnalysers(); diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs index b6e03126fd..1526001fbf 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfig.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Globalization; @@ -10,6 +10,7 @@ using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -24,6 +25,7 @@ public sealed class ImmutableConfig : IConfig // if something is an array here instead of hashset it means it must have a guaranteed order of elements private readonly ImmutableArray columnProviders; private readonly ImmutableArray exporters; + private readonly ImmutableArray fileLocators; private readonly ImmutableHashSet loggers; private readonly ImmutableHashSet diagnosers; private readonly ImmutableHashSet analysers; @@ -41,6 +43,7 @@ internal ImmutableConfig( ImmutableHashSet uniqueHardwareCounters, ImmutableHashSet uniqueDiagnosers, ImmutableArray uniqueExporters, + ImmutableArray uniqueFileLocators, ImmutableHashSet uniqueAnalyzers, ImmutableHashSet uniqueValidators, ImmutableHashSet uniqueFilters, @@ -63,6 +66,7 @@ internal ImmutableConfig( hardwareCounters = uniqueHardwareCounters; diagnosers = uniqueDiagnosers; exporters = uniqueExporters; + fileLocators = uniqueFileLocators; analysers = uniqueAnalyzers; validators = uniqueValidators; filters = uniqueFilters; @@ -92,6 +96,7 @@ internal ImmutableConfig( public IEnumerable GetColumnProviders() => columnProviders; public IEnumerable GetExporters() => exporters; + public IEnumerable GetFileLocators() => fileLocators; public IEnumerable GetLoggers() => loggers; public IEnumerable GetDiagnosers() => diagnosers; public IEnumerable GetAnalysers() => analysers; diff --git a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs index d7c0b5eb0f..6394c0e999 100644 --- a/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs +++ b/src/BenchmarkDotNet/Configs/ImmutableConfigBuilder.cs @@ -1,4 +1,4 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using BenchmarkDotNet.Analysers; @@ -44,6 +44,7 @@ public static ImmutableConfig Create(IConfig source) var uniqueHardwareCounters = source.GetHardwareCounters().Where(counter => counter != HardwareCounter.NotSet).ToImmutableHashSet(); var uniqueDiagnosers = GetDiagnosers(source.GetDiagnosers(), uniqueHardwareCounters); var uniqueExporters = GetExporters(source.GetExporters(), uniqueDiagnosers, configAnalyse); + var uniqueFileLocators = source.GetFileLocators().ToImmutableArray(); var uniqueAnalyzers = GetAnalysers(source.GetAnalysers(), uniqueDiagnosers); var uniqueValidators = GetValidators(source.GetValidators(), MandatoryValidators, source.Options); @@ -61,6 +62,7 @@ public static ImmutableConfig Create(IConfig source) uniqueHardwareCounters, uniqueDiagnosers, uniqueExporters, + uniqueFileLocators, uniqueAnalyzers, uniqueValidators, uniqueFilters, diff --git a/src/BenchmarkDotNet/Configs/ManualConfig.cs b/src/BenchmarkDotNet/Configs/ManualConfig.cs index 5ea1be24e9..c05b5c45db 100644 --- a/src/BenchmarkDotNet/Configs/ManualConfig.cs +++ b/src/BenchmarkDotNet/Configs/ManualConfig.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.ComponentModel; using System.Globalization; @@ -11,6 +11,7 @@ using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Filters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -26,6 +27,7 @@ public class ManualConfig : IConfig private readonly List columnProviders = new List(); private readonly List exporters = new List(); + private readonly List locators = new List(); private readonly List loggers = new List(); private readonly List diagnosers = new List(); private readonly List analysers = new List(); @@ -39,6 +41,7 @@ public class ManualConfig : IConfig public IEnumerable GetColumnProviders() => columnProviders; public IEnumerable GetExporters() => exporters; + public IEnumerable GetFileLocators() => locators; public IEnumerable GetLoggers() => loggers; public IEnumerable GetDiagnosers() => diagnosers; public IEnumerable GetAnalysers() => analysers; @@ -139,6 +142,12 @@ public ManualConfig AddExporter(params IExporter[] newExporters) return this; } + public ManualConfig AddFileLocator(params IFileLocator[] newLocators) + { + locators.AddRange(newLocators); + return this; + } + [EditorBrowsable(EditorBrowsableState.Never)] [Obsolete("This method will soon be removed, please start using .AddLogger() instead.")] public void Add(params ILogger[] newLoggers) => AddLogger(newLoggers); @@ -256,6 +265,7 @@ public void Add(IConfig config) { columnProviders.AddRange(config.GetColumnProviders()); exporters.AddRange(config.GetExporters()); + locators.AddRange(config.GetFileLocators()); loggers.AddRange(config.GetLoggers()); diagnosers.AddRange(config.GetDiagnosers()); analysers.AddRange(config.GetAnalysers()); diff --git a/src/BenchmarkDotNet/Locators/FileLocatorArgs.cs b/src/BenchmarkDotNet/Locators/FileLocatorArgs.cs new file mode 100644 index 0000000000..ddbf31d229 --- /dev/null +++ b/src/BenchmarkDotNet/Locators/FileLocatorArgs.cs @@ -0,0 +1,16 @@ +using BenchmarkDotNet.Loggers; +using BenchmarkDotNet.Running; + +namespace BenchmarkDotNet.Locators; + +public class FileLocatorArgs +{ + public FileLocatorArgs(BenchmarkCase benchmarkCase, ILogger logger) + { + BenchmarkCase = benchmarkCase; + Logger = logger; + } + + public BenchmarkCase BenchmarkCase { get; } + public ILogger Logger { get; } +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Locators/FileLocatorType.cs b/src/BenchmarkDotNet/Locators/FileLocatorType.cs new file mode 100644 index 0000000000..5242edfc6c --- /dev/null +++ b/src/BenchmarkDotNet/Locators/FileLocatorType.cs @@ -0,0 +1,6 @@ +namespace BenchmarkDotNet.Locators; + +public enum FileLocatorType +{ + Project +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Locators/IFileLocator.cs b/src/BenchmarkDotNet/Locators/IFileLocator.cs new file mode 100644 index 0000000000..4dd32417e5 --- /dev/null +++ b/src/BenchmarkDotNet/Locators/IFileLocator.cs @@ -0,0 +1,22 @@ +using System.IO; + +namespace BenchmarkDotNet.Locators; + +/// +/// Locators can be used to extend the default behavior of finding files +/// +public interface IFileLocator +{ + /// + /// The type of locator + /// + FileLocatorType LocatorType { get; } + + /// + /// Tries to locate a file + /// + /// The arguments such as benchmark and logger + /// The file is provided by the implementation + /// True when a file was successfully found, False otherwise. + bool TryLocate(FileLocatorArgs fileLocatorArgs, out FileInfo fileInfo); +} \ No newline at end of file diff --git a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs index 7e91c36ff4..e0684ac9bf 100644 --- a/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/CsProj/CsProjGenerator.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics.CodeAnalysis; @@ -11,6 +11,7 @@ using BenchmarkDotNet.Extensions; using BenchmarkDotNet.Helpers; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Running; using BenchmarkDotNet.Toolchains.DotNetCli; @@ -71,7 +72,7 @@ protected override string GetIntermediateDirectoryPath(string buildArtifactsDire protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) { var benchmark = buildPartition.RepresentativeBenchmarkCase; - var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger); + var projectFile = GetProjectFilePath(benchmark, logger); var xmlDoc = new XmlDocument(); xmlDoc.Load(projectFile.FullName); @@ -246,8 +247,29 @@ private static string GetIndentedXmlString(XmlDocument doc) /// returns a path to the project file which defines the benchmarks /// [PublicAPI] - protected virtual FileInfo GetProjectFilePath(Type benchmarkTarget, ILogger logger) + protected virtual FileInfo GetProjectFilePath(BenchmarkCase benchmark, ILogger logger) { + var args = new FileLocatorArgs(benchmark, logger); + + // Try locators first. Logic is provided by the user for uses-cases such as they have set AssemblyName to a custom value. + var notFound = new List(); + foreach (var locator in benchmark.Config.GetFileLocators()) + { + if (locator.LocatorType != FileLocatorType.Project) + { + continue; + } + + if (locator.TryLocate(args, out var fileInfo)) + { + if (fileInfo.Exists) + return fileInfo; + + notFound.Add(fileInfo.FullName); + } + } + + // Fall back to default project detection logic if (!GetSolutionRootDirectory(out var rootDirectory) && !GetProjectRootDirectory(out rootDirectory)) { logger.WriteLineError( @@ -256,7 +278,7 @@ protected virtual FileInfo GetProjectFilePath(Type benchmarkTarget, ILogger logg } // important assumption! project's file name === output dll name - string projectName = benchmarkTarget.GetTypeInfo().Assembly.GetName().Name; + string projectName = benchmark.Descriptor.Type.GetTypeInfo().Assembly.GetName().Name; var possibleNames = new HashSet { $"{projectName}.csproj", $"{projectName}.fsproj", $"{projectName}.vbproj" }; var projectFiles = rootDirectory @@ -266,12 +288,18 @@ protected virtual FileInfo GetProjectFilePath(Type benchmarkTarget, ILogger logg if (projectFiles.Length == 0) { - throw new NotSupportedException( - $"Unable to find {projectName} in {rootDirectory.FullName} and its subfolders. Most probably the name of output exe is different than the name of the .(c/f)sproj"); + string message; + + if (notFound.Count > 0) + message = $"Unable to find {projectName} in any of the paths: {string.Join(", ", notFound)} or in {rootDirectory.FullName} and its subfolders"; + else + message = $"Unable to find {projectName} in {rootDirectory.FullName} and its subfolders. Most probably the name of output exe is different than the name of the .(c/f)sproj. You can add an IFileLocator to the config if this is on purpose."; + + throw new FileNotFoundException(message); } else if (projectFiles.Length > 1) { - throw new NotSupportedException( + throw new InvalidOperationException( $"Found more than one matching project file for {projectName} in {rootDirectory.FullName} and its subfolders: {string.Join(",", projectFiles.Select(pf => $"'{pf.FullName}'"))}. Benchmark project names needs to be unique."); } diff --git a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs index de3cef53d3..906735a425 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoAotLLVM/MonoAotLLVMGenerator.cs @@ -28,7 +28,7 @@ public MonoAotLLVMGenerator(string targetFrameworkMoniker, string cliPath, strin protected override void GenerateProject(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, ILogger logger) { BenchmarkCase benchmark = buildPartition.RepresentativeBenchmarkCase; - var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger); + var projectFile = GetProjectFilePath(benchmark, logger); string useLLVM = AotCompilerMode == MonoAotCompilerMode.llvm ? "true" : "false"; diff --git a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs index 7c9aa8826f..e5e4262429 100644 --- a/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs +++ b/src/BenchmarkDotNet/Toolchains/MonoWasm/WasmGenerator.cs @@ -41,7 +41,7 @@ protected override void GenerateProject(BuildPartition buildPartition, Artifacts protected void GenerateProjectFile(BuildPartition buildPartition, ArtifactsPaths artifactsPaths, bool aot, ILogger logger) { BenchmarkCase benchmark = buildPartition.RepresentativeBenchmarkCase; - var projectFile = GetProjectFilePath(benchmark.Descriptor.Type, logger); + var projectFile = GetProjectFilePath(benchmark, logger); WasmRuntime runtime = (WasmRuntime) buildPartition.Runtime; diff --git a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs index 061b64a9a8..3c97bab265 100644 --- a/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs +++ b/src/BenchmarkDotNet/Toolchains/NativeAot/Generator.cs @@ -151,17 +151,17 @@ private string GenerateProjectForNuGetBuild(BuildPartition buildPartition, Artif {GetILCompilerPackageReference()} - + - {string.Join(Environment.NewLine, GetRdXmlFiles(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, logger).Select(file => $""))} + {string.Join(Environment.NewLine, GetRdXmlFiles(buildPartition.RepresentativeBenchmarkCase, logger).Select(file => $""))} {GetCustomProperties(buildPartition, logger)} "; private string GetCustomProperties(BuildPartition buildPartition, ILogger logger) { - var projectFile = GetProjectFilePath(buildPartition.RepresentativeBenchmarkCase.Descriptor.Type, logger); + var projectFile = GetProjectFilePath(buildPartition.RepresentativeBenchmarkCase, logger); var xmlDoc = new XmlDocument(); xmlDoc.Load(projectFile.FullName); @@ -186,11 +186,11 @@ private string GetInstructionSetSettings(BuildPartition buildPartition) return !string.IsNullOrEmpty(instructionSet) ? $"{instructionSet}" : ""; } - public IEnumerable GetRdXmlFiles(Type benchmarkTarget, ILogger logger) + public IEnumerable GetRdXmlFiles(BenchmarkCase benchmark, ILogger logger) { yield return GeneratedRdXmlFileName; - var projectFile = GetProjectFilePath(benchmarkTarget, logger); + var projectFile = GetProjectFilePath(benchmark, logger); var projectFileFolder = projectFile.DirectoryName; var rdXml = Path.Combine(projectFileFolder, "rd.xml"); if (File.Exists(rdXml)) diff --git a/tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs b/tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs new file mode 100644 index 0000000000..0dc62277d9 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests.FileLocators/AssemblyNameIsSetBenchmarks.cs @@ -0,0 +1,13 @@ +using BenchmarkDotNet.Attributes; + +namespace BenchmarkDotNet.IntegrationTests.FileLocators +{ + public class AssemblyNameIsSetBenchmarks + { + [Benchmark] + public string Benchmark() + { + return "This will only run when a FileLocator is set due to in the csproj"; + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj b/tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj new file mode 100644 index 0000000000..3cfa0494e4 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj @@ -0,0 +1,14 @@ + + + + net462;net8.0 + + + MyCustomName + false + false + + + + + \ No newline at end of file diff --git a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj index 16f12c41b4..5a08fe5a24 100644 --- a/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj +++ b/tests/BenchmarkDotNet.IntegrationTests/BenchmarkDotNet.IntegrationTests.csproj @@ -26,11 +26,13 @@ + + diff --git a/tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs b/tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs new file mode 100644 index 0000000000..d41fbf1341 --- /dev/null +++ b/tests/BenchmarkDotNet.IntegrationTests/FileLocatorTests.cs @@ -0,0 +1,54 @@ +using System; +using System.IO; +using System.Linq; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.IntegrationTests.FileLocators; +using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; +using BenchmarkDotNet.Toolchains.CsProj; +using Xunit; +using Xunit.Abstractions; + +namespace BenchmarkDotNet.IntegrationTests +{ + public class FileLocatorTests : BenchmarkTestExecutor + { + public FileLocatorTests(ITestOutputHelper output) : base(output) { } + + [Fact] + public void ExecutionWithoutFileLocatorShouldFail() + { + var config = ManualConfig.CreateMinimumViable() + .AddJob(Job.Dry + .WithToolchain(CsProjClassicNetToolchain.Net462) + .WithToolchain(CsProjCoreToolchain.NetCoreApp80)); + + var summary = CanExecute(config, false); + Assert.True(summary.Reports.All(r => !r.BuildResult.IsBuildSuccess)); + } + + [Fact] + public void ExecutionWithFileLocatorShouldSucceed() + { + var config = ManualConfig.CreateMinimumViable() + .AddJob(Job.Dry + .WithToolchain(CsProjClassicNetToolchain.Net462) + .WithToolchain(CsProjCoreToolchain.NetCoreApp80)) + .AddFileLocator(new CustomFileLocator()); + + CanExecute(config); + } + + private class CustomFileLocator : IFileLocator + { + public FileLocatorType LocatorType => FileLocatorType.Project; + + public bool TryLocate(FileLocatorArgs args, out FileInfo fileInfo) + { + // We manually locate the csproj file, since the default logic of using the AssemblyName does not work + fileInfo = new FileInfo(Path.Combine(Environment.CurrentDirectory, "../../../../BenchmarkDotNet.IntegrationTests.FileLocators/BenchmarkDotNet.IntegrationTests.FileLocators.csproj")); + return true; + } + } + } +} \ No newline at end of file diff --git a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs index 2393cb737c..d7e17701e6 100644 --- a/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs +++ b/tests/BenchmarkDotNet.Tests/Configs/ImmutableConfigTests.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.IO; using System.Linq; using BenchmarkDotNet.Analysers; using BenchmarkDotNet.Columns; @@ -8,6 +9,7 @@ using BenchmarkDotNet.Environments; using BenchmarkDotNet.Exporters; using BenchmarkDotNet.Jobs; +using BenchmarkDotNet.Locators; using BenchmarkDotNet.Loggers; using BenchmarkDotNet.Order; using BenchmarkDotNet.Reports; @@ -452,5 +454,43 @@ public void GenerateWarningWhenExporterDependencyAlreadyExistInConfig() } } + + [Fact] + public void LocatorsAreAddedCorrectly() + { + var mutable = ManualConfig.CreateEmpty(); + + var expected = new TestFileLocator(); + mutable.AddFileLocator(expected); + + var final = ImmutableConfigBuilder.Create(mutable); + + var actual = Assert.Single(final.GetFileLocators()); + Assert.Same(expected, actual); + } + + [Fact] + public void DuplicateLocatorsAreAllowed() + { + var mutable = ManualConfig.CreateEmpty(); + + var locator = new TestFileLocator(); + mutable.AddFileLocator(locator); + mutable.AddFileLocator(locator); + + var final = ImmutableConfigBuilder.Create(mutable); + + Assert.Equal(2, final.GetFileLocators().Count()); + } + + private class TestFileLocator : IFileLocator + { + public FileLocatorType LocatorType => FileLocatorType.Project; + public bool TryLocate(FileLocatorArgs fileLocatorArgs, out FileInfo fileInfo) + { + fileInfo = new FileInfo(""); + return true; + } + } } }