-
Notifications
You must be signed in to change notification settings - Fork 399
Validate C# DefineConstants input #9612
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
b3df917
2548986
d955d17
f96abbe
298c98f
2330881
4aa87f4
259125b
5588830
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// 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.md file in the project root for more information. | ||
|
||
using Microsoft.Build.Construction; | ||
using Microsoft.VisualStudio.ProjectSystem.Properties; | ||
|
||
namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; | ||
|
||
public class DefineConstantsCAndFSharpValueProviderTests | ||
{ | ||
private const string PropertyName = "DefineConstants"; | ||
|
||
[Theory] | ||
[InlineData("DEBUG;TRACE", "DEBUG=False,TRACE=False")] | ||
[InlineData("", "")] | ||
public async Task GetExistingUnevaluatedValue(string? defineConstantsValue, string expectedFormattedValue) | ||
{ | ||
var provider = CreateInstance(defineConstantsValue, out _, out _); | ||
|
||
var actualPropertyValue = await provider.OnGetUnevaluatedPropertyValueAsync(string.Empty, string.Empty, null!); | ||
Assert.Equal(expectedFormattedValue, actualPropertyValue); | ||
} | ||
|
||
[Theory] | ||
[InlineData("DEBUG,TRACE", null, "DEBUG;TRACE", "DEBUG=False,TRACE=False")] | ||
[InlineData("$(DefineConstants),DEBUG,TRACE", "PROP1;PROP2", "$(DefineConstants);DEBUG;TRACE", "$(DefineConstants)=False,DEBUG=False,TRACE=False")] | ||
public async Task SetUnevaluatedValue(string unevaluatedValueToSet, string? defineConstantsValue, string? expectedSetUnevaluatedValue, string expectedFormattedValue) | ||
{ | ||
var provider = CreateInstance(null, out var projectAccessor, out var project); | ||
Mock<IProjectProperties> mockProjectProperties = new Mock<IProjectProperties>(); | ||
mockProjectProperties | ||
.Setup(p => p.GetUnevaluatedPropertyValueAsync(ConfiguredBrowseObject.DefineConstantsProperty)) | ||
.ReturnsAsync(defineConstantsValue); | ||
|
||
var setPropertyValue = await provider.OnSetPropertyValueAsync(PropertyName, unevaluatedValueToSet, mockProjectProperties.Object); | ||
Assert.Equal(expectedSetUnevaluatedValue, setPropertyValue); | ||
|
||
await SetDefineConstantsPropertyAsync(projectAccessor, project, setPropertyValue); | ||
|
||
var actualPropertyFormattedValue = await provider.OnGetUnevaluatedPropertyValueAsync(string.Empty, string.Empty, null!); | ||
Assert.Equal(expectedFormattedValue, actualPropertyFormattedValue); | ||
} | ||
|
||
private static DefineConstantsCAndFSharpValueProvider CreateInstance(string? defineConstantsValue, out IProjectAccessor projectAccessor, out ConfiguredProject project) | ||
{ | ||
var projectXml = defineConstantsValue is not null | ||
? $""" | ||
<Project> | ||
<PropertyGroup> | ||
<{ConfiguredBrowseObject.DefineConstantsProperty}>{defineConstantsValue}</{ConfiguredBrowseObject.DefineConstantsProperty}> | ||
</PropertyGroup> | ||
</Project> | ||
""" | ||
: "<Project></Project>"; | ||
|
||
projectAccessor = IProjectAccessorFactory.Create(ProjectRootElementFactory.Create(projectXml)); | ||
project = ConfiguredProjectFactory.Create(); | ||
|
||
return new DefineConstantsCAndFSharpValueProvider(projectAccessor, project); | ||
} | ||
|
||
private static async Task SetDefineConstantsPropertyAsync(IProjectAccessor projectAccessor, ConfiguredProject project, string? setPropertyValue) | ||
{ | ||
await projectAccessor.OpenProjectXmlForWriteAsync(project.UnconfiguredProject, projectXml => | ||
{ | ||
projectXml.AddProperty(PropertyName, setPropertyValue); | ||
}); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
// 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.md file in the project root for more information. | ||
|
||
using Microsoft.VisualStudio.ProjectSystem.Debug; | ||
|
||
namespace Microsoft.VisualStudio.ProjectSystem.VS.Properties; | ||
|
||
public class KeyValuePairListEncodingTests | ||
{ | ||
[Theory] | ||
[InlineData("key1=value1;key2=value2", new[] { "key1", "value1", "key2", "value2" })] | ||
[InlineData("key1=value1;;key2=value2", new[] { "key1", "value1", "key2", "value2" })] | ||
[InlineData("key1=value1;;;key2=value2", new[] { "key1", "value1", "key2", "value2" })] | ||
[InlineData("key1=value1;key2=value2;key3=value3", new[] { "key1", "value1", "key2", "value2", "key3", "value3" })] | ||
[InlineData("key1;key2=value2", new[] { "key1", "", "key2", "value2" })] | ||
[InlineData("key1;key2;key3=value3", new[] { "key1", "", "key2", "", "key3", "value3" })] | ||
[InlineData("key1;;;key3;;", new[] { "key1", "", "key3", "" })] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would be good to cover some more inputs:
If there are invalid inputs, a test that ensures that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I added test cases on those inputs except null, as the method accepts a non-nullable input string. But I wouldn't necessarily agree that there are invalid inputs possible here, since There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for adding more tests.
I'm less confident about that than you. I feel like There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm not entirely in agreement; the difference between = and the other delimiters is that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I mean that it's ambiguous whether the name or value is What happens if the For robustness, we should either throw on invalid inputs, or implement escaping so that these values are handled correctly. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if you enter strings in the UI containing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good points. I switched back to escaping the = |
||
[InlineData("", new string[0])] | ||
[InlineData(" ", new string[0])] | ||
[InlineData("=", new string[0])] | ||
[InlineData("/=", new[] { "=", "" })] | ||
[InlineData("key1=value1;/=value2=", new[] { "key1", "value1", "=value2", "" })] | ||
[InlineData("key1=value1;=value2", new[] { "key1", "value1" })] | ||
[InlineData("==", new string[0])] | ||
[InlineData("=/=", new string[0])] | ||
[InlineData("/==", new[] { "=", "" })] | ||
[InlineData(";", new string[0])] | ||
public void Parse_ValidInput_ReturnsExpectedPairs(string input, string[] expectedPairs) | ||
{ | ||
var result = KeyValuePairListEncoding.Parse(input, ';').SelectMany(pair => new[] { pair.Name, pair.Value }).ToArray(); | ||
Assert.Equal(expectedPairs, result); | ||
} | ||
|
||
[Theory] | ||
[InlineData(new[] { "key1", "value1", "key2", "value2" }, "key1=value1;key2=value2")] | ||
[InlineData(new[] { "key1", "value1", "key2", "value2", "key3", "value3" }, "key1=value1;key2=value2;key3=value3")] | ||
[InlineData(new[] { "key1", "", "key2", "value2" }, "key1;key2=value2")] | ||
[InlineData(new[] { "key1=", "", "key2=", "value2=" }, "key1/=;key2/==value2/=")] | ||
[InlineData(new[] { "key1", "", "key2", "", "key3", "value3" }, "key1;key2;key3=value3")] | ||
adamint marked this conversation as resolved.
Show resolved
Hide resolved
|
||
[InlineData(new string[0], "")] | ||
public void Format_ValidPairs_ReturnsExpectedString(string[] pairs, string expectedString) | ||
{ | ||
var nameValuePairs = ToNameValues(pairs); | ||
var result = KeyValuePairListEncoding.Format(nameValuePairs, ';'); | ||
Assert.Equal(expectedString, result); | ||
return; | ||
|
||
static IEnumerable<(string Name, string Value)> ToNameValues(IEnumerable<string> pairs) | ||
{ | ||
using var e = pairs.GetEnumerator(); | ||
while (e.MoveNext()) | ||
{ | ||
var name = e.Current; | ||
Assert.True(e.MoveNext()); | ||
var value = e.Current; | ||
yield return (name, value); | ||
} | ||
} | ||
} | ||
} |
Uh oh!
There was an error while loading. Please reload this page.