/
githubmirror
/
roslyn
Обзор
Документация
Войти
/
githubmirror
/
roslyn
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/Compilers/Core/MSBuildTaskTests/MapSourceRootTests.cs
502 строки
23 KB
Jason Malinowski
Remove AssertEx.NotNull
21 апр 2026, 23:28
21 апр 2026, 23:28
4c5adf0
Код
Авторство
О чём код?
// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. using System; using System.Collections.Generic; using System.Linq; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; using Roslyn.Test.Utilities; using Roslyn.Utilities; using Xunit; namespace Microsoft.CodeAnalysis.BuildTasks.UnitTests { public sealed class MapSourceRootsTests { private string InspectSourceRoot(ITaskItem sourceRoot) => $"'{sourceRoot.ItemSpec}'" + $" SourceControl='{sourceRoot.GetMetadata("SourceControl")}'" + $" RevisionId='{sourceRoot.GetMetadata("RevisionId")}'" + $" NestedRoot='{sourceRoot.GetMetadata("NestedRoot")}'" + $" ContainingRoot='{sourceRoot.GetMetadata("ContainingRoot")}'" + $" MappedPath='{sourceRoot.GetMetadata("MappedPath")}'" + $" SourceLinkUrl='{sourceRoot.GetMetadata("SourceLinkUrl")}'"; [Fact] public void BasicMapping() { var engine = new MockEngine(); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = new[] { new TaskItem(Utilities.FixFilePath(@"c:\packages\SourcePackage1\")), new TaskItem(@"/packages/SourcePackage2/"), new TaskItem(Utilities.FixFilePath(@"c:\MyProjects\MyProject\"), new Dictionary<string, string> { { "SourceControl", "Git" }, }), new TaskItem(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\b\"), new Dictionary<string, string> { { "SourceControl", "Git" }, { "NestedRoot", "a/b" }, { "ContainingRoot", Utilities.FixFilePath(@"c:\MyProjects\MyProject\") }, { "some metadata", "some value" }, }), }, Deterministic = true }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); RoslynDebug.Assert(task.MappedSourceRoots is object); Assert.Equal(4, task.MappedSourceRoots.Length); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"c:\packages\SourcePackage1\")), task.MappedSourceRoots[0].ItemSpec); Assert.Equal(@"/_1/", task.MappedSourceRoots[0].GetMetadata("MappedPath")); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath("/packages/SourcePackage2/")), task.MappedSourceRoots[1].ItemSpec); Assert.Equal(@"/_2/", task.MappedSourceRoots[1].GetMetadata("MappedPath")); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"c:\MyProjects\MyProject\")), task.MappedSourceRoots[2].ItemSpec); Assert.Equal(@"/_/", task.MappedSourceRoots[2].GetMetadata("MappedPath")); Assert.Equal(@"Git", task.MappedSourceRoots[2].GetMetadata("SourceControl")); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\b\")), task.MappedSourceRoots[3].ItemSpec); Assert.Equal(@"/_/a/b/", task.MappedSourceRoots[3].GetMetadata("MappedPath")); Assert.Equal(@"Git", task.MappedSourceRoots[3].GetMetadata("SourceControl")); Assert.Equal(@"some value", task.MappedSourceRoots[3].GetMetadata("some metadata")); Assert.True(result); } [Fact] public void InvalidChars() { var engine = new MockEngine(); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = new[] { new TaskItem(@"!@#:;$%^&*()_+|{}\"), new TaskItem(@"****/", new Dictionary<string, string> { { "SourceControl", "Git" }, }), new TaskItem(@"****\|||:;\", new Dictionary<string, string> { { "SourceControl", "Git" }, { "NestedRoot", "|||:;" }, { "ContainingRoot", @"****/" }, }), }, Deterministic = true }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); RoslynDebug.Assert(task.MappedSourceRoots is object); Assert.Equal(3, task.MappedSourceRoots.Length); Assert.Equal(Utilities.FixFilePath(Utilities.GetFullPathNoThrow(@"!@#:;$%^&*()_+|{}\")), task.MappedSourceRoots[0].ItemSpec); Assert.Equal(@"/_1/", task.MappedSourceRoots[0].GetMetadata("MappedPath")); Assert.Equal(Utilities.FixFilePath(Utilities.GetFullPathNoThrow("****/")), task.MappedSourceRoots[1].ItemSpec); Assert.Equal(@"/_/", task.MappedSourceRoots[1].GetMetadata("MappedPath")); Assert.Equal(@"Git", task.MappedSourceRoots[1].GetMetadata("SourceControl")); Assert.Equal(Utilities.FixFilePath(Utilities.GetFullPathNoThrow(@"****\|||:;\")), task.MappedSourceRoots[2].ItemSpec); Assert.Equal(@"/_/|||:;/", task.MappedSourceRoots[2].GetMetadata("MappedPath")); Assert.Equal(@"Git", task.MappedSourceRoots[2].GetMetadata("SourceControl")); Assert.True(result); } [Fact] public void SourceRootPaths_EndWithSeparator() { var engine = new MockEngine(); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = new[] { new TaskItem(@"C:\"), new TaskItem(@"C:/"), new TaskItem(@"C:"), new TaskItem(@"C"), }, Deterministic = true }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences($@" ERROR : {string.Format(ErrorString.MapSourceRoots_PathMustEndWithSlashOrBackslash, "SourceRoot", "C:")} ERROR : {string.Format(ErrorString.MapSourceRoots_PathMustEndWithSlashOrBackslash, "SourceRoot", "C")} ", engine.Log); Assert.False(result); } [Fact] public void NestedRoots_Separators() { var engine = new MockEngine(); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = new[] { new TaskItem(Utilities.FixFilePath(@"c:\MyProjects\MyProject\")), new TaskItem(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\a\"), new Dictionary<string, string> { { "NestedRoot", @"a/a/" }, { "ContainingRoot", Utilities.FixFilePath(@"c:\MyProjects\MyProject\") }, }), new TaskItem(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\b\"), new Dictionary<string, string> { { "NestedRoot", @"a/b\" }, { "ContainingRoot", Utilities.FixFilePath(@"c:\MyProjects\MyProject\") }, }), new TaskItem(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\c\"), new Dictionary<string, string> { { "NestedRoot", @"a\c" }, { "ContainingRoot", Utilities.FixFilePath(@"c:\MyProjects\MyProject\") }, }), }, Deterministic = true }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); RoslynDebug.Assert(task.MappedSourceRoots is object); Assert.Equal(4, task.MappedSourceRoots.Length); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"c:\MyProjects\MyProject\")), task.MappedSourceRoots[0].ItemSpec); Assert.Equal(@"/_/", task.MappedSourceRoots[0].GetMetadata("MappedPath")); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\a\")), task.MappedSourceRoots[1].ItemSpec); Assert.Equal(@"/_/a/a/", task.MappedSourceRoots[1].GetMetadata("MappedPath")); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\b\")), task.MappedSourceRoots[2].ItemSpec); Assert.Equal(@"/_/a/b/", task.MappedSourceRoots[2].GetMetadata("MappedPath")); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\c\")), task.MappedSourceRoots[3].ItemSpec); Assert.Equal(@"/_/a/c/", task.MappedSourceRoots[3].GetMetadata("MappedPath")); Assert.True(result); } [Fact] public void SourceRootCaseSensitive() { var engine = new MockEngine(); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = new[] { new TaskItem(Utilities.FixFilePath(@"c:\packages\SourcePackage1\")), new TaskItem(Utilities.FixFilePath(@"C:\packages\SourcePackage1\")), new TaskItem(Utilities.FixFilePath(@"c:\packages\SourcePackage2\")), }, Deterministic = true }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); RoslynDebug.Assert(task.MappedSourceRoots is object); Assert.Equal(3, task.MappedSourceRoots.Length); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"c:\packages\SourcePackage1\")), task.MappedSourceRoots[0].ItemSpec); Assert.Equal(@"/_/", task.MappedSourceRoots[0].GetMetadata("MappedPath")); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"C:\packages\SourcePackage1\")), task.MappedSourceRoots[1].ItemSpec); Assert.Equal(@"/_1/", task.MappedSourceRoots[1].GetMetadata("MappedPath")); Assert.Equal(Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"c:\packages\SourcePackage2\")), task.MappedSourceRoots[2].ItemSpec); Assert.Equal(@"/_2/", task.MappedSourceRoots[2].GetMetadata("MappedPath")); Assert.True(result); } [Fact] public void Error_Recursion() { var engine = new MockEngine(); var path1 = Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\1\"); var path2 = Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\2\"); var path3 = Utilities.FixFilePath(@"c:\MyProjects\MyProject\"); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = new[] { new TaskItem(path1, new Dictionary<string, string> { { "ContainingRoot", path2 }, { "NestedRoot", "a/1" }, }), new TaskItem(path2, new Dictionary<string, string> { { "ContainingRoot", path1 }, { "NestedRoot", "a/2" }, }), new TaskItem(path3), }, Deterministic = true }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences( "ERROR : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.NoSuchTopLevelSourceRoot", "SourceRoot.ContainingRoot", "SourceRoot", Utilities.GetFullPathNoThrow(path2))) + Environment.NewLine + "ERROR : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.NoSuchTopLevelSourceRoot", "SourceRoot.ContainingRoot", "SourceRoot", Utilities.GetFullPathNoThrow(path1))) + Environment.NewLine, engine.Log); Assert.Null(task.MappedSourceRoots); Assert.False(result); } [Theory] [InlineData(new object[] { true })] [InlineData(new object[] { false })] public void MetadataMerge1(bool deterministic) { var engine = new MockEngine(); var path1 = Utilities.FixFilePath(@"c:\packages\SourcePackage1\"); var path2 = Utilities.FixFilePath(@"c:\packages\SourcePackage2\"); var path3 = Utilities.FixFilePath(@"c:\packages\SourcePackage3\"); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = new[] { new TaskItem(path1, new Dictionary<string, string> { { "NestedRoot", @"NR1A" }, { "ContainingRoot", path3 }, { "RevisionId", "RevId1" }, { "SourceControl", "git" }, { "MappedPath", "MP1" }, { "SourceLinkUrl", "URL1" }, }), new TaskItem(path1, new Dictionary<string, string> { { "NestedRoot", @"NR1B" }, { "ContainingRoot", @"CR" }, { "RevisionId", "RevId2" }, { "SourceControl", "tfvc" }, { "MappedPath", "MP2" }, { "SourceLinkUrl", "URL2" }, }), new TaskItem(path2, new Dictionary<string, string> { { "NestedRoot", @"NR2" }, { "SourceControl", "git" }, }), new TaskItem(path2, new Dictionary<string, string> { { "ContainingRoot", path3 }, { "SourceControl", "git" }, }), new TaskItem(path3), }, Deterministic = deterministic }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences( "WARNING : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.ContainsDuplicate", "SourceRoot", Utilities.GetFullPathNoThrow(path1), "SourceControl", "git", "tfvc")) + Environment.NewLine + "WARNING : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.ContainsDuplicate", "SourceRoot", Utilities.GetFullPathNoThrow(path1), "RevisionId", "RevId1", "RevId2")) + Environment.NewLine + "WARNING : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.ContainsDuplicate", "SourceRoot", Utilities.GetFullPathNoThrow(path1), "NestedRoot", "NR1A", "NR1B")) + Environment.NewLine + "WARNING : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.ContainsDuplicate", "SourceRoot", Utilities.GetFullPathNoThrow(path1), "ContainingRoot", path3, "CR")) + Environment.NewLine + "WARNING : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.ContainsDuplicate", "SourceRoot", Utilities.GetFullPathNoThrow(path1), "MappedPath", "MP1", "MP2")) + Environment.NewLine + "WARNING : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.ContainsDuplicate", "SourceRoot", Utilities.GetFullPathNoThrow(path1), "SourceLinkUrl", "URL1", "URL2")) + Environment.NewLine, engine.Log); Assert.NotNull(task.MappedSourceRoots); AssertEx.Equal(string.Join("\n", [ $"'{Utilities.GetFullPathNoThrow(path1)}' SourceControl='git' RevisionId='RevId1' NestedRoot='NR1A' ContainingRoot='{(deterministic ? Utilities.GetFullPathNoThrow(path3) : path3)}' MappedPath='{(deterministic ? "/_/NR1A/" : Utilities.GetFullPathNoThrow(path1))}' SourceLinkUrl='URL1'", $"'{Utilities.GetFullPathNoThrow(path2)}' SourceControl='git' RevisionId='' NestedRoot='NR2' ContainingRoot='{(deterministic ? Utilities.GetFullPathNoThrow(path3) : path3)}' MappedPath='{(deterministic ? "/_/NR2/" : Utilities.GetFullPathNoThrow(path2))}' SourceLinkUrl=''", $"'{Utilities.GetFullPathNoThrow(path3)}' SourceControl='' RevisionId='' NestedRoot='' ContainingRoot='' MappedPath='{(deterministic ? "/_/" : Utilities.GetFullPathNoThrow(path3))}' SourceLinkUrl=''", ]), string.Join("\n", task.MappedSourceRoots.Select(InspectSourceRoot))); Assert.True(result); } [Fact] public void Error_MissingContainingRoot() { var engine = new MockEngine(); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = new[] { new TaskItem(Utilities.FixFilePath(@"c:\MyProjects\MYPROJECT\")), new TaskItem(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\b\"), new Dictionary<string, string> { { "SourceControl", "Git" }, { "NestedRoot", "a/b" }, { "ContainingRoot", Utilities.FixFilePath(@"c:\MyProjects\MyProject\") }, }), }, Deterministic = true }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences("ERROR : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.NoSuchTopLevelSourceRoot", "SourceRoot.ContainingRoot", "SourceRoot", Utilities.GetFullPathNoThrow(Utilities.FixFilePath(@"c:\MyProjects\MyProject\")))) + Environment.NewLine, engine.Log); Assert.Null(task.MappedSourceRoots); Assert.False(result); } [Fact] public void Error_NoContainingRootSpecified() { var engine = new MockEngine(); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = new[] { new TaskItem(@"c:\MyProjects\MyProject\"), new TaskItem(@"c:\MyProjects\MyProject\a\b\", new Dictionary<string, string> { { "SourceControl", "Git" }, { "NestedRoot", "a/b" }, }), }, Deterministic = true }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences("ERROR : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.NoSuchTopLevelSourceRoot", "SourceRoot.ContainingRoot", "SourceRoot", @"")) + Environment.NewLine, engine.Log); Assert.Null(task.MappedSourceRoots); Assert.False(result); } [Theory] [InlineData(new object[] { true })] [InlineData(new object[] { false })] public void Error_NoTopLevelSourceRoot(bool deterministic) { var engine = new MockEngine(); var path1 = Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\b\"); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = new[] { new TaskItem(path1, new Dictionary<string, string> { { "ContainingRoot", path1 }, { "NestedRoot", "a/b" }, }), }, Deterministic = deterministic }; bool result = task.Execute(); if (deterministic) { AssertEx.AssertEqualToleratingWhitespaceDifferences("ERROR : " + string.Format(task.Log.FormatResourceString( "MapSourceRoots.NoTopLevelSourceRoot", "SourceRoot", "DeterministicSourcePaths")) + Environment.NewLine, engine.Log); Assert.Null(task.MappedSourceRoots); Assert.False(result); } else { Assert.NotNull(task.MappedSourceRoots); AssertEx.Equal(string.Join("\n", [ $"'{Utilities.GetFullPathNoThrow(path1)}' SourceControl='' RevisionId='' NestedRoot='a/b' ContainingRoot='{(deterministic ? Utilities.GetFullPathNoThrow(path1) : path1)}' MappedPath='{Utilities.GetFullPathNoThrow(path1)}' SourceLinkUrl=''", ]), string.Join("\n", task.MappedSourceRoots.Select(InspectSourceRoot))); Assert.True(result); } } [Theory, CombinatorialData, WorkItem("https://github.com/dotnet/roslyn/issues/82112")] public void NormalizePaths(bool deterministic, [CombinatorialValues("e", "d/../e")] string nestedRoot) { var engine = new MockEngine(); var originalPath1 = Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\b\..\c\"); var normalizedPath1 = Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\c\"); var originalPath2 = Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\b\..\c\d\..\e\"); var normalizedPath2 = Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\c\e\"); var task = new MapSourceRoots { BuildEngine = engine, SourceRoots = [ new TaskItem(originalPath1), new TaskItem(originalPath2, new Dictionary<string, string> { { "ContainingRoot", originalPath1 }, { "NestedRoot", nestedRoot }, }), ], Deterministic = deterministic, }; bool result = task.Execute(); AssertEx.AssertEqualToleratingWhitespaceDifferences("", engine.Log); var expectedMappedPath1 = deterministic ? "/_/" : Utilities.GetFullPathNoThrow(normalizedPath1); var expectedNestedRoot = deterministic ? "e" : nestedRoot; var expectedContainingRoot = deterministic ? Utilities.GetFullPathNoThrow(normalizedPath1) : originalPath1; var expectedMappedPath2 = deterministic ? "/_/e/" : Utilities.GetFullPathNoThrow(normalizedPath2); Assert.NotNull(task.MappedSourceRoots); AssertEx.Equal( $""" '{Utilities.GetFullPathNoThrow(normalizedPath1)}' SourceControl='' RevisionId='' NestedRoot='' ContainingRoot='' MappedPath='{expectedMappedPath1}' SourceLinkUrl='' '{Utilities.GetFullPathNoThrow(normalizedPath2)}' SourceControl='' RevisionId='' NestedRoot='{expectedNestedRoot}' ContainingRoot='{expectedContainingRoot}' MappedPath='{expectedMappedPath2}' SourceLinkUrl='' """, string.Join(Environment.NewLine, task.MappedSourceRoots.Select(InspectSourceRoot))); Assert.True(result); } } }