Browse Source

Fix flaky process output reading for dotnet-watch tests (#7173)

BrennanConroy 7 years ago
parent
commit
2365dced43

+ 4 - 3
build/RepositoryBuild.targets

@@ -19,7 +19,8 @@
         <Repository>%(RepositoryBuildOrder.Identity)</Repository>
         <AdditionalProperties>
           RepositoryToBuild=%(RepositoryBuildOrder.Identity);
-          BuildRepositoryRoot=$([MSBuild]::NormalizeDirectory(%(RepositoryBuildOrder.RootPath)))
+          BuildRepositoryRoot=$([MSBuild]::NormalizeDirectory(%(RepositoryBuildOrder.RootPath)));
+          SkipTests=%(RepositoryBuildOrder.SkipTests)
         </AdditionalProperties>
       </BatchedRepository>
     </ItemGroup>
@@ -171,7 +172,7 @@
 
     <Message Text="============ Testing $(RepositoryToBuild) ============" Importance="High" />
 
-    <Exec Condition="'$(SkipTestsDueToMissingSharedFx)' != 'true' "
+    <Exec Condition="'$(SkipTestsDueToMissingSharedFx)' != 'true' AND '$(SkipTests)' != 'true'"
       Command="./$(_BuildScriptToExecute) -Path $(BuildRepositoryRoot) $(BuildArguments)"
       IgnoreStandardErrorWarningFormat="true"
       WorkingDirectory="$(RepositoryRoot)"
@@ -182,7 +183,7 @@
     <CallTarget Targets="_RestoreOriginalRepoLockFile" />
 
     <ItemGroup>
-      <RepositoryTestResult Update="$(RepositoryToBuild)" Success="true" Condition="'$(TestExitCode)' == '0' OR '$(SkipTestsDueToMissingSharedFx)' == 'true' " />
+      <RepositoryTestResult Update="$(RepositoryToBuild)" Success="true" Condition="'$(TestExitCode)' == '0' OR '$(SkipTestsDueToMissingSharedFx)' == 'true' OR '$(SkipTests)' == 'true' " />
     </ItemGroup>
 
     <Message Text="============ Done testing $(RepositoryToBuild) ============" Importance="High" />

+ 1 - 1
build/buildorder.props

@@ -7,7 +7,7 @@
   </ItemDefinitionGroup>
 
   <ItemGroup>
-    <RepositoryBuildOrder Include="EntityFrameworkCore" Order="8" />
+    <RepositoryBuildOrder Include="EntityFrameworkCore" Order="8" SkipTests="true" />
     <RepositoryBuildOrder Include="Templating" Order="17" RootPath="$(RepositoryRoot)src\Templating\" />
   </ItemGroup>
 </Project>

+ 19 - 14
src/Tools/dotnet-watch/src/Internal/ProcessRunner.cs

@@ -37,17 +37,31 @@ namespace Microsoft.DotNet.Watcher.Internal
             {
                 cancellationToken.Register(() => processState.TryKill());
 
+                process.OutputDataReceived += (_, a) =>
+                {
+                    if (!string.IsNullOrEmpty(a.Data))
+                    {
+                        processSpec.OutputCapture.AddLine(a.Data);
+                    }
+                };
+                process.ErrorDataReceived += (_, a) =>
+                {
+                    if (!string.IsNullOrEmpty(a.Data))
+                    {
+                        processSpec.OutputCapture.AddLine(a.Data);
+                    }
+                };
+
                 stopwatch.Start();
                 process.Start();
+
                 _reporter.Verbose($"Started '{processSpec.Executable}' with process id {process.Id}");
 
                 if (processSpec.IsOutputCaptured)
                 {
-                    await Task.WhenAll(
-                        processState.Task,
-                        ConsumeStreamAsync(process.StandardOutput, processSpec.OutputCapture.AddLine),
-                        ConsumeStreamAsync(process.StandardError, processSpec.OutputCapture.AddLine)
-                    );
+                    process.BeginErrorReadLine();
+                    process.BeginOutputReadLine();
+                    await processState.Task;
                 }
                 else
                 {
@@ -86,15 +100,6 @@ namespace Microsoft.DotNet.Watcher.Internal
             return process;
         }
 
-        private static async Task ConsumeStreamAsync(StreamReader reader, Action<string> consume)
-        {
-            string line;
-            while ((line = await reader.ReadLineAsync().ConfigureAwait(false)) != null)
-            {
-                consume?.Invoke(line);
-            }
-        }
-
         private class ProcessState : IDisposable
         {
             private readonly IReporter _reporter;

+ 2 - 13
src/Tools/dotnet-watch/test/DotNetWatcherTests.cs

@@ -49,19 +49,8 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
 
                 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();
-                }
+                File.SetLastWriteTime(source, DateTime.Now);
+                await _app.HasRestarted(TimeSpan.FromMinutes(1));
             }
         }
 

+ 7 - 0
src/Tools/dotnet-watch/test/Properties/AssemblyInfo.cs

@@ -0,0 +1,7 @@
+// 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 Microsoft.AspNetCore.Testing.xunit;
+using Xunit;
+
+[assembly: CollectionBehavior(DisableTestParallelization = true)]

+ 6 - 3
src/Tools/dotnet-watch/test/Scenario/WatchableApp.cs

@@ -42,7 +42,10 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
         public string SourceDirectory { get; }
 
         public Task HasRestarted()
-            => Process.GetOutputLineAsync(StartedMessage, DefaultMessageTimeOut);
+            => HasRestarted(DefaultMessageTimeOut);
+
+        public Task HasRestarted(TimeSpan timeout)
+            => Process.GetOutputLineAsync(StartedMessage, timeout);
 
         public async Task HasExited()
         {
@@ -50,9 +53,9 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
             await Process.GetOutputLineStartsWithAsync(WatchExitedMessage, DefaultMessageTimeOut);
         }
 
-        public async Task IsWaitingForFileChange()
+        public Task IsWaitingForFileChange()
         {
-            await Process.GetOutputLineStartsWithAsync(WaitingForFileChangeMessage, DefaultMessageTimeOut);
+            return Process.GetOutputLineStartsWithAsync(WaitingForFileChangeMessage, DefaultMessageTimeOut);
         }
 
         public bool UsePollingWatcher { get; set; }