using System; using System.Collections.Generic; using System.IO; using System.Linq; using UnitTestSharp; namespace Vellum.UnitTests { public class DictionaryConverterTests : TestFixture { public static SaveSession Session() { return SaveConvertersTests.TestConverters().CreateSession(); } /// /// Saves to Lua text and loads it back. /// public static Dictionary ThroughLuaText( Dictionary value) { SaveSession session = Session(); SaveNode saved = session.ToNode(value, typeof(Dictionary)); SaveNode node = LuaSaveFormat.Parse(LuaSaveFormat.Format(saved)); return (Dictionary)session.FromNode(node, typeof(Dictionary)); } public static SaveNode PointNode(double x, double y) { return Session().ToNode(new Point(x, y), typeof(Point)); } public class WriteTests : TestFixture { public void Entries_AreSavedAsAMapInTheOrderTheDictionaryEnumeratesThem() { var value = new Dictionary { { "b", 2 }, { "a", 1 } }; SaveNode node = Session().ToNode(value, typeof(Dictionary)); IReadOnlyList> entries = node.RequireEntry("entries").RequireMap(); CheckEqual(new[] { "b", "a" }, entries.Select(entry => entry.Key.Text).ToArray()); CheckEqual(new[] { "2", "1" }, entries.Select(entry => entry.Value.Text).ToArray()); } public void DefaultComparer_IsLeftOut() { SaveNode node = Session().ToNode(new Dictionary(), typeof(Dictionary)); CheckEqual(new[] { "entries" }, node.Entries.Keys.ToArray()); } public void NamedComparer_IsSavedBesideTheEntries() { var value = new Dictionary(StringComparer.Ordinal); SaveNode node = Session().ToNode(value, typeof(Dictionary)); CheckEqual("Ordinal", node.RequireEntry("comparer").Text); } public void ComparerThatCantBeSaved_Throws() { var value = new Dictionary(new SavedComparerTests.EveryoneEqual()); CheckThrow(typeof(NotSupportedException)); Session().ToNode(value, typeof(Dictionary)); } public void ConvertedKeys_AreSavedAsTables() { var value = new Dictionary { { new Point(1.5, 0), "home" } }; SaveNode node = Session().ToNode(value, typeof(Dictionary)); KeyValuePair entry = node.RequireEntry("entries").RequireMap().Single(); CheckEqual("0x3FF8000000000000", entry.Key.RequireEntry("x").Text); CheckEqual("home", entry.Value.Text); } public void NullValue_IsANullNode() { var value = new Dictionary { { "a", null } }; SaveNode node = Session().ToNode(value, typeof(Dictionary)); CheckEqual(SaveNode.Kinds.Null, node.RequireEntry("entries").RequireMap().Single().Value.Kind); } public void NullDictionary_IsNull() { CheckEqual(SaveNode.Kinds.Null, Session().ToNode(null, typeof(Dictionary)).Kind); } } public class ReadTests : TestFixture { public static SaveNode NodeHoldingItself() { SaveNode node = SaveNode.NewTable(); SaveNode entries = SaveNode.NewTable(); entries.Entries["self"] = node; node.Entries[DictionaryConverter.EntriesKey] = entries; return node; } public void DictionaryHoldingItself_ReadsAsADictionaryHoldingItself() { var loaded = (Dictionary)new SaveConverters(new SaveConverter[0]).CreateSession() .FromNode(NodeHoldingItself(), typeof(Dictionary)); CheckTrue(ReferenceEquals(loaded, loaded["self"])); } public void TextKeys_RoundTrip() { var value = new Dictionary { { "b", 2 }, { "a", 1 } }; Dictionary loaded = ThroughLuaText(value); CheckEqual(2, loaded.Count); CheckEqual(1, loaded["a"]); CheckEqual(2, loaded["b"]); } public void KeysThatAreNotIdentifiers_RoundTrip() { var value = new Dictionary { { "end", 1 }, { "two words", 2 }, { "", 3 }, { "é\"\n", 4 }, }; Dictionary loaded = ThroughLuaText(value); CheckEqual(1, loaded["end"]); CheckEqual(2, loaded["two words"]); CheckEqual(3, loaded[""]); CheckEqual(4, loaded["é\"\n"]); } public void EmptyDictionary_RoundTrips() { CheckEqual(0, ThroughLuaText(new Dictionary()).Count); } public void NonTextKeys_RoundTrip() { var value = new Dictionary { { 10, "ten" }, { -1, "minus one" } }; Dictionary loaded = ThroughLuaText(value); CheckEqual("ten", loaded[10]); CheckEqual("minus one", loaded[-1]); } public void ConvertedKeys_RoundTrip() { var value = new Dictionary { { new Point(1.5, 0), "home" }, { new Point(0, 2), "mine" }, }; Dictionary loaded = ThroughLuaText(value); CheckEqual(2, loaded.Count); CheckEqual("home", loaded[new Point(1.5, 0)]); CheckEqual("mine", loaded[new Point(0, 2)]); } public void ListKeys_RoundTrip() { var value = new Dictionary { { new[] { "a", "b" }, 1 } }; Dictionary loaded = ThroughLuaText(value); CheckEqual(new[] { "a", "b" }, loaded.Keys.Single()); } public void ValuesThatAreCollections_RoundTrip() { var value = new Dictionary> { { "route", new List { new Point(1.5, 0) } } }; Dictionary> loaded = ThroughLuaText(value); CheckEqual(1.5, loaded["route"][0].X); } public void NullValue_RoundTrips() { CheckNull(ThroughLuaText(new Dictionary { { "a", null } })["a"]); } public void Comparer_RoundTrips() { var value = new Dictionary(StringComparer.OrdinalIgnoreCase) { { "a", 1 } }; Dictionary loaded = ThroughLuaText(value); CheckTrue(loaded.Comparer == StringComparer.OrdinalIgnoreCase); CheckEqual(1, loaded["A"]); } public void NoComparer_LoadsWithTheDefault() { Dictionary loaded = ThroughLuaText(new Dictionary { { "a", 1 } }); CheckFalse(loaded.ContainsKey("A")); } public void SaveLoadSave_IsIdenticalText() { SaveSession session = Session(); var value = new Dictionary { { new Point(1.5, 0), "home" }, { new Point(0, 2), "mine" }, { new Point(3, 3), "far" }, }; string first = LuaSaveFormat.Format(session.ToNode(value, typeof(Dictionary))); var loaded = (Dictionary)session.FromNode(LuaSaveFormat.Parse(first), typeof(Dictionary)); string second = LuaSaveFormat.Format(session.ToNode(loaded, typeof(Dictionary))); CheckEqual(first, second); } public void NullForADictionary_IsNull() { CheckNull(Session().FromNode(SaveNode.Null, typeof(Dictionary))); } public void SameKeyTwice_KeepsTheFirstAndRecordsTheSecond() { SaveNode entries = SaveNode.NewMap(); entries.Pairs.Add(new KeyValuePair(PointNode(1, 2), SaveNode.OfText("a"))); entries.Pairs.Add(new KeyValuePair(PointNode(1, 2), SaveNode.OfText("b"))); SaveNode node = SaveNode.NewTable(); node.Entries["entries"] = entries; SaveSession session = Session(); var loaded = (Dictionary)session.FromNode(node, typeof(Dictionary)); CheckEqual("a", loaded.Single().Value); CheckEqual(new[] { "entries: [1]" }, SaveConvertersTests.IssuePaths(session)); } public void KeysTheComparerCallsEqual_KeepsTheFirstAndRecordsTheSecond() { SaveNode node = SaveNode.NewTable(); node.Entries["comparer"] = SaveNode.OfText("OrdinalIgnoreCase"); node.Entries["entries"] = SaveNode.NewTable(); node.Entries["entries"].Entries["a"] = SaveNode.OfText("1"); node.Entries["entries"].Entries["A"] = SaveNode.OfText("2"); SaveSession session = Session(); var loaded = (Dictionary)session.FromNode(node, typeof(Dictionary)); CheckEqual(2, loaded["a"]); CheckEqual(new[] { "entries: [1]" }, SaveConvertersTests.IssuePaths(session)); } public void MissingEntries_Throws() { CheckThrow(typeof(InvalidDataException)); Session().FromNode(SaveNode.NewTable(), typeof(Dictionary)); } public void NodeThatIsNotATable_Throws() { CheckThrow(typeof(InvalidDataException)); Session().FromNode(SaveNode.OfText("3"), typeof(Dictionary)); } public void NullForAValueType_LeavesTheEntryOutAndRecordsIt() { SaveNode node = SaveNode.NewTable(); node.Entries["entries"] = SaveNode.NewTable(); node.Entries["entries"].Entries["a"] = SaveNode.Null; node.Entries["entries"].Entries["b"] = SaveNode.OfText("2"); SaveSession session = Session(); var loaded = (Dictionary)session.FromNode(node, typeof(Dictionary)); CheckEqual(new[] { "b" }, loaded.Keys.ToArray()); CheckEqual(new[] { "entries: [0]" }, SaveConvertersTests.IssuePaths(session)); } } public class TryListChildrenTests : TestFixture { public void EachKeyAndValue_AreListedInPairsAsTheirTypes() { var value = new Dictionary { { "a", new Waypoint { Name = "x" } }, { "b", null } }; SaveNode node = Session().ToNode(value, typeof(Dictionary)); var children = new ChildList(); CheckTrue(new DictionaryConverter().TryListChildren(node, children)); IReadOnlyList> pairs = node.RequireEntry("entries").RequireMap(); CheckEqual(4, children.Count); CheckTrue(ReferenceEquals(pairs[0].Key, children.NodeAt(0))); CheckTrue(ReferenceEquals(pairs[0].Value, children.NodeAt(1))); CheckTrue(ReferenceEquals(pairs[1].Value, children.NodeAt(3))); CheckEqual(typeof(string), children.TypeAt(0)); CheckEqual(typeof(Waypoint), children.TypeAt(1)); } public void TextKeysReadBackFromLuaText_AreListedFromTheTable() { var value = new Dictionary { { "a", new Waypoint { Name = "x" } } }; SaveNode node = SaveConvertersTests.ThroughLuaText( Session().ToNode(value, typeof(Dictionary))); var children = new ChildList(); CheckTrue(new DictionaryConverter().TryListChildren(node, children)); CheckEqual(2, children.Count); CheckTrue(ReferenceEquals(node.RequireEntry("entries").RequireEntry("a"), children.NodeAt(1))); } public void EmptyDictionary_ListsNothingAndSucceeds() { SaveNode node = SaveNode.NewTable(); node.Entries["entries"] = SaveNode.NewMap(); var children = new ChildList(); CheckTrue(new DictionaryConverter().TryListChildren(node, children)); CheckEqual(0, children.Count); } public void MissingEntries_ReturnsFalse() { CheckFalse(new DictionaryConverter().TryListChildren( SaveNode.NewTable(), new ChildList())); } public void EntriesThatAreNotAMap_ReturnsFalse() { SaveNode node = SaveNode.NewTable(); node.Entries["entries"] = SaveNode.OfText("3"); CheckFalse(new DictionaryConverter().TryListChildren(node, new ChildList())); } } public class AllocateTests : TestFixture { public void SavedValue_GivesAnEmptyCollection() { SaveSession session = SaveConvertersTests.Session(); SaveNode node = session.ToNode(new Dictionary { { 3, "c" }, { 1, "a" } }); CheckEqual(0, new DictionaryConverter().Allocate(node, session).Count); } public void NodeOfTheWrongShape_Throws() { CheckThrow(typeof(InvalidDataException)); new DictionaryConverter().Allocate(SaveNode.OfText("3"), SaveConvertersTests.Session()); } } public class FillTests : TestFixture { public void SavedValue_AddsWhatReadLoads() { SaveSession session = SaveConvertersTests.Session(); SaveNode node = session.ToNode(new Dictionary { { 3, "c" }, { 1, "a" } }); var converter = new DictionaryConverter(); Dictionary filled = converter.Allocate(node, session); converter.Fill(filled, node, session); CheckTrue(converter.Read(node, SaveConvertersTests.Session()).SequenceEqual(filled)); } } public class FillNeedsFilledPartTests : TestFixture { public void TheHashedOrSortedType_IsTrue() { CheckTrue(new DictionaryConverter().FillNeedsFilledPart(typeof(int))); } public void AnotherType_IsFalse() { CheckFalse(new DictionaryConverter().FillNeedsFilledPart(typeof(string))); } } } }