// If file is a link, and link target does not exists, return DateTime.MinValue
// since the link's LastWriteTimeUtc doesn't convey anything for this scenario.
// If file is not a link, return null to inform the caller that file is not a link.
- public static DateTime? GetFileLinkTargetLastWriteTimeUtc(FileInfo fileInfo)
+ public static DateTime? GetFileLinkTargetLastWriteTimeUtc(FileInfo fileInfo, bool isSecondTry = false)
{
#if NETCOREAPP
Debug.Assert(fileInfo.Exists);
if (fileInfo.LinkTarget != null)
{
- FileSystemInfo targetInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true);
- if (targetInfo.Exists)
+ try
{
- return targetInfo.LastWriteTimeUtc;
+ FileSystemInfo targetInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true);
+ if (targetInfo.Exists)
+ {
+ return targetInfo.LastWriteTimeUtc;
+ }
+ }
+ catch (FileNotFoundException)
+ {
+ // The file ceased to exist between LinkTarget and ResolveLinkTarget.
+ // Try one more time, if it fails again just give up.
+ if (!isSecondTry)
+ {
+ GetFileLinkTargetLastWriteTimeUtc(fileInfo, isSecondTry: true);
+ }
}
return DateTime.MinValue;
[Theory]
[InlineData(false)]
[InlineData(true)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)]
public async Task UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard)
{
// Arrange
File.WriteAllText(filePath, "v1.1");
using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true };
- IChangeToken token = provider.Watch(useWildcard ? "*" : fileName);
+ IChangeToken changeToken = provider.Watch(useWildcard ? "*" : fileName);
- var tcs = new TaskCompletionSource<object>();
- token.RegisterChangeCallback(_ => { tcs.TrySetResult(null); }, null);
+ var tcs = new TaskCompletionSource<bool>();
+ changeToken.RegisterChangeCallback(_ => { tcs.TrySetResult(true); }, null);
+
+ var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
+ cts.Token.Register(() => tcs.TrySetCanceled());
// Act
await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951.
File.WriteAllText(filePath, "v1.2");
// Assert
- Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)),
+ Assert.True(await tcs.Task,
$"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}");
}
[Theory]
[InlineData(false)]
[InlineData(true)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)]
- public void UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard)
+ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard)
{
// Arrange
using var root = new DisposableFileSystem();
string filter = useWildcard ? "*" : fileName;
using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true };
- IChangeToken token = provider.Watch(filter);
+ IChangeToken changeToken = provider.Watch(filter);
+
+ var tcs = new TaskCompletionSource<bool>();
+ changeToken.RegisterChangeCallback(_ => { tcs.TrySetResult(true); }, null);
- var tcs = new TaskCompletionSource<object>();
- token.RegisterChangeCallback(_ => { tcs.TrySetResult(null); }, null);
+ var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
+ cts.Token.Register(() => tcs.TrySetCanceled());
// Act
File.Delete(filePath);
// Assert
- Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)),
+ Assert.True(await tcs.Task,
$"Change event was not raised - current time: {DateTime.UtcNow:O}, file Exists: {File.Exists(filePath)}.");
}
using System;
using System.IO;
+using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Primitives;
using Xunit;
[Theory]
[InlineData(false)]
[InlineData(true)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)]
public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool useWildcard)
{
// Arrange
using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true };
IChangeToken token = provider.Watch(useWildcard ? "*" : linkName);
- var tcs = new TaskCompletionSource();
- token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null);
+ var tcs = new TaskCompletionSource<bool>();
+ token.RegisterChangeCallback(_ => { tcs.TrySetResult(true); }, null);
+
+ var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
+ cts.Token.Register(() => tcs.TrySetCanceled());
// Act
await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951.
File.WriteAllText(filePath, "v1.2");
// Assert
- Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)),
+ Assert.True(await tcs.Task,
$"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}.");
}
[Theory]
[InlineData(false)]
[InlineData(true)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)]
- public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetNotExists(bool useWildcard)
+ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetNotExists(bool useWildcard)
{
// Arrange
using var rootOfLink = new DisposableFileSystem();
IChangeToken token = provider.Watch(useWildcard ? "*" : linkName);
var tcs = new TaskCompletionSource();
- token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null);
+ token.RegisterChangeCallback(_ => { Assert.True(false, "Change event was raised when it was not expected."); }, null);
- // Assert
- Assert.False(tcs.Task.Wait(TimeSpan.FromSeconds(30)),
- "Change event was raised when it was not expected.");
+ var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
+ cts.Token.Register(() => tcs.TrySetCanceled());
+
+ await Assert.ThrowsAsync<TaskCanceledException>(() => tcs.Task);
}
[Theory]
[InlineData(false, true)]
[InlineData(true, false)]
[InlineData(true, true)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)]
public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetChanged(bool useWildcard, bool linkWasBroken)
{
// Arrange
using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true };
IChangeToken token = provider.Watch(filter);
- var tcs = new TaskCompletionSource();
- token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null);
+ var tcs = new TaskCompletionSource<bool>();
+ token.RegisterChangeCallback(_ => { tcs.TrySetResult(true); }, null);
+
+ var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
+ cts.Token.Register(() => tcs.TrySetCanceled());
// Act - Change link target to file 2.
File.Delete(linkPath);
File.CreateSymbolicLink(linkPath, file2Path);
// Assert - It should report the change regardless of the timestamp being older.
- Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)),
+ Assert.True(await tcs.Task,
$"Change event was not raised - current time: {DateTime.UtcNow:O}, file1 LastWriteTimeUtc: {File.GetLastWriteTimeUtc(file1Path):O}, file2 LastWriteTime: {File.GetLastWriteTimeUtc(file2Path):O}.");
}
[Theory]
[InlineData(false)]
[InlineData(true)]
- [ActiveIssue("https://github.com/dotnet/runtime/issues/56190", TestPlatforms.AnyUnix)]
- public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetDeleted(bool useWildcard)
+ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetDeleted(bool useWildcard)
{
// Arrange
using var rootOfFile = new DisposableFileSystem();
using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true };
IChangeToken token = provider.Watch(filter);
- var tcs = new TaskCompletionSource();
- token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null);
+ var tcs = new TaskCompletionSource<bool>();
+ token.RegisterChangeCallback(_ => { tcs.TrySetResult(true); }, null);
+
+ var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
+ cts.Token.Register(() => tcs.TrySetCanceled());
// Act
File.Delete(linkPath);
// Assert
- Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)),
+ Assert.True(await tcs.Task,
$"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}.");
}
}