Merge aspnet/DotNetTools release/2.2
This commit is contained in:
commit
0782a9dfa9
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<TargetFramework>netcoreapp2.2</TargetFramework>
|
||||
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
|
||||
</PropertyGroup>
|
||||
|
||||
|
|
|
|||
|
|
@ -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"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue