Skip to content

Commit

Permalink
Support escaping , in Test filter (#1374)
Browse files Browse the repository at this point in the history
* Support for Escaping test cases with comma
  • Loading branch information
Nitin Gurram authored Jan 17, 2018
1 parent 25fa1b6 commit 46f1cae
Show file tree
Hide file tree
Showing 4 changed files with 318 additions and 5 deletions.
47 changes: 47 additions & 0 deletions src/Microsoft.TestPlatform.Utilities/StringUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

namespace Microsoft.VisualStudio.TestPlatform.Utilities
{
using System.Collections.Generic;
using System.Text;

public static class StringExtensions
{
public static IEnumerable<string> Tokenize(this string input, char separator, char escape)
{
if (string.IsNullOrEmpty(input)) yield break;

var buffer = new StringBuilder();
var escaping = false;
foreach (var c in input)
{
if (escaping)
{
buffer.Append(c);
escaping = false;
}
else if (c == escape)
{
escaping = true;
}
else if (c == separator)
{
yield return buffer.Flush();
}
else
{
buffer.Append(c);
}
}
if (buffer.Length > 0 || input[input.Length - 1] == separator) yield return buffer.Flush();
}

private static string Flush(this StringBuilder stringBuilder)
{
var result = stringBuilder.ToString();
stringBuilder.Clear();
return result;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,13 @@ namespace Microsoft.VisualStudio.TestPlatform.CommandLine.Processors

using Microsoft.VisualStudio.TestPlatform.Client.RequestHelper;
using Microsoft.VisualStudio.TestPlatform.CommandLine.TestPlatformHelpers;
using Microsoft.VisualStudio.TestPlatform.CommandLine.Processors.Utilities;
using Microsoft.VisualStudio.TestPlatform.CommandLineUtilities;
using Microsoft.VisualStudio.TestPlatform.Common;
using Microsoft.VisualStudio.TestPlatform.Common.Interfaces;
using Microsoft.VisualStudio.TestPlatform.ObjectModel;
using Microsoft.VisualStudio.TestPlatform.ObjectModel.Client;
using Microsoft.VisualStudio.TestPlatform.Utilities;

using CommandLineResources = Microsoft.VisualStudio.TestPlatform.CommandLine.Resources.Resources;

internal class RunSpecificTestsArgumentProcessor : IArgumentProcessor
Expand Down Expand Up @@ -83,6 +83,9 @@ internal class RunSpecificTestsArgumentProcessorCapabilities : BaseArgumentProce

internal class RunSpecificTestsArgumentExecutor : IArgumentExecutor
{
public const char SplitDelimiter = ',';
public const char EscapeDelimiter = '\\';

#region Fields

/// <summary>
Expand Down Expand Up @@ -171,7 +174,10 @@ public void Initialize(string argument)
{
if (!string.IsNullOrWhiteSpace(argument))
{
this.selectedTestNames = new Collection<string>(argument.Split(new[] { CommandLineResources.SearchStringDelimiter }, StringSplitOptions.RemoveEmptyEntries));
this.selectedTestNames = new Collection<string>(
argument.Tokenize(SplitDelimiter, EscapeDelimiter)
.Where(x => !string.IsNullOrWhiteSpace(x))
.Select(s => s.Trim()).ToList());
}

if (this.selectedTestNames == null || this.selectedTestNames.Count <= 0)
Expand All @@ -194,7 +200,7 @@ public ArgumentProcessorResult Execute()
Contract.Assert(this.testRequestManager != null);
Contract.Assert(!string.IsNullOrWhiteSpace(this.runSettingsManager.ActiveRunSettings.SettingsXml));

if (this.commandLineOptions.Sources.Count() <= 0)
if (!this.commandLineOptions.Sources.Any())
{
throw new CommandLineException(string.Format(CultureInfo.CurrentUICulture, CommandLineResources.MissingTestSourceFile));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.Linq;

namespace Microsoft.TestPlatform.Utilities.UnitTests
{
using Castle.Core.Internal;
using Microsoft.VisualStudio.TestPlatform.Utilities;
using Microsoft.VisualStudio.TestTools.UnitTesting;

[TestClass]
public class StringUtilitiesTests
{
[TestMethod]
public void SplitShouldReturnWhenStringisNullOrEmpty()
{
var argsList = string.Empty.Tokenize(SplitChar, EscapeChar);

Assert.IsTrue(argsList.IsNullOrEmpty());
}

[TestMethod]
public void SplitShouldReturnWhenStringDoesntContainSplitChar()
{
var data = "foobar";
var argsList = data.Tokenize(SplitChar, EscapeChar);
var enumerable = argsList as string[] ?? argsList.ToArray();

Assert.IsTrue(enumerable.Length == 1);
Assert.IsTrue(enumerable.First().Equals(data));
}

[TestMethod]
public void SplitShouldSplitWhenStringContainsSplitChar()
{
var data = "foo,bar";
var argsList = data.Tokenize(SplitChar, EscapeChar);
var enumerable = argsList as string[] ?? argsList.ToArray();

Assert.IsTrue(enumerable.Length == 2);
}

[TestMethod]
public void SplitShouldSplitWhenStringWithSplitCharStartEnd()
{
var data = ",foo,bar,";
var argsList = data.Tokenize(SplitChar, EscapeChar);
var enumerable = argsList as string[] ?? argsList.ToArray();

Assert.IsTrue(enumerable.Length == 4);
}

[TestMethod]
public void SplitShouldEscapeSplitCharWhenEscapedCharPresent()
{
var data = "foo\\,bar";
var argsList = data.Tokenize(SplitChar, EscapeChar);
var enumerable = argsList as string[] ?? argsList.ToArray();

Assert.IsTrue(enumerable.Length == 1);
Assert.IsTrue(enumerable.First().Equals("foo,bar"));
}

[TestMethod]
public void SplitShouldEscapeSplitCharWhenEscapedNonEscapedCharPresent()
{
var data = "foo\\,,bar";
var argsList = data.Tokenize(SplitChar, EscapeChar);
var enumerable = argsList as string[] ?? argsList.ToArray();
Assert.IsTrue(enumerable.Length == 2);
Assert.IsTrue(enumerable.First().Equals("foo,"));
}

[TestMethod]
public void SplitShouldSplitWhenOnlySplitCharPresent()
{
var data = ",";
var argsList = data.Tokenize(SplitChar, EscapeChar);
var enumerable = argsList as string[] ?? argsList.ToArray();

Assert.IsTrue(enumerable.Length == 2);
}

[TestMethod]
public void SplitShouldNotSplitWhenNoSplitCharPresent()
{
var data = "foo\\bar";
var argsList = data.Tokenize(SplitChar, EscapeChar);
var enumerable = argsList as string[] ?? argsList.ToArray();

Assert.IsTrue(enumerable.Length == 1);
}

private const char SplitChar = ',';
private const char EscapeChar = '\\';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,12 +101,69 @@ public void CapabilitiesShouldReturnAppropriateProperties()

#region RunSpecificTestsArgumentExecutorTests

[TestMethod]
public void InitializeShouldThrowIfArgumentIsNull()
{
CommandLineOptions.Instance.Reset();

var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, new TestPlatform(), TestLoggerManager.Instance, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask);
var executor = GetExecutor(testRequestManager);

Assert.ThrowsException<CommandLineException>(() => { executor.Initialize(null); });
}

[TestMethod]
public void InitializeShouldThrowIfArgumentIsEmpty()
{
CommandLineOptions.Instance.Reset();

var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, new TestPlatform(), TestLoggerManager.Instance, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask);
var executor = GetExecutor(testRequestManager);

Assert.ThrowsException<CommandLineException>(() => { executor.Initialize(String.Empty); });
}

[TestMethod]
public void InitializeShouldThrowIfArgumentIsWhiteSpace()
{
CommandLineOptions.Instance.Reset();

var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, new TestPlatform(), TestLoggerManager.Instance, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask);
var executor = GetExecutor(testRequestManager);

Assert.ThrowsException<CommandLineException>(() => { executor.Initialize(" "); });
}

[TestMethod]
public void InitializeShouldThrowIfArgumentsAreEmpty()
{
CommandLineOptions.Instance.Reset();

var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, new TestPlatform(), TestLoggerManager.Instance, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask);
var executor = GetExecutor(testRequestManager);

Assert.ThrowsException<CommandLineException>(() => { executor.Initialize(" , "); });
}

[TestMethod]
public void ExecutorShouldSplitTestsSeparatedByComma()
{
CommandLineOptions.Instance.Reset();

var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, new TestPlatform(), TestLoggerManager.Instance, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask);
var executor = GetExecutor(testRequestManager);

Assert.ThrowsException<CommandLineException>(() => executor.Execute());
}

[TestMethod]
public void ExecutorExecuteForNoSourcesShouldThrowCommandLineException()
{
CommandLineOptions.Instance.Reset();

var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, new TestPlatform(), TestLoggerManager.Instance, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask);
var executor = GetExecutor(testRequestManager);

Assert.ThrowsException<CommandLineException>(() => executor.Execute());
}

Expand Down Expand Up @@ -260,7 +317,6 @@ public void ExecutorExecuteShouldCatchInvalidOperationExceptionThrownDuringExecu
public void ExecutorExecuteShouldForValidSourcesAndNoTestsDiscoveredShouldLogWarningAndReturnSuccess()
{
var mockTestPlatform = new Mock<ITestPlatform>();
var mockTestRunRequest = new Mock<ITestRunRequest>();
var mockDiscoveryRequest = new Mock<IDiscoveryRequest>();

this.ResetAndAddSourceToCommandLineOptions();
Expand All @@ -285,7 +341,6 @@ public void ExecutorExecuteShouldForValidSourcesAndNoTestsDiscoveredShouldLogWar
public void ExecutorExecuteShouldForValidSourcesAndNoTestsDiscoveredShouldLogAppropriateWarningIfTestAdapterPathIsNotSetAndReturnSuccess()
{
var mockTestPlatform = new Mock<ITestPlatform>();
var mockTestRunRequest = new Mock<ITestRunRequest>();
var mockDiscoveryRequest = new Mock<IDiscoveryRequest>();

this.ResetAndAddSourceToCommandLineOptions();
Expand Down Expand Up @@ -328,6 +383,113 @@ public void ExecutorExecuteShouldForValidSourcesAndValidSelectedTestsRunsTestsAn
Assert.AreEqual(ArgumentProcessorResult.Success, argumentProcessorResult);
}

[TestMethod]
public void ExecutorShouldRunTestsWhenTestsAreCommaSeparated()
{
var mockTestPlatform = new Mock<ITestPlatform>();
var mockTestRunRequest = new Mock<ITestRunRequest>();
var mockDiscoveryRequest = new Mock<IDiscoveryRequest>();

ResetAndAddSourceToCommandLineOptions();

List<TestCase> list = new List<TestCase>();
list.Add(new TestCase("Test1", new Uri("http://FooTestUri1"), "Source1"));
list.Add(new TestCase("Test2", new Uri("http://FooTestUri1"), "Source1"));
mockDiscoveryRequest.Setup(dr => dr.DiscoverAsync()).Raises(dr => dr.OnDiscoveredTests += null, new DiscoveredTestsEventArgs(list));

mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny<IRequestData>(), It.IsAny<TestRunCriteria>())).Returns(mockTestRunRequest.Object);
mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny<IRequestData>(), It.IsAny<DiscoveryCriteria>())).Returns(mockDiscoveryRequest.Object);

var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestLoggerManager.Instance, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask);
var executor = GetExecutor(testRequestManager);

executor.Initialize("Test1, Test2");
ArgumentProcessorResult argumentProcessorResult = executor.Execute();

mockOutput.Verify(o => o.WriteLine(It.IsAny<string>(), OutputLevel.Warning), Times.Never);
Assert.AreEqual(ArgumentProcessorResult.Success, argumentProcessorResult);
}

[TestMethod]
public void ExecutorShouldRunTestsWhenTestsAreFiltered()
{
var mockTestPlatform = new Mock<ITestPlatform>();
var mockTestRunRequest = new Mock<ITestRunRequest>();
var mockDiscoveryRequest = new Mock<IDiscoveryRequest>();

ResetAndAddSourceToCommandLineOptions();

List<TestCase> list = new List<TestCase>();
list.Add(new TestCase("Test1", new Uri("http://FooTestUri1"), "Source1"));
list.Add(new TestCase("Test2", new Uri("http://FooTestUri1"), "Source1"));
mockDiscoveryRequest.Setup(dr => dr.DiscoverAsync()).Raises(dr => dr.OnDiscoveredTests += null, new DiscoveredTestsEventArgs(list));

mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny<IRequestData>(), It.IsAny<TestRunCriteria>())).Returns(mockTestRunRequest.Object);
mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny<IRequestData>(), It.IsAny<DiscoveryCriteria>())).Returns(mockDiscoveryRequest.Object);

var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestLoggerManager.Instance, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask);
var executor = GetExecutor(testRequestManager);

executor.Initialize("Test1");
ArgumentProcessorResult argumentProcessorResult = executor.Execute();

mockOutput.Verify(o => o.WriteLine(It.IsAny<string>(), OutputLevel.Warning), Times.Never);
Assert.AreEqual(ArgumentProcessorResult.Success, argumentProcessorResult);
}

[TestMethod]
public void ExecutorShouldWarnWhenTestsAreNotAvailable()
{
var mockTestPlatform = new Mock<ITestPlatform>();
var mockTestRunRequest = new Mock<ITestRunRequest>();
var mockDiscoveryRequest = new Mock<IDiscoveryRequest>();

ResetAndAddSourceToCommandLineOptions();

List<TestCase> list = new List<TestCase>();
list.Add(new TestCase("Test2", new Uri("http://FooTestUri1"), "Source1"));
mockDiscoveryRequest.Setup(dr => dr.DiscoverAsync()).Raises(dr => dr.OnDiscoveredTests += null, new DiscoveredTestsEventArgs(list));

mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny<IRequestData>(), It.IsAny<TestRunCriteria>())).Returns(mockTestRunRequest.Object);
mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny<IRequestData>(), It.IsAny<DiscoveryCriteria>())).Returns(mockDiscoveryRequest.Object);

var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestLoggerManager.Instance, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask);
var executor = GetExecutor(testRequestManager);

executor.Initialize("Test1, Test2");
ArgumentProcessorResult argumentProcessorResult = executor.Execute();

mockOutput.Verify(o => o.WriteLine("A total of 1 tests were discovered but some tests do not match the specified selection criteria(Test1). Use right value(s) and try again.", OutputLevel.Warning), Times.Once);
Assert.AreEqual(ArgumentProcessorResult.Success, argumentProcessorResult);
}

[TestMethod]
public void ExecutorShouldRunTestsWhenTestsAreCommaSeparatedWithEscape()
{
var mockTestPlatform = new Mock<ITestPlatform>();
var mockTestRunRequest = new Mock<ITestRunRequest>();
var mockDiscoveryRequest = new Mock<IDiscoveryRequest>();

ResetAndAddSourceToCommandLineOptions();

List<TestCase> list = new List<TestCase>();
list.Add(new TestCase("Test1(a,b)", new Uri("http://FooTestUri1"), "Source1"));
list.Add(new TestCase("Test2(c,d)", new Uri("http://FooTestUri1"), "Source1"));
mockDiscoveryRequest.Setup(dr => dr.DiscoverAsync()).Raises(dr => dr.OnDiscoveredTests += null, new DiscoveredTestsEventArgs(list));

mockTestPlatform.Setup(tp => tp.CreateTestRunRequest(It.IsAny<IRequestData>(), It.IsAny<TestRunCriteria>())).Returns(mockTestRunRequest.Object);
mockTestPlatform.Setup(tp => tp.CreateDiscoveryRequest(It.IsAny<IRequestData>(), It.IsAny<DiscoveryCriteria>())).Returns(mockDiscoveryRequest.Object);

var testRequestManager = new TestRequestManager(CommandLineOptions.Instance, mockTestPlatform.Object, TestLoggerManager.Instance, TestRunResultAggregator.Instance, this.mockTestPlatformEventSource.Object, this.inferHelper, this.mockMetricsPublisherTask);
var executor = GetExecutor(testRequestManager);

executor.Initialize("Test1(a\\,b), Test2(c\\,d)");
ArgumentProcessorResult argumentProcessorResult = executor.Execute();

mockOutput.Verify(o => o.WriteLine(It.IsAny<string>(), OutputLevel.Warning), Times.Never);
Assert.AreEqual(ArgumentProcessorResult.Success, argumentProcessorResult);
}

#endregion

private void ResetAndAddSourceToCommandLineOptions()
Expand Down

0 comments on commit 46f1cae

Please sign in to comment.