Skip to content
This repository was archived by the owner on Nov 6, 2018. It is now read-only.

Commit 1a7b10c

Browse files
natemcmasterNate McMaster
authored andcommitted
Handle flaky OnRenamed events more gracefully (#314)
In some conditions, System.IO.FileSystemWatcher will raise OnRenamed without the new file name. This causes an ArgumentException when attempting to determine if this file change should trigger a change token
1 parent 85cc84b commit 1a7b10c

File tree

3 files changed

+37
-5
lines changed

3 files changed

+37
-5
lines changed

src/Microsoft.Extensions.FileProviders.Physical/PhysicalFilesWatcher.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -245,6 +245,14 @@ ex is SecurityException ||
245245

246246
private void ReportChangeForMatchedEntries(string path)
247247
{
248+
if (string.IsNullOrEmpty(path))
249+
{
250+
// System.IO.FileSystemWatcher may trigger events that are missing the file name,
251+
// which makes it appear as if the root directory is renamed or deleted. Moving the root directory
252+
// of the file watcher is not supported, so this type of event is ignored.
253+
return;
254+
}
255+
248256
path = NormalizePath(path);
249257

250258
var matched = false;

test/Microsoft.Extensions.FileProviders.Physical.Tests/PhysicalFileProviderTests.cs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ namespace Microsoft.Extensions.FileProviders
1616
{
1717
public class PhysicalFileProviderTests
1818
{
19-
private const int WaitTimeForTokenToFire = 2 * 100;
19+
private const int WaitTimeForTokenToFire = 500;
2020

2121
[Fact]
2222
public void GetFileInfoReturnsNotFoundFileInfoForNullPath()
@@ -320,8 +320,8 @@ public async Task TokenCallbackInvokedOnFileChange()
320320
{
321321
var token = provider.Watch(fileName);
322322
Assert.NotNull(token);
323-
Assert.False(token.HasChanged);
324-
Assert.True(token.ActiveChangeCallbacks);
323+
Assert.False(token.HasChanged, "Token should not have changed yet");
324+
Assert.True(token.ActiveChangeCallbacks, "Token should have active callbacks");
325325

326326
bool callbackInvoked = false;
327327
token.RegisterChangeCallback(state =>
@@ -332,7 +332,7 @@ public async Task TokenCallbackInvokedOnFileChange()
332332
fileSystemWatcher.CallOnChanged(new FileSystemEventArgs(WatcherChangeTypes.Changed, root.RootPath, fileName));
333333
await Task.Delay(WaitTimeForTokenToFire);
334334

335-
Assert.True(callbackInvoked);
335+
Assert.True(callbackInvoked, "Callback should have been invoked");
336336
}
337337
}
338338
}
@@ -419,7 +419,7 @@ public async Task TokensFiredOnFileDeleted()
419419
Assert.True(token.ActiveChangeCallbacks);
420420

421421
fileSystemWatcher.CallOnDeleted(new FileSystemEventArgs(WatcherChangeTypes.Deleted, root.RootPath, fileName));
422-
await Task.Delay(WaitTimeForTokenToFire);
422+
await Task.Delay(WaitTimeForTokenToFire).ConfigureAwait(false);
423423

424424
Assert.True(token.HasChanged);
425425
}

test/Microsoft.Extensions.FileProviders.Physical.Tests/PhysicalFilesWatcherTests.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@
22
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
33

44
using System.IO;
5+
using System.Threading.Tasks;
56
using Xunit;
67

78
namespace Microsoft.Extensions.FileProviders.Physical.Tests
89
{
910
public class PhysicalFilesWatcherTests
1011
{
12+
private const int WaitTimeForTokenToFire = 500;
13+
1114
[Fact]
1215
public void CreateFileChangeToken_DoesNotAllowPathsAboveRoot()
1316
{
@@ -25,5 +28,26 @@ public void CreateFileChangeToken_DoesNotAllowPathsAboveRoot()
2528
Assert.IsType<NullChangeToken>(token);
2629
}
2730
}
31+
32+
[Fact]
33+
public async Task HandlesOnRenamedEventsThatMatchRootPath()
34+
{
35+
using (var root = new DisposableFileSystem())
36+
using (var fileSystemWatcher = new MockFileSystemWatcher(root.RootPath))
37+
using (var physicalFilesWatcher = new PhysicalFilesWatcher(root.RootPath + Path.DirectorySeparatorChar, fileSystemWatcher, pollForChanges: false))
38+
{
39+
var token = physicalFilesWatcher.CreateFileChangeToken("**");
40+
var called = false;
41+
token.RegisterChangeCallback(o => called = true, null);
42+
43+
fileSystemWatcher.CallOnRenamed(new RenamedEventArgs(WatcherChangeTypes.Renamed, root.RootPath, string.Empty, string.Empty));
44+
await Task.Delay(WaitTimeForTokenToFire).ConfigureAwait(false);
45+
Assert.False(called, "Callback should not have been triggered");
46+
47+
fileSystemWatcher.CallOnRenamed(new RenamedEventArgs(WatcherChangeTypes.Renamed, root.RootPath, "old.txt", "new.txt"));
48+
await Task.Delay(WaitTimeForTokenToFire).ConfigureAwait(false);
49+
Assert.True(called, "Callback should have been triggered");
50+
}
51+
}
2852
}
2953
}

0 commit comments

Comments
 (0)