enable nullable annotations in unit tests (#837)
This commit is contained in:
parent
c3851ae2e6
commit
5f7a92a745
|
@ -1,8 +1,7 @@
|
|||
#nullable disable
|
||||
|
||||
// <generated />
|
||||
using Microsoft.CodeAnalysis;
|
||||
// ReSharper disable All -- generated code
|
||||
using System;
|
||||
using Microsoft.CodeAnalysis;
|
||||
|
||||
namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
|
||||
{
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
#nullable disable
|
||||
|
||||
// <generated />
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
// ReSharper disable All -- generated code
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using System.Linq;
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.CSharp;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using Microsoft.CodeAnalysis.Text;
|
||||
|
||||
namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
|
||||
{
|
||||
|
@ -61,7 +61,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
|
|||
var diagnostics = new List<Diagnostic>();
|
||||
foreach (Project project in projects)
|
||||
{
|
||||
CompilationWithAnalyzers compilationWithAnalyzers = project.GetCompilationAsync().Result.WithAnalyzers(ImmutableArray.Create(analyzer));
|
||||
CompilationWithAnalyzers compilationWithAnalyzers = project.GetCompilationAsync().Result!.WithAnalyzers(ImmutableArray.Create(analyzer));
|
||||
var diags = compilationWithAnalyzers.GetAnalyzerDiagnosticsAsync().Result;
|
||||
foreach (Diagnostic diag in diags)
|
||||
{
|
||||
|
@ -74,7 +74,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
|
|||
for (int i = 0; i < documents.Length; i++)
|
||||
{
|
||||
Document document = documents[i];
|
||||
SyntaxTree tree = document.GetSyntaxTreeAsync().Result;
|
||||
SyntaxTree? tree = document.GetSyntaxTreeAsync().Result;
|
||||
if (tree == diag.Location.SourceTree)
|
||||
{
|
||||
diagnostics.Add(diag);
|
||||
|
@ -126,17 +126,6 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
|
|||
return documents;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a Document from a string through creating a project that contains it.
|
||||
/// </summary>
|
||||
/// <param name="source">Classes in the form of a string</param>
|
||||
/// <param name="language">The language the source code is in</param>
|
||||
/// <returns>A Document created from the source string</returns>
|
||||
protected static Document CreateDocument(string source, string language = LanguageNames.CSharp)
|
||||
{
|
||||
return CreateProject(new[] { source }, language).Documents.First();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a project using the inputted strings as sources.
|
||||
/// </summary>
|
||||
|
@ -167,7 +156,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
|
|||
solution = solution.AddDocument(documentId, newFileName, SourceText.From(source));
|
||||
count++;
|
||||
}
|
||||
return solution.GetProject(projectId);
|
||||
return solution.GetProject(projectId)!;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#nullable disable
|
||||
|
||||
// <generated />
|
||||
// ReSharper disable All -- generated code
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
@ -19,18 +19,7 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
|
|||
/// <summary>
|
||||
/// Get the CSharp analyzer being tested - to be implemented in non-abstract class
|
||||
/// </summary>
|
||||
protected virtual DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get the Visual Basic analyzer being tested (C#) - to be implemented in non-abstract class
|
||||
/// </summary>
|
||||
protected virtual DiagnosticAnalyzer GetBasicDiagnosticAnalyzer()
|
||||
{
|
||||
return null;
|
||||
}
|
||||
protected abstract DiagnosticAnalyzer GetCSharpDiagnosticAnalyzer();
|
||||
#endregion
|
||||
|
||||
#region Verifier wrappers
|
||||
|
@ -46,17 +35,6 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
|
|||
this.VerifyDiagnostics(new[] { source }, LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzer(), expected);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called to test a C# DiagnosticAnalyzer when applied on the inputted strings as a source
|
||||
/// Note: input a DiagnosticResult for each Diagnostic expected
|
||||
/// </summary>
|
||||
/// <param name="sources">An array of strings to create source documents from to run the analyzers on</param>
|
||||
/// <param name="expected">DiagnosticResults that should appear after the analyzer is run on the sources</param>
|
||||
protected void VerifyCSharpDiagnostic(string[] sources, params DiagnosticResult[] expected)
|
||||
{
|
||||
this.VerifyDiagnostics(sources, LanguageNames.CSharp, this.GetCSharpDiagnosticAnalyzer(), expected);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// General method that gets a collection of actual diagnostics found in the source after the analyzer is run,
|
||||
/// then verifies each of them.
|
||||
|
@ -222,11 +200,10 @@ namespace SMAPI.ModBuildConfig.Analyzer.Tests.Framework
|
|||
Assert.IsTrue(location.IsInSource,
|
||||
$"Test base does not currently handle diagnostics in metadata locations. Diagnostic in metadata: {diagnostics[i]}\r\n");
|
||||
|
||||
string resultMethodName = diagnostics[i].Location.SourceTree.FilePath.EndsWith(".cs") ? "GetCSharpResultAt" : "GetBasicResultAt";
|
||||
var linePosition = diagnostics[i].Location.GetLineSpan().StartLinePosition;
|
||||
|
||||
builder.AppendFormat("{0}({1}, {2}, {3}.{4})",
|
||||
resultMethodName,
|
||||
"GetCSharpResultAt",
|
||||
linePosition.Line + 1,
|
||||
linePosition.Character + 1,
|
||||
analyzerType.Name,
|
||||
|
|
|
@ -1,12 +1,8 @@
|
|||
#nullable disable
|
||||
|
||||
// ReSharper disable CheckNamespace -- matches Stardew Valley's code
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Netcode
|
||||
{
|
||||
/// <summary>A simplified version of Stardew Valley's <c>Netcode.NetCollection</c> for unit testing.</summary>
|
||||
public class NetCollection<T> : Collection<T>, IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable { }
|
||||
public class NetCollection<T> : Collection<T> { }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
// ReSharper disable CheckNamespace -- matches Stardew Valley's code
|
||||
namespace Netcode
|
||||
{
|
||||
|
@ -9,10 +7,13 @@ namespace Netcode
|
|||
public class NetFieldBase<T, TSelf> where TSelf : NetFieldBase<T, TSelf>
|
||||
{
|
||||
/// <summary>The synchronised value.</summary>
|
||||
public T Value { get; set; }
|
||||
public T? Value { get; set; }
|
||||
|
||||
/// <summary>Implicitly convert a net field to the its type.</summary>
|
||||
/// <param name="field">The field to convert.</param>
|
||||
public static implicit operator T(NetFieldBase<T, TSelf> field) => field.Value;
|
||||
public static implicit operator T?(NetFieldBase<T, TSelf> field)
|
||||
{
|
||||
return field.Value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
// ReSharper disable CheckNamespace -- matches Stardew Valley's code
|
||||
namespace Netcode
|
||||
{
|
||||
|
|
|
@ -1,11 +1,8 @@
|
|||
#nullable disable
|
||||
|
||||
// ReSharper disable CheckNamespace -- matches Stardew Valley's code
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Netcode
|
||||
{
|
||||
/// <summary>A simplified version of Stardew Valley's <c>Netcode.NetObjectList</c> for unit testing.</summary>
|
||||
public class NetList<T> : List<T>, IList<T>, ICollection<T>, IEnumerable<T>, IEnumerable { }
|
||||
public class NetList<T> : List<T> { }
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
// ReSharper disable CheckNamespace -- matches Stardew Valley's code
|
||||
namespace Netcode
|
||||
{
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
#nullable disable
|
||||
|
||||
// ReSharper disable CheckNamespace, InconsistentNaming -- matches Stardew Valley's code
|
||||
#pragma warning disable 649 // (never assigned) -- only used to test type conversions
|
||||
// ReSharper disable UnusedMember.Global -- used dynamically for unit tests
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace StardewValley
|
||||
|
@ -10,6 +8,6 @@ namespace StardewValley
|
|||
internal class Farmer
|
||||
{
|
||||
/// <summary>A sample field which should be replaced with a different property.</summary>
|
||||
public readonly IDictionary<string, int[]> friendships;
|
||||
public readonly IDictionary<string, int[]> friendships = new Dictionary<string, int[]>();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#nullable disable
|
||||
|
||||
// ReSharper disable CheckNamespace, InconsistentNaming -- matches Stardew Valley's code
|
||||
// ReSharper disable UnusedMember.Global -- used dynamically for unit tests
|
||||
using Netcode;
|
||||
|
||||
namespace StardewValley
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
// ReSharper disable CheckNamespace, InconsistentNaming -- matches Stardew Valley's code
|
||||
using Netcode;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using Microsoft.CodeAnalysis;
|
||||
using Microsoft.CodeAnalysis.Diagnostics;
|
||||
using NUnit.Framework;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using SMAPI.Tests.ModApiConsumer.Interfaces;
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
namespace SMAPI.Tests.ModApiProvider.Framework
|
||||
{
|
||||
/// <summary>The base class for <see cref="SimpleApi"/>.</summary>
|
||||
|
@ -9,6 +7,6 @@ namespace SMAPI.Tests.ModApiProvider.Framework
|
|||
** Test interface
|
||||
*********/
|
||||
/// <summary>A property inherited from a base class.</summary>
|
||||
public string InheritedProperty { get; set; }
|
||||
public string? InheritedProperty { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
#nullable disable
|
||||
// ReSharper disable UnusedMember.Global -- used dynamically through proxies
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
@ -16,7 +16,7 @@ namespace SMAPI.Tests.ModApiProvider.Framework
|
|||
** Events
|
||||
****/
|
||||
/// <summary>A simple event field.</summary>
|
||||
public event EventHandler<int> OnEventRaised;
|
||||
public event EventHandler<int>? OnEventRaised;
|
||||
|
||||
/// <summary>A simple event property with custom add/remove logic.</summary>
|
||||
public event EventHandler<int> OnEventRaisedProperty
|
||||
|
@ -33,16 +33,16 @@ namespace SMAPI.Tests.ModApiProvider.Framework
|
|||
public int NumberProperty { get; set; }
|
||||
|
||||
/// <summary>A simple object property.</summary>
|
||||
public object ObjectProperty { get; set; }
|
||||
public object? ObjectProperty { get; set; }
|
||||
|
||||
/// <summary>A simple list property.</summary>
|
||||
public List<string> ListProperty { get; set; }
|
||||
public List<string>? ListProperty { get; set; }
|
||||
|
||||
/// <summary>A simple list property with an interface.</summary>
|
||||
public IList<string> ListPropertyWithInterface { get; set; }
|
||||
public IList<string>? ListPropertyWithInterface { get; set; }
|
||||
|
||||
/// <summary>A property with nested generics.</summary>
|
||||
public IDictionary<string, IList<string>> GenericsProperty { get; set; }
|
||||
public IDictionary<string, IList<string>>? GenericsProperty { get; set; }
|
||||
|
||||
/// <summary>A property using an enum available to both mods.</summary>
|
||||
public BindingFlags EnumProperty { get; set; }
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using SMAPI.Tests.ModApiProvider.Framework;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using FluentAssertions;
|
||||
|
@ -28,7 +26,7 @@ namespace SMAPI.Tests.Core
|
|||
[TestCase("Characters/Dialogue/Abigail.fr-FR", "Characters/Dialogue/Abigail", "fr-FR", LocalizedContentManager.LanguageCode.fr)]
|
||||
[TestCase("Characters/Dialogue\\Abigail.fr-FR", "Characters/Dialogue/Abigail.fr-FR", null, null)]
|
||||
[TestCase("Characters/Dialogue/Abigail.fr-FR", "Characters/Dialogue/Abigail", "fr-FR", LocalizedContentManager.LanguageCode.fr)]
|
||||
public void Constructor_Valid(string name, string expectedBaseName, string expectedLocale, LocalizedContentManager.LanguageCode? expectedLanguageCode)
|
||||
public void Constructor_Valid(string name, string expectedBaseName, string? expectedLocale, LocalizedContentManager.LanguageCode? expectedLanguageCode)
|
||||
{
|
||||
// arrange
|
||||
name = PathUtilities.NormalizeAssetName(name);
|
||||
|
@ -55,13 +53,13 @@ namespace SMAPI.Tests.Core
|
|||
[TestCase(" ")]
|
||||
[TestCase("\t")]
|
||||
[TestCase(" \t ")]
|
||||
public void Constructor_NullOrWhitespace(string name)
|
||||
public void Constructor_NullOrWhitespace(string? name)
|
||||
{
|
||||
// act
|
||||
ArgumentException exception = Assert.Throws<ArgumentException>(() => _ = AssetName.Parse(name, null));
|
||||
ArgumentException exception = Assert.Throws<ArgumentException>(() => _ = AssetName.Parse(name!, null))!;
|
||||
|
||||
// assert
|
||||
exception!.ParamName.Should().Be("rawName");
|
||||
exception.ParamName.Should().Be("rawName");
|
||||
exception.Message.Should().Be("The asset name can't be null or empty. (Parameter 'rawName')");
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
@ -41,7 +39,7 @@ namespace SMAPI.Tests.Core
|
|||
public void CanProxy_EventField()
|
||||
{
|
||||
// arrange
|
||||
var providerMod = new ProviderMod();
|
||||
ProviderMod providerMod = new();
|
||||
object implementation = providerMod.GetModApi();
|
||||
int expectedValue = this.Random.Next();
|
||||
|
||||
|
@ -61,7 +59,7 @@ namespace SMAPI.Tests.Core
|
|||
public void CanProxy_EventProperty()
|
||||
{
|
||||
// arrange
|
||||
var providerMod = new ProviderMod();
|
||||
ProviderMod providerMod = new();
|
||||
object implementation = providerMod.GetModApi();
|
||||
int expectedValue = this.Random.Next();
|
||||
|
||||
|
@ -86,7 +84,7 @@ namespace SMAPI.Tests.Core
|
|||
public void CanProxy_Properties(string setVia)
|
||||
{
|
||||
// arrange
|
||||
var providerMod = new ProviderMod();
|
||||
ProviderMod providerMod = new();
|
||||
object implementation = providerMod.GetModApi();
|
||||
int expectedNumber = this.Random.Next();
|
||||
int expectedObject = this.Random.Next();
|
||||
|
@ -317,13 +315,13 @@ namespace SMAPI.Tests.Core
|
|||
/// <summary>Get a property value from an instance.</summary>
|
||||
/// <param name="parent">The instance whose property to read.</param>
|
||||
/// <param name="name">The property name.</param>
|
||||
private object GetPropertyValue(object parent, string name)
|
||||
private object? GetPropertyValue(object parent, string name)
|
||||
{
|
||||
if (parent is null)
|
||||
throw new ArgumentNullException(nameof(parent));
|
||||
|
||||
Type type = parent.GetType();
|
||||
PropertyInfo property = type.GetProperty(name);
|
||||
PropertyInfo? property = type.GetProperty(name);
|
||||
if (property is null)
|
||||
throw new InvalidOperationException($"The '{type.FullName}' type has no public property named '{name}'.");
|
||||
|
||||
|
|
|
@ -507,7 +507,7 @@ namespace SMAPI.Tests.Core
|
|||
/// <param name="allowStatusChange">Whether the code being tested is allowed to change the mod status.</param>
|
||||
private Mock<IModMetadata> GetMetadata(string uniqueID, string[] dependencies, bool allowStatusChange = false)
|
||||
{
|
||||
IManifest manifest = this.GetManifest(id: uniqueID, version: "1.0", dependencies: dependencies?.Select(dependencyID => (IManifestDependency)new ManifestDependency(dependencyID, null as ISemanticVersion)).ToArray());
|
||||
IManifest manifest = this.GetManifest(id: uniqueID, version: "1.0", dependencies: dependencies.Select(dependencyID => (IManifestDependency)new ManifestDependency(dependencyID, null as ISemanticVersion)).ToArray());
|
||||
return this.GetMetadata(manifest, allowStatusChange);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using NUnit.Framework;
|
||||
using StardewModdingAPI;
|
||||
|
@ -18,7 +17,7 @@ namespace SMAPI.Tests.Core
|
|||
** Data
|
||||
*********/
|
||||
/// <summary>Sample translation text for unit tests.</summary>
|
||||
public static string[] Samples = { null, "", " ", "boop", " boop " };
|
||||
public static string?[] Samples = { null, "", " ", "boop", " boop " };
|
||||
|
||||
|
||||
/*********
|
||||
|
@ -36,13 +35,13 @@ namespace SMAPI.Tests.Core
|
|||
// act
|
||||
ITranslationHelper helper = new TranslationHelper("ModID", "en", LocalizedContentManager.LanguageCode.en).SetTranslations(data);
|
||||
Translation translation = helper.Get("key");
|
||||
Translation[] translationList = helper.GetTranslations()?.ToArray();
|
||||
Translation[]? translationList = helper.GetTranslations()?.ToArray();
|
||||
|
||||
// assert
|
||||
Assert.AreEqual("en", helper.Locale, "The locale doesn't match the input value.");
|
||||
Assert.AreEqual(LocalizedContentManager.LanguageCode.en, helper.LocaleEnum, "The locale enum doesn't match the input value.");
|
||||
Assert.IsNotNull(translationList, "The full list of translations is unexpectedly null.");
|
||||
Assert.AreEqual(0, translationList.Length, "The full list of translations is unexpectedly not empty.");
|
||||
Assert.AreEqual(0, translationList!.Length, "The full list of translations is unexpectedly not empty.");
|
||||
|
||||
Assert.IsNotNull(translation, "The translation helper unexpectedly returned a null translation.");
|
||||
Assert.AreEqual(this.GetPlaceholderText("key"), translation.ToString(), "The translation returned an unexpected value.");
|
||||
|
@ -56,7 +55,7 @@ namespace SMAPI.Tests.Core
|
|||
var expected = this.GetExpectedTranslations();
|
||||
|
||||
// act
|
||||
var actual = new Dictionary<string, Translation[]>();
|
||||
var actual = new Dictionary<string, Translation[]?>();
|
||||
TranslationHelper helper = new TranslationHelper("ModID", "en", LocalizedContentManager.LanguageCode.en).SetTranslations(data);
|
||||
foreach (string locale in expected.Keys)
|
||||
{
|
||||
|
@ -109,13 +108,13 @@ namespace SMAPI.Tests.Core
|
|||
[TestCase(" ", ExpectedResult = true)]
|
||||
[TestCase("boop", ExpectedResult = true)]
|
||||
[TestCase(" boop ", ExpectedResult = true)]
|
||||
public bool Translation_HasValue(string text)
|
||||
public bool Translation_HasValue(string? text)
|
||||
{
|
||||
return new Translation("pt-BR", "key", text).HasValue();
|
||||
}
|
||||
|
||||
[Test(Description = "Assert that the translation's ToString method returns the expected text for various inputs.")]
|
||||
public void Translation_ToString([ValueSource(nameof(TranslationTests.Samples))] string text)
|
||||
public void Translation_ToString([ValueSource(nameof(TranslationTests.Samples))] string? text)
|
||||
{
|
||||
// act
|
||||
Translation translation = new("pt-BR", "key", text);
|
||||
|
@ -128,7 +127,7 @@ namespace SMAPI.Tests.Core
|
|||
}
|
||||
|
||||
[Test(Description = "Assert that the translation's implicit string conversion returns the expected text for various inputs.")]
|
||||
public void Translation_ImplicitStringConversion([ValueSource(nameof(TranslationTests.Samples))] string text)
|
||||
public void Translation_ImplicitStringConversion([ValueSource(nameof(TranslationTests.Samples))] string? text)
|
||||
{
|
||||
// act
|
||||
Translation translation = new("pt-BR", "key", text);
|
||||
|
@ -141,7 +140,7 @@ namespace SMAPI.Tests.Core
|
|||
}
|
||||
|
||||
[Test(Description = "Assert that the translation returns the expected text given a use-placeholder setting.")]
|
||||
public void Translation_UsePlaceholder([Values(true, false)] bool value, [ValueSource(nameof(TranslationTests.Samples))] string text)
|
||||
public void Translation_UsePlaceholder([Values(true, false)] bool value, [ValueSource(nameof(TranslationTests.Samples))] string? text)
|
||||
{
|
||||
// act
|
||||
Translation translation = new Translation("pt-BR", "key", text).UsePlaceholder(value);
|
||||
|
@ -156,7 +155,7 @@ namespace SMAPI.Tests.Core
|
|||
}
|
||||
|
||||
[Test(Description = "Assert that the translation returns the expected text after setting the default.")]
|
||||
public void Translation_Default([ValueSource(nameof(TranslationTests.Samples))] string text, [ValueSource(nameof(TranslationTests.Samples))] string @default)
|
||||
public void Translation_Default([ValueSource(nameof(TranslationTests.Samples))] string? text, [ValueSource(nameof(TranslationTests.Samples))] string? @default)
|
||||
{
|
||||
// act
|
||||
Translation translation = new Translation("pt-BR", "key", text).Default(@default);
|
||||
|
@ -192,7 +191,7 @@ namespace SMAPI.Tests.Core
|
|||
break;
|
||||
|
||||
case "class":
|
||||
translation = translation.Tokens(new TokenModel { Start = start, Middle = middle, End = end });
|
||||
translation = translation.Tokens(new TokenModel(start, middle, end));
|
||||
break;
|
||||
|
||||
case "IDictionary<string, object>":
|
||||
|
@ -331,16 +330,36 @@ namespace SMAPI.Tests.Core
|
|||
** Test models
|
||||
*********/
|
||||
/// <summary>A model used to test token support.</summary>
|
||||
[SuppressMessage("ReSharper", "NotAccessedField.Local", Justification = "Used dynamically via translation helper.")]
|
||||
[SuppressMessage("ReSharper", "UnusedAutoPropertyAccessor.Local", Justification = "Used dynamically via translation helper.")]
|
||||
private class TokenModel
|
||||
{
|
||||
/*********
|
||||
** Accessors
|
||||
*********/
|
||||
/// <summary>A sample token property.</summary>
|
||||
public string Start { get; set; }
|
||||
public string Start { get; }
|
||||
|
||||
/// <summary>A sample token property.</summary>
|
||||
public string Middle { get; set; }
|
||||
public string Middle { get; }
|
||||
|
||||
/// <summary>A sample token field.</summary>
|
||||
public string End;
|
||||
|
||||
|
||||
/*********
|
||||
** public methods
|
||||
*********/
|
||||
/// <summary>Construct an instance.</summary>
|
||||
/// <param name="start">A sample token property.</param>
|
||||
/// <param name="middle">A sample token field.</param>
|
||||
/// <param name="end">A sample token property.</param>
|
||||
public TokenModel(string start, string middle, string end)
|
||||
{
|
||||
this.Start = start;
|
||||
this.Middle = middle;
|
||||
this.End = end;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
|
||||
namespace SMAPI.Tests
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
|
@ -46,7 +44,7 @@ namespace SMAPI.Tests.Utilities
|
|||
[TestCase(",", ExpectedResult = "None")]
|
||||
[TestCase("A,", ExpectedResult = "A")]
|
||||
[TestCase(",A", ExpectedResult = "A")]
|
||||
public string TryParse_MultiValues(string input)
|
||||
public string TryParse_MultiValues(string? input)
|
||||
{
|
||||
// act
|
||||
bool success = KeybindList.TryParse(input, out KeybindList parsed, out string[] errors);
|
||||
|
@ -100,13 +98,15 @@ namespace SMAPI.Tests.Utilities
|
|||
public SButtonState GetState(string input, string stateMap)
|
||||
{
|
||||
// act
|
||||
bool success = KeybindList.TryParse(input, out KeybindList parsed, out string[] errors);
|
||||
bool success = KeybindList.TryParse(input, out KeybindList? parsed, out string[] errors);
|
||||
if (success && parsed?.Keybinds != null)
|
||||
{
|
||||
foreach (var keybind in parsed.Keybinds)
|
||||
foreach (Keybind? keybind in parsed.Keybinds)
|
||||
{
|
||||
#pragma warning disable 618 // method is marked obsolete because it should only be used in unit tests
|
||||
keybind.GetButtonState = key => this.GetStateFromMap(key, stateMap);
|
||||
#pragma warning restore 618
|
||||
}
|
||||
}
|
||||
|
||||
// assert
|
||||
|
@ -114,7 +114,7 @@ namespace SMAPI.Tests.Utilities
|
|||
Assert.IsNotNull(parsed, "The parsed result should not be null.");
|
||||
Assert.IsNotNull(errors, message: "The errors should never be null.");
|
||||
Assert.IsEmpty(errors, message: "The input bindings incorrectly reported errors.");
|
||||
return parsed.GetState();
|
||||
return parsed!.GetState();
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.IO;
|
||||
using NUnit.Framework;
|
||||
using StardewModdingAPI.Toolkit.Utilities;
|
||||
|
@ -8,6 +7,7 @@ namespace SMAPI.Tests.Utilities
|
|||
{
|
||||
/// <summary>Unit tests for <see cref="PathUtilities"/>.</summary>
|
||||
[TestFixture]
|
||||
[SuppressMessage("ReSharper", "StringLiteralTypo", Justification = "These are standard game install paths.")]
|
||||
internal class PathUtilitiesTests
|
||||
{
|
||||
/*********
|
||||
|
@ -16,136 +16,125 @@ namespace SMAPI.Tests.Utilities
|
|||
/// <summary>Sample paths used in unit tests.</summary>
|
||||
public static readonly SamplePath[] SamplePaths = {
|
||||
// Windows absolute path
|
||||
new()
|
||||
{
|
||||
OriginalPath = @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley",
|
||||
new(
|
||||
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" },
|
||||
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"
|
||||
},
|
||||
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()
|
||||
{
|
||||
OriginalPath = @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\",
|
||||
new(
|
||||
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\" },
|
||||
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/"
|
||||
},
|
||||
NormalizedOnWindows: @"C:\Program Files (x86)\Steam\steamapps\common\Stardew Valley\",
|
||||
NormalizedOnUnix: @"C:/Program Files (x86)/Steam/steamapps/common/Stardew Valley/"
|
||||
),
|
||||
|
||||
// Windows relative path
|
||||
new()
|
||||
{
|
||||
OriginalPath = @"Content\Characters\Dialogue\Abigail",
|
||||
new(
|
||||
OriginalPath: @"Content\Characters\Dialogue\Abigail",
|
||||
|
||||
Segments = new [] { "Content", "Characters", "Dialogue", "Abigail" },
|
||||
SegmentsLimit3 = new [] { "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"
|
||||
},
|
||||
NormalizedOnWindows: @"Content\Characters\Dialogue\Abigail",
|
||||
NormalizedOnUnix: @"Content/Characters/Dialogue/Abigail"
|
||||
),
|
||||
|
||||
// Windows relative path (with directory climbing)
|
||||
new()
|
||||
{
|
||||
OriginalPath = @"..\..\Content",
|
||||
new(
|
||||
OriginalPath: @"..\..\Content",
|
||||
|
||||
Segments = new [] { "..", "..", "Content" },
|
||||
SegmentsLimit3 = new [] { "..", "..", "Content" },
|
||||
Segments: new [] { "..", "..", "Content" },
|
||||
SegmentsLimit3: new [] { "..", "..", "Content" },
|
||||
|
||||
NormalizedOnWindows = @"..\..\Content",
|
||||
NormalizedOnUnix = @"../../Content"
|
||||
},
|
||||
NormalizedOnWindows: @"..\..\Content",
|
||||
NormalizedOnUnix: @"../../Content"
|
||||
),
|
||||
|
||||
// Windows UNC path
|
||||
new()
|
||||
{
|
||||
OriginalPath = @"\\unc\path",
|
||||
new(
|
||||
OriginalPath: @"\\unc\path",
|
||||
|
||||
Segments = new [] { "unc", "path" },
|
||||
SegmentsLimit3 = new [] { "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
|
||||
},
|
||||
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()
|
||||
{
|
||||
OriginalPath = @"/home/.steam/steam/steamapps/common/Stardew Valley",
|
||||
new(
|
||||
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" },
|
||||
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"
|
||||
},
|
||||
NormalizedOnWindows: @"\home\.steam\steam\steamapps\common\Stardew Valley",
|
||||
NormalizedOnUnix: @"/home/.steam/steam/steamapps/common/Stardew Valley"
|
||||
),
|
||||
|
||||
// Linux absolute path (with trailing slash)
|
||||
new()
|
||||
{
|
||||
OriginalPath = @"/home/.steam/steam/steamapps/common/Stardew Valley/",
|
||||
new(
|
||||
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/" },
|
||||
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/"
|
||||
},
|
||||
NormalizedOnWindows: @"\home\.steam\steam\steamapps\common\Stardew Valley\",
|
||||
NormalizedOnUnix: @"/home/.steam/steam/steamapps/common/Stardew Valley/"
|
||||
),
|
||||
|
||||
// Linux absolute path (with ~)
|
||||
new()
|
||||
{
|
||||
OriginalPath = @"~/.steam/steam/steamapps/common/Stardew Valley",
|
||||
new(
|
||||
OriginalPath: @"~/.steam/steam/steamapps/common/Stardew Valley",
|
||||
|
||||
Segments = new [] { "~", ".steam", "steam", "steamapps", "common", "Stardew Valley" },
|
||||
SegmentsLimit3 = new [] { "~", ".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"
|
||||
},
|
||||
NormalizedOnWindows: @"~\.steam\steam\steamapps\common\Stardew Valley",
|
||||
NormalizedOnUnix: @"~/.steam/steam/steamapps/common/Stardew Valley"
|
||||
),
|
||||
|
||||
// Linux relative path
|
||||
new()
|
||||
{
|
||||
OriginalPath = @"Content/Characters/Dialogue/Abigail",
|
||||
new(
|
||||
OriginalPath: @"Content/Characters/Dialogue/Abigail",
|
||||
|
||||
Segments = new [] { "Content", "Characters", "Dialogue", "Abigail" },
|
||||
SegmentsLimit3 = new [] { "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"
|
||||
},
|
||||
NormalizedOnWindows: @"Content\Characters\Dialogue\Abigail",
|
||||
NormalizedOnUnix: @"Content/Characters/Dialogue/Abigail"
|
||||
),
|
||||
|
||||
// Linux relative path (with directory climbing)
|
||||
new()
|
||||
{
|
||||
OriginalPath = @"../../Content",
|
||||
new(
|
||||
OriginalPath: @"../../Content",
|
||||
|
||||
Segments = new [] { "..", "..", "Content" },
|
||||
SegmentsLimit3 = new [] { "..", "..", "Content" },
|
||||
Segments: new [] { "..", "..", "Content" },
|
||||
SegmentsLimit3: new [] { "..", "..", "Content" },
|
||||
|
||||
NormalizedOnWindows = @"..\..\Content",
|
||||
NormalizedOnUnix = @"../../Content"
|
||||
},
|
||||
NormalizedOnWindows: @"..\..\Content",
|
||||
NormalizedOnUnix: @"../../Content"
|
||||
),
|
||||
|
||||
// Mixed directory separators
|
||||
new()
|
||||
{
|
||||
OriginalPath = @"C:\some/mixed\path/separators",
|
||||
new(
|
||||
OriginalPath: @"C:\some/mixed\path/separators",
|
||||
|
||||
Segments = new [] { "C:", "some", "mixed", "path", "separators" },
|
||||
SegmentsLimit3 = new [] { "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"
|
||||
},
|
||||
NormalizedOnWindows: @"C:\some\mixed\path\separators",
|
||||
NormalizedOnUnix: @"C:/some/mixed/path/separators"
|
||||
)
|
||||
};
|
||||
|
||||
|
||||
|
@ -283,14 +272,14 @@ namespace SMAPI.Tests.Utilities
|
|||
/*********
|
||||
** Private classes
|
||||
*********/
|
||||
public class SamplePath
|
||||
/// <summary>A sample path in multiple formats.</summary>
|
||||
/// <param name="OriginalPath">The original path to pass to the <see cref="PathUtilities"/>.</param>
|
||||
/// <param name="Segments">The normalized path segments.</param>
|
||||
/// <param name="SegmentsLimit3">The normalized path segments, if we stop segmenting after the second one.</param>
|
||||
/// <param name="NormalizedOnWindows">The normalized form on Windows.</param>
|
||||
/// <param name="NormalizedOnUnix">The normalized form on Linux or macOS.</param>
|
||||
public record SamplePath(string OriginalPath, string[] Segments, string[] SegmentsLimit3, string NormalizedOnWindows, string NormalizedOnUnix)
|
||||
{
|
||||
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;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
@ -258,7 +256,7 @@ namespace SMAPI.Tests.Utilities
|
|||
{
|
||||
SDate date = new(day, season, year);
|
||||
int hash = date.GetHashCode();
|
||||
if (hashes.TryGetValue(hash, out SDate otherDate))
|
||||
if (hashes.TryGetValue(hash, out SDate? otherDate))
|
||||
Assert.Fail($"Received identical hash code {hash} for dates {otherDate} and {date}.");
|
||||
if (hash < lastHash)
|
||||
Assert.Fail($"Received smaller hash code for date {date} ({hash}) relative to {hashes[lastHash]} ({lastHash}).");
|
||||
|
@ -298,7 +296,7 @@ namespace SMAPI.Tests.Utilities
|
|||
[TestCase(Dates.Now, Dates.NextDay, ExpectedResult = false)]
|
||||
[TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = false)]
|
||||
[TestCase(Dates.Now, Dates.NextYear, ExpectedResult = false)]
|
||||
public bool Operators_Equals(string now, string other)
|
||||
public bool Operators_Equals(string? now, string other)
|
||||
{
|
||||
return this.GetDate(now) == this.GetDate(other);
|
||||
}
|
||||
|
@ -312,7 +310,7 @@ namespace SMAPI.Tests.Utilities
|
|||
[TestCase(Dates.Now, Dates.NextDay, ExpectedResult = true)]
|
||||
[TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = true)]
|
||||
[TestCase(Dates.Now, Dates.NextYear, ExpectedResult = true)]
|
||||
public bool Operators_NotEquals(string now, string other)
|
||||
public bool Operators_NotEquals(string? now, string other)
|
||||
{
|
||||
return this.GetDate(now) != this.GetDate(other);
|
||||
}
|
||||
|
@ -326,7 +324,7 @@ namespace SMAPI.Tests.Utilities
|
|||
[TestCase(Dates.Now, Dates.NextDay, ExpectedResult = true)]
|
||||
[TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = true)]
|
||||
[TestCase(Dates.Now, Dates.NextYear, ExpectedResult = true)]
|
||||
public bool Operators_LessThan(string now, string other)
|
||||
public bool Operators_LessThan(string? now, string other)
|
||||
{
|
||||
return this.GetDate(now) < this.GetDate(other);
|
||||
}
|
||||
|
@ -340,7 +338,7 @@ namespace SMAPI.Tests.Utilities
|
|||
[TestCase(Dates.Now, Dates.NextDay, ExpectedResult = true)]
|
||||
[TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = true)]
|
||||
[TestCase(Dates.Now, Dates.NextYear, ExpectedResult = true)]
|
||||
public bool Operators_LessThanOrEqual(string now, string other)
|
||||
public bool Operators_LessThanOrEqual(string? now, string other)
|
||||
{
|
||||
return this.GetDate(now) <= this.GetDate(other);
|
||||
}
|
||||
|
@ -354,7 +352,7 @@ namespace SMAPI.Tests.Utilities
|
|||
[TestCase(Dates.Now, Dates.NextDay, ExpectedResult = false)]
|
||||
[TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = false)]
|
||||
[TestCase(Dates.Now, Dates.NextYear, ExpectedResult = false)]
|
||||
public bool Operators_MoreThan(string now, string other)
|
||||
public bool Operators_MoreThan(string? now, string other)
|
||||
{
|
||||
return this.GetDate(now) > this.GetDate(other);
|
||||
}
|
||||
|
@ -368,7 +366,7 @@ namespace SMAPI.Tests.Utilities
|
|||
[TestCase(Dates.Now, Dates.NextDay, ExpectedResult = false)]
|
||||
[TestCase(Dates.Now, Dates.NextMonth, ExpectedResult = false)]
|
||||
[TestCase(Dates.Now, Dates.NextYear, ExpectedResult = false)]
|
||||
public bool Operators_MoreThanOrEqual(string now, string other)
|
||||
public bool Operators_MoreThanOrEqual(string? now, string other)
|
||||
{
|
||||
return this.GetDate(now) > this.GetDate(other);
|
||||
}
|
||||
|
@ -379,7 +377,8 @@ namespace SMAPI.Tests.Utilities
|
|||
*********/
|
||||
/// <summary>Convert a string date into a game date, to make unit tests easier to read.</summary>
|
||||
/// <param name="dateStr">The date string like "dd MMMM yy".</param>
|
||||
private SDate GetDate(string dateStr)
|
||||
[return: NotNullIfNotNull("dateStr")]
|
||||
private SDate? GetDate(string? dateStr)
|
||||
{
|
||||
if (dateStr == null)
|
||||
return null;
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
#nullable disable
|
||||
|
||||
using System.Collections.Generic;
|
||||
using NUnit.Framework;
|
||||
using StardewModdingAPI;
|
||||
|
@ -22,15 +20,14 @@ namespace SMAPI.Tests.WikiClient
|
|||
{
|
||||
// arrange
|
||||
string rawDescriptor = "-Nexus:2400, -B, XX → YY, Nexus:451,+A, XXX → YYY, invalidA →, → invalidB";
|
||||
string[] expectedAdd = new[] { "Nexus:451", "A" };
|
||||
string[] expectedRemove = new[] { "Nexus:2400", "B" };
|
||||
string[] expectedAdd = { "Nexus:451", "A" };
|
||||
string[] expectedRemove = { "Nexus:2400", "B" };
|
||||
IDictionary<string, string> expectedReplace = new Dictionary<string, string>
|
||||
{
|
||||
["XX"] = "YY",
|
||||
["XXX"] = "YYY"
|
||||
};
|
||||
string[] expectedErrors = new[]
|
||||
{
|
||||
string[] expectedErrors = {
|
||||
"Failed parsing ' invalidA →': can't map to a blank value. Use the '-value' format to remove a value.",
|
||||
"Failed parsing ' → invalidB': can't map from a blank old value. Use the '+value' format to add a value."
|
||||
};
|
||||
|
@ -50,15 +47,14 @@ namespace SMAPI.Tests.WikiClient
|
|||
{
|
||||
// arrange
|
||||
string rawDescriptor = "-1.0.1, -2.0-beta, 1.00 → 1.0, 1.0.0,+2.0-beta.15, 2.0 → 2.0-beta, invalidA →, → invalidB";
|
||||
string[] expectedAdd = new[] { "1.0.0", "2.0.0-beta.15" };
|
||||
string[] expectedRemove = new[] { "1.0.1", "2.0.0-beta" };
|
||||
string[] expectedAdd = { "1.0.0", "2.0.0-beta.15" };
|
||||
string[] expectedRemove = { "1.0.1", "2.0.0-beta" };
|
||||
IDictionary<string, string> expectedReplace = new Dictionary<string, string>
|
||||
{
|
||||
["1.00"] = "1.0.0",
|
||||
["2.0.0"] = "2.0.0-beta"
|
||||
};
|
||||
string[] expectedErrors = new[]
|
||||
{
|
||||
string[] expectedErrors = {
|
||||
"Failed parsing ' invalidA →': can't map to a blank value. Use the '-value' format to remove a value.",
|
||||
"Failed parsing ' → invalidB': can't map from a blank old value. Use the '+value' format to add a value."
|
||||
};
|
||||
|
@ -67,7 +63,7 @@ namespace SMAPI.Tests.WikiClient
|
|||
ChangeDescriptor parsed = ChangeDescriptor.Parse(
|
||||
rawDescriptor,
|
||||
out string[] errors,
|
||||
formatValue: raw => SemanticVersion.TryParse(raw, out ISemanticVersion version)
|
||||
formatValue: raw => SemanticVersion.TryParse(raw, out ISemanticVersion? version)
|
||||
? version.ToString()
|
||||
: raw
|
||||
);
|
||||
|
@ -111,9 +107,9 @@ namespace SMAPI.Tests.WikiClient
|
|||
[TestCase("", "+Nexus:A, Nexus:B, -Chucklefish:14, Nexus:2400 → Nexus:2401, Nexus:A→Nexus:B", ExpectedResult = "Nexus:A, Nexus:B")]
|
||||
[TestCase("Nexus:2400", "+Nexus:A, Nexus:B, -Chucklefish:14, Nexus:2400 → Nexus:2401, Nexus:A→Nexus:B", ExpectedResult = "Nexus:2401, Nexus:A, Nexus:B")]
|
||||
[TestCase("Nexus:2400, Nexus:2401, Nexus:B,Chucklefish:14", "+Nexus:A, Nexus:B, -Chucklefish:14, Nexus:2400 → Nexus:2401, Nexus:A→Nexus:B", ExpectedResult = "Nexus:2401, Nexus:2401, Nexus:B, Nexus:A")]
|
||||
public string Apply_Raw(string input, string descriptor)
|
||||
public string Apply_Raw(string input, string? descriptor)
|
||||
{
|
||||
var parsed = ChangeDescriptor.Parse(descriptor, out string[] errors);
|
||||
ChangeDescriptor parsed = ChangeDescriptor.Parse(descriptor, out string[] errors);
|
||||
|
||||
Assert.IsEmpty(errors, "Parsing the descriptor failed.");
|
||||
|
||||
|
@ -128,7 +124,7 @@ namespace SMAPI.Tests.WikiClient
|
|||
[TestCase("-Nexus:2400", ExpectedResult = "-Nexus:2400")]
|
||||
[TestCase(" Nexus:2400 →Nexus:2401 ", ExpectedResult = "Nexus:2400 → Nexus:2401")]
|
||||
[TestCase("+Nexus:A, Nexus:B, -Chucklefish:14, Nexus:2400 → Nexus:2401, Nexus:A→Nexus:B", ExpectedResult = "+Nexus:A, +Nexus:B, -Chucklefish:14, Nexus:2400 → Nexus:2401, Nexus:A → Nexus:B")]
|
||||
public string ToString(string descriptor)
|
||||
public string ToString(string? descriptor)
|
||||
{
|
||||
var parsed = ChangeDescriptor.Parse(descriptor, out string[] errors);
|
||||
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
|
||||
namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
|
||||
|
@ -47,6 +48,9 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
|
|||
/// <summary>Apply the change descriptors to a comma-delimited field.</summary>
|
||||
/// <param name="rawField">The raw field text.</param>
|
||||
/// <returns>Returns the modified field.</returns>
|
||||
#if NET5_0_OR_GREATER
|
||||
[return: NotNullIfNotNull("rawField")]
|
||||
#endif
|
||||
public string? ApplyToCopy(string? rawField)
|
||||
{
|
||||
// get list
|
||||
|
@ -119,7 +123,7 @@ namespace StardewModdingAPI.Toolkit.Framework.Clients.Wiki
|
|||
/// <param name="descriptor">The raw change descriptor.</param>
|
||||
/// <param name="errors">The human-readable error message describing any invalid values that were ignored.</param>
|
||||
/// <param name="formatValue">Format a raw value into a normalized form if needed.</param>
|
||||
public static ChangeDescriptor Parse(string descriptor, out string[] errors, Func<string, string>? formatValue = null)
|
||||
public static ChangeDescriptor Parse(string? descriptor, out string[] errors, Func<string, string>? formatValue = null)
|
||||
{
|
||||
// init
|
||||
formatValue ??= p => p;
|
||||
|
|
Loading…
Reference in New Issue