From 8c2059e759ab8b547a0d7c7686e0e571fc915632 Mon Sep 17 00:00:00 2001 From: Paul Hebble Date: Wed, 6 Nov 2024 10:46:16 -0600 Subject: [PATCH] Exclude DLLs being upgraded to full modules --- Core/Relationships/RelationshipResolver.cs | 12 +++- Core/Relationships/ResolvedRelationship.cs | 3 +- .../ResolvedRelationshipsTree.cs | 21 ++++--- Netkan/Extensions/JObjectExtensions.cs | 8 +++ .../RelationshipResolverTests.cs | 56 +++++++++++++++++-- 5 files changed, 84 insertions(+), 16 deletions(-) diff --git a/Core/Relationships/RelationshipResolver.cs b/Core/Relationships/RelationshipResolver.cs index 019ce5ef2..20adfc2ff 100644 --- a/Core/Relationships/RelationshipResolver.cs +++ b/Core/Relationships/RelationshipResolver.cs @@ -50,7 +50,11 @@ public RelationshipResolver(IEnumerable modulesToInstall, } var toInst = modulesToInstall.ToArray(); - resolved = new ResolvedRelationshipsTree(toInst, registry, + // DLLs that we are upgrading to full modules should be excluded + dlls = registry.InstalledDlls + .Except(modulesToInstall.Select(m => m.identifier)) + .ToHashSet(); + resolved = new ResolvedRelationshipsTree(toInst, registry, dlls, installed_modules, versionCrit, options.OptionalHandling()); if (!options.proceed_with_inconsistencies) @@ -127,7 +131,7 @@ private void AddModulesToInstall(CkanModule[] modules) { // Check that our solution is actually sane SanityChecker.EnforceConsistency(modlist.Values.Concat(installed_modules), - registry.InstalledDlls, + dlls, registry.InstalledDlc); } catch (BadRelationshipsKraken k) when (options.without_enforce_consistency) @@ -276,7 +280,7 @@ private void ResolveStanza(List? stanza, // If it's already installed, skip. if (descriptor.MatchesAny(installed_modules, - registry.InstalledDlls, + dlls, registry.InstalledDlc, out CkanModule? installedCandidate)) { @@ -649,6 +653,8 @@ private void AddReason(CkanModule module, SelectionReason reason) private readonly Dictionary> reasons = new Dictionary>(); + private readonly HashSet dlls; + /// /// Depends relationships with suppress_recommendations=true, /// to be applied to all recommendations and suggestions diff --git a/Core/Relationships/ResolvedRelationship.cs b/Core/Relationships/ResolvedRelationship.cs index 250fe4fba..9a58c43b6 100644 --- a/Core/Relationships/ResolvedRelationship.cs +++ b/Core/Relationships/ResolvedRelationship.cs @@ -142,6 +142,7 @@ public ResolvedByNew(CkanModule source, ICollection definitelyInstalling, ICollection allInstalling, IRegistryQuerier registry, + ICollection dlls, ICollection installed, GameVersionCriteria crit, OptionalRelationships optRels, @@ -149,7 +150,7 @@ public ResolvedByNew(CkanModule source, : this(source, relationship, reason, providers.ToDictionary(prov => prov, prov => ResolvedRelationshipsTree.ResolveModule( - prov, definitelyInstalling, allInstalling, registry, installed, crit, + prov, definitelyInstalling, allInstalling, registry, dlls, installed, crit, relationship.suppress_recommendations ? optRels & ~OptionalRelationships.Recommendations & ~OptionalRelationships.Suggestions diff --git a/Core/Relationships/ResolvedRelationshipsTree.cs b/Core/Relationships/ResolvedRelationshipsTree.cs index 5bf1e228e..1db97f94f 100644 --- a/Core/Relationships/ResolvedRelationshipsTree.cs +++ b/Core/Relationships/ResolvedRelationshipsTree.cs @@ -23,31 +23,33 @@ public class ResolvedRelationshipsTree { public ResolvedRelationshipsTree(ICollection modules, IRegistryQuerier registry, + ICollection dlls, ICollection installed, GameVersionCriteria crit, OptionalRelationships optRels) { - resolved = ResolveManyCached(modules, registry, installed, crit, optRels, relationshipCache).ToArray(); + resolved = ResolveManyCached(modules, registry, dlls, installed, crit, optRels, relationshipCache).ToArray(); } public static IEnumerable ResolveModule(CkanModule module, ICollection definitelyInstalling, ICollection allInstalling, IRegistryQuerier registry, + ICollection dlls, ICollection installed, GameVersionCriteria crit, OptionalRelationships optRels, RelationshipCache relationshipCache) => ResolveRelationships(module, module.depends, new SelectionReason.Depends(module), - definitelyInstalling, allInstalling, registry, installed, crit, optRels, relationshipCache) + definitelyInstalling, allInstalling, registry, dlls, installed, crit, optRels, relationshipCache) .Concat((optRels & OptionalRelationships.Recommendations) == 0 ? Enumerable.Empty() : ResolveRelationships(module, module.recommends, new SelectionReason.Recommended(module, 0), - definitelyInstalling, allInstalling, registry, installed, crit, optRels, relationshipCache)) + definitelyInstalling, allInstalling, registry, dlls, installed, crit, optRels, relationshipCache)) .Concat((optRels & OptionalRelationships.Suggestions) == 0 ? Enumerable.Empty() : ResolveRelationships(module, module.suggests, new SelectionReason.Suggested(module), - definitelyInstalling, allInstalling, registry, installed, crit, optRels, relationshipCache)); + definitelyInstalling, allInstalling, registry, dlls, installed, crit, optRels, relationshipCache)); public IEnumerable Unsatisfied() => resolved.SelectMany(UnsatisfiedFrom); @@ -102,11 +104,12 @@ public override string ToString() private static IEnumerable ResolveManyCached(ICollection modules, IRegistryQuerier registry, + ICollection dlls, ICollection installed, GameVersionCriteria crit, OptionalRelationships optRels, RelationshipCache relationshipCache) - => modules.SelectMany(m => ResolveModule(m, modules, modules, registry, installed, crit, optRels, + => modules.SelectMany(m => ResolveModule(m, modules, modules, registry, dlls, installed, crit, optRels, relationshipCache)); private static IEnumerable ResolveRelationships(CkanModule module, @@ -115,12 +118,13 @@ private static IEnumerable ResolveRelationships(CkanModule ICollection definitelyInstalling, ICollection allInstalling, IRegistryQuerier registry, + ICollection dlls, ICollection installed, GameVersionCriteria crit, OptionalRelationships optRels, RelationshipCache relationshipCache) => relationships?.Select(dep => Resolve(module, dep, reason, - definitelyInstalling, allInstalling, registry, installed, + definitelyInstalling, allInstalling, registry, dlls, installed, crit, optRels, relationshipCache)) ?? Enumerable.Empty(); @@ -130,6 +134,7 @@ private static ResolvedRelationship Resolve(CkanModule source, ICollection definitelyInstalling, ICollection allInstalling, IRegistryQuerier registry, + ICollection dlls, ICollection installed, GameVersionCriteria crit, OptionalRelationships optRels, @@ -137,7 +142,7 @@ private static ResolvedRelationship Resolve(CkanModule source, => relationshipCache.TryGetValue(relationship, out ResolvedRelationship? cachedRel) ? cachedRel.WithSource(source, reason) - : relationship.MatchesAny(installed, registry.InstalledDlls, registry.InstalledDlc, + : relationship.MatchesAny(installed, dlls, registry.InstalledDlc, out CkanModule? installedMatch) ? relationshipCache.GetOrAdd( relationship, @@ -156,7 +161,7 @@ private static ResolvedRelationship Resolve(CkanModule source, installed, definitelyInstalling), definitelyInstalling, allInstalling.Append(source).ToArray(), - registry, installed, crit, optRels, + registry, dlls, installed, crit, optRels, relationshipCache)); private readonly ResolvedRelationship[] resolved; diff --git a/Netkan/Extensions/JObjectExtensions.cs b/Netkan/Extensions/JObjectExtensions.cs index 5f687c33e..c63836c33 100644 --- a/Netkan/Extensions/JObjectExtensions.cs +++ b/Netkan/Extensions/JObjectExtensions.cs @@ -81,6 +81,14 @@ public static void SafeMerge(this JObject jobject, string propertyName, JToken? } } + public static void SafeMerge(this JObject jobject, JObject other) + { + foreach (var property in other.Properties()) + { + jobject.SafeAdd(property.Name, property.Value); + } + } + public static JToken? ToJValueOrJArray(this IEnumerable source) where T: notnull { var items = source.OfType() diff --git a/Tests/Core/Relationships/RelationshipResolverTests.cs b/Tests/Core/Relationships/RelationshipResolverTests.cs index c83478278..b645e2bc9 100644 --- a/Tests/Core/Relationships/RelationshipResolverTests.cs +++ b/Tests/Core/Relationships/RelationshipResolverTests.cs @@ -13,6 +13,7 @@ using CKAN.Games.KerbalSpaceProgram; using CKAN.Versioning; using RelationshipDescriptor = CKAN.RelationshipDescriptor; +using CKAN.NetKAN.Extensions; namespace Tests.Core.Relationships { @@ -1065,6 +1066,55 @@ public void AutodetectedCanSatisfyRelationships() } } + [Test, + TestCase(new string[] { + @"{ + ""identifier"": ""Deferred"", + ""conflicts"": [ + { + ""name"": ""RasterPropMonitor"", + ""max_version"": ""1:v0.31.13.4"" + } + ] + }", + @"{ + ""identifier"": ""RasterPropMonitor"", + ""version"": ""1:v1.0.2"" + }", + }, + new string[] { "Deferred", "RasterPropMonitor" })] + public void Constructor_UpgradeVersionSpecificConflictedAutoDetected_DoesNotThrow( + string[] availableModules, + string[] installIdents) + { + var user = new NullUser(); + var crit = new GameVersionCriteria(new GameVersion(1, 12, 5)); + using (var repo = new TemporaryRepository(availableModules.Select(MergeWithDefaults) + .ToArray())) + using (var repoData = new TemporaryRepositoryData(user, repo.repo)) + using (var inst = new DisposableKSP()) + { + var registry = new CKAN.Registry(repoData.Manager, repo.repo); + registry.SetDlls(new Dictionary() + { + { + "RasterPropMonitor", + inst.KSP.ToRelativeGameDir(Path.Combine(inst.KSP.game.PrimaryModDirectory(inst.KSP), + "RasterPropMonitor.dll")) + } + }); + Assert.DoesNotThrow(() => + { + var rr = new RelationshipResolver( + installIdents.Select(ident => registry.LatestAvailable(ident, crit)) + .OfType(), + null, + RelationshipResolverOptions.DependsOnlyOpts(), + registry, inst.KSP.game, crit); + }); + } + } + // Models the EVE - EVE-Config - AVP - AVP-Textures relationship [Test] public void UninstallingConflictingModule_InstallingRecursiveDependencies_ResolvesSuccessfully() @@ -1510,9 +1560,7 @@ public void Constructor_WithPlayModes_DoesNotThrow(string[] availableModules, .OfType(), null, RelationshipResolverOptions.DependsOnlyOpts(), - registry, - game, - crit); + registry, game, crit); var idents = rr.ModList().Select(m => m.identifier).ToArray(); foreach (var goodSubstring in goodSubstrings) { @@ -1534,7 +1582,7 @@ public void Constructor_WithPlayModes_DoesNotThrow(string[] availableModules, public static string MergeWithDefaults(string json) { var incoming = JObject.Parse(json); - incoming.Merge(moduleDefaults); + incoming.SafeMerge(moduleDefaults); return incoming.ToString(); }