diff --git a/src/Downloader.DummyHttpServer/Controllers/DummyFileController.cs b/src/Downloader.DummyHttpServer/Controllers/DummyFileController.cs index c73a17a3..bb327028 100644 --- a/src/Downloader.DummyHttpServer/Controllers/DummyFileController.cs +++ b/src/Downloader.DummyHttpServer/Controllers/DummyFileController.cs @@ -5,6 +5,7 @@ namespace Downloader.DummyHttpServer.Controllers { [ApiController] [Route("[controller]")] + [DummyApiExceptionFilter] public class DummyFileController : ControllerBase { private readonly ILogger _logger; @@ -160,7 +161,7 @@ public FileStreamResult GetOverflowedFile(long size, int offset = 0) /// timeout offset /// File stream [HttpGet] - [Route("file/size/{size}/timeout/{offset}")] + [Route("file/size/{size}/timeout/{offset}")] public FileStreamResult GetSlowFile(long size, int offset = 0) { _logger.LogTrace($"file/size/{size}/timeout/{offset}"); diff --git a/src/Downloader.DummyHttpServer/Downloader.DummyHttpServer.csproj b/src/Downloader.DummyHttpServer/Downloader.DummyHttpServer.csproj index f1ee808a..0247eaf7 100644 --- a/src/Downloader.DummyHttpServer/Downloader.DummyHttpServer.csproj +++ b/src/Downloader.DummyHttpServer/Downloader.DummyHttpServer.csproj @@ -2,7 +2,7 @@ netcoreapp3.1;net6.0; - 9.0 + latestMajor Downloader.DummyHttpServer.HttpServer Exe diff --git a/src/Downloader.DummyHttpServer/DummyApiException.cs b/src/Downloader.DummyHttpServer/DummyApiException.cs index e86a9bd9..e881cf75 100644 --- a/src/Downloader.DummyHttpServer/DummyApiException.cs +++ b/src/Downloader.DummyHttpServer/DummyApiException.cs @@ -1,12 +1,11 @@ using System.Net; -namespace Downloader.DummyHttpServer +namespace Downloader.DummyHttpServer; + +public class DummyApiException : WebException { - public class DummyApiException : WebException + public DummyApiException(string message) + : base(message, WebExceptionStatus.Timeout) { - public DummyApiException(string message) - : base(message, WebExceptionStatus.Timeout) - { - } } -} +} \ No newline at end of file diff --git a/src/Downloader.DummyHttpServer/DummyApiExceptionFilterAttribute.cs b/src/Downloader.DummyHttpServer/DummyApiExceptionFilterAttribute.cs new file mode 100644 index 00000000..ce35ade4 --- /dev/null +++ b/src/Downloader.DummyHttpServer/DummyApiExceptionFilterAttribute.cs @@ -0,0 +1,21 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using System.Net; +using static System.Console; + +namespace Downloader.DummyHttpServer; + +public class DummyApiExceptionFilterAttribute : ExceptionFilterAttribute +{ + public override void OnException(ExceptionContext context) + { + if (context.Exception is DummyApiException) + { + context.Result = new StatusCodeResult((int)HttpStatusCode.RequestedRangeNotSatisfiable); + } + else + { + WriteLine($"Exception on {context.ActionDescriptor.DisplayName}: {context.Exception.Message}"); + } + } +} \ No newline at end of file diff --git a/src/Downloader.Test/ConfigAwait.cs b/src/Downloader.Test/ConfigAwait.cs new file mode 100644 index 00000000..b2a85fe0 --- /dev/null +++ b/src/Downloader.Test/ConfigAwait.cs @@ -0,0 +1,4 @@ +using Fody; + +[assembly: ConfigureAwait(false)] +namespace Downloader.Test; \ No newline at end of file diff --git a/src/Downloader.Test/Downloader.Test.csproj b/src/Downloader.Test/Downloader.Test.csproj index 8b5dcebc..a4e13a4a 100644 --- a/src/Downloader.Test/Downloader.Test.csproj +++ b/src/Downloader.Test/Downloader.Test.csproj @@ -8,6 +8,7 @@ True True Downloader Tests + latestMajor @@ -24,6 +25,7 @@ all + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/src/Downloader.Test/FodyWeavers.xml b/src/Downloader.Test/FodyWeavers.xml index 2df8def1..216d7345 100644 --- a/src/Downloader.Test/FodyWeavers.xml +++ b/src/Downloader.Test/FodyWeavers.xml @@ -1,3 +1,4 @@  + \ No newline at end of file diff --git a/src/Downloader.Test/FodyWeavers.xsd b/src/Downloader.Test/FodyWeavers.xsd index 68c94237..23fce48d 100644 --- a/src/Downloader.Test/FodyWeavers.xsd +++ b/src/Downloader.Test/FodyWeavers.xsd @@ -4,6 +4,11 @@ + + + + + diff --git a/src/Downloader.Test/Helper/AssertHelper.cs b/src/Downloader.Test/Helper/AssertHelper.cs index afcf8e1e..12bdeb9d 100644 --- a/src/Downloader.Test/Helper/AssertHelper.cs +++ b/src/Downloader.Test/Helper/AssertHelper.cs @@ -1,37 +1,35 @@ -using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; using System.Linq; -using System.Reflection; -using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Downloader.Test.Helper +namespace Downloader.Test.Helper; + +public static class AssertHelper { - public static class AssertHelper + public static void DoesNotThrow(Action action) where T : Exception { - public static void DoesNotThrow(Action action) where T : Exception + try { - try - { - action(); - } - catch (T) - { - Assert.Fail("Expected no {0} to be thrown", typeof(T).Name); - } - catch - { - return; - } + action(); } - - public static void AreEquals(Chunk source, Chunk destination) + catch (T) { - Assert.IsNotNull(source); - Assert.IsNotNull(destination); + Assert.Fail("Expected no {0} to be thrown", typeof(T).Name); + } + catch + { + return; + } + } + + public static void AreEquals(Chunk source, Chunk destination) + { + Assert.IsNotNull(source); + Assert.IsNotNull(destination); - foreach (var prop in typeof(Chunk).GetProperties().Where(p => p.CanRead && p.CanWrite)) - { - Assert.AreEqual(prop.GetValue(source), prop.GetValue(destination), prop.Name); - } + foreach (var prop in typeof(Chunk).GetProperties().Where(p => p.CanRead && p.CanWrite)) + { + Assert.AreEqual(prop.GetValue(source), prop.GetValue(destination), prop.Name); } } } diff --git a/src/Downloader.Test/Helper/DownloadServiceEventsState.cs b/src/Downloader.Test/Helper/DownloadServiceEventsState.cs index d808d34d..ee42bd90 100644 --- a/src/Downloader.Test/Helper/DownloadServiceEventsState.cs +++ b/src/Downloader.Test/Helper/DownloadServiceEventsState.cs @@ -1,34 +1,33 @@ using System; -namespace Downloader.Test.Helper +namespace Downloader.Test.Helper; + +public class DownloadServiceEventsState { - public class DownloadServiceEventsState - { - public bool DownloadStarted { get; set; } - public string ActualFileName { get; set; } - public bool DownloadSuccessfullCompleted { get; set; } - public bool IsDownloadCancelled { get; set; } - public bool DownloadProgressIsCorrect { get; set; } = true; - public int DownloadProgressCount { get; set; } = 0; - public Exception DownloadError { get; set; } + public bool DownloadStarted { get; set; } + public string ActualFileName { get; set; } + public bool DownloadSuccessfullCompleted { get; set; } + public bool IsDownloadCancelled { get; set; } + public bool DownloadProgressIsCorrect { get; set; } = true; + public int DownloadProgressCount { get; set; } = 0; + public Exception DownloadError { get; set; } - public DownloadServiceEventsState(IDownloadService downloadService) - { - downloadService.DownloadStarted += (s, e) => { - DownloadStarted = true; - ActualFileName = e.FileName; - }; + public DownloadServiceEventsState(IDownloadService downloadService) + { + downloadService.DownloadStarted += (s, e) => { + DownloadStarted = true; + ActualFileName = e.FileName; + }; - downloadService.DownloadProgressChanged += (s, e) => { - DownloadProgressCount++; - DownloadProgressIsCorrect &= e.ProgressPercentage == downloadService.Package.SaveProgress; - }; + downloadService.DownloadProgressChanged += (s, e) => { + DownloadProgressCount++; + DownloadProgressIsCorrect &= e.ProgressPercentage == downloadService.Package.SaveProgress; + }; - downloadService.DownloadFileCompleted += (s, e) => { - DownloadSuccessfullCompleted = e.Error == null && !e.Cancelled; - DownloadError = e.Error; - IsDownloadCancelled = DownloadSuccessfullCompleted == false && DownloadError == null; - }; - } + downloadService.DownloadFileCompleted += (s, e) => { + DownloadSuccessfullCompleted = e.Error == null && !e.Cancelled; + DownloadError = e.Error; + IsDownloadCancelled = DownloadSuccessfullCompleted == false && DownloadError == null; + }; } } diff --git a/src/Downloader.Test/Helper/ExceptionThrower.cs b/src/Downloader.Test/Helper/ExceptionThrower.cs index ec2adb9a..94ba5dba 100644 --- a/src/Downloader.Test/Helper/ExceptionThrower.cs +++ b/src/Downloader.Test/Helper/ExceptionThrower.cs @@ -3,70 +3,69 @@ using System.Net; using System.Net.Http; -namespace Downloader.Test.Helper +namespace Downloader.Test.Helper; + +public static class ExceptionThrower { - public static class ExceptionThrower + public static Exception GetException() { - public static Exception GetException() + try + { + ThrowException(); + return new Exception(); // This code will never run. + } + catch (Exception e) { - try - { - ThrowException(); - return new Exception(); // This code will never run. - } - catch (Exception e) - { - return e; - } + return e; } - public static Exception GetWebException() + } + public static Exception GetWebException() + { + try { - try - { - ThrowWebException(); - return new WebException(); // This code will never run. - } - catch (Exception e) - { - return e; - } + ThrowWebException(); + return new WebException(); // This code will never run. } - private static void ThrowWebException() + catch (Exception e) { - try - { - ThrowIoException(); - } - catch (Exception e) - { - throw new WebException("High level exception", e); - } + return e; } - private static void ThrowException() + } + private static void ThrowWebException() + { + try { - try - { - ThrowIoException(); - } - catch (Exception e) - { - throw new Exception("High level exception", e); - } + ThrowIoException(); } - private static void ThrowIoException() + catch (Exception e) { - try - { - ThrowHttpRequestException(); - } - catch (Exception e) - { - throw new IOException("Mid level exception", e); - } + throw new WebException("High level exception", e); } - private static void ThrowHttpRequestException() + } + private static void ThrowException() + { + try { - throw new HttpRequestException("Low level exception"); + ThrowIoException(); } + catch (Exception e) + { + throw new Exception("High level exception", e); + } + } + private static void ThrowIoException() + { + try + { + ThrowHttpRequestException(); + } + catch (Exception e) + { + throw new IOException("Mid level exception", e); + } + } + private static void ThrowHttpRequestException() + { + throw new HttpRequestException("Low level exception"); } } diff --git a/src/Downloader.Test/HelperTests/AssertHelperTest.cs b/src/Downloader.Test/HelperTests/AssertHelperTest.cs index 18f3a3e0..11895948 100644 --- a/src/Downloader.Test/HelperTests/AssertHelperTest.cs +++ b/src/Downloader.Test/HelperTests/AssertHelperTest.cs @@ -2,76 +2,75 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; using System; -namespace Downloader.Test.HelperTests +namespace Downloader.Test.HelperTests; + +[TestClass] +public class AssertHelperTest { - [TestClass] - public class AssertHelperTest + [TestMethod] + public void TestDoesNotThrowWhenThrowExp() { - [TestMethod] - public void TestDoesNotThrowWhenThrowExp() - { - void ThrowException() => throw new DivideByZeroException("TEST"); + void ThrowException() => throw new DivideByZeroException("TEST"); - AssertHelper.DoesNotThrow(ThrowException); - } + AssertHelper.DoesNotThrow(ThrowException); + } - [TestMethod] - public void TestChunksAreEquals() - { - // arrange - var chunk1 = new Chunk() { - Id = "test-id", - Start = 255, - End = 512, - MaxTryAgainOnFailover = 1, - Position = 386, - Timeout = 1000 - }; + [TestMethod] + public void TestChunksAreEquals() + { + // arrange + var chunk1 = new Chunk() { + Id = "test-id", + Start = 255, + End = 512, + MaxTryAgainOnFailover = 1, + Position = 386, + Timeout = 1000 + }; - var chunk2 = new Chunk() { - Id = "test-id", - Start = 255, - End = 512, - MaxTryAgainOnFailover = 1, - Position = 386, - Timeout = 1000 - }; + var chunk2 = new Chunk() { + Id = "test-id", + Start = 255, + End = 512, + MaxTryAgainOnFailover = 1, + Position = 386, + Timeout = 1000 + }; - // act - AssertHelper.AreEquals(chunk1, chunk2); + // act + AssertHelper.AreEquals(chunk1, chunk2); - // assert - Assert.AreNotEqual(chunk1, chunk2); - } + // assert + Assert.AreNotEqual(chunk1, chunk2); + } - [TestMethod] - public void TestChunksAreNotEquals() - { - // arrange - var chunk1 = new Chunk() { - Id = "test-id", - Start = 255, - End = 512, - MaxTryAgainOnFailover = 1, - Position = 386, - Timeout = 1000 - }; + [TestMethod] + public void TestChunksAreNotEquals() + { + // arrange + var chunk1 = new Chunk() { + Id = "test-id", + Start = 255, + End = 512, + MaxTryAgainOnFailover = 1, + Position = 386, + Timeout = 1000 + }; - var chunk2 = new Chunk() { - Id = "test-id", - Start = 512, - End = 1024, - MaxTryAgainOnFailover = 1, - Position = 386, - Timeout = 1000 - }; + var chunk2 = new Chunk() { + Id = "test-id", + Start = 512, + End = 1024, + MaxTryAgainOnFailover = 1, + Position = 386, + Timeout = 1000 + }; - // act - void testAssertHelper() => AssertHelper.AreEquals(chunk1, chunk2); + // act + void testAssertHelper() => AssertHelper.AreEquals(chunk1, chunk2); - // assert - Assert.ThrowsException(testAssertHelper); - Assert.AreNotEqual(chunk1, chunk2); - } + // assert + Assert.ThrowsException(testAssertHelper); + Assert.AreNotEqual(chunk1, chunk2); } } diff --git a/src/Downloader.Test/IntegrationTests/DownloadIntegrationTest.cs b/src/Downloader.Test/IntegrationTests/DownloadIntegrationTest.cs index a0ba486e..605300b9 100644 --- a/src/Downloader.Test/IntegrationTests/DownloadIntegrationTest.cs +++ b/src/Downloader.Test/IntegrationTests/DownloadIntegrationTest.cs @@ -1,4 +1,5 @@ using Downloader.DummyHttpServer; +using Fody; using Microsoft.VisualStudio.TestTools.UnitTesting; using Newtonsoft.Json; using System; @@ -8,824 +9,826 @@ using System.Threading; using System.Threading.Tasks; -namespace Downloader.Test.IntegrationTests +namespace Downloader.Test.IntegrationTests; + +[ConfigureAwait(false)] +public abstract class DownloadIntegrationTest { - public abstract class DownloadIntegrationTest + protected DownloadConfiguration Config { get; set; } + protected string URL { get; set; } = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); + protected string Filename => Path.GetTempFileName(); + + [TestInitialize] + public abstract void InitialTest(); + + [TestMethod] + public async Task DownloadUrlWithFilenameOnMemoryTest() { - protected DownloadConfiguration Config { get; set; } - protected string URL { get; set; } = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); - protected string Filename => Path.GetTempFileName(); + // arrange + var downloadCompletedSuccessfully = false; + var downloader = new DownloadService(Config); + downloader.DownloadFileCompleted += (s, e) => { + if (e.Cancelled == false && e.Error == null) + { + downloadCompletedSuccessfully = true; + } + }; + + // act + using var memoryStream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + + // assert + Assert.IsTrue(downloadCompletedSuccessfully); + Assert.IsNotNull(memoryStream); + Assert.IsTrue(downloader.Package.IsSaveComplete); + Assert.IsNull(downloader.Package.FileName); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, memoryStream.Length); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(100.0, downloader.Package.SaveProgress); + Assert.IsTrue(DummyFileHelper.File16Kb.AreEqual(memoryStream)); + } - [TestInitialize] - public abstract void InitialTest(); + [TestMethod] + public async Task DownloadAndReadFileOnDownloadFileCompletedEventTest() + { + // arrange + var destFilename = Filename; + byte[] downloadedBytes = null; + var downloadCompletedSuccessfully = false; + var downloader = new DownloadService(Config); + downloader.DownloadFileCompleted += (s, e) => { + if (e.Cancelled == false && e.Error == null) + { + // Execute the downloaded file within completed event + // Note: Execute within this event caused to an IOException: + // The process cannot access the file '...\Temp\tmp14D3.tmp' + // because it is being used by another process.) - [TestMethod] - public async Task DownloadUrlWithFilenameOnMemoryTest() - { - // arrange - var downloadCompletedSuccessfully = false; - var downloader = new DownloadService(Config); - downloader.DownloadFileCompleted += (s, e) => { - if (e.Cancelled == false && e.Error == null) - { - downloadCompletedSuccessfully = true; - } - }; - - // act - using var memoryStream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - - // assert - Assert.IsTrue(downloadCompletedSuccessfully); - Assert.IsNotNull(memoryStream); - Assert.IsTrue(downloader.Package.IsSaveComplete); - Assert.IsNull(downloader.Package.FileName); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, memoryStream.Length); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(100.0, downloader.Package.SaveProgress); - Assert.IsTrue(DummyFileHelper.File16Kb.AreEqual(memoryStream)); - } + downloadCompletedSuccessfully = true; + downloadedBytes = File.ReadAllBytes(destFilename); + } + }; - [TestMethod] - public async Task DownloadAndReadFileOnDownloadFileCompletedEventTest() - { - // arrange - var destFilename = Filename; - byte[] downloadedBytes = null; - var downloadCompletedSuccessfully = false; - var downloader = new DownloadService(Config); - downloader.DownloadFileCompleted += (s, e) => { - if (e.Cancelled == false && e.Error == null) - { - // Execute the downloaded file within completed event - // Note: Execute within this event caused to an IOException: - // The process cannot access the file '...\Temp\tmp14D3.tmp' - // because it is being used by another process.) - - downloadCompletedSuccessfully = true; - downloadedBytes = File.ReadAllBytes(destFilename); - } - }; - - // act - await downloader.DownloadFileTaskAsync(URL, destFilename).ConfigureAwait(false); - - // assert - Assert.IsTrue(downloadCompletedSuccessfully); - Assert.IsNotNull(downloadedBytes); - Assert.AreEqual(destFilename, downloader.Package.FileName); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloadedBytes.Length); - Assert.IsTrue(DummyFileHelper.File16Kb.SequenceEqual(downloadedBytes)); - - File.Delete(destFilename); - } + // act + await downloader.DownloadFileTaskAsync(URL, destFilename).ConfigureAwait(false); - [TestMethod] - public async Task Download16KbWithoutFilenameOnDirectoryTest() - { - // arrange - var dir = new DirectoryInfo(DummyFileHelper.TempDirectory); - var downloader = new DownloadService(Config); - var filename = Path.Combine(dir.FullName, DummyFileHelper.FileSize16Kb.ToString()); - File.Delete(filename); - - // act - await downloader.DownloadFileTaskAsync(URL, dir).ConfigureAwait(false); - - // assert - Assert.IsTrue(downloader.Package.IsSaveComplete); - Assert.IsTrue(File.Exists(downloader.Package.FileName)); - Assert.IsNotNull(downloader.Package.FileName); - Assert.IsTrue(downloader.Package.FileName.StartsWith(DummyFileHelper.TempDirectory)); - Assert.AreEqual(filename, downloader.Package.FileName); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.IsTrue(DummyFileHelper.File16Kb.AreEqual(File.OpenRead(downloader.Package.FileName))); - - File.Delete(downloader.Package.FileName); - } + // assert + Assert.IsTrue(downloadCompletedSuccessfully); + Assert.IsNotNull(downloadedBytes); + Assert.AreEqual(destFilename, downloader.Package.FileName); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloadedBytes.Length); + Assert.IsTrue(DummyFileHelper.File16Kb.SequenceEqual(downloadedBytes)); + File.Delete(destFilename); + } - [TestMethod] - public async Task Download16KbWithFilenameTest() - { - // arrange - var downloader = new DownloadService(Config); + [TestMethod] + public async Task Download16KbWithoutFilenameOnDirectoryTest() + { + // arrange + var dir = new DirectoryInfo(DummyFileHelper.TempDirectory); + var downloader = new DownloadService(Config); + var filename = Path.Combine(dir.FullName, DummyFileHelper.FileSize16Kb.ToString()); + File.Delete(filename); + + // act + await downloader.DownloadFileTaskAsync(URL, dir).ConfigureAwait(false); + + // assert + Assert.IsTrue(downloader.Package.IsSaveComplete); + Assert.IsTrue(File.Exists(downloader.Package.FileName)); + Assert.IsNotNull(downloader.Package.FileName); + Assert.IsTrue(downloader.Package.FileName.StartsWith(DummyFileHelper.TempDirectory)); + Assert.AreEqual(filename, downloader.Package.FileName); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.IsTrue(DummyFileHelper.File16Kb.AreEqual(File.OpenRead(downloader.Package.FileName))); + + File.Delete(downloader.Package.FileName); + } - // act - await downloader.DownloadFileTaskAsync(URL, Path.GetTempFileName()).ConfigureAwait(false); - // assert - Assert.IsTrue(File.Exists(downloader.Package.FileName)); - Assert.IsNotNull(downloader.Package.FileName); - Assert.IsTrue(downloader.Package.FileName.StartsWith(DummyFileHelper.TempDirectory)); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.IsTrue(DummyFileHelper.File16Kb.AreEqual(File.OpenRead(downloader.Package.FileName))); + [TestMethod] + public async Task Download16KbWithFilenameTest() + { + // arrange + var downloader = new DownloadService(Config); - File.Delete(downloader.Package.FileName); - } + // act + await downloader.DownloadFileTaskAsync(URL, Path.GetTempFileName()).ConfigureAwait(false); - [TestMethod] - public async Task Download1KbWhenAnotherBiggerFileExistTest() - { - // arrange - var url1KbFile = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize1Kb); - var file = new FileInfo(Path.GetTempFileName()); - var downloader = new DownloadService(Config); - - // act - // write file bigger than download file - File.WriteAllBytes(file.FullName, DummyData.GenerateSingleBytes(2048, 250)); - // override file with downloader - await downloader.DownloadFileTaskAsync(url1KbFile, file.FullName).ConfigureAwait(false); - - // assert - Assert.IsTrue(File.Exists(file.FullName)); - Assert.AreEqual(file.FullName, downloader.Package.FileName); - Assert.AreEqual(DummyFileHelper.FileSize1Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(DummyFileHelper.FileSize1Kb, file.Length); - Assert.IsTrue(DummyFileHelper.File1Kb.AreEqual(file.OpenRead())); - - file.Delete(); - } + // assert + Assert.IsTrue(File.Exists(downloader.Package.FileName)); + Assert.IsNotNull(downloader.Package.FileName); + Assert.IsTrue(downloader.Package.FileName.StartsWith(DummyFileHelper.TempDirectory)); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.IsTrue(DummyFileHelper.File16Kb.AreEqual(File.OpenRead(downloader.Package.FileName))); - [TestMethod] - public async Task Download16KbOnMemoryTest() - { - // arrange - var downloader = new DownloadService(Config); + File.Delete(downloader.Package.FileName); + } - // act - var fileBytes = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + [TestMethod] + [Timeout(20_000)] + public async Task Download1KbWhenAnotherBiggerFileExistTest() + { + // arrange + var url1KbFile = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize1Kb); + var file = new FileInfo(Path.GetTempFileName()); + var downloader = new DownloadService(Config); + + // act + // write file bigger than download file + await File.WriteAllBytesAsync(file.FullName, DummyData.GenerateSingleBytes(2048, 250)); + // override file with downloader + await downloader.DownloadFileTaskAsync(url1KbFile, file.FullName).ConfigureAwait(false); + + // assert + Assert.IsTrue(File.Exists(file.FullName)); + Assert.AreEqual(file.FullName, downloader.Package.FileName); + Assert.AreEqual(DummyFileHelper.FileSize1Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(DummyFileHelper.FileSize1Kb, file.Length); + Assert.IsTrue(DummyFileHelper.File1Kb.AreEqual(file.OpenRead())); + + file.Delete(); + } - // assert - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, fileBytes.Length); - Assert.IsTrue(DummyFileHelper.File16Kb.AreEqual(fileBytes)); - } + [TestMethod] + public async Task Download16KbOnMemoryTest() + { + // arrange + var downloader = new DownloadService(Config); + + // act + var fileBytes = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + + // assert + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, fileBytes.Length); + Assert.IsTrue(DummyFileHelper.File16Kb.AreEqual(fileBytes)); + } + + [TestMethod] + public async Task DownloadProgressChangedTest() + { + // arrange + var downloader = new DownloadService(Config); + var progressChangedCount = (int)Math.Ceiling((double)DummyFileHelper.FileSize16Kb / Config.BufferBlockSize); + var progressCounter = 0; + downloader.DownloadProgressChanged += (s, e) => Interlocked.Increment(ref progressCounter); + + // act + await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + + // assert + // Note: some times received bytes on read stream method was less than block size! + Assert.IsTrue(progressChangedCount <= progressCounter); + Assert.AreEqual(100.0, downloader.Package.SaveProgress); + Assert.IsTrue(downloader.Package.IsSaveComplete); + Assert.IsFalse(downloader.Package.IsSaving); + } - [TestMethod] - public async Task DownloadProgressChangedTest() + [TestMethod] + public async Task StopResumeDownloadTest() + { + // arrange + var expectedStopCount = 2; + var stopCount = 0; + var cancellationsOccurrenceCount = 0; + var downloadFileExecutionCounter = 0; + var downloadCompletedSuccessfully = false; + var downloader = new DownloadService(Config); + downloader.DownloadFileCompleted += (s, e) => { + if (e.Cancelled && e.Error != null) + { + cancellationsOccurrenceCount++; + } + else + { + downloadCompletedSuccessfully = true; + } + }; + downloader.DownloadStarted += async delegate { + if (expectedStopCount > stopCount) + { + // Stopping after start of downloading + await downloader.CancelTaskAsync().ConfigureAwait(false); + stopCount++; + } + }; + + // act + await downloader.DownloadFileTaskAsync(URL, Path.GetTempFileName()).ConfigureAwait(false); + while (expectedStopCount > downloadFileExecutionCounter++) { - // arrange - var downloader = new DownloadService(Config); - var progressChangedCount = (int)Math.Ceiling((double)DummyFileHelper.FileSize16Kb / Config.BufferBlockSize); - var progressCounter = 0; - downloader.DownloadProgressChanged += (s, e) => Interlocked.Increment(ref progressCounter); - - // act - await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - - // assert - // Note: some times received bytes on read stream method was less than block size! - Assert.IsTrue(progressChangedCount <= progressCounter); - Assert.AreEqual(100.0, downloader.Package.SaveProgress); - Assert.IsTrue(downloader.Package.IsSaveComplete); - Assert.IsFalse(downloader.Package.IsSaving); + // resume download from stopped point. + await downloader.DownloadFileTaskAsync(downloader.Package).ConfigureAwait(false); } + var stream = File.ReadAllBytes(downloader.Package.FileName); - [TestMethod] - public async Task StopResumeDownloadTest() - { - // arrange - var expectedStopCount = 2; - var stopCount = 0; - var cancellationsOccurrenceCount = 0; - var downloadFileExecutionCounter = 0; - var downloadCompletedSuccessfully = false; - var downloader = new DownloadService(Config); - downloader.DownloadFileCompleted += (s, e) => { - if (e.Cancelled && e.Error != null) - { - cancellationsOccurrenceCount++; - } - else - { - downloadCompletedSuccessfully = true; - } - }; - downloader.DownloadStarted += async delegate { - if (expectedStopCount > stopCount) - { - // Stopping after start of downloading - await downloader.CancelTaskAsync().ConfigureAwait(false); - stopCount++; - } - }; - - // act - await downloader.DownloadFileTaskAsync(URL, Path.GetTempFileName()).ConfigureAwait(false); - while (expectedStopCount > downloadFileExecutionCounter++) + // assert + Assert.IsTrue(File.Exists(downloader.Package.FileName)); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(expectedStopCount, stopCount); + Assert.AreEqual(expectedStopCount, cancellationsOccurrenceCount); + Assert.IsTrue(downloadCompletedSuccessfully); + Assert.IsTrue(DummyFileHelper.File16Kb.SequenceEqual(stream.ToArray())); + + File.Delete(downloader.Package.FileName); + } + + [TestMethod] + public async Task PauseResumeDownloadTest() + { + // arrange + var expectedPauseCount = 2; + var pauseCount = 0; + var downloadCompletedSuccessfully = false; + var downloader = new DownloadService(Config); + downloader.DownloadFileCompleted += (s, e) => { + if (e.Cancelled == false && e.Error is null) + downloadCompletedSuccessfully = true; + }; + downloader.DownloadProgressChanged += delegate { + if (expectedPauseCount > pauseCount) { - // resume download from stopped point. - await downloader.DownloadFileTaskAsync(downloader.Package).ConfigureAwait(false); + // Stopping after start of downloading + downloader.Pause(); + pauseCount++; + downloader.Resume(); } - var stream = File.ReadAllBytes(downloader.Package.FileName); + }; - // assert - Assert.IsTrue(File.Exists(downloader.Package.FileName)); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(expectedStopCount, stopCount); - Assert.AreEqual(expectedStopCount, cancellationsOccurrenceCount); - Assert.IsTrue(downloadCompletedSuccessfully); - Assert.IsTrue(DummyFileHelper.File16Kb.SequenceEqual(stream.ToArray())); + // act + await downloader.DownloadFileTaskAsync(URL, Path.GetTempFileName()).ConfigureAwait(false); + var stream = File.ReadAllBytes(downloader.Package.FileName); - File.Delete(downloader.Package.FileName); - } + // assert + Assert.IsFalse(downloader.IsPaused); + Assert.IsTrue(File.Exists(downloader.Package.FileName)); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(expectedPauseCount, pauseCount); + Assert.IsTrue(downloadCompletedSuccessfully); + Assert.IsTrue(DummyFileHelper.File16Kb.SequenceEqual(stream.ToArray())); - [TestMethod] - public async Task PauseResumeDownloadTest() - { - // arrange - var expectedPauseCount = 2; - var pauseCount = 0; - var downloadCompletedSuccessfully = false; - var downloader = new DownloadService(Config); - downloader.DownloadFileCompleted += (s, e) => { - if (e.Cancelled == false && e.Error is null) - downloadCompletedSuccessfully = true; - }; - downloader.DownloadProgressChanged += delegate { - if (expectedPauseCount > pauseCount) - { - // Stopping after start of downloading - downloader.Pause(); - pauseCount++; - downloader.Resume(); - } - }; - - // act - await downloader.DownloadFileTaskAsync(URL, Path.GetTempFileName()).ConfigureAwait(false); - var stream = File.ReadAllBytes(downloader.Package.FileName); - - // assert - Assert.IsFalse(downloader.IsPaused); - Assert.IsTrue(File.Exists(downloader.Package.FileName)); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(expectedPauseCount, pauseCount); - Assert.IsTrue(downloadCompletedSuccessfully); - Assert.IsTrue(DummyFileHelper.File16Kb.SequenceEqual(stream.ToArray())); - - File.Delete(downloader.Package.FileName); - } + File.Delete(downloader.Package.FileName); + } - [TestMethod] - public async Task StopResumeDownloadFromLastPositionTest() - { - // arrange - var expectedStopCount = 1; - var stopCount = 0; - var downloadFileExecutionCounter = 0; - var totalProgressedByteSize = 0L; - var totalReceivedBytes = 0L; - - var config = (DownloadConfiguration)Config.Clone(); - config.BufferBlockSize = 1024; - var downloader = new DownloadService(config); - downloader.DownloadProgressChanged += (s, e) => { - totalProgressedByteSize += e.ProgressedByteSize; - totalReceivedBytes += e.ReceivedBytes.Length; - if (expectedStopCount > stopCount) - { - // Stopping after start of downloading - downloader.CancelAsync(); - stopCount++; - } - }; - - // act - await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - while (expectedStopCount > downloadFileExecutionCounter++) + [TestMethod] + public async Task StopResumeDownloadFromLastPositionTest() + { + // arrange + var expectedStopCount = 1; + var stopCount = 0; + var downloadFileExecutionCounter = 0; + var totalProgressedByteSize = 0L; + var totalReceivedBytes = 0L; + + var config = (DownloadConfiguration)Config.Clone(); + config.BufferBlockSize = 1024; + var downloader = new DownloadService(config); + downloader.DownloadProgressChanged += (s, e) => { + totalProgressedByteSize += e.ProgressedByteSize; + totalReceivedBytes += e.ReceivedBytes.Length; + if (expectedStopCount > stopCount) { - // resume download from stopped point. - await downloader.DownloadFileTaskAsync(downloader.Package).ConfigureAwait(false); + // Stopping after start of downloading + downloader.CancelAsync(); + stopCount++; } + }; - // assert - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, totalProgressedByteSize); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, totalReceivedBytes); + // act + await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + while (expectedStopCount > downloadFileExecutionCounter++) + { + // resume download from stopped point. + await downloader.DownloadFileTaskAsync(downloader.Package).ConfigureAwait(false); } - [TestMethod] - public async Task StopResumeDownloadOverFirstPackagePositionTest() - { - // arrange - var cancellationCount = 4; - var downloader = new DownloadService(Config); - var isSavingStateOnCancel = false; - var isSavingStateBeforCancel = false; - - downloader.DownloadProgressChanged += async (s, e) => { - isSavingStateBeforCancel |= downloader.Package.IsSaving; - if (--cancellationCount > 0) - { - // Stopping after start of downloading - await downloader.CancelTaskAsync().ConfigureAwait(false); - } - }; - - // act - var result = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - // check point of package for once time - var firstCheckPointPackage = JsonConvert.SerializeObject(downloader.Package); - - while (downloader.IsCancelled) - { - isSavingStateOnCancel |= downloader.Package.IsSaving; - var restoredPackage = JsonConvert.DeserializeObject(firstCheckPointPackage); + // assert + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, totalProgressedByteSize); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, totalReceivedBytes); + } - // resume download from first stopped point. - result = await downloader.DownloadFileTaskAsync(restoredPackage).ConfigureAwait(false); + [TestMethod] + public async Task StopResumeDownloadOverFirstPackagePositionTest() + { + // arrange + var cancellationCount = 4; + var downloader = new DownloadService(Config); + var isSavingStateOnCancel = false; + var isSavingStateBeforCancel = false; + + downloader.DownloadProgressChanged += async (s, e) => { + isSavingStateBeforCancel |= downloader.Package.IsSaving; + if (--cancellationCount > 0) + { + // Stopping after start of downloading + await downloader.CancelTaskAsync().ConfigureAwait(false); } + }; - // assert - Assert.IsTrue(downloader.Package.IsSaveComplete); - Assert.IsFalse(downloader.Package.IsSaving); - Assert.IsFalse(isSavingStateOnCancel); - Assert.IsTrue(isSavingStateBeforCancel); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, result.Length); - } + // act + var result = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + // check point of package for once time + var firstCheckPointPackage = JsonConvert.SerializeObject(downloader.Package); - [TestMethod] - public async Task TestTotalReceivedBytesWhenResumeDownload() + while (downloader.IsCancelled) { - // arrange - var canStopDownload = true; - var totalDownloadSize = 0L; - var lastProgressPercentage = 0.0; - - var config = (DownloadConfiguration)Config.Clone(); - config.BufferBlockSize = 1024; - config.ChunkCount = 1; - var downloader = new DownloadService(config); - downloader.DownloadProgressChanged += async (s, e) => { - totalDownloadSize += e.ReceivedBytes.Length; - lastProgressPercentage = e.ProgressPercentage; - if (canStopDownload && totalDownloadSize > DummyFileHelper.FileSize16Kb / 2) - { - // Stopping after start of downloading - await downloader.CancelTaskAsync().ConfigureAwait(false); - canStopDownload = false; - } - }; - - // act - await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - await downloader.DownloadFileTaskAsync(downloader.Package).ConfigureAwait(false); // resume download from stopped point. - - // assert - Assert.IsTrue(downloader.Package.IsSaveComplete); - Assert.IsFalse(downloader.IsCancelled); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, totalDownloadSize); - Assert.AreEqual(100.0, lastProgressPercentage); - } + isSavingStateOnCancel |= downloader.Package.IsSaving; + var restoredPackage = JsonConvert.DeserializeObject(firstCheckPointPackage); - [TestMethod] - public async Task TestTotalReceivedBytesOnResumeDownloadWhenLostDownloadedData() - { - // arrange - var canStopDownload = true; - var totalDownloadSize = 0L; - var lastProgressPercentage = 0.0; - - var config = (DownloadConfiguration)Config.Clone(); - config.BufferBlockSize = 1024; - config.ChunkCount = 1; - var downloader = new DownloadService(config); - downloader.DownloadProgressChanged += (s, e) => { - totalDownloadSize = e.ReceivedBytesSize; - lastProgressPercentage = e.ProgressPercentage; - if (canStopDownload && totalDownloadSize > DummyFileHelper.FileSize16Kb / 2) - { - // Stopping after start of downloading - downloader.CancelAsync(); - canStopDownload = false; - } - }; - - // act - await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - downloader.Package.Storage.Dispose(); // set position to zero - await downloader.DownloadFileTaskAsync(downloader.Package).ConfigureAwait(false); // resume download from stopped point. - - // assert - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, totalDownloadSize); - Assert.AreEqual(100.0, lastProgressPercentage); - Assert.AreEqual(100.0, downloader.Package.SaveProgress); + // resume download from first stopped point. + result = await downloader.DownloadFileTaskAsync(restoredPackage).ConfigureAwait(false); } - [TestMethod] - //[Timeout(17_000)] - public async Task SpeedLimitTest() - { - // arrange - double averageSpeed = 0; - var progressCounter = 0; - Config.BufferBlockSize = 1024; - Config.MaximumBytesPerSecond = 2048; // Byte/s - var downloader = new DownloadService(Config); - downloader.DownloadProgressChanged += (s, e) => { - averageSpeed = ((averageSpeed * progressCounter) + e.BytesPerSecondSpeed) / (progressCounter + 1); - progressCounter++; - }; - - // act - await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - - // assert - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.IsTrue(averageSpeed <= Config.MaximumBytesPerSecond * 1.5, $"Average Speed: {averageSpeed} , Speed Limit: {Config.MaximumBytesPerSecond}"); - } + // assert + Assert.IsTrue(downloader.Package.IsSaveComplete); + Assert.IsFalse(downloader.Package.IsSaving); + Assert.IsFalse(isSavingStateOnCancel); + Assert.IsTrue(isSavingStateBeforCancel); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, result.Length); + } - [TestMethod] - public async Task DynamicSpeedLimitTest() - { - // arrange - double upperTolerance = 1.5; // 50% upper than expected avg speed - double expectedAverageSpeed = DummyFileHelper.FileSize16Kb / 32; // == (256*16 + 512*8 + 1024*4 + 2048*2) / 32 - double averageSpeed = 0; - var progressCounter = 0; - const int oneSpeedStepSize = 4096; // DummyFileHelper.FileSize16Kb / 4 - - Config.MaximumBytesPerSecond = 256; // Byte/s - var downloader = new DownloadService(Config); - - downloader.DownloadProgressChanged += (s, e) => { - averageSpeed += e.BytesPerSecondSpeed; - progressCounter++; - - var pow = Math.Ceiling((double)e.ReceivedBytesSize / oneSpeedStepSize); - Config.MaximumBytesPerSecond = 128 * (int)Math.Pow(2, pow); // 256, 512, 1024, 2048 - }; - - // act - await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - averageSpeed /= progressCounter; - - // assert - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.IsTrue(averageSpeed <= expectedAverageSpeed * upperTolerance, - $"Avg Speed: {averageSpeed} , Expected Avg Speed Limit: {expectedAverageSpeed * upperTolerance}, " + - $"Progress Count: {progressCounter}"); - } + [TestMethod] + public async Task TestTotalReceivedBytesWhenResumeDownload() + { + // arrange + var canStopDownload = true; + var totalDownloadSize = 0L; + var lastProgressPercentage = 0.0; + + var config = (DownloadConfiguration)Config.Clone(); + config.BufferBlockSize = 1024; + config.ChunkCount = 1; + var downloader = new DownloadService(config); + downloader.DownloadProgressChanged += async (s, e) => { + totalDownloadSize += e.ReceivedBytes.Length; + lastProgressPercentage = e.ProgressPercentage; + if (canStopDownload && totalDownloadSize > DummyFileHelper.FileSize16Kb / 2) + { + // Stopping after start of downloading + await downloader.CancelTaskAsync().ConfigureAwait(false); + canStopDownload = false; + } + }; + + // act + await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + await downloader.DownloadFileTaskAsync(downloader.Package).ConfigureAwait(false); // resume download from stopped point. + + // assert + Assert.IsTrue(downloader.Package.IsSaveComplete); + Assert.IsFalse(downloader.IsCancelled); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, totalDownloadSize); + Assert.AreEqual(100.0, lastProgressPercentage); + } - [TestMethod] - public async Task TestSizeWhenDownloadOnMemoryStream() - { - // arrange - var downloader = new DownloadService(Config); + [TestMethod] + public async Task TestTotalReceivedBytesOnResumeDownloadWhenLostDownloadedData() + { + // arrange + var canStopDownload = true; + var totalDownloadSize = 0L; + var lastProgressPercentage = 0.0; + + var config = (DownloadConfiguration)Config.Clone(); + config.BufferBlockSize = 1024; + config.ChunkCount = 1; + var downloader = new DownloadService(config); + downloader.DownloadProgressChanged += (s, e) => { + totalDownloadSize = e.ReceivedBytesSize; + lastProgressPercentage = e.ProgressPercentage; + if (canStopDownload && totalDownloadSize > DummyFileHelper.FileSize16Kb / 2) + { + // Stopping after start of downloading + downloader.CancelAsync(); + canStopDownload = false; + } + }; + + // act + await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + downloader.Package.Storage.Dispose(); // set position to zero + await downloader.DownloadFileTaskAsync(downloader.Package).ConfigureAwait(false); // resume download from stopped point. + + // assert + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, totalDownloadSize); + Assert.AreEqual(100.0, lastProgressPercentage); + Assert.AreEqual(100.0, downloader.Package.SaveProgress); + } - // act - using var stream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + [TestMethod] + //[Timeout(17_000)] + public async Task SpeedLimitTest() + { + // arrange + double averageSpeed = 0; + var progressCounter = 0; + Config.BufferBlockSize = 1024; + Config.MaximumBytesPerSecond = 2048; // Byte/s + var downloader = new DownloadService(Config); + downloader.DownloadProgressChanged += (s, e) => { + averageSpeed = ((averageSpeed * progressCounter) + e.BytesPerSecondSpeed) / (progressCounter + 1); + progressCounter++; + }; + + // act + await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + + // assert + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.IsTrue(averageSpeed <= Config.MaximumBytesPerSecond * 1.5, $"Average Speed: {averageSpeed} , Speed Limit: {Config.MaximumBytesPerSecond}"); + } - // assert - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, stream.Length); - } + [TestMethod] + public async Task DynamicSpeedLimitTest() + { + // arrange + double upperTolerance = 1.5; // 50% upper than expected avg speed + double expectedAverageSpeed = DummyFileHelper.FileSize16Kb / 32; // == (256*16 + 512*8 + 1024*4 + 2048*2) / 32 + double averageSpeed = 0; + var progressCounter = 0; + const int oneSpeedStepSize = 4096; // DummyFileHelper.FileSize16Kb / 4 + + Config.MaximumBytesPerSecond = 256; // Byte/s + var downloader = new DownloadService(Config); + + downloader.DownloadProgressChanged += (s, e) => { + averageSpeed += e.BytesPerSecondSpeed; + progressCounter++; + + var pow = Math.Ceiling((double)e.ReceivedBytesSize / oneSpeedStepSize); + Config.MaximumBytesPerSecond = 128 * (int)Math.Pow(2, pow); // 256, 512, 1024, 2048 + }; + + // act + await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + averageSpeed /= progressCounter; + + // assert + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.IsTrue(averageSpeed <= expectedAverageSpeed * upperTolerance, + $"Avg Speed: {averageSpeed} , Expected Avg Speed Limit: {expectedAverageSpeed * upperTolerance}, " + + $"Progress Count: {progressCounter}"); + } - [TestMethod] - public async Task TestTypeWhenDownloadOnMemoryStream() - { - // arrange - var downloader = new DownloadService(Config); + [TestMethod] + public async Task TestSizeWhenDownloadOnMemoryStream() + { + // arrange + var downloader = new DownloadService(Config); - // act - using var stream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + // act + using var stream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - // assert - Assert.IsTrue(stream is MemoryStream); - } + // assert + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, stream.Length); + } - [TestMethod] - public async Task TestContentWhenDownloadOnMemoryStream() - { - // arrange - var downloader = new DownloadService(Config); + [TestMethod] + public async Task TestTypeWhenDownloadOnMemoryStream() + { + // arrange + var downloader = new DownloadService(Config); - // act - using var stream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - var memStream = stream as MemoryStream; + // act + using var stream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - // assert - Assert.IsTrue(DummyFileHelper.File16Kb.SequenceEqual(memStream.ToArray())); - } + // assert + Assert.IsTrue(stream is MemoryStream); + } - [TestMethod] - public async Task Download256BytesRangeOfFileTest() - { - // arrange - Config.RangeDownload = true; - Config.RangeLow = 256; - Config.RangeHigh = 511; - var totalSize = Config.RangeHigh - Config.RangeLow + 1; - var downloader = new DownloadService(Config); - - // act - using var stream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - var bytes = ((MemoryStream)stream).ToArray(); - - // assert - Assert.IsNotNull(stream); - Assert.AreEqual(totalSize, stream.Length); - Assert.AreEqual(totalSize, downloader.Package.TotalFileSize); - Assert.AreEqual(100.0, downloader.Package.SaveProgress); - for (int i = 0; i < totalSize; i++) - Assert.AreEqual((byte)i, bytes[i]); - } + [TestMethod] + public async Task TestContentWhenDownloadOnMemoryStream() + { + // arrange + var downloader = new DownloadService(Config); - [TestMethod] - public async Task DownloadNegetiveRangeOfFileTest() - { - // arrange - Config.RangeDownload = true; - Config.RangeLow = -256; - Config.RangeHigh = 255; - var totalSize = 256; - var downloader = new DownloadService(Config); - - // act - using var stream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - var bytes = ((MemoryStream)stream).ToArray(); - - // assert - Assert.IsNotNull(stream); - Assert.AreEqual(totalSize, stream.Length); - Assert.AreEqual(totalSize, downloader.Package.TotalFileSize); - Assert.AreEqual(100.0, downloader.Package.SaveProgress); - for (int i = 0; i < totalSize; i++) - Assert.AreEqual((byte)i, bytes[i]); - } + // act + using var stream = await downloader.DownloadFileTaskAsync(URL); + var memStream = stream as MemoryStream; - [TestMethod] - public async Task TestDownloadParallelVsHalfOfChunks() - { - // arrange - var maxParallelCountTasks = Config.ChunkCount / 2; - Config.ParallelCount = maxParallelCountTasks; - var downloader = new DownloadService(Config); - var actualMaxParallelCountTasks = 0; - downloader.ChunkDownloadProgressChanged += (s, e) => { - actualMaxParallelCountTasks = Math.Max(actualMaxParallelCountTasks, e.ActiveChunks); - }; - - // act - using var stream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - var bytes = ((MemoryStream)stream).ToArray(); - - // assert - Assert.IsTrue(maxParallelCountTasks >= actualMaxParallelCountTasks); - Assert.IsNotNull(stream); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, stream.Length); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(100.0, downloader.Package.SaveProgress); - for (int i = 0; i < DummyFileHelper.FileSize16Kb; i++) - Assert.AreEqual((byte)i, bytes[i]); - } + // assert + Assert.IsTrue(DummyFileHelper.File16Kb.SequenceEqual(memStream.ToArray())); + } - [TestMethod] - [Timeout(10000)] - public async Task TestResumeImmediatelyAfterCanceling() - { - // arrange - var canStopDownload = true; - var lastProgressPercentage = 0d; - bool? stopped = null; - var downloader = new DownloadService(Config); - downloader.DownloadFileCompleted += (s, e) => stopped ??= e.Cancelled; - downloader.DownloadProgressChanged += (s, e) => { - if (canStopDownload && e.ProgressPercentage > 50) - { - canStopDownload = false; - downloader.CancelAsync(); - } - else if (!canStopDownload && lastProgressPercentage <= 0) - { - lastProgressPercentage = e.ProgressPercentage; - } - }; - - // act - await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); - using var stream = await downloader.DownloadFileTaskAsync(downloader.Package).ConfigureAwait(false); // resume - - // assert - Assert.IsTrue(stopped); - Assert.IsTrue(lastProgressPercentage > 50); - Assert.IsTrue(downloader.Package.IsSaveComplete); - Assert.IsFalse(downloader.IsCancelled); - } + [TestMethod] + [Timeout(60_000)] + public async Task Download256BytesRangeOfFileTest() + { + // arrange + Config.RangeDownload = true; + Config.RangeLow = 256; + Config.RangeHigh = 511; + var totalSize = Config.RangeHigh - Config.RangeLow + 1; + var downloader = new DownloadService(Config); + + // act + using var stream = await downloader.DownloadFileTaskAsync(URL); + var bytes = ((MemoryStream)stream).ToArray(); + + // assert + Assert.IsNotNull(stream); + Assert.AreEqual(totalSize, stream.Length); + Assert.AreEqual(totalSize, downloader.Package.TotalFileSize); + Assert.AreEqual(100.0, downloader.Package.SaveProgress); + for (int i = 0; i < totalSize; i++) + Assert.AreEqual((byte)i, bytes[i]); + } - [TestMethod] - public async Task KeepFileWhenDownloadFailedTest() - { - await KeepOrRemoveFileWhenDownloadFailedTest(false); - } + [TestMethod] + public async Task DownloadNegetiveRangeOfFileTest() + { + // arrange + Config.RangeDownload = true; + Config.RangeLow = -256; + Config.RangeHigh = 255; + var totalSize = 256; + var downloader = new DownloadService(Config); + + // act + using var stream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + var bytes = ((MemoryStream)stream).ToArray(); + + // assert + Assert.IsNotNull(stream); + Assert.AreEqual(totalSize, stream.Length); + Assert.AreEqual(totalSize, downloader.Package.TotalFileSize); + Assert.AreEqual(100.0, downloader.Package.SaveProgress); + for (int i = 0; i < totalSize; i++) + Assert.AreEqual((byte)i, bytes[i]); + } - [TestMethod] - public async Task RemoveFileWhenDownloadFailedTest() - { - await KeepOrRemoveFileWhenDownloadFailedTest(true); - } + [TestMethod] + public async Task TestDownloadParallelVsHalfOfChunks() + { + // arrange + var maxParallelCountTasks = Config.ChunkCount / 2; + Config.ParallelCount = maxParallelCountTasks; + var downloader = new DownloadService(Config); + var actualMaxParallelCountTasks = 0; + downloader.ChunkDownloadProgressChanged += (s, e) => { + actualMaxParallelCountTasks = Math.Max(actualMaxParallelCountTasks, e.ActiveChunks); + }; + + // act + using var stream = await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + var bytes = ((MemoryStream)stream).ToArray(); + + // assert + Assert.IsTrue(maxParallelCountTasks >= actualMaxParallelCountTasks); + Assert.IsNotNull(stream); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, stream.Length); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(100.0, downloader.Package.SaveProgress); + for (int i = 0; i < DummyFileHelper.FileSize16Kb; i++) + Assert.AreEqual((byte)i, bytes[i]); + } - private async Task KeepOrRemoveFileWhenDownloadFailedTest(bool clearFileAfterFailure) - { - // arrange - Config.MaxTryAgainOnFailover = 0; - Config.ClearPackageOnCompletionWithFailure = clearFileAfterFailure; - var downloadService = new DownloadService(Config); - var filename = Path.GetTempFileName(); - var url = DummyFileHelper.GetFileWithFailureAfterOffset(DummyFileHelper.FileSize16Kb, DummyFileHelper.FileSize16Kb / 2); - - // act - await downloadService.DownloadFileTaskAsync(url, filename).ConfigureAwait(false); - - // assert - Assert.AreEqual(filename, downloadService.Package.FileName); - Assert.IsFalse(downloadService.Package.IsSaveComplete); - Assert.IsFalse(downloadService.Package.IsSaving); - Assert.AreNotEqual(clearFileAfterFailure, File.Exists(filename)); - } + [TestMethod] + [Timeout(10000)] + public async Task TestResumeImmediatelyAfterCanceling() + { + // arrange + var canStopDownload = true; + var lastProgressPercentage = 0d; + bool? stopped = null; + var downloader = new DownloadService(Config); + downloader.DownloadFileCompleted += (s, e) => stopped ??= e.Cancelled; + downloader.DownloadProgressChanged += (s, e) => { + if (canStopDownload && e.ProgressPercentage > 50) + { + canStopDownload = false; + downloader.CancelAsync(); + } + else if (!canStopDownload && lastProgressPercentage <= 0) + { + lastProgressPercentage = e.ProgressPercentage; + } + }; - [TestMethod] - public async Task TestRetryDownloadAfterTimeout() - { - await testRetryDownloadAfterFailure(true); - } + // act + await downloader.DownloadFileTaskAsync(URL).ConfigureAwait(false); + using var stream = await downloader.DownloadFileTaskAsync(downloader.Package).ConfigureAwait(false); // resume - [TestMethod] - public async Task TestRetryDownloadAfterFailure() - { - await testRetryDownloadAfterFailure(false); - } + // assert + Assert.IsTrue(stopped); + Assert.IsTrue(lastProgressPercentage > 50); + Assert.IsTrue(downloader.Package.IsSaveComplete); + Assert.IsFalse(downloader.IsCancelled); + } - private async Task testRetryDownloadAfterFailure(bool timeout) - { - // arrange - Exception error = null; - var fileSize = DummyFileHelper.FileSize16Kb; - var failureOffset = fileSize / 2; - Config.MaxTryAgainOnFailover = 5; - Config.BufferBlockSize = 1024; - Config.MinimumSizeOfChunking = 0; - Config.Timeout = 100; - Config.ClearPackageOnCompletionWithFailure = false; - var downloadService = new DownloadService(Config); - var url = timeout - ? DummyFileHelper.GetFileWithTimeoutAfterOffset(fileSize, failureOffset) - : DummyFileHelper.GetFileWithFailureAfterOffset(fileSize, failureOffset); - downloadService.DownloadFileCompleted += (s, e) => error = e.Error; - - // act - var stream = await downloadService.DownloadFileTaskAsync(url).ConfigureAwait(false); - var retryCount = downloadService.Package.Chunks.Sum(chunk => chunk.FailoverCount); - - // assert - Assert.IsFalse(downloadService.Package.IsSaveComplete); - Assert.IsFalse(downloadService.Package.IsSaving); - Assert.AreEqual(DownloadStatus.Failed, downloadService.Package.Status); - Assert.IsTrue(Config.MaxTryAgainOnFailover <= retryCount); - Assert.IsNotNull(error); - Assert.IsInstanceOfType(error, typeof(WebException)); - Assert.AreEqual(failureOffset, stream.Length); - - await stream.DisposeAsync(); - } + [TestMethod] + public async Task KeepFileWhenDownloadFailedTest() + { + await KeepOrRemoveFileWhenDownloadFailedTest(false); + } - [TestMethod] - public async Task DownloadMultipleFilesWithOneDownloaderInstanceTest() - { - // arrange - var size1 = 1024 * 8; - var size2 = 1024 * 16; - var size3 = 1024 * 32; - var url1 = DummyFileHelper.GetFileUrl(size1); - var url2 = DummyFileHelper.GetFileUrl(size2); - var url3 = DummyFileHelper.GetFileUrl(size3); - var downloader = new DownloadService(Config); - - // act - var file1 = await downloader.DownloadFileTaskAsync(url1).ConfigureAwait(false); - var file2 = await downloader.DownloadFileTaskAsync(url2).ConfigureAwait(false); - var file3 = await downloader.DownloadFileTaskAsync(url3).ConfigureAwait(false); - - // assert - Assert.AreEqual(size1, file1.Length); - Assert.AreEqual(size2, file2.Length); - Assert.AreEqual(size3, file3.Length); - } + [TestMethod] + public async Task RemoveFileWhenDownloadFailedTest() + { + await KeepOrRemoveFileWhenDownloadFailedTest(true); + } - [TestMethod] - public async Task TestStopDownloadWithCancellationToken() - { - // arrange - var downloadProgress = 0d; - var downloadCancelled = false; - var cancelltionTokenSource = new CancellationTokenSource(); - var downloader = new DownloadService(Config); - downloader.DownloadFileCompleted += (s, e) => downloadCancelled = e.Cancelled; - downloader.DownloadProgressChanged += (s, e) => { - downloadProgress = e.ProgressPercentage; - if (e.ProgressPercentage > 10) - { - // Stopping after 10% progress of downloading - cancelltionTokenSource.Cancel(); - } - }; - - // act - await downloader.DownloadFileTaskAsync(URL, cancelltionTokenSource.Token).ConfigureAwait(false); - - // assert - Assert.IsTrue(downloadCancelled); - Assert.IsTrue(downloader.IsCancelled); - Assert.IsTrue(downloader.Status == DownloadStatus.Stopped); - Assert.IsTrue(downloadProgress > 10); - } + private async Task KeepOrRemoveFileWhenDownloadFailedTest(bool clearFileAfterFailure) + { + // arrange + Config.MaxTryAgainOnFailover = 0; + Config.ClearPackageOnCompletionWithFailure = clearFileAfterFailure; + var downloadService = new DownloadService(Config); + var filename = Path.GetTempFileName(); + var url = DummyFileHelper.GetFileWithFailureAfterOffset(DummyFileHelper.FileSize16Kb, DummyFileHelper.FileSize16Kb / 2); + + // act + await downloadService.DownloadFileTaskAsync(url, filename).ConfigureAwait(false); + + // assert + Assert.AreEqual(filename, downloadService.Package.FileName); + Assert.IsFalse(downloadService.Package.IsSaveComplete); + Assert.IsFalse(downloadService.Package.IsSaving); + Assert.AreNotEqual(clearFileAfterFailure, File.Exists(filename)); + } - [TestMethod] - public async Task TestResumeDownloadWithAnotherUrl() - { - // arrange - var url1 = DummyFileHelper.GetFileWithNameUrl("file1.dat", DummyFileHelper.FileSize16Kb); - var url2 = DummyFileHelper.GetFileWithNameUrl("file2.dat", DummyFileHelper.FileSize16Kb); - var canStopDownload = true; - var totalDownloadSize = 0L; - var config = (DownloadConfiguration)Config.Clone(); - config.BufferBlockSize = 1024; - config.ChunkCount = 4; - var downloader = new DownloadService(config); - downloader.DownloadProgressChanged += (s, e) => { - totalDownloadSize = e.ReceivedBytesSize; - if (canStopDownload && totalDownloadSize > DummyFileHelper.FileSize16Kb / 2) - { - // Stopping after start of downloading - downloader.CancelAsync(); - canStopDownload = false; - } - }; - - // act - await downloader.DownloadFileTaskAsync(url1).ConfigureAwait(false); - await downloader.DownloadFileTaskAsync(downloader.Package, url2); // resume download with new url2. - - // assert - Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); - Assert.AreEqual(DummyFileHelper.FileSize16Kb, totalDownloadSize); - Assert.AreEqual(downloader.Package.Storage.Length, DummyFileHelper.FileSize16Kb); - Assert.AreEqual(100.0, downloader.Package.SaveProgress); - } + [TestMethod] + public async Task TestRetryDownloadAfterTimeout() + { + await testRetryDownloadAfterFailure(true); + } - [TestMethod] - public async Task DownloadAFileFrom8UrlsWith8ChunksTest() - { - await DownloadAFileFromMultipleUrlsWithMultipleChunksTest(8, 8); - } + [TestMethod] + public async Task TestRetryDownloadAfterFailure() + { + await testRetryDownloadAfterFailure(false); + } - [TestMethod] - public async Task DownloadAFileFrom2UrlsWith8ChunksTest() - { - await DownloadAFileFromMultipleUrlsWithMultipleChunksTest(2, 8); - } + private async Task testRetryDownloadAfterFailure(bool timeout) + { + // arrange + Exception error = null; + var fileSize = DummyFileHelper.FileSize16Kb; + var failureOffset = fileSize / 2; + Config.MaxTryAgainOnFailover = 5; + Config.BufferBlockSize = 1024; + Config.MinimumSizeOfChunking = 0; + Config.Timeout = 100; + Config.ClearPackageOnCompletionWithFailure = false; + var downloadService = new DownloadService(Config); + var url = timeout + ? DummyFileHelper.GetFileWithTimeoutAfterOffset(fileSize, failureOffset) + : DummyFileHelper.GetFileWithFailureAfterOffset(fileSize, failureOffset); + downloadService.DownloadFileCompleted += (s, e) => error = e.Error; + + // act + var stream = await downloadService.DownloadFileTaskAsync(url).ConfigureAwait(false); + var retryCount = downloadService.Package.Chunks.Sum(chunk => chunk.FailoverCount); + + // assert + Assert.IsFalse(downloadService.Package.IsSaveComplete); + Assert.IsFalse(downloadService.Package.IsSaving); + Assert.AreEqual(DownloadStatus.Failed, downloadService.Package.Status); + Assert.IsTrue(Config.MaxTryAgainOnFailover <= retryCount); + Assert.IsNotNull(error); + Assert.IsInstanceOfType(error, typeof(WebException)); + Assert.AreEqual(failureOffset, stream.Length); + + await stream.DisposeAsync(); + } - [TestMethod] - public async Task DownloadAFileFrom8UrlsWith2ChunksTest() - { - await DownloadAFileFromMultipleUrlsWithMultipleChunksTest(8, 2); - } + [TestMethod] + public async Task DownloadMultipleFilesWithOneDownloaderInstanceTest() + { + // arrange + var size1 = 1024 * 8; + var size2 = 1024 * 16; + var size3 = 1024 * 32; + var url1 = DummyFileHelper.GetFileUrl(size1); + var url2 = DummyFileHelper.GetFileUrl(size2); + var url3 = DummyFileHelper.GetFileUrl(size3); + var downloader = new DownloadService(Config); + + // act + var file1 = await downloader.DownloadFileTaskAsync(url1).ConfigureAwait(false); + var file2 = await downloader.DownloadFileTaskAsync(url2).ConfigureAwait(false); + var file3 = await downloader.DownloadFileTaskAsync(url3).ConfigureAwait(false); + + // assert + Assert.AreEqual(size1, file1.Length); + Assert.AreEqual(size2, file2.Length); + Assert.AreEqual(size3, file3.Length); + } - public async Task DownloadAFileFromMultipleUrlsWithMultipleChunksTest(int urlsCount, int chunksCount) - { - // arrange - Config.ChunkCount = chunksCount; - Config.ParallelCount = chunksCount; - var totalSize = DummyFileHelper.FileSize16Kb; - var chunkSize = totalSize / Config.ChunkCount; - var downloader = new DownloadService(Config); - var urls = Enumerable.Range(1, urlsCount) - .Select(i => DummyFileHelper.GetFileWithNameUrl("testfile_" + i, totalSize, (byte)i)) - .ToArray(); - - // act - using var stream = await downloader.DownloadFileTaskAsync(urls).ConfigureAwait(false); - var bytes = ((MemoryStream)stream).ToArray(); - - // assert - Assert.IsNotNull(stream); - Assert.AreEqual(totalSize, stream.Length); - Assert.AreEqual(totalSize, downloader.Package.TotalFileSize); - Assert.AreEqual(100.0, downloader.Package.SaveProgress); - for (int i = 0; i < totalSize; i++) + [TestMethod] + public async Task TestStopDownloadWithCancellationToken() + { + // arrange + var downloadProgress = 0d; + var downloadCancelled = false; + var cancelltionTokenSource = new CancellationTokenSource(); + var downloader = new DownloadService(Config); + downloader.DownloadFileCompleted += (s, e) => downloadCancelled = e.Cancelled; + downloader.DownloadProgressChanged += (s, e) => { + downloadProgress = e.ProgressPercentage; + if (e.ProgressPercentage > 10) { - var chunkIndex = (byte)(i / chunkSize); - var expectedByte = (chunkIndex % urlsCount) + 1; - Assert.AreEqual(expectedByte, bytes[i]); + // Stopping after 10% progress of downloading + cancelltionTokenSource.Cancel(); } - } + }; + // act + await downloader.DownloadFileTaskAsync(URL, cancelltionTokenSource.Token).ConfigureAwait(false); + + // assert + Assert.IsTrue(downloadCancelled); + Assert.IsTrue(downloader.IsCancelled); + Assert.IsTrue(downloader.Status == DownloadStatus.Stopped); + Assert.IsTrue(downloadProgress > 10); + } + + [TestMethod] + public async Task TestResumeDownloadWithAnotherUrl() + { + // arrange + var url1 = DummyFileHelper.GetFileWithNameUrl("file1.dat", DummyFileHelper.FileSize16Kb); + var url2 = DummyFileHelper.GetFileWithNameUrl("file2.dat", DummyFileHelper.FileSize16Kb); + var canStopDownload = true; + var totalDownloadSize = 0L; + var config = (DownloadConfiguration)Config.Clone(); + config.BufferBlockSize = 1024; + config.ChunkCount = 4; + var downloader = new DownloadService(config); + downloader.DownloadProgressChanged += (s, e) => { + totalDownloadSize = e.ReceivedBytesSize; + if (canStopDownload && totalDownloadSize > DummyFileHelper.FileSize16Kb / 2) + { + // Stopping after start of downloading + downloader.CancelAsync(); + canStopDownload = false; + } + }; + + // act + await downloader.DownloadFileTaskAsync(url1).ConfigureAwait(false); + await downloader.DownloadFileTaskAsync(downloader.Package, url2); // resume download with new url2. + + // assert + Assert.AreEqual(DummyFileHelper.FileSize16Kb, downloader.Package.TotalFileSize); + Assert.AreEqual(DummyFileHelper.FileSize16Kb, totalDownloadSize); + Assert.AreEqual(downloader.Package.Storage.Length, DummyFileHelper.FileSize16Kb); + Assert.AreEqual(100.0, downloader.Package.SaveProgress); + } + + [TestMethod] + public async Task DownloadAFileFrom8UrlsWith8ChunksTest() + { + await DownloadAFileFromMultipleUrlsWithMultipleChunksTest(8, 8); } + + [TestMethod] + public async Task DownloadAFileFrom2UrlsWith8ChunksTest() + { + await DownloadAFileFromMultipleUrlsWithMultipleChunksTest(2, 8); + } + + [TestMethod] + public async Task DownloadAFileFrom8UrlsWith2ChunksTest() + { + await DownloadAFileFromMultipleUrlsWithMultipleChunksTest(8, 2); + } + + public async Task DownloadAFileFromMultipleUrlsWithMultipleChunksTest(int urlsCount, int chunksCount) + { + // arrange + Config.ChunkCount = chunksCount; + Config.ParallelCount = chunksCount; + var totalSize = DummyFileHelper.FileSize16Kb; + var chunkSize = totalSize / Config.ChunkCount; + var downloader = new DownloadService(Config); + var urls = Enumerable.Range(1, urlsCount) + .Select(i => DummyFileHelper.GetFileWithNameUrl("testfile_" + i, totalSize, (byte)i)) + .ToArray(); + + // act + using var stream = await downloader.DownloadFileTaskAsync(urls).ConfigureAwait(false); + var bytes = ((MemoryStream)stream).ToArray(); + + // assert + Assert.IsNotNull(stream); + Assert.AreEqual(totalSize, stream.Length); + Assert.AreEqual(totalSize, downloader.Package.TotalFileSize); + Assert.AreEqual(100.0, downloader.Package.SaveProgress); + for (int i = 0; i < totalSize; i++) + { + var chunkIndex = (byte)(i / chunkSize); + var expectedByte = (chunkIndex % urlsCount) + 1; + Assert.AreEqual(expectedByte, bytes[i]); + } + } + } \ No newline at end of file diff --git a/src/Downloader.Test/IntegrationTests/DownloadServiceTest.cs b/src/Downloader.Test/IntegrationTests/DownloadServiceTest.cs index 05f59cfc..d2643d24 100644 --- a/src/Downloader.Test/IntegrationTests/DownloadServiceTest.cs +++ b/src/Downloader.Test/IntegrationTests/DownloadServiceTest.cs @@ -11,776 +11,775 @@ using System.Threading; using System.Threading.Tasks; -namespace Downloader.Test.IntegrationTests +namespace Downloader.Test.IntegrationTests; + +[TestClass] +public class DownloadServiceTest : DownloadService { - [TestClass] - public class DownloadServiceTest : DownloadService - { - private string Filename { get; set; } + private string Filename { get; set; } - [TestCleanup] - public void Cleanup() - { - Package?.Clear(); - Package?.Storage?.Dispose(); - if (!string.IsNullOrWhiteSpace(Filename)) - File.Delete(Filename); - } + [TestCleanup] + public void Cleanup() + { + Package?.Clear(); + Package?.Storage?.Dispose(); + if (!string.IsNullOrWhiteSpace(Filename)) + File.Delete(Filename); + } - private DownloadConfiguration GetDefaultConfig() - { - return new DownloadConfiguration { - BufferBlockSize = 1024, - ChunkCount = 8, - ParallelCount = 4, - ParallelDownload = true, - MaxTryAgainOnFailover = 5, - MinimumSizeOfChunking = 0, + private DownloadConfiguration GetDefaultConfig() + { + return new DownloadConfiguration { + BufferBlockSize = 1024, + ChunkCount = 8, + ParallelCount = 4, + ParallelDownload = true, + MaxTryAgainOnFailover = 5, + MinimumSizeOfChunking = 0, + Timeout = 3000, + RequestConfiguration = new RequestConfiguration { Timeout = 3000, - RequestConfiguration = new RequestConfiguration { - Timeout = 3000, - AllowAutoRedirect = true, - KeepAlive = false, - UserAgent = "test", - } - }; - } + AllowAutoRedirect = true, + KeepAlive = false, + UserAgent = "test", + } + }; + } - [TestMethod] - public async Task CancelAsyncTest() - { - // arrange - AsyncCompletedEventArgs eventArgs = null; - string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - DownloadStarted += (s, e) => CancelAsync(); - DownloadFileCompleted += (s, e) => eventArgs = e; - - // act - await DownloadFileTaskAsync(address).ConfigureAwait(false); - - // assert - Assert.IsTrue(IsCancelled); - Assert.IsNotNull(eventArgs); - Assert.IsTrue(eventArgs.Cancelled); - Assert.AreEqual(typeof(TaskCanceledException), eventArgs.Error.GetType()); - } + [TestMethod] + public async Task CancelAsyncTest() + { + // arrange + AsyncCompletedEventArgs eventArgs = null; + string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + DownloadStarted += (s, e) => CancelAsync(); + DownloadFileCompleted += (s, e) => eventArgs = e; + + // act + await DownloadFileTaskAsync(address); + + // assert + Assert.IsTrue(IsCancelled); + Assert.IsNotNull(eventArgs); + Assert.IsTrue(eventArgs.Cancelled); + Assert.AreEqual(typeof(TaskCanceledException), eventArgs.Error.GetType()); + } - [TestMethod] - public async Task CancelTaskAsyncTest() - { - // arrange - AsyncCompletedEventArgs eventArgs = null; - string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - DownloadStarted += async (s, e) => await CancelTaskAsync().ConfigureAwait(false); - DownloadFileCompleted += (s, e) => eventArgs = e; - - // act - await DownloadFileTaskAsync(address).ConfigureAwait(false); - - // assert - Assert.IsTrue(IsCancelled); - Assert.IsNotNull(eventArgs); - Assert.IsTrue(eventArgs.Cancelled); - Assert.AreEqual(typeof(TaskCanceledException), eventArgs.Error.GetType()); - } + [TestMethod] + public async Task CancelTaskAsyncTest() + { + // arrange + AsyncCompletedEventArgs eventArgs = null; + string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + DownloadStarted += async (s, e) => await CancelTaskAsync(); + DownloadFileCompleted += (s, e) => eventArgs = e; + + // act + await DownloadFileTaskAsync(address); + + // assert + Assert.IsTrue(IsCancelled); + Assert.IsNotNull(eventArgs); + Assert.IsTrue(eventArgs.Cancelled); + Assert.AreEqual(typeof(TaskCanceledException), eventArgs.Error.GetType()); + } - [TestMethod] - //[Timeout(10000)] - public async Task CompletesWithErrorWhenBadUrlTest() - { - // arrange - Exception onCompletionException = null; - string address = "https://nofile"; - Filename = Path.GetTempFileName(); - Options = GetDefaultConfig(); - Options.MaxTryAgainOnFailover = 0; - DownloadFileCompleted += (s, e) => { - onCompletionException = e.Error; - }; - - // act - await DownloadFileTaskAsync(address, Filename).ConfigureAwait(false); - - // assert - Assert.IsFalse(IsBusy); - Assert.IsNotNull(onCompletionException); - Assert.AreEqual(typeof(WebException), onCompletionException.GetType()); - } + [TestMethod] + //[Timeout(10000)] + public async Task CompletesWithErrorWhenBadUrlTest() + { + // arrange + Exception onCompletionException = null; + string address = "https://nofile"; + Filename = Path.GetTempFileName(); + Options = GetDefaultConfig(); + Options.MaxTryAgainOnFailover = 0; + DownloadFileCompleted += (s, e) => { + onCompletionException = e.Error; + }; + + // act + await DownloadFileTaskAsync(address, Filename); + + // assert + Assert.IsFalse(IsBusy); + Assert.IsNotNull(onCompletionException); + Assert.AreEqual(typeof(WebException), onCompletionException.GetType()); + } - [TestMethod] - public async Task ClearTest() - { - // arrange - await CancelTaskAsync().ConfigureAwait(false); + [TestMethod] + public async Task ClearTest() + { + // arrange + await CancelTaskAsync(); - // act - await Clear(); + // act + await Clear(); - // assert - Assert.IsFalse(IsCancelled); - } + // assert + Assert.IsFalse(IsCancelled); + } + + [TestMethod] + public async Task TestPackageSituationAfterDispose() + { + // arrange + var sampleDataLength = 1024; + var sampleData = DummyData.GenerateRandomBytes(sampleDataLength); + Package.TotalFileSize = sampleDataLength * 64; + Options.ChunkCount = 1; + new ChunkHub(Options).SetFileChunks(Package); + Package.BuildStorage(false, 1024 * 1024); + await Package.Storage.WriteAsync(0, sampleData, sampleDataLength); + Package.Storage.Flush(); + + // act + Dispose(); + + // assert + Assert.IsNotNull(Package.Chunks); + Assert.AreEqual(sampleDataLength, Package.Storage.Length); + Assert.AreEqual(sampleDataLength * 64, Package.TotalFileSize); + } - [TestMethod] - public async Task TestPackageSituationAfterDispose() + [TestMethod] + public async Task TestPackageChunksDataAfterDispose() + { + // arrange + var chunkSize = 1024; + var dummyData = DummyData.GenerateOrderedBytes(chunkSize); + Options.ChunkCount = 64; + Package.TotalFileSize = chunkSize * 64; + Package.BuildStorage(false, 1024 * 1024); + new ChunkHub(Options).SetFileChunks(Package); + for (int i = 0; i < Package.Chunks.Length; i++) { - // arrange - var sampleDataLength = 1024; - var sampleData = DummyData.GenerateRandomBytes(sampleDataLength); - Package.TotalFileSize = sampleDataLength * 64; - Options.ChunkCount = 1; - new ChunkHub(Options).SetFileChunks(Package); - Package.BuildStorage(false, 1024 * 1024); - await Package.Storage.WriteAsync(0, sampleData, sampleDataLength); - Package.Storage.Flush(); - - // act - Dispose(); - - // assert - Assert.IsNotNull(Package.Chunks); - Assert.AreEqual(sampleDataLength, Package.Storage.Length); - Assert.AreEqual(sampleDataLength * 64, Package.TotalFileSize); + var chunk = Package.Chunks[i]; + await Package.Storage.WriteAsync(chunk.Start, dummyData, chunkSize); } - [TestMethod] - public async Task TestPackageChunksDataAfterDispose() + // act + Dispose(); + var stream = Package.Storage.OpenRead(); + + // assert + Assert.IsNotNull(Package.Chunks); + for (int i = 0; i < Package.Chunks.Length; i++) { - // arrange - var chunkSize = 1024; - var dummyData = DummyData.GenerateOrderedBytes(chunkSize); - Options.ChunkCount = 64; - Package.TotalFileSize = chunkSize * 64; - Package.BuildStorage(false, 1024 * 1024); - new ChunkHub(Options).SetFileChunks(Package); - for (int i = 0; i < Package.Chunks.Length; i++) + var buffer = new byte[chunkSize]; + await stream.ReadAsync(buffer, 0, chunkSize); + Assert.IsTrue(dummyData.SequenceEqual(buffer)); + } + } + + [TestMethod] + public async Task CancelPerformanceTest() + { + // arrange + AsyncCompletedEventArgs eventArgs = null; + var watch = new Stopwatch(); + string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + DownloadProgressChanged += async (s, e) => { + watch.Start(); + await CancelTaskAsync(); + }; + DownloadFileCompleted += (s, e) => eventArgs = e; + + // act + await DownloadFileTaskAsync(address); + watch.Stop(); + + // assert + Assert.IsTrue(eventArgs?.Cancelled); + Assert.IsTrue(watch.ElapsedMilliseconds < 1000); + Assert.AreEqual(4, Options.ParallelCount); + Assert.AreEqual(8, Options.ChunkCount); + } + + [TestMethod] + public async Task ResumePerformanceTest() + { + // arrange + AsyncCompletedEventArgs eventArgs = null; + var watch = new Stopwatch(); + var isCancelled = false; + string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + DownloadFileCompleted += (s, e) => eventArgs = e; + DownloadProgressChanged += async (s, e) => { + if (isCancelled == false) + { + await CancelTaskAsync(); + isCancelled = true; + } + else { - var chunk = Package.Chunks[i]; - await Package.Storage.WriteAsync(chunk.Start, dummyData, chunkSize); + watch.Stop(); } + }; + + // act + await DownloadFileTaskAsync(address); + watch.Start(); + await DownloadFileTaskAsync(Package); + + // assert + Assert.IsFalse(eventArgs?.Cancelled); + Assert.IsTrue(watch.ElapsedMilliseconds < 1000); + Assert.AreEqual(4, Options.ParallelCount); + Assert.AreEqual(8, Options.ChunkCount); + } - // act - Dispose(); - var stream = Package.Storage.OpenRead(); + [TestMethod] + public async Task PauseResumeTest() + { + // arrange + AsyncCompletedEventArgs eventArgs = null; + var paused = false; + var cancelled = false; + string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + DownloadFileCompleted += (s, e) => eventArgs = e; + + // act + DownloadProgressChanged += (s, e) => { + Pause(); + cancelled = IsCancelled; + paused = IsPaused; + Resume(); + }; + await DownloadFileTaskAsync(address); + + // assert + Assert.IsTrue(paused); + Assert.IsFalse(cancelled); + Assert.AreEqual(4, Options.ParallelCount); + Assert.AreEqual(8, Options.ChunkCount); + } - // assert - Assert.IsNotNull(Package.Chunks); - for (int i = 0; i < Package.Chunks.Length; i++) + [TestMethod] + public async Task CancelAfterPauseTest() + { + // arrange + AsyncCompletedEventArgs eventArgs = null; + var pauseStateBeforeCancel = false; + var cancelStateBeforeCancel = false; + var pauseStateAfterCancel = false; + var cancelStateAfterCancel = false; + string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + DownloadFileCompleted += (s, e) => eventArgs = e; + + // act + DownloadProgressChanged += async (s, e) => { + Pause(); + cancelStateBeforeCancel = IsCancelled; + pauseStateBeforeCancel = IsPaused; + await CancelTaskAsync(); + pauseStateAfterCancel = IsPaused; + cancelStateAfterCancel = IsCancelled; + }; + await DownloadFileTaskAsync(address); + + // assert + Assert.IsTrue(pauseStateBeforeCancel); + Assert.IsFalse(cancelStateBeforeCancel); + Assert.IsFalse(pauseStateAfterCancel); + Assert.IsTrue(cancelStateAfterCancel); + Assert.AreEqual(4, Options.ParallelCount); + Assert.AreEqual(8, Options.ChunkCount); + Assert.AreEqual(8, Options.ChunkCount); + Assert.IsFalse(Package.IsSaveComplete); + Assert.IsTrue(eventArgs.Cancelled); + } + + [TestMethod] + public async Task DownloadParallelNotSupportedUrlTest() + { + // arrange + var actualChunksCount = 0; + AsyncCompletedEventArgs eventArgs = null; + string address = DummyFileHelper.GetFileWithNoAcceptRangeUrl("test.dat", DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + DownloadFileCompleted += (s, e) => eventArgs = e; + DownloadStarted += (s, e) => { + actualChunksCount = Package.Chunks.Length; + }; + + // act + await DownloadFileTaskAsync(address); + + // assert + Assert.IsFalse(Package.IsSupportDownloadInRange); + Assert.AreEqual(1, Options.ParallelCount); + Assert.AreEqual(1, Options.ChunkCount); + Assert.IsFalse(eventArgs?.Cancelled); + Assert.IsTrue(Package.IsSaveComplete); + Assert.IsNull(eventArgs?.Error); + Assert.AreEqual(1, actualChunksCount); + } + + [TestMethod] + public async Task ResumeNotSupportedUrlTest() + { + // arrange + AsyncCompletedEventArgs eventArgs = null; + var isCancelled = false; + var actualChunksCount = 0; + var progressCount = 0; + var cancelOnProgressNo = 6; + var maxProgressPercentage = 0d; + var address = DummyFileHelper.GetFileWithNoAcceptRangeUrl("test.dat", DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + DownloadFileCompleted += (s, e) => eventArgs = e; + DownloadProgressChanged += async (s, e) => { + if (cancelOnProgressNo == progressCount++) { - var buffer = new byte[chunkSize]; - await stream.ReadAsync(buffer, 0, chunkSize); - Assert.IsTrue(dummyData.SequenceEqual(buffer)); + await CancelTaskAsync(); + isCancelled = true; } - } + else if (isCancelled) + { + actualChunksCount = Package.Chunks.Length; + } + maxProgressPercentage = Math.Max(e.ProgressPercentage, maxProgressPercentage); + }; + + // act + await DownloadFileTaskAsync(address); // start the download + await DownloadFileTaskAsync(Package); // resume the downlaod after canceling + + // assert + Assert.IsTrue(isCancelled); + Assert.IsFalse(Package.IsSupportDownloadInRange); + Assert.AreEqual(1, Options.ParallelCount); + Assert.AreEqual(1, Options.ChunkCount); + Assert.IsFalse(eventArgs?.Cancelled); + Assert.IsTrue(Package.IsSaveComplete); + Assert.IsNull(eventArgs?.Error); + Assert.AreEqual(1, actualChunksCount); + Assert.AreEqual(100, maxProgressPercentage); + } - [TestMethod] - public async Task CancelPerformanceTest() - { - // arrange - AsyncCompletedEventArgs eventArgs = null; - var watch = new Stopwatch(); - string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - DownloadProgressChanged += async (s, e) => { - watch.Start(); - await CancelTaskAsync(); - }; - DownloadFileCompleted += (s, e) => eventArgs = e; - - // act - await DownloadFileTaskAsync(address).ConfigureAwait(false); - watch.Stop(); - - // assert - Assert.IsTrue(eventArgs?.Cancelled); - Assert.IsTrue(watch.ElapsedMilliseconds < 1000); - Assert.AreEqual(4, Options.ParallelCount); - Assert.AreEqual(8, Options.ChunkCount); - } + [TestMethod] + public async Task ActiveChunksTest() + { + // arrange + var allActiveChunksCount = new List(20); + string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + + // act + DownloadProgressChanged += (s, e) => { + allActiveChunksCount.Add(e.ActiveChunks); + }; + await DownloadFileTaskAsync(address); + + // assert + Assert.AreEqual(4, Options.ParallelCount); + Assert.AreEqual(8, Options.ChunkCount); + Assert.IsTrue(Package.IsSupportDownloadInRange); + Assert.IsTrue(Package.IsSaveComplete); + foreach (var activeChunks in allActiveChunksCount) + Assert.IsTrue(activeChunks >= 1 && activeChunks <= 4); + } - [TestMethod] - public async Task ResumePerformanceTest() - { - // arrange - AsyncCompletedEventArgs eventArgs = null; - var watch = new Stopwatch(); - var isCancelled = false; - string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - DownloadFileCompleted += (s, e) => eventArgs = e; - DownloadProgressChanged += async (s, e) => { - if (isCancelled == false) - { - await CancelTaskAsync().ConfigureAwait(false); - isCancelled = true; - } - else - { - watch.Stop(); - } - }; + [TestMethod] + public async Task ActiveChunksWithRangeNotSupportedUrlTest() + { + // arrange + var allActiveChunksCount = new List(20); + string address = DummyFileHelper.GetFileWithNoAcceptRangeUrl("test.dat", DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + + // act + DownloadProgressChanged += (s, e) => { + allActiveChunksCount.Add(e.ActiveChunks); + }; + await DownloadFileTaskAsync(address); + + // assert + Assert.AreEqual(1, Options.ParallelCount); + Assert.AreEqual(1, Options.ChunkCount); + Assert.IsFalse(Package.IsSupportDownloadInRange); + Assert.IsTrue(Package.IsSaveComplete); + foreach (var activeChunks in allActiveChunksCount) + Assert.IsTrue(activeChunks >= 1 && activeChunks <= 4); + } - // act - await DownloadFileTaskAsync(address).ConfigureAwait(false); - watch.Start(); - await DownloadFileTaskAsync(Package).ConfigureAwait(false); + [TestMethod] + public async Task ActiveChunksAfterCancelResumeWithNotSupportedUrlTest() + { + // arrange + var allActiveChunksCount = new List(20); + var isCancelled = false; + var actualChunksCount = 0; + var progressCount = 0; + var cancelOnProgressNo = 6; + var address = DummyFileHelper.GetFileWithNoAcceptRangeUrl("test.dat", DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + DownloadProgressChanged += async (s, e) => { + allActiveChunksCount.Add(e.ActiveChunks); + if (cancelOnProgressNo == progressCount++) + { + await CancelTaskAsync(); + isCancelled = true; + } + else if (isCancelled) + { + actualChunksCount = Package.Chunks.Length; + } + }; + + // act + await DownloadFileTaskAsync(address); // start the download + await DownloadFileTaskAsync(Package); // resume the downlaod after canceling + + // assert + Assert.IsTrue(isCancelled); + Assert.IsFalse(Package.IsSupportDownloadInRange); + Assert.IsTrue(Package.IsSaveComplete); + Assert.AreEqual(1, actualChunksCount); + Assert.AreEqual(1, Options.ParallelCount); + Assert.AreEqual(1, Options.ChunkCount); + foreach (var activeChunks in allActiveChunksCount) + Assert.IsTrue(activeChunks >= 1 && activeChunks <= 4); + } - // assert - Assert.IsFalse(eventArgs?.Cancelled); - Assert.IsTrue(watch.ElapsedMilliseconds < 1000); - Assert.AreEqual(4, Options.ParallelCount); - Assert.AreEqual(8, Options.ChunkCount); - } + [TestMethod] + public async Task TestPackageDataAfterCompletionWithSuccess() + { + // arrange + Options.ClearPackageOnCompletionWithFailure = false; + var states = new DownloadServiceEventsState(this); + var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); + + // act + await DownloadFileTaskAsync(url); + + // assert + Assert.AreEqual(url, Package.Urls.First()); + Assert.IsTrue(states.DownloadSuccessfullCompleted); + Assert.IsTrue(states.DownloadProgressIsCorrect); + Assert.IsNull(states.DownloadError); + Assert.IsTrue(Package.IsSaveComplete); + Assert.IsFalse(Package.IsSaving); + Assert.IsNull(Package.Chunks); + } - [TestMethod] - public async Task PauseResumeTest() - { - // arrange - AsyncCompletedEventArgs eventArgs = null; - var paused = false; - var cancelled = false; - string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - DownloadFileCompleted += (s, e) => eventArgs = e; - - // act - DownloadProgressChanged += (s, e) => { + [TestMethod] + public async Task TestPackageStatusAfterCompletionWithSuccess() + { + // arrange + var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); + var noneStatus = Package.Status; + var createdStatus = DownloadStatus.None; + var runningStatus = DownloadStatus.None; + var pausedStatus = DownloadStatus.None; + var resumeStatus = DownloadStatus.None; + var completedStatus = DownloadStatus.None; + + DownloadStarted += (s, e) => createdStatus = Package.Status; + DownloadProgressChanged += (s, e) => { + runningStatus = Package.Status; + if (e.ProgressPercentage > 50 && e.ProgressPercentage < 70) + { Pause(); - cancelled = IsCancelled; - paused = IsPaused; + pausedStatus = Package.Status; Resume(); - }; - await DownloadFileTaskAsync(address).ConfigureAwait(false); - - // assert - Assert.IsTrue(paused); - Assert.IsFalse(cancelled); - Assert.AreEqual(4, Options.ParallelCount); - Assert.AreEqual(8, Options.ChunkCount); - } - - [TestMethod] - public async Task CancelAfterPauseTest() - { - // arrange - AsyncCompletedEventArgs eventArgs = null; - var pauseStateBeforeCancel = false; - var cancelStateBeforeCancel = false; - var pauseStateAfterCancel = false; - var cancelStateAfterCancel = false; - string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - DownloadFileCompleted += (s, e) => eventArgs = e; - - // act - DownloadProgressChanged += async (s, e) => { - Pause(); - cancelStateBeforeCancel = IsCancelled; - pauseStateBeforeCancel = IsPaused; - await CancelTaskAsync().ConfigureAwait(false); - pauseStateAfterCancel = IsPaused; - cancelStateAfterCancel = IsCancelled; - }; - await DownloadFileTaskAsync(address).ConfigureAwait(false); - - // assert - Assert.IsTrue(pauseStateBeforeCancel); - Assert.IsFalse(cancelStateBeforeCancel); - Assert.IsFalse(pauseStateAfterCancel); - Assert.IsTrue(cancelStateAfterCancel); - Assert.AreEqual(4, Options.ParallelCount); - Assert.AreEqual(8, Options.ChunkCount); - Assert.AreEqual(8, Options.ChunkCount); - Assert.IsFalse(Package.IsSaveComplete); - Assert.IsTrue(eventArgs.Cancelled); - } - - [TestMethod] - public async Task DownloadParallelNotSupportedUrlTest() - { - // arrange - var actualChunksCount = 0; - AsyncCompletedEventArgs eventArgs = null; - string address = DummyFileHelper.GetFileWithNoAcceptRangeUrl("test.dat", DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - DownloadFileCompleted += (s, e) => eventArgs = e; - DownloadStarted += (s, e) => { - actualChunksCount = Package.Chunks.Length; - }; - - // act - await DownloadFileTaskAsync(address).ConfigureAwait(false); - - // assert - Assert.IsFalse(Package.IsSupportDownloadInRange); - Assert.AreEqual(1, Options.ParallelCount); - Assert.AreEqual(1, Options.ChunkCount); - Assert.IsFalse(eventArgs?.Cancelled); - Assert.IsTrue(Package.IsSaveComplete); - Assert.IsNull(eventArgs?.Error); - Assert.AreEqual(1, actualChunksCount); - } + resumeStatus = Package.Status; + } + }; + DownloadFileCompleted += (s, e) => completedStatus = Package.Status; + + // act + await DownloadFileTaskAsync(url); + + // assert + Assert.IsTrue(Package.IsSaveComplete); + Assert.IsFalse(Package.IsSaving); + Assert.AreEqual(DownloadStatus.Completed, Package.Status); + Assert.AreEqual(DownloadStatus.Running, createdStatus); + Assert.AreEqual(DownloadStatus.Running, runningStatus); + Assert.AreEqual(DownloadStatus.Paused, pausedStatus); + Assert.AreEqual(DownloadStatus.Running, resumeStatus); + Assert.AreEqual(DownloadStatus.Completed, completedStatus); + } - [TestMethod] - public async Task ResumeNotSupportedUrlTest() - { - // arrange - AsyncCompletedEventArgs eventArgs = null; - var isCancelled = false; - var actualChunksCount = 0; - var progressCount = 0; - var cancelOnProgressNo = 6; - var maxProgressPercentage = 0d; - var address = DummyFileHelper.GetFileWithNoAcceptRangeUrl("test.dat", DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - DownloadFileCompleted += (s, e) => eventArgs = e; - DownloadProgressChanged += async (s, e) => { - if (cancelOnProgressNo == progressCount++) - { - await CancelTaskAsync(); - isCancelled = true; - } - else if (isCancelled) - { - actualChunksCount = Package.Chunks.Length; - } - maxProgressPercentage = Math.Max(e.ProgressPercentage, maxProgressPercentage); - }; - - // act - await DownloadFileTaskAsync(address).ConfigureAwait(false); // start the download - await DownloadFileTaskAsync(Package).ConfigureAwait(false); // resume the downlaod after canceling - - // assert - Assert.IsTrue(isCancelled); - Assert.IsFalse(Package.IsSupportDownloadInRange); - Assert.AreEqual(1, Options.ParallelCount); - Assert.AreEqual(1, Options.ChunkCount); - Assert.IsFalse(eventArgs?.Cancelled); - Assert.IsTrue(Package.IsSaveComplete); - Assert.IsNull(eventArgs?.Error); - Assert.AreEqual(1, actualChunksCount); - Assert.AreEqual(100, maxProgressPercentage); - } + [TestMethod] + public async Task TestSerializePackageAfterCancelOnMemory() + { + await TestSerializePackageAfterCancel(true); + } - [TestMethod] - public async Task ActiveChunksTest() - { - // arrange - var allActiveChunksCount = new List(20); - string address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - - // act - DownloadProgressChanged += (s, e) => { - allActiveChunksCount.Add(e.ActiveChunks); - }; - await DownloadFileTaskAsync(address).ConfigureAwait(false); - - // assert - Assert.AreEqual(4, Options.ParallelCount); - Assert.AreEqual(8, Options.ChunkCount); - Assert.IsTrue(Package.IsSupportDownloadInRange); - Assert.IsTrue(Package.IsSaveComplete); - foreach (var activeChunks in allActiveChunksCount) - Assert.IsTrue(activeChunks >= 1 && activeChunks <= 4); - } + [TestMethod] + public async Task TestSerializePackageAfterCancelOnFile() + { + await TestSerializePackageAfterCancel(false); + } - [TestMethod] - public async Task ActiveChunksWithRangeNotSupportedUrlTest() + public async Task TestSerializePackageAfterCancel(bool onMemory) + { + // arrange + var path = Path.GetTempFileName(); + DownloadPackage package = null; + var packageText = string.Empty; + var url = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + ChunkDownloadProgressChanged += (s, e) => CancelAsync(); + DownloadFileCompleted += (s, e) => { + package = e.UserState as DownloadPackage; + if (package!.Status != DownloadStatus.Completed) + packageText = System.Text.Json.JsonSerializer.Serialize(package!); + }; + + // act + if (onMemory) { - // arrange - var allActiveChunksCount = new List(20); - string address = DummyFileHelper.GetFileWithNoAcceptRangeUrl("test.dat", DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - - // act - DownloadProgressChanged += (s, e) => { - allActiveChunksCount.Add(e.ActiveChunks); - }; - await DownloadFileTaskAsync(address).ConfigureAwait(false); - - // assert - Assert.AreEqual(1, Options.ParallelCount); - Assert.AreEqual(1, Options.ChunkCount); - Assert.IsFalse(Package.IsSupportDownloadInRange); - Assert.IsTrue(Package.IsSaveComplete); - foreach (var activeChunks in allActiveChunksCount) - Assert.IsTrue(activeChunks >= 1 && activeChunks <= 4); + await DownloadFileTaskAsync(url); } - - [TestMethod] - public async Task ActiveChunksAfterCancelResumeWithNotSupportedUrlTest() + else { - // arrange - var allActiveChunksCount = new List(20); - var isCancelled = false; - var actualChunksCount = 0; - var progressCount = 0; - var cancelOnProgressNo = 6; - var address = DummyFileHelper.GetFileWithNoAcceptRangeUrl("test.dat", DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - DownloadProgressChanged += async (s, e) => { - allActiveChunksCount.Add(e.ActiveChunks); - if (cancelOnProgressNo == progressCount++) - { - await CancelTaskAsync().ConfigureAwait(false); - isCancelled = true; - } - else if (isCancelled) - { - actualChunksCount = Package.Chunks.Length; - } - }; - - // act - await DownloadFileTaskAsync(address).ConfigureAwait(false); // start the download - await DownloadFileTaskAsync(Package).ConfigureAwait(false); // resume the downlaod after canceling - - // assert - Assert.IsTrue(isCancelled); - Assert.IsFalse(Package.IsSupportDownloadInRange); - Assert.IsTrue(Package.IsSaveComplete); - Assert.AreEqual(1, actualChunksCount); - Assert.AreEqual(1, Options.ParallelCount); - Assert.AreEqual(1, Options.ChunkCount); - foreach (var activeChunks in allActiveChunksCount) - Assert.IsTrue(activeChunks >= 1 && activeChunks <= 4); + await DownloadFileTaskAsync(url, path); } - [TestMethod] - public async Task TestPackageDataAfterCompletionWithSuccess() - { - // arrange - Options.ClearPackageOnCompletionWithFailure = false; - var states = new DownloadServiceEventsState(this); - var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); - - // act - await DownloadFileTaskAsync(url).ConfigureAwait(false); - - // assert - Assert.AreEqual(url, Package.Urls.First()); - Assert.IsTrue(states.DownloadSuccessfullCompleted); - Assert.IsTrue(states.DownloadProgressIsCorrect); - Assert.IsNull(states.DownloadError); - Assert.IsTrue(Package.IsSaveComplete); - Assert.IsFalse(Package.IsSaving); - Assert.IsNull(Package.Chunks); - } + // assert + Assert.IsTrue(IsCancelled); + Assert.IsNotNull(package); + Assert.IsFalse(string.IsNullOrWhiteSpace(packageText)); - [TestMethod] - public async Task TestPackageStatusAfterCompletionWithSuccess() - { - // arrange - var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); - var noneStatus = Package.Status; - var createdStatus = DownloadStatus.None; - var runningStatus = DownloadStatus.None; - var pausedStatus = DownloadStatus.None; - var resumeStatus = DownloadStatus.None; - var completedStatus = DownloadStatus.None; - - DownloadStarted += (s, e) => createdStatus = Package.Status; - DownloadProgressChanged += (s, e) => { - runningStatus = Package.Status; - if (e.ProgressPercentage > 50 && e.ProgressPercentage < 70) - { - Pause(); - pausedStatus = Package.Status; - Resume(); - resumeStatus = Package.Status; - } - }; - DownloadFileCompleted += (s, e) => completedStatus = Package.Status; - - // act - await DownloadFileTaskAsync(url).ConfigureAwait(false); - - // assert - Assert.IsTrue(Package.IsSaveComplete); - Assert.IsFalse(Package.IsSaving); - Assert.AreEqual(DownloadStatus.Completed, Package.Status); - Assert.AreEqual(DownloadStatus.Running, createdStatus); - Assert.AreEqual(DownloadStatus.Running, runningStatus); - Assert.AreEqual(DownloadStatus.Paused, pausedStatus); - Assert.AreEqual(DownloadStatus.Running, resumeStatus); - Assert.AreEqual(DownloadStatus.Completed, completedStatus); - } + Cleanup(); + } - [TestMethod] - public async Task TestSerializePackageAfterCancelOnMemory() - { - await TestSerializePackageAfterCancel(true); - } + [TestMethod] + public async Task TestResumeFromSerializedPackageOnMemory() + { + await TestResumeFromSerializedPackage(true); + } - [TestMethod] - public async Task TestSerializePackageAfterCancelOnFile() - { - await TestSerializePackageAfterCancel(false); - } + [TestMethod] + public async Task TestResumeFromSerializedPackageOnFile() + { + await TestResumeFromSerializedPackage(false); + } - public async Task TestSerializePackageAfterCancel(bool onMemory) - { - // arrange - var path = Path.GetTempFileName(); - DownloadPackage package = null; - var packageText = string.Empty; - var url = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - ChunkDownloadProgressChanged += (s, e) => CancelAsync(); - DownloadFileCompleted += (s, e) => { - package = e.UserState as DownloadPackage; - if (package!.Status != DownloadStatus.Completed) - packageText = System.Text.Json.JsonSerializer.Serialize(package!); - }; - - // act - if (onMemory) - { - await DownloadFileTaskAsync(url).ConfigureAwait(false); - } - else + public async Task TestResumeFromSerializedPackage(bool onMemory) + { + // arrange + var isCancelOccurred = false; + var path = Path.GetTempFileName(); + DownloadPackage package = null; + var packageText = string.Empty; + var url = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); + Options = GetDefaultConfig(); + ChunkDownloadProgressChanged += async (s, e) => { + if (isCancelOccurred == false) { - await DownloadFileTaskAsync(url, path).ConfigureAwait(false); + isCancelOccurred = true; + await CancelTaskAsync(); } - - // assert - Assert.IsTrue(IsCancelled); - Assert.IsNotNull(package); - Assert.IsFalse(string.IsNullOrWhiteSpace(packageText)); - - Cleanup(); - } - - [TestMethod] - public async Task TestResumeFromSerializedPackageOnMemory() + }; + DownloadFileCompleted += (s, e) => { + package = e.UserState as DownloadPackage; + if (package!.Status != DownloadStatus.Completed) + packageText = System.Text.Json.JsonSerializer.Serialize(package!); + }; + + // act + if (onMemory) { - await TestResumeFromSerializedPackage(true); + await DownloadFileTaskAsync(url); } - - [TestMethod] - public async Task TestResumeFromSerializedPackageOnFile() + else { - await TestResumeFromSerializedPackage(false); + await DownloadFileTaskAsync(url, path); } - public async Task TestResumeFromSerializedPackage(bool onMemory) - { - // arrange - var isCancelOccurred = false; - var path = Path.GetTempFileName(); - DownloadPackage package = null; - var packageText = string.Empty; - var url = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb); - Options = GetDefaultConfig(); - ChunkDownloadProgressChanged += async (s, e) => { - if (isCancelOccurred == false) - { - isCancelOccurred = true; - await CancelTaskAsync(); - } - }; - DownloadFileCompleted += (s, e) => { - package = e.UserState as DownloadPackage; - if (package!.Status != DownloadStatus.Completed) - packageText = System.Text.Json.JsonSerializer.Serialize(package!); - }; - - // act - if (onMemory) - { - await DownloadFileTaskAsync(url).ConfigureAwait(false); - } - else - { - await DownloadFileTaskAsync(url, path).ConfigureAwait(false); - } + // resume act + var reversedPackage = System.Text.Json.JsonSerializer.Deserialize(packageText); + await DownloadFileTaskAsync(reversedPackage); - // resume act - var reversedPackage = System.Text.Json.JsonSerializer.Deserialize(packageText); - await DownloadFileTaskAsync(reversedPackage).ConfigureAwait(false); + // assert + Assert.IsFalse(IsCancelled); + Assert.IsNotNull(package); + Assert.IsNotNull(reversedPackage); + Assert.IsTrue(reversedPackage.IsSaveComplete); + Assert.IsFalse(string.IsNullOrWhiteSpace(packageText)); - // assert - Assert.IsFalse(IsCancelled); - Assert.IsNotNull(package); - Assert.IsNotNull(reversedPackage); - Assert.IsTrue(reversedPackage.IsSaveComplete); - Assert.IsFalse(string.IsNullOrWhiteSpace(packageText)); + Cleanup(); + } - Cleanup(); - } + [TestMethod] + public async Task TestPackageStatusAfterCancellation() + { + // arrange + var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); + var noneStatus = Package.Status; + var createdStatus = DownloadStatus.None; + var runningStatus = DownloadStatus.None; + var cancelledStatus = DownloadStatus.None; + var completedStatus = DownloadStatus.None; + + DownloadStarted += (s, e) => createdStatus = Package.Status; + DownloadProgressChanged += async (s, e) => { + runningStatus = Package.Status; + if (e.ProgressPercentage > 50 && e.ProgressPercentage < 70) + { + await CancelTaskAsync(); + cancelledStatus = Package.Status; + } + }; + DownloadFileCompleted += (s, e) => completedStatus = Package.Status; + + // act + await DownloadFileTaskAsync(url); + + // assert + Assert.IsFalse(Package.IsSaveComplete); + Assert.IsFalse(Package.IsSaving); + Assert.AreEqual(DownloadStatus.Stopped, Package.Status); + Assert.AreEqual(DownloadStatus.Running, createdStatus); + Assert.AreEqual(DownloadStatus.Running, runningStatus); + Assert.AreEqual(DownloadStatus.Stopped, cancelledStatus); + Assert.AreEqual(DownloadStatus.Stopped, completedStatus); + } - [TestMethod] - public async Task TestPackageStatusAfterCancellation() - { - // arrange - var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); - var noneStatus = Package.Status; - var createdStatus = DownloadStatus.None; - var runningStatus = DownloadStatus.None; - var cancelledStatus = DownloadStatus.None; - var completedStatus = DownloadStatus.None; - - DownloadStarted += (s, e) => createdStatus = Package.Status; - DownloadProgressChanged += async (s, e) => { - runningStatus = Package.Status; - if (e.ProgressPercentage > 50 && e.ProgressPercentage < 70) + [TestMethod] + [Timeout(5000)] + public async Task TestResumeDownloadImmedietalyAfterCancellationAsync() + { + // arrange + var completedState = DownloadStatus.None; + var checkProgress = false; + var secondStartProgressPercent = -1d; + var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); + var tcs = new TaskCompletionSource(); + DownloadFileCompleted += (s, e) => completedState = Package.Status; + + // act + DownloadProgressChanged += async (s, e) => { + if (secondStartProgressPercent < 0) + { + if (checkProgress) { - await CancelTaskAsync().ConfigureAwait(false); - cancelledStatus = Package.Status; + checkProgress = false; + secondStartProgressPercent = e.ProgressPercentage; } - }; - DownloadFileCompleted += (s, e) => completedStatus = Package.Status; - - // act - await DownloadFileTaskAsync(url).ConfigureAwait(false); - - // assert - Assert.IsFalse(Package.IsSaveComplete); - Assert.IsFalse(Package.IsSaving); - Assert.AreEqual(DownloadStatus.Stopped, Package.Status); - Assert.AreEqual(DownloadStatus.Running, createdStatus); - Assert.AreEqual(DownloadStatus.Running, runningStatus); - Assert.AreEqual(DownloadStatus.Stopped, cancelledStatus); - Assert.AreEqual(DownloadStatus.Stopped, completedStatus); - } - - [TestMethod] - [Timeout(5000)] - public async Task TestResumeDownloadImmedietalyAfterCancellationAsync() - { - // arrange - var completedState = DownloadStatus.None; - var checkProgress = false; - var secondStartProgressPercent = -1d; - var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); - var tcs = new TaskCompletionSource(); - DownloadFileCompleted += (s, e) => completedState = Package.Status; - - // act - DownloadProgressChanged += async (s, e) => { - if (secondStartProgressPercent < 0) + else if (e.ProgressPercentage > 50 && e.ProgressPercentage < 60) { - if (checkProgress) - { - checkProgress = false; - secondStartProgressPercent = e.ProgressPercentage; - } - else if (e.ProgressPercentage > 50 && e.ProgressPercentage < 60) - { - await CancelTaskAsync().ConfigureAwait(false); - checkProgress = true; - await DownloadFileTaskAsync(Package).ConfigureAwait(false); - tcs.SetResult(true); - } + await CancelTaskAsync(); + checkProgress = true; + await DownloadFileTaskAsync(Package); + tcs.SetResult(true); } - }; - await DownloadFileTaskAsync(url).ConfigureAwait(false); - tcs.Task.Wait(); - - // assert - Assert.IsTrue(Package.IsSaveComplete); - Assert.IsFalse(Package.IsSaving); - Assert.AreEqual(DownloadStatus.Completed, Package.Status); - Assert.IsTrue(secondStartProgressPercent > 50, $"progress percent is {secondStartProgressPercent}"); - } + } + }; + await DownloadFileTaskAsync(url); + await tcs.Task; + + // assert + Assert.IsTrue(Package.IsSaveComplete); + Assert.IsFalse(Package.IsSaving); + Assert.AreEqual(DownloadStatus.Completed, Package.Status); + Assert.IsTrue(secondStartProgressPercent > 50, $"progress percent is {secondStartProgressPercent}"); + } - [TestMethod] - [Timeout(5000)] - public async Task TestStopDownloadOnClearWhenRunning() - { - // arrange - var completedState = DownloadStatus.None; - var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); - DownloadFileCompleted += (s, e) => completedState = Package.Status; - - // act - DownloadProgressChanged += async (s, e) => { - if (e.ProgressPercentage > 50 && e.ProgressPercentage < 60) - await Clear(); - }; - await DownloadFileTaskAsync(url).ConfigureAwait(false); - - // assert - Assert.IsFalse(Package.IsSaveComplete); - Assert.IsFalse(Package.IsSaving); - Assert.AreEqual(DownloadStatus.Stopped, completedState); - Assert.AreEqual(DownloadStatus.Stopped, Package.Status); - } + [TestMethod] + [Timeout(5000)] + public async Task TestStopDownloadOnClearWhenRunning() + { + // arrange + var completedState = DownloadStatus.None; + var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); + DownloadFileCompleted += (s, e) => completedState = Package.Status; + + // act + DownloadProgressChanged += async (s, e) => { + if (e.ProgressPercentage > 50 && e.ProgressPercentage < 60) + await Clear(); + }; + await DownloadFileTaskAsync(url); + + // assert + Assert.IsFalse(Package.IsSaveComplete); + Assert.IsFalse(Package.IsSaving); + Assert.AreEqual(DownloadStatus.Stopped, completedState); + Assert.AreEqual(DownloadStatus.Stopped, Package.Status); + } - [TestMethod] - [Timeout(5000)] - public async Task TestStopDownloadOnClearWhenPaused() - { - // arrange - var completedState = DownloadStatus.None; - var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); - DownloadFileCompleted += (s, e) => completedState = Package.Status; - - // act - DownloadProgressChanged += async (s, e) => { - if (e.ProgressPercentage > 50 && e.ProgressPercentage < 60) - { - Pause(); - await Clear(); - } - }; - await DownloadFileTaskAsync(url).ConfigureAwait(false); - - // assert - Assert.IsFalse(Package.IsSaveComplete); - Assert.IsFalse(Package.IsSaving); - Assert.AreEqual(DownloadStatus.Stopped, completedState); - Assert.AreEqual(DownloadStatus.Stopped, Package.Status); - } + [TestMethod] + [Timeout(5000)] + public async Task TestStopDownloadOnClearWhenPaused() + { + // arrange + var completedState = DownloadStatus.None; + var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); + DownloadFileCompleted += (s, e) => completedState = Package.Status; + + // act + DownloadProgressChanged += async (s, e) => { + if (e.ProgressPercentage > 50 && e.ProgressPercentage < 60) + { + Pause(); + await Clear(); + } + }; + await DownloadFileTaskAsync(url); + + // assert + Assert.IsFalse(Package.IsSaveComplete); + Assert.IsFalse(Package.IsSaving); + Assert.AreEqual(DownloadStatus.Stopped, completedState); + Assert.AreEqual(DownloadStatus.Stopped, Package.Status); + } - [TestMethod] - public async Task TestMinimumSizeOfChunking() - { - // arrange - Options = GetDefaultConfig(); - Options.MinimumSizeOfChunking = DummyFileHelper.FileSize16Kb; - var states = new DownloadServiceEventsState(this); - var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); - var activeChunks = 0; - int? chunkCounts = null; - var progressIds = new Dictionary(); - ChunkDownloadProgressChanged += (s, e) => { - activeChunks = Math.Max(activeChunks, e.ActiveChunks); - progressIds[e.ProgressId] = true; - chunkCounts ??= Package.Chunks.Length; - }; - - // act - await DownloadFileTaskAsync(url).ConfigureAwait(false); - - // assert - Assert.IsTrue(Package.IsSaveComplete); - Assert.AreEqual(1, activeChunks); - Assert.AreEqual(1, progressIds.Count); - Assert.AreEqual(1, chunkCounts); - } + [TestMethod] + public async Task TestMinimumSizeOfChunking() + { + // arrange + Options = GetDefaultConfig(); + Options.MinimumSizeOfChunking = DummyFileHelper.FileSize16Kb; + var states = new DownloadServiceEventsState(this); + var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile16KbName, DummyFileHelper.FileSize16Kb); + var activeChunks = 0; + int? chunkCounts = null; + var progressIds = new Dictionary(); + ChunkDownloadProgressChanged += (s, e) => { + activeChunks = Math.Max(activeChunks, e.ActiveChunks); + progressIds[e.ProgressId] = true; + chunkCounts ??= Package.Chunks.Length; + }; + + // act + await DownloadFileTaskAsync(url); + + // assert + Assert.IsTrue(Package.IsSaveComplete); + Assert.AreEqual(1, activeChunks); + Assert.AreEqual(1, progressIds.Count); + Assert.AreEqual(1, chunkCounts); + } - [TestMethod] - public async Task TestCreatePathIfNotExist() - { - // arrange - Options = GetDefaultConfig(); - var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile1KbName, DummyFileHelper.FileSize1Kb); - var path = Path.Combine(Path.GetTempPath(), "TestFolder1", "TestFolder2"); - var dir = new DirectoryInfo(path); - - // act - if (dir.Exists) - dir.Delete(true); - await DownloadFileTaskAsync(url, dir).ConfigureAwait(false); - - // assert - Assert.IsTrue(Package.IsSaveComplete); - Assert.IsTrue(Package.FileName.StartsWith(dir.FullName)); - Assert.IsTrue(File.Exists(Package.FileName), "FileName: " + Package.FileName); - } + [TestMethod] + public async Task TestCreatePathIfNotExist() + { + // arrange + Options = GetDefaultConfig(); + var url = DummyFileHelper.GetFileWithNameUrl(DummyFileHelper.SampleFile1KbName, DummyFileHelper.FileSize1Kb); + var path = Path.Combine(Path.GetTempPath(), "TestFolder1", "TestFolder2"); + var dir = new DirectoryInfo(path); + + // act + if (dir.Exists) + dir.Delete(true); + await DownloadFileTaskAsync(url, dir); + + // assert + Assert.IsTrue(Package.IsSaveComplete); + Assert.IsTrue(Package.FileName.StartsWith(dir.FullName)); + Assert.IsTrue(File.Exists(Package.FileName), "FileName: " + Package.FileName); } } \ No newline at end of file diff --git a/src/Downloader.Test/IntegrationTests/ParallelDownloadIntegrationTest.cs b/src/Downloader.Test/IntegrationTests/ParallelDownloadIntegrationTest.cs index 796a9eed..66035476 100644 --- a/src/Downloader.Test/IntegrationTests/ParallelDownloadIntegrationTest.cs +++ b/src/Downloader.Test/IntegrationTests/ParallelDownloadIntegrationTest.cs @@ -1,20 +1,19 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Downloader.Test.IntegrationTests +namespace Downloader.Test.IntegrationTests; + +[TestClass] +public class ParallelDownloadIntegrationTest : DownloadIntegrationTest { - [TestClass] - public class ParallelDownloadIntegrationTest : DownloadIntegrationTest + [TestInitialize] + public override void InitialTest() { - [TestInitialize] - public override void InitialTest() - { - Config = new DownloadConfiguration { - ParallelDownload = true, - BufferBlockSize = 1024, - ParallelCount = 4, - ChunkCount = 8, - MaxTryAgainOnFailover = 100 - }; - } + Config = new DownloadConfiguration { + ParallelDownload = true, + BufferBlockSize = 1024, + ParallelCount = 4, + ChunkCount = 8, + MaxTryAgainOnFailover = 100 + }; } } diff --git a/src/Downloader.Test/IntegrationTests/SerialDownloadIntegrationTest.cs b/src/Downloader.Test/IntegrationTests/SerialDownloadIntegrationTest.cs index b192b66d..1165ec7a 100644 --- a/src/Downloader.Test/IntegrationTests/SerialDownloadIntegrationTest.cs +++ b/src/Downloader.Test/IntegrationTests/SerialDownloadIntegrationTest.cs @@ -1,20 +1,19 @@ using Microsoft.VisualStudio.TestTools.UnitTesting; -namespace Downloader.Test.IntegrationTests +namespace Downloader.Test.IntegrationTests; + +[TestClass] +public class SerialDownloadIntegrationTest : DownloadIntegrationTest { - [TestClass] - public class SerialDownloadIntegrationTest : DownloadIntegrationTest + [TestInitialize] + public override void InitialTest() { - [TestInitialize] - public override void InitialTest() - { - Config = new DownloadConfiguration { - ParallelDownload = false, - BufferBlockSize = 1024, - ParallelCount = 4, - ChunkCount = 4, - MaxTryAgainOnFailover = 100 - }; - } + Config = new DownloadConfiguration { + ParallelDownload = false, + BufferBlockSize = 1024, + ParallelCount = 4, + ChunkCount = 4, + MaxTryAgainOnFailover = 100 + }; } } diff --git a/src/Downloader.Test/IntegrationTests/ThrottledStreamTest.cs b/src/Downloader.Test/IntegrationTests/ThrottledStreamTest.cs index 19e859cf..a1810197 100644 --- a/src/Downloader.Test/IntegrationTests/ThrottledStreamTest.cs +++ b/src/Downloader.Test/IntegrationTests/ThrottledStreamTest.cs @@ -7,180 +7,179 @@ using System.Threading; using System.Threading.Tasks; -namespace Downloader.Test.IntegrationTests +namespace Downloader.Test.IntegrationTests; + +[TestClass] +public class ThrottledStreamTest { - [TestClass] - public class ThrottledStreamTest + [TestMethod] + public async Task TestStreamReadSpeed() { - [TestMethod] - public void TestStreamReadSpeed() - { - TestReadStreamSpeed(1, false).Wait(); - } + await TestReadStreamSpeed(1, false); + } - [TestMethod] - public async Task TestStreamReadSpeedAsync() - { - await TestReadStreamSpeed(1, true); - } + [TestMethod] + public async Task TestStreamReadSpeedAsync() + { + await TestReadStreamSpeed(1, true); + } - [TestMethod] - public void TestStreamReadByDynamicSpeed() - { - TestReadStreamSpeed(2, false).Wait(); - } + [TestMethod] + public async Task TestStreamReadByDynamicSpeed() + { + await TestReadStreamSpeed(2, false); + } - [TestMethod] - public async Task TestStreamReadByDynamicSpeedAsync() - { - await TestReadStreamSpeed(2, true); - } + [TestMethod] + public async Task TestStreamReadByDynamicSpeedAsync() + { + await TestReadStreamSpeed(2, true); + } - private static async Task TestReadStreamSpeed(int speedX = 1, bool asAsync = false) + private static async Task TestReadStreamSpeed(int speedX = 1, bool asAsync = false) + { + // arrange + var limitationCoefficient = 0.9; // 90% + var size = 10240; // 10KB + var halfSize = size / 2; // 5KB + var maxBytesPerSecond = 1024; // 1024 Byte/s + var maxBytesPerSecondForSecondHalf = 1024 * speedX; // 1024 * X Byte/s + var expectedTimeForFirstHalf = (halfSize / maxBytesPerSecond) * 1000; + var expectedTimeForSecondHalf = (halfSize / maxBytesPerSecondForSecondHalf) * 1000; + var totalExpectedTime = (expectedTimeForFirstHalf + expectedTimeForSecondHalf) * limitationCoefficient; + var bytes = DummyData.GenerateOrderedBytes(size); + var buffer = new byte[maxBytesPerSecond / 8]; + var readSize = 1; + var totalReadSize = 0L; + using ThrottledStream stream = new ThrottledStream(new MemoryStream(bytes), maxBytesPerSecond); + var stopWatcher = Stopwatch.StartNew(); + + // act + stream.Seek(0, SeekOrigin.Begin); + while (readSize > 0) { - // arrange - var limitationCoefficient = 0.9; // 90% - var size = 10240; // 10KB - var halfSize = size / 2; // 5KB - var maxBytesPerSecond = 1024; // 1024 Byte/s - var maxBytesPerSecondForSecondHalf = 1024 * speedX; // 1024 * X Byte/s - var expectedTimeForFirstHalf = (halfSize / maxBytesPerSecond) * 1000; - var expectedTimeForSecondHalf = (halfSize / maxBytesPerSecondForSecondHalf) * 1000; - var totalExpectedTime = (expectedTimeForFirstHalf + expectedTimeForSecondHalf) * limitationCoefficient; - var bytes = DummyData.GenerateOrderedBytes(size); - var buffer = new byte[maxBytesPerSecond / 8]; - var readSize = 1; - var totalReadSize = 0L; - using ThrottledStream stream = new ThrottledStream(new MemoryStream(bytes), maxBytesPerSecond); - var stopWatcher = Stopwatch.StartNew(); - - // act - stream.Seek(0, SeekOrigin.Begin); - while (readSize > 0) + readSize = asAsync + ? await stream.ReadAsync(buffer, 0, buffer.Length, new CancellationToken()).ConfigureAwait(false) + : stream.Read(buffer, 0, buffer.Length); + totalReadSize += readSize; + + // increase speed (2X) after downloading half size + if (totalReadSize > halfSize && maxBytesPerSecond == stream.BandwidthLimit) { - readSize = asAsync - ? await stream.ReadAsync(buffer, 0, buffer.Length, new CancellationToken()).ConfigureAwait(false) - : stream.Read(buffer, 0, buffer.Length); - totalReadSize += readSize; - - // increase speed (2X) after downloading half size - if (totalReadSize > halfSize && maxBytesPerSecond == stream.BandwidthLimit) - { - stream.BandwidthLimit = maxBytesPerSecondForSecondHalf; - } + stream.BandwidthLimit = maxBytesPerSecondForSecondHalf; } - stopWatcher.Stop(); - - // assert - Assert.IsTrue(stopWatcher.ElapsedMilliseconds >= totalExpectedTime, - $"expected duration is: {totalExpectedTime}ms , but actual duration is: {stopWatcher.ElapsedMilliseconds}ms"); } + stopWatcher.Stop(); - [TestMethod] - public void TestStreamWriteSpeed() - { - // arrange - var size = 1024; - var bytesPerSecond = 256; // 256 B/s - var tolerance = 50; // 50 ms - var expectedTime = size / bytesPerSecond * 1000; // 4000 Milliseconds - var randomBytes = DummyData.GenerateRandomBytes(size); - using Stream stream = new ThrottledStream(new MemoryStream(), bytesPerSecond); - var stopWatcher = Stopwatch.StartNew(); - - // act - stream.Write(randomBytes, 0, randomBytes.Length); - stopWatcher.Stop(); - - // assert - Assert.IsTrue(stopWatcher.ElapsedMilliseconds + tolerance >= expectedTime, - $"actual duration is: {stopWatcher.ElapsedMilliseconds}ms"); - } + // assert + Assert.IsTrue(stopWatcher.ElapsedMilliseconds >= totalExpectedTime, + $"expected duration is: {totalExpectedTime}ms , but actual duration is: {stopWatcher.ElapsedMilliseconds}ms"); + } - [TestMethod] - public async Task TestStreamWriteSpeedAsync() - { - // arrange - var size = 1024; - var bytesPerSecond = 256; // 256 B/s - var tolerance = 50; // 50 ms - var expectedTime = size / bytesPerSecond * 1000; // 4000 Milliseconds - var randomBytes = DummyData.GenerateRandomBytes(size); - using Stream stream = new ThrottledStream(new MemoryStream(), bytesPerSecond); - var stopWatcher = Stopwatch.StartNew(); - - // act - await stream.WriteAsync(randomBytes, 0, randomBytes.Length).ConfigureAwait(false); - stopWatcher.Stop(); - - // assert - Assert.IsTrue(stopWatcher.ElapsedMilliseconds + tolerance >= expectedTime, - $"actual duration is: {stopWatcher.ElapsedMilliseconds}ms"); - } + [TestMethod] + public void TestStreamWriteSpeed() + { + // arrange + var size = 1024; + var bytesPerSecond = 256; // 256 B/s + var tolerance = 50; // 50 ms + var expectedTime = size / bytesPerSecond * 1000; // 4000 Milliseconds + var randomBytes = DummyData.GenerateRandomBytes(size); + using Stream stream = new ThrottledStream(new MemoryStream(), bytesPerSecond); + var stopWatcher = Stopwatch.StartNew(); + + // act + stream.Write(randomBytes, 0, randomBytes.Length); + stopWatcher.Stop(); + + // assert + Assert.IsTrue(stopWatcher.ElapsedMilliseconds + tolerance >= expectedTime, + $"actual duration is: {stopWatcher.ElapsedMilliseconds}ms"); + } - [TestMethod] - public void TestNegativeBandwidth() - { - // arrange - int maximumBytesPerSecond = -1; + [TestMethod] + public async Task TestStreamWriteSpeedAsync() + { + // arrange + var size = 1024; + var bytesPerSecond = 256; // 256 B/s + var tolerance = 50; // 50 ms + var expectedTime = size / bytesPerSecond * 1000; // 4000 Milliseconds + var randomBytes = DummyData.GenerateRandomBytes(size); + using Stream stream = new ThrottledStream(new MemoryStream(), bytesPerSecond); + var stopWatcher = Stopwatch.StartNew(); + + // act + await stream.WriteAsync(randomBytes, 0, randomBytes.Length).ConfigureAwait(false); + stopWatcher.Stop(); + + // assert + Assert.IsTrue(stopWatcher.ElapsedMilliseconds + tolerance >= expectedTime, + $"actual duration is: {stopWatcher.ElapsedMilliseconds}ms"); + } - // act - void CreateThrottledStream() - { - using var throttledStream = new ThrottledStream(new MemoryStream(), maximumBytesPerSecond); - } + [TestMethod] + public void TestNegativeBandwidth() + { + // arrange + int maximumBytesPerSecond = -1; - // assert - Assert.ThrowsException(CreateThrottledStream); + // act + void CreateThrottledStream() + { + using var throttledStream = new ThrottledStream(new MemoryStream(), maximumBytesPerSecond); } - [TestMethod] - public void TestZeroBandwidth() - { - // arrange - int maximumBytesPerSecond = 0; + // assert + Assert.ThrowsException(CreateThrottledStream); + } - // act - using var throttledStream = new ThrottledStream(new MemoryStream(), maximumBytesPerSecond); + [TestMethod] + public void TestZeroBandwidth() + { + // arrange + int maximumBytesPerSecond = 0; - // assert - Assert.AreEqual(long.MaxValue, throttledStream.BandwidthLimit); - } + // act + using var throttledStream = new ThrottledStream(new MemoryStream(), maximumBytesPerSecond); - [TestMethod] - public void TestStreamIntegrityWithSpeedMoreThanSize() - { - TestStreamIntegrity(500, 1024); - } + // assert + Assert.AreEqual(long.MaxValue, throttledStream.BandwidthLimit); + } - [TestMethod] - public void TestStreamIntegrityWithMaximumSpeed() - { - TestStreamIntegrity(4096, long.MaxValue); - } + [TestMethod] + public void TestStreamIntegrityWithSpeedMoreThanSize() + { + TestStreamIntegrity(500, 1024); + } - [TestMethod] - public void TestStreamIntegrityWithSpeedLessThanSize() - { - TestStreamIntegrity(247, 77); - } + [TestMethod] + public void TestStreamIntegrityWithMaximumSpeed() + { + TestStreamIntegrity(4096, long.MaxValue); + } - private static void TestStreamIntegrity(int streamSize, long maximumBytesPerSecond) - { - // arrange - byte[] data = DummyData.GenerateOrderedBytes(streamSize); - byte[] copiedData = new byte[streamSize]; - using Stream stream = new ThrottledStream(new MemoryStream(), maximumBytesPerSecond); - - // act - stream.Write(data, 0, data.Length); - stream.Seek(0, SeekOrigin.Begin); - stream.Read(copiedData, 0, copiedData.Length); - - // assert - Assert.AreEqual(streamSize, data.Length); - Assert.AreEqual(streamSize, copiedData.Length); - Assert.IsTrue(data.SequenceEqual(copiedData)); - } + [TestMethod] + public void TestStreamIntegrityWithSpeedLessThanSize() + { + TestStreamIntegrity(247, 77); + } + + private static void TestStreamIntegrity(int streamSize, long maximumBytesPerSecond) + { + // arrange + byte[] data = DummyData.GenerateOrderedBytes(streamSize); + byte[] copiedData = new byte[streamSize]; + using Stream stream = new ThrottledStream(new MemoryStream(), maximumBytesPerSecond); + + // act + stream.Write(data, 0, data.Length); + stream.Seek(0, SeekOrigin.Begin); + stream.Read(copiedData, 0, copiedData.Length); + + // assert + Assert.AreEqual(streamSize, data.Length); + Assert.AreEqual(streamSize, copiedData.Length); + Assert.IsTrue(data.SequenceEqual(copiedData)); } } diff --git a/src/Downloader.Test/UnitTests/DownloadBuilderTest.cs b/src/Downloader.Test/UnitTests/DownloadBuilderTest.cs index 6f8b50d8..9a8b78d2 100644 --- a/src/Downloader.Test/UnitTests/DownloadBuilderTest.cs +++ b/src/Downloader.Test/UnitTests/DownloadBuilderTest.cs @@ -112,7 +112,7 @@ public void TestPathless() } [TestMethod] - public void TestPackageWhenNewUrl() + public async Task TestPackageWhenNewUrl() { // arrange DownloadPackage beforePackage = null; @@ -122,7 +122,7 @@ public void TestPackageWhenNewUrl() // act beforePackage = download.Package; - download.StartAsync().Wait(); + await download.StartAsync(); // assert Assert.IsNotNull(beforePackage); @@ -132,7 +132,7 @@ public void TestPackageWhenNewUrl() } [TestMethod] - public void TestPackageWhenResume() + public async Task TestPackageWhenResume() { // arrange DownloadPackage package = new DownloadPackage() { @@ -143,7 +143,7 @@ public void TestPackageWhenResume() DownloadPackage beforeStartPackage = download.Package; // act - download.StartAsync().Wait(); + await download.StartAsync(); // assert Assert.IsNotNull(beforeStartPackage); diff --git a/src/Downloader.Test/UnitTests/StorageTest.cs b/src/Downloader.Test/UnitTests/StorageTest.cs index c7075921..75b2542a 100644 --- a/src/Downloader.Test/UnitTests/StorageTest.cs +++ b/src/Downloader.Test/UnitTests/StorageTest.cs @@ -132,16 +132,16 @@ public async Task WriteAsyncMultipleTimeTest() } [TestMethod] - public void WriteAsyncOutOfRangeExceptionTest() + public async Task WriteAsyncOutOfRangeExceptionTest() { // arrange var length = DataLength + 1; // act - void WriteMethod() => Storage.WriteAsync(0, Data, length).Wait(); + var writeMethod = async () => await Storage.WriteAsync(0, Data, length); // assert - Assert.ThrowsException(WriteMethod); + await Assert.ThrowsExceptionAsync(writeMethod); } [TestMethod]