diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InitialisationProcessTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InitialisationProcessTests.cs index 7d35a9418f..0c62a9ae01 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InitialisationProcessTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/InitialisationProcessTests.cs @@ -59,9 +59,13 @@ public void InitialisationProcess_ShouldCallNeededResolvers() testRunnerMock.Object, assemblyReferenceResolverMock.Object); - var options = new StrykerOptions(); + var options = new StrykerOptions + { + ProjectName = "TheProjectName", + ProjectVersion = "TheProjectVersion" + }; - var result = target.Initialize(options, dashboardReporter: null); + var result = target.Initialize(options); inputFileResolverMock.Verify(x => x.ResolveInput(It.IsAny()), Times.Once); } @@ -104,9 +108,13 @@ public void InitialisationProcess_ShouldThrowOnFailedInitialTestRun() initialTestProcessMock.Object, testRunnerMock.Object, assemblyReferenceResolverMock.Object); - var options = new StrykerOptions(); + var options = new StrykerOptions + { + ProjectName = "TheProjectName", + ProjectVersion = "TheProjectVersion" + }; - target.Initialize(options, dashboardReporter: null); + target.Initialize(options); Assert.Throws(() => target.InitialTest(options)); inputFileResolverMock.Verify(x => x.ResolveInput(It.IsAny()), Times.Once); diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/ProjectMutatorTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/ProjectMutatorTests.cs index 6dabe4f3dd..2d166fdbe4 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/ProjectMutatorTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/ProjectMutatorTests.cs @@ -51,7 +51,7 @@ public void ShouldInitializeEachProjectInSolution() var options = new StrykerOptions(); var target = new ProjectMutator(_initialisationProcessProviderMock.Object, _mutationTestProcessProviderMock.Object); - _initialisationProcessMock.Setup(x => x.Initialize(It.IsAny(), It.IsAny())).Returns(_mutationTestInput); + _initialisationProcessMock.Setup(x => x.Initialize(It.IsAny())).Returns(_mutationTestInput); _initialisationProcessMock.Setup(x => x.InitialTest(options)) .Returns(new InitialTestRun(new TestRunResult(true), new TimeoutValueCalculator(500))); // act diff --git a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/ProjectOrchestratorTests.cs b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/ProjectOrchestratorTests.cs index 3d9cc1b6c7..a7411aaab4 100644 --- a/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/ProjectOrchestratorTests.cs +++ b/src/Stryker.Core/Stryker.Core.UnitTest/Initialisation/ProjectOrchestratorTests.cs @@ -63,7 +63,7 @@ public void ShouldInitializeEachProjectInSolution() }; var target = new ProjectOrchestrator(_buildalyzerProviderMock.Object, _projectMutatorMock.Object); - _initialisationProcessMock.Setup(x => x.Initialize(It.IsAny(), It.IsAny())).Returns(_mutationTestInput); + _initialisationProcessMock.Setup(x => x.Initialize(It.IsAny())).Returns(_mutationTestInput); _initialisationProcessMock.Setup(x => x.InitialTest(It.IsAny())).Returns(new InitialTestRun(new TestRunResult(true), new TimeoutValueCalculator(5))); _buildalyzerProviderMock.Setup(x => x.Provide(It.IsAny(), It.IsAny())).Returns(buildalyzerAnalyzerManagerMock.Object); // The analyzer finds two projects diff --git a/src/Stryker.Core/Stryker.Core/Clients/DashboardClient.cs b/src/Stryker.Core/Stryker.Core/Clients/DashboardClient.cs index acd3ced4fe..d7af6dd0b4 100644 --- a/src/Stryker.Core/Stryker.Core/Clients/DashboardClient.cs +++ b/src/Stryker.Core/Stryker.Core/Clients/DashboardClient.cs @@ -11,7 +11,6 @@ namespace Stryker.Core.Clients { public interface IDashboardClient { - string ProjectName { get; set; } Task PublishReport(JsonReport json, string version); Task PullReport(string version); } @@ -35,11 +34,8 @@ public DashboardClient(StrykerOptions options, HttpClient httpClient = null, ILo _httpClient = new HttpClient(); _httpClient.DefaultRequestHeaders.Add("X-Api-Key", _options.DashboardApiKey); } - ProjectName = _options.ProjectName; } - public string ProjectName { get; set; } - public async Task PublishReport(JsonReport report, string version) { var url = GetUrl(version); @@ -49,6 +45,7 @@ public async Task PublishReport(JsonReport report, string version) try { using var response = await _httpClient.PutAsJsonAsync(url, report); + response.EnsureSuccessStatusCode(); var result = await response.Content.ReadFromJsonAsync(); return result?.Href; } @@ -78,7 +75,7 @@ public async Task PullReport(string version) private Uri GetUrl(string version) { - var url = new Uri($"{_options.DashboardUrl}/api/reports/{ProjectName}/{version}"); + var url = new Uri($"{_options.DashboardUrl}/api/reports/{_options.ProjectName}/{version}"); if (_options.ModuleName != null) { diff --git a/src/Stryker.Core/Stryker.Core/Initialisation/InitialisationProcess.cs b/src/Stryker.Core/Stryker.Core/Initialisation/InitialisationProcess.cs index eba169bb0e..4f7cd9a90d 100644 --- a/src/Stryker.Core/Stryker.Core/Initialisation/InitialisationProcess.cs +++ b/src/Stryker.Core/Stryker.Core/Initialisation/InitialisationProcess.cs @@ -4,6 +4,7 @@ using System.Linq; using Microsoft.Extensions.Logging; using Mono.Cecil; +using Stryker.Core.Baseline.Providers; using Stryker.Core.Exceptions; using Stryker.Core.Initialisation.Buildalyzer; using Stryker.Core.Logging; @@ -29,7 +30,7 @@ public class InitialisationProcessProvider : IInitialisationProcessProvider public interface IInitialisationProcess { - MutationTestInput Initialize(StrykerOptions options, DashboardReporter dashboardReporter); + MutationTestInput Initialize(StrykerOptions options); InitialTestRun InitialTest(StrykerOptions options); } @@ -57,7 +58,7 @@ public InitialisationProcess( _logger = ApplicationLogging.LoggerFactory.CreateLogger(); } - public MutationTestInput Initialize(StrykerOptions options, DashboardReporter dashboardReporter) + public MutationTestInput Initialize(StrykerOptions options) { // resolve project info var projectInfo = _inputFileResolver.ResolveInput(options); @@ -78,7 +79,7 @@ public MutationTestInput Initialize(StrykerOptions options, DashboardReporter da options.MsBuildPath); } - InitializeDashboardReporter(dashboardReporter, projectInfo); + InitializeDashboardProjectInformation(options, projectInfo); if (_testRunner == null) { @@ -99,16 +100,19 @@ public InitialTestRun InitialTest(StrykerOptions options) => // initial test _initialTestProcess.InitialTest(options, _testRunner); - private void InitializeDashboardReporter(DashboardReporter dashboardReporter, ProjectInfo projectInfo) + private void InitializeDashboardProjectInformation(StrykerOptions options, ProjectInfo projectInfo) { - if (dashboardReporter == null) + var dashboardReporterEnabled = options.Reporters.Contains(Reporter.Dashboard) || options.Reporters.Contains(Reporter.All); + var dashboardBaselineEnabled = options.WithBaseline && options.BaselineProvider == BaselineProvider.Dashboard; + var requiresProjectInformation = dashboardReporterEnabled || dashboardBaselineEnabled; + if (!requiresProjectInformation) { return; } - // try to read the repository URL + version for the dashboard report - var missingProjectName = string.IsNullOrEmpty(dashboardReporter.ProjectName); - var missingProjectVersion = string.IsNullOrEmpty(dashboardReporter.ProjectVersion); + // try to read the repository URL + version for the dashboard report or dashboard baseline + var missingProjectName = string.IsNullOrEmpty(options.ProjectName); + var missingProjectVersion = string.IsNullOrEmpty(options.ProjectVersion); if (missingProjectName || missingProjectVersion) { var subject = missingProjectName switch @@ -135,14 +139,14 @@ private void InitializeDashboardReporter(DashboardReporter dashboardReporter, Pr var details = $"To solve this issue, either specify the {subject.ToLowerInvariant()} in the stryker configuration or configure [SourceLink](/~https://github.com/dotnet/sourcelink#readme) in {projectFilePath}"; if (missingProjectName) { - dashboardReporter.ProjectName = ReadProjectName(module, details); - _logger.LogDebug("Using {ProjectName} as project name for the dashboard reporter. (Read from the AssemblyMetadata/RepositoryUrl assembly attribute of {TargetName})", dashboardReporter.ProjectName, targetName); + options.ProjectName = ReadProjectName(module, details); + _logger.LogDebug("Using {ProjectName} as project name for the dashboard reporter. (Read from the AssemblyMetadata/RepositoryUrl assembly attribute of {TargetName})", options.ProjectName, targetName); } if (missingProjectVersion) { - dashboardReporter.ProjectVersion = ReadProjectVersion(module, details); - _logger.LogDebug("Using {ProjectVersion} as project version for the dashboard reporter. (Read from the AssemblyInformationalVersion assembly attribute of {TargetName})", dashboardReporter.ProjectVersion, targetName); + options.ProjectVersion = ReadProjectVersion(module, details); + _logger.LogDebug("Using {ProjectVersion} as project version for the dashboard reporter. (Read from the AssemblyInformationalVersion assembly attribute of {TargetName})", options.ProjectVersion, targetName); } } catch (Exception e) when (e is not InputException) diff --git a/src/Stryker.Core/Stryker.Core/Initialisation/ProjectMutator.cs b/src/Stryker.Core/Stryker.Core/Initialisation/ProjectMutator.cs index 2a4dfbef75..122a0d9907 100644 --- a/src/Stryker.Core/Stryker.Core/Initialisation/ProjectMutator.cs +++ b/src/Stryker.Core/Stryker.Core/Initialisation/ProjectMutator.cs @@ -27,8 +27,7 @@ public IMutationTestProcess MutateProject(StrykerOptions options, IReporter repo // get a new instance of InitialisationProcess for each project var initialisationProcess = _initialisationProcessProvider.Provide(); // initialize - var dashboardReporter = GetDashboardReporter(reporters); - var input = initialisationProcess.Initialize(options, dashboardReporter); + var input = initialisationProcess.Initialize(options); var process = _mutationTestProcessProvider.Provide( mutationTestInput: input, @@ -44,15 +43,5 @@ public IMutationTestProcess MutateProject(StrykerOptions options, IReporter repo return process; } - - private static DashboardReporter GetDashboardReporter(IReporter reporters) - { - if (reporters is BroadcastReporter broadcastReporter) - { - return broadcastReporter.Reporters.OfType().FirstOrDefault(); - } - - return reporters as DashboardReporter; - } } } diff --git a/src/Stryker.Core/Stryker.Core/Options/StrykerOptions.cs b/src/Stryker.Core/Stryker.Core/Options/StrykerOptions.cs index c59e90cc96..9f7b4a6064 100644 --- a/src/Stryker.Core/Stryker.Core/Options/StrykerOptions.cs +++ b/src/Stryker.Core/Stryker.Core/Options/StrykerOptions.cs @@ -39,14 +39,12 @@ public class StrykerOptions public string DashboardUrl { get; init; } public string DashboardApiKey { get; init; } - public string ProjectName { get; init; } public bool Since { get; init; } public string SinceTarget { get; init; } public IEnumerable DiffIgnoreChanges { get; init; } = Enumerable.Empty(); public string FallbackVersion { get; init; } - public string ProjectVersion { get; init; } public string ModuleName { get; init; } public IEnumerable Mutate { get; init; } = new[] { FilePattern.Parse("**/*") }; @@ -56,8 +54,40 @@ public class StrykerOptions public OptimizationModes OptimizationMode { get; init; } + private string _projectName; + public string ProjectName + { + get => _projectName; + set + { + _projectName = value; + if (_parentOptions is not null) + { + _parentOptions.ProjectName = value; + } + } + } + + private string _projectVersion; + public string ProjectVersion + { + get => _projectVersion; + set + { + _projectVersion = value; + if (_parentOptions is not null) + { + _parentOptions.ProjectVersion = value; + } + } + } + + // Keep a reference on the parent instance in order to flow get/set properties (ProjectName and ProjectVersion) up to the parent + private StrykerOptions _parentOptions; + public StrykerOptions Copy(string basePath, string projectUnderTest, IEnumerable testProjects) => new() { + _parentOptions = this, AdditionalTimeout = AdditionalTimeout, AzureFileStorageSas = AzureFileStorageSas, AzureFileStorageUrl = AzureFileStorageUrl, diff --git a/src/Stryker.Core/Stryker.Core/Reporters/DashboardReporter.cs b/src/Stryker.Core/Stryker.Core/Reporters/DashboardReporter.cs index f5b8fb88ae..e51348f9d7 100644 --- a/src/Stryker.Core/Stryker.Core/Reporters/DashboardReporter.cs +++ b/src/Stryker.Core/Stryker.Core/Reporters/DashboardReporter.cs @@ -12,7 +12,7 @@ namespace Stryker.Core.Reporters { - public partial class DashboardReporter : IReporter + public class DashboardReporter : IReporter { private readonly StrykerOptions _options; private readonly IDashboardClient _dashboardClient; @@ -25,22 +25,13 @@ public DashboardReporter(StrykerOptions options, IDashboardClient dashboardClien _dashboardClient = dashboardClient ?? new DashboardClient(options); _logger = logger ?? ApplicationLogging.LoggerFactory.CreateLogger(); _consoleWriter = consoleWriter ?? Console.Out; - ProjectVersion = _options.ProjectVersion; } - public string ProjectName - { - get => _dashboardClient.ProjectName; - set => _dashboardClient.ProjectName = value; - } - - public string ProjectVersion { get; set; } - public void OnAllMutantsTested(IReadOnlyProjectComponent reportComponent) { var mutationReport = JsonReport.Build(_options, reportComponent); - var reportUrl = _dashboardClient.PublishReport(mutationReport, ProjectVersion).Result; + var reportUrl = _dashboardClient.PublishReport(mutationReport, _options.ProjectVersion).Result; if (reportUrl != null) { diff --git a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportFileComponent.cs b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportFileComponent.cs index ed098f4296..a7eac50889 100644 --- a/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportFileComponent.cs +++ b/src/Stryker.Core/Stryker.Core/Reporters/JsonReporter/JsonReportFileComponent.cs @@ -33,11 +33,11 @@ public JsonReportFileComponent(ReadOnlyFileLeaf file, ILogger logger = null) var jsonMutant = new JsonMutant { Id = mutant.Id.ToString(), - MutatorName = mutant.Mutation.DisplayName, + MutatorName = mutant.Mutation.DisplayName ?? "", Replacement = mutant.Mutation.ReplacementNode.ToFullString(), Location = new JsonMutantLocation(mutant.Mutation.OriginalNode.GetLocation().GetMappedLineSpan()), Status = mutant.ResultStatus.ToString(), - Description = mutant.Mutation.Description + Description = mutant.Mutation.Description ?? "" }; if (!Mutants.Add(jsonMutant))