|
// 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(@"c:\packages\SourcePackage1\"),
new TaskItem(@"/packages/SourcePackage2/"),
new TaskItem(@"c:\MyProjects\MyProject\", new Dictionary<string, string>
{
{ "SourceControl", "Git" },
}),
new TaskItem(@"c:\MyProjects\MyProject\a\b\", new Dictionary<string, string>
{
{ "SourceControl", "Git" },
{ "NestedRoot", "a/b" },
{ "ContainingRoot", @"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.FixFilePath(@"c:\packages\SourcePackage1\"), task.MappedSourceRoots[0].ItemSpec);
Assert.Equal(@"/_1/", task.MappedSourceRoots[0].GetMetadata("MappedPath"));
Assert.Equal(Utilities.FixFilePath(@"/packages/SourcePackage2/"), task.MappedSourceRoots[1].ItemSpec);
Assert.Equal(@"/_2/", task.MappedSourceRoots[1].GetMetadata("MappedPath"));
Assert.Equal(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.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(@"!@#:;$%^&*()_+|{}\"), task.MappedSourceRoots[0].ItemSpec);
Assert.Equal(@"/_1/", task.MappedSourceRoots[0].GetMetadata("MappedPath"));
Assert.Equal(Utilities.FixFilePath("****/"), task.MappedSourceRoots[1].ItemSpec);
Assert.Equal(@"/_/", task.MappedSourceRoots[1].GetMetadata("MappedPath"));
Assert.Equal(@"Git", task.MappedSourceRoots[1].GetMetadata("SourceControl"));
Assert.Equal(Utilities.FixFilePath(@"****\|||:;\"), 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(@"c:\MyProjects\MyProject\"),
new TaskItem(@"c:\MyProjects\MyProject\a\a\", new Dictionary<string, string>
{
{ "NestedRoot", @"a/a/" },
{ "ContainingRoot", @"c:\MyProjects\MyProject\" },
}),
new TaskItem(@"c:\MyProjects\MyProject\a\b\", new Dictionary<string, string>
{
{ "NestedRoot", @"a/b\" },
{ "ContainingRoot", @"c:\MyProjects\MyProject\" },
}),
new TaskItem(@"c:\MyProjects\MyProject\a\c\", new Dictionary<string, string>
{
{ "NestedRoot", @"a\c" },
{ "ContainingRoot", @"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.FixFilePath(@"c:\MyProjects\MyProject\"), task.MappedSourceRoots[0].ItemSpec);
Assert.Equal(@"/_/", task.MappedSourceRoots[0].GetMetadata("MappedPath"));
Assert.Equal(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\a\"), task.MappedSourceRoots[1].ItemSpec);
Assert.Equal(@"/_/a/a/", task.MappedSourceRoots[1].GetMetadata("MappedPath"));
Assert.Equal(Utilities.FixFilePath(@"c:\MyProjects\MyProject\a\b\"), task.MappedSourceRoots[2].ItemSpec);
Assert.Equal(@"/_/a/b/", task.MappedSourceRoots[2].GetMetadata("MappedPath"));
Assert.Equal(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(@"c:\packages\SourcePackage1\"),
new TaskItem(@"C:\packages\SourcePackage1\"),
new TaskItem(@"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.FixFilePath(@"c:\packages\SourcePackage1\"), task.MappedSourceRoots[0].ItemSpec);
Assert.Equal(@"/_/", task.MappedSourceRoots[0].GetMetadata("MappedPath"));
Assert.Equal(Utilities.FixFilePath(@"C:\packages\SourcePackage1\"), task.MappedSourceRoots[1].ItemSpec);
Assert.Equal(@"/_1/", task.MappedSourceRoots[1].GetMetadata("MappedPath"));
Assert.Equal(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", path2)) + Environment.NewLine +
"ERROR : " + string.Format(task.Log.FormatResourceString(
"MapSourceRoots.NoSuchTopLevelSourceRoot", "SourceRoot.ContainingRoot", "SourceRoot", 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", path1, "SourceControl", "git", "tfvc")) + Environment.NewLine +
"WARNING : " + string.Format(task.Log.FormatResourceString(
"MapSourceRoots.ContainsDuplicate", "SourceRoot", path1, "RevisionId", "RevId1", "RevId2")) + Environment.NewLine +
"WARNING : " + string.Format(task.Log.FormatResourceString(
"MapSourceRoots.ContainsDuplicate", "SourceRoot", path1, "NestedRoot", "NR1A", "NR1B")) + Environment.NewLine +
"WARNING : " + string.Format(task.Log.FormatResourceString(
"MapSourceRoots.ContainsDuplicate", "SourceRoot", path1, "ContainingRoot", path3, "CR")) + Environment.NewLine +
"WARNING : " + string.Format(task.Log.FormatResourceString(
"MapSourceRoots.ContainsDuplicate", "SourceRoot", path1, "MappedPath", "MP1", "MP2")) + Environment.NewLine +
"WARNING : " + string.Format(task.Log.FormatResourceString(
"MapSourceRoots.ContainsDuplicate", "SourceRoot", path1, "SourceLinkUrl", "URL1", "URL2")) + Environment.NewLine,
engine.Log);
AssertEx.NotNull(task.MappedSourceRoots);
AssertEx.Equal(new[]
{
$"'{path1}' SourceControl='git' RevisionId='RevId1' NestedRoot='NR1A' ContainingRoot='{path3}' MappedPath='{(deterministic ? "/_/NR1A/" : path1)}' SourceLinkUrl='URL1'",
$"'{path2}' SourceControl='git' RevisionId='' NestedRoot='NR2' ContainingRoot='{path3}' MappedPath='{(deterministic ? "/_/NR2/" : path2)}' SourceLinkUrl=''",
$"'{path3}' SourceControl='' RevisionId='' NestedRoot='' ContainingRoot='' MappedPath='{(deterministic ? "/_/" : path3)}' SourceLinkUrl=''",
}, 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(@"c:\MyProjects\MYPROJECT\"),
new TaskItem(@"c:\MyProjects\MyProject\a\b\", new Dictionary<string, string>
{
{ "SourceControl", "Git" },
{ "NestedRoot", "a/b" },
{ "ContainingRoot", @"c:\MyProjects\MyProject\" },
}),
},
Deterministic = true
};
bool result = task.Execute();
AssertEx.AssertEqualToleratingWhitespaceDifferences("ERROR : " + string.Format(task.Log.FormatResourceString(
"MapSourceRoots.NoSuchTopLevelSourceRoot", "SourceRoot.ContainingRoot", "SourceRoot", @"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
{
AssertEx.NotNull(task.MappedSourceRoots);
AssertEx.Equal(new[]
{
$"'{path1}' SourceControl='' RevisionId='' NestedRoot='a/b' ContainingRoot='{path1}' MappedPath='{path1}' SourceLinkUrl=''",
}, task.MappedSourceRoots.Select(InspectSourceRoot));
Assert.True(result);
}
}
}
}
|