|
|
@@ -6,6 +6,8 @@ using System.Collections.Generic;
|
|
|
using System.IO;
|
|
|
using System.Linq;
|
|
|
using System.Threading;
|
|
|
+using System.Threading.Tasks;
|
|
|
+using Microsoft.AspNetCore.Testing;
|
|
|
using Microsoft.DotNet.Watcher.Internal;
|
|
|
using Xunit;
|
|
|
using Xunit.Abstractions;
|
|
|
@@ -19,32 +21,33 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
_output = output;
|
|
|
}
|
|
|
|
|
|
- private const int DefaultTimeout = 10 * 1000; // 10 sec
|
|
|
+ private readonly TimeSpan DefaultTimeout = TimeSpan.FromSeconds(60);
|
|
|
+ private readonly TimeSpan NegativeTimeout = TimeSpan.FromSeconds(5);
|
|
|
private readonly ITestOutputHelper _output;
|
|
|
|
|
|
[Theory]
|
|
|
[InlineData(true)]
|
|
|
[InlineData(false)]
|
|
|
- public void NewFile(bool usePolling)
|
|
|
+ public async Task NewFile(bool usePolling)
|
|
|
{
|
|
|
- UsingTempDirectory(dir =>
|
|
|
+ await UsingTempDirectory(async dir =>
|
|
|
{
|
|
|
- using (var changedEv = new ManualResetEvent(false))
|
|
|
using (var watcher = FileWatcherFactory.CreateWatcher(dir, usePolling))
|
|
|
{
|
|
|
+ var changedEv = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
var filesChanged = new HashSet<string>();
|
|
|
|
|
|
watcher.OnFileChange += (_, f) =>
|
|
|
{
|
|
|
filesChanged.Add(f);
|
|
|
- changedEv.Set();
|
|
|
+ changedEv.TrySetResult(0);
|
|
|
};
|
|
|
watcher.EnableRaisingEvents = true;
|
|
|
|
|
|
var testFileFullPath = Path.Combine(dir, "foo");
|
|
|
File.WriteAllText(testFileFullPath, string.Empty);
|
|
|
|
|
|
- Assert.True(changedEv.WaitOne(DefaultTimeout));
|
|
|
+ await changedEv.Task.TimeoutAfter(DefaultTimeout);
|
|
|
Assert.Equal(testFileFullPath, filesChanged.Single());
|
|
|
}
|
|
|
});
|
|
|
@@ -53,16 +56,16 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
[Theory]
|
|
|
[InlineData(true)]
|
|
|
[InlineData(false)]
|
|
|
- public void ChangeFile(bool usePolling)
|
|
|
+ public async Task ChangeFile(bool usePolling)
|
|
|
{
|
|
|
- UsingTempDirectory(dir =>
|
|
|
+ await UsingTempDirectory(async dir =>
|
|
|
{
|
|
|
var testFileFullPath = Path.Combine(dir, "foo");
|
|
|
File.WriteAllText(testFileFullPath, string.Empty);
|
|
|
|
|
|
- using (var changedEv = new ManualResetEvent(false))
|
|
|
using (var watcher = FileWatcherFactory.CreateWatcher(dir, usePolling))
|
|
|
{
|
|
|
+ var changedEv = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
var filesChanged = new HashSet<string>();
|
|
|
|
|
|
EventHandler<string> handler = null;
|
|
|
@@ -72,7 +75,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
watcher.OnFileChange -= handler;
|
|
|
|
|
|
filesChanged.Add(f);
|
|
|
- changedEv.Set();
|
|
|
+ changedEv.TrySetResult(0);
|
|
|
};
|
|
|
|
|
|
watcher.OnFileChange += handler;
|
|
|
@@ -81,10 +84,10 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
// 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);
|
|
|
+ await Task.Delay(1000);
|
|
|
File.WriteAllText(testFileFullPath, string.Empty);
|
|
|
|
|
|
- Assert.True(changedEv.WaitOne(DefaultTimeout));
|
|
|
+ await changedEv.Task.TimeoutAfter(DefaultTimeout);
|
|
|
Assert.Equal(testFileFullPath, filesChanged.Single());
|
|
|
}
|
|
|
});
|
|
|
@@ -93,18 +96,18 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
[Theory]
|
|
|
[InlineData(true)]
|
|
|
[InlineData(false)]
|
|
|
- public void MoveFile(bool usePolling)
|
|
|
+ public async Task MoveFile(bool usePolling)
|
|
|
{
|
|
|
- UsingTempDirectory(dir =>
|
|
|
+ await UsingTempDirectory(async dir =>
|
|
|
{
|
|
|
var srcFile = Path.Combine(dir, "foo");
|
|
|
var dstFile = Path.Combine(dir, "foo2");
|
|
|
|
|
|
File.WriteAllText(srcFile, string.Empty);
|
|
|
|
|
|
- using (var changedEv = new ManualResetEvent(false))
|
|
|
using (var watcher = FileWatcherFactory.CreateWatcher(dir, usePolling))
|
|
|
{
|
|
|
+ var changedEv = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
var filesChanged = new HashSet<string>();
|
|
|
|
|
|
EventHandler<string> handler = null;
|
|
|
@@ -117,7 +120,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
watcher.EnableRaisingEvents = false;
|
|
|
watcher.OnFileChange -= handler;
|
|
|
|
|
|
- changedEv.Set();
|
|
|
+ changedEv.TrySetResult(0);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -126,7 +129,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
|
|
|
File.Move(srcFile, dstFile);
|
|
|
|
|
|
- Assert.True(changedEv.WaitOne(DefaultTimeout));
|
|
|
+ await changedEv.Task.TimeoutAfter(DefaultTimeout);
|
|
|
Assert.Contains(srcFile, filesChanged);
|
|
|
Assert.Contains(dstFile, filesChanged);
|
|
|
}
|
|
|
@@ -134,9 +137,9 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
}
|
|
|
|
|
|
[Fact]
|
|
|
- public void FileInSubdirectory()
|
|
|
+ public async Task FileInSubdirectory()
|
|
|
{
|
|
|
- UsingTempDirectory(dir =>
|
|
|
+ await UsingTempDirectory(async dir =>
|
|
|
{
|
|
|
var subdir = Path.Combine(dir, "subdir");
|
|
|
Directory.CreateDirectory(subdir);
|
|
|
@@ -144,9 +147,9 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
var testFileFullPath = Path.Combine(subdir, "foo");
|
|
|
File.WriteAllText(testFileFullPath, string.Empty);
|
|
|
|
|
|
- using (var changedEv = new ManualResetEvent(false))
|
|
|
using (var watcher = FileWatcherFactory.CreateWatcher(dir, true))
|
|
|
{
|
|
|
+ var changedEv = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
var filesChanged = new HashSet<string>();
|
|
|
|
|
|
EventHandler<string> handler = null;
|
|
|
@@ -158,7 +161,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
{
|
|
|
watcher.EnableRaisingEvents = false;
|
|
|
watcher.OnFileChange -= handler;
|
|
|
- changedEv.Set();
|
|
|
+ changedEv.TrySetResult(0);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -168,10 +171,10 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
// 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);
|
|
|
+ await Task.Delay(1000);
|
|
|
File.WriteAllText(testFileFullPath, string.Empty);
|
|
|
|
|
|
- Assert.True(changedEv.WaitOne(DefaultTimeout));
|
|
|
+ await changedEv.Task.TimeoutAfter(DefaultTimeout);
|
|
|
Assert.Contains(subdir, filesChanged);
|
|
|
Assert.Contains(testFileFullPath, filesChanged);
|
|
|
}
|
|
|
@@ -181,14 +184,14 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
[Theory]
|
|
|
[InlineData(true)]
|
|
|
[InlineData(false)]
|
|
|
- public void NoNotificationIfDisabled(bool usePolling)
|
|
|
+ public async Task NoNotificationIfDisabled(bool usePolling)
|
|
|
{
|
|
|
- UsingTempDirectory(dir =>
|
|
|
+ await UsingTempDirectory(async dir =>
|
|
|
{
|
|
|
using (var watcher = FileWatcherFactory.CreateWatcher(dir, usePolling))
|
|
|
- using (var changedEv = new ManualResetEvent(false))
|
|
|
{
|
|
|
- watcher.OnFileChange += (_, f) => changedEv.Set();
|
|
|
+ var changedEv = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
+ watcher.OnFileChange += (_, f) => changedEv.TrySetResult(0);
|
|
|
|
|
|
// Disable
|
|
|
watcher.EnableRaisingEvents = false;
|
|
|
@@ -198,10 +201,10 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
// 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);
|
|
|
+ await Task.Delay(1000);
|
|
|
File.WriteAllText(testFileFullPath, string.Empty);
|
|
|
|
|
|
- Assert.False(changedEv.WaitOne(DefaultTimeout / 2));
|
|
|
+ await Assert.ThrowsAsync<TimeoutException>(() => changedEv.Task.TimeoutAfter(NegativeTimeout));
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
@@ -209,37 +212,35 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
[Theory]
|
|
|
[InlineData(true)]
|
|
|
[InlineData(false)]
|
|
|
- public void DisposedNoEvents(bool usePolling)
|
|
|
+ public async Task DisposedNoEvents(bool usePolling)
|
|
|
{
|
|
|
- UsingTempDirectory(dir =>
|
|
|
+ await UsingTempDirectory(async dir =>
|
|
|
{
|
|
|
- using (var changedEv = new ManualResetEvent(false))
|
|
|
+ var changedEv = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
+ using (var watcher = FileWatcherFactory.CreateWatcher(dir, usePolling))
|
|
|
{
|
|
|
- using (var watcher = FileWatcherFactory.CreateWatcher(dir, usePolling))
|
|
|
- {
|
|
|
- watcher.OnFileChange += (_, f) => changedEv.Set();
|
|
|
- watcher.EnableRaisingEvents = true;
|
|
|
- }
|
|
|
+ watcher.OnFileChange += (_, f) => changedEv.TrySetResult(0);
|
|
|
+ watcher.EnableRaisingEvents = true;
|
|
|
+ }
|
|
|
|
|
|
- var testFileFullPath = Path.Combine(dir, "foo");
|
|
|
+ var testFileFullPath = Path.Combine(dir, "foo");
|
|
|
|
|
|
- // 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.WriteAllText(testFileFullPath, string.Empty);
|
|
|
+ // 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
|
|
|
+ await Task.Delay(1000);
|
|
|
+ File.WriteAllText(testFileFullPath, string.Empty);
|
|
|
|
|
|
- Assert.False(changedEv.WaitOne(DefaultTimeout / 2));
|
|
|
- }
|
|
|
+ await Assert.ThrowsAsync<TimeoutException>(() => changedEv.Task.TimeoutAfter(NegativeTimeout));
|
|
|
});
|
|
|
}
|
|
|
|
|
|
[Theory]
|
|
|
[InlineData(true)]
|
|
|
[InlineData(false)]
|
|
|
- public void MultipleFiles(bool usePolling)
|
|
|
+ public async Task MultipleFiles(bool usePolling)
|
|
|
{
|
|
|
- UsingTempDirectory(dir =>
|
|
|
+ await UsingTempDirectory(async dir =>
|
|
|
{
|
|
|
File.WriteAllText(Path.Combine(dir, "foo1"), string.Empty);
|
|
|
File.WriteAllText(Path.Combine(dir, "foo2"), string.Empty);
|
|
|
@@ -249,9 +250,9 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
|
|
|
var testFileFullPath = Path.Combine(dir, "foo3");
|
|
|
|
|
|
- using (var changedEv = new ManualResetEvent(false))
|
|
|
using (var watcher = FileWatcherFactory.CreateWatcher(dir, usePolling))
|
|
|
{
|
|
|
+ var changedEv = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
var filesChanged = new HashSet<string>();
|
|
|
|
|
|
EventHandler<string> handler = null;
|
|
|
@@ -260,7 +261,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
watcher.EnableRaisingEvents = false;
|
|
|
watcher.OnFileChange -= handler;
|
|
|
filesChanged.Add(f);
|
|
|
- changedEv.Set();
|
|
|
+ changedEv.TrySetResult(0);
|
|
|
};
|
|
|
|
|
|
watcher.OnFileChange += handler;
|
|
|
@@ -269,11 +270,11 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
// 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);
|
|
|
+ await Task.Delay(1000);
|
|
|
|
|
|
File.WriteAllText(testFileFullPath, string.Empty);
|
|
|
|
|
|
- Assert.True(changedEv.WaitOne(DefaultTimeout));
|
|
|
+ await changedEv.Task.TimeoutAfter(DefaultTimeout);
|
|
|
Assert.Equal(testFileFullPath, filesChanged.Single());
|
|
|
}
|
|
|
});
|
|
|
@@ -282,11 +283,11 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
[Theory]
|
|
|
[InlineData(true)]
|
|
|
[InlineData(false)]
|
|
|
- public void MultipleTriggers(bool usePolling)
|
|
|
+ public async Task MultipleTriggers(bool usePolling)
|
|
|
{
|
|
|
var filesChanged = new HashSet<string>();
|
|
|
|
|
|
- UsingTempDirectory(dir =>
|
|
|
+ await UsingTempDirectory(async dir =>
|
|
|
{
|
|
|
using (var watcher = FileWatcherFactory.CreateWatcher(dir, usePolling))
|
|
|
{
|
|
|
@@ -294,7 +295,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
|
|
|
for (var i = 0; i < 5; i++)
|
|
|
{
|
|
|
- AssertFileChangeRaisesEvent(dir, watcher);
|
|
|
+ await AssertFileChangeRaisesEvent(dir, watcher);
|
|
|
}
|
|
|
|
|
|
watcher.EnableRaisingEvents = false;
|
|
|
@@ -302,55 +303,53 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- private void AssertFileChangeRaisesEvent(string directory, IFileSystemWatcher watcher)
|
|
|
+ private async Task AssertFileChangeRaisesEvent(string directory, IFileSystemWatcher watcher)
|
|
|
{
|
|
|
- using (var semaphoreSlim = new SemaphoreSlim(0))
|
|
|
+ var changedEv = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
+ var expectedPath = Path.Combine(directory, Path.GetRandomFileName());
|
|
|
+ EventHandler<string> handler = (object _, string f) =>
|
|
|
{
|
|
|
- var expectedPath = Path.Combine(directory, Path.GetRandomFileName());
|
|
|
- EventHandler<string> handler = (object _, string f) =>
|
|
|
+ _output.WriteLine("File changed: " + f);
|
|
|
+ try
|
|
|
{
|
|
|
- _output.WriteLine("File changed: " + f);
|
|
|
- try
|
|
|
- {
|
|
|
- if (string.Equals(f, expectedPath, StringComparison.OrdinalIgnoreCase))
|
|
|
- {
|
|
|
- semaphoreSlim.Release();
|
|
|
- }
|
|
|
- }
|
|
|
- catch (ObjectDisposedException)
|
|
|
+ if (string.Equals(f, expectedPath, StringComparison.OrdinalIgnoreCase))
|
|
|
{
|
|
|
- // 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.
|
|
|
+ changedEv.TrySetResult(0);
|
|
|
}
|
|
|
- };
|
|
|
-
|
|
|
- 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
|
|
|
+ catch (ObjectDisposedException)
|
|
|
{
|
|
|
- watcher.OnFileChange -= handler;
|
|
|
+ // 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
|
|
|
+ {
|
|
|
+ // 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
|
|
|
+ await Task.Delay(1000);
|
|
|
+ File.AppendAllText(expectedPath, " ");
|
|
|
+ await changedEv.Task.TimeoutAfter(DefaultTimeout);
|
|
|
+ }
|
|
|
+ finally
|
|
|
+ {
|
|
|
+ watcher.OnFileChange -= handler;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
[Theory]
|
|
|
[InlineData(true)]
|
|
|
[InlineData(false)]
|
|
|
- public void DeleteSubfolder(bool usePolling)
|
|
|
+ public async Task DeleteSubfolder(bool usePolling)
|
|
|
{
|
|
|
- UsingTempDirectory(dir =>
|
|
|
+ await UsingTempDirectory(async dir =>
|
|
|
{
|
|
|
var subdir = Path.Combine(dir, "subdir");
|
|
|
Directory.CreateDirectory(subdir);
|
|
|
@@ -363,9 +362,9 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
File.WriteAllText(f2, string.Empty);
|
|
|
File.WriteAllText(f3, string.Empty);
|
|
|
|
|
|
- using (var changedEv = new AutoResetEvent(false))
|
|
|
using (var watcher = FileWatcherFactory.CreateWatcher(dir, usePolling))
|
|
|
{
|
|
|
+ var changedEv = new TaskCompletionSource<int>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
var filesChanged = new HashSet<string>();
|
|
|
|
|
|
EventHandler<string> handler = null;
|
|
|
@@ -377,7 +376,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
{
|
|
|
watcher.EnableRaisingEvents = false;
|
|
|
watcher.OnFileChange -= handler;
|
|
|
- changedEv.Set();
|
|
|
+ changedEv.TrySetResult(0);
|
|
|
}
|
|
|
};
|
|
|
|
|
|
@@ -386,7 +385,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
|
|
|
Directory.Delete(subdir, recursive: true);
|
|
|
|
|
|
- Assert.True(changedEv.WaitOne(DefaultTimeout));
|
|
|
+ await changedEv.Task.TimeoutAfter(DefaultTimeout);
|
|
|
|
|
|
Assert.Contains(f1, filesChanged);
|
|
|
Assert.Contains(f2, filesChanged);
|
|
|
@@ -396,7 +395,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- private static void UsingTempDirectory(Action<string> action)
|
|
|
+ private static async Task UsingTempDirectory(Func<string, Task> func)
|
|
|
{
|
|
|
var tempFolder = Path.Combine(Path.GetTempPath(), $"{nameof(FileWatcherTests)}-{Guid.NewGuid().ToString("N")}");
|
|
|
if (Directory.Exists(tempFolder))
|
|
|
@@ -408,7 +407,7 @@ namespace Microsoft.DotNet.Watcher.Tools.FunctionalTests
|
|
|
|
|
|
try
|
|
|
{
|
|
|
- action(tempFolder);
|
|
|
+ await func(tempFolder);
|
|
|
}
|
|
|
finally
|
|
|
{
|