diff --git a/FollowingFileStream.Tests/FollowingFileStreamTest.cs b/FollowingFileStream.Tests/FollowingFileStreamTest.cs index 19186e6..7fe0d48 100644 --- a/FollowingFileStream.Tests/FollowingFileStreamTest.cs +++ b/FollowingFileStream.Tests/FollowingFileStreamTest.cs @@ -15,7 +15,7 @@ public FollowingFileStreamTest() { using (var sw = File.CreateText(inputFilePath)) { - sw.WriteLine("coucou"); + sw.Write("coucou"); } } @@ -66,6 +66,7 @@ public void FFS_Caps() Assert.IsTrue(ffs.CanRead); Assert.IsFalse(ffs.CanWrite); Assert.IsTrue(ffs.CanSeek); + Assert.IsTrue(ffs.CanTimeout); } } @@ -104,11 +105,11 @@ public void FFS_Read(bool async) { using (var ffs = new FollowingFileStream(inputFilePath, 4*1096, async)) { + var expected = "coucou"; Assert.AreEqual(0, ffs.Position); - Assert.AreEqual(8, ffs.Length); + Assert.AreEqual(expected.Length, ffs.Length); - var expected = "coucou" + Environment.NewLine; - var bytes = new byte[8]; + var bytes = new byte[expected.Length]; Assert.AreEqual(expected.Length, ffs.Read(bytes, 0, bytes.Length)); Assert.AreEqual(expected, System.Text.Encoding.Default.GetString(bytes)); @@ -134,6 +135,7 @@ public void FFS_CopyTo(bool async) using (var ffs = new FollowingFileStream(inputFilePath, 4*1096, async)) using (var destination = File.CreateText(outputFilePath)) { + ffs.ReadTimeout = 0; ffs.CopyTo(destination.BaseStream); } Assert.IsTrue(FileCompare(inputFilePath, outputFilePath)); @@ -148,16 +150,18 @@ public void FFS_FollowingRead(bool async) using (var ffs = new FollowingFileStream(inputFilePath, 4*1024, async)) using (var destination = File.CreateText(outputFilePath)) { + ffs.ReadTimeout = 400; destination.AutoFlush = true; var os = destination.BaseStream; var copy = ffs.CopyToAsync(os); Assert.AreEqual(0, os.Length); - Thread.Sleep(200); + Thread.Sleep(ffs.ReadTimeout/2); Assert.IsFalse(copy.IsCompleted); input.WriteLine("coucou2"); input.Close(); - Thread.Sleep(200); - Assert.IsTrue(copy.IsCompletedSuccessfully); + //Thread.Sleep(200); + //Assert.IsTrue(copy.IsCompletedSuccessfully); + Assert.IsTrue(copy.Wait(3*ffs.ReadTimeout)); } Assert.IsTrue(FileCompare(inputFilePath, outputFilePath)); } diff --git a/FollowingFileStream/FollowingFileStream.cs b/FollowingFileStream/FollowingFileStream.cs index 027435f..c1d6153 100644 --- a/FollowingFileStream/FollowingFileStream.cs +++ b/FollowingFileStream/FollowingFileStream.cs @@ -1,5 +1,6 @@ using System; using System.IO; +using System.Runtime.InteropServices; using System.Threading; using System.Threading.Tasks; @@ -243,6 +244,8 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, read = await fileStream.ReadAsync(buffer, offset, count, cancellationToken); } + TotalTime = 0; + return read; } @@ -257,12 +260,20 @@ public override async Task ReadAsync(byte[] buffer, int offset, int count, /// private async Task RetryNeededAsync() { - bool retry = IsFileLockedForWriting(); + bool retry = true; + // File locking for read/write operations is only supported on Windows + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + retry = IsFileLockedForWriting(); + } if (retry) { try { - await Task.Delay(MillisecondsRetryTimeout, cts.Token).ConfigureAwait(false); + var duration = MillisecondsRetryTimeout; + await Task.Delay(duration, cts.Token).ConfigureAwait(false); + TotalTime += duration; + retry = ReadTimeout == Timeout.Infinite || TotalTime < ReadTimeout; } catch (TaskCanceledException) { @@ -272,6 +283,18 @@ private async Task RetryNeededAsync() return retry; } + private long TotalTime; + + /// + /// + /// + public override bool CanTimeout => true; + + /// + /// + /// + public override int ReadTimeout { get; set; } = Timeout.Infinite; + /// /// Synchronously checks whether the file is locked for writing /// diff --git a/azure-pipelines.yml b/azure-pipelines.yml index 28d7674..eab6a82 100644 --- a/azure-pipelines.yml +++ b/azure-pipelines.yml @@ -6,8 +6,17 @@ trigger: - master +strategy: + matrix: + linux: + imageName: 'ubuntu-latest' + mac: + imageName: 'macos-latest' + windows: + imageName: 'windows-latest' + pool: - vmImage: 'windows-latest' + vmImage: $(imageName) variables: buildConfiguration: 'Release' @@ -89,6 +98,7 @@ steps: versionEnvVar: 'GitVersion.NuGetVersion' - task: PublishSymbols@2 + condition: eq( variables['Agent.OS'], 'Windows_NT' ) inputs: SearchPattern: | **/bin/**/FollowingFileStream.pdb