Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
25 changes: 21 additions & 4 deletions source/Reloaded.Mod.Launcher.Lib/Update.cs
Original file line number Diff line number Diff line change
Expand Up @@ -264,15 +264,32 @@ public static async Task<ModDependencyResolveResult> GetMissingDependenciesToDow
// Get Dependencies
var resolver = DependencyResolverFactory.GetInstance(IoC.Get<AggregateNugetRepository>());

var results = new List<Task<ModDependencyResolveResult>>();
var taskToDependencyMap = new Dictionary<Task<ModDependencyResolveResult>, string>();
foreach (var dependencyItem in missingDeps.Items)
foreach (var dependency in dependencyItem.Dependencies)
results.Add(resolver.ResolveAsync(dependency, dependencyItem.Mod.PluginData, token));
{
var task = resolver.ResolveAsync(dependency, dependencyItem.Mod.PluginData, token);
taskToDependencyMap[task] = dependency;
}

await Task.WhenAll(results);
// Handle each result individually to avoid stopping on failures
var resolveResults = new List<ModDependencyResolveResult>();
foreach (var kvp in taskToDependencyMap)
{
try
{
var taskResult = await kvp.Key;
resolveResults.Add(taskResult);
}
catch (Exception ex)
{
// Create error result for unexpected exceptions that weren't caught by resolvers
resolveResults.Add(ModDependencyResolveResult.FromError(kvp.Value, ex, "UnknownResolver"));
}
}

// Merge Results
var result = ModDependencyResolveResult.Combine(results.Select(x => x.Result));;
var result = ModDependencyResolveResult.Combine(resolveResults);
if (result.NotFoundDependencies.Count <= 0)
return result;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
<OutputPath>..\Output\Tests\</OutputPath>
<Nullable>disable</Nullable>
<NoWarn>$(NoWarn);NU1605;NU1701;MSB3270</NoWarn>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,33 @@ public async Task ResolveAsync_WithNoPackage_ReturnsMissing()
Assert.NotEmpty(result.NotFoundDependencies);
Assert.Empty(result.FoundDependencies);
}

[Fact]
public async Task ResolveAsync_WithInvalidConfig_ReturnsError()
{
// Arrange
var config = new GameBananaUpdateResolverFactory.GameBananaConfig()
{
ItemId = -1, // Invalid item ID that should cause InitializeAsync to fail
ItemType = "Mod"
};
var clonedDependency = _testEnvironmoent.TestModConfigBTuple.DeepClone();
Singleton<GameBananaUpdateResolverFactory>.Instance.SetConfiguration(clonedDependency, config);
var clonedOriginal = _testEnvironmoent.TestModConfigATuple.DeepClone();

var gameBanana = new GameBananaDependencyMetadataWriter();
gameBanana.Update(clonedOriginal.Config, [clonedDependency.Config]);

// Act
var resolver = new GameBananaDependencyResolver();
var result = await resolver.ResolveAsync(clonedDependency.Config.ModId, clonedOriginal.Config.PluginData);

// Assert
Assert.Single(result.Errors);
Assert.Equal(clonedDependency.Config.ModId, result.Errors[0].PackageId);
Assert.Equal(nameof(GameBananaDependencyResolver), result.Errors[0].Resolver);
Assert.NotNull(result.Errors[0].Exception);
Assert.Contains(clonedDependency.Config.ModId, result.NotFoundDependencies);
Assert.Empty(result.FoundDependencies);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,11 @@
/// </summary>
public HashSet<string> NotFoundDependencies { get; } = new HashSet<string>();

/// <summary>
/// List of errors that occurred during dependency resolution.
/// </summary>
public List<DependencyResolveError> Errors { get; } = new List<DependencyResolveError>();

/// <summary>
/// Combines the results of multiple resolve operations.
/// </summary>
Expand All @@ -48,7 +53,7 @@

if (idToNewestVersion.TryGetValue(found.Id, out var existing))
{
if (existing.Version < found.Version)

Check warning on line 56 in source/Reloaded.Mod.Loader.Update/Interfaces/IDependencyResolver.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'version2' in 'bool SemanticVersion.operator <(SemanticVersion version1, SemanticVersion version2)'.

Check warning on line 56 in source/Reloaded.Mod.Loader.Update/Interfaces/IDependencyResolver.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'version1' in 'bool SemanticVersion.operator <(SemanticVersion version1, SemanticVersion version2)'.

Check warning on line 56 in source/Reloaded.Mod.Loader.Update/Interfaces/IDependencyResolver.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'version2' in 'bool SemanticVersion.operator <(SemanticVersion version1, SemanticVersion version2)'.

Check warning on line 56 in source/Reloaded.Mod.Loader.Update/Interfaces/IDependencyResolver.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'version1' in 'bool SemanticVersion.operator <(SemanticVersion version1, SemanticVersion version2)'.

Check warning on line 56 in source/Reloaded.Mod.Loader.Update/Interfaces/IDependencyResolver.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'version2' in 'bool SemanticVersion.operator <(SemanticVersion version1, SemanticVersion version2)'.

Check warning on line 56 in source/Reloaded.Mod.Loader.Update/Interfaces/IDependencyResolver.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'version1' in 'bool SemanticVersion.operator <(SemanticVersion version1, SemanticVersion version2)'.

Check warning on line 56 in source/Reloaded.Mod.Loader.Update/Interfaces/IDependencyResolver.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'version2' in 'bool SemanticVersion.operator <(SemanticVersion version1, SemanticVersion version2)'.

Check warning on line 56 in source/Reloaded.Mod.Loader.Update/Interfaces/IDependencyResolver.cs

View workflow job for this annotation

GitHub Actions / build

Possible null reference argument for parameter 'version1' in 'bool SemanticVersion.operator <(SemanticVersion version1, SemanticVersion version2)'.
idToNewestVersion[found.Id] = found;

continue;
Expand All @@ -59,6 +64,9 @@

foreach (var notFound in result.NotFoundDependencies)
returnValue.NotFoundDependencies.Add(notFound);

foreach (var error in result.Errors)
returnValue.Errors.Add(error);
}

// Remove dependencies that were found from the notFound set.
Expand All @@ -68,4 +76,53 @@
returnValue.FoundDependencies.AddRange(idToNewestVersion.Values);
return returnValue;
}

/// <summary>
/// Creates a result with an error for a specific package.
/// </summary>
/// <param name="packageId">The package ID that failed to resolve.</param>
/// <param name="exception">The exception that occurred during resolution.</param>
/// <param name="resolver">The resolver that caused the error.</param>
/// <returns>A result containing the error information.</returns>
public static ModDependencyResolveResult FromError(string packageId, Exception exception, string resolver)
{
var result = new ModDependencyResolveResult();
result.Errors.Add(new DependencyResolveError(packageId, exception, resolver));
result.NotFoundDependencies.Add(packageId);
return result;
}
}

/// <summary>
/// Represents an error that occurred during dependency resolution.
/// </summary>
public class DependencyResolveError
{
/// <summary>
/// The package ID that failed to resolve.
/// </summary>
public string PackageId { get; }

/// <summary>
/// The exception that occurred during resolution.
/// </summary>
public Exception Exception { get; }

/// <summary>
/// The name of the resolver that caused the error.
/// </summary>
public string Resolver { get; }

/// <summary>
/// Creates a new dependency resolve error.
/// </summary>
/// <param name="packageId">The package ID that failed to resolve.</param>
/// <param name="exception">The exception that occurred during resolution.</param>
/// <param name="resolver">The name of the resolver that caused the error.</param>
public DependencyResolveError(string packageId, Exception exception, string resolver)
{
PackageId = packageId;
Exception = exception;
Resolver = resolver;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,19 @@ public async Task<ModDependencyResolveResult> ResolveAsync(string packageId, Dic
ModType = gbConfig.Config.ItemType
}, new CommonPackageResolverSettings() { MetadataFileName = gbConfig.ReleaseMetadataName });

await resolver.InitializeAsync();

result.FoundDependencies.Add(new UpdateDownloadablePackage(resolver)
try
{
await resolver.InitializeAsync();

result.FoundDependencies.Add(new UpdateDownloadablePackage(resolver)
{
Id = packageId
});
}
catch (Exception ex)
{
Id = packageId
});
return ModDependencyResolveResult.FromError(packageId, ex, nameof(GameBananaDependencyResolver));
}

return result;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,19 @@ public async Task<ModDependencyResolveResult> ResolveAsync(string packageId, Dic
InheritVersionFromTag = gitConfig.Config.UseReleaseTag
}, new CommonPackageResolverSettings() { MetadataFileName = gitConfig.ReleaseMetadataName });

await ((IPackageResolver)resolver).InitializeAsync();
result.FoundDependencies.Add(new UpdateDownloadablePackage(resolver)
try
{
Id = packageId
});
await ((IPackageResolver)resolver).InitializeAsync();

result.FoundDependencies.Add(new UpdateDownloadablePackage(resolver)
{
Id = packageId
});
}
catch (Exception ex)
{
return ModDependencyResolveResult.FromError(packageId, ex, nameof(GitHubDependencyResolver));
}

return result;
}
Expand Down
Loading