I'm trying to create a simple logging tool to monitor file changes. I have used the FileSystemWatcher to detect changes to the file, but I've discovered that the events are only triggered when the file is closed, not when the buffer is flushed. This means that if multiple lines are added before the file is closed, I will only see that when the file is closed.
here is my test example.
[TestClass]
public class FileWriteTests
{
[TestMethod]
public void TestMethodAfterClose()
{
var currentDir = Environment.CurrentDirectory;
var fileToMonitor = "test.txt";
List<string> output = new List<string>();
var watcherTest = new FileWatcherTest(fileToMonitor, currentDir, output);
File.Delete(Path.Combine(currentDir, fileToMonitor));
using (var writer = new StreamWriter(Path.Combine(currentDir, fileToMonitor), true))
{
writer.WriteLine($"test");
writer.Flush();
}
System.Threading.Thread.Sleep(10);
Assert.AreEqual(1, output.Count);
Assert.AreEqual("test", output[0]);
}
[TestMethod]
public void TestMethodAfterFlush()
{
var currentDir = Environment.CurrentDirectory;
var fileToMonitor = "test.txt";
List<string> output = new List<string>();
var watcherTest = new FileWatcherTest(fileToMonitor, currentDir, output);
File.Delete(Path.Combine(currentDir, fileToMonitor));
using (var writer = new StreamWriter(Path.Combine(currentDir, fileToMonitor), true))
{
try
{
writer.WriteLine($"test");
writer.Flush();
System.Threading.Thread.Sleep(1000);
// add break point here for BareTail
Assert.AreEqual(1, output.Count);
Assert.AreEqual("test", output[0]);
}
catch
{
Assert.Fail("Test failed");
}
}
}
public class FileWatcherTest
{
public string FileName { get; set; }
public string Directory { get; set; }
private List<string> linesRead;
private FileSystemWatcher watcher;
public FileWatcherTest(string fileName, string directory, List<string> output)
{
FileName = fileName;
Directory = directory;
linesRead = output;
watcher = new FileSystemWatcher();
watcher.Path = directory;
watcher.Filter = FileName;
watcher.Changed += Watcher_Changed;
watcher.EnableRaisingEvents = true;
watcher.NotifyFilter = NotifyFilters.Attributes |
NotifyFilters.CreationTime |
NotifyFilters.DirectoryName |
NotifyFilters.FileName |
NotifyFilters.LastAccess |
NotifyFilters.LastWrite |
NotifyFilters.Security |
NotifyFilters.Size;
}
private void Watcher_Changed(object sender, FileSystemEventArgs e)
{
using (var fileStream = File.Open(Path.Combine(Directory, FileName), FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete | FileShare.Inheritable))
{
using (var reader = new StreamReader(fileStream))
{
string line;
while ((line = reader.ReadLine()) != null)
{
linesRead.Add(line);
}
}
}
}
}
}
right now TestMethodAfterClose succeeds and TestMethodAfterFlush fails. When I use the program BareTail and wait at the breakpoint, I see that it updates the display before the file is closed. So that gives me an indication that it's possible. I don't know if it's possible in C# and I might need to import some native functions using dllimport. The problem is I don't know where to look
How do I make both tests succeed, while not using a timer?
EDIT: updated the FileWatcherTest class