Merge aspnet/DotNetTools release/2.2

This commit is contained in:
Nate McMaster 2018-11-14 14:12:52 -08:00
commit 0782a9dfa9
No known key found for this signature in database
GPG Key ID: A778D9601BD78810
28 changed files with 247 additions and 178 deletions

View File

@ -12,73 +12,12 @@
"dotnet-sql-cache": {
"packageTypes": [
"DotnetTool"
],
"Exclusions": {
"NEUTRAL_RESOURCES_LANGUAGE": {
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
},
"WRONG_PUBLICKEYTOKEN": {
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg."
},
"ASSEMBLY_INFORMATIONAL_VERSION_MISMATCH": {
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg."
},
"ASSEMBLY_FILE_VERSION_MISMATCH": {
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg."
},
"ASSEMBLY_VERSION_MISMATCH": {
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": "Assembly is built by another project but bundled in our nupkg."
}
}
]
},
"dotnet-user-secrets": {
"packageTypes": [
"DotnetTool"
],
"Exclusions": {
"NEUTRAL_RESOURCES_LANGUAGE": {
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
},
"SERVICING_ATTRIBUTE": {
"tools/netcoreapp2.1/any/Newtonsoft.Json.dll": "Assembly is built by another project but bundled in our nupkg."
},
"WRONG_PUBLICKEYTOKEN": {
"tools/netcoreapp2.1/any/Newtonsoft.Json.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
},
"ASSEMBLY_INFORMATIONAL_VERSION_MISMATCH": {
"tools/netcoreapp2.1/any/Newtonsoft.Json.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
},
"ASSEMBLY_FILE_VERSION_MISMATCH": {
"tools/netcoreapp2.1/any/Newtonsoft.Json.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
},
"ASSEMBLY_VERSION_MISMATCH": {
"tools/netcoreapp2.1/any/Newtonsoft.Json.dll": "Assembly is built by another project but bundled in our nupkg.",
"tools/netcoreapp2.1/any/System.Runtime.CompilerServices.Unsafe.dll": "Assembly is built by another project but bundled in our nupkg."
}
}
]
},
"dotnet-dev-certs": {
"packageTypes": [
@ -88,7 +27,7 @@
"Microsoft.AspNetCore.DeveloperCertificates.XPlat": {
"Exclusions": {
"DOC_MISSING": {
"lib/netcoreapp2.1/Microsoft.AspNetCore.DeveloperCertificates.XPlat.dll": "Docs not required to shipoob package"
"lib/netcoreapp2.2/Microsoft.AspNetCore.DeveloperCertificates.XPlat.dll": "Docs not required to shipoob package"
}
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<Description>Package for the CLI first run experience.</Description>
<DefineConstants>$(DefineConstants);XPLAT</DefineConstants>
<PackageTags>aspnet;cli</PackageTags>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<OutputType>exe</OutputType>
<Description>Command line tool to generate certificates used in ASP.NET Core during development.</Description>
<RootNamespace>Microsoft.AspNetCore.DeveloperCertificates.Tools</RootNamespace>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<OutputType>exe</OutputType>
<Description>Command line tool to create tables and indexes in a Microsoft SQL Server database for distributed caching.</Description>
<PackageTags>cache;distributedcache;sqlserver</PackageTags>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<OutputType>exe</OutputType>
<Description>Command line tool to manage user secrets for Microsoft.Extensions.Configuration.</Description>
<GenerateUserSecretsAttribute>false</GenerateUserSecretsAttribute>

View File

@ -35,7 +35,7 @@ namespace Microsoft.Extensions.Configuration.UserSecrets.Tests
private const string ProjectTemplate = @"<Project ToolsVersion=""15.0"" Sdk=""Microsoft.NET.Sdk"">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFrameworks>netcoreapp2.1</TargetFrameworks>
<TargetFrameworks>netcoreapp2.2</TargetFrameworks>
{0}
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
</PropertyGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AssemblyName>Microsoft.Extensions.SecretManager.Tools.Tests</AssemblyName>
</PropertyGroup>

View File

@ -53,6 +53,10 @@ Environment variables:
DOTNET_WATCH
dotnet-watch sets this variable to '1' on all child processes launched.
DOTNET_WATCH_ITERATION
dotnet-watch sets this variable to '1' and increments by one each time
a file is changed and the command is restarted.
Remarks:
The special option '--' is used to delimit the end of the options and
the beginning of arguments that will be passed to the child dotnet process.

View File

@ -1,7 +1,8 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. 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.Globalization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.DotNet.Watcher.Internal;
@ -32,8 +33,13 @@ namespace Microsoft.DotNet.Watcher
cancellationToken.Register(state => ((TaskCompletionSource<object>) state).TrySetResult(null),
cancelledTaskSource);
var iteration = 1;
while (true)
{
processSpec.EnvironmentVariables["DOTNET_WATCH_ITERATION"] = iteration.ToString(CultureInfo.InvariantCulture);
iteration++;
var fileSet = await fileSetFactory.CreateAsync(cancellationToken);
if (fileSet == null)
@ -69,13 +75,15 @@ namespace Microsoft.DotNet.Watcher
await Task.WhenAll(processTask, fileSetTask);
if (processTask.Result == 0)
if (processTask.Result != 0 && finishedTask == processTask && !cancellationToken.IsCancellationRequested)
{
_reporter.Output("Exited");
// Only show this error message if the process exited non-zero due to a normal process exit.
// Don't show this if dotnet-watch killed the inner process due to file change or CTRL+C by the user
_reporter.Error($"Exited with error code {processTask.Result}");
}
else
{
_reporter.Error($"Exited with error code {processTask.Result}");
_reporter.Output("Exited");
}
if (finishedTask == cancelledTaskSource.Task || cancellationToken.IsCancellationRequested)

View File

@ -10,6 +10,8 @@ namespace Microsoft.DotNet.Watcher.Internal
{
internal class DotnetFileWatcher : IFileSystemWatcher
{
private volatile bool _disposed;
private readonly Func<string, FileSystemWatcher> _watcherFactory;
private FileSystemWatcher _fileSystemWatcher;
@ -46,6 +48,11 @@ namespace Microsoft.DotNet.Watcher.Internal
private void WatcherErrorHandler(object sender, ErrorEventArgs e)
{
if (_disposed)
{
return;
}
var exception = e.GetException();
// Win32Exception may be triggered when setting EnableRaisingEvents on a file system type
@ -62,6 +69,11 @@ namespace Microsoft.DotNet.Watcher.Internal
private void WatcherRenameHandler(object sender, RenamedEventArgs e)
{
if (_disposed)
{
return;
}
NotifyChange(e.OldFullPath);
NotifyChange(e.FullPath);
@ -79,6 +91,11 @@ namespace Microsoft.DotNet.Watcher.Internal
private void WatcherChangeHandler(object sender, FileSystemEventArgs e)
{
if (_disposed)
{
return;
}
NotifyChange(e.FullPath);
}
@ -98,15 +115,7 @@ namespace Microsoft.DotNet.Watcher.Internal
{
enableEvents = _fileSystemWatcher.EnableRaisingEvents;
_fileSystemWatcher.EnableRaisingEvents = false;
_fileSystemWatcher.Created -= WatcherChangeHandler;
_fileSystemWatcher.Deleted -= WatcherChangeHandler;
_fileSystemWatcher.Changed -= WatcherChangeHandler;
_fileSystemWatcher.Renamed -= WatcherRenameHandler;
_fileSystemWatcher.Error -= WatcherErrorHandler;
_fileSystemWatcher.Dispose();
DisposeInnerWatcher();
}
_fileSystemWatcher = _watcherFactory(BasePath);
@ -122,6 +131,19 @@ namespace Microsoft.DotNet.Watcher.Internal
}
}
private void DisposeInnerWatcher()
{
_fileSystemWatcher.EnableRaisingEvents = false;
_fileSystemWatcher.Created -= WatcherChangeHandler;
_fileSystemWatcher.Deleted -= WatcherChangeHandler;
_fileSystemWatcher.Changed -= WatcherChangeHandler;
_fileSystemWatcher.Renamed -= WatcherRenameHandler;
_fileSystemWatcher.Error -= WatcherErrorHandler;
_fileSystemWatcher.Dispose();
}
public bool EnableRaisingEvents
{
get => _fileSystemWatcher.EnableRaisingEvents;
@ -130,7 +152,8 @@ namespace Microsoft.DotNet.Watcher.Internal
public void Dispose()
{
_fileSystemWatcher.Dispose();
_disposed = true;
DisposeInnerWatcher();
}
}
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@ -33,7 +33,7 @@ namespace Microsoft.DotNet.Watcher.Internal
var stopwatch = new Stopwatch();
using (var process = CreateProcess(processSpec))
using (var processState = new ProcessState(process))
using (var processState = new ProcessState(process, _reporter))
{
cancellationToken.Register(() => processState.TryKill());
@ -97,27 +97,36 @@ namespace Microsoft.DotNet.Watcher.Internal
private class ProcessState : IDisposable
{
private readonly IReporter _reporter;
private readonly Process _process;
private readonly TaskCompletionSource<object> _tcs = new TaskCompletionSource<object>();
private volatile bool _disposed;
public ProcessState(Process process)
public ProcessState(Process process, IReporter reporter)
{
_reporter = reporter;
_process = process;
_process.Exited += OnExited;
Task = _tcs.Task.ContinueWith(_ =>
{
// We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously
// this code used Process.Exited, which could result in us missing some output due to the ordering of
// events.
//
// See the remarks here: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit#System_Diagnostics_Process_WaitForExit_System_Int32_
if (!process.WaitForExit(Int32.MaxValue))
try
{
throw new TimeoutException();
}
// We need to use two WaitForExit calls to ensure that all of the output/events are processed. Previously
// this code used Process.Exited, which could result in us missing some output due to the ordering of
// events.
//
// See the remarks here: https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.process.waitforexit#System_Diagnostics_Process_WaitForExit_System_Int32_
if (!_process.WaitForExit(Int32.MaxValue))
{
throw new TimeoutException();
}
process.WaitForExit();
_process.WaitForExit();
}
catch (InvalidOperationException)
{
// suppress if this throws if no process is associated with this object anymore.
}
});
}
@ -125,15 +134,26 @@ namespace Microsoft.DotNet.Watcher.Internal
public void TryKill()
{
if (_disposed)
{
return;
}
try
{
if (!_process.HasExited)
{
_reporter.Verbose($"Killing process {_process.Id}");
_process.KillTree();
}
}
catch
{ }
catch (Exception ex)
{
_reporter.Verbose($"Error while killing process '{_process.StartInfo.FileName} {_process.StartInfo.Arguments}': {ex.Message}");
#if DEBUG
_reporter.Verbose(ex.ToString());
#endif
}
}
private void OnExited(object sender, EventArgs args)
@ -143,8 +163,8 @@ namespace Microsoft.DotNet.Watcher.Internal
{
if (!_disposed)
{
_disposed = true;
TryKill();
_disposed = true;
_process.Exited -= OnExited;
_process.Dispose();
}

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<OutputType>exe</OutputType>
<Description>Command line tool to watch for source file changes during development and restart the dotnet command.</Description>
<RootNamespace>Microsoft.DotNet.Watcher.Tools</RootNamespace>

View File

@ -1,12 +1,12 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. 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.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using System.Threading.Tasks.Dataflow;
using Microsoft.AspNetCore.Testing;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.CommandLineUtils;
using Xunit.Abstractions;
@ -17,16 +17,26 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
{
private Process _process;
private readonly ProcessSpec _spec;
private readonly List<string> _lines;
private BufferBlock<string> _source;
private ITestOutputHelper _logger;
private TaskCompletionSource<int> _exited;
public AwaitableProcess(ProcessSpec spec, ITestOutputHelper logger)
{
_spec = spec;
_logger = logger;
_source = new BufferBlock<string>();
_lines = new List<string>();
_exited = new TaskCompletionSource<int>();
}
public IEnumerable<string> Output => _lines;
public Task Exited => _exited.Task;
public int Id => _process.Id;
public void Start()
{
if (_process != null)
@ -52,6 +62,11 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
}
};
foreach (var env in _spec.EnvironmentVariables)
{
_process.StartInfo.EnvironmentVariables[env.Key] = env.Value;
}
_process.OutputDataReceived += OnData;
_process.ErrorDataReceived += OnData;
_process.Exited += OnExit;
@ -65,24 +80,30 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
public async Task<string> GetOutputLineAsync(string message, TimeSpan timeout)
{
_logger.WriteLine($"Waiting for output line [msg == '{message}']. Will wait for {timeout.TotalSeconds} sec.");
return await GetOutputLineAsync(m => message == m).TimeoutAfter(timeout);
var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);
return await GetOutputLineAsync($"[msg == '{message}']", m => string.Equals(m, message, StringComparison.Ordinal), cts.Token);
}
public async Task<string> GetOutputLineStartsWithAsync(string message, TimeSpan timeout)
{
_logger.WriteLine($"Waiting for output line [msg.StartsWith('{message}')]. Will wait for {timeout.TotalSeconds} sec.");
return await GetOutputLineAsync(m => m.StartsWith(message)).TimeoutAfter(timeout);
var cts = new CancellationTokenSource();
cts.CancelAfter(timeout);
return await GetOutputLineAsync($"[msg.StartsWith('{message}')]", m => m != null && m.StartsWith(message, StringComparison.Ordinal), cts.Token);
}
private async Task<string> GetOutputLineAsync(Predicate<string> predicate)
private async Task<string> GetOutputLineAsync(string predicateName, Predicate<string> predicate, CancellationToken cancellationToken)
{
while (!_source.Completion.IsCompleted)
{
while (await _source.OutputAvailableAsync())
while (await _source.OutputAvailableAsync(cancellationToken))
{
var next = await _source.ReceiveAsync();
_logger.WriteLine($"{DateTime.Now}: recv: '{next}'");
if (predicate(next))
var next = await _source.ReceiveAsync(cancellationToken);
_lines.Add(next);
var match = predicate(next);
_logger.WriteLine($"{DateTime.Now}: recv: '{next}'. {(match ? "Matches" : "Does not match")} condition '{predicateName}'.");
if (match)
{
return next;
}
@ -92,14 +113,14 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
return null;
}
public async Task<IList<string>> GetAllOutputLines()
public async Task<IList<string>> GetAllOutputLinesAsync(CancellationToken cancellationToken)
{
var lines = new List<string>();
while (!_source.Completion.IsCompleted)
{
while (await _source.OutputAvailableAsync())
while (await _source.OutputAvailableAsync(cancellationToken))
{
var next = await _source.ReceiveAsync();
var next = await _source.ReceiveAsync(cancellationToken);
_logger.WriteLine($"{DateTime.Now}: recv: '{next}'");
lines.Add(next);
}
@ -119,6 +140,8 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
// Wait to ensure the process has exited and all output consumed
_process.WaitForExit();
_source.Complete();
_exited.TrySetResult(_process.ExitCode);
_logger.WriteLine($"Process {_process.Id} has exited");
}
public void Dispose()
@ -135,6 +158,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
_process.ErrorDataReceived -= OnData;
_process.OutputDataReceived -= OnData;
_process.Exited -= OnExit;
_process.Dispose();
}
}
}

View File

@ -3,6 +3,8 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Globalization;
using System.Threading.Tasks;
using Xunit;
using Xunit.Abstractions;
@ -11,10 +13,12 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
{
public class DotNetWatcherTests : IDisposable
{
private readonly ITestOutputHelper _logger;
private readonly KitchenSinkApp _app;
public DotNetWatcherTests(ITestOutputHelper logger)
{
_logger = logger;
_app = new KitchenSinkApp(logger);
}
@ -30,6 +34,37 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
Assert.Equal("1", envValue);
}
[Fact]
public async Task RunsWithIterationEnvVariable()
{
await _app.StartWatcherAsync();
var source = Path.Combine(_app.SourceDirectory, "Program.cs");
var contents = File.ReadAllText(source);
const string messagePrefix = "DOTNET_WATCH_ITERATION = ";
for (var i = 1; i <= 3; i++)
{
var message = await _app.Process.GetOutputLineStartsWithAsync(messagePrefix, TimeSpan.FromMinutes(2));
var count = int.Parse(message.Substring(messagePrefix.Length), CultureInfo.InvariantCulture);
Assert.Equal(i, count);
await _app.IsWaitingForFileChange();
try
{
File.SetLastWriteTime(source, DateTime.Now);
await _app.HasRestarted();
}
catch (Exception ex)
{
_logger.WriteLine("Retrying. First attempt to restart app failed: " + ex.Message);
// retry
File.SetLastWriteTime(source, DateTime.Now);
await _app.HasRestarted();
}
}
}
public void Dispose()
{
_app.Dispose();

View File

@ -304,42 +304,44 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
private void AssertFileChangeRaisesEvent(string directory, IFileSystemWatcher watcher)
{
var semaphoreSlim = new SemaphoreSlim(0);
var expectedPath = Path.Combine(directory, Path.GetRandomFileName());
EventHandler<string> handler = (object _, string f) =>
using (var semaphoreSlim = new SemaphoreSlim(0))
{
_output.WriteLine("File changed: " + f);
var expectedPath = Path.Combine(directory, Path.GetRandomFileName());
EventHandler<string> handler = (object _, string f) =>
{
_output.WriteLine("File changed: " + f);
try
{
if (string.Equals(f, expectedPath, StringComparison.OrdinalIgnoreCase))
{
semaphoreSlim.Release();
}
}
catch (ObjectDisposedException)
{
// There's a known race condition here:
// even though we tell the watcher to stop raising events and we unsubscribe the handler
// there might be in-flight events that will still process. Since we dispose the reset
// event, this code will fail if the handler executes after Dispose happens.
}
};
File.AppendAllText(expectedPath, " ");
watcher.OnFileChange += handler;
try
{
if (string.Equals(f, expectedPath, StringComparison.OrdinalIgnoreCase))
{
semaphoreSlim.Release();
}
// On Unix the file write time is in 1s increments;
// if we don't wait, there's a chance that the polling
// watcher will not detect the change
Thread.Sleep(1000);
File.AppendAllText(expectedPath, " ");
Assert.True(semaphoreSlim.Wait(DefaultTimeout), "Expected a file change event for " + expectedPath);
}
catch (ObjectDisposedException)
finally
{
// There's a known race condition here:
// even though we tell the watcher to stop raising events and we unsubscribe the handler
// there might be in-flight events that will still process. Since we dispose the reset
// event, this code will fail if the handler executes after Dispose happens.
watcher.OnFileChange -= handler;
}
};
File.AppendAllText(expectedPath, " ");
watcher.OnFileChange += handler;
try
{
// On Unix the file write time is in 1s increments;
// if we don't wait, there's a chance that the polling
// watcher will not detect the change
Thread.Sleep(1000);
File.AppendAllText(expectedPath, " ");
Assert.True(semaphoreSlim.Wait(DefaultTimeout), "Expected a file change event for " + expectedPath);
}
finally
{
watcher.OnFileChange -= handler;
}
}

View File

@ -4,6 +4,7 @@
using System;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.DotNet.Watcher.Tools.Tests;
using Xunit;
@ -101,7 +102,10 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
{
await _app.PrepareAsync();
_app.Start(new [] { "--list" });
var lines = await _app.Process.GetAllOutputLines();
var cts = new CancellationTokenSource();
cts.CancelAfter(TimeSpan.FromSeconds(30));
var lines = await _app.Process.GetAllOutputLinesAsync(cts.Token);
var files = lines.Where(l => !l.StartsWith("watch :"));
AssertEx.EqualFileList(
_app.Scenario.WorkFolder,
@ -111,7 +115,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
"GlobbingApp/include/Foo.cs",
"GlobbingApp/GlobbingApp.csproj",
},
lines);
files);
}
public void Dispose()

View File

@ -15,10 +15,12 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
private static readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(30);
private readonly WatchableApp _app;
private readonly ITestOutputHelper _output;
public NoDepsAppTests(ITestOutputHelper logger)
{
_app = new WatchableApp("NoDepsApp", logger);
_output = logger;
}
[Fact]
@ -33,11 +35,10 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
File.WriteAllText(fileToChange, programCs);
await _app.HasRestarted();
Assert.DoesNotContain(_app.Process.Output, l => l.StartsWith("Exited with error code"));
var pid2 = await _app.GetProcessId();
Assert.NotEqual(pid, pid2);
// first app should have shut down
Assert.Throws<ArgumentException>(() => Process.GetProcessById(pid));
}
[Fact]
@ -49,10 +50,19 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
await _app.IsWaitingForFileChange();
var fileToChange = Path.Combine(_app.SourceDirectory, "Program.cs");
var programCs = File.ReadAllText(fileToChange);
File.WriteAllText(fileToChange, programCs);
await _app.HasRestarted();
try
{
File.SetLastWriteTime(fileToChange, DateTime.Now);
await _app.HasRestarted();
}
catch
{
// retry
File.SetLastWriteTime(fileToChange, DateTime.Now);
await _app.HasRestarted();
}
var pid2 = await _app.GetProcessId();
Assert.NotEqual(pid, pid2);
await _app.HasExited(); // process should exit after run

View File

@ -28,23 +28,25 @@ namespace Microsoft.DotNet.Watcher.Tools.Tests
{
_tempDir
.WithCSharpProject("testproj")
.WithTargetFrameworks("netcoreapp1.0")
.WithTargetFrameworks("netcoreapp2.2")
.Dir()
.WithFile("Program.cs")
.Create();
var stdout = new StringBuilder();
_console.Out = new StringWriter(stdout);
var program = new Program(_console, _tempDir.Root)
.RunAsync(new[] { "run" });
var output = new StringBuilder();
_console.Error = _console.Out = new StringWriter(output);
using (var app = new Program(_console, _tempDir.Root))
{
var run = app.RunAsync(new[] { "run" });
await _console.CancelKeyPressSubscribed.TimeoutAfter(TimeSpan.FromSeconds(30));
_console.ConsoleCancelKey();
await _console.CancelKeyPressSubscribed.TimeoutAfter(TimeSpan.FromSeconds(30));
_console.ConsoleCancelKey();
var exitCode = await program.TimeoutAfter(TimeSpan.FromSeconds(30));
var exitCode = await run.TimeoutAfter(TimeSpan.FromSeconds(30));
Assert.Contains("Shutdown requested. Press Ctrl+C again to force exit.", stdout.ToString());
Assert.Equal(0, exitCode);
Assert.Contains("Shutdown requested. Press Ctrl+C again to force exit.", output.ToString());
Assert.Equal(0, exitCode);
}
}
public void Dispose()

View File

@ -1,8 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. 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.Diagnostics;
using System.IO;
using System.Linq;
@ -12,7 +11,6 @@ using System.Threading.Tasks;
using System.Xml.Linq;
using Microsoft.Extensions.CommandLineUtils;
using Microsoft.Extensions.Internal;
using Microsoft.Extensions.Tools.Internal;
using Xunit.Abstractions;
namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests

View File

@ -47,7 +47,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
public async Task HasExited()
{
await Process.GetOutputLineAsync(ExitingMessage, DefaultMessageTimeOut);
await Process.GetOutputLineAsync(WatchExitedMessage, DefaultMessageTimeOut);
await Process.GetOutputLineStartsWithAsync(WatchExitedMessage, DefaultMessageTimeOut);
}
public async Task IsWaitingForFileChange()

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<OutputType>exe</OutputType>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<OutputType>exe</OutputType>
<EnableDefaultCompileItems>false</EnableDefaultCompileItems>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>

View File

@ -9,7 +9,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

View File

@ -13,6 +13,7 @@ namespace KitchenSink
Console.WriteLine("Started");
Console.WriteLine("PID = " + Process.GetCurrentProcess().Id);
Console.WriteLine("DOTNET_WATCH = " + Environment.GetEnvironmentVariable("DOTNET_WATCH"));
Console.WriteLine("DOTNET_WATCH_ITERATION = " + Environment.GetEnvironmentVariable("DOTNET_WATCH_ITERATION"));
}
}
}

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<OutputType>exe</OutputType>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

View File

@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netcoreapp2.1</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<AssemblyName>Microsoft.DotNet.Watcher.Tools.Tests</AssemblyName>
<DefaultItemExcludes>$(DefaultItemExcludes);TestProjects\**\*</DefaultItemExcludes>
</PropertyGroup>

View File

@ -1,4 +1,4 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
@ -8,14 +8,13 @@ namespace Microsoft.Extensions.Tools.Internal
public static class CliContext
{
/// <summary>
/// dotnet --verbose subcommand
/// dotnet -d|--diagnostics subcommand
/// </summary>
/// <returns></returns>
public static bool IsGlobalVerbose()
{
bool globalVerbose;
bool.TryParse(Environment.GetEnvironmentVariable("DOTNET_CLI_CONTEXT_VERBOSE"), out globalVerbose);
bool.TryParse(Environment.GetEnvironmentVariable("DOTNET_CLI_CONTEXT_VERBOSE"), out bool globalVerbose);
return globalVerbose;
}
}
}
}