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 FamilyMemberTests : TestFixture { public static EquatableArray Names(params string[] names) { return new EquatableArray(names.ToImmutableArray()); } public static FamilyMember Variant(string name = "Wolf", string[] classNames = null, string[] interfaceNames = null, string[] repeatedInterfaces = null, SourcePlace place = null) { return new FamilyMember(name, Names(classNames ?? new[] { "Wolf" }), Names(interfaceNames ?? new[] { "IPet" }), Names(repeatedInterfaces ?? new string[0]), place ?? SourcePlaceTests.Variant()); } public class CtorTests : TestFixture { public void KeepsEveryArgument() { FamilyMember member = Variant(repeatedInterfaces: new[] { "IBowl" }); CheckEqual("Wolf", member.Name); CheckEqual(new[] { "Wolf" }, member.ClassNames.Items.ToArray()); CheckEqual(new[] { "IPet" }, member.InterfaceNames.Items.ToArray()); CheckEqual(new[] { "IBowl" }, member.RepeatedInterfaces.Items.ToArray()); CheckEqual(SourcePlaceTests.Variant(), member.Place); } } public class OfTests : TestFixture { public static FamilyMember Member(string metadataName, string source) { return FamilyMember.Of(SaveableClassTests.TypeOf(metadataName, ConverterScanTests.DescribeTests.Pets + source)); } public void ClassImplementingInterfaces_ListsThemAndItsClasses() { FamilyMember member = Member("Wolf", "public class Animal { }\npublic class Wolf : Animal, IDog { }"); CheckEqual("Wolf", member.Name); CheckEqual(new[] { "Wolf", "Animal" }, member.ClassNames.Items.ToArray()); CheckEqual(new[] { "IDog", "IPet" }, member.InterfaceNames.Items.ToArray()); } public void StructImplementingAnInterface_IsListed() { CheckEqual(new[] { "IPet" }, Member("Puppy", "public struct Puppy : IPet { }") .InterfaceNames.Items.ToArray()); } public void GenericInterfaceImplementedTwice_IsListedAsRepeated() { FamilyMember member = Member("Kennel", "public class Kennel : IBowl, IBowl { }"); CheckEqual(new[] { "IBowl" }, member.RepeatedInterfaces.Items.ToArray()); } public void TypeImplementingNothing_HasNothingToCheck() { CheckNull(Member("Plain", "public class Plain { }")); } public void AbstractClassAndInterface_HaveNothingToCheck() { CheckNull(Member("Base", "public abstract class Base : IPet { }")); CheckNull(Member("IDog", "")); } public void Class_IsPlacedAtItsName() { var type = SaveableClassTests.TypeOf("Wolf", ConverterScanTests.DescribeTests.Pets + "public class Wolf : IPet { }"); FamilyMember member = FamilyMember.Of(type); CheckEqual("Wolf", type.Locations[0].SourceTree.GetText().ToString(member.Place.Span)); } } public class CheckTests : TestFixture { public static readonly Dictionary> Interfaces = new Dictionary> { { "IPet", Names() }, { "IDog", Names("IPet") }, { "IAquatic", Names() }, }; public static Dictionary> Families(params string[] bases) { return bases.ToDictionary(name => name, name => default(EquatableArray)); } public static List Checked(FamilyMember member, Dictionary> families) { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); Dictionary> interfaceFamilies = Interfaces .Where(pair => families.ContainsKey(pair.Key)).ToDictionary(pair => pair.Key, pair => pair.Value); return member.Check(compilation, families, interfaceFamilies).Select(diagnostic => diagnostic.Id) .ToList(); } public void TwoUnrelatedInterfaceFamilies_AreAmbiguous() { FamilyMember member = Variant(interfaceNames: new[] { "IPet", "IAquatic" }); CheckEqual(new[] { "VEL0014" }, Checked(member, Families("IPet", "IAquatic"))); } public void MessageNamesTheTypeAndBothInterfaces() { CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); FamilyMember member = Variant(interfaceNames: new[] { "IPet", "IAquatic" }); string message = member.Check(compilation, Families("IPet", "IAquatic"), Interfaces) .Single().GetMessage(); CheckTrue(message.StartsWith("'Wolf' implements IPet and IAquatic")); } public void MoreDerivedInterfaceFamily_OverridesTheOneItExtends() { FamilyMember member = Variant(interfaceNames: new[] { "IDog", "IPet" }); CheckEqual(0, Checked(member, Families("IPet", "IDog")).Count); } public void OnlyOneOfTheInterfacesHavingAFamily_IsFine() { FamilyMember member = Variant(interfaceNames: new[] { "IPet", "IAquatic" }); CheckEqual(0, Checked(member, Families("IPet")).Count); } public void ClassFamily_SavesTheTypeBeforeAnyInterfaceFamily() { FamilyMember member = Variant(interfaceNames: new[] { "IPet", "IAquatic" }); CheckEqual(0, Checked(member, Families("IPet", "IAquatic", "Wolf")).Count); } public void GenericInterfaceImplementedTwiceByAFamily_IsReported() { var interfaces = new Dictionary> { { "IBowl", Names() } }; CSharpCompilation compilation = VellumRun.Compile(("Source.cs", "")); FamilyMember member = Variant(interfaceNames: new[] { "IBowl" }, repeatedInterfaces: new[] { "IBowl" }); CheckEqual(new[] { "VEL0015" }, member.Check(compilation, Families("IBowl"), interfaces) .Select(diagnostic => diagnostic.Id).ToList()); } public void GenericInterfaceImplementedTwiceWithNoFamily_IsFine() { FamilyMember member = Variant(interfaceNames: new[] { "IBowl" }, repeatedInterfaces: new[] { "IBowl" }); CheckEqual(0, Checked(member, Families()).Count); } } public class ReferencedTests : TestFixture { public const string Interfaces = "public interface IPet { }\npublic interface IAquatic { }\n"; public static ConverterList Families(string assembly, params string[] interfaces) { ConverterModel[] models = interfaces.Select(name => ConverterModelTests.Variant(name: name + "Family", familyBase: name, familyIsInterface: true, familyBaseAssembly: assembly)).ToArray(); return new ConverterList(new EquatableArray(models.ToImmutableArray()), new EquatableArray()); } public static List Names(string animals, ConverterList families) { MetadataReference pets = VellumRun.CompileLibrary("Pets", Interfaces + "public class Eel : IPet { }"); MetadataReference zoo = VellumRun.CompileLibrary("Zoo", animals, runGenerator: true, libraries: new[] { pets }); CSharpCompilation compilation = VellumRun.Compile(new[] { pets, zoo }, ("Source.cs", "")); return FamilyMember.Referenced(compilation, families).Items.Select(member => member.Name) .OrderBy(name => name, System.StringComparer.Ordinal).ToList(); } public void TypesImplementingAFamilysInterface_AreFound() { CheckEqual(new[] { "Eel", "Goldfish", "Lynx" }, Names("public class Goldfish : IPet, IAquatic { }\n" + "public struct Lynx : IPet { }", Families("Pets", "IPet", "IAquatic"))); } public void TypeImplementingNoFamilysInterface_IsLeftOut() { CheckEqual(new[] { "Eel" }, Names("public class Plain { }\npublic class Disposable : " + "System.IDisposable { public void Dispose() { } }", Families("Pets", "IPet"))); } public void AbstractTypeAndInterface_AreLeftOut() { CheckEqual(new[] { "Eel" }, Names("public abstract class Base : IPet { }\n" + "public interface IDog : IPet { }", Families("Pets", "IPet"))); } public void TypesInNestedNamespacesAndNestedTypes_AreFound() { CheckEqual(new[] { "Eel", "Inner", "Koi" }, Names("namespace Pond.Deep { public class Koi : IPet { } }\n" + "public abstract class Outer { public class Inner : IPet { } }", Families("Pets", "IPet"))); } public void NoInterfaceFamilies_FindsNothing() { CheckEqual(0, Names("public class Goldfish : IPet { }", Families("Pets")).Count); } public void AssembliesThatNeverReferenceTheInterfacesAssembly_AreNotScanned() { CheckEqual(0, Names("public class Goldfish : IPet { }", Families("Elsewhere", "IPet")).Count); } public void FoundType_HasNoLocation() { MetadataReference pets = VellumRun.CompileLibrary("Pets", Interfaces + "public class Eel : IPet { }"); CSharpCompilation compilation = VellumRun.Compile(new[] { pets }, ("Source.cs", "")); CheckEqual(LocationKind.None, FamilyMember.Referenced(compilation, Families("Pets", "IPet")) .Items.Single().Place.LocationIn(compilation).Kind); } } public class EqualsTests : TestFixture { public void SameValues_AreEqual() { CheckTrue(Variant().Equals(Variant())); CheckTrue(Variant().Equals((object)Variant())); } public void Null_IsNotEqual() { CheckFalse(Variant().Equals((FamilyMember)null)); CheckFalse(Variant().Equals((object)null)); } public void DifferentName_IsNotEqual() { CheckFalse(Variant().Equals(Variant(name: "Other"))); } public void DifferentClassNames_AreNotEqual() { CheckFalse(Variant().Equals(Variant(classNames: new[] { "Other" }))); } public void DifferentInterfaceNames_AreNotEqual() { CheckFalse(Variant().Equals(Variant(interfaceNames: new[] { "IOther" }))); } public void DifferentRepeatedInterfaces_AreNotEqual() { CheckFalse(Variant().Equals(Variant(repeatedInterfaces: new[] { "IPet" }))); } public void DifferentPlace_IsNotEqual() { CheckFalse(Variant().Equals(Variant(place: SourcePlaceTests.Variant(start: 2)))); } } public class GetHashCodeTests : TestFixture { public void EqualMembers_HaveEqualHashCodes() { CheckEqual(Variant().GetHashCode(), Variant().GetHashCode()); } } } }