Merge branch 'rel/1.0.1' into dev

This commit is contained in:
Nate McMaster 2017-05-01 14:55:24 -07:00
commit edcba47b84
10 changed files with 120 additions and 68 deletions

View File

@ -15,6 +15,7 @@ branches:
- master
- release
- dev
- /^rel\/.*/
- /^(.*\/)?ci-.*$/
before_install:
- if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; fi

View File

@ -4,6 +4,7 @@ branches:
only:
- master
- release
- /^rel\/.*/
- dev
- /^(.*\/)?ci-.*$/
build_script:

View File

@ -17,7 +17,6 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
public AppWithDepsTests(ITestOutputHelper logger)
{
_app = new AppWithDeps(logger);
_app.Prepare();
}
[Fact]

View File

@ -4,9 +4,6 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using Microsoft.Extensions.Internal;
@ -21,12 +18,12 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
private readonly ProcessSpec _spec;
private BufferBlock<string> _source;
private ITestOutputHelper _logger;
private int _reading;
public AwaitableProcess(ProcessSpec spec, ITestOutputHelper logger)
{
_spec = spec;
_logger = logger;
_source = new BufferBlock<string>();
}
public void Start()
@ -36,19 +33,32 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
throw new InvalidOperationException("Already started");
}
var psi = new ProcessStartInfo
_process = new Process
{
UseShellExecute = false,
FileName = _spec.Executable,
WorkingDirectory = _spec.WorkingDirectory,
Arguments = ArgumentEscaper.EscapeAndConcatenate(_spec.Arguments),
RedirectStandardOutput = true,
RedirectStandardError = true
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo
{
UseShellExecute = false,
FileName = _spec.Executable,
WorkingDirectory = _spec.WorkingDirectory,
Arguments = ArgumentEscaper.EscapeAndConcatenate(_spec.Arguments),
RedirectStandardOutput = true,
RedirectStandardError = true,
Environment =
{
["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "true"
}
}
};
_process = Process.Start(psi);
_logger.WriteLine($"{DateTime.Now}: process start: '{psi.FileName} {psi.Arguments}'");
StartProcessingOutput(_process.StandardOutput);
StartProcessingOutput(_process.StandardError);;
_process.OutputDataReceived += OnData;
_process.ErrorDataReceived += OnData;
_process.Exited += OnExit;
_process.Start();
_process.BeginErrorReadLine();
_process.BeginOutputReadLine();
_logger.WriteLine($"{DateTime.Now}: process start: '{_process.StartInfo.FileName} {_process.StartInfo.Arguments}'");
}
public Task<string> GetOutputLineAsync(string message)
@ -87,32 +97,33 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
return lines;
}
private void StartProcessingOutput(StreamReader streamReader)
private void OnData(object sender, DataReceivedEventArgs args)
{
_source = _source ?? new BufferBlock<string>();
Interlocked.Increment(ref _reading);
Task.Run(() =>
{
string line;
while ((line = streamReader.ReadLine()) != null)
{
_logger.WriteLine($"{DateTime.Now}: post: '{line}'");
_source.Post(line);
}
var line = args.Data ?? string.Empty;
_logger.WriteLine($"{DateTime.Now}: post: '{line}'");
_source.Post(line);
}
if (Interlocked.Decrement(ref _reading) <= 0)
{
_source.Complete();
}
}).ConfigureAwait(false);
private void OnExit(object sender, EventArgs args)
{
_source.Complete();
}
public void Dispose()
{
if (_process != null && !_process.HasExited)
_source.Complete();
if (_process != null)
{
_process.KillTree();
if (!_process.HasExited)
{
_process.KillTree();
}
_process.ErrorDataReceived -= OnData;
_process.OutputDataReceived -= OnData;
_process.Exited -= OnExit;
}
}
}
}
}

View File

@ -15,7 +15,6 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
public DotNetWatcherTests(ITestOutputHelper logger)
{
_app = new KitchenSinkApp(logger);
_app.Prepare();
}
[Fact]

View File

@ -20,7 +20,6 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
public GlobbingAppTests(ITestOutputHelper logger)
{
_app = new GlobbingApp(logger);
_app.Prepare();
}
[Theory]
@ -118,6 +117,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
[Fact]
public async Task ListsFiles()
{
await _app.PrepareAsync();
_app.Start(new [] { "--list" });
var lines = await _app.Process.GetAllOutputLines();

View File

@ -20,7 +20,6 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
public NoDepsAppTests(ITestOutputHelper logger)
{
_app = new WatchableApp("NoDepsApp", logger);
_app.Prepare();
}
[Fact]

View File

@ -7,7 +7,10 @@ using System.Diagnostics;
using System.IO;
using System.Reflection;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Tools.Internal;
using Xunit.Abstractions;
namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
@ -55,50 +58,84 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
}
}
public void Restore(string project)
public Task RestoreAsync(string project)
{
_logger?.WriteLine($"Restoring msbuild project in {project}");
ExecuteCommand(project, "restore");
return ExecuteCommandAsync(project, TimeSpan.FromSeconds(120), "restore");
}
public void Build(string project)
public Task BuildAsync(string project)
{
_logger?.WriteLine($"Building {project}");
ExecuteCommand(project, "build");
return ExecuteCommandAsync(project, TimeSpan.FromSeconds(60), "build");
}
private void ExecuteCommand(string project, params string[] arguments)
private async Task ExecuteCommandAsync(string project, TimeSpan timeout, params string[] arguments)
{
var tcs = new TaskCompletionSource<object>();
project = Path.Combine(WorkFolder, project);
var psi = new ProcessStartInfo
_logger?.WriteLine($"Project directory: '{project}'");
var process = new Process
{
FileName = DotNetMuxer.MuxerPathOrDefault(),
Arguments = ArgumentEscaper.EscapeAndConcatenate(arguments),
WorkingDirectory = project,
RedirectStandardOutput = true,
RedirectStandardError = true
EnableRaisingEvents = true,
StartInfo = new ProcessStartInfo
{
FileName = DotNetMuxer.MuxerPathOrDefault(),
Arguments = ArgumentEscaper.EscapeAndConcatenate(arguments),
WorkingDirectory = project,
RedirectStandardOutput = true,
RedirectStandardError = true,
Environment =
{
["DOTNET_SKIP_FIRST_TIME_EXPERIENCE"] = "true"
}
},
};
var process = new Process()
void OnData(object sender, DataReceivedEventArgs args)
=> _logger?.WriteLine(args.Data ?? string.Empty);
void OnExit(object sender, EventArgs args)
{
StartInfo = psi,
EnableRaisingEvents = true
};
_logger?.WriteLine($"Process exited {process.Id}");
tcs.TrySetResult(null);
}
void WriteLine(object sender, DataReceivedEventArgs args)
=> _logger.WriteLine(args.Data);
process.ErrorDataReceived += WriteLine;
process.OutputDataReceived += WriteLine;
process.ErrorDataReceived += OnData;
process.OutputDataReceived += OnData;
process.Exited += OnExit;
process.Start();
process.WaitForExit();
process.ErrorDataReceived -= WriteLine;
process.OutputDataReceived -= WriteLine;
process.BeginErrorReadLine();
process.BeginOutputReadLine();
_logger?.WriteLine($"Started process {process.Id}: {process.StartInfo.FileName} {process.StartInfo.Arguments}");
var done = await Task.WhenAny(tcs.Task, Task.Delay(timeout));
process.CancelErrorRead();
process.CancelOutputRead();
process.ErrorDataReceived -= OnData;
process.OutputDataReceived -= OnData;
process.Exited -= OnExit;
if (!ReferenceEquals(done, tcs.Task))
{
if (!process.HasExited)
{
_logger?.WriteLine($"Killing process {process.Id}");
process.KillTree();
}
throw new TimeoutException($"Process timed out after {timeout.TotalSeconds} seconds");
}
_logger?.WriteLine($"Process exited {process.Id} with code {process.ExitCode}");
if (process.ExitCode != 0)
{
throw new InvalidOperationException($"Exit code {process.ExitCode}");
}
}

View File

@ -53,10 +53,10 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
return int.Parse(pid);
}
public void Prepare()
public async Task PrepareAsync()
{
Scenario.Restore(_appName);
Scenario.Build(_appName);
await Scenario.RestoreAsync(_appName);
await Scenario.BuildAsync(_appName);
_prepared = true;
}
@ -64,7 +64,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
{
if (!_prepared)
{
throw new InvalidOperationException("Call .Prepare() first");
throw new InvalidOperationException($"Call {nameof(PrepareAsync)} first");
}
var args = Scenario
@ -91,6 +91,11 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
public async Task StartWatcherAsync(string[] arguments, [CallerMemberName] string name = null)
{
if (!_prepared)
{
await PrepareAsync();
}
var args = new[] { "run", "--" }.Concat(arguments);
Start(args, name);
@ -103,7 +108,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
{
_logger?.WriteLine("Disposing WatchableApp");
Process?.Dispose();
Scenario.Dispose();
Scenario?.Dispose();
}
}
}

View File

@ -21,7 +21,7 @@ namespace Microsoft.DotNet.Watcher.Tools.Tests
{
Func<string, string> normalize = p => p.Replace('\\', '/');
var expected = new HashSet<string>(expectedFiles.Select(normalize));
Assert.True(expected.SetEquals(actualFiles.Select(normalize)), "File sets should be equal");
Assert.True(expected.SetEquals(actualFiles.Where(p => !string.IsNullOrEmpty(p)).Select(normalize)), "File sets should be equal");
}
}
}
}