using System.Threading.Tasks; using Microsoft.CodeAnalysis.CSharp.Testing; using Microsoft.CodeAnalysis.Testing; using NUnit.Framework; using VerifyCS = Microsoft.CodeAnalysis.CSharp.Testing.CSharpAnalyzerVerifier; namespace Robust.Analyzers.Tests; [Parallelizable(ParallelScope.All | ParallelScope.Fixtures)] [TestFixture, TestOf(typeof(AfterAutoHandleStateAnalyzer))] public sealed class AfterAutoHandleStateAnalyzerTest { private const string SubscribeEventDef = """ using System; namespace Robust.Shared.GameObjects; public readonly struct EntityUid; public abstract class EntitySystem { public void SubscribeLocalEvent() where TEvent : notnull { } } public interface IComponent; public interface IComponentState; """; // A rare case for block-scoped namespace, I thought. Then I realized this // only needed the one type definition. private const string OtherTypeDefs = """ using System; namespace JetBrains.Annotations { public sealed class BaseTypeRequiredAttribute(Type baseType) : Attribute; } """; private static Task Verifier(string code, params DiagnosticResult[] expected) { var test = new CSharpAnalyzerTest { TestState = { Sources = { code } } }; TestHelper.AddEmbeddedSources(test.TestState, "Robust.Shared.Analyzers.ComponentNetworkGeneratorAuxiliary.cs", "Robust.Shared.GameObjects.EventBusAttributes.cs"); test.TestState.Sources.Add(("EntitySystem.Subscriptions.cs", SubscribeEventDef)); test.TestState.Sources.Add(("Types.cs", OtherTypeDefs)); test.TestState.ExpectedDiagnostics.AddRange(expected); return test.RunAsync(); } [Test] public async Task Test() { const string code = """ using Robust.Shared.Analyzers; using Robust.Shared.GameObjects; [AutoGenerateComponentState(true)] public sealed class AutoGenTrue; [AutoGenerateComponentState(true, true)] public sealed class AutoGenTrueTrue; public sealed class NotAutoGen; [AutoGenerateComponentState] public sealed class AutoGenNoArgs; [AutoGenerateComponentState(false)] public sealed class AutoGenFalse; public sealed class Foo : EntitySystem { public void Good() { // Subscribing to other events works SubscribeLocalEvent(); // First arg true allows subscribing SubscribeLocalEvent(); SubscribeLocalEvent(); } public void Bad() { // Can't subscribe if AutoGenerateComponentState isn't even present SubscribeLocalEvent(); // Can't subscribe if first arg is not specified/false SubscribeLocalEvent(); SubscribeLocalEvent(); } } """; await Verifier(code, // /0/Test0.cs(29,9): error RA0040: Tried to subscribe to AfterAutoHandleStateEvent for 'NotAutoGen' which doesn't have an AutoGenerateComponentState attribute VerifyCS.Diagnostic(AfterAutoHandleStateAnalyzer.MissingAttribute).WithSpan(29, 9, 29, 69).WithArguments("NotAutoGen"), // /0/Test0.cs(32,9): error RA0041: Tried to subscribe to AfterAutoHandleStateEvent for 'AutoGenNoArgs' which doesn't have raiseAfterAutoHandleState set VerifyCS.Diagnostic(AfterAutoHandleStateAnalyzer.MissingAttributeParam).WithSpan(32, 9, 32, 72).WithArguments("AutoGenNoArgs"), // /0/Test0.cs(33,9): error RA0041: Tried to subscribe to AfterAutoHandleStateEvent for 'AutoGenFalse' which doesn't have raiseAfterAutoHandleState set VerifyCS.Diagnostic(AfterAutoHandleStateAnalyzer.MissingAttributeParam).WithSpan(33, 9, 33, 71).WithArguments("AutoGenFalse") ); } }