make unit test easier to extend (#296)

This commit is contained in:
Jesse Plamondon-Willard 2017-05-28 11:21:49 -04:00
parent 2958381b54
commit af28b87660
2 changed files with 37 additions and 25 deletions

View File

@ -19,8 +19,11 @@ namespace StardewModdingAPI.Tests
/// <summary>A token structure type.</summary> /// <summary>A token structure type.</summary>
public enum TokenType public enum TokenType
{ {
/// <summary>The tokens are passed in a dictionary.</summary> /// <summary>The tokens are passed in a string/object dictionary.</summary>
Dictionary, DictionaryStringObject,
/// <summary>The tokens are passed in a string/string dictionary.</summary>
DictionaryStringString,
/// <summary>The tokens are passed in an anonymous object.</summary> /// <summary>The tokens are passed in an anonymous object.</summary>
AnonymousObject AnonymousObject
@ -190,7 +193,7 @@ namespace StardewModdingAPI.Tests
** Translation tokens ** Translation tokens
****/ ****/
[Test(Description = "Assert that multiple translation tokens are replaced correctly regardless of the token structure.")] [Test(Description = "Assert that multiple translation tokens are replaced correctly regardless of the token structure.")]
public void Translation_Tokens([Values(TokenType.AnonymousObject, TokenType.Dictionary)] TokenType tokenType) public void Translation_Tokens([Values(TokenType.AnonymousObject, TokenType.DictionaryStringObject, TokenType.DictionaryStringString)] TokenType tokenType)
{ {
// arrange // arrange
string start = Guid.NewGuid().ToString("N"); string start = Guid.NewGuid().ToString("N");
@ -207,10 +210,14 @@ namespace StardewModdingAPI.Tests
translation = translation.Tokens(new { start, middle, end }); translation = translation.Tokens(new { start, middle, end });
break; break;
case TokenType.Dictionary: case TokenType.DictionaryStringObject:
translation = translation.Tokens(new Dictionary<string, object> { ["start"] = start, ["middle"] = middle, ["end"] = end }); translation = translation.Tokens(new Dictionary<string, object> { ["start"] = start, ["middle"] = middle, ["end"] = end });
break; break;
case TokenType.DictionaryStringString:
translation = translation.Tokens(new Dictionary<string, string> { ["start"] = start, ["middle"] = middle, ["end"] = end });
break;
default: default:
throw new NotSupportedException($"Unknown token type {tokenType}."); throw new NotSupportedException($"Unknown token type {tokenType}.");
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Reflection;
using System.Text.RegularExpressions; using System.Text.RegularExpressions;
namespace StardewModdingAPI namespace StardewModdingAPI
@ -82,37 +83,41 @@ namespace StardewModdingAPI
} }
/// <summary>Replace tokens in the text like <c>{{value}}</c> with the given values. Returns a new instance.</summary> /// <summary>Replace tokens in the text like <c>{{value}}</c> with the given values. Returns a new instance.</summary>
/// <param name="tokens">An anonymous object containing token key/value pairs, like <c>new { value = 42, name = "Cranberries" }</c>.</param> /// <param name="tokens">An object containing token key/value pairs. This can be an anonymous object (like <c>new { value = 42, name = "Cranberries" }</c>) or a dictionary of token values.</param>
/// <exception cref="ArgumentNullException">The <paramref name="tokens"/> argument is <c>null</c>.</exception> /// <exception cref="ArgumentNullException">The <paramref name="tokens"/> argument is <c>null</c>.</exception>
public Translation Tokens(object tokens) public Translation Tokens(object tokens)
{ {
if (tokens == null) if (tokens == null)
throw new ArgumentNullException(nameof(tokens)); throw new ArgumentNullException(nameof(tokens));
IDictionary<string, object> dictionary = tokens // get dictionary of tokens
.GetType() IDictionary<string, string> tokenLookup = new Dictionary<string, string>(StringComparer.InvariantCultureIgnoreCase);
.GetProperties() {
.ToDictionary( // from dictionary
p => p.Name, if (tokens is IDictionary inputLookup)
p => p.GetValue(tokens) {
); foreach (DictionaryEntry entry in inputLookup)
return this.Tokens(dictionary); {
} string key = entry.Key?.ToString().Trim();
if (key != null)
tokenLookup[key] = entry.Value?.ToString();
}
}
/// <summary>Replace tokens in the text like <c>{{value}}</c> with the given values. Returns a new instance.</summary> // from object properties
/// <param name="tokens">A dictionary containing token key/value pairs.</param> else
/// <exception cref="ArgumentNullException">The <paramref name="tokens"/> argument is <c>null</c>.</exception> {
public Translation Tokens(IDictionary<string, object> tokens) foreach (PropertyInfo prop in tokens.GetType().GetProperties())
{ tokenLookup[prop.Name] = prop.GetValue(tokens)?.ToString();
if (tokens == null) }
throw new ArgumentNullException(nameof(tokens)); }
tokens = tokens.ToDictionary(p => p.Key.Trim(), p => p.Value, StringComparer.InvariantCultureIgnoreCase); // format translation
string text = Regex.Replace(this.Text, @"{{([ \w\.\-]+)}}", match => string text = Regex.Replace(this.Text, @"{{([ \w\.\-]+)}}", match =>
{ {
string key = match.Groups[1].Value.Trim(); string key = match.Groups[1].Value.Trim();
return tokens.TryGetValue(key, out object value) return tokenLookup.TryGetValue(key, out string value)
? value?.ToString() ? value
: match.Value; : match.Value;
}); });
return new Translation(this.ModName, this.Locale, this.Key, text); return new Translation(this.ModName, this.Locale, this.Key, text);