1 // Licensed to the .NET Foundation under one or more agreements.
2 // The .NET Foundation licenses this file to you under the MIT license.
3 // See the LICENSE file in the project root for more information.
4 
5 using System;
6 using System.Collections.Generic;
7 using System.ComponentModel;
8 using System.IO;
9 using System.Linq;
10 using System.Runtime.InteropServices;
11 using System.Threading;
12 using Xunit;
13 
14 namespace System.IO.Tests
15 {
16     public class FileSystemWatcherTests_netstandard17 : FileSystemWatcherTest
17     {
18         public class TestSite : ISite
19         {
20             public bool designMode;
21             public bool DesignMode => designMode;
22             public IComponent Component => null;
23             public IContainer Container => null;
24             public string Name { get; set; }
25             public object GetService(Type serviceType) => null;
26         }
27 
28         [Fact]
Site_GetSetRoundtrips()29         public void Site_GetSetRoundtrips()
30         {
31             using (var testDirectory = new TempDirectory(GetTestFilePath()))
32             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
33             {
34                 TestSite site = new TestSite();
35                 Assert.Null(watcher.Site);
36                 watcher.Site = site;
37                 Assert.Equal(site, watcher.Site);
38                 watcher.Site = null;
39                 Assert.Null(watcher.Site);
40                 Assert.False(watcher.EnableRaisingEvents);
41             }
42         }
43 
44         /// <summary>
45         /// When the FSW Site is set to a nonnull Site with DesignMode enabled, Event raising will be set to true
46         /// </summary>
47         [Fact]
Site_NonNullSetEnablesRaisingEvents()48         public void Site_NonNullSetEnablesRaisingEvents()
49         {
50             using (var testDirectory = new TempDirectory(GetTestFilePath()))
51             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
52             {
53                 TestSite site = new TestSite() { designMode = true };
54                 watcher.Site = site;
55                 Assert.True(watcher.EnableRaisingEvents);
56             }
57         }
58 
59         internal class TestISynchronizeInvoke : ISynchronizeInvoke
60         {
61             public bool BeginInvoke_Called;
62             public Delegate ExpectedDelegate;
63 
BeginInvoke(Delegate method, object[] args)64             public IAsyncResult BeginInvoke(Delegate method, object[] args)
65             {
66                 Assert.Equal(ExpectedDelegate, method);
67                 BeginInvoke_Called = true;
68                 return null;
69             }
70 
71             public bool InvokeRequired => true;
72             public object EndInvoke(IAsyncResult result) => null;
Invoke(Delegate method, object[] args)73             public object Invoke(Delegate method, object[] args) => null;
74         }
75 
76         [Fact]
SynchronizingObject_GetSetRoundtrips()77         public void SynchronizingObject_GetSetRoundtrips()
78         {
79             TestISynchronizeInvoke invoker = new TestISynchronizeInvoke() { };
80             using (var testDirectory = new TempDirectory(GetTestFilePath()))
81             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
82             {
83                 Assert.Null(watcher.SynchronizingObject);
84                 watcher.SynchronizingObject = invoker;
85                 Assert.Equal(invoker, watcher.SynchronizingObject);
86                 watcher.SynchronizingObject = null;
87                 Assert.Null(watcher.SynchronizingObject);
88             }
89         }
90 
91         /// <summary>
92         /// Ensure that the SynchronizeObject is invoked when an event occurs
93         /// </summary>
94         [Theory]
95         [InlineData(WatcherChangeTypes.Changed)]
96         [InlineData(WatcherChangeTypes.Deleted)]
97         [InlineData(WatcherChangeTypes.Created)]
SynchronizingObject_CalledOnEvent(WatcherChangeTypes expectedChangeType)98         public void SynchronizingObject_CalledOnEvent(WatcherChangeTypes expectedChangeType)
99         {
100             FileSystemEventHandler dele = (sender, e) => { Assert.Equal(expectedChangeType, e.ChangeType); };
101             TestISynchronizeInvoke invoker = new TestISynchronizeInvoke() { ExpectedDelegate = dele };
102             using (var testDirectory = new TempDirectory(GetTestFilePath()))
103             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
104             {
105                 watcher.SynchronizingObject = invoker;
106                 if (expectedChangeType == WatcherChangeTypes.Created)
107                 {
108                     watcher.Created += dele;
109                     watcher.CallOnCreated(new FileSystemEventArgs(WatcherChangeTypes.Created, "test", "name"));
110                 }
111                 else if (expectedChangeType == WatcherChangeTypes.Deleted)
112                 {
113                     watcher.Deleted += dele;
114                     watcher.CallOnDeleted(new FileSystemEventArgs(WatcherChangeTypes.Deleted, "test", "name"));
115                 }
116                 else if (expectedChangeType == WatcherChangeTypes.Changed)
117                 {
118                     watcher.Changed += dele;
119                     watcher.CallOnChanged(new FileSystemEventArgs(WatcherChangeTypes.Changed, "test", "name"));
120                 }
121                 Assert.True(invoker.BeginInvoke_Called);
122             }
123         }
124 
125         /// <summary>
126         /// Ensure that the SynchronizeObject is invoked when an Renamed event occurs
127         /// </summary>
128         [Fact]
SynchronizingObject_CalledOnRenamed()129         public void SynchronizingObject_CalledOnRenamed()
130         {
131             RenamedEventHandler dele = (sender, e) => { Assert.Equal(WatcherChangeTypes.Renamed, e.ChangeType); };
132             TestISynchronizeInvoke invoker = new TestISynchronizeInvoke() { ExpectedDelegate = dele };
133             using (var testDirectory = new TempDirectory(GetTestFilePath()))
134             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
135             {
136                 watcher.SynchronizingObject = invoker;
137                 watcher.Renamed += dele;
138                 watcher.CallOnRenamed(new RenamedEventArgs(WatcherChangeTypes.Changed, "test", "name", "oldname"));
139                 Assert.True(invoker.BeginInvoke_Called);
140             }
141         }
142 
143         /// <summary>
144         /// Ensure that the SynchronizeObject is invoked when an Error event occurs
145         /// </summary>
146         [Fact]
SynchronizingObject_CalledOnError()147         public void SynchronizingObject_CalledOnError()
148         {
149             ErrorEventHandler dele = (sender, e) => { Assert.IsType<FileNotFoundException>(e.GetException()); };
150             TestISynchronizeInvoke invoker = new TestISynchronizeInvoke() { ExpectedDelegate = dele };
151             using (var testDirectory = new TempDirectory(GetTestFilePath()))
152             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
153             {
154                 watcher.SynchronizingObject = invoker;
155                 watcher.Error += dele;
156                 watcher.CallOnError(new ErrorEventArgs(new FileNotFoundException()));
157                 Assert.True(invoker.BeginInvoke_Called);
158             }
159         }
160 
161         /// <summary>
162         /// Calling BeginInit and EndInit in a loop is fine. If events are enabled, they will start and stop in the loop as well.
163         /// </summary>
164         [Fact]
BeginEndInit_Repeated()165         public void BeginEndInit_Repeated()
166         {
167             using (var testDirectory = new TempDirectory(GetTestFilePath()))
168             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
169             {
170                 watcher.BeginInit();
171                 watcher.EndInit();
172                 watcher.BeginInit();
173                 watcher.EndInit();
174                 Assert.False(watcher.EnableRaisingEvents);
175             }
176         }
177 
178         /// <summary>
179         /// BeginInit followed by a EnableRaisingEvents=true does not cause the watcher to begin.
180         /// </summary>
181         [Fact]
BeginInit_PausesEnableRaisingEvents()182         public void BeginInit_PausesEnableRaisingEvents()
183         {
184             using (var testDirectory = new TempDirectory(GetTestFilePath()))
185             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
186             {
187                 watcher.Created += (obj, e) => { Assert.False(true, "Created event should not occur"); };
188                 watcher.Deleted += (obj, e) => { Assert.False(true, "Deleted event should not occur"); };
189                 watcher.BeginInit();
190                 watcher.EnableRaisingEvents = true;
191                 new TempFile(Path.Combine(testDirectory.Path, GetTestFileName())).Dispose();
192                 Thread.Sleep(WaitForExpectedEventTimeout);
193             }
194         }
195 
196         /// <summary>
197         /// EndInit will begin EnableRaisingEvents if we previously set EnableRaisingEvents=true
198         /// </summary>
199         [Fact]
EndInit_ResumesPausedEnableRaisingEvents()200         public void EndInit_ResumesPausedEnableRaisingEvents()
201         {
202             using (var testDirectory = new TempDirectory(GetTestFilePath()))
203             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
204             {
205                 watcher.BeginInit();
206                 watcher.EnableRaisingEvents = true;
207                 watcher.EndInit();
208                 ExpectEvent(watcher, WatcherChangeTypes.Created | WatcherChangeTypes.Deleted, () => new TempFile(Path.Combine(testDirectory.Path, GetTestFileName())).Dispose(), null);
209             }
210         }
211 
212         /// <summary>
213         /// EndInit will begin EnableRaisingEvents if we previously set EnableRaisingEvents=true
214         /// </summary>
215         [Theory]
216         [InlineData(true)]
217         [InlineData(false)]
EndInit_ResumesPausedEnableRaisingEvents(bool setBeforeBeginInit)218         public void EndInit_ResumesPausedEnableRaisingEvents(bool setBeforeBeginInit)
219         {
220             using (var testDirectory = new TempDirectory(GetTestFilePath()))
221             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
222             {
223                 if (setBeforeBeginInit)
224                     watcher.EnableRaisingEvents = true;
225                 watcher.BeginInit();
226                 if (!setBeforeBeginInit)
227                     watcher.EnableRaisingEvents = true;
228                 watcher.EndInit();
229                 ExpectEvent(watcher, WatcherChangeTypes.Created | WatcherChangeTypes.Deleted, () => new TempFile(Path.Combine(testDirectory.Path, GetTestFileName())).Dispose(), null);
230             }
231         }
232 
233         /// <summary>
234         /// Stopping events during the initialization period will prevent the watcher from restarting after EndInit()
235         /// </summary>
236         [Fact]
EndRaisingEventsDuringPause()237         public void EndRaisingEventsDuringPause()
238         {
239             using (var testDirectory = new TempDirectory(GetTestFilePath()))
240             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
241             {
242                 watcher.EnableRaisingEvents = true;
243                 watcher.BeginInit();
244                 watcher.EnableRaisingEvents = false;
245                 watcher.EndInit();
246                 new TempFile(Path.Combine(testDirectory.Path, GetTestFileName())).Dispose();
247                 Thread.Sleep(WaitForExpectedEventTimeout);
248             }
249         }
250 
251         /// <summary>
252         /// EndInit will not start event raising unless EnableRaisingEvents was set to true.
253         /// </summary>
254         [Fact]
EndInit_DoesNotEnableEventRaisedEvents()255         public void EndInit_DoesNotEnableEventRaisedEvents()
256         {
257             using (var testDirectory = new TempDirectory(GetTestFilePath()))
258             using (var watcher = new TestFileSystemWatcher(testDirectory.Path, "*"))
259             {
260                 watcher.Created += (obj, e) => { Assert.False(true, "Created event should not occur"); };
261                 watcher.Deleted += (obj, e) => { Assert.False(true, "Deleted event should not occur"); };
262                 watcher.BeginInit();
263                 watcher.EndInit();
264                 new TempFile(Path.Combine(testDirectory.Path, GetTestFileName())).Dispose();
265                 Thread.Sleep(WaitForExpectedEventTimeout);
266             }
267         }
268     }
269 }
270