summaryrefslogtreecommitdiff
path: root/src/SMAPI.Tests
diff options
context:
space:
mode:
Diffstat (limited to 'src/SMAPI.Tests')
-rw-r--r--src/SMAPI.Tests/Core/ModResolverTests.cs22
-rw-r--r--src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs285
2 files changed, 296 insertions, 11 deletions
diff --git a/src/SMAPI.Tests/Core/ModResolverTests.cs b/src/SMAPI.Tests/Core/ModResolverTests.cs
index 45b3673b..78056ef7 100644
--- a/src/SMAPI.Tests/Core/ModResolverTests.cs
+++ b/src/SMAPI.Tests/Core/ModResolverTests.cs
@@ -154,7 +154,7 @@ namespace SMAPI.Tests.Core
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
// assert
- mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
+ mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
}
[Test(Description = "Assert that validation fails when the minimum API version is higher than the current SMAPI version.")]
@@ -169,7 +169,7 @@ namespace SMAPI.Tests.Core
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
// assert
- mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
+ mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
}
[Test(Description = "Assert that validation fails when the manifest references a DLL that does not exist.")]
@@ -183,7 +183,7 @@ namespace SMAPI.Tests.Core
new ModResolver().ValidateManifests(new[] { mock.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
// assert
- mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
+ mock.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the metadata.");
}
[Test(Description = "Assert that validation fails when multiple mods have the same unique ID.")]
@@ -200,8 +200,8 @@ namespace SMAPI.Tests.Core
new ModResolver().ValidateManifests(new[] { modA.Object, modB.Object }, apiVersion: new SemanticVersion("1.0"), getUpdateUrl: key => null);
// assert
- modA.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the first mod with a unique ID.");
- modB.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "The validation did not fail the second mod with a unique ID.");
+ modA.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the first mod with a unique ID.");
+ modB.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "The validation did not fail the second mod with a unique ID.");
}
[Test(Description = "Assert that validation fails when the manifest references a DLL that does not exist.")]
@@ -367,9 +367,9 @@ namespace SMAPI.Tests.Core
Assert.AreEqual(5, mods.Length, 0, "Expected to get the same number of mods input.");
Assert.AreSame(modA.Object, mods[0], "The load order is incorrect: mod A should be first since it's needed by mod B.");
Assert.AreSame(modB.Object, mods[1], "The load order is incorrect: mod B should be second since it needs mod A.");
- modC.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "Mod C was expected to fail since it's part of a dependency loop.");
- modD.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "Mod D was expected to fail since it's part of a dependency loop.");
- modE.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "Mod E was expected to fail since it's part of a dependency loop.");
+ modC.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "Mod C was expected to fail since it's part of a dependency loop.");
+ modD.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "Mod D was expected to fail since it's part of a dependency loop.");
+ modE.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "Mod E was expected to fail since it's part of a dependency loop.");
}
[Test(Description = "Assert that dependencies are sorted correctly even if some of the mods failed during metadata loading.")]
@@ -408,7 +408,7 @@ namespace SMAPI.Tests.Core
// assert
Assert.AreEqual(2, mods.Length, 0, "Expected to get the same number of mods input.");
- modB.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<string>()), Times.Once, "Mod B unexpectedly didn't fail even though it needs a newer version of Mod A.");
+ modB.Verify(p => p.SetStatus(ModMetadataStatus.Failed, It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()), Times.Once, "Mod B unexpectedly didn't fail even though it needs a newer version of Mod A.");
}
[Test(Description = "Assert that dependencies are accepted if they meet the minimum version.")]
@@ -525,8 +525,8 @@ namespace SMAPI.Tests.Core
if (allowStatusChange)
{
mod
- .Setup(p => p.SetStatus(It.IsAny<ModMetadataStatus>(), It.IsAny<string>()))
- .Callback<ModMetadataStatus, string>((status, message) => Console.WriteLine($"<{manifest.UniqueID} changed status: [{status}] {message}"))
+ .Setup(p => p.SetStatus(It.IsAny<ModMetadataStatus>(), It.IsAny<ModFailReason>(), It.IsAny<string>(), It.IsAny<string>()))
+ .Callback<ModMetadataStatus, ModFailReason, string, string>((status, failReason, message, errorDetails) => Console.WriteLine($"<{manifest.UniqueID} changed status: [{status}] {message}\n{failReason}\n{errorDetails}"))
.Returns(mod.Object);
}
return mod;
diff --git a/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs b/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs
new file mode 100644
index 00000000..ea69a9ea
--- /dev/null
+++ b/src/SMAPI.Tests/Utilities/PathUtilitiesTests.cs
@@ -0,0 +1,285 @@
+using NUnit.Framework;
+using StardewModdingAPI.Toolkit.Utilities;
+
+namespace SMAPI.Tests.Utilities
+{
+ /// <summary>Unit tests for <see cref="PathUtilities"/>.</summary>
+ [TestFixture]
+ internal class PathUtilitiesTests
+ {
+ /*********
+ ** Sample data
+ *********/
+ /// <summary>Sample paths used in unit tests.</summary>
+ public static readonly SamplePath[] SamplePaths = {
+ // Windows absolute path
+ new SamplePath
+ {
+ OriginalPath = @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley",
+
+ Segments = new[] { "C:", "Program Files (x86)", "Steam", "steamapps", "common", "Stardew Valley" },
+ SegmentsLimit3 = new [] { "C:", "Program Files (x86)", @"Steam\steamapps\common\Stardew Valley" },
+
+ NormalizedOnWindows = @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley",
+ NormalizedOnUnix = @"C:/Program Files (x86)/Steam/steamapps/common/Stardew Valley"
+ },
+
+ // Windows absolute path (with trailing slash)
+ new SamplePath
+ {
+ OriginalPath = @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\",
+
+ Segments = new[] { "C:", "Program Files (x86)", "Steam", "steamapps", "common", "Stardew Valley" },
+ SegmentsLimit3 = new [] { "C:", "Program Files (x86)", @"Steam\steamapps\common\Stardew Valley\" },
+
+ NormalizedOnWindows = @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\",
+ NormalizedOnUnix = @"C:/Program Files (x86)/Steam/steamapps/common/Stardew Valley/"
+ },
+
+ // Windows relative path
+ new SamplePath
+ {
+ OriginalPath = @"Content\Characters\Dialogue\Abigail",
+
+ Segments = new [] { "Content", "Characters", "Dialogue", "Abigail" },
+ SegmentsLimit3 = new [] { "Content", "Characters", @"Dialogue\Abigail" },
+
+ NormalizedOnWindows = @"Content\Characters\Dialogue\Abigail",
+ NormalizedOnUnix = @"Content/Characters/Dialogue/Abigail"
+ },
+
+ // Windows relative path (with directory climbing)
+ new SamplePath
+ {
+ OriginalPath = @"..\..\Content",
+
+ Segments = new [] { "..", "..", "Content" },
+ SegmentsLimit3 = new [] { "..", "..", "Content" },
+
+ NormalizedOnWindows = @"..\..\Content",
+ NormalizedOnUnix = @"../../Content"
+ },
+
+ // Windows UNC path
+ new SamplePath
+ {
+ OriginalPath = @"\\unc\path",
+
+ Segments = new [] { "unc", "path" },
+ SegmentsLimit3 = new [] { "unc", "path" },
+
+ NormalizedOnWindows = @"\\unc\path",
+ NormalizedOnUnix = "/unc/path" // there's no good way to normalize this on Unix since UNC paths aren't supported; path normalization is meant for asset names anyway, so this test only ensures it returns some sort of sane value
+ },
+
+ // Linux absolute path
+ new SamplePath
+ {
+ OriginalPath = @"/home/.steam/steam/steamapps/common/Stardew Valley",
+
+ Segments = new [] { "home", ".steam", "steam", "steamapps", "common", "Stardew Valley" },
+ SegmentsLimit3 = new [] { "home", ".steam", "steam/steamapps/common/Stardew Valley" },
+
+ NormalizedOnWindows = @"\home\.steam\steam\steamapps\common\Stardew Valley",
+ NormalizedOnUnix = @"/home/.steam/steam/steamapps/common/Stardew Valley"
+ },
+
+ // Linux absolute path (with trailing slash)
+ new SamplePath
+ {
+ OriginalPath = @"/home/.steam/steam/steamapps/common/Stardew Valley/",
+
+ Segments = new [] { "home", ".steam", "steam", "steamapps", "common", "Stardew Valley" },
+ SegmentsLimit3 = new [] { "home", ".steam", "steam/steamapps/common/Stardew Valley/" },
+
+ NormalizedOnWindows = @"\home\.steam\steam\steamapps\common\Stardew Valley\",
+ NormalizedOnUnix = @"/home/.steam/steam/steamapps/common/Stardew Valley/"
+ },
+
+ // Linux absolute path (with ~)
+ new SamplePath
+ {
+ OriginalPath = @"~/.steam/steam/steamapps/common/Stardew Valley",
+
+ Segments = new [] { "~", ".steam", "steam", "steamapps", "common", "Stardew Valley" },
+ SegmentsLimit3 = new [] { "~", ".steam", "steam/steamapps/common/Stardew Valley" },
+
+ NormalizedOnWindows = @"~\.steam\steam\steamapps\common\Stardew Valley",
+ NormalizedOnUnix = @"~/.steam/steam/steamapps/common/Stardew Valley"
+ },
+
+ // Linux relative path
+ new SamplePath
+ {
+ OriginalPath = @"Content/Characters/Dialogue/Abigail",
+
+ Segments = new [] { "Content", "Characters", "Dialogue", "Abigail" },
+ SegmentsLimit3 = new [] { "Content", "Characters", "Dialogue/Abigail" },
+
+ NormalizedOnWindows = @"Content\Characters\Dialogue\Abigail",
+ NormalizedOnUnix = @"Content/Characters/Dialogue/Abigail"
+ },
+
+ // Linux relative path (with directory climbing)
+ new SamplePath
+ {
+ OriginalPath = @"../../Content",
+
+ Segments = new [] { "..", "..", "Content" },
+ SegmentsLimit3 = new [] { "..", "..", "Content" },
+
+ NormalizedOnWindows = @"..\..\Content",
+ NormalizedOnUnix = @"../../Content"
+ },
+
+ // Mixed directory separators
+ new SamplePath
+ {
+ OriginalPath = @"C:\some/mixed\path/separators",
+
+ Segments = new [] { "C:", "some", "mixed", "path", "separators" },
+ SegmentsLimit3 = new [] { "C:", "some", @"mixed\path/separators" },
+
+ NormalizedOnWindows = @"C:\some\mixed\path\separators",
+ NormalizedOnUnix = @"C:/some/mixed/path/separators"
+ },
+ };
+
+
+ /*********
+ ** Unit tests
+ *********/
+ /****
+ ** GetSegments
+ ****/
+ [Test(Description = "Assert that PathUtilities.GetSegments splits paths correctly.")]
+ [TestCaseSource(nameof(PathUtilitiesTests.SamplePaths))]
+ public void GetSegments(SamplePath path)
+ {
+ // act
+ string[] segments = PathUtilities.GetSegments(path.OriginalPath);
+
+ // assert
+ Assert.AreEqual(path.Segments, segments);
+ }
+
+ [Test(Description = "Assert that PathUtilities.GetSegments splits paths correctly when given a limit.")]
+ [TestCaseSource(nameof(PathUtilitiesTests.SamplePaths))]
+ public void GetSegments_WithLimit(SamplePath path)
+ {
+ // act
+ string[] segments = PathUtilities.GetSegments(path.OriginalPath, 3);
+
+ // assert
+ Assert.AreEqual(path.SegmentsLimit3, segments);
+ }
+
+ /****
+ ** NormalizePathSeparators
+ ****/
+ [Test(Description = "Assert that PathUtilities.NormalizePathSeparators normalizes paths correctly.")]
+ [TestCaseSource(nameof(PathUtilitiesTests.SamplePaths))]
+ public void NormalizePathSeparators(SamplePath path)
+ {
+ // act
+ string normalized = PathUtilities.NormalizePathSeparators(path.OriginalPath);
+
+ // assert
+#if SMAPI_FOR_WINDOWS
+ Assert.AreEqual(path.NormalizedOnWindows, normalized);
+#else
+ Assert.AreEqual(path.NormalizedOnUnix, normalized);
+#endif
+ }
+
+ /****
+ ** GetRelativePath
+ ****/
+ [Test(Description = "Assert that PathUtilities.GetRelativePath returns the expected values.")]
+#if SMAPI_FOR_WINDOWS
+ [TestCase(
+ @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley",
+ @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\Mods\Automate",
+ ExpectedResult = @"Mods\Automate"
+ )]
+ [TestCase(
+ @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\Mods\Automate",
+ @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\Content",
+ ExpectedResult = @"..\..\Content"
+ )]
+ [TestCase(
+ @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\Mods\Automate",
+ @"D:\another-drive",
+ ExpectedResult = @"D:\another-drive"
+ )]
+ [TestCase(
+ @"\\parent\unc",
+ @"\\parent\unc\path\to\child",
+ ExpectedResult = @"path\to\child"
+ )]
+ [TestCase(
+ @"\\parent\unc",
+ @"\\adjacent\unc",
+ ExpectedResult = @"\\adjacent\unc"
+ )]
+ [TestCase(
+ @"C:\same\path",
+ @"C:\same\path",
+ ExpectedResult = @"."
+ )]
+ [TestCase(
+ @"C:\parent",
+ @"C:\PARENT\child",
+ ExpectedResult = @"child"
+ )]
+#else
+ [TestCase(
+ @"~/.steam/steam/steamapps/common/Stardew Valley",
+ @"~/.steam/steam/steamapps/common/Stardew Valley/Mods/Automate",
+ ExpectedResult = @"Mods/Automate"
+ )]
+ [TestCase(
+ @"~/.steam/steam/steamapps/common/Stardew Valley/Mods/Automate",
+ @"~/.steam/steam/steamapps/common/Stardew Valley/Content",
+ ExpectedResult = @"../../Content"
+ )]
+ [TestCase(
+ @"~/.steam/steam/steamapps/common/Stardew Valley/Mods/Automate",
+ @"/mnt/another-drive",
+ ExpectedResult = @"/mnt/another-drive"
+ )]
+ [TestCase(
+ @"~/same/path",
+ @"~/same/path",
+ ExpectedResult = @"."
+ )]
+ [TestCase(
+ @"~/parent",
+ @"~/PARENT/child",
+ ExpectedResult = @"child" // note: incorrect on Linux and sometimes MacOS, but not worth the complexity of detecting whether the filesystem is case-sensitive for SMAPI's purposes
+ )]
+#endif
+ public string GetRelativePath(string sourceDir, string targetPath)
+ {
+ return PathUtilities.GetRelativePath(sourceDir, targetPath);
+ }
+
+
+ /*********
+ ** Private classes
+ *********/
+ public class SamplePath
+ {
+ public string OriginalPath { get; set; }
+ public string[] Segments { get; set; }
+ public string[] SegmentsLimit3 { get; set; }
+ public string NormalizedOnWindows { get; set; }
+ public string NormalizedOnUnix { get; set; }
+
+ public override string ToString()
+ {
+ return this.OriginalPath;
+ }
+ }
+ }
+}