using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp; using UnitTestSharp; namespace Vellum.Generator.UnitTests { public class SaveableChecksTests : TestFixture { /// /// Returns checks that differ from the default ones only in the arguments given. /// public static SaveableChecks Variant(string name = "A", string convertedName = "Ns.A", bool generates = true, SaveableProblem problem = SaveableProblem.None, SourcePlace problemPlace = null, bool inheritsMark = false, MemberCheck[] members = null) { return new SaveableChecks(name, convertedName, generates, problem, problemPlace ?? SourcePlaceTests.Variant(), inheritsMark, new EquatableArray((members ?? new[] { MemberCheckTests.Variant() }).ToImmutableArray())); } public class CtorTests : TestFixture { public void KeepsEveryArgument() { SaveableChecks checks = Variant(generates: false, problem: SaveableProblem.PublicLoader, inheritsMark: true); CheckEqual("A", checks.Name); CheckEqual("Ns.A", checks.ConvertedName); CheckFalse(checks.Generates); CheckEqual(SaveableProblem.PublicLoader, checks.Problem); CheckEqual(SourcePlaceTests.Variant(), checks.ProblemPlace); CheckTrue(checks.InheritsMark); CheckEqual(MemberCheckTests.Variant(), checks.Members.Items.Single()); } } public class CheckTests : TestFixture { public static List CheckedDiagnostics(string metadataName, string source) { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", source)); SaveableChecks checks = SaveableClass.Describe(compilation.GetTypeByMetadataName(metadataName)).Checks; ConverterList converters = VellumRun.Converters(compilation); return checks.Check(compilation, converters, converters.ConvertedNames()); } public static List Checked(string metadataName, string source) { return CheckedDiagnostics(metadataName, source).Select(VellumRun.Describe).ToList(); } public void ValidType_HasNothingToReport() { CheckEqual(0, Checked("Point", "[Saveable] public partial struct Point { public int X; }").Count); } public void UnsupportedShape_IsReportedAtTheTypesName() { CheckEqual(new[] { "VEL0005:Whole" }, Checked("Whole", "[Saveable] public struct Whole { }")); } public void NonPrivateOwnLoader_IsReportedAtTheLoader() { CheckEqual(new[] { "VEL0006:Open" }, Checked("Open", "[Saveable] public partial struct Open { public Open(SaveReader reader) { } }")); } public void SubclassThatIsntPartial_IsReportedWithWhyItIsSaveable() { List found = CheckedDiagnostics("Dog", "[Saveable] public partial class Animal { }\npublic class Dog : Animal { }"); CheckEqual(1, found.Count); CheckEqual("VEL0005", found[0].Id); CheckTrue(found[0].GetMessage().Contains("because a class it derives from, or an interface it " + "implements, is")); } public void ImplementerWhoseBaseIsntSaveable_IsReportedWithWhyItIsSaveable() { List found = CheckedDiagnostics("Mission", "[Saveable] public interface IPet { }\n" + "public class Framework { }\npublic partial class Mission : Framework, IPet { }"); CheckEqual(1, found.Count); CheckEqual("VEL0005", found[0].Id); CheckTrue(found[0].GetMessage().Contains("interface it implements")); } public void MarkedClassThatIsntPartial_IsReportedWithoutThatReason() { List found = CheckedDiagnostics("Open", "[Saveable] public class Open { }"); CheckFalse(found[0].GetMessage().Contains("because")); } public void MemberNamedForAKeyVellumUses_IsReportedWhereItIsDeclared() { CheckEqual(new[] { "VEL0008:__type", "VEL0008:__value" }, Checked("Tagged", "[Saveable] public sealed partial class Tagged { public int __type; public int __value; }")); } public void MemberSavedUnderAKeyABaseClassUses_IsReportedWhereItIsDeclared() { CheckEqual(new[] { "VEL0009:Name" }, Checked("Dog", "[Saveable] public partial class Animal { public string Name; }\n" + "[Saveable] public sealed partial class Dog : Animal { public new string Name; }")); } public void MemberOfASubclass_ThatNoBaseSaves_IsNotReported() { CheckEqual(0, Checked("Dog", "[Saveable] public partial class Animal { public string Name; }\n" + "[Saveable] public sealed partial class Dog : Animal { public bool Barks; }").Count); } public void HandWrittenConverterOfAGeneratedType_IsReportedAtTheConverter() { CheckEqual(new[] { "VEL0002:PointConverter" }, Checked("Point", "[Saveable] public partial struct Point { }\n" + ConverterScanTests.Converter("PointConverter", "Point"))); } public void HandWrittenConverterOfATypeThatAlreadyHasOne_IsNotReported() { CheckEqual(0, Checked("Point", "[Saveable] public partial struct Point { public class __Converter { } }\n" + ConverterScanTests.Converter("PointConverter", "Point")).Count); } public void MemberThatCantBeSaved_IsReportedWhereItsDeclared() { CheckEqual(new[] { "VEL0004:Stops" }, Checked("Route", "[Saveable] public partial struct Route { public int Length; public object Stops; }")); } public void MemberWithAConverter_IsAllowed() { CheckEqual(0, Checked("Route", ConverterScanTests.Point + ConverterScanTests.Converter("PointConverter", "Point") + "[Saveable] public partial struct Route { public Point Start; public Point? End; }").Count); } public void MembersOfAnUnsupportedShape_AreStillChecked() { CheckEqual(new[] { "VEL0005:Whole", "VEL0004:Anything" }, Checked("Whole", "[Saveable] public struct Whole { public object Anything; }")); } } public class EqualsTests : TestFixture { public void SameValues_AreEqual() { CheckTrue(Variant().Equals(Variant())); CheckTrue(Variant().Equals((object)Variant())); } public void Null_IsNotEqual() { CheckFalse(Variant().Equals((SaveableChecks)null)); CheckFalse(Variant().Equals((object)null)); } public void OtherType_IsNotEqual() { CheckFalse(Variant().Equals("A")); } public void DifferentName_IsNotEqual() { CheckFalse(Variant().Equals(Variant(name: "B"))); } public void DifferentConvertedName_IsNotEqual() { CheckFalse(Variant().Equals(Variant(convertedName: "Ns.B"))); } public void DifferentGenerates_IsNotEqual() { CheckFalse(Variant().Equals(Variant(generates: false))); } public void DifferentProblem_IsNotEqual() { CheckFalse(Variant().Equals(Variant(problem: SaveableProblem.Unsupported))); } public void DifferentProblemPlace_IsNotEqual() { CheckFalse(Variant().Equals(Variant(problemPlace: SourcePlaceTests.Variant(start: 2)))); } public void DifferentInheritsMark_IsNotEqual() { CheckFalse(Variant().Equals(Variant(inheritsMark: true))); } public void DifferentMembers_AreNotEqual() { CheckFalse(Variant().Equals(Variant(members: new[] { MemberCheckTests.Variant(key: "Total") }))); } } public class GetHashCodeTests : TestFixture { public void EqualChecks_HaveEqualHashCodes() { CheckEqual(Variant().GetHashCode(), Variant().GetHashCode()); } } } }