Skip to content

Commit

Permalink
Merge branch 'release/v3.0.1'
Browse files Browse the repository at this point in the history
  • Loading branch information
bezzad committed Nov 2, 2022
2 parents 23e97ab + 194ccf9 commit 6d875e9
Show file tree
Hide file tree
Showing 14 changed files with 132 additions and 132 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@
<StartupObject>Downloader.DummyHttpServer.HttpServer</StartupObject>
<ApplicationIcon />
<OutputType>Exe</OutputType>
<SignAssembly>True</SignAssembly>
<DelaySign>True</DelaySign>
<AssemblyOriginatorKeyFile>sgKey.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)' == 'Debug'">
Expand Down
18 changes: 16 additions & 2 deletions src/Downloader.DummyHttpServer/MockMemoryStream.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,23 @@ public MockMemoryStream(long size, long failureOffset = 0, bool timeout = false)
GC.Collect();
}

// called when framework will be .NetCore 3.1
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken)
{
var validCount = await ReadAsync(count);
Array.Fill(buffer, _value, offset, validCount);
return validCount;
}

// called when framework will be .Net 6.0
public override async ValueTask<int> ReadAsync(Memory<byte> destination, CancellationToken cancellationToken = default)
{
var validCount = await ReadAsync(destination.Length);
destination.Span.Fill(_value);
return validCount;
}

private async ValueTask<int> ReadAsync(int count)
{
var validCount = Math.Min(Math.Min(count, Length - Position), _failureOffset - Position);
if (validCount == 0 && _failureOffset > 0)
Expand All @@ -40,10 +56,8 @@ public override async Task<int> ReadAsync(byte[] buffer, int offset, int count,
}
}

Array.Fill(buffer, _value, offset, (int)validCount);
await Task.Delay(_delayTime);
Position += validCount;

return (int)validCount;
}
}
Expand Down
Binary file added src/Downloader.DummyHttpServer/sgKey.snk
Binary file not shown.
20 changes: 10 additions & 10 deletions src/Downloader.Test/Downloader.Test.csproj
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>netcoreapp3.1</TargetFramework>

<TargetFrameworks>netcoreapp3.1;net6.0</TargetFrameworks>
<IsPackable>false</IsPackable>

<SignAssembly>true</SignAssembly>

<AssemblyOriginatorKeyFile>Sign.snk</AssemblyOriginatorKeyFile>
<EnforceCodeStyleInBuild>True</EnforceCodeStyleInBuild>
<IncludeSymbols>True</IncludeSymbols>
<Title>Downloader Tests</Title>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
Expand All @@ -24,15 +24,15 @@
<PackageReference Include="AssertMessage.Fody" Version="2.1.0">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Fody" Version="6.6.0">
<PackageReference Include="Fody" Version="6.6.4">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Moq" Version="4.16.1" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.8" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.8" />
<PackageReference Include="coverlet.collector" Version="3.1.0">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.3.2" />
<PackageReference Include="Moq" Version="4.18.2" />
<PackageReference Include="MSTest.TestAdapter" Version="2.2.10" />
<PackageReference Include="MSTest.TestFramework" Version="2.2.10" />
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
Expand Down
2 changes: 1 addition & 1 deletion src/Downloader.Test/HelperTests/DummyFileControllerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace Downloader.Test.HelperTests
[TestClass]
public class DummyFileControllerTest
{
private string contentType = "application/octet-stream";
private readonly string contentType = "application/octet-stream";
private WebHeaderCollection headers;

[TestMethod]
Expand Down
33 changes: 30 additions & 3 deletions src/Downloader.Test/IntegrationTests/DownloadIntegrationTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,9 @@ public async Task StopResumeDownloadFromLastPositionTest()
public async Task StopResumeDownloadOverFirstPackagePositionTest()
{
// arrange
var packageCheckPoint = new DownloadPackage() { Address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb) };
var packageCheckPoint = new DownloadPackage() {
Address = DummyFileHelper.GetFileUrl(DummyFileHelper.FileSize16Kb)
};
var stopThreshold = 4100;
var totalReceivedBytes = 0L;
var downloader = new DownloadService(Config);
Expand All @@ -264,6 +266,7 @@ public async Task StopResumeDownloadOverFirstPackagePositionTest()
// check point of package for once time
packageCheckPoint.Chunks ??= downloader.Package.Chunks.Clone() as Chunk[];
packageCheckPoint.Storage ??= downloader.Package.Storage;
}
};

Expand All @@ -274,7 +277,8 @@ public async Task StopResumeDownloadOverFirstPackagePositionTest()
isSavingStateOnCancel |= downloader.Package.IsSaving;
var firstCheckPointClone = new DownloadPackage() {
Address = packageCheckPoint.Address,
Chunks = packageCheckPoint.Chunks.Clone() as Chunk[]
Chunks = packageCheckPoint.Chunks.Clone() as Chunk[],
Storage = packageCheckPoint.Storage
};
// resume download from first stopped point.
await downloader.DownloadFileTaskAsync(firstCheckPointClone).ConfigureAwait(false);
Expand Down Expand Up @@ -616,7 +620,7 @@ private async Task testRetryDownloadAfterFailure(bool timeout)
Config.MinimumSizeOfChunking = 0;
Config.Timeout = 100;
var downloadService = new DownloadService(Config);
var url = timeout
var url = timeout
? DummyFileHelper.GetFileWithTimeoutAfterOffset(fileSize, failureOffset)
: DummyFileHelper.GetFileWithFailureAfterOffset(fileSize, failureOffset);
downloadService.DownloadFileCompleted += (s, e) => error = e.Error;
Expand All @@ -636,5 +640,28 @@ private async Task testRetryDownloadAfterFailure(bool timeout)

await stream.DisposeAsync();
}

[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);
}
}
}
21 changes: 20 additions & 1 deletion src/Downloader.Test/IntegrationTests/DownloadServiceTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -557,7 +557,6 @@ public async Task TestResumeDownloadImmedietalyAfterCancellationAsync()
Assert.IsTrue(secondStartProgressPercent > 50, $"progress percent is {secondStartProgressPercent}");
}


[TestMethod]
[Timeout(5000)]
public async Task TestStopDownloadOnClearWhenRunning()
Expand Down Expand Up @@ -633,5 +632,25 @@ public async Task TestMinimumSizeOfChunking()
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);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,143 +2,78 @@
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;

namespace Downloader.Test.UnitTests
namespace Downloader.Test.IntegrationTests
{
[TestClass]
public class ThrottledStreamTest
{
[TestMethod]
public void TestStreamReadSpeed()
{
// arrange
var limitationCoefficient = 0.8; // 80%
var size = 10240; // 10KB
var maxBytesPerSecond = 1024; // 1024 Byte/s
var expectedTime = size / maxBytesPerSecond * 1000 * limitationCoefficient; // 80% of 10000 Milliseconds
var randomBytes = DummyData.GenerateRandomBytes(size);
var buffer = new byte[maxBytesPerSecond/8];
var readSize = 1;
using Stream stream = new ThrottledStream(new MemoryStream(randomBytes), maxBytesPerSecond);
var stopWatcher = Stopwatch.StartNew();

// act
stream.Seek(0, SeekOrigin.Begin);
while (readSize > 0)
{
readSize = stream.Read(buffer, 0, buffer.Length);
}
stopWatcher.Stop();

// assert
Assert.IsTrue(stopWatcher.ElapsedMilliseconds >= expectedTime,
$"expected duration is: {expectedTime}ms , but actual duration is: {stopWatcher.ElapsedMilliseconds}ms");
TestReadStreamSpeed(1, false).Wait();
}

[TestMethod]
public async Task TestStreamReadSpeedAsync()
{
// arrange
var limitationCoefficient = 0.8; // 80%
var size = 10240; // 10KB
var maxBytesPerSecond = 1024; // 1024 Byte/s
var expectedTime = size / maxBytesPerSecond * 1000 * limitationCoefficient; // 80% of 10000 Milliseconds
var randomBytes = DummyData.GenerateRandomBytes(size);
var buffer = new byte[maxBytesPerSecond/8];
var readSize = 1;
using Stream stream = new ThrottledStream(new MemoryStream(randomBytes), maxBytesPerSecond);
var stopWatcher = Stopwatch.StartNew();

// act
stream.Seek(0, SeekOrigin.Begin);
while (readSize > 0)
{
readSize = await stream.ReadAsync(buffer, 0, buffer.Length, new CancellationToken()).ConfigureAwait(false);
}
stopWatcher.Stop();

// assert
Assert.IsTrue(stopWatcher.ElapsedMilliseconds >= expectedTime,
$"expected duration is: {expectedTime}ms , but actual duration is: {stopWatcher.ElapsedMilliseconds}ms");
await TestReadStreamSpeed(1, true);
}

[TestMethod]
public void TestStreamReadByDynamicSpeed()
{
// arrange
var limitationCoefficient = 0.9; // 90%
var size = 10240; // 10KB
var maxBytesPerSecond = 1024; // 1 KByte/s
var halfSize = size/2;
// 90% of 10000 Milliseconds
var expectedTime = ((halfSize/maxBytesPerSecond) + (halfSize/(maxBytesPerSecond*2))) * 1000 * limitationCoefficient;
var randomBytes = DummyData.GenerateRandomBytes(size);
var buffer = new byte[maxBytesPerSecond/8];
var readSize = 1;
var totalReadSize = 0L;
using ThrottledStream stream = new ThrottledStream(new MemoryStream(randomBytes), maxBytesPerSecond);
var stopWatcher = Stopwatch.StartNew();

// act
stream.Seek(0, SeekOrigin.Begin);
while (readSize > 0)
{
readSize = stream.Read(buffer, 0, buffer.Length);
totalReadSize += readSize;

// increase speed (2X) after downloading half size
if (totalReadSize > size/2 && maxBytesPerSecond == stream.BandwidthLimit)
{
stream.BandwidthLimit *= 2;
}
}
stopWatcher.Stop();

// assert
Assert.IsTrue(stopWatcher.ElapsedMilliseconds >= expectedTime,
$"expected duration is: {expectedTime}ms , but actual duration is: {stopWatcher.ElapsedMilliseconds}ms");
TestReadStreamSpeed(2, false).Wait();
}

[TestMethod]
public async Task TestStreamReadByDynamicSpeedAsync()
{
await TestReadStreamSpeed(2, true);
}

private static async Task TestReadStreamSpeed(int speedX = 1, bool asAsync = false)
{
// arrange
var limitationCoefficient = 0.9; // 90%
var size = 10240; // 10KB
var maxBytesPerSecond = 1024; // 1 KByte/s
var halfSize = size/2;
// 90% of 10000 Milliseconds
var expectedTime = ((halfSize/maxBytesPerSecond) + (halfSize/(maxBytesPerSecond*2))) * 1000 * limitationCoefficient;
var randomBytes = DummyData.GenerateRandomBytes(size);
var buffer = new byte[maxBytesPerSecond/8];
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(randomBytes), maxBytesPerSecond);
using ThrottledStream stream = new ThrottledStream(new MemoryStream(bytes), maxBytesPerSecond);
var stopWatcher = Stopwatch.StartNew();

// act
stream.Seek(0, SeekOrigin.Begin);
while (readSize > 0)
{
readSize = await stream.ReadAsync(buffer, 0, buffer.Length, new CancellationToken()).ConfigureAwait(false);
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 > size/2 && maxBytesPerSecond == stream.BandwidthLimit)
if (totalReadSize > halfSize && maxBytesPerSecond == stream.BandwidthLimit)
{
stream.BandwidthLimit *= 2;
stream.BandwidthLimit = maxBytesPerSecondForSecondHalf;
}
}
stopWatcher.Stop();

// assert
Assert.IsTrue(stopWatcher.ElapsedMilliseconds >= expectedTime,
$"expected duration is: {expectedTime}ms , but actual duration is: {stopWatcher.ElapsedMilliseconds}ms");
Assert.IsTrue(stopWatcher.ElapsedMilliseconds >= totalExpectedTime,
$"expected duration is: {totalExpectedTime}ms , but actual duration is: {stopWatcher.ElapsedMilliseconds}ms");
}

[TestMethod]
Expand Down Expand Up @@ -230,7 +165,7 @@ public void TestStreamIntegrityWithSpeedLessThanSize()
TestStreamIntegrity(247, 77);
}

private void TestStreamIntegrity(int streamSize, long maximumBytesPerSecond)
private static void TestStreamIntegrity(int streamSize, long maximumBytesPerSecond)
{
// arrange
byte[] data = DummyData.GenerateOrderedBytes(streamSize);
Expand Down
Loading

0 comments on commit 6d875e9

Please sign in to comment.