Skip to content

Commit

Permalink
Merge pull request #575 from jnm2/test_parameter_semicolons
Browse files Browse the repository at this point in the history
Add --testparam to enable test parameters to contain semicolons
  • Loading branch information
rprouse authored Mar 24, 2019
2 parents b0408ff + 3526ffe commit 28bde31
Show file tree
Hide file tree
Showing 3 changed files with 186 additions and 50 deletions.
142 changes: 132 additions & 10 deletions src/NUnitConsole/nunit3-console.tests/CommandLineTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -709,7 +709,7 @@ public void ShouldNotFailOnEmptyLine()
// Not copying this test file into releases
Assume.That(testListPath, Does.Exist);
var options = new ConsoleOptions("--testlist=" + testListPath);
Assert.That(options.errorMessages, Is.Empty);
Assert.That(options.ErrorMessages, Is.Empty);
Assert.That(options.TestList, Is.EqualTo(new[] {"AmazingTest"}));
}

Expand All @@ -718,44 +718,166 @@ public void ShouldNotFailOnEmptyLine()
#region Test Parameters

[Test]
public void SingleTestParameter()
public void SingleDeprecatedTestParameter()
{
var options = new ConsoleOptions("--params=X=5");
Assert.That(options.errorMessages, Is.Empty);
Assert.That(options.ErrorMessages, Is.Empty);
Assert.That(options.WarningMessages, Has.One.Contains("deprecated").IgnoreCase);
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { { "X", "5" } }));
}

[Test]
public void TwoTestParametersInOneOption()
public void TwoDeprecatedTestParametersInOneOption()
{
var options = new ConsoleOptions("--params:X=5;Y=7");
Assert.That(options.errorMessages, Is.Empty);
Assert.That(options.ErrorMessages, Is.Empty);
Assert.That(options.WarningMessages, Has.One.Contains("deprecated").IgnoreCase);
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { { "X", "5" }, { "Y", "7" } }));
}

[Test]
public void TwoTestParametersInSeparateOptions()
public void TwoDeprecatedTestParametersInSeparateOptions()
{
var options = new ConsoleOptions("-p:X=5", "-p:Y=7");
Assert.That(options.errorMessages, Is.Empty);
Assert.That(options.ErrorMessages, Is.Empty);
Assert.That(options.WarningMessages, Has.One.Contains("deprecated").IgnoreCase);
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { { "X", "5" }, { "Y", "7" } }));
}

[Test]
public void ThreeTestParametersInTwoOptions()
public void ThreeDeprecatedTestParametersInTwoOptions()
{
var options = new ConsoleOptions("--params:X=5;Y=7", "-p:Z=3");
Assert.That(options.errorMessages, Is.Empty);
Assert.That(options.ErrorMessages, Is.Empty);
Assert.That(options.WarningMessages, Has.One.Contains("deprecated").IgnoreCase);
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { { "X", "5" }, { "Y", "7" }, { "Z", "3" } }));
}

[Test]
public void ParameterWithoutEqualSignIsInvalid()
public void DeprecatedParameterWithoutEqualSignIsInvalid()
{
var options = new ConsoleOptions("--params=X5");
Assert.That(options.WarningMessages, Has.One.Contains("deprecated").IgnoreCase);
Assert.That(options.ErrorMessages.Count, Is.EqualTo(1));
}

[Test]
public void SingleTestParameter()
{
var options = new ConsoleOptions("--testparam=X=5");
Assert.That(options.ErrorMessages, Is.Empty);
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { { "X", "5" } }));
}

[Test]
public void SemicolonsDoNotSplitTestParameters()
{
var options = new ConsoleOptions("--testparam:X=5;Y=7");
Assert.That(options.ErrorMessages, Is.Empty);
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { { "X", "5;Y=7" } }));
}

[Test]
public void TwoTestParametersInSeparateOptions()
{
var options = new ConsoleOptions("--testparam:X=5", "--testparam:Y=7");
Assert.That(options.ErrorMessages, Is.Empty);
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { { "X", "5" }, { "Y", "7" } }));
}

[Test]
public void ParameterWithoutEqualSignIsInvalid()
{
var options = new ConsoleOptions("--testparam=X5");
Assert.That(options.ErrorMessages.Count, Is.EqualTo(1));
}

[Test]
public void ParameterWithMissingNameIsInvalid()
{
var options = new ConsoleOptions("--testparam:=5");
Assert.That(options.ErrorMessages.Count, Is.EqualTo(1));
}

[Test]
public void ParameterWithMissingValueIsInvalid()
{
var options = new ConsoleOptions("--testparam:X=");
Assert.That(options.ErrorMessages.Count, Is.EqualTo(1));
}

[Test]
public void LeadingWhitespaceIsPreservedInParameterName()
{
// Command line examples to get in this scenario:
// --testparams:" X"=5
// --testparams:" X=5"
// "--testparams: X=5"

var options = new ConsoleOptions("--testparam: X=5");
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { [" X"] = "5" }));
}

[Test]
public void TrailingWhitespaceIsPreservedInParameterName()
{
// Command line examples to get in this scenario:
// --testparams:"X "=5
// --testparams:"X =5"
// "--testparams:X =5"

var options = new ConsoleOptions("--testparam:X =5");
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { ["X "] = "5" }));
}

[Test]
public void WhitespaceIsPermittedAsParameterName()
{
// Command line examples to get in this scenario:
// --testparams:" "=5
// --testparams:" =5"
// "--testparams: =5"

var options = new ConsoleOptions("--testparam: =5");
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { [" "] = "5" }));
}

[Test]
public void LeadingWhitespaceIsPreservedInParameterValue()
{
// Command line examples to get in this scenario:
// --testparams:X=" 5"
// --testparams:"X= 5"
// "--testparams:X= 5"

var options = new ConsoleOptions("--testparam:X= 5");
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { ["X"] = " 5" }));
}

[Test]
public void TrailingWhitespaceIsPreservedInParameterValue()
{
// Command line examples to get in this scenario:
// --testparams:X="5 "
// --testparams:"X=5 "
// "--testparams:X=5 "

var options = new ConsoleOptions("--testparam:X=5 ");
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { ["X"] = "5 " }));
}

[Test]
public void WhitespaceIsPermittedAsParameterValue()
{
// Command line examples to get in this scenario:
// --testparams:X=" "
// --testparams:"X= "
// "--testparams:X= "

var options = new ConsoleOptions("--testparam:X= ");
Assert.That(options.TestParameters, Is.EqualTo(new Dictionary<string, string> { ["X"] = " " }));
}

[Test]
public void DisplayTestParameters()
{
Expand Down
86 changes: 46 additions & 40 deletions src/NUnitConsole/nunit3-console/CommandLineOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,31 +86,24 @@ public CommandLineOptions(params string[] args)

// Select tests

private List<string> inputFiles = new List<string>();
public IList<string> InputFiles { get { return inputFiles; } }
public IList<string> InputFiles { get; } = new List<string>();

private List<string> testList = new List<string>();
public IList<string> TestList { get { return testList; } }
public IList<string> TestList { get; } = new List<string>();

private Dictionary<string, string> testParameters = new Dictionary<string, string>();
public IDictionary<string, string> TestParameters { get { return testParameters; } }
public IDictionary<string, string> TestParameters { get; } = new Dictionary<string, string>();

public string WhereClause { get; private set; }
public bool WhereClauseSpecified { get { return WhereClause != null; } }

private int defaultTimeout = -1;
public int DefaultTimeout { get { return defaultTimeout; } }
public bool DefaultTimeoutSpecified { get { return defaultTimeout >= 0; } }
public int DefaultTimeout { get; private set; } = -1;
public bool DefaultTimeoutSpecified { get { return DefaultTimeout >= 0; } }

private int randomSeed = -1;
public int RandomSeed { get { return randomSeed; } }
public bool RandomSeedSpecified { get { return randomSeed >= 0; } }
public int RandomSeed { get; private set; } = -1;
public bool RandomSeedSpecified { get { return RandomSeed >= 0; } }

public string DefaultTestNamePattern { get; private set; }

private int numWorkers = -1;
public int NumberOfTestWorkers { get { return numWorkers; } }
public bool NumberOfTestWorkersSpecified { get { return numWorkers >= 0; } }
public int NumberOfTestWorkers { get; private set; } = -1;
public bool NumberOfTestWorkersSpecified { get { return NumberOfTestWorkers >= 0; } }

public bool StopOnError { get; private set; }

Expand Down Expand Up @@ -141,7 +134,7 @@ public string WorkDirectory
public string InternalTraceLevel { get; private set; }
public bool InternalTraceLevelSpecified { get { return InternalTraceLevel != null; } }

private List<OutputSpecification> resultOutputSpecifications = new List<OutputSpecification>();
private readonly List<OutputSpecification> resultOutputSpecifications = new List<OutputSpecification>();
public IList<OutputSpecification> ResultOutputSpecifications
{
get
Expand All @@ -157,17 +150,17 @@ public IList<OutputSpecification> ResultOutputSpecifications
}
}

private List<OutputSpecification> exploreOutputSpecifications = new List<OutputSpecification>();
public IList<OutputSpecification> ExploreOutputSpecifications { get { return exploreOutputSpecifications; } }
public IList<OutputSpecification> ExploreOutputSpecifications { get; } = new List<OutputSpecification>();

// Error Processing

public List<string> errorMessages = new List<string>();
public IList<string> ErrorMessages { get { return errorMessages; } }
public IList<string> WarningMessages { get; } = new List<string>();

#endregion
public IList<string> ErrorMessages { get; } = new List<string>();

#endregion

#region Public Methods
#region Public Methods

public bool Validate()
{
Expand Down Expand Up @@ -292,37 +285,33 @@ protected virtual void ConfigureOptions()
this.Add("where=", "Test selection {EXPRESSION} indicating what tests will be run. See description below.",
v => WhereClause = RequiredValue(v, "--where"));

this.Add("params|p=", "Define a test parameter.",
this.Add("params|p=", "Deprecated and will be removed in a future release. Please use --testparam instead.",
v =>
{
const string deprecationWarning = "--params is deprecated and will be removed in a future release. Please use --testparam instead.";
if (!WarningMessages.Contains(deprecationWarning))
WarningMessages.Add(deprecationWarning);
string parameters = RequiredValue( v, "--params");
// This can be changed without breaking backwards compatibility with frameworks.
foreach (string param in parameters.Split(new[] { ';' }))
{
int eq = param.IndexOf("=");
if (eq == -1 || eq == param.Length - 1)
{
ErrorMessages.Add("Invalid format for test parameter. Use NAME=VALUE.");
}
else
{
string name = param.Substring(0, eq);
string val = param.Substring(eq + 1);
TestParameters[name] = val;
}
ApplyTestParameter(param);
}
});

this.Add("testparam|tp=", "Followed by a key-value pair separated by an equals sign. Test code can access the value by name.",
v => ApplyTestParameter(RequiredValue(v, "--testparam")));

this.Add("timeout=", "Set timeout for each test case in {MILLISECONDS}.",
v => defaultTimeout = RequiredInt(v, "--timeout"));
v => DefaultTimeout = RequiredInt(v, "--timeout"));

this.Add("seed=", "Set the random {SEED} used to generate test cases.",
v => randomSeed = RequiredInt(v, "--seed"));
v => RandomSeed = RequiredInt(v, "--seed"));

this.Add("workers=", "Specify the {NUMBER} of worker threads to be used in running tests. If not specified, defaults to 2 or the number of processors, whichever is greater.",
v => numWorkers = RequiredInt(v, "--workers"));
v => NumberOfTestWorkers = RequiredInt(v, "--workers"));

this.Add("stoponerror", "Stop run immediately upon any test failure or error.",
v => StopOnError = v != null);
Expand Down Expand Up @@ -386,6 +375,23 @@ protected virtual void ConfigureOptions()
});
}

private void ApplyTestParameter(string testParameterSpecification)
{
var equalsIndex = testParameterSpecification.IndexOf("=");

if (equalsIndex <= 0 || equalsIndex == testParameterSpecification.Length - 1)
{
ErrorMessages.Add("Invalid format for test parameter. Use NAME=VALUE.");
}
else
{
string name = testParameterSpecification.Substring(0, equalsIndex);
string value = testParameterSpecification.Substring(equalsIndex + 1);

TestParameters[name] = value;
}
}

private void ResolveOutputSpecification(string value, IList<OutputSpecification> outputSpecifications)
{
if (value == null)
Expand Down
8 changes: 8 additions & 0 deletions src/NUnitConsole/nunit3-console/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ public static int Main(string[] args)
if (Options.ShowVersion)
return ConsoleRunner.OK;

if (Options.WarningMessages.Count != 0)
{
foreach (string message in Options.WarningMessages)
OutWriter.WriteLine(ColorStyle.Warning, message);

OutWriter.WriteLine();
}

if (!Options.Validate())
{
using (new ColorConsole(ColorStyle.Error))
Expand Down

0 comments on commit 28bde31

Please sign in to comment.