using System.Collections.Generic; using System.Linq; using Microsoft.CodeAnalysis; using UnitTestSharp; namespace Ariadne.Generator.UnitTests { public class SavedFieldsTests : TestFixture { public static List MembersOf(string typeName, params (string Path, string Script)[] files) { return SavedFields.Of(GeneratorRun.Compile(files).GetTypeByMetadataName(typeName)); } public static List KeysOf(string typeName, string script) { return MembersOf(typeName, ("Script.cs", script)).Select(member => member.Key).ToList(); } public class IdentifierTests : TestFixture { public void Keyword_GetsAnAt() { CheckEqual("@event", SavedFields.Identifier("event")); } public void OtherNames_AreUnchanged() { CheckEqual("count", SavedFields.Identifier("count")); CheckEqual("value", SavedFields.Identifier("value")); } } public class OfTests : TestFixture { public void WritableFieldsAndAutoProperties_AreSavedInDeclarationOrder() { List keys = KeysOf("Kinds", @" public abstract class Kinds : Arc { public int Plain; private string hidden; public int Auto { get; set; } public int Guarded { get; private set; } }"); CheckEqual(new[] { "Plain", "hidden", "Auto", "Guarded" }, keys); } public void ConstAndStatic_AreNotSaved() { List keys = KeysOf("Fixed", @" public abstract class Fixed : Arc { public const int Constant = 1; public static int Shared; public static readonly int SharedFrozen = 2; }"); CheckEqual(0, keys.Count); } public void ReadonlyFieldsAndGetOnlyAutoProperties_AreSaved() { List keys = KeysOf("Frozen", @" public abstract class Frozen : Arc { public readonly int Field = 2; public int GetOnly { get; } = 3; public int InitOnly { get; init; } }"); CheckEqual(new[] { "Field", "GetOnly", "InitOnly" }, keys); } public void GetOnlyPropertyThatComputesItsValue_IsNotSaved() { List keys = KeysOf("Computed", @" public abstract class Computed : Arc { private int health; public bool Alive => health >= 0; public int Doubled { get { return health * 2; } } }"); CheckEqual(new[] { "health" }, keys); } public void Scratch_LeavesOutAFieldOrAutoProperty() { List keys = KeysOf("Scratched", @" public abstract class Scratched : Arc { [Scratch] public int Field; [Scratch] public int Property { get; set; } [field: Scratch] public int BackingField { get; set; } public int Kept; }"); CheckEqual(new[] { "Kept" }, keys); } public void PropertyWithItsOwnBody_IsSavedThroughItsField() { List keys = KeysOf("Bodied", @" public abstract class Bodied : Arc { private int count; public int Count { get { return count; } set { count = value; } } }"); CheckEqual(new[] { "count" }, keys); } public void FieldLikeEvent_IsNotSaved() { List keys = KeysOf("Eventful", @" public abstract class Eventful : Arc { public event System.Action Changed; }"); CheckEqual(0, keys.Count); } public void PartsInTwoFiles_AreOrderedByFileThenPosition() { List members = MembersOf("Split", ("B.cs", "public abstract partial class Split : Arc { public int Third; }"), ("A.cs", "public abstract partial class Split { public int First; public int Second; }")); CheckEqual(new[] { "First", "Second", "Third" }, members.Select(member => member.Key)); } public void MemberType_IsTheDeclaredType() { List members = MembersOf("Typed", ("Script.cs", "public abstract class Typed : Arc { public IList Visits = new List(); }")); CheckEqual("System.Collections.Generic.IList", members.Single().Type.ToDisplayString()); } } public class ReportDuplicateKeysTests : TestFixture { public void FieldHidingABaseClassField_IsReported() { GeneratorRun run = GeneratorRun.Of(@" public abstract partial class Mission : Arc { public int Stage; } public partial class Derived : Mission { public new int Stage; protected override void Run(World world) { } }"); CheckEqual(new[] { "ARI0019:Stage" }, run.Reported); } public void AutoPropertyHidingABaseClassFieldFromAnotherAssembly_IsReported() { MetadataReference missions = GeneratorRun.CompileLibrary("Missions", "public abstract class Mission : Arc { public int Stage; }"); GeneratorRun run = GeneratorRun.Of(missions, @" public partial class Derived : Mission { public new int Stage { get; set; } protected override void Run(string context) { } }"); CheckEqual(new[] { "ARI0019:Stage" }, run.Reported); } public void ScratchMemberHidingABaseClassField_IsAllowed() { GeneratorRun run = GeneratorRun.Of(@" public abstract partial class Mission : Arc { public int Stage; } public partial class Derived : Mission { [Scratch] public new int Stage; protected override void Run(World world) { } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); } } public class ReportUnsavableEventsTests : TestFixture { public void FieldLikeEvent_IsReported() { GeneratorRun run = GeneratorRun.Of(@" public partial class Eventful : Arc { public event System.Action Changed; protected override void Run(World world) { } }"); CheckEqual(new[] { "ARI0035:Changed" }, run.Reported); } public void ScratchFieldLikeEvent_IsAllowed() { GeneratorRun run = GeneratorRun.Of(@" public partial class Eventful : Arc { [Scratch] public event System.Action Changed; protected override void Run(World world) { Changed?.Invoke(); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); } public void EventWithItsOwnAccessors_IsAllowed() { GeneratorRun run = GeneratorRun.Of(@" public partial class Eventful : Arc { [Scratch] private System.Action changed; public event System.Action Changed { add { changed += value; } remove { changed -= value; } } protected override void Run(World world) { } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); } public void StaticFieldLikeEvent_IsAllowed() { GeneratorRun run = GeneratorRun.Of(@" public partial class Eventful : Arc { public static event System.Action Changed; protected override void Run(World world) { Changed?.Invoke(); } }"); CheckEqual(0, run.Reported.Count); CheckEqual(0, run.Compiled.Count); } public void EventOnABaseClass_IsReportedOnlyOnTheBase() { GeneratorRun run = GeneratorRun.Of(@" public abstract partial class Mission : Arc { public event System.Action Changed; } public partial class Derived : Mission { protected override void Run(World world) { } }"); CheckEqual(new[] { "ARI0035:Changed" }, run.Reported); } public void ClassWritingItsOwnSaveFields_IsLeftAlone() { GeneratorRun run = GeneratorRun.Of(@" public abstract class Mission : Arc { public event System.Action Changed; protected override void __SaveFields(IArcWriter writer) { } }"); CheckEqual(0, run.Reported.Count); } } } public class SavedMemberTests : TestFixture { public class AccessTests : TestFixture { public void ReadsTheMemberThroughThis() { SavedMember member = SavedFieldsTests.MembersOf("Named", ("Script.cs", "public abstract class Named : Arc { public int Count; }")).Single(); CheckEqual("this.Count", member.Access); } public void KeywordName_GetsAnAt() { SavedMember member = SavedFieldsTests.MembersOf("Keyworded", ("Script.cs", "public abstract class Keyworded : Arc { public int @event; }")).Single(); CheckEqual("event", member.Key); CheckEqual("this.@event", member.Access); } } } }