-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Fix wrong paths in SharedOutputPathAnalyzer. (#10472)
Related to #10414 Context PR #10238 added normalization of the path (which includes getting full path) before the project directory path is added to it. This led to the relative path being turned into a full path using CurrentDirectory instead of the project directory. This leads to enormous amount of false-positive analyzer's warnings (400 instead of 3) that cause the hang. This PR fixes the bug with paths and does not fix actual cause of a hang. Changes Made Reverted the PR #10238 and instead added normalization of the path later in the code when the project path is already combined with the relative path. Testing unit tests and local runs
- Loading branch information
Showing
5 changed files
with
203 additions
and
53 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
56 changes: 56 additions & 0 deletions
56
src/BuildCheck.UnitTests/MockBuildCheckRegistrationContext.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using Microsoft.Build.Experimental.BuildCheck; | ||
using Microsoft.Build.Experimental.BuildCheck.Infrastructure; | ||
|
||
namespace Microsoft.Build.BuildCheck.UnitTests | ||
{ | ||
internal sealed class MockBuildCheckRegistrationContext : IBuildCheckRegistrationContext | ||
{ | ||
private event Action<BuildCheckDataContext<TaskInvocationAnalysisData>>? _taskInvocationAction; | ||
private event Action<BuildCheckDataContext<EvaluatedPropertiesAnalysisData>>? _evaluatedPropertiesAction; | ||
|
||
public List<BuildCheckResult> Results { get; } = new(); | ||
|
||
public void RegisterEvaluatedPropertiesAction(Action<BuildCheckDataContext<EvaluatedPropertiesAnalysisData>> evaluatedPropertiesAction) | ||
=> _evaluatedPropertiesAction += evaluatedPropertiesAction; | ||
public void RegisterParsedItemsAction(Action<BuildCheckDataContext<ParsedItemsAnalysisData>> parsedItemsAction) => throw new NotImplementedException(); | ||
|
||
public void RegisterTaskInvocationAction(Action<BuildCheckDataContext<TaskInvocationAnalysisData>> taskInvocationAction) | ||
=> _taskInvocationAction += taskInvocationAction; | ||
|
||
public void TriggerTaskInvocationAction(TaskInvocationAnalysisData data) | ||
{ | ||
if (_taskInvocationAction is not null) | ||
{ | ||
BuildCheckDataContext<TaskInvocationAnalysisData> context = new BuildCheckDataContext<TaskInvocationAnalysisData>( | ||
null!, | ||
null!, | ||
null!, | ||
ResultHandler, | ||
data); | ||
_taskInvocationAction(context); | ||
} | ||
} | ||
public void TriggerEvaluatedPropertiesAction(EvaluatedPropertiesAnalysisData data) | ||
{ | ||
if (_evaluatedPropertiesAction is not null) | ||
{ | ||
BuildCheckDataContext<EvaluatedPropertiesAnalysisData> context = new BuildCheckDataContext<EvaluatedPropertiesAnalysisData>( | ||
null!, | ||
null!, | ||
null!, | ||
ResultHandler, | ||
data); | ||
_evaluatedPropertiesAction(context); | ||
} | ||
} | ||
|
||
private void ResultHandler(BuildAnalyzerWrapper wrapper, IAnalysisContext context, BuildAnalyzerConfigurationEffective[] configs, BuildCheckResult result) | ||
=> Results.Add(result); | ||
} | ||
} | ||
|
143 changes: 143 additions & 0 deletions
143
src/BuildCheck.UnitTests/SharedOutputPathAnalyzer_Tests.cs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.IO; | ||
using System.Linq; | ||
using System.Text; | ||
using System.Threading.Tasks; | ||
using Microsoft.Build.Experimental.BuildCheck; | ||
using Microsoft.Build.Experimental.BuildCheck.Analyzers; | ||
using Shouldly; | ||
using Xunit; | ||
|
||
namespace Microsoft.Build.BuildCheck.UnitTests | ||
{ | ||
public class SharedOutputPathAnalyzer_Tests | ||
{ | ||
private readonly SharedOutputPathAnalyzer _analyzer; | ||
|
||
private readonly MockBuildCheckRegistrationContext _registrationContext; | ||
|
||
public SharedOutputPathAnalyzer_Tests() | ||
{ | ||
_analyzer = new SharedOutputPathAnalyzer(); | ||
_registrationContext = new MockBuildCheckRegistrationContext(); | ||
_analyzer.RegisterActions(_registrationContext); | ||
} | ||
|
||
private EvaluatedPropertiesAnalysisData MakeEvaluatedPropertiesAction( | ||
string projectFile, | ||
Dictionary<string, string>? evaluatedProperties, | ||
IReadOnlyDictionary<string, (string EnvVarValue, string File, int Line, int Column)>? evaluatedEnvVars) | ||
{ | ||
return new EvaluatedPropertiesAnalysisData( | ||
projectFile, | ||
null, | ||
evaluatedProperties ?? new Dictionary<string, string>(), | ||
evaluatedEnvVars ?? new Dictionary<string, (string EnvVarValue, string File, int Line, int Column)>()); | ||
} | ||
|
||
[Fact] | ||
public void TestTwoProjectsWithSameRelativeOutputPath() | ||
{ | ||
// Full output and intermediate paths are different: "C:/fake1/bin/Debug" and "C:/fake1/obj/Debug". | ||
string projectFile1 = NativeMethodsShared.IsWindows ? "C:\\fake1\\project1.proj" : "/fake1/project1.proj"; | ||
_registrationContext.TriggerEvaluatedPropertiesAction(MakeEvaluatedPropertiesAction( | ||
projectFile1, | ||
new Dictionary<string, string> { | ||
{ "OutputPath", "bin/Debug" }, | ||
{ "IntermediateOutputPath", "obj/Debug" }, | ||
}, | ||
null)); | ||
|
||
// Full output and intermediate paths are different: "C:/fake2/bin/Debug" and "C:/fake2/obj/Debug". | ||
string projectFile2 = NativeMethodsShared.IsWindows ? "C:\\fake2\\project2.proj" : "/fake2/project2.proj"; | ||
_registrationContext.TriggerEvaluatedPropertiesAction(MakeEvaluatedPropertiesAction( | ||
projectFile2, | ||
new Dictionary<string, string> { | ||
{ "OutputPath", "bin/Debug" }, | ||
{ "IntermediateOutputPath", "obj/Debug" }, | ||
}, | ||
null)); | ||
|
||
// Relative paths coincide but full does not. SharedOutputPathAnalyzer should not report it. | ||
_registrationContext.Results.Count.ShouldBe(0); | ||
} | ||
|
||
[Fact] | ||
public void TestProjectsWithDifferentPathsSeparators() | ||
{ | ||
// Paths separators are messed up. | ||
string projectFile1 = NativeMethodsShared.IsWindows ? "C:\\fake\\project1.proj" : "/fake/project1.proj"; | ||
string projectFile2 = NativeMethodsShared.IsWindows ? "C:\\fake\\project2.proj" : "/fake/project2.proj"; | ||
|
||
_registrationContext.TriggerEvaluatedPropertiesAction(MakeEvaluatedPropertiesAction( | ||
projectFile1, | ||
new Dictionary<string, string> { | ||
{ "OutputPath", "bin/Debug" }, | ||
{ "IntermediateOutputPath", "obj\\Debug" }, | ||
}, | ||
null)); | ||
|
||
_registrationContext.TriggerEvaluatedPropertiesAction(MakeEvaluatedPropertiesAction( | ||
projectFile2, | ||
new Dictionary<string, string> { | ||
{ "OutputPath", "bin/Debug" }, | ||
{ "IntermediateOutputPath", "obj\\Debug" }, | ||
}, | ||
null)); | ||
|
||
// 2 reports for bin and obj folders. | ||
_registrationContext.Results.Count.ShouldBe(2); | ||
_registrationContext.Results[0].BuildAnalyzerRule.Id.ShouldBe("BC0101"); | ||
_registrationContext.Results[1].BuildAnalyzerRule.Id.ShouldBe("BC0101"); | ||
|
||
// Check that paths are formed with correct paths separators | ||
string wrongPathSeparator = NativeMethodsShared.IsWindows ? "/" : "\\"; | ||
|
||
foreach (string path in _registrationContext.Results[0].MessageArgs) | ||
{ | ||
path.ShouldNotContain(wrongPathSeparator); | ||
} | ||
foreach (string path in _registrationContext.Results[1].MessageArgs) | ||
{ | ||
path.ShouldNotContain(wrongPathSeparator); | ||
} | ||
} | ||
|
||
[Fact] | ||
public void TestThreeProjectsWithSameOutputPath() | ||
{ | ||
string projectFolder = NativeMethodsShared.IsWindows ? "C:\\fake\\" : "/fake/"; | ||
string projectFile1 = $"{projectFolder}project1.proj"; | ||
string projectFile2 = $"{projectFolder}project2.proj"; | ||
string projectFile3 = $"{projectFolder}project3.proj"; | ||
var evaluatedProperties = new Dictionary<string, string> { | ||
{ "OutputPath", "bin/Debug" }, | ||
{ "IntermediateOutputPath", "obj\\Debug" },}; | ||
|
||
_registrationContext.TriggerEvaluatedPropertiesAction(MakeEvaluatedPropertiesAction( | ||
projectFile1, | ||
evaluatedProperties, | ||
null)); | ||
|
||
_registrationContext.TriggerEvaluatedPropertiesAction(MakeEvaluatedPropertiesAction( | ||
projectFile2, | ||
evaluatedProperties, | ||
null)); | ||
|
||
_registrationContext.TriggerEvaluatedPropertiesAction(MakeEvaluatedPropertiesAction( | ||
projectFile3, | ||
evaluatedProperties, | ||
null)); | ||
|
||
_registrationContext.Results.Count.ShouldBe(4); // 4 reports for two pairs of project: (1, 2) and (1, 3). | ||
foreach (var result in _registrationContext.Results) | ||
{ | ||
result.BuildAnalyzerRule.Id.ShouldBe("BC0101"); | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters