Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
92075f2
Bump the all-deps group with 5 updates (#224)
dependabot[bot] Jun 11, 2025
79c0573
update deps
AnakinRaW Jun 11, 2025
60037a8
fix deps update
AnakinRaW Jun 11, 2025
327ed61
try fix
AnakinRaW Jun 11, 2025
4142628
Bump the all-deps group with 1 update (#227)
dependabot[bot] Jun 17, 2025
f554717
update deps & fix test
AnakinRaW Jul 15, 2025
c5b1b1d
Bump Microsoft.Bcl.AsyncInterfaces from 6.0.0 to 9.0.7 (#238)
dependabot[bot] Jul 15, 2025
d0b30e9
update deps
AnakinRaW Aug 16, 2025
1725eca
fix test
AnakinRaW Aug 16, 2025
4e5cf82
Merge branch 'main' into develop
AnakinRaW Aug 25, 2025
88c2776
Bump the actions-deps group across 1 directory with 2 updates (#245)
dependabot[bot] Aug 25, 2025
2b7c722
Bump actions/setup-dotnet from 4 to 5 in the actions-deps group (#247)
dependabot[bot] Sep 10, 2025
5f3efd8
Bump the all-deps group with 6 updates (#253)
dependabot[bot] Sep 12, 2025
662f9d9
update deps
AnakinRaW Oct 2, 2025
9175dd3
Bump the all-deps group with 1 update (#264)
dependabot[bot] Oct 7, 2025
eed19a7
Bump the actions-deps group across 1 directory with 3 updates (#286)
dependabot[bot] Dec 7, 2025
5ef2eac
migrate solution
AnakinRaW Dec 7, 2025
093d3c7
update .net
AnakinRaW Dec 7, 2025
83c15cf
reduce some warnings
AnakinRaW Dec 7, 2025
e65029f
extensions
AnakinRaW Dec 7, 2025
f8144bf
logging
AnakinRaW Dec 7, 2025
a8a56c5
revert extensions
AnakinRaW Dec 7, 2025
ee205f7
remove some warning
AnakinRaW Dec 8, 2025
f9724fc
remove more warnings
AnakinRaW Dec 8, 2025
56b0247
update deps
AnakinRaW Dec 12, 2025
36b456c
fix build
AnakinRaW Dec 12, 2025
6898486
fix build
AnakinRaW Dec 12, 2025
b1fbb6c
Bump the actions-deps group with 2 updates (#290)
dependabot[bot] Dec 15, 2025
075729f
Add ValueListDictionary (#291)
AnakinRaW Dec 28, 2025
e4cebe5
Public Testing (#292)
AnakinRaW Dec 28, 2025
9e7e787
merge main into develop
AnakinRaW Dec 28, 2025
ae1c7c9
fix merge
AnakinRaW Dec 28, 2025
2e9b2ff
fix build for real
AnakinRaW Dec 28, 2025
d61485c
Async pipeline (#294)
AnakinRaW Jan 16, 2026
187b41a
make weekly deps check
AnakinRaW Jan 16, 2026
f6665ca
update copyright year
AnakinRaW Jan 16, 2026
67b89ec
update version
AnakinRaW Jan 16, 2026
ab9cb8d
update
AnakinRaW Jan 16, 2026
989307a
expose API to create an ImmutableFrugalList from a single value
AnakinRaW Jan 16, 2026
14df72e
Lifecycle hooks for StepRunnerPipleines (#297)
AnakinRaW Jan 16, 2026
14d78f0
Customizable runner pipeline (#298)
AnakinRaW Jan 16, 2026
4fb2c23
Improved error handling & awaiting in SimplePipeline
AnakinRaW Jan 18, 2026
feabb26
Thread-safe awaitable task consistency for steps
AnakinRaW Jan 18, 2026
088403c
add tests
AnakinRaW Jan 18, 2026
8fbf933
fix failling tests
AnakinRaW Jan 19, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ updates:
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "daily"
interval: "weekly"
groups:
actions-deps:
patterns:
Expand All @@ -19,7 +19,7 @@ updates:
- package-ecosystem: "nuget"
directory: "/"
schedule:
interval: "daily"
interval: "weekly"
target-branch: "develop"
open-pull-requests-limit: 1
groups:
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ jobs:
- name: Create packages
run: dotnet pack --configuration Release --output ./packages
- name: Upload a Build Artifact
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v6
with:
name: NuGet packages
path: packages/*.*
Expand All @@ -45,7 +45,7 @@ jobs:
fetch-depth: 0
- name: Setup .NET
uses: actions/setup-dotnet@v5
- uses: actions/download-artifact@v6
- uses: actions/download-artifact@v7
with:
name: NuGet packages
path: packages
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,7 @@ jobs:
dotnet-version: |
6.0.x
8.0.x
9.0.x
10.0.x

- name: Build & Test in Release Mode
run: dotnet test --configuration Release --logger "GitHubActions"
run: dotnet test --configuration Release --report-github
2 changes: 1 addition & 1 deletion CommonUtilities.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@
<Project Path="src/CommonUtilities/src/CommonUtilities.csproj" />
<Project Path="src/CommonUtilities/test/CommonUtilities.Test.csproj" />
</Folder>
<Project Path="src/CommonUtilities.TestingUtilities/CommonUtilities.TestingUtilities.csproj" />
<Project Path="src/CommonUtilities.Testing/CommonUtilities.Testing.csproj" />
</Solution>
15 changes: 9 additions & 6 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
<Project>
<PropertyGroup>
<RepoRootPath>$(MSBuildThisFileDirectory)</RepoRootPath>
<LangVersion>preview</LangVersion>
<ImplicitUsings>disable</ImplicitUsings>
<ProduceReferenceAssembly>true</ProduceReferenceAssembly>
<EnableDynamicPlatformResolution>true</EnableDynamicPlatformResolution>
<GitVersionBaseDirectory>$(MSBuildThisFileDirectory)</GitVersionBaseDirectory>
<PackageOutputPath>$(RepoRootPath)bin\Packages\$(Configuration)\</PackageOutputPath>
</PropertyGroup>
<PropertyGroup>
<Product>.NET Common Utilities</Product>
<Copyright>Copyright © AnakinRaW 2025</Copyright>
<LangVersion>preview</LangVersion>
<ImplicitUsings>disable</ImplicitUsings>
<Nullable>enable</Nullable>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>
<PropertyGroup>
<Product>AnakinRaW Common Utilities</Product>
<Copyright>Copyright © AnakinRaW 2026</Copyright>
<Authors>AnakinRaW</Authors>
<Owners>AnakinRaW</Owners>
<PackageProjectUrl>https://github.com/AnakinRaW/CommonUtilities</PackageProjectUrl>
Expand All @@ -19,8 +23,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SauceControl.InheritDoc" Version="2.0.2" PrivateAssets="all" />
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="8.0.0">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PackageReference Include="Microsoft.SourceLink.GitHub" Version="10.0.102">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
<PackageReference Include="Nerdbank.GitVersioning" Condition="!Exists('packages.config')">
Expand Down
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 AnakinRaW
Copyright (c) 2026 AnakinRaW

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
5 changes: 5 additions & 0 deletions global.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"test": {
"runner": "Microsoft.Testing.Platform"
}
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<Description>Simple Download Manager supporting the local file system and HTTP downlaods.</Description>
<Title>CommonUtilities.DownloadManager</Title>
<PackageId>AnakinRaW.CommonUtilities.DownloadManager</PackageId>
<Description>Simple DownloadAsync Manager supporting the local file system and HTTP downlaods.</Description>
</PropertyGroup>

<PropertyGroup>
<TargetFrameworks>netstandard2.0;netstandard2.1;net9.0</TargetFrameworks>
<IsPackable>true</IsPackable>
<TargetFrameworks>netstandard2.0;netstandard2.1;net10.0</TargetFrameworks>
<RootNamespace>AnakinRaW.CommonUtilities.DownloadManager</RootNamespace>
<AssemblyName>AnakinRaW.CommonUtilities.DownloadManager</AssemblyName>
<Nullable>enable</Nullable>
<EnableNETAnalyzers>True</EnableNETAnalyzers>
<IsPackable>true</IsPackable>
<NeutralLanguage>en</NeutralLanguage>
</PropertyGroup>

<PropertyGroup>
Expand All @@ -21,6 +22,11 @@
<InheritDocEnabled>true</InheritDocEnabled>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.2" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.2" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="IsExternalInit" Version="1.0.3">
<PrivateAssets>all</PrivateAssets>
Expand All @@ -30,13 +36,14 @@
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\CommonUtilities\src\CommonUtilities.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.102" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ public override string Message
/// Initializes a new instance of the <see cref="DownloadFailedException"/> class from the specified download failures.
/// </summary>
/// <param name="downloadFailures">The failures which occurred during a file download.</param>
public DownloadFailedException(IEnumerable<DownloadFailureInformation> downloadFailures)
internal DownloadFailedException(IEnumerable<DownloadFailureInformation> downloadFailures)
{
DownloadFailures = downloadFailures;
}
Expand Down
2 changes: 1 addition & 1 deletion src/CommonUtilities.DownloadManager/src/DownloadKind.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,5 @@ public enum DownloadKind
/// <summary>
/// The provider supports downloading files from the Internet.
/// </summary>
Internet,
Internet
}
4 changes: 2 additions & 2 deletions src/CommonUtilities.DownloadManager/src/DownloadManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public sealed class DownloadManager : IDownloadManager
/// <summary>
/// Initializes a new instance of the <see cref="DownloadManager"/> class.
/// </summary>
/// <param name="serviceProvider"></param>
/// <param name="serviceProvider">The service provider.</param>
public DownloadManager(IServiceProvider serviceProvider)
: this(DownloadManagerConfiguration.Default, serviceProvider)
{
Expand Down Expand Up @@ -172,7 +172,7 @@ private async Task<DownloadResult> DownloadWithRetry(
bool validationSuccess;
try
{
validationSuccess = await validator.Validate(outputStream, summary.DownloadedSize, cancellationToken)
validationSuccess = await validator.ValidateAsync(outputStream, summary.DownloadedSize, cancellationToken)
.ConfigureAwait(false);
}
catch (Exception e)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public string? LastSuccessfulProvider
if (value == null)
return;
field = value;
// ReSharper disable once InconsistentlySynchronizedField
_preferredProviders.AddOrUpdate(value, 1, (_, existingVal) => ++existingVal);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ protected override async Task<DownloadResult> DownloadAsyncCore(
{
if (response is not null)
{
#if NET
await using var responseStream = await response.Content.ReadAsStreamAsync(cancellationToken).ConfigureAwait(false);
#if NET || NETSTANDARD2_1_OR_GREATER
await using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
#else
using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false);
#endif
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ protected override async Task<DownloadResult> DownloadAsyncCore(
try
{
summary.DownloadedSize = await StreamUtilities.CopyStreamWithProgressAsync(
responseStream,
responseStream!,
totalStreamLength,
outputStream,
progress,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ private AlwaysValidDownloadValidator()
}

/// <inheritdoc />
public Task<bool> Validate(Stream stream, long downloadedBytes, CancellationToken token = default)
public Task<bool> ValidateAsync(Stream stream, long downloadedBytes, CancellationToken token = default)
{
return Task.FromResult(true);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public HashDownloadValidator(byte[]? hash, HashTypeKey hashType, IServiceProvide
}

/// <inheritdoc />
public async Task<bool> Validate(Stream stream, long downloadedBytes, CancellationToken token = default)
public async Task<bool> ValidateAsync(Stream stream, long downloadedBytes, CancellationToken token = default)
{
if (stream == null)
throw new ArgumentNullException(nameof(stream));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,5 @@ public interface IDownloadValidator
/// <param name="downloadedBytes">The number of bytes downloaded.</param>
/// <param name="token">The cancellation token.</param>
/// <returns><see langowrd="true"/> if the download is valid; otherwise, <see langowrd="false"/>.</returns>
Task<bool> Validate(Stream stream, long downloadedBytes, CancellationToken token = default);
Task<bool> ValidateAsync(Stream stream, long downloadedBytes, CancellationToken token = default);
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public SizeDownloadValidator(long expectedDownloadBytes)
}

/// <inheritdoc />
public Task<bool> Validate(Stream stream, long downloadedBytes, CancellationToken token = default)
public Task<bool> ValidateAsync(Stream stream, long downloadedBytes, CancellationToken token = default)
{
return Task.FromResult(_expectedDownloadBytes == downloadedBytes);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,31 +1,29 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>net10.0;net8.0</TargetFrameworks>
<TargetFrameworks Condition="!$([MSBuild]::IsOsUnixLike())">$(TargetFrameworks);net481</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
<OutputType>Exe</OutputType>
</PropertyGroup>

<PropertyGroup>
<RootNamespace>AnakinRaW.CommonUtilities.DownloadManager.Test</RootNamespace>
<AssemblyName>AnakinRaW.CommonUtilities.DownloadManager.Test</AssemblyName>
</PropertyGroup>

<PropertyGroup>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="MartinCostello.Logging.XUnit" Version="0.7.0" />
<PackageReference Include="GitHubActionsTestLogger" Version="3.0.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
<PackageReference Include="System.Net.Http" Version="4.3.4" Condition="$(TargetFramework) == 'net481'" />
<PackageReference Include="MartinCostello.Logging.XUnit.v3" Version="0.7.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="10.0.2" />
<PackageReference Include="GitHubActionsTestLogger" Version="3.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="18.0.1" />
<PackageReference Include="Testably.Abstractions.Testing" Version="5.0.1" />
<PackageReference Include="xunit" Version="2.9.3" />
<PackageReference Include="xunit.v3.mtp-v2" Version="3.2.2" />
<PackageReference Include="Microsoft.Testing.Platform" Version="2.0.2" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="xunit.runner.visualstudio" Version="3.1.5">
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
Expand All @@ -37,8 +35,12 @@
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\CommonUtilities.TestingUtilities\CommonUtilities.TestingUtilities.csproj" />
<ProjectReference Include="..\src\CommonUtilities.DownloadManager.csproj" />
<ProjectReference Include="..\..\CommonUtilities.Testing\CommonUtilities.Testing.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="10.0.102" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@

namespace AnakinRaW.CommonUtilities.DownloadManager.Test;

public class DownloadManagerTest : CommonTestBase
public class DownloadManagerTest : TestBaseWithFileSystem
{
private const string Destination = "file.txt";

Expand Down Expand Up @@ -464,7 +464,7 @@ void ProgressMethod(DownloadUpdate status)

private class ThrowingValidator : IDownloadValidator
{
public Task<bool> Validate(Stream stream, long downloadedBytes, CancellationToken token = default)
public Task<bool> ValidateAsync(Stream stream, long downloadedBytes, CancellationToken token = default)
{
throw new Exception("Test");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

namespace AnakinRaW.CommonUtilities.DownloadManager.Test;

public class LeastRecentlyUsedDownloadProvidersTest : CommonTestBase
public class LeastRecentlyUsedDownloadProvidersTest : TestBaseWithFileSystem
{
private readonly LeastRecentlyUsedDownloadProviders _provider = new();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

namespace AnakinRaW.CommonUtilities.DownloadManager.Test.Providers;

public abstract class DownloadProviderTestBase : CommonTestBase
public abstract class DownloadProviderTestBase : TestBaseWithFileSystem
{
protected abstract Type ExpectedSourceNotFoundExceptionType { get; }

Expand All @@ -20,7 +20,7 @@ public abstract class DownloadProviderTestBase : CommonTestBase
public async Task DownloadAsync_SourceNotFound_Throws()
{
var source = CreateSource(false);
await Assert.ThrowsAsync(ExpectedSourceNotFoundExceptionType, async () => await Download(source, new MemoryStream(), null));
await Assert.ThrowsAsync(ExpectedSourceNotFoundExceptionType, async () => await TestDownloadAsync(source, new MemoryStream(), null, TestContext.Current.CancellationToken));
}

[Theory]
Expand All @@ -32,7 +32,7 @@ public async Task DownloadAsync(bool createDefaultOptions)
var source = CreateSource(true);

var options = createDefaultOptions ? new DownloadOptions() : null;
var result = await Download(source, outStream, options);
var result = await TestDownloadAsync(source, outStream, options, TestContext.Current.CancellationToken);
Assert.True(result.DownloadedSize > 0);
Assert.Equal(result.DownloadedSize, outStream.Length);
}
Expand All @@ -45,10 +45,10 @@ public async Task DownloadAsync_DownloadCancelled_Throws(bool createDefaultOptio
var outStream = new MemoryStream();
var source = CreateSource(true);
var options = createDefaultOptions ? new DownloadOptions() : null;
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await Download(source, outStream, options, new CancellationToken(true)));
await Assert.ThrowsAnyAsync<OperationCanceledException>(async () => await TestDownloadAsync(source, outStream, options, new CancellationToken(true)));
}

protected async Task<DownloadResult> Download(Uri source, Stream outStream, DownloadOptions? options, CancellationToken token = default)
protected async Task<DownloadResult> TestDownloadAsync(Uri source, Stream outStream, DownloadOptions? options, CancellationToken token = default)
{
var provider = CreateProvider();

Expand All @@ -65,7 +65,7 @@ protected async Task<DownloadResult> Download(Uri source, Stream outStream, Down

void Callback(DownloadUpdate status)
{
Task.Delay(100).Wait();
Task.Delay(100, TestContext.Current.CancellationToken).Wait(TestContext.Current.CancellationToken);
callBackFired = true;
}
}
Expand Down
Loading
Loading