From 53547a8ca3a5cba45bd0a5a478d0f40daa282888 Mon Sep 17 00:00:00 2001 From: Jesse Plamondon-Willard Date: Sat, 13 May 2017 21:36:50 -0400 Subject: pass API version into mod metadata validation to simplify unit testing (#285) --- .../Framework/ModLoading/IModMetadata.cs | 39 ++++++++++++++++++++++ .../Framework/ModLoading/ModMetadata.cs | 14 ++------ .../Framework/ModLoading/ModMetadataStatus.cs | 12 +++++++ .../Framework/ModLoading/ModResolver.cs | 22 ++++++------ 4 files changed, 64 insertions(+), 23 deletions(-) create mode 100644 src/StardewModdingAPI/Framework/ModLoading/IModMetadata.cs create mode 100644 src/StardewModdingAPI/Framework/ModLoading/ModMetadataStatus.cs (limited to 'src/StardewModdingAPI/Framework') diff --git a/src/StardewModdingAPI/Framework/ModLoading/IModMetadata.cs b/src/StardewModdingAPI/Framework/ModLoading/IModMetadata.cs new file mode 100644 index 00000000..3771ffdd --- /dev/null +++ b/src/StardewModdingAPI/Framework/ModLoading/IModMetadata.cs @@ -0,0 +1,39 @@ +using StardewModdingAPI.Framework.Models; + +namespace StardewModdingAPI.Framework.ModLoading +{ + /// Metadata for a mod. + internal interface IModMetadata + { + /********* + ** Accessors + *********/ + /// The mod's display name. + string DisplayName { get; } + + /// The mod's full directory path. + string DirectoryPath { get; } + + /// The mod manifest. + IManifest Manifest { get; } + + /// Optional metadata about a mod version that SMAPI should assume is compatible or broken, regardless of whether it detects incompatible code. + ModCompatibility Compatibility { get; } + + /// The metadata resolution status. + ModMetadataStatus Status { get; } + + /// The reason the metadata is invalid, if any. + string Error { get; } + + + /********* + ** Public methods + *********/ + /// Set the mod status. + /// The metadata resolution status. + /// The reason the metadata is invalid, if any. + /// Return the instance for chaining. + IModMetadata SetStatus(ModMetadataStatus status, string error = null); + } +} diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModMetadata.cs b/src/StardewModdingAPI/Framework/ModLoading/ModMetadata.cs index 5ec2d4e0..7b25e090 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/ModMetadata.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/ModMetadata.cs @@ -3,7 +3,7 @@ namespace StardewModdingAPI.Framework.ModLoading { /// Metadata for a mod. - internal class ModMetadata + internal class ModMetadata : IModMetadata { /********* ** Accessors @@ -47,21 +47,11 @@ namespace StardewModdingAPI.Framework.ModLoading /// The metadata resolution status. /// The reason the metadata is invalid, if any. /// Return the instance for chaining. - public ModMetadata SetStatus(ModMetadataStatus status, string error = null) + public IModMetadata SetStatus(ModMetadataStatus status, string error = null) { this.Status = status; this.Error = error; return this; } } - - /// Indicates the status of a mod's metadata resolution. - internal enum ModMetadataStatus - { - /// The mod has been found, but hasn't been processed yet. - Found, - - /// The mod cannot be loaded. - Failed - } } diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModMetadataStatus.cs b/src/StardewModdingAPI/Framework/ModLoading/ModMetadataStatus.cs new file mode 100644 index 00000000..1b2b0b55 --- /dev/null +++ b/src/StardewModdingAPI/Framework/ModLoading/ModMetadataStatus.cs @@ -0,0 +1,12 @@ +namespace StardewModdingAPI.Framework.ModLoading +{ + /// Indicates the status of a mod's metadata resolution. + internal enum ModMetadataStatus + { + /// The mod has been found, but hasn't been processed yet. + Found, + + /// The mod cannot be loaded. + Failed + } +} \ No newline at end of file diff --git a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs index 9b26e8b0..a3d4ce3e 100644 --- a/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs +++ b/src/StardewModdingAPI/Framework/ModLoading/ModResolver.cs @@ -18,7 +18,7 @@ namespace StardewModdingAPI.Framework.ModLoading /// The JSON helper with which to read manifests. /// Metadata about mods that SMAPI should assume is compatible or broken, regardless of whether it detects incompatible code. /// Returns the manifests by relative folder. - public IEnumerable ReadManifests(string rootPath, JsonHelper jsonHelper, IEnumerable compatibilityRecords) + public IEnumerable ReadManifests(string rootPath, JsonHelper jsonHelper, IEnumerable compatibilityRecords) { compatibilityRecords = compatibilityRecords.ToArray(); foreach (DirectoryInfo modDir in this.GetModFolders(rootPath)) @@ -75,9 +75,9 @@ namespace StardewModdingAPI.Framework.ModLoading /// Validate manifest metadata. /// The mod manifests to validate. - public void ValidateManifests(IEnumerable mods) + public void ValidateManifests(IEnumerable mods) { - foreach (ModMetadata mod in mods) + foreach (IModMetadata mod in mods) { // skip if already failed if (mod.Status == ModMetadataStatus.Failed) @@ -127,12 +127,12 @@ namespace StardewModdingAPI.Framework.ModLoading /// Sort the given mods by the order they should be loaded. /// The mods to process. - public IEnumerable ProcessDependencies(IEnumerable mods) + public IEnumerable ProcessDependencies(IEnumerable mods) { var unsortedMods = mods.ToList(); - var sortedMods = new Stack(); + var sortedMods = new Stack(); var visitedMods = new bool[unsortedMods.Count]; - var currentChain = new List(); + var currentChain = new List(); bool success = true; for (int index = 0; index < unsortedMods.Count; index++) @@ -162,7 +162,7 @@ namespace StardewModdingAPI.Framework.ModLoading /// The current change of mod dependencies. /// The mods remaining to sort. /// Returns whether the mod can be loaded. - private bool ProcessDependencies(int modIndex, bool[] visitedMods, Stack sortedMods, List currentChain, List unsortedMods) + private bool ProcessDependencies(int modIndex, bool[] visitedMods, Stack sortedMods, List currentChain, List unsortedMods) { // visit mod if (visitedMods[modIndex]) @@ -170,7 +170,7 @@ namespace StardewModdingAPI.Framework.ModLoading visitedMods[modIndex] = true; // mod already failed - ModMetadata mod = unsortedMods[modIndex]; + IModMetadata mod = unsortedMods[modIndex]; if (mod.Status == ModMetadataStatus.Failed) return false; @@ -194,7 +194,7 @@ namespace StardewModdingAPI.Framework.ModLoading } // get mods which should be loaded before this one - ModMetadata[] modsToLoadFirst = + IModMetadata[] modsToLoadFirst = ( from unsorted in unsortedMods where mod.Manifest.Dependencies.Any(required => required.UniqueID == unsorted.Manifest.UniqueID) @@ -203,7 +203,7 @@ namespace StardewModdingAPI.Framework.ModLoading .ToArray(); // detect circular references - ModMetadata circularReferenceMod = currentChain.FirstOrDefault(modsToLoadFirst.Contains); + IModMetadata circularReferenceMod = currentChain.FirstOrDefault(modsToLoadFirst.Contains); if (circularReferenceMod != null) { mod.SetStatus(ModMetadataStatus.Failed, $"its dependencies have a circular reference: {string.Join(" => ", currentChain.Select(p => p.DisplayName))} => {circularReferenceMod.DisplayName})."); @@ -212,7 +212,7 @@ namespace StardewModdingAPI.Framework.ModLoading currentChain.Add(mod); // recursively sort dependencies - foreach (ModMetadata requiredMod in modsToLoadFirst) + foreach (IModMetadata requiredMod in modsToLoadFirst) { int index = unsortedMods.IndexOf(requiredMod); success = this.ProcessDependencies(index, visitedMods, sortedMods, currentChain, unsortedMods); -- cgit