-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Expand file tree
/
Copy pathBuiltInMetadataExpander_Tests.cs
More file actions
131 lines (116 loc) · 5.37 KB
/
Copy pathBuiltInMetadataExpander_Tests.cs
File metadata and controls
131 lines (116 loc) · 5.37 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.IO;
using Microsoft.Build.Framework;
using Shouldly;
using Xunit;
#nullable enable
namespace Microsoft.Build.UnitTests
{
/// <summary>
/// Tests for <see cref="BuiltInMetadataExpander"/>, which expands references such as <c>%(Filename)</c> when
/// an item is read after it crossed a process boundary.
/// </summary>
public class BuiltInMetadataExpander_Tests
{
private static readonly string s_sep = Path.DirectorySeparatorChar.ToString();
private static readonly string s_itemSpec = $"folder{Path.DirectorySeparatorChar}hello.txt";
private static string? Expand(string? value, string? recursiveDir = null, string? itemSpec = null)
{
ItemSpecModifiers.Cache cache = default;
return BuiltInMetadataExpander.Expand(
value,
itemSpec ?? s_itemSpec,
escapedDefiningProject: "project.proj",
escapedRecursiveDir: recursiveDir,
ref cache);
}
[Theory]
[InlineData("%(Filename)", "hello")]
[InlineData("%(Extension)", ".txt")]
[InlineData("%(Identity)", @"folder\hello.txt")]
[InlineData("a%(Filename)b", "ahellob")]
[InlineData("%(Filename)%(Extension)", "hello.txt")]
[InlineData("%(Filename)%(Filename)", "hellohello")]
[InlineData("%(Filename)trailing", "hellotrailing")]
[InlineData("leading%(Filename)", "leadinghello")]
public void ExpandsBuiltInMetadata(string value, string expected)
=> Expand(value).ShouldBe(expected.Replace(@"\", s_sep));
[Theory]
[InlineData("%( Filename )", "hello")]
[InlineData("%( Filename )", "hello")]
[InlineData("%(FILENAME)", "hello")]
[InlineData("%(filename)", "hello")]
public void AcceptsWhitespaceAndAnyCasing(string value, string expected)
=> Expand(value).ShouldBe(expected);
[Theory]
// No reference at all.
[InlineData("")]
[InlineData("plain text")]
[InlineData("100% done")]
// Not a well formed reference.
[InlineData("%(Filename")]
[InlineData("a%(")]
[InlineData("%()")]
[InlineData("%( )")]
[InlineData("%((Filename)")]
[InlineData("%(Fi lename)")]
// A name that is not built-in metadata. Evaluation expands custom metadata, so a value that reaches
// this point with one left in it is text.
[InlineData("%(NotAModifier)")]
public void LeavesEverythingElseAsItIs(string value)
=> Expand(value).ShouldBe(value);
[Fact]
public void ReturnsNullForNull()
=> Expand(null).ShouldBeNull();
/// <summary>
/// A value with nothing to expand must come back as the same instance, not a copy. Metadata is read on
/// hot paths, and most values have no reference in them.
/// </summary>
[Fact]
public void DoesNotAllocateWhenThereIsNothingToExpand()
{
string value = "no reference here";
Expand(value).ShouldBeSameAs(value);
Expand("%(NotAModifier)").ShouldBeSameAs("%(NotAModifier)");
}
/// <summary>
/// RecursiveDir comes from the wildcard the item was expanded from, so it is supplied rather than derived.
/// </summary>
[Theory]
[InlineData(@"sub1\sub2\", @"out\sub1\sub2\hello.txt")]
[InlineData("", @"out\hello.txt")]
[InlineData(null, @"out\hello.txt")]
public void UsesTheSuppliedRecursiveDir(string? recursiveDir, string expected)
=> Expand(@"out\%(RecursiveDir)%(Filename)%(Extension)".Replace(@"\", s_sep), recursiveDir?.Replace(@"\", s_sep))
.ShouldBe(expected.Replace(@"\", s_sep));
/// <summary>
/// The expander derives from the item spec it is given, so the same value gives a different result for a
/// different item. This is why metadata is expanded on each read instead of one time.
/// </summary>
[Fact]
public void DerivesFromTheGivenItemSpec()
{
Expand("%(Filename)", itemSpec: $"other{s_sep}renamed.md").ShouldBe("renamed");
Expand("%(Extension)", itemSpec: $"other{s_sep}renamed.md").ShouldBe(".md");
}
/// <summary>
/// A reference that is not the first thing in the value must still be found after an earlier reference
/// failed to parse.
/// </summary>
[Theory]
[InlineData("%(NotAModifier)%(Filename)", "%(NotAModifier)hello")]
[InlineData("%(Filename)%(NotAModifier)", "hello%(NotAModifier)")]
[InlineData("%(Fi lename)%(Filename)", "%(Fi lename)hello")]
public void FindsLaterReferencesAfterOneThatDoesNotResolve(string value, string expected)
=> Expand(value).ShouldBe(expected);
/// <summary>
/// An unterminated reference must not hide a well formed one that follows it inside the same text.
/// </summary>
[Theory]
[InlineData("%(foo%(Filename)", "%(foohello")]
[InlineData("%(%(Filename)", "%(hello")]
public void FindsAReferenceNestedInsideOneThatDoesNotResolve(string value, string expected)
=> Expand(value).ShouldBe(expected);
}
}