Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,9 @@ private FeatureFlag() { }
// Disable turning dynamic code coverage for native code to OFF by default. Setting this to 1 will skip adding the setting.
public const string VSTEST_DISABLE_DYNAMICNATIVE_CODECOVERAGE_DEFAULT_SETTING = nameof(VSTEST_DISABLE_DYNAMICNATIVE_CODECOVERAGE_DEFAULT_SETTING);

// Disable fixed answer file parsing in case we broke something more. The original issue had no info on what the code that introduced the bug was trying to fix.
public const string VSTEST_DISABLE_ANSWERFILE_PARSING_FIX = nameof(VSTEST_DISABLE_ANSWERFILE_PARSING_FIX);

Comment thread
nohwnd marked this conversation as resolved.
Outdated


[Obsolete("Only use this in tests.")]
Expand Down
54 changes: 39 additions & 15 deletions src/Microsoft.TestPlatform.Utilities/CommandLineUtilities.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,9 @@ public static bool SplitCommandLineIntoArguments(string args, out string[] argum

try
{
while (true)
while (index < args.Length)
{
// skip whitespace
// Skip whitespace.
while (char.IsWhiteSpace(args[index]))
{
index++;
Expand All @@ -33,49 +33,73 @@ public static bool SplitCommandLineIntoArguments(string args, out string[] argum
if (args[index] == '#')
{
index++;
while (args[index] != '\n')
while (index < args.Length && args[index] != '\n')
{
index++;
}

// We are done processing comment move to next statement.
continue;
}

// do one argument
// Read argument until next whitespace (not in quotes).
do
{
if (args[index] == '\\')
{
int cSlashes = 1;
// Move to next char.
index++;
while (index == args.Length && args[index] == '\\')
{
cSlashes++;
}

if (index == args.Length || args[index] != '"')
// If this was the last char then output the slash.
if (index == args.Length)
{
currentArg.Append('\\', cSlashes);
currentArg.Append('\\');

index++;
continue;
}
else
{
currentArg.Append('\\', (cSlashes >> 1));
if (0 != (cSlashes & 1))
// If the char after '\' is also a '\', output the second '\' and skip over to the next char.
if (args[index] == '\\')
{
currentArg.Append('\\');

// We processed the escaped \, move to next char.
index++;
continue;
}

// If the char after '\' is a '"', output '"' and skip over to the next char.
if (index <= args.Length && args[index] == '"')
{
currentArg.Append('"');

// We processed the escaped " move to next char.
index++;
continue;
}
else

// If the char after '\' is anything else, output the slash. And continue processing the next char.
if (index <= args.Length)
{
inQuotes = !inQuotes;
currentArg.Append('\\');

// Don't skip to the next char. We outputted the \ because it was not escaping \ or ". Let the next character to be processed by the loop.
// index++;
continue;
}
}
}
// Unescaped quote enters and leaves quoted mode.
else if (args[index] == '"')
{
inQuotes = !inQuotes;
index++;
}
else
{
// Collect all other characters.
currentArg.Append(args[index]);
index++;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,28 @@ namespace Microsoft.TestPlatform.Utilities.Tests;
[TestClass]
public class CommandLineUtilitiesTest
{
private static void VerifyCommandLineSplitter(string commandLine, string[] expected)
[TestMethod]
[DataRow("", new string[] { })]
[DataRow(" /a:b ", new string[] { "/a:b" })]
[DataRow("""
/param1
/param2:value2
/param3:"value with spaces"
""", new string[] { "/param1", "/param2:value2", "/param3:value with spaces" })]
[DataRow("""/param3 #comment""", new string[] { "/param3" })]
[DataRow("""
/param3 #comment ends with newline \" \\
/param4
""", new string[] { "/param3", "/param4" })]
[DataRow("""/testadapterpath:"c:\Path" """, new string[] { @"/testadapterpath:c:\Path" })]
[DataRow("""/testadapterpath:"c:\Path" /logger:"trx" """, new string[] { @"/testadapterpath:c:\Path", "/logger:trx" })]
[DataRow("""/testadapterpath:"c:\Path" /logger:"trx" /diag:"log.txt" """, new string[] { @"/testadapterpath:c:\Path", "/logger:trx", "/diag:log.txt" })]
[DataRow("""/Tests:"Test(\"iCT 256\")" """, new string[] { """/Tests:Test("iCT 256")""" })]
public void VerifyCommandLineSplitter(string input, string[] expected)
{
CommandLineUtilities.SplitCommandLineIntoArguments(commandLine, out var actual);
CommandLineUtilities.SplitCommandLineIntoArguments(input, out var actual);

Assert.AreEqual(expected.Length, actual.Length);
for (int i = 0; i < actual.Length; ++i)
{
Assert.AreEqual(expected[i], actual[i]);
}
CollectionAssert.AreEqual(expected, actual);
}

[TestMethod]
public void TestCommandLineSplitter()
{
VerifyCommandLineSplitter("", []);
VerifyCommandLineSplitter("/testadapterpath:\"c:\\Path\"", [@"/testadapterpath:c:\Path"]);
VerifyCommandLineSplitter("/testadapterpath:\"c:\\Path\" /logger:\"trx\"", [@"/testadapterpath:c:\Path", "/logger:trx"]);
VerifyCommandLineSplitter("/testadapterpath:\"c:\\Path\" /logger:\"trx\" /diag:\"log.txt\"", [@"/testadapterpath:c:\Path", "/logger:trx", "/diag:log.txt"]);
}
}