Skip to content

Commit

Permalink
Use new Verifier, Add verification options
Browse files Browse the repository at this point in the history
  • Loading branch information
JanKrivanek committed Sep 22, 2022
1 parent 8d398fe commit 48320b7
Show file tree
Hide file tree
Showing 9 changed files with 253 additions and 121 deletions.
2 changes: 1 addition & 1 deletion eng/dependabot/Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="17.2.0" />
<PackageReference Update="xunit.abstractions" Version="2.0.3" />
<PackageReference Update="Newtonsoft.Json.Schema" Version="3.0.14" />
<PackageReference Update="Verify.XUnit" Version="17.2.1" />
<PackageReference Update="Verify.XUnit" Version="18.0.0-beta.18" />
<PackageReference Update="Verify.DiffPlex" Version="1.3.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System.CommandLine;
using System.CommandLine.Binding;
using System.CommandLine.Parsing;
using Microsoft.DotNet.Cli.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.TemplateEngine.Authoring.TemplateVerifier;
Expand Down Expand Up @@ -89,6 +90,14 @@ internal class VerifyCommand : ExecutableCommand<VerifyCommandArgs>
Description = "If set to true - 'dotnet new' command is expected to return nonzero return code.",
};

private readonly Option<IEnumerable<string>> _uniqueForOption = new("--unique-for")
{
//TODO: localize
Description = "Sets the Verifier expectations directory naming convention - by indicating which scenarios should be differentiated.",
Arity = new ArgumentArity(0, 999),
AllowMultipleArgumentsPerToken = true,
};

public VerifyCommand(ILoggerFactory loggerFactory)
: base(CommandName, "Runs the template with specified arguments and compares the result with expectations files (or creates those if yet don't exist).", loggerFactory)
{
Expand All @@ -103,6 +112,12 @@ public VerifyCommand(ILoggerFactory loggerFactory)
AddOption(_excludePatternOption);
AddOption(_verifyCommandOutputOption);
AddOption(_isCommandExpectedToFailOption);
FromAmongCaseInsensitive(
_uniqueForOption,
System.Enum.GetNames(typeof(UniqueForOption))
.Where(v => !v.Equals(UniqueForOption.None.ToString(), StringComparison.OrdinalIgnoreCase))
.ToArray());
AddOption(_uniqueForOption);
}

protected override async Task<int> ExecuteAsync(VerifyCommandArgs args, CancellationToken cancellationToken = default)
Expand All @@ -124,7 +139,8 @@ protected override async Task<int> ExecuteAsync(VerifyCommandArgs args, Cancella
ExpectationsDirectory = args.ExpectationsDirectory,
OutputDirectory = args.OutputDirectory,
VerifyCommandOutput = args.VerifyCommandOutput,
IsCommandExpectedToFail = args.IsCommandExpectedToFail
IsCommandExpectedToFail = args.IsCommandExpectedToFail,
UniqueFor = args.UniqueFor,
},
Logger
);
Expand All @@ -142,6 +158,29 @@ protected override async Task<int> ExecuteAsync(VerifyCommandArgs args, Cancella

protected override BinderBase<VerifyCommandArgs> GetModelBinder() => new VerifyModelBinder(this);

/// <summary>
/// Case insensitive version for <see cref="System.CommandLine.OptionExtensions.FromAmong{TOption}(TOption, string[])"/>.
/// </summary>
private static void FromAmongCaseInsensitive(Option<IEnumerable<string>> option, string[]? allowedValues = null, string? allowedHiddenValue = null)
{
allowedValues ??= Array.Empty<string>();
option.AddValidator(optionResult => ValidateAllowedValues(optionResult, allowedValues, allowedHiddenValue));
option.AddCompletions(allowedValues);
}

private static void ValidateAllowedValues(OptionResult optionResult, string[] allowedValues, string? allowedHiddenValue = null)
{
var invalidArguments = optionResult.Tokens.Where(token => !allowedValues.Append(allowedHiddenValue).Contains(token.Value, StringComparer.OrdinalIgnoreCase)).ToList();
if (invalidArguments.Any())
{
//TODO: localize
optionResult.ErrorMessage = string.Format(
"Argument(s) {0} are not recognized. Must be one of: {1}.",
string.Join(", ", invalidArguments.Select(arg => $"'{arg.Value}'")),
string.Join(", ", allowedValues.Select(allowedValue => $"'{allowedValue}'")));
}
}

private class VerifyModelBinder : BinderBase<VerifyCommandArgs>
{
private readonly VerifyCommand _verifyCommand;
Expand All @@ -164,7 +203,8 @@ protected override VerifyCommandArgs GetBoundValue(BindingContext bindingContext
disableDefaultVerificationExcludePatterns: bindingContext.ParseResult.GetValueForOption(_verifyCommand._disableDefaultExcludePatternsOption),
verificationExcludePatterns: bindingContext.ParseResult.GetValueForOption(_verifyCommand._excludePatternOption),
verifyCommandOutput: bindingContext.ParseResult.GetValueForOption(_verifyCommand._verifyCommandOutputOption),
isCommandExpectedToFail: bindingContext.ParseResult.GetValueForOption(_verifyCommand._isCommandExpectedToFailOption));
isCommandExpectedToFail: bindingContext.ParseResult.GetValueForOption(_verifyCommand._isCommandExpectedToFailOption),
uniqueForOptions: bindingContext.ParseResult.GetValueForOption(_verifyCommand._uniqueForOption));
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// The .NET Foundation licenses this file to you under the MIT license.

using System.Text.RegularExpressions;
using Microsoft.TemplateEngine.Authoring.TemplateVerifier;

namespace Microsoft.TemplateEngine.Authoring.CLI.Commands.Verify
{
Expand All @@ -21,7 +22,8 @@ public VerifyCommandArgs(
bool? disableDefaultVerificationExcludePatterns,
IEnumerable<string>? verificationExcludePatterns,
bool? verifyCommandOutput,
bool isCommandExpectedToFail)
bool isCommandExpectedToFail,
IEnumerable<string>? uniqueForOptions)
{
TemplateName = templateName;
TemplatePath = templatePath;
Expand All @@ -34,6 +36,7 @@ public VerifyCommandArgs(
VerificationExcludePatterns = verificationExcludePatterns;
VerifyCommandOutput = verifyCommandOutput;
IsCommandExpectedToFail = isCommandExpectedToFail;
UniqueFor = FromOptionTokens(uniqueForOptions);
}

/// <summary>
Expand Down Expand Up @@ -93,6 +96,11 @@ public VerifyCommandArgs(
/// </summary>
public bool IsCommandExpectedToFail { get; init; }

/// <summary>
/// Gets the Verifier expectations directory naming convention - by indicating which scenarios should be differentiated.
/// </summary>
public UniqueForOption? UniqueFor { get; init; }

public static IEnumerable<string> TokenizeJoinedArgs(string? joinedArgs)
{
if (string.IsNullOrEmpty(joinedArgs))
Expand Down Expand Up @@ -128,5 +136,22 @@ private static string RemoveEnclosingQuotation(string input)
return input;
}
}

private static UniqueForOption? FromOptionTokens(IEnumerable<string>? tokens)
{
if (tokens == null)
{
return null;
}

UniqueForOption result = UniqueForOption.None;

foreach (string token in tokens)
{
result |= Enum.Parse<UniqueForOption>(token, true);
}

return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<PackageReference Include="Microsoft.Extensions.Logging" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" />
<PackageReference Include="System.CommandLine" />
<PackageReference Include="Verify.Xunit" Version="17.1.4" />
<PackageReference Include="Verify.Xunit" Version="18.0.0-beta.18" />
<PackageReference Include="Verify.DiffPlex" Version="1.3.0" />
</ItemGroup>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,34 @@

using System.Text;
using Microsoft.Extensions.Options;
using Microsoft.TemplateEngine.Utils;

namespace Microsoft.TemplateEngine.Authoring.TemplateVerifier
{
public delegate void ScrubTemplateFileContent(string filename, StringBuilder content);

public delegate Task VerifyFileContent(string filename, AsyncLazy<string> content, Task defaultVarifierAction);
public class ScrubbersDefinition
{
public ScrubbersDefinition() { }

public ScrubbersDefinition(Action<StringBuilder> scrubber, string? extension = null)
{
this.AddScrubber(scrubber, extension);
}

public Dictionary<string, Action<StringBuilder>> ScrubersByExtension { get; private set; } = new Dictionary<string, Action<StringBuilder>>();

public Action<StringBuilder>? GeneralScrubber { get; private set; }

public void AddScrubber(Action<StringBuilder> scrubber, string? extension = null)
{
if (extension == null)
{
GeneralScrubber += scrubber;
}
else
{
ScrubersByExtension[extension] = scrubber;
}
}
}

public class TemplateVerifierOptions : IOptions<TemplateVerifierOptions>
{
Expand Down Expand Up @@ -71,14 +92,19 @@ public class TemplateVerifierOptions : IOptions<TemplateVerifierOptions>
public string? OutputDirectory { get; init; }

/// <summary>
/// Gets the delegate that performs custom scrubbing of template output contents before verifications.
/// Gets the Verifier expectations directory naming convention - by indicating which scenarios should be differentiated.
/// </summary>
public UniqueForOption? UniqueFor { get; init; }

/// <summary>
/// Gets the delegates that perform custom scrubbing of template output contents before verifications.
/// </summary>
public ScrubTemplateFileContent? CustomScrubber { get; init; }
public ScrubbersDefinition? CustomScrubbers { get; init; }

/// <summary>
/// Gets the delegate that performs custom verification of template output contents.
/// </summary>
public VerifyFileContent? CustomVerifier { get; init; }
public Func<string, Task>? CustomVerifyDirectory { get; init; }

TemplateVerifierOptions IOptions<TemplateVerifierOptions>.Value => this;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.TemplateEngine.Authoring.TemplateVerifier
{
[Flags]
public enum UniqueForOption
{
None = 0,
Architecture = 1,
OsPlatform = 2,
Runtime = 4,
RuntimeAndVersion = 8,
TargetFramework = 16,
TargetFrameworkAndVersion = 32
}
}
Loading

0 comments on commit 48320b7

Please sign in to comment.