using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using UnitTestSharp; namespace Vellum.Generator.UnitTests { public class SaveTemplateTests : TestFixture { public const string Marker0 = "global::Vellum.SaveMethodParameter0"; public static SaveTemplate Concrete(string format, params SaveTemplate[] parts) { return new SaveTemplate(-1, format, new EquatableArray( System.Collections.Immutable.ImmutableArray.Create(parts))); } public static SaveTemplate ListOf(SaveTemplate item) { return Concrete("global::System.Collections.Generic.List<{0}>", item); } public class CtorTests : TestFixture { public void KeepsEveryArgument() { SaveTemplate part = SaveTemplate.OfParameter(1); SaveTemplate template = ListOf(part); CheckEqual(-1, template.Parameter); CheckEqual("global::System.Collections.Generic.List<{0}>", template.Format); CheckEqual(new[] { part }, template.Parts.Items); } public void ParameterAlone_HasAParameter() { CheckTrue(SaveTemplate.OfParameter(0).HasParameter); } public void ParameterDeepInside_HasAParameter() { CheckTrue(ListOf(ListOf(SaveTemplate.OfParameter(0))).HasParameter); } public void NoParameter_HasNone() { CheckFalse(ListOf(Concrete("global::System.String")).HasParameter); } public void NullPart_IsNotAParameter() { CheckFalse(ListOf(null).HasParameter); } } public class OfClassParameterTests : TestFixture { public void StandsForItsNumberAmongTheClassParameters() { SaveTemplate template = SaveTemplate.OfClassParameter(2); CheckEqual(2, template.ClassParameter); CheckEqual(-1, template.Parameter); CheckNull(template.Format); CheckEqual(0, template.Parts.Items.Length); CheckTrue(template.HasParameter); CheckFalse(template.HasUnspellable); CheckEqual(1, template.Depth); } public void HasClassParameter_IsTrueWhereverAClassParameterIs() { CheckTrue(SaveTemplate.OfClassParameter(0).HasClassParameter); CheckTrue(ListOf(ListOf(SaveTemplate.OfClassParameter(1))).HasClassParameter); } public void HasClassParameter_IsFalseForMethodParametersAndClosedTypes() { CheckFalse(SaveTemplate.OfParameter(0).HasClassParameter); CheckFalse(ListOf(SaveTemplate.OfParameter(0)).HasClassParameter); CheckFalse(Concrete("global::System.String").HasClassParameter); CheckFalse(SaveTemplate.Unspellable.HasClassParameter); } public void MethodParameter_HasNoClassParameter() { CheckEqual(-1, SaveTemplate.OfParameter(2).ClassParameter); } public void InsideAType_MakesTheTypeHaveAParameter() { CheckTrue(ListOf(SaveTemplate.OfClassParameter(0)).HasParameter); } public void IsNotTheMethodParameterOfTheSameNumber() { CheckFalse(SaveTemplate.OfClassParameter(1).Equals(SaveTemplate.OfParameter(1))); CheckTrue(SaveTemplate.OfClassParameter(1).Equals(SaveTemplate.OfClassParameter(1))); CheckFalse(SaveTemplate.OfClassParameter(1).Equals(SaveTemplate.OfClassParameter(2))); } public void EqualTemplates_HaveEqualHashCodes() { CheckEqual(SaveTemplate.OfClassParameter(3).GetHashCode(), SaveTemplate.OfClassParameter(3).GetHashCode()); } } public class OfParameterTests : TestFixture { public void StandsForItsNumber() { SaveTemplate template = SaveTemplate.OfParameter(3); CheckEqual(3, template.Parameter); CheckNull(template.Format); CheckEqual(0, template.Parts.Items.Length); } } public class StandInNumberTests : TestFixture { public static ITypeSymbol Type(string source, string name) { return VellumRun.Compile(("Source.cs", source)).GetTypeByMetadataName(name); } public void StandInType_IsItsNumber() { CheckEqual(5, SaveTemplate.StandInNumber(Type("", "Vellum.SaveMethodParameter5"))); } public void OtherType_IsMinusOne() { CheckEqual(-1, SaveTemplate.StandInNumber(Type("", "System.String"))); } public void LookalikeInAnotherNamespace_IsMinusOne() { CheckEqual(-1, SaveTemplate.StandInNumber( Type("namespace Elsewhere { public class SaveMethodParameter2 { } }", "Elsewhere.SaveMethodParameter2"))); } public void VellumTypeWithoutANumber_IsMinusOne() { CheckEqual(-1, SaveTemplate.StandInNumber( Type("namespace Vellum { public class SaveMethodParameterFoo { } }", "Vellum.SaveMethodParameterFoo"))); } public static INamedTypeSymbol Pair(CSharpCompilation compilation) { return compilation.GetTypeByMetadataName("Vellum.SaveMethodParameter`2"); } public static ITypeSymbol Digit(CSharpCompilation compilation, int digit) { return compilation.GetTypeByMetadataName("Vellum.SaveMethodParameter" + digit); } public void GenericStandIn_IsItsDigitsReadTogether() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); INamedTypeSymbol pair = Pair(compilation); ITypeSymbol one = Digit(compilation, 1); ITypeSymbol two = Digit(compilation, 2); ITypeSymbol three = Digit(compilation, 3); CheckEqual(12, SaveTemplate.StandInNumber(pair.Construct(one, two))); CheckEqual(123, SaveTemplate.StandInNumber(pair.Construct(pair.Construct(one, two), three))); } public void GenericStandInWhoseLastPartIsNotADigit_IsMinusOne() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); INamedTypeSymbol pair = Pair(compilation); ITypeSymbol one = Digit(compilation, 1); ITypeSymbol text = compilation.GetSpecialType(SpecialType.System_String); CheckEqual(-1, SaveTemplate.StandInNumber(pair.Construct(one, text))); CheckEqual(-1, SaveTemplate.StandInNumber(pair.Construct(one, pair.Construct(one, one)))); } public void GenericStandInWhoseLeadingPartIsNotAStandIn_IsMinusOne() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); CheckEqual(-1, SaveTemplate.StandInNumber(Pair(compilation).Construct( compilation.GetSpecialType(SpecialType.System_String), Digit(compilation, 2)))); } public void NumberWithTooManyDigits_IsMinusOne() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); INamedTypeSymbol pair = Pair(compilation); ITypeSymbol one = Digit(compilation, 1); ITypeSymbol number = one; for (int i = 1; i < SaveTemplate.MaxDigits; ++i) { number = pair.Construct(number, one); } CheckEqual(111111111, SaveTemplate.StandInNumber(number)); CheckEqual(-1, SaveTemplate.StandInNumber(pair.Construct(number, one))); } public void GenericTypeOfAnotherName_IsMinusOne() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); INamedTypeSymbol open = compilation.GetTypeByMetadataName("System.Collections.Generic.Dictionary`2"); ITypeSymbol one = Digit(compilation, 1); CheckEqual(-1, SaveTemplate.StandInNumber(open.Construct(one, one))); } public void ClassStandIn_IsNotAMethodStandIn() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); INamedTypeSymbol wrapper = compilation.GetTypeByMetadataName("Vellum.SaveClassParameter`1"); CheckEqual(-1, SaveTemplate.StandInNumber(wrapper.Construct(Digit(compilation, 2)))); } public void ClassStandIn_IsItsNumber() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); INamedTypeSymbol wrapper = compilation.GetTypeByMetadataName("Vellum.SaveClassParameter`1"); INamedTypeSymbol pair = Pair(compilation); CheckEqual(2, SaveTemplate.ClassStandInNumber(wrapper.Construct(Digit(compilation, 2)))); CheckEqual(12, SaveTemplate.ClassStandInNumber( wrapper.Construct(pair.Construct(Digit(compilation, 1), Digit(compilation, 2))))); } public void ClassStandInOfSomethingElse_IsMinusOne() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); INamedTypeSymbol wrapper = compilation.GetTypeByMetadataName("Vellum.SaveClassParameter`1"); CheckEqual(-1, SaveTemplate.ClassStandInNumber( wrapper.Construct(compilation.GetSpecialType(SpecialType.System_String)))); } public void ClassStandInNumber_OfAMethodStandIn_IsMinusOne() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); CheckEqual(-1, SaveTemplate.ClassStandInNumber(Digit(compilation, 2))); CheckEqual(-1, SaveTemplate.ClassStandInNumber(compilation.GetSpecialType(SpecialType.System_String))); } public void ClassStandInInAnotherNamespace_IsMinusOne() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "namespace Elsewhere { public class SaveClassParameter { } }")); INamedTypeSymbol lookalike = compilation.GetTypeByMetadataName("Elsewhere.SaveClassParameter`1"); CheckEqual(-1, SaveTemplate.ClassStandInNumber(lookalike.Construct(Digit(compilation, 2)))); } public void NotANamedType_IsMinusOne() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); CheckEqual(-1, SaveTemplate.StandInNumber( compilation.CreateArrayTypeSymbol(compilation.GetSpecialType(SpecialType.System_Int32)))); } } public class OfTests : TestFixture { public static CSharpCompilation Compilation() { return VellumRun.Compile(("Source.cs", "")); } public static ITypeParameterSymbol ParameterOf(CSharpCompilation compilation, string type, int index) { return compilation.GetTypeByMetadataName(type).TypeParameters[index]; } public void TypeParameterOfTheHoldingType_IsAClassParameter() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "public class Bag { }")); IMethodSymbol owner = null; SaveTemplate template = SaveTemplate.Of(ParameterOf(compilation, "Bag`2", 1), ref owner); CheckEqual(1, template.ClassParameter); CheckEqual(-1, template.Parameter); CheckNull(owner); } public void TypeParameterOfANestedType_IsNumberedAfterTheOuterTypes() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "public class Outer { public class Inner { public class Deep { } } }")); IMethodSymbol owner = null; CheckEqual(0, SaveTemplate.Of(ParameterOf(compilation, "Outer`2", 0), ref owner).ClassParameter); CheckEqual(1, SaveTemplate.Of(ParameterOf(compilation, "Outer`2", 1), ref owner).ClassParameter); CheckEqual(2, SaveTemplate.Of(ParameterOf(compilation, "Outer`2+Inner`1", 0), ref owner) .ClassParameter); CheckEqual(3, SaveTemplate.Of(ParameterOf(compilation, "Outer`2+Inner`1+Deep`2", 0), ref owner) .ClassParameter); CheckEqual(4, SaveTemplate.Of(ParameterOf(compilation, "Outer`2+Inner`1+Deep`2", 1), ref owner) .ClassParameter); } public void TypeParameterOfAPlainTypesNestedGenericType_IsNumberedFromZero() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "public class Outer { public class Inner { } }")); IMethodSymbol owner = null; CheckEqual(0, SaveTemplate.Of(ParameterOf(compilation, "Outer+Inner`1", 0), ref owner).ClassParameter); } public void ClassParameter_LeavesTheOwnerAlone() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "public class Bag { public void Put() { } }")); IMethodSymbol put = compilation.GetTypeByMetadataName("Bag`1").GetMembers("Put") .OfType().Single(); IMethodSymbol owner = put; SaveTemplate template = SaveTemplate.Of(ParameterOf(compilation, "Bag`1", 0), ref owner); CheckEqual(0, template.ClassParameter); CheckTrue(ReferenceEquals(put, owner)); } public void ClassStandInType_IsAClassParameter() { CSharpCompilation compilation = Compilation(); INamedTypeSymbol wrapper = compilation.GetTypeByMetadataName("Vellum.SaveClassParameter`1"); IMethodSymbol owner = null; SaveTemplate template = SaveTemplate.Of( wrapper.Construct(compilation.GetTypeByMetadataName("Vellum.SaveMethodParameter1")), ref owner); CheckEqual(1, template.ClassParameter); CheckEqual(-1, template.Parameter); CheckNull(owner); } public void ClassStandInInsideAGeneric_IsKept() { CSharpCompilation compilation = Compilation(); INamedTypeSymbol wrapper = compilation.GetTypeByMetadataName("Vellum.SaveClassParameter`1"); INamedTypeSymbol list = compilation.GetTypeByMetadataName("System.Collections.Generic.List`1") .Construct(wrapper.Construct(compilation.GetTypeByMetadataName("Vellum.SaveMethodParameter0"))); IMethodSymbol owner = null; SaveTemplate template = SaveTemplate.Of(list, ref owner); CheckEqual("global::System.Collections.Generic.List>", template.ToSource()); } public void ClosedType_IsSpelledWhole() { CSharpCompilation compilation = Compilation(); IMethodSymbol owner = null; SaveTemplate template = SaveTemplate.Of(compilation.GetSpecialType(SpecialType.System_String), ref owner); CheckEqual("global::System.String", template.ToSource()); CheckFalse(template.HasParameter); CheckNull(owner); } public void StandInType_IsAParameter() { CSharpCompilation compilation = Compilation(); IMethodSymbol owner = null; ITypeSymbol standIn = compilation.GetTypeByMetadataName("Vellum.SaveMethodParameter2"); SaveTemplate template = SaveTemplate.Of(standIn, ref owner); CheckEqual(2, template.Parameter); CheckNull(owner); } public void ErrorType_IsLeftOut() { CSharpCompilation compilation = Compilation(); IMethodSymbol owner = null; CheckNull(SaveTemplate.Of(compilation.CreateErrorTypeSymbol(null, "Missing", 0), ref owner)); } public void PointerType_IsLeftOut() { CSharpCompilation compilation = Compilation(); IMethodSymbol owner = null; CheckNull(SaveTemplate.Of( compilation.CreatePointerTypeSymbol(compilation.GetSpecialType(SpecialType.System_Int32)), ref owner)); } public void GenericArguments_AreSpelledInOrder() { CSharpCompilation compilation = Compilation(); INamedTypeSymbol open = compilation.GetTypeByMetadataName("System.Collections.Generic.Dictionary`2"); INamedTypeSymbol dictionary = open .Construct(compilation.GetSpecialType(SpecialType.System_Int32), compilation.GetSpecialType(SpecialType.System_String)); IMethodSymbol owner = null; SaveTemplate template = SaveTemplate.Of(dictionary, ref owner); CheckEqual("global::System.Collections.Generic.Dictionary", template.ToSource()); } public void ArrayOfArrays_PutsTheOutermostRankFirst() { CSharpCompilation compilation = Compilation(); IArrayTypeSymbol inner = compilation.CreateArrayTypeSymbol( compilation.GetSpecialType(SpecialType.System_Int32), rank: 2); IMethodSymbol owner = null; SaveTemplate template = SaveTemplate.Of(compilation.CreateArrayTypeSymbol(inner, rank: 1), ref owner); CheckEqual("global::System.Int32[][,]", template.ToSource()); } public void TypeNestedInGenericTypes_ListsEachLevelsArguments() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "public class Outer { public class Inner { } }")); INamedTypeSymbol outer = compilation.GetTypeByMetadataName("Outer`1") .Construct(compilation.GetSpecialType(SpecialType.System_Int32)); INamedTypeSymbol closed = outer.GetTypeMembers("Inner").Single() .Construct(compilation.GetSpecialType(SpecialType.System_String)); IMethodSymbol owner = null; SaveTemplate template = SaveTemplate.Of(closed, ref owner); CheckEqual("global::Outer.Inner", template.ToSource()); } public void TypeInTheGlobalNamespace_HasNoNamespacePrefix() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "public class Plain { }")); IMethodSymbol owner = null; CheckEqual("global::Plain", SaveTemplate.Of(compilation.GetTypeByMetadataName("Plain"), ref owner).ToSource()); } public void PrivateType_IsLeftOut() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "public class Outer { private class Hidden { } }")); IMethodSymbol owner = null; CheckNull(SaveTemplate.Of(compilation.GetTypeByMetadataName("Outer+Hidden"), ref owner)); } public void PublicTypeInsideAPrivateOne_IsLeftOut() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "public class Outer { private class Hidden { public class Open { } } }")); IMethodSymbol owner = null; CheckNull(SaveTemplate.Of(compilation.GetTypeByMetadataName("Outer+Hidden+Open"), ref owner)); } public void NestingPastTheLimit_IsLeftOut() { CSharpCompilation compilation = Compilation(); INamedTypeSymbol list = compilation.GetTypeByMetadataName("System.Collections.Generic.List`1"); ITypeSymbol type = compilation.GetSpecialType(SpecialType.System_Int32); for (int i = 0; i < SaveTemplate.MaxDepth; ++i) { type = list.Construct(type); } IMethodSymbol owner = null; CheckNull(SaveTemplate.Of(type, ref owner)); } public void NestingAtTheLimit_IsKept() { CSharpCompilation compilation = Compilation(); INamedTypeSymbol list = compilation.GetTypeByMetadataName("System.Collections.Generic.List`1"); ITypeSymbol type = compilation.GetSpecialType(SpecialType.System_Int32); for (int i = 0; i < SaveTemplate.MaxDepth - 1; ++i) { type = list.Construct(type); } IMethodSymbol owner = null; CheckNotNull(SaveTemplate.Of(type, ref owner)); } } public class FillTests : TestFixture { public static System.Collections.Immutable.ImmutableArray Arguments( params SaveTemplate[] arguments) { return System.Collections.Immutable.ImmutableArray.Create(arguments); } public void Parameter_IsTheArgumentOfItsNumber() { SaveTemplate second = Concrete("global::Second"); CheckEqual(second, SaveTemplate.OfParameter(1).Fill(Arguments(Concrete("global::First"), second))); } public void ParameterWithoutAnArgument_IsNull() { CheckNull(SaveTemplate.OfParameter(1).Fill(Arguments(Concrete("global::First")))); } public void ParameterWhoseArgumentCantBeSpelled_IsNull() { CheckNull(SaveTemplate.OfParameter(0).Fill(Arguments((SaveTemplate)null))); } public void TypeWithoutParameters_IsItself() { SaveTemplate closed = ListOf(Concrete("global::Item")); CheckTrue(ReferenceEquals(closed, closed.Fill(Arguments()))); } public void ClassParameter_IsTheClassArgumentOfItsNumber() { SaveTemplate second = Concrete("global::Second"); SaveTemplate filled = SaveTemplate.OfClassParameter(1).Fill(Arguments(Concrete("global::Method")), Arguments(Concrete("global::First"), second)); CheckEqual(second, filled); } public void ClassParameterWithoutAClassArgument_IsNull() { CheckNull(SaveTemplate.OfClassParameter(0).Fill(Arguments(Concrete("global::Method")), Arguments())); CheckNull(SaveTemplate.OfClassParameter(0).Fill(Arguments(Concrete("global::Method")))); } public void ClassParameterWhoseArgumentCantBeSpelled_IsNull() { CheckNull(SaveTemplate.OfClassParameter(0).Fill(Arguments(), Arguments((SaveTemplate)null))); } public void MethodParameter_IgnoresTheClassArguments() { SaveTemplate filled = SaveTemplate.OfParameter(0).Fill(Arguments(Concrete("global::Method")), Arguments(Concrete("global::Class"))); CheckEqual("global::Method", filled.ToSource()); } public void ClassAndMethodParametersTogether_AreFilledFromTheirOwnLists() { SaveTemplate template = Concrete("global::Pair<{0}, {1}>", SaveTemplate.OfClassParameter(0), SaveTemplate.OfParameter(0)); SaveTemplate filled = template.Fill(Arguments(Concrete("global::Method")), Arguments(Concrete("global::Class"))); CheckEqual("global::Pair", filled.ToSource()); CheckFalse(filled.HasParameter); } public void ParameterInsideAType_IsReplacedInPlace() { SaveTemplate template = Concrete("global::Pair<{0}, {1}>", ListOf(SaveTemplate.OfParameter(0)), SaveTemplate.OfParameter(1)); SaveTemplate filled = template.Fill(Arguments(Concrete("global::A"), Concrete("global::B"))); CheckEqual("global::Pair, global::B>", filled.ToSource()); CheckFalse(filled.HasParameter); } public void ParameterInsideATypeWithoutAnArgument_IsNull() { CheckNull(ListOf(SaveTemplate.OfParameter(2)).Fill(Arguments(Concrete("global::A")))); } public void NullPart_IsNull() { CheckNull(Concrete("global::Pair<{0}, {1}>", SaveTemplate.OfParameter(0), null) .Fill(Arguments(Concrete("global::A")))); } public void ArgumentThatUsesParameters_KeepsThem() { SaveTemplate filled = ListOf(SaveTemplate.OfParameter(0)) .Fill(Arguments(ListOf(SaveTemplate.OfParameter(3)))); CheckTrue(filled.HasParameter); CheckEqual(3, filled.Depth); } } public class DepthTests : TestFixture { public void Parameter_IsOneDeep() { CheckEqual(1, SaveTemplate.OfParameter(0).Depth); } public void Nesting_CountsEachType() { CheckEqual(3, ListOf(ListOf(SaveTemplate.OfParameter(0))).Depth); } public void DeepestPart_Wins() { CheckEqual(3, Concrete("global::Pair<{0}, {1}>", Concrete("global::A"), ListOf(Concrete("global::B"))).Depth); } } public class RemapClassParametersTests : TestFixture { public static ImmutableArray Map(params int[] numbers) { return ImmutableArray.Create(numbers); } public void ClassParameter_TakesItsNewNumber() { CheckEqual(SaveTemplate.OfClassParameter(0), SaveTemplate.OfClassParameter(1).RemapClassParameters(Map(-1, 0))); } public void ClassParametersCanSwap() { SaveTemplate pair = Concrete("global::Pair<{0}, {1}>", SaveTemplate.OfClassParameter(0), SaveTemplate.OfClassParameter(1)); CheckEqual("global::Pair<" + ClassMarker(1) + ", " + ClassMarker(0) + ">", pair.RemapClassParameters(Map(1, 0)).ToSource()); } public static string ClassMarker(int number) { return SaveTemplate.OfClassParameter(number).ToSource(); } public void ClassParameterWithNoNewNumber_IsNull() { CheckNull(SaveTemplate.OfClassParameter(0).RemapClassParameters(Map(-1))); } public void ClassParameterPastTheMap_IsNull() { CheckNull(SaveTemplate.OfClassParameter(2).RemapClassParameters(Map(0, 1))); CheckNull(SaveTemplate.OfClassParameter(0).RemapClassParameters(ImmutableArray.Empty)); } public void ClassParameterInsideATypeWithNoNewNumber_IsNull() { CheckNull(ListOf(ListOf(SaveTemplate.OfClassParameter(1))).RemapClassParameters(Map(0, -1))); } public void MethodParameters_AreLeftAlone() { SaveTemplate pair = Concrete("global::Pair<{0}, {1}>", SaveTemplate.OfClassParameter(1), SaveTemplate.OfParameter(1)); CheckEqual("global::Pair<" + ClassMarker(0) + ", " + SaveTemplate.OfParameter(1).ToSource() + ">", pair.RemapClassParameters(Map(-1, 0)).ToSource()); } public void TemplateWithNoClassParameters_IsItself() { SaveTemplate template = ListOf(SaveTemplate.OfParameter(0)); CheckTrue(ReferenceEquals(template, template.RemapClassParameters(ImmutableArray.Empty))); CheckTrue(ReferenceEquals(SaveTemplate.Unspellable, SaveTemplate.Unspellable.RemapClassParameters(ImmutableArray.Empty))); } public void NullPart_IsNull() { CheckNull(Concrete("global::Pair<{0}, {1}>", SaveTemplate.OfClassParameter(0), null) .RemapClassParameters(Map(0))); } } public class UnspellableTests : TestFixture { public void Alone_HasNoParameterButIsUnspellable() { CheckTrue(SaveTemplate.Unspellable.HasUnspellable); CheckFalse(SaveTemplate.Unspellable.HasParameter); } public void Parameter_IsNotUnspellable() { CheckFalse(SaveTemplate.OfParameter(0).HasUnspellable); } public void InsideAType_MakesTheTypeUnspellable() { CheckTrue(ListOf(ListOf(SaveTemplate.Unspellable)).HasUnspellable); CheckFalse(ListOf(SaveTemplate.OfParameter(0)).HasUnspellable); } public void Source_Throws() { CheckThrow(typeof(System.InvalidOperationException)); SaveTemplate.Unspellable.ToSource(); } public void SourceOfATypeHoldingOne_Throws() { CheckThrow(typeof(System.InvalidOperationException)); ListOf(SaveTemplate.Unspellable).ToSource(); } public void FilledInto_MakesTheResultUnspellable() { SaveTemplate filled = ListOf(SaveTemplate.OfParameter(0)).Fill( System.Collections.Immutable.ImmutableArray.Create(SaveTemplate.Unspellable)); CheckTrue(filled.HasUnspellable); } public void Equals_IsByValue() { CheckTrue(SaveTemplate.Unspellable.Equals( new SaveTemplate(SaveTemplate.UnspellableParameter, null, default))); CheckFalse(SaveTemplate.Unspellable.Equals(SaveTemplate.OfParameter(0))); } } public class MaxDepthTests : TestFixture { public void IsTheDepthASavedValueCanNest() { CheckEqual(SaveNode.MaxDepth, SaveTemplate.MaxDepth); CheckEqual(TypeShape.MaxDepth, SaveTemplate.MaxDepth); } } public class ToSourceTests : TestFixture { public void Parameter_IsItsStandIn() { CheckEqual("global::Vellum.SaveMethodParameter4", SaveTemplate.OfParameter(4).ToSource()); } public void ParameterPastTheNinth_IsItsDigitsWrapped() { CheckEqual("global::Vellum.SaveMethodParameter", SaveTemplate.OfParameter(12).ToSource()); CheckEqual("global::Vellum.SaveMethodParameter, " + "global::Vellum.SaveMethodParameter0>", SaveTemplate.OfParameter(100).ToSource()); } public void ClassParameter_IsItsStandInWrapped() { CheckEqual("global::Vellum.SaveClassParameter", SaveTemplate.OfClassParameter(3).ToSource()); } public void ClassParameterPastTheNinth_IsItsDigitsWrapped() { CheckEqual("global::Vellum.SaveClassParameter>", SaveTemplate.OfClassParameter(12).ToSource()); } public void ClosedType_IsItsFormat() { CheckEqual("global::System.String", Concrete("global::System.String").ToSource()); } public void Parts_FillTheirSlots() { SaveTemplate template = Concrete("global::Pair<{0}, {1}>", ListOf(SaveTemplate.OfParameter(0)), Concrete("global::System.String")); CheckEqual("global::Pair, global::System.String>", template.ToSource()); } } public class EqualsTests : TestFixture { public const string ListFormat = "global::System.Collections.Generic.List<{0}>"; public static SaveTemplate Variant(int parameter = -1, string format = ListFormat, int part = 0) { return new SaveTemplate(parameter, format, new EquatableArray( System.Collections.Immutable.ImmutableArray.Create(SaveTemplate.OfParameter(part)))); } public void SameValues_AreEqual() { CheckTrue(Variant().Equals(Variant())); CheckTrue(Variant().Equals((object)Variant())); } public void Null_IsNotEqual() { CheckFalse(Variant().Equals((SaveTemplate)null)); CheckFalse(Variant().Equals((object)null)); } public void DifferentParameter_IsNotEqual() { CheckFalse(Variant().Equals(Variant(parameter: 1))); } public void DifferentFormat_IsNotEqual() { CheckFalse(Variant().Equals(Variant(format: "global::System.Collections.Generic.HashSet<{0}>"))); } public void DifferentParts_AreNotEqual() { CheckFalse(Variant().Equals(Variant(part: 1))); } public void NoFormat_IsComparable() { CheckTrue(SaveTemplate.OfParameter(1).Equals(SaveTemplate.OfParameter(1))); CheckFalse(SaveTemplate.OfParameter(1).Equals(Variant())); } } public class GetHashCodeTests : TestFixture { public void EqualTemplates_HaveEqualHashCodes() { CheckEqual(ListOf(SaveTemplate.OfParameter(0)).GetHashCode(), ListOf(SaveTemplate.OfParameter(0)).GetHashCode()); CheckEqual(SaveTemplate.OfParameter(2).GetHashCode(), SaveTemplate.OfParameter(2).GetHashCode()); } } } }