提交 f206f9cf 编写于 作者: C Cyrus Najmabadi

Merge branch 'master' into waitAndGetResult

......@@ -11,6 +11,8 @@
<Configuration Condition="'$(Configuration)' == ''">Debug</Configuration>
<RunTestArgs Condition="'$(ManualTest)' == ''">$(RunTestArgs) -xml</RunTestArgs>
<RunTestArgs Condition="'$(Test64)' == 'true'">$(RunTestArgs) -test64</RunTestArgs>
<RunTestArgs Condition="'$(Trait)' != ''">$(RunTestArgs) -trait:$(Trait)</RunTestArgs>
<RunTestArgs Condition="'$(NoTrait)' != ''">$(RunTestArgs) -notrait:$(NoTrait)</RunTestArgs>
<IncludePattern Condition="'$(IncludePattern)' == ''">*.UnitTests*.dll</IncludePattern>
<OutputDirectory>Binaries\$(Configuration)</OutputDirectory>
<NuGetPackageRoot>$(UserProfile)\.nuget\packages</NuGetPackageRoot>
......
......@@ -198,7 +198,7 @@ protected override bool NeedsProxy(Symbol localOrParameter)
// No closures or lambda methods are generated.
// E.g.
// int y = 0;
// var b = false && from z in new X(y) select f(z + y)
// var b = false && (from z in new X(y) select f(z + y))
return loweredBody;
}
......
......@@ -6500,7 +6500,7 @@ private StatementSyntax ParseStatementNoDeclaration(bool allowAnyExpression)
}
else if (this.IsQueryExpression(mayBeVariableDeclaration: true, mayBeMemberDeclaration: allowAnyExpression))
{
return this.ParseExpressionStatement(this.ParseQueryExpression());
return this.ParseExpressionStatement(this.ParseQueryExpression(0));
}
else
{
......@@ -8330,7 +8330,7 @@ private ExpressionSyntax ParseSubExpressionCore(uint precedence)
}
else if (this.IsQueryExpression(mayBeVariableDeclaration: false, mayBeMemberDeclaration: false))
{
leftOperand = this.ParseQueryExpression();
leftOperand = this.ParseQueryExpression(precedence);
}
else if (this.CurrentToken.ContextualKind == SyntaxKind.FromKeyword && IsInQuery)
{
......@@ -8448,10 +8448,10 @@ private ExpressionSyntax ParseSubExpressionCore(uint precedence)
{
var questionToken = this.EatToken();
var colonLeft = this.ParseSubExpression(nullCoalescingPrecedence - 1);
var colonLeft = this.ParseExpressionCore();
var colon = this.EatToken(SyntaxKind.ColonToken);
var colonRight = this.ParseSubExpression(nullCoalescingPrecedence - 1);
var colonRight = this.ParseExpressionCore();
leftOperand = _syntaxFactory.ConditionalExpression(leftOperand, questionToken, colonLeft, colon, colonRight);
}
......@@ -10193,11 +10193,16 @@ private bool IsQueryExpressionAfterFrom(bool mayBeVariableDeclaration, bool mayB
return false;
}
private QueryExpressionSyntax ParseQueryExpression()
private QueryExpressionSyntax ParseQueryExpression(uint precedence)
{
this.EnterQuery();
var fc = this.ParseFromClause();
fc = CheckFeatureAvailability(fc, MessageID.IDS_FeatureQueryExpression);
if (precedence > 1 && IsStrict)
{
fc = this.AddError(fc, ErrorCode.ERR_InvalidExprTerm, SyntaxFacts.GetText(SyntaxKind.FromKeyword));
}
var body = this.ParseQueryBody();
this.LeaveQuery();
return _syntaxFactory.QueryExpression(fc, body);
......@@ -10440,6 +10445,8 @@ private QueryContinuationSyntax ParseQueryContinuation()
return _syntaxFactory.QueryContinuation(@into, name, body);
}
private bool IsStrict => this.Options.Features.ContainsKey("strict");
[Obsolete("Use IsIncrementalAndFactoryContextMatches")]
private new bool IsIncremental
{
......
......@@ -1797,6 +1797,18 @@ static int Main()
return (errCount > 0) ? 1 : 0;
}
}";
// the grammar does not allow a query on the right-hand-side of &&, but we allow it except in strict mode.
CreateCompilationWithMscorlibAndSystemCore(source, parseOptions: TestOptions.Regular.WithFeature("strict", "true")).VerifyDiagnostics(
// (23,26): error CS1525: Invalid expression term 'from'
// var b = false && from x in src select x; // WRN CS0429
Diagnostic(ErrorCode.ERR_InvalidExprTerm, "from x in src").WithArguments("from").WithLocation(23, 26),
// (4,1): hidden CS8019: Unnecessary using directive.
// using System.Collections.Generic;
Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Collections.Generic;").WithLocation(4, 1),
// (3,1): hidden CS8019: Unnecessary using directive.
// using System.Linq;
Diagnostic(ErrorCode.HDN_UnusedUsingDirective, "using System.Linq;").WithLocation(3, 1)
);
CompileAndVerify(source, additionalRefs: new[] { LinqAssemblyRef },
expectedOutput: "0");
}
......
......@@ -508,6 +508,16 @@ public void TestConditional()
Assert.Equal("c", ts.WhenFalse.ToString());
}
[Fact]
public void TestConditional02()
{
// ensure that ?: has lower precedence than assignment.
var text = "a ? b=c : d=e";
var expr = this.ParseExpression(text);
Assert.Equal(SyntaxKind.ConditionalExpression, expr.Kind());
Assert.False(expr.HasErrors);
}
[Fact]
public void TestCast()
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
namespace RunTests
{
internal class Options
{
public bool UseHtml { get; set; }
public bool Test64 { get; set; }
public string Trait { get; set; }
public string NoTrait { get; set; }
public List<string> Assemblies { get; set; }
public List<string> MissingAssemblies { get; set; }
public string XunitPath { get; set; }
internal static Options Parse(string[] args)
{
if (args == null || args.Any(a => a == null) || args.Length < 2)
{
return null;
}
var opt = new Options {XunitPath = args[0], UseHtml = true};
int index = 1;
var comp = StringComparer.OrdinalIgnoreCase;
while (index < args.Length)
{
var current = args[index];
if (comp.Equals(current, "-test64"))
{
opt.Test64 = true;
index++;
}
else if (comp.Equals(current, "-xml"))
{
opt.UseHtml = false;
index++;
}
else if (current.Length > 7 && current.StartsWith("-trait:", StringComparison.OrdinalIgnoreCase))
{
opt.Trait = current.Substring(7);
index++;
}
else if (current.Length > 9 && current.StartsWith("-notrait:", StringComparison.OrdinalIgnoreCase))
{
opt.NoTrait = current.Substring(9);
index++;
}
else
{
break;
}
}
try
{
opt.XunitPath = opt.Test64
? Path.Combine(opt.XunitPath, "xunit.console.exe")
: Path.Combine(opt.XunitPath, "xunit.console.x86.exe");
}
catch (ArgumentException ex)
{
Console.WriteLine($"{opt.XunitPath} is not a valid path: {ex.Message}");
return null;
}
if (!File.Exists(opt.XunitPath))
{
Console.WriteLine($"The file '{opt.XunitPath}' does not exist.");
return null;
}
opt.Assemblies = new List<string>();
opt.MissingAssemblies = new List<string>();
var assemblyArgs = args.Skip(index).ToArray();
if (!assemblyArgs.Any())
{
Console.WriteLine("No test assemblies specified.");
return null;
}
foreach (var assemblyPath in assemblyArgs)
{
if (File.Exists(assemblyPath))
{
opt.Assemblies.Add(assemblyPath);
continue;
}
opt.MissingAssemblies.Add(assemblyPath);
}
return opt;
}
public static void PrintUsage()
{
Console.WriteLine("runtests [xunit-console-runner] [-test64] [-xml] [-trait:name1=value1;...] [-notrait:name1=value1;...] [assembly1] [assembly2] [...]");
Console.WriteLine("Example:");
Console.WriteLine(@"runtests c:\path-that-contains-xunit.console.exe\ -trait:Feature=Classification Assembly1.dll Assembly2.dll");
}
}
}
\ No newline at end of file
......@@ -15,29 +15,14 @@ internal sealed class Program
{
internal static int Main(string[] args)
{
if (args.Length < 2)
{
PrintUsage();
return 1;
}
var xunitPath = args[0];
var index = 1;
var test64 = false;
var useHtml = true;
ParseArgs(args, ref index, ref test64, ref useHtml);
var list = new List<string>(args.Skip(index));
if (list.Count == 0)
var options = Options.Parse(args);
if (options == null)
{
PrintUsage();
Options.PrintUsage();
return 1;
}
var xunit = test64
? Path.Combine(xunitPath, "xunit.console.exe")
: Path.Combine(xunitPath, "xunit.console.x86.exe");
// Setup cancellation for ctrl-c key presses
var cts = new CancellationTokenSource();
Console.CancelKeyPress += delegate
......@@ -45,14 +30,20 @@ internal static int Main(string[] args)
cts.Cancel();
};
var testRunner = new TestRunner(xunit, useHtml);
var testRunner = new TestRunner(options);
var start = DateTime.Now;
Console.WriteLine("Running {0} test assemblies", list.Count);
Console.WriteLine("Running {0} test assemblies", options.Assemblies.Count());
var orderedList = OrderAssemblyList(list);
var orderedList = OrderAssemblyList(options.Assemblies);
var result = testRunner.RunAllAsync(orderedList, cts.Token).Result;
var span = DateTime.Now - start;
foreach (var assemblyPath in options.MissingAssemblies)
{
ConsoleUtil.WriteLine(ConsoleColor.Red, $"The file '{assemblyPath}' does not exist, is an invalid file name, or you do not have sufficient permissions to read the specified file.");
}
if (!result)
{
ConsoleUtil.WriteLine(ConsoleColor.Red, "Test failures encountered: {0}", span);
......@@ -60,35 +51,7 @@ internal static int Main(string[] args)
}
Console.WriteLine("All tests passed: {0}", span);
return 0;
}
private static void PrintUsage()
{
Console.WriteLine("runtests [xunit-console-runner] [assembly1] [assembly2] [...]");
}
private static void ParseArgs(string[] args, ref int index, ref bool test64, ref bool useHtml)
{
var comp = StringComparer.OrdinalIgnoreCase;
while (index < args.Length)
{
var current = args[index];
if (comp.Equals(current, "-test64"))
{
test64 = true;
index++;
}
else if (comp.Equals(current, "-xml"))
{
useHtml = false;
index++;
}
else
{
break;
}
}
return options.MissingAssemblies.Any() ? 1 : 0;
}
/// <summary>
......@@ -96,7 +59,7 @@ private static void ParseArgs(string[] args, ref int index, ref bool test64, ref
/// is not ideal as the largest assembly does not necessarily take the most time.
/// </summary>
/// <param name="list"></param>
private static IOrderedEnumerable<string> OrderAssemblyList(List<string> list) =>
private static IOrderedEnumerable<string> OrderAssemblyList(IEnumerable<string> list) =>
list.OrderByDescending((assemblyName) => new FileInfo(assemblyName).Length);
}
}
}
\ No newline at end of file
......@@ -23,6 +23,7 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Options.cs" />
<Compile Include="ProcessRunner.cs" />
<Compile Include="Program.cs" />
<Compile Include="TestRunner.cs" />
......@@ -31,4 +32,4 @@
<ImportGroup Label="Targets">
<Import Project="..\..\..\..\build\Targets\VSL.Imports.targets" />
</ImportGroup>
</Project>
</Project>
\ No newline at end of file
......@@ -30,13 +30,11 @@ internal TestResult(bool succeeded, string assemblyName, TimeSpan elapsed, strin
}
}
private readonly string _xunitConsolePath;
private readonly bool _useHtml;
private readonly Options _options;
internal TestRunner(string xunitConsolePath, bool useHtml)
internal TestRunner(Options options)
{
_xunitConsolePath = xunitConsolePath;
_useHtml = useHtml;
this._options = options;
}
internal async Task<bool> RunAllAsync(IEnumerable<string> assemblyList, CancellationToken cancellationToken)
......@@ -120,8 +118,8 @@ private async Task<TestResult> RunTest(string assemblyPath, CancellationToken ca
try
{
var assemblyName = Path.GetFileName(assemblyPath);
var resultsDir = Path.Combine(Path.GetDirectoryName(assemblyPath), "xUnitResults");
var resultsFile = Path.Combine(resultsDir, $"{assemblyName}.{(_useHtml ? "html" : "xml")}");
var resultsFile = Path.Combine(Path.GetDirectoryName(assemblyPath), "xUnitResults", $"{assemblyName}.{(_options.UseHtml ? "html" : "xml")}");
var resultsDir = Path.GetDirectoryName(resultsFile);
var outputLogPath = Path.Combine(resultsDir, $"{assemblyName}.out.log");
// NOTE: xUnit doesn't always create the log directory
......@@ -133,13 +131,31 @@ private async Task<TestResult> RunTest(string assemblyPath, CancellationToken ca
var builder = new StringBuilder();
builder.AppendFormat(@"""{0}""", assemblyPath);
builder.AppendFormat(@" -{0} ""{1}""", _useHtml ? "html" : "xml", resultsFile);
builder.AppendFormat(@" -{0} ""{1}""", _options.UseHtml ? "html" : "xml", resultsFile);
builder.Append(" -noshadow -verbose");
if (!string.IsNullOrWhiteSpace(_options.Trait))
{
var traits = _options.Trait.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var trait in traits)
{
builder.AppendFormat(" -trait {0}", trait);
}
}
if (!string.IsNullOrWhiteSpace(_options.NoTrait))
{
var traits = _options.NoTrait.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var trait in traits)
{
builder.AppendFormat(" -notrait {0}", trait);
}
}
var errorOutput = new StringBuilder();
var start = DateTime.UtcNow;
var xunitPath = _xunitConsolePath;
var xunitPath = _options.XunitPath;
var processOutput = await ProcessRunner.RunProcessAsync(
xunitPath,
builder.ToString(),
......@@ -173,7 +189,7 @@ private async Task<TestResult> RunTest(string assemblyPath, CancellationToken ca
File.Delete(resultsFile);
}
errorOutput.AppendLine($"Command: {_xunitConsolePath} {builder}");
errorOutput.AppendLine($"Command: {_options.XunitPath} {builder}");
errorOutput.AppendLine($"xUnit output: {outputLogPath}");
if (processOutput.ErrorLines.Any())
......@@ -190,7 +206,7 @@ private async Task<TestResult> RunTest(string assemblyPath, CancellationToken ca
// If the results are html, use Process.Start to open in the browser.
if (_useHtml && resultData.Length > 0)
if (_options.UseHtml && resultData.Length > 0)
{
Process.Start(resultsFile);
}
......@@ -200,7 +216,7 @@ private async Task<TestResult> RunTest(string assemblyPath, CancellationToken ca
}
catch (Exception ex)
{
throw new Exception($"Unable to run {assemblyPath} with {_xunitConsolePath}. {ex}");
throw new Exception($"Unable to run {assemblyPath} with {_options.XunitPath}. {ex}");
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册