From f5157108d084bc976f1387a5a40d712de6ad8eac Mon Sep 17 00:00:00 2001 From: David Cantu Date: Wed, 14 Jul 2021 10:55:59 -0700 Subject: [PATCH 01/17] Relax LinkTarget so it always returns null when steps on an error --- .../src/System/IO/FileSystem.Windows.cs | 22 ++++++++++++------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs index 5f88f53a7c539f..698e3bc57245e8 100644 --- a/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/IO/FileSystem.Windows.cs @@ -427,20 +427,20 @@ internal static void CreateSymbolicLink(string path, string pathToTarget, bool i { string? targetPath = returnFinalTarget ? GetFinalLinkTarget(linkPath, isDirectory) : - GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: true, returnFullPath: true); + GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: true, returnFullPath: true); return targetPath == null ? null : isDirectory ? new DirectoryInfo(targetPath) : new FileInfo(targetPath); } internal static string? GetLinkTarget(string linkPath, bool isDirectory) - => GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: false, returnFullPath: false); + => GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: false, returnFullPath: false); /// /// Gets reparse point information associated to . /// /// The immediate link target, absolute or relative or null if the file is not a supported link. - internal static unsafe string? GetImmediateLinkTarget(string linkPath, bool isDirectory, bool throwOnUnreachable, bool returnFullPath) + internal static unsafe string? GetImmediateLinkTarget(string linkPath, bool isDirectory, bool throwOnError, bool returnFullPath) { using SafeFileHandle handle = OpenSafeFileHandle(linkPath, Interop.Kernel32.FileOperations.FILE_FLAG_BACKUP_SEMANTICS | @@ -448,13 +448,12 @@ internal static void CreateSymbolicLink(string path, string pathToTarget, bool i if (handle.IsInvalid) { - int error = Marshal.GetLastWin32Error(); - - if (!throwOnUnreachable && IsPathUnreachableError(error)) + if (!throwOnError) { return null; } + int error = Marshal.GetLastWin32Error(); // File not found doesn't make much sense coming from a directory. if (isDirectory && error == Interop.Errors.ERROR_FILE_NOT_FOUND) { @@ -479,6 +478,11 @@ internal static void CreateSymbolicLink(string path, string pathToTarget, bool i if (!success) { + if (!throwOnError) + { + return null; + } + int error = Marshal.GetLastWin32Error(); // The file or directory is not a reparse point. if (error == Interop.Errors.ERROR_NOT_A_REPARSE_POINT) @@ -600,13 +604,15 @@ uint GetFinalPathNameByHandle(SafeFileHandle handle, char[] buffer) { // Since all these paths will be passed to CreateFile, which takes a string anyway, it is pointless to use span. // I am not sure if it's possible to change CreateFile's param to ROS and avoid all these allocations. - string? current = GetImmediateLinkTarget(linkPath, isDirectory, throwOnUnreachable: false, returnFullPath: true); + + // We don't throw on error since we already did all the proper validations before. + string? current = GetImmediateLinkTarget(linkPath, isDirectory, throwOnError: false, returnFullPath: true); string? prev = null; while (current != null) { prev = current; - current = GetImmediateLinkTarget(current, isDirectory, throwOnUnreachable: false, returnFullPath: true); + current = GetImmediateLinkTarget(current, isDirectory, throwOnError: false, returnFullPath: true); } return prev; From 08233bac350b19ab2501deafbb3d6683621fe1bd Mon Sep 17 00:00:00 2001 From: David Cantu Date: Wed, 14 Jul 2021 11:17:50 -0700 Subject: [PATCH 02/17] Make polling use the symbolic link target's LastWriteTime --- ...tensions.FileProviders.Abstractions.csproj | 5 +- ...tensions.FileProviders.Abstractions.csproj | 7 ++- .../Directory.Build.props | 1 + ...t.Extensions.FileProviders.Physical.csproj | 5 +- .../src/Internal/FileSystemInfoHelper.cs | 24 +++++++ ...t.Extensions.FileProviders.Physical.csproj | 15 ++++- .../src/PollingFileChangeToken.cs | 2 +- .../src/PollingWildCardChangeToken.cs | 3 +- .../tests/PhysicalFileProviderTests.cs | 62 +++++++++++++++++++ ...osoft.Extensions.FileSystemGlobbing.csproj | 6 +- ...osoft.Extensions.FileSystemGlobbing.csproj | 11 +++- 11 files changed, 130 insertions(+), 11 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj index 32239798f1f1e2..143dcc93cfb9dc 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj @@ -1,6 +1,6 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 @@ -8,4 +8,7 @@ + + + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj index 43164c743a994f..07289f58670e5b 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/src/Microsoft.Extensions.FileProviders.Abstractions.csproj @@ -2,7 +2,7 @@ Microsoft.Extensions.FileProviders - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 true Abstractions of files and directories. @@ -21,4 +21,9 @@ Microsoft.Extensions.FileProviders.IFileProvider + + + + + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/Directory.Build.props b/src/libraries/Microsoft.Extensions.FileProviders.Physical/Directory.Build.props index 668e3954f0b426..8ec207de7d8b3f 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/Directory.Build.props +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/Directory.Build.props @@ -2,5 +2,6 @@ true + browser \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj index cb4948ba130acb..e2d8ddfb16335a 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj @@ -1,6 +1,6 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 @@ -10,4 +10,7 @@ + + + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs index 067a1545860e2a..f6489b7d5ae252 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs @@ -2,6 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Diagnostics; using System.IO; namespace Microsoft.Extensions.FileProviders.Physical @@ -27,5 +28,28 @@ public static bool IsExcluded(FileSystemInfo fileSystemInfo, ExclusionFilters fi return false; } + + public static FileInfo ResolveFileLinkTarget(string filePath) +#if NETCOREAPP + => ResolveFileLinkTarget(new FileInfo(filePath)); +#else + => null; +#endif + + public static FileInfo ResolveFileLinkTarget(FileInfo fileInfo) + { +#if NETCOREAPP + if (fileInfo.Exists && fileInfo.LinkTarget != null) + { + FileSystemInfo targetInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true); + if (targetInfo.Exists) + { + return (FileInfo)targetInfo; + } + } +#endif + + return null; + } } } diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj index 9801bac9fead45..355b0a05bbc432 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj @@ -1,8 +1,8 @@ - + Microsoft.Extensions.FileProviders - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 true true File provider for physical files for Microsoft.Extensions.FileProviders. @@ -26,4 +26,15 @@ + + + + + + + + + + + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs index 23d8c8d306c74c..3b77771d9174c8 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs @@ -38,7 +38,7 @@ public class PollingFileChangeToken : IPollingChangeToken /// The to poll public PollingFileChangeToken(FileInfo fileInfo) { - _fileInfo = fileInfo; + _fileInfo = FileSystemInfoHelper.ResolveFileLinkTarget(fileInfo) ?? fileInfo; _previousWriteTimeUtc = GetLastWriteTimeUtc(); } diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs index 998aaa29772fef..b0a778182543d3 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs @@ -143,7 +143,8 @@ private bool CalculateChanges() /// The that the file was last modified. protected virtual DateTime GetLastWriteUtc(string path) { - return File.GetLastWriteTimeUtc(Path.Combine(_directoryInfo.FullName, path)); + string filePath = Path.Combine(_directoryInfo.FullName, path); + return FileSystemInfoHelper.ResolveFileLinkTarget(filePath)?.LastWriteTimeUtc ?? File.GetLastWriteTimeUtc(filePath); } private static bool ArrayEquals(byte[] previousHash, byte[] currentHash) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 5bb882e5cccd9c..3a2af832eb983b 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -1512,6 +1512,68 @@ public void UsePollingFileWatcher_FileWatcherNotNull_ReturnsFalse() } } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) + { + // Arrange + using var root = new DisposableFileSystem(); + string fileName = Path.GetRandomFileName(); + string filePath = Path.Combine(root.RootPath, fileName); + File.WriteAllText(filePath, "v1.1"); + + using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(useWildcard ? "*" : fileName); + Assert.False(token.HasChanged); + + // Act + File.WriteAllText(filePath, "v1.2"); + Thread.Sleep(GetTokenPollingInterval(token)); + + // Assert + Assert.True(token.HasChanged); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool useWildcard) + { + // Arrange + using var rootOfFile = new DisposableFileSystem(); + string filePath = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); + File.WriteAllText(filePath, "v1.1"); + + using var rootOfLink = new DisposableFileSystem(); + string linkName = Path.GetRandomFileName(); + string linkPath = Path.Combine(rootOfLink.RootPath, linkName); + File.CreateSymbolicLink(linkPath, filePath); + + using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); + Assert.False(token.HasChanged); + + // Act + File.WriteAllText(filePath, "v1.2"); + Thread.Sleep(GetTokenPollingInterval(token)); + + // Assert + Assert.True(token.HasChanged); + } + + private int GetTokenPollingInterval(IChangeToken changeToken) + { + TimeSpan pollingInterval = (changeToken as CompositeChangeToken).ChangeTokens[1] switch + { + PollingWildCardChangeToken wildcardChangeToken => wildcardChangeToken.PollingInterval, + PollingFileChangeToken => PollingFileChangeToken.PollingInterval, + _ => throw new InvalidOperationException() + }; + + return (int)pollingInterval.TotalMilliseconds; + } + [Fact] public void CreateFileWatcher_CreatesWatcherWithPollingAndActiveFlags() { diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj index b939996a35bcf8..b5fd596661d466 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj @@ -1,8 +1,12 @@ - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 + + + + diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj index 3b3239f39e12b6..2b6f76bd26c4fb 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/src/Microsoft.Extensions.FileSystemGlobbing.csproj @@ -1,7 +1,7 @@ - + - netstandard2.0;net461 + $(NetCoreAppCurrent);netstandard2.0;net461 true File system globbing to find files matching a specified pattern. @@ -10,5 +10,10 @@ - + + + + + + From d78099512d6d07ffec3513be4b20df8f2cdd1c1c Mon Sep 17 00:00:00 2001 From: David Cantu Date: Thu, 15 Jul 2021 16:08:35 -0700 Subject: [PATCH 03/17] Fix for the case where the link can change its target --- .../src/Internal/FileSystemInfoHelper.cs | 14 ++++++++++---- .../src/PollingFileChangeToken.cs | 10 ++++++++-- 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs index f6489b7d5ae252..b4ef88bc619cb1 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs @@ -30,16 +30,22 @@ public static bool IsExcluded(FileSystemInfo fileSystemInfo, ExclusionFilters fi } public static FileInfo ResolveFileLinkTarget(string filePath) + { #if NETCOREAPP - => ResolveFileLinkTarget(new FileInfo(filePath)); -#else - => null; + var fileInfo = new FileInfo(filePath); + if (fileInfo.Exists) + { + return ResolveFileLinkTarget(fileInfo); + } #endif + return null; + } public static FileInfo ResolveFileLinkTarget(FileInfo fileInfo) { #if NETCOREAPP - if (fileInfo.Exists && fileInfo.LinkTarget != null) + Debug.Assert(fileInfo.Exists); + if (fileInfo.LinkTarget != null) { FileSystemInfo targetInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true); if (targetInfo.Exists) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs index 3b77771d9174c8..3865ba68487c8b 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs @@ -38,7 +38,7 @@ public class PollingFileChangeToken : IPollingChangeToken /// The to poll public PollingFileChangeToken(FileInfo fileInfo) { - _fileInfo = FileSystemInfoHelper.ResolveFileLinkTarget(fileInfo) ?? fileInfo; + _fileInfo = fileInfo; _previousWriteTimeUtc = GetLastWriteTimeUtc(); } @@ -48,7 +48,13 @@ public PollingFileChangeToken(FileInfo fileInfo) private DateTime GetLastWriteTimeUtc() { _fileInfo.Refresh(); - return _fileInfo.Exists ? _fileInfo.LastWriteTimeUtc : DateTime.MinValue; + + if (!_fileInfo.Exists) + { + return DateTime.MinValue; + } + + return FileSystemInfoHelper.ResolveFileLinkTarget(_fileInfo)?.LastWriteTimeUtc ?? _fileInfo.LastWriteTimeUtc; } /// From ac4a8457c5b4f8cab4a1c0cc0132846646241a41 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Thu, 15 Jul 2021 16:09:47 -0700 Subject: [PATCH 04/17] Add more test cases and exclude them from non-netcoreapp tfms --- ...nsions.FileProviders.Physical.Tests.csproj | 6 +- .../tests/PhysicalFileProviderTests.cs | 64 +------- .../PhysicalFileProviderTests.netcoreapp.cs | 146 ++++++++++++++++++ 3 files changed, 152 insertions(+), 64 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj index d57fed0ca59497..bfa6846326fdee 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj @@ -1,4 +1,4 @@ - + Microsoft.Extensions.FileProviders.Physical @@ -12,6 +12,10 @@ Link="Common\System\Threading\Tasks\TaskTimeoutExtensions.cs" /> + + + + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 3a2af832eb983b..96f967fda24bfe 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -15,7 +15,7 @@ namespace Microsoft.Extensions.FileProviders { - public class PhysicalFileProviderTests + public partial class PhysicalFileProviderTests { private const int WaitTimeForTokenToFire = 500; private const int WaitTimeForTokenCallback = 10000; @@ -1512,68 +1512,6 @@ public void UsePollingFileWatcher_FileWatcherNotNull_ReturnsFalse() } } - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) - { - // Arrange - using var root = new DisposableFileSystem(); - string fileName = Path.GetRandomFileName(); - string filePath = Path.Combine(root.RootPath, fileName); - File.WriteAllText(filePath, "v1.1"); - - using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; - IChangeToken token = provider.Watch(useWildcard ? "*" : fileName); - Assert.False(token.HasChanged); - - // Act - File.WriteAllText(filePath, "v1.2"); - Thread.Sleep(GetTokenPollingInterval(token)); - - // Assert - Assert.True(token.HasChanged); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool useWildcard) - { - // Arrange - using var rootOfFile = new DisposableFileSystem(); - string filePath = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); - File.WriteAllText(filePath, "v1.1"); - - using var rootOfLink = new DisposableFileSystem(); - string linkName = Path.GetRandomFileName(); - string linkPath = Path.Combine(rootOfLink.RootPath, linkName); - File.CreateSymbolicLink(linkPath, filePath); - - using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; - IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); - Assert.False(token.HasChanged); - - // Act - File.WriteAllText(filePath, "v1.2"); - Thread.Sleep(GetTokenPollingInterval(token)); - - // Assert - Assert.True(token.HasChanged); - } - - private int GetTokenPollingInterval(IChangeToken changeToken) - { - TimeSpan pollingInterval = (changeToken as CompositeChangeToken).ChangeTokens[1] switch - { - PollingWildCardChangeToken wildcardChangeToken => wildcardChangeToken.PollingInterval, - PollingFileChangeToken => PollingFileChangeToken.PollingInterval, - _ => throw new InvalidOperationException() - }; - - return (int)pollingInterval.TotalMilliseconds; - } - [Fact] public void CreateFileWatcher_CreatesWatcherWithPollingAndActiveFlags() { diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs new file mode 100644 index 00000000000000..a7957beb9115ee --- /dev/null +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs @@ -0,0 +1,146 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using System; +using System.IO; +using System.Threading; +using Microsoft.Extensions.FileProviders.Physical; +using Microsoft.Extensions.Primitives; +using Xunit; + +namespace Microsoft.Extensions.FileProviders +{ + public partial class PhysicalFileProviderTests + { + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) + { + // Arrange + using var root = new DisposableFileSystem(); + string fileName = Path.GetRandomFileName(); + string filePath = Path.Combine(root.RootPath, fileName); + File.WriteAllText(filePath, "v1.1"); + + using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(useWildcard ? "*" : fileName); + Assert.False(token.HasChanged); + + // Act + File.WriteAllText(filePath, "v1.2"); + Thread.Sleep(GetTokenPollingInterval(token)); + + // Assert + Assert.True(token.HasChanged); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool useWildcard) + { + // Arrange + using var rootOfFile = new DisposableFileSystem(); + string filePath = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); + File.WriteAllText(filePath, "v1.1"); + + using var rootOfLink = new DisposableFileSystem(); + string linkName = Path.GetRandomFileName(); + string linkPath = Path.Combine(rootOfLink.RootPath, linkName); + File.CreateSymbolicLink(linkPath, filePath); + + using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); + Assert.False(token.HasChanged); + + // Act + File.WriteAllText(filePath, "v1.2"); + Thread.Sleep(GetTokenPollingInterval(token)); + + // Assert + Assert.True(token.HasChanged); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetNotExists(bool useWildcard) + { + // Arrange + using var rootOfLink = new DisposableFileSystem(); + string linkName = Path.GetRandomFileName(); + string linkPath = Path.Combine(rootOfLink.RootPath, linkName); + File.CreateSymbolicLink(linkPath, "not-existent-file"); + + // Act + using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); + + // Assert + Assert.False(token.HasChanged); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetChanged(bool useWildcard) + { + // Arrange + using var rootOfFile = new DisposableFileSystem(); + string file1Path = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); + File.WriteAllText(file1Path, "v1.1"); + + string file2Path = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); + File.WriteAllText(file2Path, "v2.1"); + + using var rootOfLink = new DisposableFileSystem(); + string linkName = Path.GetRandomFileName(); + string linkPath = Path.Combine(rootOfLink.RootPath, linkName); + File.CreateSymbolicLink(linkPath, file1Path); + + string filter = useWildcard ? "*" : linkName; + using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(filter); + Assert.False(token.HasChanged); + + // Act 1 - Change file 1's content. + File.WriteAllText(file1Path, "v1.2"); + Thread.Sleep(GetTokenPollingInterval(token)); + + // Assert 1 + Assert.True(token.HasChanged); + + // Act 2 - Change link target to file 2. + token = provider.Watch(filter); // Once HasChanged is true, the value will always be true. Get a new change token. + Assert.False(token.HasChanged); + File.Delete(linkPath); + File.CreateSymbolicLink(linkPath, file2Path); + Thread.Sleep(GetTokenPollingInterval(token)); + + // Assert 2 + Assert.True(token.HasChanged); // It should report the change regardless of the timestamp being older. + + // Act 3 - Change file 2's content. + token = provider.Watch(filter); + Assert.False(token.HasChanged); + File.WriteAllText(file2Path, "v2.2"); + Thread.Sleep(GetTokenPollingInterval(token)); + + // Assert 3 + Assert.True(token.HasChanged); + } + + private int GetTokenPollingInterval(IChangeToken changeToken) + { + TimeSpan pollingInterval = (changeToken as CompositeChangeToken).ChangeTokens[1] switch + { + PollingWildCardChangeToken wildcardChangeToken => wildcardChangeToken.PollingInterval, + PollingFileChangeToken => PollingFileChangeToken.PollingInterval, + _ => throw new InvalidOperationException() + }; + + return (int)pollingInterval.TotalMilliseconds; + } + } +} From b4895adfecea263cf0d059012b206bcc7aefa2c8 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Thu, 15 Jul 2021 16:41:43 -0700 Subject: [PATCH 05/17] Fix project references in ref projects --- ...Microsoft.Extensions.FileProviders.Abstractions.csproj | 2 +- .../Microsoft.Extensions.FileProviders.Physical.csproj | 4 ++-- .../ref/Microsoft.Extensions.FileSystemGlobbing.csproj | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj index 143dcc93cfb9dc..bd02f83be9189b 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Abstractions/ref/Microsoft.Extensions.FileProviders.Abstractions.csproj @@ -9,6 +9,6 @@ - + diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj index e2d8ddfb16335a..0f7dc64cb84a29 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/ref/Microsoft.Extensions.FileProviders.Physical.csproj @@ -10,7 +10,7 @@ - - + + diff --git a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj index b5fd596661d466..ade27836c9d808 100644 --- a/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj +++ b/src/libraries/Microsoft.Extensions.FileSystemGlobbing/ref/Microsoft.Extensions.FileSystemGlobbing.csproj @@ -5,8 +5,8 @@ - - - - + + + + From ebb032688b393d184620e2b6de0ca9a5d544e853 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Thu, 15 Jul 2021 18:00:43 -0700 Subject: [PATCH 06/17] Do not use UnsupportedOSPlatforms on test project in order to fix CI issue --- .../tests/Directory.Build.props | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Directory.Build.props diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Directory.Build.props b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Directory.Build.props new file mode 100644 index 00000000000000..52fc39ae90994a --- /dev/null +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Directory.Build.props @@ -0,0 +1,6 @@ + + + + true + + \ No newline at end of file From 1164e3355030a95c46099395e9806d0be2a182df Mon Sep 17 00:00:00 2001 From: David Cantu Date: Mon, 19 Jul 2021 14:52:41 -0700 Subject: [PATCH 07/17] Do not return link's LastWriteTime when target not exists --- .../src/Internal/FileSystemInfoHelper.cs | 14 ++++++++++---- .../src/PollingFileChangeToken.cs | 2 +- .../src/PollingWildCardChangeToken.cs | 2 +- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs index b4ef88bc619cb1..5ae854ae6efce8 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Internal/FileSystemInfoHelper.cs @@ -29,19 +29,23 @@ public static bool IsExcluded(FileSystemInfo fileSystemInfo, ExclusionFilters fi return false; } - public static FileInfo ResolveFileLinkTarget(string filePath) + public static DateTime? GetFileLinkTargetLastWriteTimeUtc(string filePath) { #if NETCOREAPP var fileInfo = new FileInfo(filePath); if (fileInfo.Exists) { - return ResolveFileLinkTarget(fileInfo); + return GetFileLinkTargetLastWriteTimeUtc(fileInfo); } #endif return null; } - public static FileInfo ResolveFileLinkTarget(FileInfo fileInfo) + // If file is a link and link target exists, return target's LastWriteTimeUtc. + // 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) { #if NETCOREAPP Debug.Assert(fileInfo.Exists); @@ -50,8 +54,10 @@ public static FileInfo ResolveFileLinkTarget(FileInfo fileInfo) FileSystemInfo targetInfo = fileInfo.ResolveLinkTarget(returnFinalTarget: true); if (targetInfo.Exists) { - return (FileInfo)targetInfo; + return targetInfo.LastWriteTimeUtc; } + + return DateTime.MinValue; } #endif diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs index 3865ba68487c8b..ddf2ae07423f0a 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingFileChangeToken.cs @@ -54,7 +54,7 @@ private DateTime GetLastWriteTimeUtc() return DateTime.MinValue; } - return FileSystemInfoHelper.ResolveFileLinkTarget(_fileInfo)?.LastWriteTimeUtc ?? _fileInfo.LastWriteTimeUtc; + return FileSystemInfoHelper.GetFileLinkTargetLastWriteTimeUtc(_fileInfo) ?? _fileInfo.LastWriteTimeUtc; } /// diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs index b0a778182543d3..28148d2315b966 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/PollingWildCardChangeToken.cs @@ -144,7 +144,7 @@ private bool CalculateChanges() protected virtual DateTime GetLastWriteUtc(string path) { string filePath = Path.Combine(_directoryInfo.FullName, path); - return FileSystemInfoHelper.ResolveFileLinkTarget(filePath)?.LastWriteTimeUtc ?? File.GetLastWriteTimeUtc(filePath); + return FileSystemInfoHelper.GetFileLinkTargetLastWriteTimeUtc(filePath) ?? File.GetLastWriteTimeUtc(filePath); } private static bool ArrayEquals(byte[] previousHash, byte[] currentHash) From 98b737a0ded3ae8aa6ada563012a466070fbb0b5 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Mon, 19 Jul 2021 15:21:10 -0700 Subject: [PATCH 08/17] Address feedback on tests and improve them to cover more scenarios. --- .../tests/PhysicalFileProviderTests.cs | 61 ++++++++++++ .../PhysicalFileProviderTests.netcoreapp.cs | 96 ++++++++----------- 2 files changed, 101 insertions(+), 56 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 96f967fda24bfe..30896874d7eded 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -1512,6 +1512,67 @@ public void UsePollingFileWatcher_FileWatcherNotNull_ReturnsFalse() } } + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) + { + // Arrange + using var root = new DisposableFileSystem(); + string fileName = Path.GetRandomFileName(); + string filePath = Path.Combine(root.RootPath, fileName); + File.WriteAllText(filePath, "v1.1"); + + using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(useWildcard ? "*" : fileName); + Assert.False(token.HasChanged); + + // Act + Thread.Sleep(100); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. + File.WriteAllText(filePath, "v1.2"); + int timeout = GetTokenPollingInterval(token); + Thread.Sleep(timeout); + + // Assert + Assert.True(token.HasChanged); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard) + { + // Arrange + using var root = new DisposableFileSystem(); + string fileName = Path.GetRandomFileName(); + string filePath = Path.Combine(root.RootPath, fileName); + File.WriteAllText(filePath, "v1.1"); + + string filter = useWildcard ? "*" : fileName; + using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(filter); + Assert.False(token.HasChanged); + + // Act + File.Delete(filePath); + Thread.Sleep(GetTokenPollingInterval(token)); + + // Assert + Assert.True(token.HasChanged); + } + + private int GetTokenPollingInterval(IChangeToken changeToken) + { + TimeSpan pollingInterval = (changeToken as CompositeChangeToken).ChangeTokens[1] switch + { + PollingWildCardChangeToken wildcardChangeToken => wildcardChangeToken.PollingInterval, + PollingFileChangeToken => PollingFileChangeToken.PollingInterval, + _ => throw new InvalidOperationException() + }; + + return (int)pollingInterval.TotalMilliseconds; + } + [Fact] public void CreateFileWatcher_CreatesWatcherWithPollingAndActiveFlags() { diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs index a7957beb9115ee..04aceaac29d92c 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs @@ -12,29 +12,6 @@ namespace Microsoft.Extensions.FileProviders { public partial class PhysicalFileProviderTests { - [Theory] - [InlineData(false)] - [InlineData(true)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) - { - // Arrange - using var root = new DisposableFileSystem(); - string fileName = Path.GetRandomFileName(); - string filePath = Path.Combine(root.RootPath, fileName); - File.WriteAllText(filePath, "v1.1"); - - using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; - IChangeToken token = provider.Watch(useWildcard ? "*" : fileName); - Assert.False(token.HasChanged); - - // Act - File.WriteAllText(filePath, "v1.2"); - Thread.Sleep(GetTokenPollingInterval(token)); - - // Assert - Assert.True(token.HasChanged); - } - [Theory] [InlineData(false)] [InlineData(true)] @@ -55,6 +32,7 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool Assert.False(token.HasChanged); // Act + Thread.Sleep(100); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(filePath, "v1.2"); Thread.Sleep(GetTokenPollingInterval(token)); @@ -82,18 +60,25 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe } [Theory] - [InlineData(false)] - [InlineData(true)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetChanged(bool useWildcard) + [InlineData(false, false)] + [InlineData(false, true)] + [InlineData(true, false)] + [InlineData(true, true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetChanged(bool useWildcard, bool fromTargetNonExistent) { // Arrange using var rootOfFile = new DisposableFileSystem(); - string file1Path = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); - File.WriteAllText(file1Path, "v1.1"); - + // Create file 2 first as we want to verify that the change is reported regardless of the timestamp being older. string file2Path = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); File.WriteAllText(file2Path, "v2.1"); + string file1Path = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); + if (!fromTargetNonExistent) + { + Thread.Sleep(100); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. + File.WriteAllText(file1Path, "v1.1"); + } + using var rootOfLink = new DisposableFileSystem(); string linkName = Path.GetRandomFileName(); string linkPath = Path.Combine(rootOfLink.RootPath, linkName); @@ -104,43 +89,42 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe IChangeToken token = provider.Watch(filter); Assert.False(token.HasChanged); - // Act 1 - Change file 1's content. - File.WriteAllText(file1Path, "v1.2"); - Thread.Sleep(GetTokenPollingInterval(token)); - - // Assert 1 - Assert.True(token.HasChanged); - - // Act 2 - Change link target to file 2. - token = provider.Watch(filter); // Once HasChanged is true, the value will always be true. Get a new change token. - Assert.False(token.HasChanged); + // Act - Change link target to file 2. File.Delete(linkPath); File.CreateSymbolicLink(linkPath, file2Path); Thread.Sleep(GetTokenPollingInterval(token)); - // Assert 2 + // Assert Assert.True(token.HasChanged); // It should report the change regardless of the timestamp being older. + } - // Act 3 - Change file 2's content. - token = provider.Watch(filter); + [Theory] + [InlineData(false)] + [InlineData(true)] + public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetDeleted(bool useWildcard) + { + // Arrange + using var rootOfFile = new DisposableFileSystem(); + + string filePath = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); + File.WriteAllText(filePath, "v1.1"); + + using var rootOfLink = new DisposableFileSystem(); + string linkName = Path.GetRandomFileName(); + string linkPath = Path.Combine(rootOfLink.RootPath, linkName); + File.CreateSymbolicLink(linkPath, filePath); + + string filter = useWildcard ? "*" : linkName; + using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; + IChangeToken token = provider.Watch(filter); Assert.False(token.HasChanged); - File.WriteAllText(file2Path, "v2.2"); + + // Act + File.Delete(linkPath); Thread.Sleep(GetTokenPollingInterval(token)); - // Assert 3 + // Assert Assert.True(token.HasChanged); } - - private int GetTokenPollingInterval(IChangeToken changeToken) - { - TimeSpan pollingInterval = (changeToken as CompositeChangeToken).ChangeTokens[1] switch - { - PollingWildCardChangeToken wildcardChangeToken => wildcardChangeToken.PollingInterval, - PollingFileChangeToken => PollingFileChangeToken.PollingInterval, - _ => throw new InvalidOperationException() - }; - - return (int)pollingInterval.TotalMilliseconds; - } } } From 75fcf967bd423b351caf290e62683296dfafde39 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Mon, 19 Jul 2021 15:41:17 -0700 Subject: [PATCH 09/17] Make the project unsupported in browser. --- ...t.Extensions.FileProviders.Physical.csproj | 6 ++- .../src/Resources/Strings.resx | 53 ++++++++++--------- 2 files changed, 33 insertions(+), 26 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj index 355b0a05bbc432..dda1504be01860 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj @@ -2,11 +2,15 @@ Microsoft.Extensions.FileProviders - $(NetCoreAppCurrent);netstandard2.0;net461 + $(NetCoreAppCurrent);$(NetCoreAppCurrent)-Browser;netstandard2.0;net461 true true File provider for physical files for Microsoft.Extensions.FileProviders. + + + SR.FileProvidersPhysical_PlatformNotSupported + @@ -129,4 +129,7 @@ Unexpected type of FileSystemInfo + + Microsoft.Extensions.FileProviders.Physical is not supported on this platform. + \ No newline at end of file From 144335aa51ffe69a12048b5a688de51c84241ba2 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Mon, 19 Jul 2021 15:42:09 -0700 Subject: [PATCH 10/17] Fix duplicate reference to PlatformAttributes with IncludePlatformAttributes=false --- .../tests/Directory.Build.props | 6 ------ ...Microsoft.Extensions.FileProviders.Physical.Tests.csproj | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Directory.Build.props diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Directory.Build.props b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Directory.Build.props deleted file mode 100644 index 52fc39ae90994a..00000000000000 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Directory.Build.props +++ /dev/null @@ -1,6 +0,0 @@ - - - - true - - \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj index bfa6846326fdee..6b4ab37b34045f 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/Microsoft.Extensions.FileProviders.Physical.Tests.csproj @@ -5,6 +5,7 @@ $(NetCoreAppCurrent);net461 true true + false From 9c50a3a99895aa28d7f01dd063fc0ddae60e1f43 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Mon, 19 Jul 2021 17:27:39 -0700 Subject: [PATCH 11/17] Disable default items for browser --- .../src/Microsoft.Extensions.FileProviders.Physical.csproj | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj index dda1504be01860..534a08b11ff87a 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Microsoft.Extensions.FileProviders.Physical.csproj @@ -8,8 +8,9 @@ File provider for physical files for Microsoft.Extensions.FileProviders. - - SR.FileProvidersPhysical_PlatformNotSupported + + false + SR.FileProvidersPhysical_PlatformNotSupported From b2f9badced4e610f195a1504b8e391e68df07313 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Mon, 19 Jul 2021 17:31:46 -0700 Subject: [PATCH 12/17] Undo unrelated changes to Strings.resx --- .../src/Resources/Strings.resx | 50 +++++++++---------- 1 file changed, 25 insertions(+), 25 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Resources/Strings.resx index deade557d4d7dc..2ff2106bca01fe 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/src/Resources/Strings.resx @@ -1,17 +1,17 @@  From d8d143a2e8f3859cb15867201b92dd9ae87d5638 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Mon, 19 Jul 2021 23:41:50 -0700 Subject: [PATCH 13/17] Replace Thread.Sleep with Task.Delay, add assertion messages to try to debug CI failures and increase delay between writes --- .../tests/PhysicalFileProviderTests.cs | 17 +++++------ .../PhysicalFileProviderTests.netcoreapp.cs | 29 +++++++++---------- 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 30896874d7eded..823d68d140851c 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -1515,7 +1515,7 @@ public void UsePollingFileWatcher_FileWatcherNotNull_ReturnsFalse() [Theory] [InlineData(false)] [InlineData(true)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) + public async Task UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) { // Arrange using var root = new DisposableFileSystem(); @@ -1528,19 +1528,18 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWildcard) Assert.False(token.HasChanged); // Act - Thread.Sleep(100); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. + await Task.Delay(200); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(filePath, "v1.2"); - int timeout = GetTokenPollingInterval(token); - Thread.Sleep(timeout); + await Task.Delay(GetTokenPollingInterval(token)); // Assert - Assert.True(token.HasChanged); + Assert.True(token.HasChanged, $"Current time: {DateTime.UtcNow:O} file LastWriteTime: {File.GetLastWriteTimeUtc(filePath):O}"); } [Theory] - [InlineData(false)] + //[InlineData(false)] [InlineData(true)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard) + public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard) { // Arrange using var root = new DisposableFileSystem(); @@ -1555,10 +1554,10 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool u // Act File.Delete(filePath); - Thread.Sleep(GetTokenPollingInterval(token)); + await Task.Delay(GetTokenPollingInterval(token)); // Assert - Assert.True(token.HasChanged); + Assert.True(token.HasChanged, $"Current time: {DateTime.UtcNow:O} file LastWriteTime: {File.GetLastWriteTimeUtc(filePath):O}"); } private int GetTokenPollingInterval(IChangeToken changeToken) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs index 04aceaac29d92c..1bf55f850e85ea 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs @@ -3,8 +3,7 @@ using System; using System.IO; -using System.Threading; -using Microsoft.Extensions.FileProviders.Physical; +using System.Threading.Tasks; using Microsoft.Extensions.Primitives; using Xunit; @@ -15,7 +14,7 @@ public partial class PhysicalFileProviderTests [Theory] [InlineData(false)] [InlineData(true)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool useWildcard) + public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool useWildcard) { // Arrange using var rootOfFile = new DisposableFileSystem(); @@ -32,12 +31,12 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink(bool Assert.False(token.HasChanged); // Act - Thread.Sleep(100); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. + await Task.Delay(200); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(filePath, "v1.2"); - Thread.Sleep(GetTokenPollingInterval(token)); + await Task.Delay(GetTokenPollingInterval(token)); // Assert - Assert.True(token.HasChanged); + Assert.True(token.HasChanged, $"Current time: {DateTime.UtcNow:O} file LastWriteTime: {File.GetLastWriteTimeUtc(filePath):O}"); } [Theory] @@ -64,7 +63,7 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe [InlineData(false, true)] [InlineData(true, false)] [InlineData(true, true)] - public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetChanged(bool useWildcard, bool fromTargetNonExistent) + public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetChanged(bool useWildcard, bool linkWasBroken) { // Arrange using var rootOfFile = new DisposableFileSystem(); @@ -73,9 +72,9 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe File.WriteAllText(file2Path, "v2.1"); string file1Path = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); - if (!fromTargetNonExistent) + if (!linkWasBroken) { - Thread.Sleep(100); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. + await Task.Delay(200); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(file1Path, "v1.1"); } @@ -92,16 +91,16 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe // Act - Change link target to file 2. File.Delete(linkPath); File.CreateSymbolicLink(linkPath, file2Path); - Thread.Sleep(GetTokenPollingInterval(token)); + await Task.Delay(GetTokenPollingInterval(token)); - // Assert - Assert.True(token.HasChanged); // It should report the change regardless of the timestamp being older. + // Assert - It should report the change regardless of the timestamp being older. + Assert.True(token.HasChanged, $"Current time: {DateTime.UtcNow:O} file 1 LastWriteTime: {File.GetLastWriteTimeUtc(file1Path):O} file 2 LastWriteTime: {File.GetLastWriteTimeUtc(file2Path):O}"); } [Theory] [InlineData(false)] [InlineData(true)] - 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(); @@ -121,10 +120,10 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe // Act File.Delete(linkPath); - Thread.Sleep(GetTokenPollingInterval(token)); + await Task.Delay(GetTokenPollingInterval(token)); // Assert - Assert.True(token.HasChanged); + Assert.True(token.HasChanged, $"Current time: {DateTime.UtcNow:O} file LastWriteTime: {File.GetLastWriteTimeUtc(filePath):O}"); } } } From c7e28d4c9c2ce94b36da6ead9ba6a44702fbfd9d Mon Sep 17 00:00:00 2001 From: David Cantu Date: Tue, 20 Jul 2021 17:29:26 -0700 Subject: [PATCH 14/17] Replace HasChanged for RegisterChangeCallback in tests --- .../tests/PhysicalFileProviderTests.cs | 30 +++++++------------ .../PhysicalFileProviderTests.netcoreapp.cs | 28 ++++++++++------- 2 files changed, 27 insertions(+), 31 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 823d68d140851c..49e04d73efc915 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -1525,21 +1525,22 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWild using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(useWildcard ? "*" : fileName); - Assert.False(token.HasChanged); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(null); }, null); // Act await Task.Delay(200); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(filePath, "v1.2"); - await Task.Delay(GetTokenPollingInterval(token)); // Assert - Assert.True(token.HasChanged, $"Current time: {DateTime.UtcNow:O} file LastWriteTime: {File.GetLastWriteTimeUtc(filePath):O}"); + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30))); } [Theory] - //[InlineData(false)] + [InlineData(false)] [InlineData(true)] - public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard) + public void UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool useWildcard) { // Arrange using var root = new DisposableFileSystem(); @@ -1550,26 +1551,15 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted( string filter = useWildcard ? "*" : fileName; using var provider = new PhysicalFileProvider(root.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(filter); - Assert.False(token.HasChanged); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(null); }, null); // Act File.Delete(filePath); - await Task.Delay(GetTokenPollingInterval(token)); // Assert - Assert.True(token.HasChanged, $"Current time: {DateTime.UtcNow:O} file LastWriteTime: {File.GetLastWriteTimeUtc(filePath):O}"); - } - - private int GetTokenPollingInterval(IChangeToken changeToken) - { - TimeSpan pollingInterval = (changeToken as CompositeChangeToken).ChangeTokens[1] switch - { - PollingWildCardChangeToken wildcardChangeToken => wildcardChangeToken.PollingInterval, - PollingFileChangeToken => PollingFileChangeToken.PollingInterval, - _ => throw new InvalidOperationException() - }; - - return (int)pollingInterval.TotalMilliseconds; + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30))); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs index 1bf55f850e85ea..7fe2a91c165e92 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs @@ -28,15 +28,16 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(useWildcard ? "*" : linkName); - Assert.False(token.HasChanged); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); // Act await Task.Delay(200); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(filePath, "v1.2"); - await Task.Delay(GetTokenPollingInterval(token)); // Assert - Assert.True(token.HasChanged, $"Current time: {DateTime.UtcNow:O} file LastWriteTime: {File.GetLastWriteTimeUtc(filePath):O}"); + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30))); } [Theory] @@ -54,8 +55,11 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe 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); + // Assert - Assert.False(token.HasChanged); + Assert.False(tcs.Task.Wait(TimeSpan.FromSeconds(30))); } [Theory] @@ -86,21 +90,22 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink string filter = useWildcard ? "*" : linkName; using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(filter); - Assert.False(token.HasChanged); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); // Act - Change link target to file 2. File.Delete(linkPath); File.CreateSymbolicLink(linkPath, file2Path); - await Task.Delay(GetTokenPollingInterval(token)); // Assert - It should report the change regardless of the timestamp being older. - Assert.True(token.HasChanged, $"Current time: {DateTime.UtcNow:O} file 1 LastWriteTime: {File.GetLastWriteTimeUtc(file1Path):O} file 2 LastWriteTime: {File.GetLastWriteTimeUtc(file2Path):O}"); + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30))); } [Theory] [InlineData(false)] [InlineData(true)] - public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetDeleted(bool useWildcard) + public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_TargetDeleted(bool useWildcard) { // Arrange using var rootOfFile = new DisposableFileSystem(); @@ -116,14 +121,15 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink string filter = useWildcard ? "*" : linkName; using var provider = new PhysicalFileProvider(rootOfLink.RootPath) { UsePollingFileWatcher = true, UseActivePolling = true }; IChangeToken token = provider.Watch(filter); - Assert.False(token.HasChanged); + + var tcs = new TaskCompletionSource(); + token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); // Act File.Delete(linkPath); - await Task.Delay(GetTokenPollingInterval(token)); // Assert - Assert.True(token.HasChanged, $"Current time: {DateTime.UtcNow:O} file LastWriteTime: {File.GetLastWriteTimeUtc(filePath):O}"); + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30))); } } } From 5b85631a183142cc4c1345eaa429b7917fcb74a9 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Wed, 21 Jul 2021 08:47:09 -0700 Subject: [PATCH 15/17] Add messages to asserts to attempt to debug CI issues --- .../tests/PhysicalFileProviderTests.cs | 6 ++++-- .../tests/PhysicalFileProviderTests.netcoreapp.cs | 9 ++++++--- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 49e04d73efc915..359e59fba26ce4 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -1534,7 +1534,8 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWild File.WriteAllText(filePath, "v1.2"); // Assert - Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30))); + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + $"Change event was not raised - current time: {DateTime.UtcNow}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath)}"); } [Theory] @@ -1559,7 +1560,8 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool u File.Delete(filePath); // Assert - Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30))); + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + $"Change event was not raised - current time: {DateTime.UtcNow}, file Exists: {File.Exists(filePath)}"); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs index 7fe2a91c165e92..cf68aee4ecc159 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs @@ -37,7 +37,8 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink File.WriteAllText(filePath, "v1.2"); // Assert - Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30))); + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + $"Change event was not raised - current time: {DateTime.UtcNow}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath)}"); } [Theory] @@ -99,7 +100,8 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink 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(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + $"Change event was not raised - current time: {DateTime.UtcNow}, file1 LastWriteTimeUtc: {File.GetLastWriteTimeUtc(file1Path)}, file2 LastWriteTime: {File.GetLastWriteTimeUtc(file2Path)}"); } [Theory] @@ -129,7 +131,8 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe File.Delete(linkPath); // Assert - Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30))); + Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + $"Change event was not raised - current time: {DateTime.UtcNow}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath)}"); } } } From 5c27caea5fef4e9c7ecd167192c9632ba0910e88 Mon Sep 17 00:00:00 2001 From: David Cantu Date: Wed, 21 Jul 2021 09:52:47 -0700 Subject: [PATCH 16/17] Add date format to assertion messages. --- .../tests/PhysicalFileProviderTests.cs | 4 ++-- .../tests/PhysicalFileProviderTests.netcoreapp.cs | 9 +++++---- 2 files changed, 7 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 359e59fba26ce4..6b41da72a1b57d 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -1535,7 +1535,7 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWild // Assert Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), - $"Change event was not raised - current time: {DateTime.UtcNow}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath)}"); + $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}"); } [Theory] @@ -1561,7 +1561,7 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_FileDeleted(bool u // Assert Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), - $"Change event was not raised - current time: {DateTime.UtcNow}, file Exists: {File.Exists(filePath)}"); + $"Change event was not raised - current time: {DateTime.UtcNow:O}, file Exists: {File.Exists(filePath)}."); } [Fact] diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs index cf68aee4ecc159..fefab8481b3b37 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs @@ -38,7 +38,7 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink // Assert Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), - $"Change event was not raised - current time: {DateTime.UtcNow}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath)}"); + $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}."); } [Theory] @@ -60,7 +60,8 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); // Assert - Assert.False(tcs.Task.Wait(TimeSpan.FromSeconds(30))); + Assert.False(tcs.Task.Wait(TimeSpan.FromSeconds(30)), + "Change event was raised when it was not expected."); } [Theory] @@ -101,7 +102,7 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink // Assert - It should report the change regardless of the timestamp being older. Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), - $"Change event was not raised - current time: {DateTime.UtcNow}, file1 LastWriteTimeUtc: {File.GetLastWriteTimeUtc(file1Path)}, file2 LastWriteTime: {File.GetLastWriteTimeUtc(file2Path)}"); + $"Change event was not raised - current time: {DateTime.UtcNow:O}, file1 LastWriteTimeUtc: {File.GetLastWriteTimeUtc(file1Path):O}, file2 LastWriteTime: {File.GetLastWriteTimeUtc(file2Path):O}."); } [Theory] @@ -132,7 +133,7 @@ public void UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink_Targe // Assert Assert.True(tcs.Task.Wait(TimeSpan.FromSeconds(30)), - $"Change event was not raised - current time: {DateTime.UtcNow}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath)}"); + $"Change event was not raised - current time: {DateTime.UtcNow:O}, file LastWriteTimeUtc: {File.GetLastWriteTimeUtc(filePath):O}."); } } } From 19dd9c35f5fd790cfb69b3b3a1357e384d1143fa Mon Sep 17 00:00:00 2001 From: David Cantu Date: Wed, 21 Jul 2021 12:36:03 -0700 Subject: [PATCH 17/17] Increase delay between writes to one second since OSX doesn't report milliseconds --- .../tests/PhysicalFileProviderTests.cs | 2 +- .../tests/PhysicalFileProviderTests.netcoreapp.cs | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs index 6b41da72a1b57d..b4e37df4f11636 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.cs @@ -1530,7 +1530,7 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged(bool useWild token.RegisterChangeCallback(_ => { tcs.TrySetResult(null); }, null); // Act - await Task.Delay(200); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. + await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(filePath, "v1.2"); // Assert diff --git a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs index fefab8481b3b37..d8b332b6409f56 100644 --- a/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs +++ b/src/libraries/Microsoft.Extensions.FileProviders.Physical/tests/PhysicalFileProviderTests.netcoreapp.cs @@ -33,7 +33,7 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink token.RegisterChangeCallback(_ => { tcs.TrySetResult(); }, null); // Act - await Task.Delay(200); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. + await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(filePath, "v1.2"); // Assert @@ -80,7 +80,7 @@ public async Task UsePollingFileWatcher_UseActivePolling_HasChanged_SymbolicLink string file1Path = Path.Combine(rootOfFile.RootPath, Path.GetRandomFileName()); if (!linkWasBroken) { - await Task.Delay(200); // Wait a bit before writing again, see https://github.com/dotnet/runtime/issues/55951. + await Task.Delay(1000); // Wait a second before writing again, see https://github.com/dotnet/runtime/issues/55951. File.WriteAllText(file1Path, "v1.1"); }