From d2c96c86bf30873991c280c1d781ec82cd45bcb5 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Wed, 7 Sep 2022 02:40:47 +0700 Subject: [PATCH 1/9] Add Akka.Hosting.TestKit --- Akka.Hosting.sln | 12 + .../Akka.Hosting.TestKit.Tests.csproj | 18 + .../HostingSpecSpec.cs | 47 ++ .../NoImplicitSenderSpec.cs | 77 +++ .../TestActorRefTests/BossActor.cs | 67 +++ .../TestActorRefTests/FsmActor.cs | 57 +++ .../TestActorRefTests/Logger.cs | 29 ++ .../TestActorRefTests/NestingActor.cs | 37 ++ .../TestActorRefTests/ReplyActor.cs | 47 ++ .../TestActorRefTests/SenderActor.cs | 51 ++ .../TestActorRefTests/TActorBase.cs | 42 ++ .../TestActorRefTests/TestActorRefSpec.cs | 240 +++++++++ .../TestActorRefTests/TestProbeSpec.cs | 124 +++++ .../TestActorRefTests/WatchAndForwardActor.cs | 33 ++ .../TestActorRefTests/WorkerActor.cs | 39 ++ .../TestActorRefTests/WrappedTerminated.cs | 24 + .../AllTestForEventFilterBase.cs | 290 +++++++++++ .../AllTestForEventFilterBase_Instances.cs | 143 ++++++ .../TestEventListenerTests/ConfigTests.cs | 35 ++ .../CustomEventFilterTests.cs | 77 +++ .../DeadLettersEventFilterTests.cs | 93 ++++ .../EventFilterTestBase.cs | 66 +++ .../ExceptionEventFilterTests.cs | 203 ++++++++ .../ForwardAllEventsTestEventListener.cs | 47 ++ .../TestFSMRefTests/TestFSMRefSpec.cs | 102 ++++ .../TestKitBaseTests/AwaitAssertTests.cs | 72 +++ .../TestKitBaseTests/DilatedTests.cs | 91 ++++ .../TestKitBaseTests/ExpectTests.cs | 76 +++ .../TestKitBaseTests/IgnoreMessagesTests.cs | 76 +++ .../TestKitBaseTests/ReceiveTests.cs | 279 ++++++++++ .../TestKitBaseTests/RemainingTests.cs | 22 + .../TestKitBaseTests/WithinTests.cs | 75 +++ .../TestKit_Config_Tests.cs | 43 ++ .../TestSchedulerTests.cs | 205 ++++++++ .../Akka.Hosting.TestKit.csproj | 19 + .../Akka.Hosting.TestKit.csproj.DotSettings | 2 + src/Akka.Hosting.TestKit/HostingSpec.cs | 188 +++++++ .../Internals/XUnitLogger.cs | 86 ++++ .../Internals/XUnitLoggerProvider.cs | 28 + .../Wrapper/HostingSpec_ActorOf.cs | 313 ++++++++++++ .../Wrapper/HostingSpec_AwaitAssert.cs | 58 +++ .../Wrapper/HostingSpec_AwaitCondition.cs | 142 ++++++ .../Wrapper/HostingSpec_Expect.cs | 374 ++++++++++++++ .../Wrapper/HostingSpec_ExpectMsgFrom.cs | 204 ++++++++ .../Wrapper/HostingSpec_Receive.cs | 403 +++++++++++++++ .../Wrapper/HostingSpec_TestKit.cs | 479 ++++++++++++++++++ .../Wrapper/HostingSpec_Within.cs | 177 +++++++ 47 files changed, 5412 insertions(+) create mode 100644 src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj create mode 100644 src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/BossActor.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/FsmActor.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/Logger.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/NestingActor.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/ReplyActor.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/SenderActor.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TActorBase.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WatchAndForwardActor.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WorkerActor.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WrappedTerminated.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ForwardAllEventsTestEventListener.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs create mode 100644 src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs create mode 100644 src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj create mode 100644 src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj.DotSettings create mode 100644 src/Akka.Hosting.TestKit/HostingSpec.cs create mode 100644 src/Akka.Hosting.TestKit/Internals/XUnitLogger.cs create mode 100644 src/Akka.Hosting.TestKit/Internals/XUnitLoggerProvider.cs create mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ActorOf.cs create mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitAssert.cs create mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitCondition.cs create mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Expect.cs create mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ExpectMsgFrom.cs create mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Receive.cs create mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_TestKit.cs create mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Within.cs diff --git a/Akka.Hosting.sln b/Akka.Hosting.sln index ce8668b3..28ff68a4 100644 --- a/Akka.Hosting.sln +++ b/Akka.Hosting.sln @@ -41,6 +41,10 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Persistence.Hosting.Te EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Hosting.LoggingDemo", "src\Examples\Akka.Hosting.LoggingDemo\Akka.Hosting.LoggingDemo.csproj", "{4F79325B-9EE7-4501-800F-7A1F8DFBCC80}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Hosting.TestKit", "src\Akka.Hosting.TestKit\Akka.Hosting.TestKit.csproj", "{E28D4F3C-6C34-497B-BDC8-F2B3EA8BA309}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.Hosting.TestKit.Tests", "src\Akka.Hosting.TestKit.Tests\Akka.Hosting.TestKit.Tests.csproj", "{3883AD08-B981-4943-8153-1E7FFD2C3127}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -105,6 +109,14 @@ Global {4F79325B-9EE7-4501-800F-7A1F8DFBCC80}.Debug|Any CPU.Build.0 = Debug|Any CPU {4F79325B-9EE7-4501-800F-7A1F8DFBCC80}.Release|Any CPU.ActiveCfg = Release|Any CPU {4F79325B-9EE7-4501-800F-7A1F8DFBCC80}.Release|Any CPU.Build.0 = Release|Any CPU + {E28D4F3C-6C34-497B-BDC8-F2B3EA8BA309}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E28D4F3C-6C34-497B-BDC8-F2B3EA8BA309}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E28D4F3C-6C34-497B-BDC8-F2B3EA8BA309}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E28D4F3C-6C34-497B-BDC8-F2B3EA8BA309}.Release|Any CPU.Build.0 = Release|Any CPU + {3883AD08-B981-4943-8153-1E7FFD2C3127}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3883AD08-B981-4943-8153-1E7FFD2C3127}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3883AD08-B981-4943-8153-1E7FFD2C3127}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3883AD08-B981-4943-8153-1E7FFD2C3127}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj b/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj new file mode 100644 index 00000000..38dde52f --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.1 + + + + + + + + + + + + + + diff --git a/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs b/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs new file mode 100644 index 00000000..cd37c35e --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs @@ -0,0 +1,47 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using Akka.Actor; +using Akka.TestKit.TestActors; +using Xunit; +using Xunit.Abstractions; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; + +namespace Akka.Hosting.TestKit.Tests +{ + public class HostingSpecSpec: HostingSpec + { + private enum Echo + { } + + public HostingSpecSpec(ITestOutputHelper output) + : base(nameof(HostingSpecSpec), output, logLevel: LogLevel.Debug) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + builder.WithActors((system, registry) => + { + var echo = system.ActorOf(Props.Create(() => new SimpleEchoActor())); + registry.Register(echo); + }); + } + + [Fact] + public void ActorTest() + { + var echo = ActorRegistry.Get(); + var probe = CreateTestProbe(); + + echo.Tell("TestMessage", probe); + var msg = probe.ExpectMsg("TestMessage"); + Log.Info(msg); + } + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs b/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs new file mode 100644 index 00000000..e46a26d6 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.TestKit; +using Akka.Actor.Dsl; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests +{ + public class NoImplicitSenderSpec : HostingSpec, INoImplicitSender + { + [Fact(Skip = "Type assertion on null message causes NullReferenceException")] + public async Task When_Not_ImplicitSender_then_testActor_is_not_sender() + { + var echoActor = Sys.ActorOf(c => c.ReceiveAny((m, ctx) => TestActor.Tell(ctx.Sender))); + echoActor.Tell("message"); + await ExpectMsgAsync(actorRef => Equals(actorRef, ActorRefs.NoSender)); + } + + public NoImplicitSenderSpec(ITestOutputHelper output) : base(nameof(NoImplicitSenderSpec), output) + { } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { } + } + + public class ImplicitSenderSpec : HostingSpec + { + public ImplicitSenderSpec(ITestOutputHelper output) : base(nameof(ImplicitSenderSpec), output) + { } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { } + + [Fact] + public async Task ImplicitSender_should_have_testActor_as_sender() + { + var echoActor = Sys.ActorOf(c => c.ReceiveAny((m, ctx) => TestActor.Tell(ctx.Sender))); + echoActor.Tell("message"); + await ExpectMsgAsync(actorRef => Equals(actorRef, TestActor)); + + //Test that it works after we know that context has been changed + echoActor.Tell("message"); + await ExpectMsgAsync(actorRef => Equals(actorRef, TestActor)); + } + + + [Fact] + public async Task ImplicitSender_should_not_change_when_creating_Testprobes() + { + //Verifies that bug #459 has been fixed + var testProbe = CreateTestProbe(); + TestActor.Tell("message"); + await ReceiveOneAsync(); + LastSender.Should().Be(TestActor); + } + + [Fact] + public async Task ImplicitSender_should_not_change_when_creating_TestActors() + { + var testActor2 = CreateTestActor("test2"); + TestActor.Tell("message"); + await ReceiveOneAsync(); + LastSender.Should().Be(TestActor); + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/BossActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/BossActor.cs new file mode 100644 index 00000000..70d0fa47 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/BossActor.cs @@ -0,0 +1,67 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading; +using Akka.Actor; +using Akka.TestKit; +using Akka.Util; +using Akka.Util.Internal; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public class BossActor : TActorBase + { + private readonly TestActorRef _child; + + public BossActor(AtomicCounter counter, Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) + { + _child = new TestActorRef(Context.System, Props.Create(() => new InternalActor(counter, parentThread, otherThread)), Self, "child"); + } + + protected override SupervisorStrategy SupervisorStrategy() + { + return new OneForOneStrategy(maxNrOfRetries: 5, withinTimeRange: TimeSpan.FromSeconds(1), localOnlyDecider: ex => ex is ActorKilledException ? Directive.Restart : Directive.Escalate); + } + + protected override bool ReceiveMessage(object message) + { + if(message is string && ((string)message) == "sendKill") + { + _child.Tell(Kill.Instance); + return true; + } + return false; + } + + private class InternalActor : TActorBase + { + private readonly AtomicCounter _counter; + + public InternalActor(AtomicCounter counter, Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) + { + _counter = counter; + } + + protected override void PreRestart(Exception reason, object message) + { + _counter.Decrement(); + } + + protected override void PostRestart(Exception reason) + { + _counter.Decrement(); + } + + protected override bool ReceiveMessage(object message) + { + return true; + } + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/FsmActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/FsmActor.cs new file mode 100644 index 00000000..5ddc74ec --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/FsmActor.cs @@ -0,0 +1,57 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using Akka.Actor; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public enum TestFsmState + { + First, + Last + } + + public class FsmActor : FSM + { + private readonly IActorRef _replyActor; + + public FsmActor(IActorRef replyActor) + { + _replyActor = replyActor; + + When(TestFsmState.First, e => + { + if (e.FsmEvent.Equals("check")) + { + _replyActor.Tell("first"); + } + else if (e.FsmEvent.Equals("next")) + { + return GoTo(TestFsmState.Last); + } + + return Stay(); + }); + + When(TestFsmState.Last, e => + { + if (e.FsmEvent.Equals("check")) + { + _replyActor.Tell("last"); + } + else if (e.FsmEvent.Equals("next")) + { + return GoTo(TestFsmState.First); + } + + return Stay(); + }); + + StartWith(TestFsmState.First, "foo"); + } + } +} diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/Logger.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/Logger.cs new file mode 100644 index 00000000..2211cb4b --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/Logger.cs @@ -0,0 +1,29 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using Akka.Actor; +using Akka.Event; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public class Logger : ActorBase + { + private int _count; + private string _msg; + protected override bool Receive(object message) + { + if(message is Warning { Message: string _ } warning) + { + _count++; + _msg = (string)warning.Message; + return true; + } + return false; + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/NestingActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/NestingActor.cs new file mode 100644 index 00000000..31a256f4 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/NestingActor.cs @@ -0,0 +1,37 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using Akka.Actor; +using Akka.TestKit; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public class NestingActor : ActorBase + { + private readonly IActorRef _nested; + + public NestingActor(bool createTestActorRef) + { + _nested = createTestActorRef ? Context.System.ActorOf() : new TestActorRef(Context.System, Props.Create(), null, null); + } + + protected override bool Receive(object message) + { + Sender.Tell(_nested, Self); + return true; + } + + private class NestedActor : ActorBase + { + protected override bool Receive(object message) + { + return true; + } + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/ReplyActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/ReplyActor.cs new file mode 100644 index 00000000..97961775 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/ReplyActor.cs @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System.Threading; +using Akka.Actor; +using Akka.TestKit; +using Akka.Util; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public class ReplyActor : TActorBase + { + private IActorRef _replyTo; + + protected override bool ReceiveMessage(object message) + { + switch((string)message) + { + case "complexRequest": + _replyTo = Sender; + var worker = new TestActorRef(System, Props.Create(() => new WorkerActor(ParentThread, OtherThread))); + worker.Tell("work"); + return true; + case "complexRequest2": + var worker2 = new TestActorRef(System, Props.Create(() => new WorkerActor(ParentThread, OtherThread))); + worker2.Tell(Sender, Self); + return true; + case "workDone": + _replyTo.Tell("complexReply", Self); + return true; + case "simpleRequest": + Sender.Tell("simpleReply", Self); + return true; + } + return false; + } + + public ReplyActor(Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) + { + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/SenderActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/SenderActor.cs new file mode 100644 index 00000000..77528486 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/SenderActor.cs @@ -0,0 +1,51 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System.Threading; +using Akka.Actor; +using Akka.Util; +using Akka.Util.Internal; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public class SenderActor : TActorBase + { + private readonly AtomicCounter _counter; + private readonly IActorRef _replyActor; + + public SenderActor(IActorRef replyActor, AtomicCounter counter, Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) + { + _replyActor = replyActor; + _counter = counter; + } + + protected override bool ReceiveMessage(object message) + { + var strMessage = message as string; + switch(strMessage) + { + case "complex": + _replyActor.Tell("complexRequest", Self); + return true; + case "complex2": + _replyActor.Tell("complexRequest2", Self); + return true; + case "simple": + _replyActor.Tell("simpleRequest", Self); + return true; + case "complexReply": + _counter.Decrement(); + return true; + case "simpleReply": + _counter.Decrement(); + return true; + } + return false; + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TActorBase.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TActorBase.cs new file mode 100644 index 00000000..1e6c958b --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TActorBase.cs @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System.Threading; +using Akka.Actor; +using Akka.Util; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + // ReSharper disable once InconsistentNaming + public abstract class TActorBase : ActorBase + { + protected readonly Thread ParentThread; + protected readonly AtomicReference OtherThread; + + protected TActorBase(Thread parentThread, AtomicReference otherThread) + { + ParentThread = parentThread; + OtherThread = otherThread; + } + + protected sealed override bool Receive(object message) + { + var currentThread = Thread.CurrentThread; + if (currentThread != ParentThread) + OtherThread.GetAndSet(currentThread); + return ReceiveMessage(message); + } + + protected abstract bool ReceiveMessage(object message); + + protected ActorSystem System + { + get { return ((LocalActorRef)Self).Cell.System; } + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs new file mode 100644 index 00000000..d88a0057 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs @@ -0,0 +1,240 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading; +using Akka.Actor; +using Akka.Configuration; +using Akka.Dispatch; +using Akka.TestKit; +using Akka.TestKit.Internal; +using Akka.Util; +using Akka.Util.Internal; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public class TestActorRefSpec : HostingSpec + { + private readonly AtomicCounter _counter = new AtomicCounter(4); + private readonly Thread _thread = Thread.CurrentThread; + private readonly AtomicReference _otherThread = new AtomicReference(null); + + public TestActorRefSpec(ITestOutputHelper output) : base(nameof(TestActorRefSpec), output) + { } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + builder.AddHocon(GetConfig()); + } + + private TimeSpan DefaultTimeout => Dilated(TestKitSettings.DefaultTimeout); + + private static Config GetConfig() + { + return "test-dispatcher1.type=\"Akka.Dispatch.PinnedDispatcherConfigurator, Akka\""; + } + + private void AssertThread() + { + Assert.True(_otherThread.Value == null || _otherThread.Value == _thread, "Thread"); + } + + [Fact] + public void TestActorRef_name_must_start_with_double_dollar_sign() + { + //Looking at the scala code, this might not be obvious that the name starts with $$ + //object TestActorRef (TestActorRef.scala) contain this code: + // private[testkit] def randomName: String = { + // val l = number.getAndIncrement() + // "$" + akka.util.Helpers.base64(l) + // } + //So it adds one $. The second is added by akka.util.Helpers.base64(l) which by default + //creates a StringBuilder and adds adds $. Hence, 2 $$ + var testActorRef = new TestActorRef(Sys, Props.Create()); + + Assert.Equal("$$", testActorRef.Path.Name.Substring(0, 2)); + } + + [Fact] + public void TestActorRef_must_support_nested_Actor_creation_when_used_with_TestActorRef() + { + var a = new TestActorRef(Sys, Props.Create(() => new NestingActor(true))); + Assert.NotNull(a); + var nested = a.Ask("any", DefaultTimeout).Result; + Assert.NotNull(nested); + Assert.NotSame(a, nested); + } + + [Fact] + public void TestActorRef_must_support_nested_Actor_creation_when_used_with_ActorRef() + { + var a = new TestActorRef(Sys, Props.Create(() => new NestingActor(false))); + Assert.NotNull(a); + var nested = a.Ask("any", DefaultTimeout).Result; + Assert.NotNull(nested); + Assert.NotSame(a, nested); + } + + [Fact] + public void TestActorRef_must_support_reply_via_sender() + { + var serverRef = new TestActorRef(Sys, Props.Create(() => + new ReplyActor(_thread, _otherThread))); + var clientRef = new TestActorRef(Sys, Props.Create(() => + new SenderActor(serverRef, _counter, _thread, _otherThread))); + + _counter.GetAndSet(4); + clientRef.Tell("complex"); + clientRef.Tell("simple"); + clientRef.Tell("simple"); + clientRef.Tell("simple"); + _counter.Current.Should().Be(0); + + _counter.GetAndSet(4); + clientRef.Tell("complex2"); + clientRef.Tell("simple"); + clientRef.Tell("simple"); + clientRef.Tell("simple"); + _counter.Current.Should().Be(0); + + AssertThread(); + } + + [Fact] + public void TestActorRef_must_stop_when_sent_a_PoisonPill() + { + //TODO: Should have this surrounding all code EventFilter[ActorKilledException]() intercept { + var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread)), null, "will-be-killed"); + Sys.ActorOf(Props.Create(() => new WatchAndForwardActor(a, TestActor)), "forwarder"); + a.Tell(PoisonPill.Instance); + ExpectMsg(w => w.Terminated.ActorRef == a, TimeSpan.FromSeconds(10), + $"that the terminated actor was the one killed, i.e. {a.Path}"); + var actorRef = (InternalTestActorRef)a.Ref; + actorRef.IsTerminated.Should().Be(true); + AssertThread(); + } + + [Fact] + public void TestActorRef_must_restart_when_killed() + { + //TODO: Should have this surrounding all code EventFilter[ActorKilledException]() intercept { + _counter.GetAndSet(2); + var boss = new TestActorRef(Sys, Props.Create(() => new BossActor(_counter, _thread, _otherThread))); + + boss.Tell("sendKill"); + Assert.Equal(0, _counter.Current); + AssertThread(); + } + + [Fact] + public void TestActorRef_must_support_futures() + { + var worker = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread))); + var task = worker.Ask("work"); + Assert.True(task.IsCompleted, "Task should be completed"); + if (!task.Wait(DefaultTimeout)) + throw new XunitException("Timed out"); //Using a timeout to stop the test if there is something wrong with the code + Assert.Equal("workDone", task.Result); + } + + [Fact] + public void TestActorRef_must_allow_access_to_internals() + { + var actorRef = new TestActorRef(Sys, Props.Create(() => new SaveStringActor(_thread, _otherThread))); + actorRef.Tell("Hejsan!"); + var actor = actorRef.UnderlyingActor; + Assert.Equal("Hejsan!", actor.ReceivedString); + } + + [Fact] + public void TestActorRef_must_set_ReceiveTimeout_to_None() + { + var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread))); + ((IInternalActor)a.UnderlyingActor).ActorContext.ReceiveTimeout.Should().Be(null); + } + + [Fact] + public void TestActorRef_must_set_CallingThreadDispatcher() + { + var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread))); + var actorRef = (InternalTestActorRef)a.Ref; + Assert.IsType(actorRef.Cell.Dispatcher); + } + + [Fact] + public void TestActorRef_must_allow_override_of_dispatcher() + { + var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread)).WithDispatcher("test-dispatcher1")); + var actorRef = (InternalTestActorRef)a.Ref; + Assert.IsType(actorRef.Cell.Dispatcher); + } + + [Fact] + public void TestActorRef_must_proxy_receive_for_the_underlying_actor_without_sender() + { + var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread))); + a.Receive("work"); + var actorRef = (InternalTestActorRef)a.Ref; + Assert.True(actorRef.IsTerminated); + } + + [Fact] + public void TestActorRef_must_proxy_receive_for_the_underlying_actor_with_sender() + { + var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread))); + a.Receive("work", TestActor); //This will stop the actor + var actorRef = (InternalTestActorRef)a.Ref; + Assert.True(actorRef.IsTerminated); + ExpectMsg("workDone"); + } + + [Fact] + public void TestFsmActorRef_must_proxy_receive_for_underlying_actor_with_sender() + { + var a = new TestFSMRef(Sys, Props.Create(() => new FsmActor(TestActor))); + a.Receive("check"); + ExpectMsg("first"); + + // verify that we can change state + a.SetState(TestFsmState.Last); + a.Receive("check"); + ExpectMsg("last"); + } + + [Fact] + public void BugFix1709_TestFsmActorRef_must_work_with_Fsms_with_constructor_arguments() + { + var a = ActorOfAsTestFSMRef(Props.Create(() => new FsmActor(TestActor))); + a.Receive("check"); + ExpectMsg("first"); + + // verify that we can change state + a.SetState(TestFsmState.Last); + a.Receive("check"); + ExpectMsg("last"); + } + + private class SaveStringActor : TActorBase + { + public string ReceivedString { get; private set; } + + protected override bool ReceiveMessage(object message) + { + ReceivedString = message as string; + return true; + } + + public SaveStringActor(Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) + { } + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs new file mode 100644 index 00000000..23bed24d --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs @@ -0,0 +1,124 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using Akka.Actor; +using Akka.TestKit; +using Akka.TestKit.TestActors; +using Akka.Util.Internal; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public class TestProbeSpec : HostingSpec + { + public TestProbeSpec(ITestOutputHelper output) : base(nameof(TestProbeSpec), output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + } + + [Fact] + public void TestProbe_should_equal_underlying_Ref() + { + var p = CreateTestProbe(); + p.Equals(p.Ref).Should().BeTrue(); + p.Ref.Equals(p).Should().BeTrue(); + var hs = new HashSet {p, p.Ref}; + hs.Count.Should().Be(1); + } + + /// + /// Should be able to receive a message from a + /// if we're deathwatching it and it terminates. + /// + [Fact] + public void TestProbe_should_send_Terminated_when_killed() + { + var p = CreateTestProbe(); + Watch(p); + Sys.Stop(p); + ExpectTerminated(p); + } + + /// + /// If we deathwatch the underlying actor ref or TestProbe itself, it shouldn't matter. + /// + /// They should be equivalent either way. + /// + [Fact] + public void TestProbe_underlying_Ref_should_be_equivalent_to_TestProbe() + { + var p = CreateTestProbe(); + Watch(p.Ref); + Sys.Stop(p); + ExpectTerminated(p); + } + + /// + /// Should be able to receive a message from a + /// if we're deathwatching it and it terminates. + /// + [Fact] + public void TestProbe_underlying_Ref_should_send_Terminated_when_killed() + { + var p = CreateTestProbe(); + Watch(p.Ref); + Sys.Stop(p.Ref); + ExpectTerminated(p.Ref); + } + + [Fact] + public void TestProbe_should_create_a_child_when_invoking_ChildActorOf() + { + var probe = CreateTestProbe(); + var child = probe.ChildActorOf(Props.Create()); + child.Path.Parent.Should().Be(probe.Ref.Path); + var namedChild = probe.ChildActorOf("actorName"); + namedChild.Path.Name.Should().Be("actorName"); + } + + [Fact] + public void TestProbe_restart_a_failing_child_if_the_given_supervisor_says_so() + { + var restarts = new AtomicCounter(0); + var probe = CreateTestProbe(); + var child = probe.ChildActorOf(Props.Create(() => new FailingActor(restarts)), SupervisorStrategy.DefaultStrategy); + AwaitAssert(() => + { + child.Tell("hello"); + restarts.Current.Should().BeGreaterThan(1); + }); + } + + private class FailingActor : ActorBase + { + private AtomicCounter Restarts { get; } + + public FailingActor(AtomicCounter restarts) + { + Restarts = restarts; + } + + protected override bool Receive(object message) + { + throw new Exception("Simulated failure"); + } + + protected override void PostRestart(Exception reason) + { + Restarts.IncrementAndGet(); + } + } + } +} diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WatchAndForwardActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WatchAndForwardActor.cs new file mode 100644 index 00000000..b1ea69d9 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WatchAndForwardActor.cs @@ -0,0 +1,33 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using Akka.Actor; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public class WatchAndForwardActor : ActorBase + { + private readonly IActorRef _forwardToActor; + + public WatchAndForwardActor(IActorRef watchedActor, IActorRef forwardToActor) + { + _forwardToActor = forwardToActor; + Context.Watch(watchedActor); + } + + protected override bool Receive(object message) + { + var terminated = message as Terminated; + if(terminated != null) + _forwardToActor.Tell(new WrappedTerminated(terminated), Sender); + else + _forwardToActor.Tell(message, Sender); + return true; + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WorkerActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WorkerActor.cs new file mode 100644 index 00000000..18b4f745 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WorkerActor.cs @@ -0,0 +1,39 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System.Threading; +using Akka.Actor; +using Akka.Util; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public class WorkerActor : TActorBase + { + public WorkerActor(Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) + { + } + + protected override bool ReceiveMessage(object message) + { + if((message as string) == "work") + { + Sender.Tell("workDone"); + Context.Stop(Self); + return true; + + } + //TODO: case replyTo: Promise[_] ⇒ replyTo.asInstanceOf[Promise[Any]].success("complexReply") + if(message is IActorRef) + { + ((IActorRef)message).Tell("complexReply", Self); + return true; + } + return false; + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WrappedTerminated.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WrappedTerminated.cs new file mode 100644 index 00000000..a0c02200 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WrappedTerminated.cs @@ -0,0 +1,24 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using Akka.Actor; + +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +{ + public class WrappedTerminated + { + private readonly Terminated _terminated; + + public WrappedTerminated(Terminated terminated) + { + _terminated = terminated; + } + + public Terminated Terminated { get { return _terminated; } } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs new file mode 100644 index 00000000..5f271781 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs @@ -0,0 +1,290 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Event; +using Akka.TestKit; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; +using static FluentAssertions.FluentActions; + +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests +{ + public abstract class AllTestForEventFilterBase : EventFilterTestBase where TLogEvent : LogEvent + { + private EventFilterFactory _testingEventFilter; + + protected AllTestForEventFilterBase(string config, string systemName, ITestOutputHelper output) + : base(config, systemName, output) + { + } + + protected new LogLevel LogLevel { get; private set; } + protected abstract EventFilterFactory CreateTestingEventFilter(); + + protected override async Task BeforeTestStart() + { + await base.BeforeTestStart(); + + LogLevel = Event.Logging.LogLevelFor(); + _testingEventFilter = CreateTestingEventFilter(); + } + + protected void LogMessage(string message) + { + Log.Log(LogLevel, message); + } + + protected override void SendRawLogEventMessage(object message) + { + PublishMessage(message, "test"); + } + + protected abstract void PublishMessage(object message, string source); + + [Fact] + public async Task Single_message_is_intercepted() + { + await _testingEventFilter.ForLogLevel(LogLevel) + .ExpectOneAsync(async () => LogMessage("whatever")); + TestSuccessful = true; + } + + + [Fact] + public async Task Can_intercept_messages_when_start_is_specified() + { + await _testingEventFilter.ForLogLevel(LogLevel, start: "what") + .ExpectOneAsync(async () => LogMessage("whatever")); + TestSuccessful = true; + } + + [Fact] + public async Task Do_not_intercept_messages_when_start_does_not_match() + { + await _testingEventFilter.ForLogLevel(LogLevel, start: "what").ExpectOneAsync(async () => + { + LogMessage("let-me-thru"); + LogMessage("whatever"); + }); + await ExpectMsgAsync(err => (string)err.Message == "let-me-thru"); + TestSuccessful = true; + } + + [Fact] + public async Task Can_intercept_messages_when_message_is_specified() + { + await _testingEventFilter.ForLogLevel(LogLevel, message: "whatever") + .ExpectOneAsync(async () => LogMessage("whatever")); + TestSuccessful = true; + } + + [Fact] + public async Task Do_not_intercept_messages_when_message_does_not_match() + { + await EventFilter.ForLogLevel(LogLevel, message: "whatever").ExpectOneAsync(async () => + { + LogMessage("let-me-thru"); + LogMessage("whatever"); + }); + await ExpectMsgAsync(err => (string)err.Message == "let-me-thru"); + TestSuccessful = true; + } + + [Fact] + public async Task Can_intercept_messages_when_contains_is_specified() + { + await _testingEventFilter.ForLogLevel(LogLevel, contains: "ate") + .ExpectOneAsync(async () => LogMessage("whatever")); + TestSuccessful = true; + } + + [Fact] + public async Task Do_not_intercept_messages_when_contains_does_not_match() + { + await _testingEventFilter.ForLogLevel(LogLevel, contains: "eve").ExpectOneAsync(async () => + { + LogMessage("let-me-thru"); + LogMessage("whatever"); + }); + ExpectMsg(err => (string)err.Message == "let-me-thru"); + TestSuccessful = true; + } + + + [Fact] + public async Task Can_intercept_messages_when_source_is_specified() + { + var type = GetType(); + await _testingEventFilter.ForLogLevel(LogLevel, source: LogSource.FromType(GetType(), Sys)) + .ExpectOneAsync(async () => LogMessage("whatever")); + TestSuccessful = true; + } + + [Fact] + public async Task Do_not_intercept_messages_when_source_does_not_match() + { + await _testingEventFilter.ForLogLevel(LogLevel, source: "expected-source").ExpectOneAsync(() => + { + PublishMessage("message", source: "expected-source"); + PublishMessage("message", source: "let-me-thru"); + return Task.CompletedTask; + }); + await ExpectMsgAsync(err => err.LogSource == "let-me-thru"); + TestSuccessful = true; + } + + [Fact] + public async Task Specified_numbers_of_messages_can_be_intercepted() + { + await _testingEventFilter.ForLogLevel(LogLevel).ExpectAsync(2, async () => + { + LogMessage("whatever"); + LogMessage("whatever"); + }); + TestSuccessful = true; + } + + [Fact] + public async Task Expect_0_events_Should_work() + { + await Awaiting(async () => + { + await EventFilter.Error().ExpectAsync(0, () => + { + Log.Error("something"); + return Task.CompletedTask; + }); + }).Should().ThrowAsync("Expected 0 events"); + } + + [Fact] + public async Task ExpectAsync_0_events_Should_work() + { + await Awaiting(async () => + { + await EventFilter.Error().ExpectAsync(0, async () => + { + await Task.Delay(100); // bug only happens when error is not logged instantly + Log.Error("something"); + }); + }).Should().ThrowAsync("Expected 0 errors logged, but there are error logs"); + } + + /// issue: InternalExpectAsync does not await actionAsync() - causing actionAsync to run as a detached task #5537 + [Fact] + public async Task ExpectAsync_should_await_actionAsync() + { + await Assert.ThrowsAnyAsync(async () => + { + await _testingEventFilter.ForLogLevel(LogLevel).ExpectAsync(0, actionAsync: async () => + { + Assert.False(true); + await Task.CompletedTask; + }); + }); + } + + // issue: InterceptAsync seems to run func() as a detached task #5586 + [Fact] + public async Task InterceptAsync_should_await_func() + { + await Assert.ThrowsAnyAsync(async () => + { + await _testingEventFilter.ForLogLevel(LogLevel).ExpectAsync(0, async () => + { + Assert.False(true); + await Task.CompletedTask; + }, TimeSpan.FromSeconds(.1)); + }); + } + + [Fact] + public async Task Messages_can_be_muted() + { + await _testingEventFilter.ForLogLevel(LogLevel).MuteAsync(async () => + { + LogMessage("whatever"); + LogMessage("whatever"); + }); + TestSuccessful = true; + } + + + [Fact] + public async Task Messages_can_be_muted_from_now_on() + { + var unmutableFilter = _testingEventFilter.ForLogLevel(LogLevel).Mute(); + LogMessage("whatever"); + LogMessage("whatever"); + unmutableFilter.Unmute(); + TestSuccessful = true; + } + + [Fact] + public async Task Messages_can_be_muted_from_now_on_with_using() + { + using(_testingEventFilter.ForLogLevel(LogLevel).Mute()) + { + LogMessage("whatever"); + LogMessage("whatever"); + } + TestSuccessful = true; + } + + + [Fact] + public async Task Make_sure_async_works() + { + await _testingEventFilter.ForLogLevel(LogLevel).ExpectAsync(1, TimeSpan.FromSeconds(2), async () => + { + await Task.Delay(TimeSpan.FromMilliseconds(10)).ContinueWith(async t => { LogMessage("whatever"); }); + }); + } + + [Fact] + public async Task Chain_many_filters() + { + await _testingEventFilter + .ForLogLevel(LogLevel,message:"Message 1").And + .ForLogLevel(LogLevel,message:"Message 3") + .ExpectAsync(2, async () => + { + LogMessage("Message 1"); + LogMessage("Message 2"); + LogMessage("Message 3"); + + }); + await ExpectMsgAsync(m => (string) m.Message == "Message 2"); + } + + + [Fact] + public async Task Should_timeout_if_too_few_messages() + { + await Awaiting(async () => + { + await _testingEventFilter.ForLogLevel(LogLevel).ExpectAsync(2, TimeSpan.FromMilliseconds(50), async () => + { + LogMessage("whatever"); + }); + }).Should().ThrowAsync().WithMessage("Timeout (*"); + } + + [Fact] + public async Task Should_log_when_not_muting() + { + const string message = "This should end up in the log since it's not filtered"; + LogMessage(message); + await ExpectMsgAsync( msg => (string)msg.Message == message); + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs new file mode 100644 index 00000000..4a246df3 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs @@ -0,0 +1,143 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using Akka.Event; +using Akka.TestKit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests +{ + public class EventFilterDebugTests : AllTestForEventFilterBase + { + public EventFilterDebugTests(ITestOutputHelper output) + : base("akka.loglevel=DEBUG", nameof(EventFilterDebugTests), output){} + + protected override EventFilterFactory CreateTestingEventFilter() + { + return EventFilter; + } + + protected override void PublishMessage(object message, string source) + { + Sys.EventStream.Publish(new Debug(source,GetType(),message)); + } + } + + public class CustomEventFilterDebugTests : AllTestForEventFilterBase + { + public CustomEventFilterDebugTests(ITestOutputHelper output) + : base("akka.loglevel=DEBUG", nameof(CustomEventFilterDebugTests), output) { } + + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); + } + + protected override void PublishMessage(object message, string source) + { + Sys.EventStream.Publish(new Debug(source, GetType(), message)); + } + } + + public class EventFilterInfoTests : AllTestForEventFilterBase + { + public EventFilterInfoTests(ITestOutputHelper output) + : base("akka.loglevel=INFO", nameof(EventFilterInfoTests), output) { } + + protected override EventFilterFactory CreateTestingEventFilter() + { + return EventFilter; + } + + protected override void PublishMessage(object message, string source) + { + Sys.EventStream.Publish(new Info(source, GetType(), message)); + } + } + + public class CustomEventFilterInfoTests : AllTestForEventFilterBase + { + public CustomEventFilterInfoTests(ITestOutputHelper output) : base( + "akka.loglevel=INFO", nameof(CustomEventFilterInfoTests), output) { } + + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); + } + + protected override void PublishMessage(object message, string source) + { + Sys.EventStream.Publish(new Info(source, GetType(), message)); + } + } + + + public class EventFilterWarningTests : AllTestForEventFilterBase + { + public EventFilterWarningTests(ITestOutputHelper output) : base( + "akka.loglevel=WARNING", nameof(EventFilterWarningTests), output) { } + + protected override EventFilterFactory CreateTestingEventFilter() + { + return EventFilter; + } + + protected override void PublishMessage(object message, string source) + { + Sys.EventStream.Publish(new Warning(source, GetType(), message)); + } + } + + public class CustomEventFilterWarningTests : AllTestForEventFilterBase + { + public CustomEventFilterWarningTests(ITestOutputHelper output) + : base("akka.loglevel=WARNING", nameof(CustomEventFilterWarningTests), output) { } + + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); + } + + protected override void PublishMessage(object message, string source) + { + Sys.EventStream.Publish(new Warning(source, GetType(), message)); + } + } + + public class EventFilterErrorTests : AllTestForEventFilterBase + { + public EventFilterErrorTests(ITestOutputHelper output) + : base("akka.loglevel=ERROR", nameof(EventFilterErrorTests), output) { } + + protected override EventFilterFactory CreateTestingEventFilter() + { + return EventFilter; + } + + protected override void PublishMessage(object message, string source) + { + Sys.EventStream.Publish(new Error(null, source, GetType(), message)); + } + } + + public class CustomEventFilterErrorTests : AllTestForEventFilterBase + { + public CustomEventFilterErrorTests(ITestOutputHelper output) + : base("akka.loglevel=ERROR", nameof(CustomEventFilterErrorTests), output) { } + + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); + } + + protected override void PublishMessage(object message, string source) + { + Sys.EventStream.Publish(new Error(null, source, GetType(), message)); + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs new file mode 100644 index 00000000..8cca36dc --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Linq; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests +{ + public class ConfigTests : HostingSpec + { + [Fact] + public void TestEventListener_is_in_config_by_default() + { + var configLoggers = Sys.Settings.Config.GetStringList("akka.loggers", new string[] { }); + configLoggers.Any(logger => logger.Contains("Akka.TestKit.TestEventListener")).Should().BeTrue(); + configLoggers.Any(logger => logger.Contains("Akka.Event.DefaultLogger")).Should().BeFalse(); + } + + public ConfigTests(ITestOutputHelper output = null) : base(nameof(ConfigTests), output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs new file mode 100644 index 00000000..7fa4ef87 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System.Threading.Tasks; +using Akka.Event; +using Akka.TestKit; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests +{ + public abstract class CustomEventFilterTestsBase : EventFilterTestBase + { + protected CustomEventFilterTestsBase(string systemName, ITestOutputHelper output) + : base("akka.loglevel=ERROR", systemName, output) { } + + protected override void SendRawLogEventMessage(object message) + { + Sys.EventStream.Publish(new Error(null, "CustomEventFilterTests", GetType(), message)); + } + + protected abstract EventFilterFactory CreateTestingEventFilter(); + + [Fact] + public async Task Custom_filter_should_match() + { + var eventFilter = CreateTestingEventFilter(); + await eventFilter.Custom(logEvent => logEvent is Error && (string) logEvent.Message == "whatever") + .ExpectOneAsync(() => + { + Log.Error("whatever"); + return Task.CompletedTask; + }); + } + + [Fact] + public async Task Custom_filter_should_match2() + { + var eventFilter = CreateTestingEventFilter(); + await eventFilter.Custom(logEvent => (string)logEvent.Message == "whatever") + .ExpectOneAsync(() => + { + Log.Error("whatever"); + return Task.CompletedTask; + }); + } + } + + public class CustomEventFilterTests : CustomEventFilterTestsBase + { + protected override EventFilterFactory CreateTestingEventFilter() + { + return EventFilter; + } + + public CustomEventFilterTests(ITestOutputHelper output) : base(nameof(CustomEventFilterTests), output) + { + } + } + + public class CustomEventFilterCustomFilterTests : CustomEventFilterTestsBase + { + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); + } + + public CustomEventFilterCustomFilterTests(ITestOutputHelper output) : base(nameof(CustomEventFilterCustomFilterTests), output) + { + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs new file mode 100644 index 00000000..98e4fa47 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Event; +using Akka.TestKit; +using Akka.TestKit.TestActors; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests +{ + public abstract class DeadLettersEventFilterTestsBase : EventFilterTestBase + { + private enum DeadActorKey + { } + + private IActorRef _deadActor; + + protected DeadLettersEventFilterTestsBase(string systemName, ITestOutputHelper output) + : base("akka.loglevel=ERROR", systemName, output) + { + + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + base.ConfigureAkka(builder, provider); + builder.WithActors((system, registry) => + { + var actor = system.ActorOf(BlackHoleActor.Props, "dead-actor"); + registry.Register(actor); + }); + } + + protected override async Task BeforeTestStart() + { + _deadActor = ActorRegistry.Get(); + Watch(_deadActor); + Sys.Stop(_deadActor); + await ExpectTerminatedAsync(_deadActor); + } + + protected override void SendRawLogEventMessage(object message) + { + Sys.EventStream.Publish(new Error(null, "DeadLettersEventFilterTests", GetType(), message)); + } + + protected abstract EventFilterFactory CreateTestingEventFilter(); + + [Fact] + public async Task Should_be_able_to_filter_dead_letters() + { + var eventFilter = CreateTestingEventFilter(); + await eventFilter.DeadLetter().ExpectOneAsync(() => + { + _deadActor.Tell("whatever"); + return Task.CompletedTask; + }); + } + } + + public class DeadLettersEventFilterTests : DeadLettersEventFilterTestsBase + { + protected override EventFilterFactory CreateTestingEventFilter() + { + return EventFilter; + } + + public DeadLettersEventFilterTests(ITestOutputHelper output) : base(nameof(DeadLettersEventFilterTests), output) + { + } + } + + public class DeadLettersCustomEventFilterTests : DeadLettersEventFilterTestsBase + { + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); + } + + public DeadLettersCustomEventFilterTests(ITestOutputHelper output) : base(nameof(DeadLettersCustomEventFilterTests), output) + { + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs new file mode 100644 index 00000000..b635ae26 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Configuration; +using Akka.Event; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests +{ + public abstract class EventFilterTestBase : HostingSpec + { + /// + /// Used to signal that the test was successful and that we should ensure no more messages were logged + /// + protected bool TestSuccessful; + + protected override Config Config { get; } + + protected EventFilterTestBase(string config, string systemName, ITestOutputHelper output) + : base(systemName, output) + { + Config = $"akka.loggers = [\"{typeof(ForwardAllEventsTestEventListener).AssemblyQualifiedName}\"], {(string.IsNullOrEmpty(config) ? "" : config)}"; + } + + protected override async Task BeforeTestStart() + { + await base.BeforeTestStart(); + //We send a ForwardAllEventsTo containing message to the TestEventListenerToForwarder logger (configured as a logger above). + //It should respond with an "OK" message when it has received the message. + var initLoggerMessage = new ForwardAllEventsTestEventListener.ForwardAllEventsTo(TestActor); + + SendRawLogEventMessage(initLoggerMessage); + await ExpectMsgAsync("OK"); + //From now on we know that all messages will be forwarded to TestActor + } + + protected abstract void SendRawLogEventMessage(object message); + + protected override async Task AfterAllAsync() + { + //After every test we make sure no uncatched messages have been logged + if(TestSuccessful) + { + await EnsureNoMoreLoggedMessages(); + } + await base.AfterAllAsync(); + } + + private async Task EnsureNoMoreLoggedMessages() + { + //We log a Finished message. When it arrives to TestActor we know no other message has been logged. + //If we receive something else it means another message was logged, and ExpectMsg will fail + const string message = "<>"; + SendRawLogEventMessage(message); + await ExpectMsgAsync(err => (string) err.Message == message,hint: "message to be \"" + message + "\""); + } + + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs new file mode 100644 index 00000000..d740b350 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs @@ -0,0 +1,203 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Event; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; +using static FluentAssertions.FluentActions; + +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests +{ + public class ExceptionEventFilterTests : EventFilterTestBase + { + public ExceptionEventFilterTests(ITestOutputHelper output) + : base("akka.logLevel=ERROR", nameof(ExceptionEventFilterTests), output) + { + } + + public class SomeException : Exception { } + + protected override void SendRawLogEventMessage(object message) + { + Sys.EventStream.Publish(new Error(null, nameof(ExceptionEventFilterTests), GetType(), message)); + } + + [Fact] + public async Task SingleExceptionIsIntercepted() + { + await EventFilter.Exception() + .ExpectOneAsync(() => + { + Log.Error(new SomeException(), "whatever"); + return Task.CompletedTask; + }); + await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public async Task CanInterceptMessagesWhenStartIsSpecified() + { + await EventFilter.Exception(start: "what") + .ExpectOneAsync(() => + { + Log.Error(new SomeException(), "whatever"); + return Task.CompletedTask; + }); + await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public async Task DoNotInterceptMessagesWhenStartDoesNotMatch() + { + EventFilter.Exception(start: "this is clearly not in message"); + Log.Error(new SomeException(), "whatever"); + await ExpectMsgAsync(err => (string)err.Message == "whatever"); + } + + [Fact] + public async Task CanInterceptMessagesWhenMessageIsSpecified() + { + await EventFilter.Exception(message: "whatever") + .ExpectOneAsync(() => + { + Log.Error(new SomeException(), "whatever"); + return Task.CompletedTask; + }); + await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public async Task DoNotInterceptMessagesWhenMessageDoesNotMatch() + { + EventFilter.Exception(message: "this is clearly not the message"); + Log.Error(new SomeException(), "whatever"); + await ExpectMsgAsync(err => (string)err.Message == "whatever"); + } + + [Fact] + public async Task CanInterceptMessagesWhenContainsIsSpecified() + { + await EventFilter.Exception(contains: "ate") + .ExpectOneAsync(() => + { + Log.Error(new SomeException(), "whatever"); + return Task.CompletedTask; + }); + await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public async Task DoNotInterceptMessagesWhenContainsDoesNotMatch() + { + EventFilter.Exception(contains: "this is clearly not in the message"); + Log.Error(new SomeException(), "whatever"); + await ExpectMsgAsync(err => (string)err.Message == "whatever"); + } + + + [Fact] + public async Task CanInterceptMessagesWhenSourceIsSpecified() + { + await EventFilter.Exception(source: LogSource.Create(this, Sys).Source) + .ExpectOneAsync(() => + { + Log.Error(new SomeException(), "whatever"); + return Task.CompletedTask; + }); + await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public async Task DoNotInterceptMessagesWhenSourceDoesNotMatch() + { + EventFilter.Exception(source: "this is clearly not the source"); + Log.Error(new SomeException(), "whatever"); + await ExpectMsgAsync(err => (string)err.Message == "whatever"); + } + + + [Fact] + public async Task SpecifiedNumbersOfExceptionsCanBeIntercepted() + { + await EventFilter.Exception() + .ExpectAsync(2, () => + { + Log.Error(new SomeException(), "whatever"); + Log.Error(new SomeException(), "whatever"); + return Task.CompletedTask; + }); + await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); + } + + [Fact] + public async Task ShouldFailIfMoreExceptionsThenSpecifiedAreLogged() + { + await Awaiting(async () => + { + await EventFilter.Exception().ExpectAsync(2, () => + { + Log.Error(new SomeException(), "whatever"); + Log.Error(new SomeException(), "whatever"); + Log.Error(new SomeException(), "whatever"); + return Task.CompletedTask; + }); + }) + .Should().ThrowAsync().WithMessage("Received 1 message too many.*"); + } + + [Fact] + public async Task ShouldReportCorrectMessageCount() + { + var toSend = "Eric Cartman"; + var actor = ActorOf( ExceptionTestActor.Props() ); + + await EventFilter + .Exception(source: actor.Path.ToString()) + // expecting 2 because the same exception is logged in PostRestart + .ExpectAsync(2, () => + { + actor.Tell(toSend); + return Task.CompletedTask; + }); + } + + internal sealed class ExceptionTestActor : UntypedActor + { + private ILoggingAdapter Log { get; } = Context.GetLogger(); + + protected override void PostRestart(Exception reason) + { + Log.Error(reason, "[PostRestart]"); + base.PostRestart(reason); + } + + protected override void OnReceive( object message ) + { + switch (message) + { + case string _: + throw new InvalidOperationException( "I'm sailing away. Set an open course" ); + + default: + Unhandled( message ); + break; + } + } + + public static Props Props() + { + return Actor.Props.Create( () => new ExceptionTestActor() ); + } + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ForwardAllEventsTestEventListener.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ForwardAllEventsTestEventListener.cs new file mode 100644 index 00000000..42712da9 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ForwardAllEventsTestEventListener.cs @@ -0,0 +1,47 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using Akka.Actor; +using Akka.Event; +using Akka.TestKit; + +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests +{ + public class ForwardAllEventsTestEventListener : TestEventListener + { + private IActorRef _forwarder; + + protected override void Print(LogEvent m) + { + if(m.Message is ForwardAllEventsTo to) + { + _forwarder = to.Forwarder; + _forwarder.Tell("OK"); + } + else if(_forwarder != null) + { + _forwarder.Forward(m); + } + else + { + base.Print(m); + } + } + + public class ForwardAllEventsTo + { + public ForwardAllEventsTo(IActorRef forwarder) + { + Forwarder = forwarder; + } + + public IActorRef Forwarder { get; } + } + } + +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs b/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs new file mode 100644 index 00000000..34a9f4cf --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Actor; +using FluentAssertions; +using FluentAssertions.Extensions; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests.TestFSMRefTests +{ + public class TestFSMRefSpec : HostingSpec + { + public TestFSMRefSpec(ITestOutputHelper output = null) : base(nameof(TestFSMRefSpec), output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + } + + [Fact] + public async Task A_TestFSMRef_must_allow_access_to_internal_state() + { + var fsm = ActorOfAsTestFSMRef("test-fsm-ref-1"); + + fsm.StateName.Should().Be(1); + fsm.StateData.Should().Be(""); + + fsm.Tell("go"); + fsm.StateName.Should().Be(2); + fsm.StateData.Should().Be("go"); + + fsm.SetState(1); + fsm.StateName.Should().Be(1); + fsm.StateData.Should().Be("go"); + + fsm.SetStateData("buh"); + fsm.StateName.Should().Be(1); + fsm.StateData.Should().Be("buh"); + + fsm.SetStateTimeout(100.Milliseconds()); + await WithinAsync(80.Milliseconds(), 500.Milliseconds(), async () => + await AwaitConditionAsync(() => Task.FromResult(fsm.StateName == 2 && fsm.StateData == "timeout")) + ); + } + + [Fact] + public void A_TestFSMRef_must_allow_access_to_timers() + { + var fsm = ActorOfAsTestFSMRef("test-fsm-ref-2"); + fsm.IsTimerActive("test").Should().Be(false); + fsm.SetTimer("test", 12, 10.Milliseconds(), true); + fsm.IsTimerActive("test").Should().Be(true); + fsm.CancelTimer("test"); + fsm.IsTimerActive("test").Should().Be(false); + } + + private class StateTestFsm : FSM + { + public StateTestFsm() + { + StartWith(1, ""); + + When(1, e => + { + var fsmEvent = e.FsmEvent; + if(Equals(fsmEvent, "go")) + return GoTo(2).Using("go"); + if(fsmEvent is StateTimeout) + return GoTo(2).Using("timeout"); + return null; + }); + + When(2, e => + { + var fsmEvent = e.FsmEvent; + if(Equals(fsmEvent, "back")) + return GoTo(1).Using("back"); + return null; + }); + } + } + private class TimerTestFsm : FSM + { + public TimerTestFsm() + { + StartWith(1, null); + When(1, e => Stay()); + } + } + + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs new file mode 100644 index 00000000..2d881e33 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs @@ -0,0 +1,72 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; +using static FluentAssertions.FluentActions; + +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +{ + public class AwaitAssertTests : HostingSpec + { + public AwaitAssertTests(ITestOutputHelper output) : base(nameof(AwaitAssertTests), output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + builder.AddHocon("akka.test.timefactor=2", HoconAddMode.Prepend); + } + + [Fact] + public async Task AwaitAssertAsync_must_not_throw_any_exception_when_assertion_is_valid() + { + await AwaitAssertAsync(() => Assert.Equal("foo", "foo")); + } + + [Fact] + public async Task AwaitAssertAsync_with_async_delegate_must_not_throw_any_exception_when_assertion_is_valid() + { + await AwaitAssertAsync(() => + { + Assert.Equal("foo", "foo"); + return Task.CompletedTask; + }); + } + + [Fact] + public async Task AwaitAssertAsync_must_throw_exception_when_assertion_is_invalid() + { + await WithinAsync(TimeSpan.FromMilliseconds(300), TimeSpan.FromSeconds(1), async () => + { + await Awaiting(async () => + await AwaitAssertAsync(() => Assert.Equal("foo", "bar"), TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(300))) + .Should().ThrowAsync(); + }); + } + + [Fact] + public async Task AwaitAssertAsync_with_async_delegate_must_throw_exception_when_assertion_is_invalid() + { + await WithinAsync(TimeSpan.FromMilliseconds(300), TimeSpan.FromSeconds(1), async () => + { + await Awaiting(async () => await AwaitAssertAsync(() => + { + Assert.Equal("foo", "bar"); + return Task.CompletedTask; + }, TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(300))) + .Should().ThrowAsync(); + }); + } + + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs new file mode 100644 index 00000000..f6912650 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Diagnostics; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; +using static FluentAssertions.FluentActions; + +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +{ + public class DilatedTests : HostingSpec + { + private const int TimeFactor = 4; + private const int Timeout = 1000; + private const int ExpectedTimeout = Timeout * TimeFactor; + private const int Margin = 1000; // margin for GC + private const int DiffDelta = 100; + + public DilatedTests(ITestOutputHelper output) : base(nameof(DilatedTests), output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + builder.AddHocon($"akka.test.timefactor={TimeFactor}", HoconAddMode.Prepend); + } + + [Fact] + public void Dilates_correctly_using_timeFactor() + { + Assert.Equal(Dilated(TimeSpan.FromMilliseconds(Timeout)), TimeSpan.FromMilliseconds(ExpectedTimeout)); + } + + [Fact] + public async Task AwaitConditionAsync_should_dilate_timeout() + { + var stopwatch = Stopwatch.StartNew(); + await Awaiting(() => AwaitConditionAsync(() => Task.FromResult(false), TimeSpan.FromMilliseconds(Timeout))) + .Should().ThrowAsync(); + stopwatch.Stop(); + AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); + } + + [Fact] + public async Task ReceiveNAsync_should_dilate_timeout() + { + var stopwatch = Stopwatch.StartNew(); + await Awaiting(async () => await ReceiveNAsync(42, TimeSpan.FromMilliseconds(Timeout)).ToListAsync()) + .Should().ThrowAsync(); + stopwatch.Stop(); + AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); + } + + [Fact] + public async Task ExpectMsgAllOfAsync_should_dilate_timeout() + { + var stopwatch = Stopwatch.StartNew(); + await Awaiting(async () => await ExpectMsgAllOfAsync(TimeSpan.FromMilliseconds(Timeout), new []{ "1", "2" }).ToListAsync()) + .Should().ThrowAsync(); + stopwatch.Stop(); + AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); + } + + [Fact] + public async Task FishForMessageAsync_should_dilate_timeout() + { + var stopwatch = Stopwatch.StartNew(); + await Awaiting(async () => await FishForMessageAsync(_=>false, TimeSpan.FromMilliseconds(Timeout))) + .Should().ThrowAsync(); + stopwatch.Stop(); + AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); + } + + private static void AssertDilated(double diff, string message = null) + { + Assert.True(diff >= ExpectedTimeout - DiffDelta, message); + Assert.True(diff < ExpectedTimeout + Margin, message); // margin for GC + } + + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs new file mode 100644 index 00000000..04e685dd --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; +using Akka.Actor; +using static FluentAssertions.FluentActions; + +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +{ + public class ExpectTests : HostingSpec + { + public ExpectTests(ITestOutputHelper output) : base(nameof(ExpectTests), output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + } + + [Fact] + public async Task ExpectMsgAllOfAsync_should_receive_correct_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("3"); + TestActor.Tell("4"); + var messages = await ExpectMsgAllOfAsync(new[] { "3", "1", "4", "2" }).ToListAsync(); + messages.Should().BeEquivalentTo(new[] { "1", "2", "3", "4" }, opt => opt.WithStrictOrdering()); + } + + [Fact] + public async Task ExpectMsgAllOfAsync_should_fail_when_receiving_unexpected() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("Totally unexpected"); + TestActor.Tell("3"); + await Awaiting(async () => + { + await ExpectMsgAllOfAsync(new[] { "3", "1", "2" }).ToListAsync(); + }).Should().ThrowAsync().WithMessage("not found [*"); + } + + [Fact] + public async Task ExpectMsgAllOfAsync_should_timeout_when_not_receiving_any_messages() + { + await Awaiting(async () => + { + await ExpectMsgAllOfAsync(TimeSpan.FromMilliseconds(100), new[] { "3", "1", "2" }).ToListAsync(); + }).Should().ThrowAsync().WithMessage("Timeout (*"); + } + + [Fact] + public async Task ExpectMsgAllOfAsync_should_timeout_if_to_few_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + await Awaiting(async () => + { + await ExpectMsgAllOfAsync(TimeSpan.FromMilliseconds(100), new[] { "3", "1", "2" }).ToListAsync(); + }).Should().ThrowAsync().WithMessage("Timeout (*"); + } + + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs new file mode 100644 index 00000000..f451f5c9 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs @@ -0,0 +1,76 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Linq; +using System.Threading.Tasks; +using FluentAssertions; +using Akka.Actor; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +{ + public class IgnoreMessagesTests : HostingSpec + { + private class IgnoredMessage + { + public IgnoredMessage(string ignoreMe = null) + { + IgnoreMe = ignoreMe; + } + + public string IgnoreMe { get; } + } + + public IgnoreMessagesTests(ITestOutputHelper output) : base(nameof(IgnoreMessagesTests), output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + } + + [Fact] + public async Task IgnoreMessages_should_ignore_messages() + { + IgnoreMessages(o => o is int i && i == 1); + TestActor.Tell(1); + TestActor.Tell("1"); + (await ReceiveOneAsync()).Should().Be("1"); + HasMessages.Should().BeFalse(); + } + + [Fact] + public async Task IgnoreMessages_should_ignore_messages_T() + { + IgnoreMessages(); + + TestActor.Tell("1"); + TestActor.Tell(new IgnoredMessage(), TestActor); + TestActor.Tell("2"); + var messages = await ReceiveNAsync(2).ToListAsync(); + messages.Should().BeEquivalentTo(new[] { "1", "2" }, opt => opt.WithStrictOrdering()); + HasMessages.Should().BeFalse(); + } + + [Fact] + public async Task IgnoreMessages_should_ignore_messages_T_with_Func() + { + IgnoreMessages(m => string.IsNullOrWhiteSpace(m.IgnoreMe)); + + var msg = new IgnoredMessage("not ignored!"); + + TestActor.Tell("1"); + TestActor.Tell(msg, TestActor); + TestActor.Tell("2"); + var messages = await ReceiveNAsync(3).ToListAsync(); + messages.Should().BeEquivalentTo(new object[] { "1", msg, "2" }, opt => opt.WithStrictOrdering()); + HasMessages.Should().BeFalse(); + } + } +} diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs new file mode 100644 index 00000000..c00b02a2 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs @@ -0,0 +1,279 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Linq; +using System.Threading.Tasks; +using Akka.Actor; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; +using static FluentAssertions.FluentActions; + +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +{ + public class ReceiveTests : HostingSpec + { + public ReceiveTests(ITestOutputHelper output) : base(nameof(ReceiveTests), output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + } + + [Fact] + public async Task ReceiveNAsync_should_receive_correct_number_of_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("3"); + TestActor.Tell("4"); + var messages = await ReceiveNAsync(3).ToListAsync(); + messages.Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); + messages = await ReceiveNAsync(1).ToListAsync(); + messages.Should().BeEquivalentTo(new[] { "4" }); + } + + [Fact] + public async Task ReceiveNAsync_should_timeout_if_no_messages() + { + await Awaiting(async () => await ReceiveNAsync(3, TimeSpan.FromMilliseconds(10)).ToListAsync()) + .Should().ThrowAsync(); + } + + [Fact] + public async Task ReceiveNAsync_should_timeout_if_to_few_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + await Awaiting(async () => await ReceiveNAsync(3, TimeSpan.FromMilliseconds(100)).ToListAsync()) + .Should().ThrowAsync(); + } + + [Fact] + public async Task FishForMessageAsync_should_return_matched_message() + { + TestActor.Tell(1); + TestActor.Tell(2); + TestActor.Tell(10); + TestActor.Tell(20); + (await FishForMessageAsync(i => i >= 10)).Should().Be(10); + } + + [Fact] + public async Task FishForMessageAsync_should_timeout_if_no_messages() + { + await Awaiting(async () => await FishForMessageAsync(_ => false, TimeSpan.FromMilliseconds(10))) + .Should().ThrowAsync(); + } + + [Fact] + public async Task FishForMessageAsync_should_timeout_if_too_few_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + await Awaiting(async () => await FishForMessageAsync(_ => false, TimeSpan.FromMilliseconds(100))) + .Should().ThrowAsync(); + } + + [Fact] + public async Task FishForMessageAsync_should_fill_the_all_messages_param_if_not_null() + { + var probe = CreateTestProbe("probe"); + probe.Tell("1"); + probe.Tell(2); + probe.Tell("3"); + probe.Tell(4); + var allMessages = new ArrayList(); + await probe.FishForMessageAsync(isMessage: s => s == "3", allMessages: allMessages); + allMessages.Should().BeEquivalentTo(new ArrayList { "1", 2 }); + } + + [Fact] + public async Task FishForMessageAsync_should_clear_the_all_messages_param_if_not_null_before_filling_it() + { + var probe = CreateTestProbe("probe"); + probe.Tell("1"); + probe.Tell(2); + probe.Tell("3"); + probe.Tell(4); + var allMessages = new ArrayList { "pre filled data" }; + await probe.FishForMessageAsync(isMessage: x => x == "3", allMessages: allMessages); + allMessages.Should().BeEquivalentTo(new ArrayList { "1", 2 }); + } + + [Fact] + public async Task FishUntilMessageAsync_should_succeed_with_good_input() + { + var probe = CreateTestProbe("probe"); + probe.Ref.Tell(1d, TestActor); + await Awaiting(() => probe.FishUntilMessageAsync(max: TimeSpan.FromMilliseconds(10))) + .Should().NotThrowAsync(); + } + + [Fact] + public async Task FishUntilMessageAsync_should_fail_with_bad_input() + { + var probe = CreateTestProbe("probe"); + probe.Ref.Tell(3, TestActor); + await Awaiting(() => probe.FishUntilMessageAsync(max: TimeSpan.FromMilliseconds(10))) + .Should().ThrowAsync(); + } + + [Fact] + public async Task WaitForRadioSilenceAsync_should_succeed_immediately_with_null_good_input() + { + var probe = CreateTestProbe("probe"); + var messages = await probe.WaitForRadioSilenceAsync(max: TimeSpan.FromMilliseconds(0)); + messages.Should().BeEquivalentTo(new ArrayList()); + } + + [Fact] + public async Task WaitForRadioSilenceAsync_should_succeed_immediately_with_good_pre_input() + { + var probe = CreateTestProbe("probe"); + probe.Ref.Tell(1, TestActor); + var messages = await probe.WaitForRadioSilenceAsync(max: TimeSpan.FromMilliseconds(0)); + messages.Should().BeEquivalentTo(new ArrayList { 1 }); + } + + [Fact] + public async Task WaitForRadioSilenceAsync_should_succeed_later_with_good_post_input() + { + var probe = CreateTestProbe("probe"); + var task = probe.WaitForRadioSilenceAsync(); + probe.Ref.Tell(1, TestActor); + var messages = await task; + messages.Should().BeEquivalentTo(new ArrayList { 1 }); + } + + [Fact] + public async Task WaitForRadioSilenceAsync_should_reset_timer_twice_only() + { + var probe = CreateTestProbe("probe"); + var max = TimeSpan.FromMilliseconds(3000); + var halfMax = TimeSpan.FromMilliseconds(max.TotalMilliseconds / 2); + var doubleMax = TimeSpan.FromMilliseconds(max.TotalMilliseconds * 2); + var task = probe.WaitForRadioSilenceAsync(max: max, maxMessages: 2); + await Task.Delay(halfMax); + probe.Ref.Tell(1, TestActor); + await Task.Delay(halfMax); + probe.Ref.Tell(2, TestActor); + await Task.Delay(doubleMax); + probe.Ref.Tell(3, TestActor); + var messages = await task; + messages.Should().BeEquivalentTo(new ArrayList { 1, 2 }); + } + + [Fact] + public async Task WaitForRadioSilenceAsync_should_fail_immediately_with_bad_input() + { + var probe = CreateTestProbe("probe"); + probe.Ref.Tell(3, TestActor); + await Awaiting(() => probe.WaitForRadioSilenceAsync(max: TimeSpan.FromMilliseconds(0), maxMessages: 0)) + .Should().ThrowAsync().WithMessage("maxMessages violated*"); + } + + [Fact] + public async Task ReceiveWhileAsync_Filter_should_on_a_timeout_return_no_messages() + { + await using var e = ReceiveWhileAsync(_ => _, TimeSpan.FromMilliseconds(10)).GetAsyncEnumerator(); + (await e.MoveNextAsync()).Should().BeFalse(); + } + + [Fact] + public async Task ReceiveWhileAsync_Filter_should_break_on_function_returning_null_and_return_correct_messages() + { + TestActor.Tell("1"); + TestActor.Tell(2); + TestActor.Tell("3"); + TestActor.Tell(99999.0); + TestActor.Tell(4); + var messages = await ReceiveWhileAsync(_ => _ is double ? null : _.ToString()).ToListAsync(); + messages.Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); + } + + [Fact] + public async Task ReceiveWhileAsync_Filter_should_not_consume_last_message_that_didnt_match() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell(4711); + await ReceiveWhileAsync(_ => _ is string ? _ : null).ToListAsync(); + await ExpectMsgAsync(4711); + } + + [Fact] + public async Task ReceiveWhileAsync_Predicate_should_on_a_timeout_return_no_messages() + { + await using var e = ReceiveWhileAsync(_ => false, TimeSpan.FromMilliseconds(10)).GetAsyncEnumerator(); + (await e.MoveNextAsync()).Should().BeFalse(); + } + + [Fact] + public async Task ReceiveWhileAsync_Predicate_should_break_when_predicate_returns_false_and_return_correct_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("3"); + TestActor.Tell("-----------"); + TestActor.Tell("4"); + var messages = await ReceiveWhileAsync(s => s.Length == 1).ToListAsync(); + messages.Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); + } + + [Fact] + public async Task ReceiveWhileAsync_Predicate_should_break_when_type_is_wrong_and_we_dont_ignore_those_and_return_correct_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("3"); + TestActor.Tell(4); + TestActor.Tell("5"); + var messages = await ReceiveWhileAsync(s => s.Length == 1, shouldIgnoreOtherMessageTypes: false).ToListAsync(); + messages.Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); + } + + [Fact] + public async Task ReceiveWhileAsync_Predicate_should_continue_when_type_is_other_but_we_ignore_other_types_and_return_correct_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("3"); + TestActor.Tell(4); + TestActor.Tell("5"); + var messages = await ReceiveWhileAsync(s => s.Length == 1, shouldIgnoreOtherMessageTypes: true).ToListAsync(); + messages.Should().BeEquivalentTo(new[] { "1", "2", "3", "5" }, opt => opt.WithStrictOrdering()); + } + + [Fact] + public async Task ReceiveWhileAsync_Predicate_should_not_consume_last_message_that_didnt_match() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell(4711); + TestActor.Tell("3"); + TestActor.Tell("4"); + TestActor.Tell("5"); + TestActor.Tell(6); + TestActor.Tell("7"); + TestActor.Tell("8"); + + var messages = await ReceiveWhileAsync(_ => _ is string).ToListAsync(); + messages.Should().BeEquivalentTo(new[] { "1", "2" }, opt => opt.WithStrictOrdering()); + await ExpectMsgAsync(4711); + + messages = await ReceiveWhileAsync(_ => _ is string).ToListAsync(); + messages.Should().BeEquivalentTo(new[] { "3", "4", "5" }, opt => opt.WithStrictOrdering()); + await ExpectMsgAsync(6); + } + } +} diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs new file mode 100644 index 00000000..04ca971b --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs @@ -0,0 +1,22 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using Xunit; + +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +{ + public class RemainingTests : Akka.TestKit.Xunit2.TestKit + { + [Fact] + public void Throw_if_remaining_is_called_outside_Within() + { + Assert.Throws(() => Remaining); + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs new file mode 100644 index 00000000..92b132c3 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs @@ -0,0 +1,75 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using FluentAssertions; +using FluentAssertions.Extensions; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; +using static FluentAssertions.FluentActions; + +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +{ + public class WithinTests : HostingSpec + { + public WithinTests(ITestOutputHelper output) : base(nameof(WithinTests), output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + } + + [Fact] + public void Within_should_increase_max_timeout_by_the_provided_epsilon_value() + { + Within(TimeSpan.FromSeconds(1), () => ExpectNoMsg(), TimeSpan.FromMilliseconds(50)); + } + + [Fact] + public void Within_should_respect_minimum_time() + { + Within(0.3.Seconds(), 1.Seconds(), () => ExpectNoMsg(0.4.Seconds()), "", 0.1.Seconds()); + } + + [Fact] + public async Task WithinAsync_should_respect_minimum_time() + { + await WithinAsync( + 0.3.Seconds(), + 1.Seconds(), + async () => await ExpectNoMsgAsync(0.4.Seconds()), + "", + 0.1.Seconds()); + } + + [Fact] + public void Within_should_throw_if_execution_is_shorter_than_minimum_time() + { + Invoking(() => + { + Within(0.5.Seconds(), 1.Seconds(), () => ExpectNoMsg(0.1.Seconds()), null, 0.1.Seconds()); + }).Should().Throw(); + } + + [Fact] + public async Task WithinAsync_should_throw_if_execution_is_shorter_than_minimum_time() + { + await Awaiting(async () => + { + await WithinAsync( + 0.5.Seconds(), + 1.Seconds(), + async () => await ExpectNoMsgAsync(0.1.Seconds()), + null, + 0.1.Seconds()); + }).Should().ThrowAsync(); + } + } +} diff --git a/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs b/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs new file mode 100644 index 00000000..49783f75 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs @@ -0,0 +1,43 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Reflection; +using Akka.TestKit; +using FluentAssertions; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests +{ + // ReSharper disable once InconsistentNaming + public class TestKit_Config_Tests : HostingSpec + { + public TestKit_Config_Tests(ITestOutputHelper output) : base("TestKitConfigTests", output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { } + + [Fact] + public void DefaultValues_should_be_correct() + { + TestKitSettings.DefaultTimeout.Should().Be(TimeSpan.FromSeconds(5)); + TestKitSettings.SingleExpectDefault.Should().Be(TimeSpan.FromSeconds(3)); + TestKitSettings.TestEventFilterLeeway.Should().Be(TimeSpan.FromSeconds(3)); + TestKitSettings.TestTimeFactor.Should().Be(1); + var callingThreadDispatcherTypeName = typeof(CallingThreadDispatcherConfigurator).FullName + ", " + typeof(CallingThreadDispatcher).GetTypeInfo().Assembly.GetName().Name; + Assert.False(Sys.Settings.Config.IsEmpty); + Sys.Settings.Config.GetString("akka.test.calling-thread-dispatcher.type", null).Should().Be(callingThreadDispatcherTypeName); + Sys.Settings.Config.GetString("akka.test.test-actor.dispatcher.type", null).Should().Be(callingThreadDispatcherTypeName); + CallingThreadDispatcher.Id.Should().Be("akka.test.calling-thread-dispatcher"); + } + } +} + diff --git a/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs b/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs new file mode 100644 index 00000000..8692406f --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs @@ -0,0 +1,205 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.TestKit; +using Akka.TestKit.Configs; +using Xunit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Tests +{ + public class TestSchedulerTests : HostingSpec + { + private enum TestActorKey + { } + + private IActorRef _testReceiveActor; + + public TestSchedulerTests(ITestOutputHelper output) : base(nameof(TestSchedulerTests), output) + { + } + + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + builder.AddHocon(TestConfigs.TestSchedulerConfig); + builder.WithActors((system, registry) => + { + var actor = system.ActorOf(Props.Create(() => new TestReceiveActor()) + .WithDispatcher(CallingThreadDispatcher.Id)); + registry.Register(actor); + }); + } + + protected override async Task BeforeTestStart() + { + await base.BeforeTestStart(); + _testReceiveActor = ActorRegistry.Get(); + } + + [Fact] + public async Task Delivers_message_when_scheduled_time_reached() + { + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1))); + await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started + + Scheduler.Advance(TimeSpan.FromSeconds(1)); + await ExpectMsgAsync(); + } + + [Fact] + public async Task Does_not_deliver_message_prematurely() + { + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1))); + await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started + + Scheduler.Advance(TimeSpan.FromMilliseconds(999)); + await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(20)); + } + + [Fact] + public async Task Delivers_messages_scheduled_for_same_time_in_order_they_were_added() + { + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1), 1)); + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1), 2)); + await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started + + Scheduler.Advance(TimeSpan.FromSeconds(1)); + var firstId = (await ExpectMsgAsync()).Id; + var secondId = (await ExpectMsgAsync()).Id; + Assert.Equal(1, firstId); + Assert.Equal(2, secondId); + } + + [Fact] + public async Task Keeps_delivering_rescheduled_message() + { + _testReceiveActor.Tell(new RescheduleMessage(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5))); + await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started + + for (int i = 0; i < 500; i ++) + { + Scheduler.Advance(TimeSpan.FromSeconds(5)); + await ExpectMsgAsync(); + } + } + + [Fact] + public async Task Uses_initial_delay_to_schedule_first_rescheduled_message() + { + _testReceiveActor.Tell(new RescheduleMessage(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5))); + await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started + + Scheduler.Advance(TimeSpan.FromSeconds(1)); + await ExpectMsgAsync(); + } + + [Fact] + public async Task Doesnt_reschedule_cancelled() + { + _testReceiveActor.Tell(new CancelableMessage(TimeSpan.FromSeconds(1))); + await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started + + Scheduler.Advance(TimeSpan.FromSeconds(1)); + await ExpectMsgAsync(); + _testReceiveActor.Tell(new CancelMessage()); + Scheduler.Advance(TimeSpan.FromSeconds(1)); + await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(20)); + } + + + [Fact] + public async Task Advance_to_takes_us_to_correct_time() + { + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1), 1)); + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(2), 2)); + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(3), 3)); + await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started + + Scheduler.AdvanceTo(Scheduler.Now.AddSeconds(2)); + var firstId = (await ExpectMsgAsync()).Id; + var secondId = (await ExpectMsgAsync()).Id; + await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(20)); + Assert.Equal(1, firstId); + Assert.Equal(2, secondId); + } + + private class TestReceiveActor : ReceiveActor + { + private Cancelable _cancelable; + + public TestReceiveActor() + { + Receive(x => + { + Context.System.Scheduler.ScheduleTellOnce(x.ScheduleOffset, Sender, x, Self); + }); + + Receive(x => + { + Context.System.Scheduler.ScheduleTellRepeatedly(x.InitialOffset, x.ScheduleOffset, Sender, x, Self); + }); + + Receive(x => + { + _cancelable = new Cancelable(Context.System.Scheduler); + Context.System.Scheduler.ScheduleTellRepeatedly(x.ScheduleOffset, x.ScheduleOffset, Sender, x, Self, _cancelable); + }); + + Receive(x => + { + _cancelable.Cancel(); + }); + + } + } + + private class CancelableMessage + { + public TimeSpan ScheduleOffset { get; } + public int Id { get; } + + public CancelableMessage(TimeSpan scheduleOffset, int id = 1) + { + ScheduleOffset = scheduleOffset; + Id = id; + } + } + + private class CancelMessage { } + + private class ScheduleOnceMessage + { + public TimeSpan ScheduleOffset { get; } + public int Id { get; } + + public ScheduleOnceMessage(TimeSpan scheduleOffset, int id = 1) + { + ScheduleOffset = scheduleOffset; + Id = id; + } + } + + private class RescheduleMessage + { + public TimeSpan InitialOffset { get; } + public TimeSpan ScheduleOffset { get; } + public int Id { get; } + + public RescheduleMessage(TimeSpan initialOffset, TimeSpan scheduleOffset, int id = 1) + { + InitialOffset = initialOffset; + ScheduleOffset = scheduleOffset; + Id = id; + } + } + + private TestScheduler Scheduler => (TestScheduler)Sys.Scheduler; + } +} diff --git a/src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj b/src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj new file mode 100644 index 00000000..798876a8 --- /dev/null +++ b/src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj @@ -0,0 +1,19 @@ + + + + TestKit for writing tests for Akka.NET using Akka.Hosting and xUnit. + $(LibraryFramework) + true + 8.0 + + + + + + + + + + + + diff --git a/src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj.DotSettings b/src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj.DotSettings new file mode 100644 index 00000000..00152058 --- /dev/null +++ b/src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/HostingSpec.cs b/src/Akka.Hosting.TestKit/HostingSpec.cs new file mode 100644 index 00000000..c73c1d4a --- /dev/null +++ b/src/Akka.Hosting.TestKit/HostingSpec.cs @@ -0,0 +1,188 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.Threading; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Annotations; +using Akka.Configuration; +using Akka.Hosting.TestKit.Internals; +using Akka.TestKit; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Xunit; +using Xunit.Abstractions; +using Xunit.Sdk; + +namespace Akka.Hosting.TestKit +{ + public abstract partial class HostingSpec: IAsyncLifetime + { + private IHost _host; + public IHost Host + { + get + { + AssertNotNull(_host); + return _host; + } + } + + private TestKitBaseUnWrapper _testKit; + public Akka.TestKit.Xunit2.TestKit TestKit + { + get + { + AssertNotNull(_testKit); + return _testKit; + } + } + + public ActorRegistry ActorRegistry => Host.Services.GetRequiredService(); + + public TimeSpan StartupTimeout { get; } + public string ActorSystemName { get; } + public ITestOutputHelper Output { get; } + public LogLevel LogLevel { get; } + + protected HostingSpec(string actorSystemName, ITestOutputHelper output = null, TimeSpan? startupTimeout = null, LogLevel logLevel = LogLevel.Information) + { + ActorSystemName = actorSystemName; + Output = output; + LogLevel = logLevel; + StartupTimeout = startupTimeout ?? TimeSpan.FromSeconds(10); + } + + protected virtual void ConfigureHostConfiguration(IConfigurationBuilder builder) + { } + + protected virtual void ConfigureAppConfiguration(HostBuilderContext context, IConfigurationBuilder builder) + { } + + protected virtual void ConfigureServices(HostBuilderContext context, IServiceCollection services) + { } + + private void InternalConfigureServices(HostBuilderContext context, IServiceCollection services) + { + ConfigureServices(context, services); + + services.AddAkka(ActorSystemName, (builder, provider) => + { + builder.AddHocon( + Config != null ? Config.WithFallback(TestKitBase.DefaultConfig) : TestKitBase.DefaultConfig, + HoconAddMode.Prepend); + + /* + builder.ConfigureLoggers(logger => + { + logger.LogLevel = ToAkkaLogLevel(LogLevel); + if (Output != null) + { + logger.AddLoggerFactory(); + } + }); + */ + + ConfigureAkka(builder, provider); + }); + } + + protected virtual Config Config { get; } = null; + + protected virtual void ConfigureLogging(ILoggingBuilder builder) + { } + + protected virtual void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { } + + [InternalApi] + public async Task InitializeAsync() + { + var hostBuilder = new HostBuilder(); + if (Output != null) + hostBuilder.ConfigureLogging(logger => + { + logger.ClearProviders(); + logger.AddProvider(new XUnitLoggerProvider(Output, LogLevel)); + logger.AddFilter("Akka.*", LogLevel); + ConfigureLogging(logger); + }); + hostBuilder + .ConfigureHostConfiguration(ConfigureHostConfiguration) + .ConfigureAppConfiguration(ConfigureAppConfiguration) + .ConfigureServices(InternalConfigureServices); + + _host = hostBuilder.Build(); + + var cts = new CancellationTokenSource(StartupTimeout); + cts.Token.Register(() => + throw new TimeoutException($"Host failed to start within {StartupTimeout.Seconds} seconds")); + try + { + await _host.StartAsync(cts.Token); + } + finally + { + cts.Dispose(); + } + + _sys = _host.Services.GetRequiredService(); + _testKit = new TestKitBaseUnWrapper(_sys); + + await BeforeTestStart(); + } + + protected virtual Task BeforeTestStart() + { + return Task.CompletedTask; + } + + /// + /// This method is called when a test ends. + /// + /// + /// If you override this, then make sure you either call base.AfterAllAsync() + /// to shut down the system. Otherwise a memory leak will occur. + /// + /// + protected virtual async Task AfterAllAsync() + { + await ShutdownAsync(); + } + + public async Task DisposeAsync() + { + await AfterAllAsync(); + if(_host != null) + { + await _host.StopAsync(); + _host.Dispose(); + } + } + + private static Event.LogLevel ToAkkaLogLevel(LogLevel logLevel) + => logLevel switch + { + LogLevel.Trace => Event.LogLevel.DebugLevel, + LogLevel.Debug => Event.LogLevel.DebugLevel, + LogLevel.Information => Event.LogLevel.InfoLevel, + LogLevel.Warning => Event.LogLevel.WarningLevel, + LogLevel.Error => Event.LogLevel.ErrorLevel, + LogLevel.Critical => Event.LogLevel.ErrorLevel, + _ => Event.LogLevel.ErrorLevel + }; + + private static void AssertNotNull(object obj) + { + if(obj is null) + throw new XunitException("Test has not been initialized yet"); + } + } +} + diff --git a/src/Akka.Hosting.TestKit/Internals/XUnitLogger.cs b/src/Akka.Hosting.TestKit/Internals/XUnitLogger.cs new file mode 100644 index 00000000..f822c5dd --- /dev/null +++ b/src/Akka.Hosting.TestKit/Internals/XUnitLogger.cs @@ -0,0 +1,86 @@ +using System; +using Microsoft.Extensions.Logging; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Internals +{ + public class XUnitLogger: ILogger + { + private const string NullFormatted = "[null]"; + + private readonly string _category; + private readonly ITestOutputHelper _helper; + private readonly LogLevel _logLevel; + + public XUnitLogger(string category, ITestOutputHelper helper, LogLevel logLevel) + { + _category = category; + _helper = helper; + _logLevel = logLevel; + } + + public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) + { + if (!IsEnabled(logLevel)) + return; + + if (!TryFormatMessage(state, exception, formatter, out var formattedMessage)) + return; + + WriteLogEntry(logLevel, eventId, formattedMessage, exception); + } + + private void WriteLogEntry(LogLevel logLevel, EventId eventId, string message, Exception exception) + { + var level = logLevel switch + { + LogLevel.Critical => "CRT", + LogLevel.Debug => "DBG", + LogLevel.Error => "ERR", + LogLevel.Information => "INF", + LogLevel.Warning => "WRN", + LogLevel.Trace => "DBG", + _ => "???" + }; + + var msg = $"{DateTime.Now}:{level}:{_category}:{eventId} {message}"; + if (exception != null) + msg += $"\n{exception.GetType()} {exception.Message}\n{exception.StackTrace}"; + _helper.WriteLine(msg); + } + + public bool IsEnabled(LogLevel logLevel) + { + return logLevel switch + { + LogLevel.None => false, + _ => logLevel >= _logLevel + }; + } + + public IDisposable BeginScope(TState state) + { + throw new NotImplementedException(); + } + + private static bool TryFormatMessage( + TState state, + Exception exception, + Func formatter, + out string result) + { + formatter = formatter ?? throw new ArgumentNullException(nameof(formatter)); + + var formattedMessage = formatter(state, exception); + if (formattedMessage == NullFormatted) + { + result = null; + return false; + } + + result = formattedMessage; + return true; + } + } +} + diff --git a/src/Akka.Hosting.TestKit/Internals/XUnitLoggerProvider.cs b/src/Akka.Hosting.TestKit/Internals/XUnitLoggerProvider.cs new file mode 100644 index 00000000..024d312c --- /dev/null +++ b/src/Akka.Hosting.TestKit/Internals/XUnitLoggerProvider.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Logging; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit.Internals +{ + public class XUnitLoggerProvider : ILoggerProvider + { + private readonly ITestOutputHelper _helper; + private readonly LogLevel _logLevel; + + public XUnitLoggerProvider(ITestOutputHelper helper, LogLevel logLevel) + { + _helper = helper; + _logLevel = logLevel; + } + + public void Dispose() + { + // no-op + } + + public ILogger CreateLogger(string categoryName) + { + return new XUnitLogger(categoryName, _helper, _logLevel); + } + } +} + diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ActorOf.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ActorOf.cs new file mode 100644 index 00000000..357563d3 --- /dev/null +++ b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ActorOf.cs @@ -0,0 +1,313 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.Linq.Expressions; +using Akka.Actor; +using Akka.Actor.Dsl; +using Akka.TestKit; + +namespace Akka.Hosting.TestKit +{ + public abstract partial class HostingSpec + { + /// + /// Create a new actor as child of . + /// + /// The props configuration object + /// TBD + public IActorRef ActorOf(Props props) + => TestKit.ActorOf(props); + + /// + /// Create a new actor as child of . + /// + /// The props configuration object + /// The name of the actor. + /// TBD + public IActorRef ActorOf(Props props, string name) + => TestKit.ActorOf(props, name); + + /// + /// Create a new actor as child of . + /// + /// The type of the actor. It must have a parameterless public constructor + /// TBD + public IActorRef ActorOf() where TActor : ActorBase, new() + => TestKit.ActorOf(); + + /// + /// Create a new actor as child of . + /// + /// The type of the actor. It must have a parameterless public constructor + /// The name of the actor. + /// TBD + public IActorRef ActorOf(string name) where TActor : ActorBase, new() + => TestKit.ActorOf(name); + + /// + /// Create a new actor as child of using an expression that calls the constructor + /// of . + /// + /// ActorOf<MyActor>(()=>new MyActor("value", 4711)) + /// + /// + /// The type of the actor. + /// An expression that calls the constructor of + /// TBD + public IActorRef ActorOf(Expression> factory) where TActor : ActorBase + => TestKit.ActorOf(factory); + + /// + /// Create a new actor as child of using an expression that calls the constructor + /// of . + /// + /// ActorOf<MyActor>(()=>new MyActor("value", 4711), "test-actor") + /// + /// + /// The type of the actor. + /// An expression that calls the constructor of + /// The name of the actor. + /// TBD + public IActorRef ActorOf(Expression> factory, string name) where TActor : ActorBase + => TestKit.ActorOf(factory, name); + + /// + /// Creates a new actor by defining the behavior inside the action. + /// + /// + /// ActorOf(c => + /// { + /// c.Receive<string>((msg, ctx) => ctx.Sender.Tell("Hello " + msg)); + /// }); + /// + /// + /// + /// An action that configures the actor's behavior. + /// Optional: The name of the actor. + /// TBD + public IActorRef ActorOf(Action configure, string name = null) + => TestKit.ActorOf(configure, name); + + /// + /// Creates a new actor by defining the behavior inside the action. + /// + /// + /// ActorOf(c => + /// { + /// c.Receive<string>((msg, ctx) => ctx.Sender.Tell("Hello " + msg)); + /// }); + /// + /// + /// + /// An action that configures the actor's behavior. + /// Optional: The name of the actor. + /// TBD + public IActorRef ActorOf(Action configure, string name = null) + => TestKit.ActorOf(configure, name); + + /// + /// Creates an + /// + /// The path of the actor(s) we want to select. + /// An ActorSelection + public ActorSelection ActorSelection(ActorPath actorPath) + => TestKit.ActorSelection(actorPath); + + /// + /// Creates an + /// + /// The path of the actor(s) we want to select. + /// An ActorSelection + public ActorSelection ActorSelection(string actorPath) + => TestKit.ActorSelection(actorPath); + + /// + /// Creates an + /// + /// The base actor that anchors the . + /// The path of the actor(s) we want to select. + /// An ActorSelection + public ActorSelection ActorSelection(IActorRef anchorRef, string actorPath) + => TestKit.ActorSelection(anchorRef, actorPath); + + /// + /// Create a new actor as child of specified supervisor and returns it as + /// to enable access to the underlying actor instance via . + /// + /// The type of the actor. It must have a parameterless public constructor + /// The object + /// The supervisor + /// Optional: The name. + /// TBD + public TestActorRef ActorOfAsTestActorRef(Props props, IActorRef supervisor, string name = null) + where TActor : ActorBase + => TestKit.ActorOfAsTestActorRef(props, supervisor, name); + + /// + /// Create a new actor as child of and returns it as + /// to enable access to the underlying actor instance via . + /// + /// The type of the actor. It must have a parameterless public constructor + /// The object + /// Optional: The name. + /// TBD + public TestActorRef ActorOfAsTestActorRef(Props props, string name = null) where TActor : ActorBase + => TestKit.ActorOfAsTestActorRef(props, name); + + /// + /// Create a new actor as child of the specified supervisor and returns it as + /// to enable access to the underlying actor instance via . + /// Uses an expression that calls the constructor of . + /// + /// ActorOf<MyActor>(()=>new MyActor("value", 4711), "test-actor") + /// + /// + /// The type of the actor. + /// An expression that calls the constructor of + /// The supervisor + /// Optional: The name. + /// TBD + public TestActorRef ActorOfAsTestActorRef(Expression> factory, IActorRef supervisor, string name = null) where TActor : ActorBase + => TestKit.ActorOfAsTestActorRef(factory, supervisor, name); + + /// + /// Create a new actor as child of and returns it as + /// to enable access to the underlying actor instance via . + /// Uses an expression that calls the constructor of . + /// + /// ActorOf<MyActor>(()=>new MyActor("value", 4711), "test-actor") + /// + /// + /// The type of the actor. + /// An expression that calls the constructor of + /// Optional: The name. + /// TBD + public TestActorRef ActorOfAsTestActorRef(Expression> factory, string name = null) where TActor : ActorBase + => TestKit.ActorOfAsTestActorRef(factory, name); + + /// + /// Create a new actor as child of the specified supervisor and returns it as + /// to enable access to the underlying actor instance via . + /// + /// The type of the actor. It must have a parameterless public constructor + /// The supervisor + /// Optional: The name. + /// TBD + public TestActorRef ActorOfAsTestActorRef(IActorRef supervisor, string name = null) where TActor : ActorBase, new() + => TestKit.ActorOfAsTestActorRef(supervisor, name); + + /// + /// Create a new actor as child of and returns it as + /// to enable access to the underlying actor instance via . + /// + /// The type of the actor. It must have a parameterless public constructor + /// Optional: The name. + /// TBD + public TestActorRef ActorOfAsTestActorRef(string name = null) where TActor : ActorBase, new() + => TestKit.ActorOfAsTestActorRef(name); + + + /// + /// Create a new as child of the specified supervisor + /// and returns it as to enable inspecting and modifying the FSM directly. + /// + /// The type of the actor. It must be a + /// The type of state name + /// The type of state data + /// The object + /// The supervisor + /// Optional: The name. + /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. + /// TBD + public TestFSMRef ActorOfAsTestFSMRef(Props props, + IActorRef supervisor, string name = null, bool withLogging = false) + where TFsmActor : FSM + => TestKit.ActorOfAsTestFSMRef(props, supervisor, name, withLogging); + + /// + /// Create a new as child of + /// and returns it as to enable inspecting and modifying the FSM directly. + /// + /// The type of the actor. It must be a and have a public parameterless constructor + /// The type of state name + /// The type of state data + /// The object + /// Optional: The name. + /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. + /// TBD + public TestFSMRef ActorOfAsTestFSMRef(Props props, string name = null, bool withLogging = false) + where TFsmActor : FSM + => TestKit.ActorOfAsTestFSMRef(props, name, withLogging); + + + /// + /// Create a new as child of the specified supervisor + /// and returns it as to enable inspecting and modifying the FSM directly. + /// must have a public parameterless constructor. + /// + /// The type of the actor. It must have a parameterless public constructor + /// The type of state name + /// The type of state data + /// The supervisor + /// Optional: The name. + /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. + /// TBD + public TestFSMRef ActorOfAsTestFSMRef(IActorRef supervisor, string name = null, bool withLogging = false) + where TFsmActor : FSM, new() + => TestKit.ActorOfAsTestFSMRef(supervisor, name, withLogging); + + /// + /// Create a new as child of + /// and returns it as to enable inspecting and modifying the FSM directly. + /// must have a public parameterless constructor. + /// + /// The type of the actor. It must have a parameterless public constructor + /// The type of state name + /// The type of state data + /// Optional: The name. + /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. + /// TBD + public TestFSMRef ActorOfAsTestFSMRef(string name = null, bool withLogging = false) + where TFsmActor : FSM, new() + => TestKit.ActorOfAsTestFSMRef(name, withLogging); + + /// + /// Create a new as child of the specified supervisor + /// and returns it as to enable inspecting and modifying the FSM directly. + /// Uses an expression that calls the constructor of . + /// + /// The type of the actor. + /// The type of state name + /// The type of state data + /// An expression that calls the constructor of + /// The supervisor + /// Optional: The name. + /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. + /// TBD + public TestFSMRef ActorOfAsTestFSMRef(Expression> factory, IActorRef supervisor, string name = null, bool withLogging = false) + where TFsmActor : FSM + => TestKit.ActorOfAsTestFSMRef(factory, supervisor, name, withLogging); + + /// + /// Create a new as child of + /// and returns it as to enable inspecting and modifying the FSM directly. + /// Uses an expression that calls the constructor of . + /// + /// The type of the actor. + /// The type of state name + /// The type of state data + /// An expression that calls the constructor of + /// Optional: The name. + /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. + /// TBD + public TestFSMRef ActorOfAsTestFSMRef(Expression> factory, string name = null, bool withLogging = false) + where TFsmActor : FSM + => TestKit.ActorOfAsTestFSMRef(factory, name, withLogging); + + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitAssert.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitAssert.cs new file mode 100644 index 00000000..ec7e0f58 --- /dev/null +++ b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitAssert.cs @@ -0,0 +1,58 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Akka.Hosting.TestKit +{ + public abstract partial class HostingSpec + { + /// + /// Await until the given assertion does not throw an exception or the timeout + /// expires, whichever comes first. If the timeout expires the last exception + /// is thrown. + /// The action is called, and if it throws an exception the thread sleeps + /// the specified interval before retrying. + /// If no timeout is given, take it from the innermost enclosing `within` + /// block. + /// Note that the timeout is scaled using , + /// which uses the configuration entry "akka.test.timefactor". + /// + /// The action. + /// The timeout. + /// The interval to wait between executing the assertion. + /// + public void AwaitAssert(Action assertion, TimeSpan? duration = null, TimeSpan? interval = null, + CancellationToken cancellationToken = default) + => TestKit.AwaitAssert(assertion, duration, interval, cancellationToken); + + /// + public Task AwaitAssertAsync(Action assertion, TimeSpan? duration=null, TimeSpan? interval=null, CancellationToken cancellationToken = default) + => TestKit.AwaitAssertAsync(assertion, duration, interval, cancellationToken); + + /// + /// Await until the given assertion does not throw an exception or the timeout + /// expires, whichever comes first. If the timeout expires the last exception + /// is thrown. + /// The action is called, and if it throws an exception the thread sleeps + /// the specified interval before retrying. + /// If no timeout is given, take it from the innermost enclosing `within` + /// block. + /// Note that the timeout is scaled using , + /// which uses the configuration entry "akka.test.timefactor". + /// + /// The action. + /// The timeout. + /// The interval to wait between executing the assertion. + /// + public Task AwaitAssertAsync(Func assertion, TimeSpan? duration=null, TimeSpan? interval=null, CancellationToken cancellationToken = default) + => TestKit.AwaitAssertAsync(assertion, duration, interval, cancellationToken); + + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitCondition.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitCondition.cs new file mode 100644 index 00000000..6d36e234 --- /dev/null +++ b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitCondition.cs @@ -0,0 +1,142 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Akka.Hosting.TestKit +{ + public abstract partial class HostingSpec + { + /// + /// Await until the given condition evaluates to true or until a timeout + /// The timeout is taken from the innermost enclosing `within` + /// block (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". + /// The value is dilated, i.e. scaled by the factor + /// specified in config value "akka.test.timefactor".. + /// A call to is done immediately, then the threads sleep + /// for about a tenth of the timeout value, before it checks the condition again. This is repeated until + /// timeout or the condition evaluates to true. To specify another interval, use the overload + /// + /// + /// + /// The condition that must be fulfilled within the duration. + /// + public void AwaitCondition(Func conditionIsFulfilled, CancellationToken cancellationToken = default) + => TestKit.AwaitCondition(conditionIsFulfilled, cancellationToken); + + public Task AwaitConditionAsync(Func> conditionIsFulfilled, CancellationToken cancellationToken = default) + => TestKit.AwaitConditionAsync(conditionIsFulfilled, cancellationToken); + + /// + /// Await until the given condition evaluates to true or the timeout + /// expires, whichever comes first. + /// If no timeout is given, take it from the innermost enclosing `within` + /// block (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". + /// The value is dilated, i.e. scaled by the factor + /// specified in config value "akka.test.timefactor".. + /// A call to is done immediately, then the threads sleep + /// for about a tenth of the timeout value, before it checks the condition again. This is repeated until + /// timeout or the condition evaluates to true. To specify another interval, use the overload + /// + /// + /// + /// The condition that must be fulfilled within the duration. + /// The maximum duration. If undefined, uses the remaining time + /// (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". + /// The value is dilated, i.e. scaled by the factor + /// specified in config value "akka.test.timefactor". + /// + public void AwaitCondition(Func conditionIsFulfilled, TimeSpan? max, CancellationToken cancellationToken = default) + => TestKit.AwaitCondition(conditionIsFulfilled, max, cancellationToken); + + public Task AwaitConditionAsync(Func> conditionIsFulfilled, TimeSpan? max, CancellationToken cancellationToken = default) + => TestKit.AwaitConditionAsync(conditionIsFulfilled, max, cancellationToken); + + /// + /// Await until the given condition evaluates to true or the timeout + /// expires, whichever comes first. + /// If no timeout is given, take it from the innermost enclosing `within` + /// block (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". + /// The value is dilated, i.e. scaled by the factor + /// specified in config value "akka.test.timefactor".. + /// A call to is done immediately, then the threads sleep + /// for about a tenth of the timeout value, before it checks the condition again. This is repeated until + /// timeout or the condition evaluates to true. To specify another interval, use the overload + /// + /// + /// + /// The condition that must be fulfilled within the duration. + /// The maximum duration. If undefined, uses the remaining time + /// (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". + /// The value is dilated, i.e. scaled by the factor + /// specified in config value "akka.test.timefactor". + /// The message used if the timeout expires. + /// + public void AwaitCondition(Func conditionIsFulfilled, TimeSpan? max, string message, CancellationToken cancellationToken = default) + => TestKit.AwaitCondition(conditionIsFulfilled, max, message, cancellationToken); + + public Task AwaitConditionAsync(Func> conditionIsFulfilled, TimeSpan? max, string message, CancellationToken cancellationToken = default) + => TestKit.AwaitConditionAsync(conditionIsFulfilled, max, message, cancellationToken); + + /// + /// Await until the given condition evaluates to true or the timeout + /// expires, whichever comes first. + /// If no timeout is given, take it from the innermost enclosing `within` + /// block. + /// Note that the timeout is dilated, i.e. scaled by the factor + /// specified in config value "akka.test.timefactor". + /// The parameter specifies the time between calls to + /// Between calls the thread sleeps. If is undefined the thread only sleeps + /// one time, using the as duration, and then rechecks the condition and ultimately + /// succeeds or fails. + /// To make sure that tests run as fast as possible, make sure you do not leave this value as undefined, + /// instead set it to a relatively small value. + /// + /// The condition that must be fulfilled within the duration. + /// The maximum duration. If undefined, uses the remaining time + /// (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". + /// The value is dilated, i.e. scaled by the factor + /// specified in config value "akka.test.timefactor". + /// The time between calls to to check + /// if the condition is fulfilled. Between calls the thread sleeps. If undefined, negative or + /// the thread only sleeps one time, using the , + /// and then rechecks the condition and ultimately succeeds or fails. + /// To make sure that tests run as fast as possible, make sure you do not set this value as undefined, + /// instead set it to a relatively small value. + /// + /// The message used if the timeout expires. + /// + public void AwaitCondition(Func conditionIsFulfilled, TimeSpan? max, TimeSpan? interval, string message = null, CancellationToken cancellationToken = default) + => TestKit.AwaitCondition(conditionIsFulfilled, max, interval, message, cancellationToken); + + public Task AwaitConditionAsync(Func> conditionIsFulfilled, TimeSpan? max, TimeSpan? interval, string message = null, CancellationToken cancellationToken = default) + => TestKit.AwaitConditionAsync(conditionIsFulfilled, max, interval, message, cancellationToken); + + /// + /// Await until the given condition evaluates to true or the timeout + /// expires, whichever comes first. Returns true if the condition was fulfilled. + /// The parameter specifies the time between calls to + /// Between calls the thread sleeps. If is not specified or null 100 ms is used. + /// + /// The condition that must be fulfilled within the duration. + /// The maximum duration. + /// Optional. The time between calls to to check + /// if the condition is fulfilled. Between calls the thread sleeps. If undefined, 100 ms is used + /// + /// + /// TBD + public bool AwaitConditionNoThrow(Func conditionIsFulfilled, TimeSpan max, TimeSpan? interval = null, + CancellationToken cancellationToken = default) + => TestKit.AwaitConditionNoThrow(conditionIsFulfilled, max, interval, cancellationToken); + + public Task AwaitConditionNoThrowAsync(Func> conditionIsFulfilled, TimeSpan max, TimeSpan? interval = null, CancellationToken cancellationToken = default) + => TestKit.AwaitConditionNoThrowAsync(conditionIsFulfilled, max, interval, cancellationToken); + + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Expect.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Expect.cs new file mode 100644 index 00000000..6a71fcc8 --- /dev/null +++ b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Expect.cs @@ -0,0 +1,374 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.TestKit; +using Akka.TestKit.Internal; +using Akka.Util; + +namespace Akka.Hosting.TestKit +{ + public abstract partial class HostingSpec + { + /// + /// Receive one message from the test actor and assert that it is of the specified type. + /// Wait time is bounded by the given duration, if specified; otherwise + /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsg( + TimeSpan? duration = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsg(duration, hint, cancellationToken); + + /// + public ValueTask ExpectMsgAsync( + TimeSpan? duration = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAsync(duration, hint, cancellationToken); + + /// + /// Receive one message of the specified type from the test actor and assert that it + /// equals the . + /// Wait time is bounded by the given duration, if specified; otherwise + /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsg( + T message, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsg(message, timeout, hint, cancellationToken); + + /// + public ValueTask ExpectMsgAsync( + T message, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAsync(message, timeout, hint, cancellationToken); + + /// + /// Receive one message of the specified type from the test actor and assert that the given + /// predicate accepts it. + /// Use this variant to implement more complicated or conditional processing. + /// + /// Wait time is bounded by the given duration, if specified; otherwise + /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsg( + Predicate isMessage, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsg(isMessage, timeout, hint, cancellationToken); + + /// + public ValueTask ExpectMsgAsync( + Predicate isMessage, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAsync(isMessage, timeout, hint, cancellationToken); + + + /// + /// Receive one message of the specified type from the test actor and calls the + /// action that performs extra assertions. + /// Use this variant to implement more complicated or conditional processing. + /// + /// Wait time is bounded by the given duration, if specified; otherwise + /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsg( + Action assert, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsg(assert, timeout, hint, cancellationToken); + + /// + public ValueTask ExpectMsgAsync( + Action assert, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAsync(assert, timeout, hint, cancellationToken); + + /// + /// Receive one message of the specified type from the test actor and assert that the given + /// predicate accepts it. + /// Use this variant to implement more complicated or conditional processing. + /// + /// Wait time is bounded by the given duration, if specified; otherwise + /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsg( + Func isMessageAndSender, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsg(isMessageAndSender, timeout, hint, cancellationToken); + + /// + public ValueTask ExpectMsgAsync( + Func isMessageAndSender, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAsync(isMessageAndSender, timeout, hint, cancellationToken); + + /// + /// Receive one message of the specified type from the test actor calls the + /// action that performs extra assertions. + /// Use this variant to implement more complicated or conditional processing. + /// + /// Wait time is bounded by the given duration, if specified; otherwise + /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsg( + Action assertMessageAndSender, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsg(assertMessageAndSender, timeout, hint, cancellationToken); + + /// + public ValueTask ExpectMsgAsync( + Action assertMessageAndSender, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAsync(assertMessageAndSender, timeout, hint, cancellationToken); + + + /// + /// Receive one message from the test actor and assert that it is equal to the expected value, + /// according to the specified comparer function. + /// + /// Wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsg( + T expected, + Func comparer, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsg(expected, comparer, timeout, hint, cancellationToken); + + /// + public ValueTask ExpectMsgAsync( + T expected, + Func comparer, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAsync(expected, comparer, timeout, hint, cancellationToken); + + /// + /// Receive one message from the test actor and assert that it is the Terminated message of the given ActorRef. + /// + /// Wait time is bounded by the given duration, if specified; otherwise + /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + /// TBD + /// TBD + /// TBD + /// + /// TBD + public Terminated ExpectTerminated( + IActorRef target, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectTerminated(target, timeout, hint, cancellationToken); + + /// + public ValueTask ExpectTerminatedAsync( + IActorRef target, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectTerminatedAsync(target, timeout, hint, cancellationToken); + + /// + /// Assert that no message is received. + /// + /// Wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + public void ExpectNoMsg(CancellationToken cancellationToken = default) + => TestKit.ExpectNoMsg(cancellationToken); + + /// + public ValueTask ExpectNoMsgAsync(CancellationToken cancellationToken = default) + => TestKit.ExpectNoMsgAsync(cancellationToken); + + /// + /// Assert that no message is received for the specified time. + /// + /// TBD + /// + public void ExpectNoMsg(TimeSpan duration, CancellationToken cancellationToken = default) + => TestKit.ExpectNoMsg(duration, cancellationToken); + + /// + public ValueTask ExpectNoMsgAsync(TimeSpan duration, CancellationToken cancellationToken = default) + => TestKit.ExpectNoMsgAsync(duration, cancellationToken); + + /// + /// Assert that no message is received for the specified time in milliseconds. + /// + /// TBD + /// + public void ExpectNoMsg(int milliseconds, CancellationToken cancellationToken = default) + => TestKit.ExpectNoMsg(milliseconds, cancellationToken); + + /// + public ValueTask ExpectNoMsgAsync(int milliseconds, CancellationToken cancellationToken = default) + => TestKit.ExpectNoMsgAsync(milliseconds, cancellationToken); + + /// + /// Receive a message from the test actor and assert that it equals + /// one of the given . Wait time is bounded by + /// as duration, with an assertion exception being thrown in case of timeout. + /// + /// The type of the messages + /// The messages. + /// + /// The received messages in received order + public T ExpectMsgAnyOf(IEnumerable messages, CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAnyOf(messages, cancellationToken); + + public ValueTask ExpectMsgAnyOfAsync(IEnumerable messages, CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAnyOfAsync(messages, cancellationToken); + + /// + /// Receive a number of messages from the test actor matching the given + /// number of objects and assert that for each given object one is received + /// which equals it and vice versa. This construct is useful when the order in + /// which the objects are received is not fixed. Wait time is bounded by + /// as duration, with an assertion exception being thrown in case of timeout. + /// + /// + /// dispatcher.Tell(SomeWork1()) + /// dispatcher.Tell(SomeWork2()) + /// ExpectMsgAllOf(TimeSpan.FromSeconds(1), Result1(), Result2()) + /// + /// + /// The type of the messages + /// The messages. + /// + /// The received messages in received order + public IReadOnlyCollection ExpectMsgAllOf( + IReadOnlyCollection messages, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAllOf(messages, cancellationToken); + + public IAsyncEnumerable ExpectMsgAllOfAsync( + IReadOnlyCollection messages, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAllOfAsync(messages, cancellationToken); + + /// + /// Receive a number of messages from the test actor matching the given + /// number of objects and assert that for each given object one is received + /// which equals it and vice versa. This construct is useful when the order in + /// which the objects are received is not fixed. Wait time is bounded by the + /// given duration, with an assertion exception being thrown in case of timeout. + /// + /// + /// dispatcher.Tell(SomeWork1()) + /// dispatcher.Tell(SomeWork2()) + /// ExpectMsgAllOf(TimeSpan.FromSeconds(1), Result1(), Result2()) + /// + /// The deadline is scaled by "akka.test.timefactor" using . + /// + /// The type of the messages + /// The deadline. The deadline is scaled by "akka.test.timefactor" using . + /// The messages. + /// + /// The received messages in received order + public IReadOnlyCollection ExpectMsgAllOf( + TimeSpan max, + IReadOnlyCollection messages, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAllOf(max, messages, cancellationToken); + + public IAsyncEnumerable ExpectMsgAllOfAsync( + TimeSpan max, + IReadOnlyCollection messages, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgAllOfAsync(max, messages, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ExpectMsgFrom.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ExpectMsgFrom.cs new file mode 100644 index 00000000..4501c4b3 --- /dev/null +++ b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ExpectMsgFrom.cs @@ -0,0 +1,204 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.Threading; +using System.Threading.Tasks; +using Akka.Actor; + +namespace Akka.Hosting.TestKit +{ + public abstract partial class HostingSpec + { + /// + /// Receive one message from the test actor and assert that it is of the specified type + /// and was sent by the specified sender + /// Wait time is bounded by the given duration if specified. + /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsgFrom( + IActorRef sender, + TimeSpan? duration = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFrom(sender, duration, hint, cancellationToken); + + public ValueTask ExpectMsgFromAsync( + IActorRef sender, + TimeSpan? duration = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFromAsync(sender, duration, hint, cancellationToken); + + /// + /// Receive one message of the specified type from the test actor and assert that it + /// equals the and was sent by the specified sender + /// Wait time is bounded by the given duration if specified. + /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsgFrom( + IActorRef sender, + T message, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFrom(sender, message, timeout, hint, cancellationToken); + + public ValueTask ExpectMsgFromAsync( + IActorRef sender, + T message, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFromAsync(sender, message, timeout, hint, cancellationToken); + + /// + /// Receive one message of the specified type from the test actor and assert that the given + /// predicate accepts it and was sent by the specified sender + /// Wait time is bounded by the given duration if specified. + /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// Use this variant to implement more complicated or conditional processing. + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsgFrom( + IActorRef sender, + Predicate isMessage, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFrom(sender, isMessage, timeout, hint, cancellationToken); + + public ValueTask ExpectMsgFromAsync( + IActorRef sender, + Predicate isMessage, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFromAsync(sender, isMessage, timeout, hint, cancellationToken); + + /// + /// Receive one message of the specified type from the test actor and assert that the given + /// predicate accepts it and was sent by a sender that matches the predicate. + /// Wait time is bounded by the given duration if specified. + /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// Use this variant to implement more complicated or conditional processing. + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsgFrom( + Predicate isSender, + Predicate isMessage, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFrom(isSender, isMessage, timeout, hint, cancellationToken); + + public ValueTask ExpectMsgFromAsync( + Predicate isSender, + Predicate isMessage, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFromAsync(isSender, isMessage, timeout, hint, cancellationToken); + + /// + /// Receive one message of the specified type from the test actor, verifies that the sender is the specified + /// and calls the action that performs extra assertions. + /// Wait time is bounded by the given duration if specified. + /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// Use this variant to implement more complicated or conditional processing. + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsgFrom( + IActorRef sender, + Action assertMessage, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFrom(sender, assertMessage, timeout, hint, cancellationToken); + + public ValueTask ExpectMsgFromAsync( + IActorRef sender, + Action assertMessage, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFromAsync(sender, assertMessage, timeout, hint, cancellationToken); + + /// + /// Receive one message of the specified type from the test actor and calls the + /// action that performs extra assertions. + /// Wait time is bounded by the given duration if specified. + /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' + /// block, if inside a 'within' block; otherwise by the config value + /// "akka.test.single-expect-default". + /// Use this variant to implement more complicated or conditional processing. + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T ExpectMsgFrom( + Action assertSender, + Action assertMessage, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFrom(assertSender, assertMessage, timeout, hint, cancellationToken); + + public ValueTask ExpectMsgFromAsync( + Action assertSender, + Action assertMessage, + TimeSpan? timeout = null, + string hint = null, + CancellationToken cancellationToken = default) + => TestKit.ExpectMsgFromAsync(assertSender, assertMessage, timeout, hint, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Receive.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Receive.cs new file mode 100644 index 00000000..31648f59 --- /dev/null +++ b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Receive.cs @@ -0,0 +1,403 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Akka.TestKit; +using Akka.TestKit.Internal; + +namespace Akka.Hosting.TestKit +{ + public abstract partial class HostingSpec + { + /// + /// Receives messages until returns true. + /// Use it to ignore certain messages while waiting for a specific message. + /// + /// The is message. + /// The maximum. + /// The hint. + /// + /// Returns the message that matched + public object FishForMessage( + Predicate isMessage, + TimeSpan? max = null, + string hint = "", + CancellationToken cancellationToken = default) + => TestKit.FishForMessage(isMessage, max, hint, cancellationToken); + + /// + public ValueTask FishForMessageAsync( + Predicate isMessage, + TimeSpan? max = null, + string hint = "", + CancellationToken cancellationToken = default) + => TestKit.FishForMessageAsync(isMessage, max, hint, cancellationToken); + + /// + /// Receives messages until returns true. + /// Use it to ignore certain messages while waiting for a specific message. + /// + /// The type of the expected message. Messages of other types are ignored. + /// The is message. + /// The maximum. + /// The hint. + /// + /// Returns the message that matched + public T FishForMessage( + Predicate isMessage, + TimeSpan? max = null, + string hint = "", + CancellationToken cancellationToken = default) + => TestKit.FishForMessage(isMessage, max, hint, cancellationToken); + + /// + public ValueTask FishForMessageAsync( + Predicate isMessage, + TimeSpan? max = null, + string hint = "", + CancellationToken cancellationToken = default) + => TestKit.FishForMessageAsync(isMessage, max, hint, cancellationToken); + + /// + /// Receives messages until returns true. + /// Use it to ignore certain messages while waiting for a specific message. + /// + /// The type of the expected message. Messages of other types are ignored. + /// The is message. + /// The maximum. + /// The hint. + /// + /// If null then will be ignored. If not null then will be initially cleared, then filled with all the messages until returns true + /// Returns the message that matched + public T FishForMessage( + Predicate isMessage, + ArrayList allMessages, + TimeSpan? max = null, + string hint = "", + CancellationToken cancellationToken = default) + => TestKit.FishForMessage(isMessage, allMessages, max, hint, cancellationToken); + + /// + public ValueTask FishForMessageAsync( + Predicate isMessage, + ArrayList allMessages, + TimeSpan? max = null, + string hint = "", + CancellationToken cancellationToken = default) + => TestKit.FishForMessageAsync(isMessage, allMessages, max, hint, cancellationToken); + + /// + /// Receives messages until . + /// + /// Ignores all messages except for a message of type . + /// Asserts that all messages are not of the of type . + /// + /// The type that the message is not supposed to be. + /// Optional. The maximum wait duration. Defaults to when unset. + /// + public Task FishUntilMessageAsync(TimeSpan? max = null, CancellationToken cancellationToken = default) + => TestKit.FishUntilMessageAsync(max, cancellationToken); + + /// + /// Waits for a period of 'radio-silence' limited to a number of . + /// Note: 'radio-silence' definition: period when no messages arrive at. + /// + /// A temporary period of 'radio-silence'. + /// The method asserts that is never reached. + /// + /// If set to null then this method will loop for an infinite number of periods. + /// NOTE: If set to null and radio-silence is never reached then this method will never return. + /// Returns all the messages encountered before 'radio-silence' was reached. + public Task WaitForRadioSilenceAsync(TimeSpan? max = null, uint? maxMessages = null, + CancellationToken cancellationToken = default) + => TestKit.WaitForRadioSilenceAsync(max, maxMessages, cancellationToken); + + /// + /// Receive one message from the internal queue of the TestActor. + /// This method blocks the specified duration or until a message + /// is received. If no message was received, null is returned. + /// This method does NOT automatically scale its Duration parameter using ! + /// + /// The maximum duration to wait. + /// If null the config value "akka.test.single-expect-default" is used as timeout. + /// If set to a negative value or , blocks forever. + /// This method does NOT automatically scale its Duration parameter using ! + /// + /// The message if one was received; null otherwise + public object ReceiveOne(TimeSpan? max = null, CancellationToken cancellationToken = default) + => TestKit.ReceiveOne(max, cancellationToken); + + /// + public ValueTask ReceiveOneAsync(TimeSpan? max = null, CancellationToken cancellationToken = default) + => TestKit.ReceiveOneAsync(max, cancellationToken); + + /// + /// Receive one message from the internal queue of the TestActor within + /// the specified duration. The method blocks the specified duration. + /// Note! that the returned + /// is a containing the sender and the message. + /// This method does NOT automatically scale its Duration parameter using ! + /// + /// The received envelope. + /// Optional: The maximum duration to wait. + /// If null the config value "akka.test.single-expect-default" is used as timeout. + /// If set to a negative value or , blocks forever. + /// This method does NOT automatically scale its Duration parameter using ! + /// + /// True if a message was received within the specified duration; false otherwise. + public bool TryReceiveOne( + out MessageEnvelope envelope, + TimeSpan? max = null, + CancellationToken cancellationToken = default) + => TestKit.TryReceiveOne(out envelope, max, cancellationToken); + + /// + public ValueTask<(bool success, MessageEnvelope envelope)> TryReceiveOneAsync( + TimeSpan? max, + CancellationToken cancellationToken = default) + => TestKit.TryReceiveOneAsync(max, cancellationToken); + + #region Peek methods + + /// + /// Peek one message from the head of the internal queue of the TestActor. + /// This method blocks the specified duration or until a message + /// is received. If no message was received, null is returned. + /// This method does NOT automatically scale its Duration parameter using ! + /// + /// The maximum duration to wait. + /// If null the config value "akka.test.single-expect-default" is used as timeout. + /// If set to a negative value or , blocks forever. + /// This method does NOT automatically scale its Duration parameter using ! + /// + /// The message if one was received; null otherwise + public object PeekOne(TimeSpan? max = null, CancellationToken cancellationToken = default) + => TestKit.PeekOne(max, cancellationToken); + + /// + public ValueTask PeekOneAsync(TimeSpan? max = null, CancellationToken cancellationToken = default) + => TestKit.PeekOneAsync(max, cancellationToken); + + /// + /// Peek one message from the head of the internal queue of the TestActor. + /// This method blocks until cancelled. + /// + /// A token used to cancel the operation + /// The message if one was received; null otherwise + public object PeekOne(CancellationToken cancellationToken) + => TestKit.PeekOne(cancellationToken); + + /// + public ValueTask PeekOneAsync(CancellationToken cancellationToken) + => TestKit.PeekOneAsync(cancellationToken); + + /// + /// Peek one message from the head of the internal queue of the TestActor within + /// the specified duration. + /// True is returned if a message existed, and the message + /// is returned in . The method blocks the + /// specified duration, and can be cancelled using the + /// . + /// + /// This method does NOT automatically scale its duration parameter using ! + /// + /// The received envelope. + /// The maximum duration to wait. + /// If null the config value "akka.test.single-expect-default" is used as timeout. + /// If set to , blocks forever (or until cancelled). + /// This method does NOT automatically scale its Duration parameter using ! + /// + /// A token used to cancel the operation. + /// True if a message was received within the specified duration; false otherwise. + public bool TryPeekOne(out MessageEnvelope envelope, TimeSpan? max, CancellationToken cancellationToken) + => TestKit.TryPeekOne(out envelope, max, cancellationToken); + + /// + public ValueTask<(bool success, MessageEnvelope envelope)> TryPeekOneAsync(TimeSpan? max, CancellationToken cancellationToken) + => TestKit.TryPeekOneAsync(max, cancellationToken); + + #endregion + + /// + /// Receive a series of messages until the function returns null or the overall + /// maximum duration is elapsed or expected messages count is reached. + /// Returns the sequence of messages. + /// + /// Note that it is not an error to hit the `max` duration in this case. + /// The max duration is scaled by + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public IReadOnlyList ReceiveWhile( + TimeSpan? max, + Func filter, + int msgs = int.MaxValue, + CancellationToken cancellationToken = default) + => TestKit.ReceiveWhile(max, filter, msgs, cancellationToken); + + /// + public IAsyncEnumerable ReceiveWhileAsync( + TimeSpan? max, + Func filter, + int msgs = int.MaxValue, + CancellationToken cancellationToken = default) + => TestKit.ReceiveWhileAsync(max, filter, msgs, cancellationToken); + + /// + /// Receive a series of messages until the function returns null or the idle + /// timeout is met or the overall maximum duration is elapsed or + /// expected messages count is reached. + /// Returns the sequence of messages. + /// + /// Note that it is not an error to hit the `max` duration in this case. + /// The max duration is scaled by + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public IReadOnlyList ReceiveWhile( + TimeSpan? max, + TimeSpan? idle, + Func filter, + int msgs = int.MaxValue, + CancellationToken cancellationToken = default) + => TestKit.ReceiveWhile(max, idle, filter, msgs, cancellationToken); + + /// + public IAsyncEnumerable ReceiveWhileAsync( + TimeSpan? max, + TimeSpan? idle, + Func filter, + int msgs = int.MaxValue, + CancellationToken cancellationToken = default) + => TestKit.ReceiveWhileAsync(max, idle, filter, msgs, cancellationToken); + + /// + /// Receive a series of messages until the function returns null or the idle + /// timeout is met (disabled by default) or the overall + /// maximum duration is elapsed or expected messages count is reached. + /// Returns the sequence of messages. + /// + /// Note that it is not an error to hit the `max` duration in this case. + /// The max duration is scaled by + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public IReadOnlyList ReceiveWhile( + Func filter, + TimeSpan? max = null, + TimeSpan? idle = null, + int msgs = int.MaxValue, + CancellationToken cancellationToken = default) + => TestKit.ReceiveWhile(filter, max, idle, msgs, cancellationToken); + + /// + public IAsyncEnumerable ReceiveWhileAsync( + Func filter, + TimeSpan? max = null, + TimeSpan? idle = null, + int msgs = int.MaxValue, + CancellationToken cancellationToken = default) + => TestKit.ReceiveWhileAsync(filter, max, idle, msgs, cancellationToken); + + /// + /// Receive a series of messages. + /// It will continue to receive messages until the predicate returns false or the idle + /// timeout is met (disabled by default) or the overall + /// maximum duration is elapsed or expected messages count is reached. + /// If a message that isn't of type the parameter + /// declares if the message should be ignored or not. + /// Returns the sequence of messages. + /// + /// Note that it is not an error to hit the `max` duration in this case. + /// The max duration is scaled by + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public IReadOnlyList ReceiveWhile( + Predicate shouldContinue, + TimeSpan? max = null, + TimeSpan? idle = null, + int msgs = int.MaxValue, + bool shouldIgnoreOtherMessageTypes = true, + CancellationToken cancellationToken = default) + => TestKit.ReceiveWhile(shouldContinue, max, idle, msgs, shouldIgnoreOtherMessageTypes, cancellationToken); + + /// + public IAsyncEnumerable ReceiveWhileAsync( + Predicate shouldContinue, + TimeSpan? max = null, + TimeSpan? idle = null, + int msgs = int.MaxValue, + bool shouldIgnoreOtherMessageTypes = true, + CancellationToken cancellationToken = default) + => TestKit.ReceiveWhileAsync(shouldContinue, max, idle, msgs, shouldIgnoreOtherMessageTypes, cancellationToken); + + /// + /// Receive the specified number of messages using as timeout. + /// + /// The number of messages. + /// + /// The received messages + public IReadOnlyCollection ReceiveN( + int numberOfMessages, + CancellationToken cancellationToken = default) + => TestKit.ReceiveN(numberOfMessages, cancellationToken); + + /// + public IAsyncEnumerable ReceiveNAsync( + int numberOfMessages, + CancellationToken cancellationToken = default) + => TestKit.ReceiveNAsync(numberOfMessages, cancellationToken); + + /// + /// Receive the specified number of messages in a row before the given deadline. + /// The deadline is scaled by "akka.test.timefactor" using . + /// + /// The number of messages. + /// The timeout scaled by "akka.test.timefactor" using . + /// + /// The received messages + public IReadOnlyCollection ReceiveN( + int numberOfMessages, + TimeSpan max, + CancellationToken cancellationToken = default) + => TestKit.ReceiveN(numberOfMessages, max, cancellationToken); + + /// + public IAsyncEnumerable ReceiveNAsync( + int numberOfMessages, + TimeSpan max, + CancellationToken cancellationToken = default) + => TestKit.ReceiveNAsync(numberOfMessages, max, cancellationToken); + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_TestKit.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_TestKit.cs new file mode 100644 index 00000000..b54f1bde --- /dev/null +++ b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_TestKit.cs @@ -0,0 +1,479 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.Threading; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Actor.Setup; +using Akka.Configuration; +using Akka.Event; +using Akka.TestKit; +using Xunit.Abstractions; + +namespace Akka.Hosting.TestKit +{ + public abstract partial class HostingSpec + { + private ActorSystem _sys; + /// + /// The that is recreated and used for each test. + /// + protected ActorSystem Sys + { + get + { + AssertNotNull(_sys); + return _sys; + } + } + + /// + /// The settings for the testkit. + /// + public TestKitSettings TestKitSettings => TestKit.TestKitSettings; + + /// + /// The last to send a message to the . + /// + public IActorRef LastSender => TestKit.LastSender; + + /// + /// The default TestKit configuration. + /// + public static Config DefaultConfig => TestKitBase.DefaultConfig; + + /// + /// A full debugging configuration with all log settings enabled. + /// + public static Config FullDebugConfig => TestKitBase.FullDebugConfig; + + /// + /// The current time. + /// + public static TimeSpan Now => TimeSpan.FromTicks(DateTime.UtcNow.Ticks); + + private ILoggingAdapter _log; + + /// + /// The built-in used by . + /// + public ILoggingAdapter Log => _log ??= Event.Logging.GetLogger(Sys, GetType()); + + /// + /// The last message received by the . + /// + public object LastMessage => TestKit.LastMessage; + + /// + /// The default TestActor. The actor can be controlled by sending it + /// special control messages, see , + /// , . + /// You can also install an to drive the actor, see + /// . All other messages are forwarded to the queue + /// and can be retrieved with Receive and the ExpectMsg overloads. + /// + public IActorRef TestActor => TestKit.TestActor; + + /// + /// Filter sent to the system's . + /// In order to be able to filter the log the special logger + /// must be installed using the config + /// akka.loggers = ["Akka.TestKit.TestEventListener, Akka.TestKit"] + /// It is installed by default in testkit. + /// + public EventFilterFactory EventFilter => TestKit.EventFilter; + + /// + /// Creates a new event filter for the specified actor system. + /// + /// Actor system. + /// A new instance of . + public EventFilterFactory CreateEventFilter(ActorSystem system) + => new EventFilterFactory(TestKit, system); + + /// + /// Returns true if messages are available. + /// + /// + /// true if messages are available; otherwise, false. + /// + public bool HasMessages => TestKit.HasMessages; + + /// + /// Ignore all messages in the test actor for which the given function + /// returns true. + /// + /// Given a message, if the function returns + /// true the message will be ignored by . + public void IgnoreMessages(Func shouldIgnoreMessage) + => TestKit.IgnoreMessages(shouldIgnoreMessage); + + /// + /// Ignore all messages in the test actor of the given TMsg type for which the given function + /// returns true. + /// + /// The type of the message to ignore. + /// Given a message, if the function returns + /// true the message will be ignored by . + public void IgnoreMessages(Func shouldIgnoreMessage) + => TestKit.IgnoreMessages(shouldIgnoreMessage); + + /// + /// Ignore all messages in the test actor of the given TMsg type. + /// + /// The type of the message to ignore. + public void IgnoreMessages() => TestKit.IgnoreMessages(); + + /// Stop ignoring messages in the test actor. + public void IgnoreNoMessages() => TestKit.IgnoreNoMessages(); + + /// + /// Have the watch an actor and receive + /// messages when the actor terminates. + /// + /// The actor to watch. + /// The actor to watch, i.e. the parameter + public IActorRef Watch(IActorRef actorToWatch) => TestKit.Watch(actorToWatch); + + /// + /// Have the stop watching an actor. + /// + /// The actor to unwatch. + /// The actor to unwatch, i.e. the parameter + public IActorRef Unwatch(IActorRef actorToUnwatch) => TestKit.Unwatch(actorToUnwatch); + + /// + /// + /// Install an to drive the . + /// The will be run for each received message and can + /// be used to send or forward messages, etc. + /// + /// + /// Each invocation must return the AutoPilot for the next round. To reuse the + /// same return . + /// + /// + /// The pilot to install. + public void SetAutoPilot(AutoPilot pilot) => TestKit.SetAutoPilot(pilot); + + /// + /// + /// Retrieves the time remaining for execution of the innermost enclosing + /// Within block. + /// If missing that, then it returns the properly dilated default for this + /// case from settings (key: "akka.test.single-expect-default"). + /// + /// The returned value is always finite. + /// + public TimeSpan RemainingOrDefault => TestKit.RemainingOrDefault; + + /// + /// + /// Retrieves the time remaining for execution of the innermost enclosing + /// Within block. + /// + /// The returned value is always finite. + /// + /// + /// This exception is thrown when called from outside of `within`. + /// + public TimeSpan Remaining => TestKit.Remaining; + + /// + /// If inside a `within` block obtain time remaining for execution of the innermost enclosing `within` + /// block; otherwise returns the given duration. + /// + /// TBD + /// TBD + /// TBD + protected TimeSpan RemainingOr(TimeSpan duration) + { + AssertNotNull(_testKit); + return _testKit.RemainingOr(duration); + } + + /// + /// If is finite it is returned after it has been scaled using . + /// If is undefined, it returns the remaining time (if within a `within` block) or the properly dilated + /// default from settings (key "akka.test.single-expect-default"). + /// If is infinite, an is thrown. + /// The returned value is always finite. + /// + /// The maximum. + /// A finite properly dilated + /// Thrown if is infinite + public TimeSpan RemainingOrDilated(TimeSpan? duration) => TestKit.RemainingOrDilated(duration); + + + /// + /// Multiplies the duration with the , + /// i.e. the config value "akka.test.timefactor" + /// + /// TBD + /// TBD + public TimeSpan Dilated(TimeSpan duration) => TestKit.Dilated(duration); + + + /// + /// If is defined it is returned; otherwise + /// the config value "akka.test.single-expect-default" is returned. + /// + /// TBD + /// TBD + public TimeSpan GetTimeoutOrDefault(TimeSpan? timeout) => TestKit.GetTimeoutOrDefault(timeout); + + /// + /// Shuts down this system. + /// On failure debug output will be logged about the remaining actors in the system. + /// If verifySystemShutdown is true, then an exception will be thrown on failure. + /// + /// Optional. The duration to wait for shutdown. Default is 5 seconds multiplied with the config value "akka.test.timefactor". + /// if set to true an exception will be thrown on failure. + /// to cancel the operation + /// TBD + public virtual void Shutdown( + TimeSpan? duration = null, + bool verifySystemShutdown = false, + CancellationToken cancellationToken = default) + => TestKit.Shutdown(duration, verifySystemShutdown, cancellationToken); + + /// + /// Shuts down this system. + /// On failure debug output will be logged about the remaining actors in the system. + /// If verifySystemShutdown is true, then an exception will be thrown on failure. + /// + /// Optional. The duration to wait for shutdown. Default is 5 seconds multiplied with the config value "akka.test.timefactor". + /// if set to true an exception will be thrown on failure. + /// to cancel the operation + /// TBD + public virtual Task ShutdownAsync( + TimeSpan? duration = null, + bool verifySystemShutdown = false, + CancellationToken cancellationToken = default) + => TestKit.ShutdownAsync(duration, verifySystemShutdown, cancellationToken); + + /// + /// Shuts down the specified system. + /// On failure debug output will be logged about the remaining actors in the system. + /// If verifySystemShutdown is true, then an exception will be thrown on failure. + /// + /// The system to shutdown. + /// The duration to wait for shutdown. Default is 5 seconds multiplied with the config value "akka.test.timefactor" + /// if set to true an exception will be thrown on failure. + /// to cancel the operation + /// TBD + protected virtual void Shutdown( + ActorSystem system, + TimeSpan? duration = null, + bool verifySystemShutdown = false, + CancellationToken cancellationToken = default) + { + AssertNotNull(_testKit); + _testKit.Shutdown(system, duration, verifySystemShutdown, cancellationToken); + } + + /// + /// Shuts down the specified system. + /// On failure debug output will be logged about the remaining actors in the system. + /// If verifySystemShutdown is true, then an exception will be thrown on failure. + /// + /// The system to shutdown. + /// The duration to wait for shutdown. Default is 5 seconds multiplied with the config value "akka.test.timefactor" + /// if set to true an exception will be thrown on failure. + /// to cancel the operation + /// TBD + protected virtual Task ShutdownAsync( + ActorSystem system, + TimeSpan? duration = null, + bool verifySystemShutdown = false, + CancellationToken cancellationToken = default) + { + AssertNotNull(_testKit); + return _testKit.ShutdownAsync(system, duration, verifySystemShutdown, cancellationToken); + } + + /// + /// Spawns an actor as a child of this test actor, and returns the child's IActorRef + /// + /// Child actor props + /// Child actor name + /// Supervisor strategy for the child actor + /// to cancel the operation + /// + public IActorRef ChildActorOf( + Props props, + string name, + SupervisorStrategy supervisorStrategy, + CancellationToken cancellationToken = default) + => TestKit.ChildActorOf(props, name, supervisorStrategy, cancellationToken); + + /// + /// Spawns an actor as a child of this test actor, and returns the child's IActorRef + /// + /// Child actor props + /// Child actor name + /// Supervisor strategy for the child actor + /// to cancel the operation + /// + public Task ChildActorOfAsync( + Props props, + string name, + SupervisorStrategy supervisorStrategy, + CancellationToken cancellationToken = default) + => TestKit.ChildActorOfAsync(props, name, supervisorStrategy, cancellationToken); + + /// + /// Spawns an actor as a child of this test actor with an auto-generated name, and returns the child's ActorRef. + /// + /// Child actor props + /// Supervisor strategy for the child actor + /// to cancel the operation + /// + public IActorRef ChildActorOf( + Props props, SupervisorStrategy supervisorStrategy, CancellationToken cancellationToken = default) + => TestKit.ChildActorOf(props, supervisorStrategy, cancellationToken); + + /// + /// Spawns an actor as a child of this test actor with an auto-generated name, and returns the child's ActorRef. + /// + /// Child actor props + /// Supervisor strategy for the child actor + /// to cancel the operation + /// + public Task ChildActorOfAsync( + Props props, SupervisorStrategy supervisorStrategy, CancellationToken cancellationToken = default) + => TestKit.ChildActorOfAsync(props, supervisorStrategy, cancellationToken); + + /// + /// Spawns an actor as a child of this test actor with a stopping supervisor strategy, and returns the child's ActorRef. + /// + /// Child actor props + /// Child actor name + /// to cancel the operation + /// + public IActorRef ChildActorOf(Props props, string name, CancellationToken cancellationToken = default) + => TestKit.ChildActorOf(props, name, cancellationToken); + + /// + /// Spawns an actor as a child of this test actor with a stopping supervisor strategy, and returns the child's ActorRef. + /// + /// Child actor props + /// Child actor name + /// to cancel the operation + /// + public Task ChildActorOfAsync( + Props props, string name, CancellationToken cancellationToken = default) + => TestKit.ChildActorOfAsync(props, name, cancellationToken); + + /// + /// Spawns an actor as a child of this test actor with an auto-generated name and stopping supervisor strategy, returning the child's ActorRef. + /// + /// Child actor props + /// to cancel the operation + /// + public IActorRef ChildActorOf(Props props, CancellationToken cancellationToken = default) + => TestKit.ChildActorOf(props, cancellationToken); + + /// + /// Spawns an actor as a child of this test actor with an auto-generated name and stopping supervisor strategy, returning the child's ActorRef. + /// + /// Child actor props + /// to cancel the operation + /// + public Task ChildActorOfAsync(Props props, CancellationToken cancellationToken = default) + => TestKit.ChildActorOfAsync(props, cancellationToken); + + /// + /// Creates a test actor with the specified name. The actor can be controlled by sending it + /// special control messages, see , + /// , , + /// . All other messages are forwarded to the queue + /// and can be retrieved with Receive and the ExpectMsg overloads. + /// The default test actor can be retrieved from the property + /// + /// The name of the new actor. + /// TBD + public IActorRef CreateTestActor(string name) + => TestKit.CreateTestActor(name); + + /// + /// Creates a new . + /// + /// Optional: The name of the probe. + /// A new instance. + public virtual TestProbe CreateTestProbe(string name = null) + => TestKit.CreateTestProbe(name); + + /// + /// Creates a new . + /// + /// For multi-actor system tests, you can specify which system the node is for. + /// Optional: The name of the probe. + /// TBD + public virtual TestProbe CreateTestProbe(ActorSystem system, string name = null) + => TestKit.CreateTestProbe(system, name); + + /// + /// Creates a Countdown latch wrapper for use in testing. + /// + /// It uses a timeout when waiting and timeouts are specified as durations. + /// There's a default timeout of 5 seconds and the default count is 1. + /// Timeouts will always throw an exception. + /// + /// Optional. The count. Default: 1 + /// A new + public virtual TestLatch CreateTestLatch(int count = 1) + => TestKit.CreateTestLatch(count); + + /// + /// Wraps a for use in testing. + /// It always uses a timeout when waiting. + /// Timeouts will always throw an exception. The default timeout is 5 seconds. + /// + /// TBD + /// TBD + public TestBarrier CreateTestBarrier(int count) + => TestKit.CreateTestBarrier(count); + } + + internal class TestKitBaseUnWrapper : Akka.TestKit.Xunit2.TestKit + { + public TestKitBaseUnWrapper(ActorSystem system = null, ITestOutputHelper output = null) : base(system, output) + { + } + + public TestKitBaseUnWrapper(ActorSystemSetup config, string actorSystemName = null, ITestOutputHelper output = null) : base(config, actorSystemName, output) + { + } + + public TestKitBaseUnWrapper(Config config, string actorSystemName = null, ITestOutputHelper output = null) : base(config, actorSystemName, output) + { + } + + public TestKitBaseUnWrapper(string config, ITestOutputHelper output = null) : base(config, output) + { + } + + public new void Shutdown( + ActorSystem system, + TimeSpan? duration = null, + bool verifySystemShutdown = false, + CancellationToken cancellationToken = default) + => base.Shutdown(system, duration, verifySystemShutdown, cancellationToken); + + public new Task ShutdownAsync( + ActorSystem system, + TimeSpan? duration = null, + bool verifySystemShutdown = false, + CancellationToken cancellationToken = default) + => base.ShutdownAsync(system, duration, verifySystemShutdown, cancellationToken); + + public new TimeSpan RemainingOr(TimeSpan duration) + => base.RemainingOr(duration); + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Within.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Within.cs new file mode 100644 index 00000000..0599470f --- /dev/null +++ b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Within.cs @@ -0,0 +1,177 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Akka.Hosting.TestKit +{ + public abstract partial class HostingSpec + { + /// + /// Execute code block while bounding its execution time between 0 seconds and . + /// `within` blocks may be nested. All methods in this class which take maximum wait times + /// are available in a version which implicitly uses the remaining time governed by + /// the innermost enclosing `within` block. + /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor" + /// + /// TBD + /// TBD + /// TBD + /// + public void Within( + TimeSpan max, + Action action, + TimeSpan? epsilonValue = null, + CancellationToken cancellationToken = default) + => TestKit.Within(max, action, epsilonValue, cancellationToken); + + /// + /// Async version of + /// that takes a instead of an + /// + public Task WithinAsync( + TimeSpan max, + Func actionAsync, + TimeSpan? epsilonValue = null, + CancellationToken cancellationToken = default) + => TestKit.WithinAsync(max, actionAsync, epsilonValue, cancellationToken); + + /// + /// Execute code block while bounding its execution time between and . + /// `within` blocks may be nested. All methods in this class which take maximum wait times + /// are available in a version which implicitly uses the remaining time governed by + /// the innermost enclosing `within` block. + /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor" + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + public void Within( + TimeSpan min, + TimeSpan max, + Action action, + string hint = null, + TimeSpan? epsilonValue = null, + CancellationToken cancellationToken = default) + => TestKit.Within(min, max, action, hint, epsilonValue, cancellationToken); + + /// + /// Async version of + /// that takes a instead of an + /// + public Task WithinAsync( + TimeSpan min, + TimeSpan max, + Func actionAsync, + string hint = null, + TimeSpan? epsilonValue = null, + CancellationToken cancellationToken = default) + => TestKit.WithinAsync(min, max, actionAsync, hint, epsilonValue, cancellationToken); + + /// + /// Execute code block while bounding its execution time between 0 seconds and . + /// `within` blocks may be nested. All methods in this class which take maximum wait times + /// are available in a version which implicitly uses the remaining time governed by + /// the innermost enclosing `within` block. + /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor" + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T Within( + TimeSpan max, + Func function, + TimeSpan? epsilonValue = null, + CancellationToken cancellationToken = default) + => TestKit.Within(max, function, epsilonValue, cancellationToken); + + /// + /// Execute code block while bounding its execution time between 0 seconds and . + /// `within` blocks may be nested. All methods in this class which take maximum wait times + /// are available in a version which implicitly uses the remaining time governed by + /// the innermost enclosing `within` block. + /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor" + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public Task WithinAsync( + TimeSpan max, + Func> function, + TimeSpan? epsilonValue = null, + CancellationToken cancellationToken = default) + => TestKit.WithinAsync(max, function, epsilonValue, cancellationToken); + + /// + /// Execute code block while bounding its execution time between and . + /// `within` blocks may be nested. All methods in this class which take maximum wait times + /// are available in a version which implicitly uses the remaining time governed by + /// the innermost enclosing `within` block. + /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor" + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public T Within( + TimeSpan min, + TimeSpan max, + Func function, + string hint = null, + TimeSpan? epsilonValue = null, + CancellationToken cancellationToken = default) + => TestKit.Within(min, max, function, hint, epsilonValue, cancellationToken); + + /// + /// Execute code block while bounding its execution time between and . + /// `within` blocks may be nested. All methods in this class which take maximum wait times + /// are available in a version which implicitly uses the remaining time governed by + /// the innermost enclosing `within` block. + /// + /// + /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor". + /// + /// + /// Note that due to how asynchronous Task is executed in managed code, there is no way to stop a running Task. + /// If this assertion fails in any way, the Task might still be running in the + /// background and might not be stopped/disposed until the unit test is over. + /// + /// + /// + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// TBD + /// + /// TBD + public Task WithinAsync( + TimeSpan min, + TimeSpan max, + Func> function, + string hint = null, + TimeSpan? epsilonValue = null, + CancellationToken cancellationToken = default) + => TestKit.WithinAsync(min, max, function, hint, epsilonValue, cancellationToken); + } +} \ No newline at end of file From 4f8786e8d562b8cac237edaa5954b2efccfc87d7 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Wed, 7 Sep 2022 02:51:01 +0700 Subject: [PATCH 2/9] Add xunit.runner.json file --- .../Akka.Hosting.TestKit.Tests.csproj | 6 ++++++ src/Akka.Hosting.TestKit.Tests/xunit.runner.json | 6 ++++++ 2 files changed, 12 insertions(+) create mode 100644 src/Akka.Hosting.TestKit.Tests/xunit.runner.json diff --git a/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj b/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj index 38dde52f..5683f19c 100644 --- a/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj +++ b/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj @@ -14,5 +14,11 @@ + + + + Always + + diff --git a/src/Akka.Hosting.TestKit.Tests/xunit.runner.json b/src/Akka.Hosting.TestKit.Tests/xunit.runner.json new file mode 100644 index 00000000..4a73b1e5 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/xunit.runner.json @@ -0,0 +1,6 @@ +{ + "$schema": "https://xunit.github.io/schema/current/xunit.runner.schema.json", + "longRunningTestSeconds": 60, + "parallelizeAssembly": false, + "parallelizeTestCollections": false +} \ No newline at end of file From 1d9c74604c122edeee05d490254e74b81e6b8f30 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Thu, 8 Sep 2022 00:02:56 +0700 Subject: [PATCH 3/9] Fix ImplicitSender --- .../NoImplicitSenderSpec.cs | 41 ++++++++- .../ActorCellKeepingSynchronizationContext.cs | 83 +++++++++++++++++++ src/Akka.Hosting.TestKit/HostingSpec.cs | 25 +++--- 3 files changed, 133 insertions(+), 16 deletions(-) create mode 100644 src/Akka.Hosting.TestKit/ActorCellKeepingSynchronizationContext.cs diff --git a/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs b/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs index e46a26d6..38128bc9 100644 --- a/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs @@ -11,6 +11,7 @@ using Akka.TestKit; using Akka.Actor.Dsl; using FluentAssertions; +using Microsoft.Extensions.Logging; using Xunit; using Xunit.Abstractions; @@ -18,12 +19,13 @@ namespace Akka.Hosting.TestKit.Tests { public class NoImplicitSenderSpec : HostingSpec, INoImplicitSender { - [Fact(Skip = "Type assertion on null message causes NullReferenceException")] - public async Task When_Not_ImplicitSender_then_testActor_is_not_sender() + [Fact] + public async Task When_Not_ImplicitSender_then_testActor_is_DeadLetters() { var echoActor = Sys.ActorOf(c => c.ReceiveAny((m, ctx) => TestActor.Tell(ctx.Sender))); echoActor.Tell("message"); - await ExpectMsgAsync(actorRef => Equals(actorRef, ActorRefs.NoSender)); + var actorRef = await ExpectMsgAsync(); + actorRef.Should().Be(Sys.DeadLetters); } public NoImplicitSenderSpec(ITestOutputHelper output) : base(nameof(NoImplicitSenderSpec), output) @@ -35,11 +37,31 @@ protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IService public class ImplicitSenderSpec : HostingSpec { + private enum TestActorKey + { } + public ImplicitSenderSpec(ITestOutputHelper output) : base(nameof(ImplicitSenderSpec), output) { } protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { } + { + builder.WithActors((system, registry) => + { + var echoActor = system.ActorOf(Props.Create(() => new ImplicitActor(this))); + registry.Register(echoActor); + }); + } + + private class ImplicitActor: ReceiveActor + { + public ImplicitActor(HostingSpec spec) + { + ReceiveAny(m => + { + spec.TestActor.Tell(Context.Sender); + }); + } + } [Fact] public async Task ImplicitSender_should_have_testActor_as_sender() @@ -53,6 +75,17 @@ public async Task ImplicitSender_should_have_testActor_as_sender() await ExpectMsgAsync(actorRef => Equals(actorRef, TestActor)); } + [Fact] + public async Task ImplicitSender_should_have_testActor_as_sender2() + { + var echoActor = ActorRegistry.Get(); + echoActor.Tell("message"); + await ExpectMsgAsync(actorRef => Equals(actorRef, TestActor)); + + //Test that it works after we know that context has been changed + echoActor.Tell("message"); + await ExpectMsgAsync(actorRef => Equals(actorRef, TestActor)); + } [Fact] public async Task ImplicitSender_should_not_change_when_creating_Testprobes() diff --git a/src/Akka.Hosting.TestKit/ActorCellKeepingSynchronizationContext.cs b/src/Akka.Hosting.TestKit/ActorCellKeepingSynchronizationContext.cs new file mode 100644 index 00000000..3af04257 --- /dev/null +++ b/src/Akka.Hosting.TestKit/ActorCellKeepingSynchronizationContext.cs @@ -0,0 +1,83 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Actor.Internal; + +namespace Akka.Hosting.TestKit +{ + /// + /// TBD + /// + class ActorCellKeepingSynchronizationContext : SynchronizationContext + { + private readonly ActorCell _cell; + + internal static ActorCell AsyncCache { get; set; } + + /// + /// TBD + /// + /// TBD + public ActorCellKeepingSynchronizationContext(ActorCell cell) + { + _cell = cell; + } + + /// + /// TBD + /// + /// TBD + /// TBD + public override void Post(SendOrPostCallback d, object state) + { + ThreadPool.QueueUserWorkItem(_ => + { + var oldCell = InternalCurrentActorCellKeeper.Current; + var oldContext = Current; + SetSynchronizationContext(this); + InternalCurrentActorCellKeeper.Current = AsyncCache ?? _cell; + + try + { + d(state); + } + finally + { + InternalCurrentActorCellKeeper.Current = oldCell; + SetSynchronizationContext(oldContext); + } + }, state); + } + + /// + /// TBD + /// + /// TBD + /// TBD + public override void Send(SendOrPostCallback d, object state) + { + var tcs = new TaskCompletionSource(); + Post(_ => + { + try + { + d(state); + tcs.SetResult(0); + } + catch (Exception e) + { + tcs.TrySetException(e); + } + }, state); + tcs.Task.Wait(); + } + } +} diff --git a/src/Akka.Hosting.TestKit/HostingSpec.cs b/src/Akka.Hosting.TestKit/HostingSpec.cs index c73c1d4a..0f95c650 100644 --- a/src/Akka.Hosting.TestKit/HostingSpec.cs +++ b/src/Akka.Hosting.TestKit/HostingSpec.cs @@ -8,6 +8,7 @@ using System.Threading; using System.Threading.Tasks; using Akka.Actor; +using Akka.Actor.Internal; using Akka.Annotations; using Akka.Configuration; using Akka.Hosting.TestKit.Internals; @@ -78,17 +79,6 @@ private void InternalConfigureServices(HostBuilderContext context, IServiceColle Config != null ? Config.WithFallback(TestKitBase.DefaultConfig) : TestKitBase.DefaultConfig, HoconAddMode.Prepend); - /* - builder.ConfigureLoggers(logger => - { - logger.LogLevel = ToAkkaLogLevel(LogLevel); - if (Output != null) - { - logger.AddLoggerFactory(); - } - }); - */ - ConfigureAkka(builder, provider); }); } @@ -133,7 +123,18 @@ public async Task InitializeAsync() } _sys = _host.Services.GetRequiredService(); - _testKit = new TestKitBaseUnWrapper(_sys); + _testKit = new TestKitBaseUnWrapper(_sys, Output); + + if (this is INoImplicitSender) + { + InternalCurrentActorCellKeeper.Current = null; + } + else + { + InternalCurrentActorCellKeeper.Current = (ActorCell)((ActorRefWithCell)_testKit.TestActor).Underlying; + } + SynchronizationContext.SetSynchronizationContext( + new ActorCellKeepingSynchronizationContext(InternalCurrentActorCellKeeper.Current)); await BeforeTestStart(); } From 9d3fab66ad5bd10524a63e2afc9c57115ed89e9b Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Thu, 8 Sep 2022 00:28:54 +0700 Subject: [PATCH 4/9] Fix spec .csproj file --- .../Akka.Hosting.TestKit.Tests.csproj | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj b/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj index 5683f19c..723c21ae 100644 --- a/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj +++ b/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj @@ -1,7 +1,7 @@ - netcoreapp3.1 + $(TestsNetCoreFramework) @@ -11,8 +11,11 @@ - + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + From 6259f0a9d77210e67036a5d317ad7081b99a9955 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Fri, 9 Sep 2022 22:53:57 +0700 Subject: [PATCH 5/9] Base the TestKit off TestKitBase instead --- .../Akka.Hosting.TestKit.Tests.csproj | 1 + .../HostingSpecSpec.cs | 10 +- .../NoImplicitSenderSpec.cs | 131 ++--- .../Properties/AssemblyInfo.cs | 35 ++ .../TestActorRefTests/BossActor.cs | 74 ++- .../TestActorRefTests/FsmActor.cs | 81 ++- .../TestActorRefTests/Logger.cs | 31 +- .../TestActorRefTests/NestingActor.cs | 38 +- .../TestActorRefTests/ReplyActor.cs | 61 +-- .../TestActorRefTests/SenderActor.cs | 69 ++- .../TestActorRefTests/TActorBase.cs | 46 +- .../TestActorRefTests/TestActorRefSpec.cs | 80 ++- .../TestActorRefTests/TestProbeSpec.cs | 20 +- .../TestActorRefTests/WatchAndForwardActor.cs | 42 +- .../TestActorRefTests/WorkerActor.cs | 42 +- .../TestActorRefTests/WrappedTerminated.cs | 24 +- .../AllTestForEventFilterBase.cs | 140 ++--- .../AllTestForEventFilterBase_Instances.cs | 181 ++++--- .../TestEventListenerTests/ConfigTests.cs | 16 +- .../CustomEventFilterTests.cs | 91 ++-- .../DeadLettersEventFilterTests.cs | 109 ++-- .../EventFilterTestBase.cs | 30 +- .../ExceptionEventFilterTests.cs | 297 +++++------ .../ForwardAllEventsTestEventListener.cs | 59 ++- .../TestFSMRefTests/TestFSMRefSpec.cs | 137 +++-- .../TestKitBaseTests/AwaitAssertTests.cs | 78 +-- .../TestKitBaseTests/DilatedTests.cs | 130 +++-- .../TestKitBaseTests/ExpectTests.cs | 98 ++-- .../TestKitBaseTests/IgnoreMessagesTests.cs | 96 ++-- .../TestKitBaseTests/ReceiveTests.cs | 443 ++++++++-------- .../TestKitBaseTests/RemainingTests.cs | 20 +- .../TestKitBaseTests/WithinTests.cs | 73 +-- .../TestKit_Config_Tests.cs | 47 +- .../TestSchedulerTests.cs | 311 ++++++------ .../Akka.Hosting.TestKit.csproj | 2 +- .../{HostingSpec.cs => TestKit.cs} | 59 ++- .../Wrapper/HostingSpec_ActorOf.cs | 313 ------------ .../Wrapper/HostingSpec_AwaitAssert.cs | 58 --- .../Wrapper/HostingSpec_AwaitCondition.cs | 142 ------ .../Wrapper/HostingSpec_Expect.cs | 374 -------------- .../Wrapper/HostingSpec_ExpectMsgFrom.cs | 204 -------- .../Wrapper/HostingSpec_Receive.cs | 403 --------------- .../Wrapper/HostingSpec_TestKit.cs | 479 ------------------ .../Wrapper/HostingSpec_Within.cs | 177 ------- 44 files changed, 1429 insertions(+), 3923 deletions(-) create mode 100644 src/Akka.Hosting.TestKit.Tests/Properties/AssemblyInfo.cs rename src/Akka.Hosting.TestKit/{HostingSpec.cs => TestKit.cs} (80%) delete mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ActorOf.cs delete mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitAssert.cs delete mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitCondition.cs delete mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Expect.cs delete mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ExpectMsgFrom.cs delete mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Receive.cs delete mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_TestKit.cs delete mode 100644 src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Within.cs diff --git a/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj b/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj index 723c21ae..920e8b5e 100644 --- a/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj +++ b/src/Akka.Hosting.TestKit.Tests/Akka.Hosting.TestKit.Tests.csproj @@ -9,6 +9,7 @@ + diff --git a/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs b/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs index cd37c35e..50d00107 100644 --- a/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs @@ -14,7 +14,7 @@ namespace Akka.Hosting.TestKit.Tests { - public class HostingSpecSpec: HostingSpec + public class HostingSpecSpec: TestKit { private enum Echo { } @@ -43,5 +43,13 @@ public void ActorTest() var msg = probe.ExpectMsg("TestMessage"); Log.Info(msg); } + + private class SimpleEchoActor : ReceiveActor + { + public SimpleEchoActor() + { + ReceiveAny(Sender.Tell); + } + } } } \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs b/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs index 38128bc9..7f94995a 100644 --- a/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs @@ -1,110 +1,63 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- -using System; -using System.Threading.Tasks; using Akka.Actor; -using Akka.TestKit; using Akka.Actor.Dsl; +using Akka.TestKit; using FluentAssertions; -using Microsoft.Extensions.Logging; using Xunit; -using Xunit.Abstractions; -namespace Akka.Hosting.TestKit.Tests +namespace Akka.Hosting.TestKit.Tests; + +public class NoImplicitSenderSpec : TestKit, INoImplicitSender { - public class NoImplicitSenderSpec : HostingSpec, INoImplicitSender + [Fact] + public void When_Not_ImplicitSender_then_testActor_is_not_sender() { - [Fact] - public async Task When_Not_ImplicitSender_then_testActor_is_DeadLetters() - { - var echoActor = Sys.ActorOf(c => c.ReceiveAny((m, ctx) => TestActor.Tell(ctx.Sender))); - echoActor.Tell("message"); - var actorRef = await ExpectMsgAsync(); - actorRef.Should().Be(Sys.DeadLetters); - } - - public NoImplicitSenderSpec(ITestOutputHelper output) : base(nameof(NoImplicitSenderSpec), output) - { } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { } + var echoActor = Sys.ActorOf(c => c.ReceiveAny((m, ctx) => TestActor.Tell(ctx.Sender))); + echoActor.Tell("message"); + var actorRef = ExpectMsg(); + actorRef.Should().Be(Sys.DeadLetters); } - public class ImplicitSenderSpec : HostingSpec - { - private enum TestActorKey - { } - - public ImplicitSenderSpec(ITestOutputHelper output) : base(nameof(ImplicitSenderSpec), output) - { } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - builder.WithActors((system, registry) => - { - var echoActor = system.ActorOf(Props.Create(() => new ImplicitActor(this))); - registry.Register(echoActor); - }); - } - - private class ImplicitActor: ReceiveActor - { - public ImplicitActor(HostingSpec spec) - { - ReceiveAny(m => - { - spec.TestActor.Tell(Context.Sender); - }); - } - } - - [Fact] - public async Task ImplicitSender_should_have_testActor_as_sender() - { - var echoActor = Sys.ActorOf(c => c.ReceiveAny((m, ctx) => TestActor.Tell(ctx.Sender))); - echoActor.Tell("message"); - await ExpectMsgAsync(actorRef => Equals(actorRef, TestActor)); +} - //Test that it works after we know that context has been changed - echoActor.Tell("message"); - await ExpectMsgAsync(actorRef => Equals(actorRef, TestActor)); - } +public class ImplicitSenderSpec : TestKit +{ + [Fact] + public void ImplicitSender_should_have_testActor_as_sender() + { + var echoActor = Sys.ActorOf(c => c.ReceiveAny((m, ctx) => TestActor.Tell(ctx.Sender))); + echoActor.Tell("message"); + ExpectMsg(actorRef => Equals(actorRef, TestActor)); - [Fact] - public async Task ImplicitSender_should_have_testActor_as_sender2() - { - var echoActor = ActorRegistry.Get(); - echoActor.Tell("message"); - await ExpectMsgAsync(actorRef => Equals(actorRef, TestActor)); + //Test that it works after we know that context has been changed + echoActor.Tell("message"); + ExpectMsg(actorRef => Equals(actorRef, TestActor)); - //Test that it works after we know that context has been changed - echoActor.Tell("message"); - await ExpectMsgAsync(actorRef => Equals(actorRef, TestActor)); - } + } - [Fact] - public async Task ImplicitSender_should_not_change_when_creating_Testprobes() - { - //Verifies that bug #459 has been fixed - var testProbe = CreateTestProbe(); - TestActor.Tell("message"); - await ReceiveOneAsync(); - LastSender.Should().Be(TestActor); - } - [Fact] - public async Task ImplicitSender_should_not_change_when_creating_TestActors() - { - var testActor2 = CreateTestActor("test2"); - TestActor.Tell("message"); - await ReceiveOneAsync(); - LastSender.Should().Be(TestActor); - } + [Fact] + public void ImplicitSender_should_not_change_when_creating_Testprobes() + { + //Verifies that bug #459 has been fixed + var testProbe = CreateTestProbe(); + TestActor.Tell("message"); + ReceiveOne(); + LastSender.Should().Be(TestActor); } -} + [Fact] + public void ImplicitSender_should_not_change_when_creating_TestActors() + { + var testActor2 = CreateTestActor("test2"); + TestActor.Tell("message"); + ReceiveOne(); + LastSender.Should().Be(TestActor); + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/Properties/AssemblyInfo.cs b/src/Akka.Hosting.TestKit.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..e2336be8 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation +// +//----------------------------------------------------------------------- + +using System.Reflection; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +using Xunit; + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("b21496c0-a536-4953-9253-d2d0d526e42d")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] + +[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)] diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/BossActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/BossActor.cs index 70d0fa47..5c5e9f1d 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/BossActor.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/BossActor.cs @@ -1,67 +1,55 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; -using System.Threading; using Akka.Actor; using Akka.TestKit; -using Akka.Util; -using Akka.Util.Internal; -namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests; + +public class BossActor : TActorBase { - public class BossActor : TActorBase + private TestActorRef _child; + + public BossActor() + { + _child = new TestActorRef(Context.System, Props.Create(), Self, "child"); + } + + protected override SupervisorStrategy SupervisorStrategy() { - private readonly TestActorRef _child; + return new OneForOneStrategy(maxNrOfRetries: 5, withinTimeRange: TimeSpan.FromSeconds(1), localOnlyDecider: ex => ex is ActorKilledException ? Directive.Restart : Directive.Escalate); + } - public BossActor(AtomicCounter counter, Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) + protected override bool ReceiveMessage(object message) + { + if(message is string && ((string)message) == "sendKill") { - _child = new TestActorRef(Context.System, Props.Create(() => new InternalActor(counter, parentThread, otherThread)), Self, "child"); + _child.Tell(Kill.Instance); + return true; } + return false; + } - protected override SupervisorStrategy SupervisorStrategy() + private class InternalActor : TActorBase + { + protected override void PreRestart(Exception reason, object message) { - return new OneForOneStrategy(maxNrOfRetries: 5, withinTimeRange: TimeSpan.FromSeconds(1), localOnlyDecider: ex => ex is ActorKilledException ? Directive.Restart : Directive.Escalate); + TestActorRefSpec.Counter--; } - protected override bool ReceiveMessage(object message) + protected override void PostRestart(Exception reason) { - if(message is string && ((string)message) == "sendKill") - { - _child.Tell(Kill.Instance); - return true; - } - return false; + TestActorRefSpec.Counter--; } - private class InternalActor : TActorBase + protected override bool ReceiveMessage(object message) { - private readonly AtomicCounter _counter; - - public InternalActor(AtomicCounter counter, Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) - { - _counter = counter; - } - - protected override void PreRestart(Exception reason, object message) - { - _counter.Decrement(); - } - - protected override void PostRestart(Exception reason) - { - _counter.Decrement(); - } - - protected override bool ReceiveMessage(object message) - { - return true; - } + return true; } } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/FsmActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/FsmActor.cs index 5ddc74ec..297f82ec 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/FsmActor.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/FsmActor.cs @@ -1,57 +1,56 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using Akka.Actor; -namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests; + +public enum TestFsmState { - public enum TestFsmState - { - First, - Last - } + First, + Last +} - public class FsmActor : FSM +public class FsmActor : FSM +{ + private readonly IActorRef _replyActor; + + public FsmActor(IActorRef replyActor) { - private readonly IActorRef _replyActor; + _replyActor = replyActor; - public FsmActor(IActorRef replyActor) + When(TestFsmState.First, e => { - _replyActor = replyActor; + if (e.FsmEvent.Equals("check")) + { + _replyActor.Tell("first"); + } + else if (e.FsmEvent.Equals("next")) + { + return GoTo(TestFsmState.Last); + } + + return Stay(); + }); - When(TestFsmState.First, e => + When(TestFsmState.Last, e => + { + if (e.FsmEvent.Equals("check")) { - if (e.FsmEvent.Equals("check")) - { - _replyActor.Tell("first"); - } - else if (e.FsmEvent.Equals("next")) - { - return GoTo(TestFsmState.Last); - } - - return Stay(); - }); - - When(TestFsmState.Last, e => + _replyActor.Tell("last"); + } + else if (e.FsmEvent.Equals("next")) { - if (e.FsmEvent.Equals("check")) - { - _replyActor.Tell("last"); - } - else if (e.FsmEvent.Equals("next")) - { - return GoTo(TestFsmState.First); - } - - return Stay(); - }); - - StartWith(TestFsmState.First, "foo"); - } + return GoTo(TestFsmState.First); + } + + return Stay(); + }); + + StartWith(TestFsmState.First, "foo"); } -} +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/Logger.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/Logger.cs index 2211cb4b..33eb78e2 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/Logger.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/Logger.cs @@ -1,29 +1,28 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using Akka.Actor; using Akka.Event; -namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests; + +public class Logger : ActorBase { - public class Logger : ActorBase + private int _count; + private string _msg; + protected override bool Receive(object message) { - private int _count; - private string _msg; - protected override bool Receive(object message) + var warning = message as Warning; + if(warning != null && warning.Message is string) { - if(message is Warning { Message: string _ } warning) - { - _count++; - _msg = (string)warning.Message; - return true; - } - return false; + _count++; + _msg = (string)warning.Message; + return true; } + return false; } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/NestingActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/NestingActor.cs index 31a256f4..64558707 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/NestingActor.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/NestingActor.cs @@ -1,37 +1,35 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using Akka.Actor; using Akka.TestKit; -namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests; + +public class NestingActor : ActorBase { - public class NestingActor : ActorBase + private readonly IActorRef _nested; + + public NestingActor(bool createTestActorRef) { - private readonly IActorRef _nested; + _nested = createTestActorRef ? Context.System.ActorOf() : new TestActorRef(Context.System, Props.Create(), null, null); + } - public NestingActor(bool createTestActorRef) - { - _nested = createTestActorRef ? Context.System.ActorOf() : new TestActorRef(Context.System, Props.Create(), null, null); - } + protected override bool Receive(object message) + { + Sender.Tell(_nested, Self); + return true; + } + private class NestedActor : ActorBase + { protected override bool Receive(object message) { - Sender.Tell(_nested, Self); return true; } - - private class NestedActor : ActorBase - { - protected override bool Receive(object message) - { - return true; - } - } } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/ReplyActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/ReplyActor.cs index 97961775..e2c111f9 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/ReplyActor.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/ReplyActor.cs @@ -1,47 +1,40 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- -using System.Threading; using Akka.Actor; using Akka.TestKit; -using Akka.Util; -namespace Akka.Hosting.TestKit.Tests.TestActorRefTests -{ - public class ReplyActor : TActorBase - { - private IActorRef _replyTo; +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests; - protected override bool ReceiveMessage(object message) - { - switch((string)message) - { - case "complexRequest": - _replyTo = Sender; - var worker = new TestActorRef(System, Props.Create(() => new WorkerActor(ParentThread, OtherThread))); - worker.Tell("work"); - return true; - case "complexRequest2": - var worker2 = new TestActorRef(System, Props.Create(() => new WorkerActor(ParentThread, OtherThread))); - worker2.Tell(Sender, Self); - return true; - case "workDone": - _replyTo.Tell("complexReply", Self); - return true; - case "simpleRequest": - Sender.Tell("simpleReply", Self); - return true; - } - return false; - } +public class ReplyActor : TActorBase +{ + private IActorRef _replyTo; - public ReplyActor(Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) + protected override bool ReceiveMessage(object message) + { + var strMessage = message as string; + switch(strMessage) { + case "complexRequest": + _replyTo = Sender; + var worker = new TestActorRef(System, Props.Create()); + worker.Tell("work"); + return true; + case "complexRequest2": + var worker2 = new TestActorRef(System, Props.Create()); + worker2.Tell(Sender, Self); + return true; + case "workDone": + _replyTo.Tell("complexReply", Self); + return true; + case "simpleRequest": + Sender.Tell("simpleReply", Self); + return true; } + return false; } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/SenderActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/SenderActor.cs index 77528486..420ec2d0 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/SenderActor.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/SenderActor.cs @@ -1,51 +1,44 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- -using System.Threading; using Akka.Actor; -using Akka.Util; -using Akka.Util.Internal; -namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests; + +public class SenderActor : TActorBase { - public class SenderActor : TActorBase - { - private readonly AtomicCounter _counter; - private readonly IActorRef _replyActor; + private readonly IActorRef _replyActor; - public SenderActor(IActorRef replyActor, AtomicCounter counter, Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) - { - _replyActor = replyActor; - _counter = counter; - } + public SenderActor(IActorRef replyActor) + { + _replyActor = replyActor; + } - protected override bool ReceiveMessage(object message) + protected override bool ReceiveMessage(object message) + { + var strMessage = message as string; + switch(strMessage) { - var strMessage = message as string; - switch(strMessage) - { - case "complex": - _replyActor.Tell("complexRequest", Self); - return true; - case "complex2": - _replyActor.Tell("complexRequest2", Self); - return true; - case "simple": - _replyActor.Tell("simpleRequest", Self); - return true; - case "complexReply": - _counter.Decrement(); - return true; - case "simpleReply": - _counter.Decrement(); - return true; - } - return false; + case "complex": + _replyActor.Tell("complexRequest", Self); + return true; + case "complex2": + _replyActor.Tell("complexRequest2", Self); + return true; + case "simple": + _replyActor.Tell("simpleRequest", Self); + return true; + case "complexReply": + TestActorRefSpec.Counter--; + return true; + case "simpleReply": + TestActorRefSpec.Counter--; + return true; } + return false; } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TActorBase.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TActorBase.cs index 1e6c958b..4bf03ac4 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TActorBase.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TActorBase.cs @@ -1,42 +1,30 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System.Threading; using Akka.Actor; -using Akka.Util; -namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests; + +// ReSharper disable once InconsistentNaming +public abstract class TActorBase : ActorBase { - // ReSharper disable once InconsistentNaming - public abstract class TActorBase : ActorBase + protected sealed override bool Receive(object message) { - protected readonly Thread ParentThread; - protected readonly AtomicReference OtherThread; - - protected TActorBase(Thread parentThread, AtomicReference otherThread) - { - ParentThread = parentThread; - OtherThread = otherThread; - } - - protected sealed override bool Receive(object message) - { - var currentThread = Thread.CurrentThread; - if (currentThread != ParentThread) - OtherThread.GetAndSet(currentThread); - return ReceiveMessage(message); - } + var currentThread = Thread.CurrentThread; + if(currentThread != TestActorRefSpec.Thread) + TestActorRefSpec.OtherThread = currentThread; + return ReceiveMessage(message); + } - protected abstract bool ReceiveMessage(object message); + protected abstract bool ReceiveMessage(object message); - protected ActorSystem System - { - get { return ((LocalActorRef)Self).Cell.System; } - } + protected ActorSystem System + { + get { return ((LocalActorRef)Self).Cell.System; } } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs index d88a0057..845199ec 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs @@ -1,50 +1,47 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; using System.Threading; +using System.Threading.Tasks; using Akka.Actor; using Akka.Configuration; using Akka.Dispatch; using Akka.TestKit; using Akka.TestKit.Internal; -using Akka.Util; -using Akka.Util.Internal; using FluentAssertions; using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; namespace Akka.Hosting.TestKit.Tests.TestActorRefTests { - public class TestActorRefSpec : HostingSpec + public class TestActorRefSpec : TestKit { - private readonly AtomicCounter _counter = new AtomicCounter(4); - private readonly Thread _thread = Thread.CurrentThread; - private readonly AtomicReference _otherThread = new AtomicReference(null); + public static int Counter = 4; + public static readonly Thread Thread = Thread.CurrentThread; + public static Thread OtherThread; - public TestActorRefSpec(ITestOutputHelper output) : base(nameof(TestActorRefSpec), output) - { } - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + public TestActorRefSpec() { - builder.AddHocon(GetConfig()); } - + private TimeSpan DefaultTimeout => Dilated(TestKitSettings.DefaultTimeout); - private static Config GetConfig() + protected override Config Config => "test-dispatcher1.type=\"Akka.Dispatch.PinnedDispatcherConfigurator, Akka\""; + + private void AssertThread() { - return "test-dispatcher1.type=\"Akka.Dispatch.PinnedDispatcherConfigurator, Akka\""; + Assert.True(OtherThread == null || OtherThread == Thread, "Thread"); } - private void AssertThread() + protected override async Task BeforeTestStart() { - Assert.True(_otherThread.Value == null || _otherThread.Value == _thread, "Thread"); + await base.BeforeTestStart(); + OtherThread = null; } [Fact] @@ -86,24 +83,22 @@ public void TestActorRef_must_support_nested_Actor_creation_when_used_with_Actor [Fact] public void TestActorRef_must_support_reply_via_sender() { - var serverRef = new TestActorRef(Sys, Props.Create(() => - new ReplyActor(_thread, _otherThread))); - var clientRef = new TestActorRef(Sys, Props.Create(() => - new SenderActor(serverRef, _counter, _thread, _otherThread))); + var serverRef = new TestActorRef(Sys, Props.Create()); + var clientRef = new TestActorRef(Sys, Props.Create(() => new SenderActor(serverRef))); - _counter.GetAndSet(4); + Counter = 4; clientRef.Tell("complex"); clientRef.Tell("simple"); clientRef.Tell("simple"); clientRef.Tell("simple"); - _counter.Current.Should().Be(0); + Counter.Should().Be(0); - _counter.GetAndSet(4); + Counter = 4; clientRef.Tell("complex2"); clientRef.Tell("simple"); clientRef.Tell("simple"); clientRef.Tell("simple"); - _counter.Current.Should().Be(0); + Counter.Should().Be(0); AssertThread(); } @@ -112,11 +107,10 @@ public void TestActorRef_must_support_reply_via_sender() public void TestActorRef_must_stop_when_sent_a_PoisonPill() { //TODO: Should have this surrounding all code EventFilter[ActorKilledException]() intercept { - var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread)), null, "will-be-killed"); + var a = new TestActorRef(Sys, Props.Create(), null, "will-be-killed"); Sys.ActorOf(Props.Create(() => new WatchAndForwardActor(a, TestActor)), "forwarder"); a.Tell(PoisonPill.Instance); - ExpectMsg(w => w.Terminated.ActorRef == a, TimeSpan.FromSeconds(10), - $"that the terminated actor was the one killed, i.e. {a.Path}"); + ExpectMsg(w => w.Terminated.ActorRef == a, TimeSpan.FromSeconds(10), string.Format("that the terminated actor was the one killed, i.e. {0}", a.Path)); var actorRef = (InternalTestActorRef)a.Ref; actorRef.IsTerminated.Should().Be(true); AssertThread(); @@ -126,29 +120,28 @@ public void TestActorRef_must_stop_when_sent_a_PoisonPill() public void TestActorRef_must_restart_when_killed() { //TODO: Should have this surrounding all code EventFilter[ActorKilledException]() intercept { - _counter.GetAndSet(2); - var boss = new TestActorRef(Sys, Props.Create(() => new BossActor(_counter, _thread, _otherThread))); + Counter = 2; + var boss = new TestActorRef(Sys, Props.Create()); boss.Tell("sendKill"); - Assert.Equal(0, _counter.Current); + Assert.Equal(0, Counter); AssertThread(); } [Fact] public void TestActorRef_must_support_futures() { - var worker = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread))); + var worker = new TestActorRef(Sys, Props.Create()); var task = worker.Ask("work"); Assert.True(task.IsCompleted, "Task should be completed"); - if (!task.Wait(DefaultTimeout)) - throw new XunitException("Timed out"); //Using a timeout to stop the test if there is something wrong with the code + if(!task.Wait(DefaultTimeout)) throw new TimeoutException("Timed out"); //Using a timeout to stop the test if there is something wrong with the code Assert.Equal("workDone", task.Result); } [Fact] public void TestActorRef_must_allow_access_to_internals() { - var actorRef = new TestActorRef(Sys, Props.Create(() => new SaveStringActor(_thread, _otherThread))); + var actorRef = new TestActorRef(Sys, Props.Create()); actorRef.Tell("Hejsan!"); var actor = actorRef.UnderlyingActor; Assert.Equal("Hejsan!", actor.ReceivedString); @@ -157,14 +150,14 @@ public void TestActorRef_must_allow_access_to_internals() [Fact] public void TestActorRef_must_set_ReceiveTimeout_to_None() { - var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread))); + var a = new TestActorRef(Sys, Props.Create()); ((IInternalActor)a.UnderlyingActor).ActorContext.ReceiveTimeout.Should().Be(null); } [Fact] public void TestActorRef_must_set_CallingThreadDispatcher() { - var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread))); + var a = new TestActorRef(Sys, Props.Create()); var actorRef = (InternalTestActorRef)a.Ref; Assert.IsType(actorRef.Cell.Dispatcher); } @@ -172,7 +165,7 @@ public void TestActorRef_must_set_CallingThreadDispatcher() [Fact] public void TestActorRef_must_allow_override_of_dispatcher() { - var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread)).WithDispatcher("test-dispatcher1")); + var a = new TestActorRef(Sys, Props.Create().WithDispatcher("test-dispatcher1")); var actorRef = (InternalTestActorRef)a.Ref; Assert.IsType(actorRef.Cell.Dispatcher); } @@ -180,7 +173,7 @@ public void TestActorRef_must_allow_override_of_dispatcher() [Fact] public void TestActorRef_must_proxy_receive_for_the_underlying_actor_without_sender() { - var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread))); + var a = new TestActorRef(Sys, Props.Create()); a.Receive("work"); var actorRef = (InternalTestActorRef)a.Ref; Assert.True(actorRef.IsTerminated); @@ -189,7 +182,7 @@ public void TestActorRef_must_proxy_receive_for_the_underlying_actor_without_sen [Fact] public void TestActorRef_must_proxy_receive_for_the_underlying_actor_with_sender() { - var a = new TestActorRef(Sys, Props.Create(() => new WorkerActor(_thread, _otherThread))); + var a = new TestActorRef(Sys, Props.Create()); a.Receive("work", TestActor); //This will stop the actor var actorRef = (InternalTestActorRef)a.Ref; Assert.True(actorRef.IsTerminated); @@ -231,9 +224,6 @@ protected override bool ReceiveMessage(object message) ReceivedString = message as string; return true; } - - public SaveStringActor(Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) - { } } } } diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs index 23bed24d..ac8113f6 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs @@ -1,7 +1,7 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- @@ -12,22 +12,12 @@ using Akka.TestKit.TestActors; using Akka.Util.Internal; using FluentAssertions; -using Microsoft.Extensions.Logging; using Xunit; -using Xunit.Abstractions; namespace Akka.Hosting.TestKit.Tests.TestActorRefTests { - public class TestProbeSpec : HostingSpec + public class TestProbeSpec : TestKit { - public TestProbeSpec(ITestOutputHelper output) : base(nameof(TestProbeSpec), output) - { - } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - } - [Fact] public void TestProbe_should_equal_underlying_Ref() { @@ -100,8 +90,8 @@ public void TestProbe_restart_a_failing_child_if_the_given_supervisor_says_so() restarts.Current.Should().BeGreaterThan(1); }); } - - private class FailingActor : ActorBase + + class FailingActor : ActorBase { private AtomicCounter Restarts { get; } diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WatchAndForwardActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WatchAndForwardActor.cs index b1ea69d9..0306bc9e 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WatchAndForwardActor.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WatchAndForwardActor.cs @@ -1,33 +1,31 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using Akka.Actor; -namespace Akka.Hosting.TestKit.Tests.TestActorRefTests -{ - public class WatchAndForwardActor : ActorBase - { - private readonly IActorRef _forwardToActor; +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests; - public WatchAndForwardActor(IActorRef watchedActor, IActorRef forwardToActor) - { - _forwardToActor = forwardToActor; - Context.Watch(watchedActor); - } +public class WatchAndForwardActor : ActorBase +{ + private readonly IActorRef _forwardToActor; - protected override bool Receive(object message) - { - var terminated = message as Terminated; - if(terminated != null) - _forwardToActor.Tell(new WrappedTerminated(terminated), Sender); - else - _forwardToActor.Tell(message, Sender); - return true; - } + public WatchAndForwardActor(IActorRef watchedActor, IActorRef forwardToActor) + { + _forwardToActor = forwardToActor; + Context.Watch(watchedActor); } -} + protected override bool Receive(object message) + { + var terminated = message as Terminated; + if(terminated != null) + _forwardToActor.Tell(new WrappedTerminated(terminated), Sender); + else + _forwardToActor.Tell(message, Sender); + return true; + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WorkerActor.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WorkerActor.cs index 18b4f745..832849b2 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WorkerActor.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WorkerActor.cs @@ -1,39 +1,31 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- -using System.Threading; using Akka.Actor; -using Akka.Util; -namespace Akka.Hosting.TestKit.Tests.TestActorRefTests +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests; + +public class WorkerActor : TActorBase { - public class WorkerActor : TActorBase + protected override bool ReceiveMessage(object message) { - public WorkerActor(Thread parentThread, AtomicReference otherThread) : base(parentThread, otherThread) + if((message as string) == "work") { + Sender.Tell("workDone"); + Context.Stop(Self); + return true; + } - - protected override bool ReceiveMessage(object message) + //TODO: case replyTo: Promise[_] ⇒ replyTo.asInstanceOf[Promise[Any]].success("complexReply") + if(message is IActorRef) { - if((message as string) == "work") - { - Sender.Tell("workDone"); - Context.Stop(Self); - return true; - - } - //TODO: case replyTo: Promise[_] ⇒ replyTo.asInstanceOf[Promise[Any]].success("complexReply") - if(message is IActorRef) - { - ((IActorRef)message).Tell("complexReply", Self); - return true; - } - return false; + ((IActorRef)message).Tell("complexReply", Self); + return true; } + return false; } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WrappedTerminated.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WrappedTerminated.cs index a0c02200..b940dedd 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WrappedTerminated.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/WrappedTerminated.cs @@ -1,24 +1,22 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using Akka.Actor; -namespace Akka.Hosting.TestKit.Tests.TestActorRefTests -{ - public class WrappedTerminated - { - private readonly Terminated _terminated; +namespace Akka.Hosting.TestKit.Tests.TestActorRefTests; - public WrappedTerminated(Terminated terminated) - { - _terminated = terminated; - } +public class WrappedTerminated +{ + private readonly Terminated _terminated; - public Terminated Terminated { get { return _terminated; } } + public WrappedTerminated(Terminated terminated) + { + _terminated = terminated; } -} + public Terminated Terminated { get { return _terminated; } } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs index 5f271781..98699273 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs @@ -1,7 +1,7 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- @@ -11,7 +11,6 @@ using Akka.TestKit; using FluentAssertions; using Xunit; -using Xunit.Abstractions; using Xunit.Sdk; using static FluentAssertions.FluentActions; @@ -19,24 +18,25 @@ namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests { public abstract class AllTestForEventFilterBase : EventFilterTestBase where TLogEvent : LogEvent { + // ReSharper disable ConvertToLambdaExpression private EventFilterFactory _testingEventFilter; - protected AllTestForEventFilterBase(string config, string systemName, ITestOutputHelper output) - : base(config, systemName, output) + protected AllTestForEventFilterBase(string config) + : base(config) { } - protected new LogLevel LogLevel { get; private set; } - protected abstract EventFilterFactory CreateTestingEventFilter(); - protected override async Task BeforeTestStart() { await base.BeforeTestStart(); - LogLevel = Event.Logging.LogLevelFor(); + // ReSharper disable once VirtualMemberCallInContructor _testingEventFilter = CreateTestingEventFilter(); } + protected new LogLevel LogLevel { get; private set; } + protected abstract EventFilterFactory CreateTestingEventFilter(); + protected void LogMessage(string message) { Log.Log(LogLevel, message); @@ -50,66 +50,62 @@ protected override void SendRawLogEventMessage(object message) protected abstract void PublishMessage(object message, string source); [Fact] - public async Task Single_message_is_intercepted() + public void Single_message_is_intercepted() { - await _testingEventFilter.ForLogLevel(LogLevel) - .ExpectOneAsync(async () => LogMessage("whatever")); + _testingEventFilter.ForLogLevel(LogLevel).ExpectOne(() => LogMessage("whatever")); TestSuccessful = true; } [Fact] - public async Task Can_intercept_messages_when_start_is_specified() + public void Can_intercept_messages_when_start_is_specified() { - await _testingEventFilter.ForLogLevel(LogLevel, start: "what") - .ExpectOneAsync(async () => LogMessage("whatever")); + _testingEventFilter.ForLogLevel(LogLevel, start: "what").ExpectOne(() => LogMessage("whatever")); TestSuccessful = true; } [Fact] - public async Task Do_not_intercept_messages_when_start_does_not_match() + public void Do_not_intercept_messages_when_start_does_not_match() { - await _testingEventFilter.ForLogLevel(LogLevel, start: "what").ExpectOneAsync(async () => + _testingEventFilter.ForLogLevel(LogLevel, start: "what").ExpectOne(() => { LogMessage("let-me-thru"); LogMessage("whatever"); }); - await ExpectMsgAsync(err => (string)err.Message == "let-me-thru"); + ExpectMsg(err => (string)err.Message == "let-me-thru"); TestSuccessful = true; } [Fact] - public async Task Can_intercept_messages_when_message_is_specified() + public void Can_intercept_messages_when_message_is_specified() { - await _testingEventFilter.ForLogLevel(LogLevel, message: "whatever") - .ExpectOneAsync(async () => LogMessage("whatever")); + _testingEventFilter.ForLogLevel(LogLevel, message: "whatever").ExpectOne(() => LogMessage("whatever")); TestSuccessful = true; } [Fact] - public async Task Do_not_intercept_messages_when_message_does_not_match() + public void Do_not_intercept_messages_when_message_does_not_match() { - await EventFilter.ForLogLevel(LogLevel, message: "whatever").ExpectOneAsync(async () => + EventFilter.ForLogLevel(LogLevel, message: "whatever").ExpectOne(() => { LogMessage("let-me-thru"); LogMessage("whatever"); }); - await ExpectMsgAsync(err => (string)err.Message == "let-me-thru"); + ExpectMsg(err => (string)err.Message == "let-me-thru"); TestSuccessful = true; } [Fact] - public async Task Can_intercept_messages_when_contains_is_specified() + public void Can_intercept_messages_when_contains_is_specified() { - await _testingEventFilter.ForLogLevel(LogLevel, contains: "ate") - .ExpectOneAsync(async () => LogMessage("whatever")); + _testingEventFilter.ForLogLevel(LogLevel, contains: "ate").ExpectOne(() => LogMessage("whatever")); TestSuccessful = true; } [Fact] - public async Task Do_not_intercept_messages_when_contains_does_not_match() + public void Do_not_intercept_messages_when_contains_does_not_match() { - await _testingEventFilter.ForLogLevel(LogLevel, contains: "eve").ExpectOneAsync(async () => + _testingEventFilter.ForLogLevel(LogLevel, contains: "eve").ExpectOne(() => { LogMessage("let-me-thru"); LogMessage("whatever"); @@ -120,31 +116,28 @@ await _testingEventFilter.ForLogLevel(LogLevel, contains: "eve").ExpectOneAsync( [Fact] - public async Task Can_intercept_messages_when_source_is_specified() + public void Can_intercept_messages_when_source_is_specified() { - var type = GetType(); - await _testingEventFilter.ForLogLevel(LogLevel, source: LogSource.FromType(GetType(), Sys)) - .ExpectOneAsync(async () => LogMessage("whatever")); + _testingEventFilter.ForLogLevel(LogLevel, source: LogSource.FromType(GetType(), Sys)).ExpectOne(() => LogMessage("whatever")); TestSuccessful = true; } [Fact] - public async Task Do_not_intercept_messages_when_source_does_not_match() + public void Do_not_intercept_messages_when_source_does_not_match() { - await _testingEventFilter.ForLogLevel(LogLevel, source: "expected-source").ExpectOneAsync(() => + _testingEventFilter.ForLogLevel(LogLevel, source: "expected-source").ExpectOne(() => { PublishMessage("message", source: "expected-source"); PublishMessage("message", source: "let-me-thru"); - return Task.CompletedTask; }); - await ExpectMsgAsync(err => err.LogSource == "let-me-thru"); + ExpectMsg(err => err.LogSource == "let-me-thru"); TestSuccessful = true; } [Fact] - public async Task Specified_numbers_of_messages_can_be_intercepted() + public void Specified_numbers_of_messagesan_be_intercepted() { - await _testingEventFilter.ForLogLevel(LogLevel).ExpectAsync(2, async () => + _testingEventFilter.ForLogLevel(LogLevel).Expect(2, () => { LogMessage("whatever"); LogMessage("whatever"); @@ -153,29 +146,35 @@ await _testingEventFilter.ForLogLevel(LogLevel).ExpectAsync(2, async () => } [Fact] - public async Task Expect_0_events_Should_work() + public void Expect_0_events_Should_work() { - await Awaiting(async () => + this.Invoking(_ => { - await EventFilter.Error().ExpectAsync(0, () => + EventFilter.Error().Expect(0, () => { Log.Error("something"); - return Task.CompletedTask; }); - }).Should().ThrowAsync("Expected 0 events"); + }).Should().Throw("Expected 0 events"); } [Fact] public async Task ExpectAsync_0_events_Should_work() { - await Awaiting(async () => + Exception ex = null; + try { await EventFilter.Error().ExpectAsync(0, async () => { await Task.Delay(100); // bug only happens when error is not logged instantly Log.Error("something"); }); - }).Should().ThrowAsync("Expected 0 errors logged, but there are error logs"); + } + catch (Exception e) + { + ex = e; + } + + ex.Should().NotBeNull("Expected 0 errors logged, but there are error logs"); } /// issue: InternalExpectAsync does not await actionAsync() - causing actionAsync to run as a detached task #5537 @@ -207,9 +206,9 @@ await _testingEventFilter.ForLogLevel(LogLevel).ExpectAsync(0, async () => } [Fact] - public async Task Messages_can_be_muted() + public void Messages_can_be_muted() { - await _testingEventFilter.ForLogLevel(LogLevel).MuteAsync(async () => + _testingEventFilter.ForLogLevel(LogLevel).Mute(() => { LogMessage("whatever"); LogMessage("whatever"); @@ -219,7 +218,7 @@ await _testingEventFilter.ForLogLevel(LogLevel).MuteAsync(async () => [Fact] - public async Task Messages_can_be_muted_from_now_on() + public void Messages_can_be_muted_from_now_on() { var unmutableFilter = _testingEventFilter.ForLogLevel(LogLevel).Mute(); LogMessage("whatever"); @@ -229,7 +228,7 @@ public async Task Messages_can_be_muted_from_now_on() } [Fact] - public async Task Messages_can_be_muted_from_now_on_with_using() + public void Messages_can_be_muted_from_now_on_with_using() { using(_testingEventFilter.ForLogLevel(LogLevel).Mute()) { @@ -241,50 +240,53 @@ public async Task Messages_can_be_muted_from_now_on_with_using() [Fact] - public async Task Make_sure_async_works() + public void Make_sure_async_works() { - await _testingEventFilter.ForLogLevel(LogLevel).ExpectAsync(1, TimeSpan.FromSeconds(2), async () => + _testingEventFilter.ForLogLevel(LogLevel).Expect(1, TimeSpan.FromSeconds(2), () => { - await Task.Delay(TimeSpan.FromMilliseconds(10)).ContinueWith(async t => { LogMessage("whatever"); }); + Task.Delay(TimeSpan.FromMilliseconds(10)).ContinueWith(t => { LogMessage("whatever"); }); }); } [Fact] - public async Task Chain_many_filters() + public void Chain_many_filters() { - await _testingEventFilter + _testingEventFilter .ForLogLevel(LogLevel,message:"Message 1").And .ForLogLevel(LogLevel,message:"Message 3") - .ExpectAsync(2, async () => - { - LogMessage("Message 1"); - LogMessage("Message 2"); - LogMessage("Message 3"); - - }); - await ExpectMsgAsync(m => (string) m.Message == "Message 2"); + .Expect(2,() => + { + LogMessage("Message 1"); + LogMessage("Message 2"); + LogMessage("Message 3"); + + }); + ExpectMsg(m => (string) m.Message == "Message 2"); } [Fact] - public async Task Should_timeout_if_too_few_messages() + public void Should_timeout_if_too_few_messages() { - await Awaiting(async () => + Invoking(() => { - await _testingEventFilter.ForLogLevel(LogLevel).ExpectAsync(2, TimeSpan.FromMilliseconds(50), async () => + _testingEventFilter.ForLogLevel(LogLevel).Expect(2, TimeSpan.FromMilliseconds(50), () => { LogMessage("whatever"); }); - }).Should().ThrowAsync().WithMessage("Timeout (*"); + }).Should().Throw().WithMessage("timeout*"); } [Fact] - public async Task Should_log_when_not_muting() + public void Should_log_when_not_muting() { const string message = "This should end up in the log since it's not filtered"; LogMessage(message); - await ExpectMsgAsync( msg => (string)msg.Message == message); + ExpectMsg( msg => (string)msg.Message == message); } + + // ReSharper restore ConvertToLambdaExpression + } } diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs index 4a246df3..fcbd4aa3 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs @@ -1,143 +1,132 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using Akka.Event; using Akka.TestKit; -using Xunit.Abstractions; -namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests -{ - public class EventFilterDebugTests : AllTestForEventFilterBase - { - public EventFilterDebugTests(ITestOutputHelper output) - : base("akka.loglevel=DEBUG", nameof(EventFilterDebugTests), output){} +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests; - protected override EventFilterFactory CreateTestingEventFilter() - { - return EventFilter; - } +public class EventFilterDebugTests : AllTestForEventFilterBase +{ + public EventFilterDebugTests() : base("akka.loglevel=DEBUG"){} - protected override void PublishMessage(object message, string source) - { - Sys.EventStream.Publish(new Debug(source,GetType(),message)); - } + protected override EventFilterFactory CreateTestingEventFilter() + { + return EventFilter; } - public class CustomEventFilterDebugTests : AllTestForEventFilterBase + protected override void PublishMessage(object message, string source) { - public CustomEventFilterDebugTests(ITestOutputHelper output) - : base("akka.loglevel=DEBUG", nameof(CustomEventFilterDebugTests), output) { } + Sys.EventStream.Publish(new Debug(source,GetType(),message)); + } +} - protected override EventFilterFactory CreateTestingEventFilter() - { - return CreateEventFilter(Sys); - } +public class CustomEventFilterDebugTests : AllTestForEventFilterBase +{ + public CustomEventFilterDebugTests() : base("akka.loglevel=DEBUG") { } - protected override void PublishMessage(object message, string source) - { - Sys.EventStream.Publish(new Debug(source, GetType(), message)); - } + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); } - public class EventFilterInfoTests : AllTestForEventFilterBase + protected override void PublishMessage(object message, string source) { - public EventFilterInfoTests(ITestOutputHelper output) - : base("akka.loglevel=INFO", nameof(EventFilterInfoTests), output) { } + Sys.EventStream.Publish(new Debug(source, GetType(), message)); + } +} - protected override EventFilterFactory CreateTestingEventFilter() - { - return EventFilter; - } +public class EventFilterInfoTests : AllTestForEventFilterBase +{ + public EventFilterInfoTests() : base("akka.loglevel=INFO") { } - protected override void PublishMessage(object message, string source) - { - Sys.EventStream.Publish(new Info(source, GetType(), message)); - } + protected override EventFilterFactory CreateTestingEventFilter() + { + return EventFilter; } - public class CustomEventFilterInfoTests : AllTestForEventFilterBase + protected override void PublishMessage(object message, string source) { - public CustomEventFilterInfoTests(ITestOutputHelper output) : base( - "akka.loglevel=INFO", nameof(CustomEventFilterInfoTests), output) { } + Sys.EventStream.Publish(new Info(source, GetType(), message)); + } +} - protected override EventFilterFactory CreateTestingEventFilter() - { - return CreateEventFilter(Sys); - } +public class CustomEventFilterInfoTests : AllTestForEventFilterBase +{ + public CustomEventFilterInfoTests() : base("akka.loglevel=INFO") { } - protected override void PublishMessage(object message, string source) - { - Sys.EventStream.Publish(new Info(source, GetType(), message)); - } + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); } - - public class EventFilterWarningTests : AllTestForEventFilterBase + protected override void PublishMessage(object message, string source) { - public EventFilterWarningTests(ITestOutputHelper output) : base( - "akka.loglevel=WARNING", nameof(EventFilterWarningTests), output) { } + Sys.EventStream.Publish(new Info(source, GetType(), message)); + } +} - protected override EventFilterFactory CreateTestingEventFilter() - { - return EventFilter; - } - protected override void PublishMessage(object message, string source) - { - Sys.EventStream.Publish(new Warning(source, GetType(), message)); - } +public class EventFilterWarningTests : AllTestForEventFilterBase +{ + public EventFilterWarningTests() : base("akka.loglevel=WARNING") { } + + protected override EventFilterFactory CreateTestingEventFilter() + { + return EventFilter; } - public class CustomEventFilterWarningTests : AllTestForEventFilterBase + protected override void PublishMessage(object message, string source) { - public CustomEventFilterWarningTests(ITestOutputHelper output) - : base("akka.loglevel=WARNING", nameof(CustomEventFilterWarningTests), output) { } + Sys.EventStream.Publish(new Warning(source, GetType(), message)); + } +} - protected override EventFilterFactory CreateTestingEventFilter() - { - return CreateEventFilter(Sys); - } +public class CustomEventFilterWarningTests : AllTestForEventFilterBase +{ + public CustomEventFilterWarningTests() : base("akka.loglevel=WARNING") { } - protected override void PublishMessage(object message, string source) - { - Sys.EventStream.Publish(new Warning(source, GetType(), message)); - } + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); } - public class EventFilterErrorTests : AllTestForEventFilterBase + protected override void PublishMessage(object message, string source) { - public EventFilterErrorTests(ITestOutputHelper output) - : base("akka.loglevel=ERROR", nameof(EventFilterErrorTests), output) { } + Sys.EventStream.Publish(new Warning(source, GetType(), message)); + } +} - protected override EventFilterFactory CreateTestingEventFilter() - { - return EventFilter; - } +public class EventFilterErrorTests : AllTestForEventFilterBase +{ + public EventFilterErrorTests() : base("akka.loglevel=ERROR") { } - protected override void PublishMessage(object message, string source) - { - Sys.EventStream.Publish(new Error(null, source, GetType(), message)); - } + protected override EventFilterFactory CreateTestingEventFilter() + { + return EventFilter; } - public class CustomEventFilterErrorTests : AllTestForEventFilterBase + protected override void PublishMessage(object message, string source) { - public CustomEventFilterErrorTests(ITestOutputHelper output) - : base("akka.loglevel=ERROR", nameof(CustomEventFilterErrorTests), output) { } + Sys.EventStream.Publish(new Error(null, source, GetType(), message)); + } +} - protected override EventFilterFactory CreateTestingEventFilter() - { - return CreateEventFilter(Sys); - } +public class CustomEventFilterErrorTests : AllTestForEventFilterBase +{ + public CustomEventFilterErrorTests() : base("akka.loglevel=ERROR") { } - protected override void PublishMessage(object message, string source) - { - Sys.EventStream.Publish(new Error(null, source, GetType(), message)); - } + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); } -} + protected override void PublishMessage(object message, string source) + { + Sys.EventStream.Publish(new Error(null, source, GetType(), message)); + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs index 8cca36dc..98e18356 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs @@ -1,19 +1,17 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- -using System; using System.Linq; using FluentAssertions; using Xunit; -using Xunit.Abstractions; namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests { - public class ConfigTests : HostingSpec + public class ConfigTests : TestKit { [Fact] public void TestEventListener_is_in_config_by_default() @@ -22,14 +20,6 @@ public void TestEventListener_is_in_config_by_default() configLoggers.Any(logger => logger.Contains("Akka.TestKit.TestEventListener")).Should().BeTrue(); configLoggers.Any(logger => logger.Contains("Akka.Event.DefaultLogger")).Should().BeFalse(); } - - public ConfigTests(ITestOutputHelper output = null) : base(nameof(ConfigTests), output) - { - } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - } } } diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs index 7fa4ef87..79ccee4e 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs @@ -1,77 +1,62 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- -using System.Threading.Tasks; using Akka.Event; using Akka.TestKit; using Xunit; -using Xunit.Abstractions; -namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests -{ - public abstract class CustomEventFilterTestsBase : EventFilterTestBase - { - protected CustomEventFilterTestsBase(string systemName, ITestOutputHelper output) - : base("akka.loglevel=ERROR", systemName, output) { } +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests; - protected override void SendRawLogEventMessage(object message) - { - Sys.EventStream.Publish(new Error(null, "CustomEventFilterTests", GetType(), message)); - } +public abstract class CustomEventFilterTestsBase : EventFilterTestBase +{ + // ReSharper disable ConvertToLambdaExpression + public CustomEventFilterTestsBase() : base("akka.loglevel=ERROR") { } - protected abstract EventFilterFactory CreateTestingEventFilter(); + protected override void SendRawLogEventMessage(object message) + { + Sys.EventStream.Publish(new Error(null, "CustomEventFilterTests", GetType(), message)); + } - [Fact] - public async Task Custom_filter_should_match() - { - var eventFilter = CreateTestingEventFilter(); - await eventFilter.Custom(logEvent => logEvent is Error && (string) logEvent.Message == "whatever") - .ExpectOneAsync(() => - { - Log.Error("whatever"); - return Task.CompletedTask; - }); - } + protected abstract EventFilterFactory CreateTestingEventFilter(); - [Fact] - public async Task Custom_filter_should_match2() + [Fact] + public void Custom_filter_should_match() + { + var eventFilter = CreateTestingEventFilter(); + eventFilter.Custom(logEvent => logEvent is Error && (string) logEvent.Message == "whatever").ExpectOne(() => { - var eventFilter = CreateTestingEventFilter(); - await eventFilter.Custom(logEvent => (string)logEvent.Message == "whatever") - .ExpectOneAsync(() => - { - Log.Error("whatever"); - return Task.CompletedTask; - }); - } + Log.Error("whatever"); + }); } - public class CustomEventFilterTests : CustomEventFilterTestsBase + [Fact] + public void Custom_filter_should_match2() { - protected override EventFilterFactory CreateTestingEventFilter() - { - return EventFilter; - } - - public CustomEventFilterTests(ITestOutputHelper output) : base(nameof(CustomEventFilterTests), output) + var eventFilter = CreateTestingEventFilter(); + eventFilter.Custom(logEvent => (string)logEvent.Message == "whatever").ExpectOne(() => { - } + Log.Error("whatever"); + }); } + // ReSharper restore ConvertToLambdaExpression +} - public class CustomEventFilterCustomFilterTests : CustomEventFilterTestsBase +public class CustomEventFilterTests : CustomEventFilterTestsBase +{ + protected override EventFilterFactory CreateTestingEventFilter() { - protected override EventFilterFactory CreateTestingEventFilter() - { - return CreateEventFilter(Sys); - } - - public CustomEventFilterCustomFilterTests(ITestOutputHelper output) : base(nameof(CustomEventFilterCustomFilterTests), output) - { - } + return EventFilter; } } +public class CustomEventFilterCustomFilterTests : CustomEventFilterTestsBase +{ + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs index 98e4fa47..20643513 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs @@ -1,93 +1,70 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- -using System; using System.Threading.Tasks; using Akka.Actor; using Akka.Event; using Akka.TestKit; using Akka.TestKit.TestActors; using Xunit; -using Xunit.Abstractions; -namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests -{ - public abstract class DeadLettersEventFilterTestsBase : EventFilterTestBase - { - private enum DeadActorKey - { } - - private IActorRef _deadActor; +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests; - protected DeadLettersEventFilterTestsBase(string systemName, ITestOutputHelper output) - : base("akka.loglevel=ERROR", systemName, output) - { - - } +public abstract class DeadLettersEventFilterTestsBase : EventFilterTestBase +{ + private IActorRef _deadActor; - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - base.ConfigureAkka(builder, provider); - builder.WithActors((system, registry) => - { - var actor = system.ActorOf(BlackHoleActor.Props, "dead-actor"); - registry.Register(actor); - }); - } + // ReSharper disable ConvertToLambdaExpression + protected DeadLettersEventFilterTestsBase() : base("akka.loglevel=ERROR") + { + } - protected override async Task BeforeTestStart() - { - _deadActor = ActorRegistry.Get(); - Watch(_deadActor); - Sys.Stop(_deadActor); - await ExpectTerminatedAsync(_deadActor); - } + protected override async Task BeforeTestStart() + { + await base.BeforeTestStart(); + _deadActor = Sys.ActorOf(BlackHoleActor.Props, "dead-actor"); + Watch(_deadActor); + Sys.Stop(_deadActor); + ExpectTerminated(_deadActor); + } - protected override void SendRawLogEventMessage(object message) - { - Sys.EventStream.Publish(new Error(null, "DeadLettersEventFilterTests", GetType(), message)); - } + protected override void SendRawLogEventMessage(object message) + { + Sys.EventStream.Publish(new Error(null, "DeadLettersEventFilterTests", GetType(), message)); + } - protected abstract EventFilterFactory CreateTestingEventFilter(); + protected abstract EventFilterFactory CreateTestingEventFilter(); - [Fact] - public async Task Should_be_able_to_filter_dead_letters() + [Fact] + public void Should_be_able_to_filter_dead_letters() + { + var eventFilter = CreateTestingEventFilter(); + eventFilter.DeadLetter().ExpectOne(() => { - var eventFilter = CreateTestingEventFilter(); - await eventFilter.DeadLetter().ExpectOneAsync(() => - { - _deadActor.Tell("whatever"); - return Task.CompletedTask; - }); - } + _deadActor.Tell("whatever"); + }); } - public class DeadLettersEventFilterTests : DeadLettersEventFilterTestsBase - { - protected override EventFilterFactory CreateTestingEventFilter() - { - return EventFilter; - } - public DeadLettersEventFilterTests(ITestOutputHelper output) : base(nameof(DeadLettersEventFilterTests), output) - { - } - } + // ReSharper restore ConvertToLambdaExpression +} - public class DeadLettersCustomEventFilterTests : DeadLettersEventFilterTestsBase +public class DeadLettersEventFilterTests : DeadLettersEventFilterTestsBase +{ + protected override EventFilterFactory CreateTestingEventFilter() { - protected override EventFilterFactory CreateTestingEventFilter() - { - return CreateEventFilter(Sys); - } - - public DeadLettersCustomEventFilterTests(ITestOutputHelper output) : base(nameof(DeadLettersCustomEventFilterTests), output) - { - } + return EventFilter; } } +public class DeadLettersCustomEventFilterTests : DeadLettersEventFilterTestsBase +{ + protected override EventFilterFactory CreateTestingEventFilter() + { + return CreateEventFilter(Sys); + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs index b635ae26..612914dc 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs @@ -1,64 +1,62 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- -using System; using System.Threading.Tasks; using Akka.Configuration; using Akka.Event; -using Xunit.Abstractions; namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests { - public abstract class EventFilterTestBase : HostingSpec + public abstract class EventFilterTestBase : TestKit { /// /// Used to signal that the test was successful and that we should ensure no more messages were logged /// protected bool TestSuccessful; - protected override Config Config { get; } - - protected EventFilterTestBase(string config, string systemName, ITestOutputHelper output) - : base(systemName, output) + protected EventFilterTestBase(string config) { Config = $"akka.loggers = [\"{typeof(ForwardAllEventsTestEventListener).AssemblyQualifiedName}\"], {(string.IsNullOrEmpty(config) ? "" : config)}"; } + protected override Config Config { get; } + + protected abstract void SendRawLogEventMessage(object message); + protected override async Task BeforeTestStart() { await base.BeforeTestStart(); + //We send a ForwardAllEventsTo containing message to the TestEventListenerToForwarder logger (configured as a logger above). //It should respond with an "OK" message when it has received the message. var initLoggerMessage = new ForwardAllEventsTestEventListener.ForwardAllEventsTo(TestActor); - + // ReSharper disable once DoNotCallOverridableMethodsInConstructor SendRawLogEventMessage(initLoggerMessage); - await ExpectMsgAsync("OK"); + ExpectMsg("OK"); //From now on we know that all messages will be forwarded to TestActor } - protected abstract void SendRawLogEventMessage(object message); - protected override async Task AfterAllAsync() { //After every test we make sure no uncatched messages have been logged if(TestSuccessful) { - await EnsureNoMoreLoggedMessages(); + EnsureNoMoreLoggedMessages(); } await base.AfterAllAsync(); } - private async Task EnsureNoMoreLoggedMessages() + private void EnsureNoMoreLoggedMessages() { //We log a Finished message. When it arrives to TestActor we know no other message has been logged. //If we receive something else it means another message was logged, and ExpectMsg will fail const string message = "<>"; SendRawLogEventMessage(message); - await ExpectMsgAsync(err => (string) err.Message == message,hint: "message to be \"" + message + "\""); + ExpectMsg(err => (string) err.Message == message,hint: "message to be \"" + message + "\""); } } diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs index d740b350..0abbd878 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs @@ -1,203 +1,174 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; -using System.Threading.Tasks; using Akka.Actor; using Akka.Event; using FluentAssertions; using Xunit; -using Xunit.Abstractions; using Xunit.Sdk; using static FluentAssertions.FluentActions; -namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests; + +public class ExceptionEventFilterTests : EventFilterTestBase { - public class ExceptionEventFilterTests : EventFilterTestBase + public ExceptionEventFilterTests() + : base("akka.logLevel=ERROR") { - public ExceptionEventFilterTests(ITestOutputHelper output) - : base("akka.logLevel=ERROR", nameof(ExceptionEventFilterTests), output) - { - } - - public class SomeException : Exception { } + } + + public class SomeException : Exception { } - protected override void SendRawLogEventMessage(object message) - { - Sys.EventStream.Publish(new Error(null, nameof(ExceptionEventFilterTests), GetType(), message)); - } + protected override void SendRawLogEventMessage(object message) + { + Sys.EventStream.Publish(new Error(null, nameof(ExceptionEventFilterTests), GetType(), message)); + } - [Fact] - public async Task SingleExceptionIsIntercepted() - { - await EventFilter.Exception() - .ExpectOneAsync(() => - { - Log.Error(new SomeException(), "whatever"); - return Task.CompletedTask; - }); - await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); - } + [Fact] + public void SingleExceptionIsIntercepted() + { + EventFilter.Exception() + .ExpectOne(() => Log.Error(new SomeException(), "whatever")); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } - [Fact] - public async Task CanInterceptMessagesWhenStartIsSpecified() - { - await EventFilter.Exception(start: "what") - .ExpectOneAsync(() => - { - Log.Error(new SomeException(), "whatever"); - return Task.CompletedTask; - }); - await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); - } + [Fact] + public void CanInterceptMessagesWhenStartIsSpecified() + { + EventFilter.Exception(start: "what") + .ExpectOne(() => Log.Error(new SomeException(), "whatever")); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } - [Fact] - public async Task DoNotInterceptMessagesWhenStartDoesNotMatch() - { - EventFilter.Exception(start: "this is clearly not in message"); - Log.Error(new SomeException(), "whatever"); - await ExpectMsgAsync(err => (string)err.Message == "whatever"); - } + [Fact] + public void DoNotInterceptMessagesWhenStartDoesNotMatch() + { + EventFilter.Exception(start: "this is clearly not in message"); + Log.Error(new SomeException(), "whatever"); + ExpectMsg(err => (string)err.Message == "whatever"); + } - [Fact] - public async Task CanInterceptMessagesWhenMessageIsSpecified() - { - await EventFilter.Exception(message: "whatever") - .ExpectOneAsync(() => - { - Log.Error(new SomeException(), "whatever"); - return Task.CompletedTask; - }); - await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); - } + [Fact] + public void CanInterceptMessagesWhenMessageIsSpecified() + { + EventFilter.Exception(message: "whatever") + .ExpectOne(() => Log.Error(new SomeException(), "whatever")); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } - [Fact] - public async Task DoNotInterceptMessagesWhenMessageDoesNotMatch() - { - EventFilter.Exception(message: "this is clearly not the message"); - Log.Error(new SomeException(), "whatever"); - await ExpectMsgAsync(err => (string)err.Message == "whatever"); - } + [Fact] + public void DoNotInterceptMessagesWhenMessageDoesNotMatch() + { + EventFilter.Exception(message: "this is clearly not the message"); + Log.Error(new SomeException(), "whatever"); + ExpectMsg(err => (string)err.Message == "whatever"); + } - [Fact] - public async Task CanInterceptMessagesWhenContainsIsSpecified() - { - await EventFilter.Exception(contains: "ate") - .ExpectOneAsync(() => - { - Log.Error(new SomeException(), "whatever"); - return Task.CompletedTask; - }); - await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); - } + [Fact] + public void CanInterceptMessagesWhenContainsIsSpecified() + { + EventFilter.Exception(contains: "ate") + .ExpectOne(() => Log.Error(new SomeException(), "whatever")); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } - [Fact] - public async Task DoNotInterceptMessagesWhenContainsDoesNotMatch() - { - EventFilter.Exception(contains: "this is clearly not in the message"); - Log.Error(new SomeException(), "whatever"); - await ExpectMsgAsync(err => (string)err.Message == "whatever"); - } + [Fact] + public void DoNotInterceptMessagesWhenContainsDoesNotMatch() + { + EventFilter.Exception(contains: "this is clearly not in the message"); + Log.Error(new SomeException(), "whatever"); + ExpectMsg(err => (string)err.Message == "whatever"); + } - [Fact] - public async Task CanInterceptMessagesWhenSourceIsSpecified() - { - await EventFilter.Exception(source: LogSource.Create(this, Sys).Source) - .ExpectOneAsync(() => - { - Log.Error(new SomeException(), "whatever"); - return Task.CompletedTask; - }); - await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); - } + [Fact] + public void CanInterceptMessagesWhenSourceIsSpecified() + { + EventFilter.Exception(source: LogSource.Create(this, Sys).Source) + .ExpectOne(() => + { + Log.Error(new SomeException(), "whatever"); + }); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } - [Fact] - public async Task DoNotInterceptMessagesWhenSourceDoesNotMatch() - { - EventFilter.Exception(source: "this is clearly not the source"); - Log.Error(new SomeException(), "whatever"); - await ExpectMsgAsync(err => (string)err.Message == "whatever"); - } + [Fact] + public void DoNotInterceptMessagesWhenSourceDoesNotMatch() + { + EventFilter.Exception(source: "this is clearly not the source"); + Log.Error(new SomeException(), "whatever"); + ExpectMsg(err => (string)err.Message == "whatever"); + } - [Fact] - public async Task SpecifiedNumbersOfExceptionsCanBeIntercepted() - { - await EventFilter.Exception() - .ExpectAsync(2, () => - { - Log.Error(new SomeException(), "whatever"); - Log.Error(new SomeException(), "whatever"); - return Task.CompletedTask; - }); - await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(100)); - } + [Fact] + public void SpecifiedNumbersOfExceptionsCanBeIntercepted() + { + EventFilter.Exception() + .Expect(2, () => + { + Log.Error(new SomeException(), "whatever"); + Log.Error(new SomeException(), "whatever"); + }); + ExpectNoMsg(TimeSpan.FromMilliseconds(100)); + } - [Fact] - public async Task ShouldFailIfMoreExceptionsThenSpecifiedAreLogged() - { - await Awaiting(async () => - { - await EventFilter.Exception().ExpectAsync(2, () => - { - Log.Error(new SomeException(), "whatever"); - Log.Error(new SomeException(), "whatever"); - Log.Error(new SomeException(), "whatever"); - return Task.CompletedTask; - }); - }) - .Should().ThrowAsync().WithMessage("Received 1 message too many.*"); - } + [Fact] + public void ShouldFailIfMoreExceptionsThenSpecifiedAreLogged() + { + Invoking(() => + EventFilter.Exception().Expect(2, () => + { + Log.Error(new SomeException(), "whatever"); + Log.Error(new SomeException(), "whatever"); + Log.Error(new SomeException(), "whatever"); + })) + .Should().Throw().WithMessage("*1 message too many*"); + } + + [Fact] + public void ShouldReportCorrectMessageCount() + { + var toSend = "Eric Cartman"; + var actor = ActorOf( ExceptionTestActor.Props() ); + + EventFilter + .Exception(source: actor.Path.ToString()) + // expecting 2 because the same exception is logged in PostRestart + .Expect(2, () => actor.Tell( toSend )); + } + + internal sealed class ExceptionTestActor : UntypedActor + { + private ILoggingAdapter Log { get; } = Context.GetLogger(); - [Fact] - public async Task ShouldReportCorrectMessageCount() + protected override void PostRestart(Exception reason) { - var toSend = "Eric Cartman"; - var actor = ActorOf( ExceptionTestActor.Props() ); - - await EventFilter - .Exception(source: actor.Path.ToString()) - // expecting 2 because the same exception is logged in PostRestart - .ExpectAsync(2, () => - { - actor.Tell(toSend); - return Task.CompletedTask; - }); + Log.Error(reason, "[PostRestart]"); + base.PostRestart(reason); } - internal sealed class ExceptionTestActor : UntypedActor + protected override void OnReceive( object message ) { - private ILoggingAdapter Log { get; } = Context.GetLogger(); - - protected override void PostRestart(Exception reason) + switch (message) { - Log.Error(reason, "[PostRestart]"); - base.PostRestart(reason); - } + case string msg: + throw new InvalidOperationException( "I'm sailing away. Set an open course" ); - protected override void OnReceive( object message ) - { - switch (message) - { - case string _: - throw new InvalidOperationException( "I'm sailing away. Set an open course" ); - - default: - Unhandled( message ); - break; - } + default: + Unhandled( message ); + break; } + } - public static Props Props() - { - return Actor.Props.Create( () => new ExceptionTestActor() ); - } + public static Props Props() + { + return Actor.Props.Create( () => new ExceptionTestActor() ); } } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ForwardAllEventsTestEventListener.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ForwardAllEventsTestEventListener.cs index 42712da9..30644a63 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ForwardAllEventsTestEventListener.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ForwardAllEventsTestEventListener.cs @@ -1,7 +1,7 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- @@ -9,39 +9,38 @@ using Akka.Event; using Akka.TestKit; -namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests +namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests; + +public class ForwardAllEventsTestEventListener : TestEventListener { - public class ForwardAllEventsTestEventListener : TestEventListener - { - private IActorRef _forwarder; + private IActorRef _forwarder; - protected override void Print(LogEvent m) - { - if(m.Message is ForwardAllEventsTo to) - { - _forwarder = to.Forwarder; - _forwarder.Tell("OK"); - } - else if(_forwarder != null) - { - _forwarder.Forward(m); - } - else - { - base.Print(m); - } + protected override void Print(LogEvent m) + { + if(m.Message is ForwardAllEventsTo) + { + _forwarder = ((ForwardAllEventsTo)m.Message).Forwarder; + _forwarder.Tell("OK"); } - - public class ForwardAllEventsTo + else if(_forwarder != null) { - public ForwardAllEventsTo(IActorRef forwarder) - { - Forwarder = forwarder; - } - - public IActorRef Forwarder { get; } + _forwarder.Forward(m); + } + else + { + base.Print(m); } } -} + public class ForwardAllEventsTo + { + private readonly IActorRef _forwarder; + + public ForwardAllEventsTo(IActorRef forwarder) + { + _forwarder = forwarder; + } + public IActorRef Forwarder { get { return _forwarder; } } + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs b/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs index 34a9f4cf..42df6ff3 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs @@ -1,102 +1,85 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; -using System.Threading.Tasks; using Akka.Actor; using FluentAssertions; -using FluentAssertions.Extensions; -using Microsoft.Extensions.Logging; using Xunit; -using Xunit.Abstractions; -namespace Akka.Hosting.TestKit.Tests.TestFSMRefTests +namespace Akka.Hosting.TestKit.Tests.TestFSMRefTests; + +public class TestFSMRefSpec : TestKit { - public class TestFSMRefSpec : HostingSpec + [Fact] + public void A_TestFSMRef_must_allow_access_to_internal_state() { - public TestFSMRefSpec(ITestOutputHelper output = null) : base(nameof(TestFSMRefSpec), output) - { - } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - } - - [Fact] - public async Task A_TestFSMRef_must_allow_access_to_internal_state() - { - var fsm = ActorOfAsTestFSMRef("test-fsm-ref-1"); + var fsm = ActorOfAsTestFSMRef("test-fsm-ref-1"); - fsm.StateName.Should().Be(1); - fsm.StateData.Should().Be(""); + fsm.StateName.Should().Be(1); + fsm.StateData.Should().Be(""); - fsm.Tell("go"); - fsm.StateName.Should().Be(2); - fsm.StateData.Should().Be("go"); + fsm.Tell("go"); + fsm.StateName.Should().Be(2); + fsm.StateData.Should().Be("go"); - fsm.SetState(1); - fsm.StateName.Should().Be(1); - fsm.StateData.Should().Be("go"); + fsm.SetState(1); + fsm.StateName.Should().Be(1); + fsm.StateData.Should().Be("go"); - fsm.SetStateData("buh"); - fsm.StateName.Should().Be(1); - fsm.StateData.Should().Be("buh"); + fsm.SetStateData("buh"); + fsm.StateName.Should().Be(1); + fsm.StateData.Should().Be("buh"); - fsm.SetStateTimeout(100.Milliseconds()); - await WithinAsync(80.Milliseconds(), 500.Milliseconds(), async () => - await AwaitConditionAsync(() => Task.FromResult(fsm.StateName == 2 && fsm.StateData == "timeout")) - ); - } + fsm.SetStateTimeout(TimeSpan.FromMilliseconds(100)); + Within(TimeSpan.FromMilliseconds(80), TimeSpan.FromMilliseconds(500), () => + AwaitCondition(() => fsm.StateName == 2 && fsm.StateData == "timeout") + ); + } - [Fact] - public void A_TestFSMRef_must_allow_access_to_timers() - { - var fsm = ActorOfAsTestFSMRef("test-fsm-ref-2"); - fsm.IsTimerActive("test").Should().Be(false); - fsm.SetTimer("test", 12, 10.Milliseconds(), true); - fsm.IsTimerActive("test").Should().Be(true); - fsm.CancelTimer("test"); - fsm.IsTimerActive("test").Should().Be(false); - } + [Fact] + public void A_TestFSMRef_must_allow_access_to_timers() + { + var fsm = ActorOfAsTestFSMRef("test-fsm-ref-2"); + fsm.IsTimerActive("test").Should().Be(false); + fsm.SetTimer("test", 12, TimeSpan.FromMilliseconds(10), true); + fsm.IsTimerActive("test").Should().Be(true); + fsm.CancelTimer("test"); + fsm.IsTimerActive("test").Should().Be(false); + } - private class StateTestFsm : FSM + private class StateTestFsm : FSM + { + public StateTestFsm() { - public StateTestFsm() + StartWith(1, ""); + When(1, e => + { + var fsmEvent = e.FsmEvent; + if(Equals(fsmEvent, "go")) + return GoTo(2, "go"); + if(fsmEvent is StateTimeout) + return GoTo(2, "timeout"); + return null; + }); + When(2, e => { - StartWith(1, ""); - - When(1, e => - { - var fsmEvent = e.FsmEvent; - if(Equals(fsmEvent, "go")) - return GoTo(2).Using("go"); - if(fsmEvent is StateTimeout) - return GoTo(2).Using("timeout"); - return null; - }); - - When(2, e => - { - var fsmEvent = e.FsmEvent; - if(Equals(fsmEvent, "back")) - return GoTo(1).Using("back"); - return null; - }); - } + var fsmEvent = e.FsmEvent; + if(Equals(fsmEvent, "back")) + return GoTo(1, "back"); + return null; + }); } - private class TimerTestFsm : FSM + } + private class TimerTestFsm : FSM + { + public TimerTestFsm() { - public TimerTestFsm() - { - StartWith(1, null); - When(1, e => Stay()); - } + StartWith(1, null); + When(1, e => Stay()); } - } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs index 2d881e33..19b22962 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs @@ -1,72 +1,34 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; -using System.Threading.Tasks; -using FluentAssertions; +using Akka.Configuration; using Xunit; -using Xunit.Abstractions; using Xunit.Sdk; -using static FluentAssertions.FluentActions; -namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests -{ - public class AwaitAssertTests : HostingSpec - { - public AwaitAssertTests(ITestOutputHelper output) : base(nameof(AwaitAssertTests), output) - { - } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - builder.AddHocon("akka.test.timefactor=2", HoconAddMode.Prepend); - } +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; - [Fact] - public async Task AwaitAssertAsync_must_not_throw_any_exception_when_assertion_is_valid() - { - await AwaitAssertAsync(() => Assert.Equal("foo", "foo")); - } +public class AwaitAssertTests : TestKit +{ + protected override Config Config { get; } = "akka.test.timefactor=2"; - [Fact] - public async Task AwaitAssertAsync_with_async_delegate_must_not_throw_any_exception_when_assertion_is_valid() - { - await AwaitAssertAsync(() => - { - Assert.Equal("foo", "foo"); - return Task.CompletedTask; - }); - } + [Fact] + public void AwaitAssert_must_not_throw_any_exception_when_assertion_is_valid() + { + AwaitAssert(() => Assert.Equal("foo", "foo")); + } - [Fact] - public async Task AwaitAssertAsync_must_throw_exception_when_assertion_is_invalid() - { - await WithinAsync(TimeSpan.FromMilliseconds(300), TimeSpan.FromSeconds(1), async () => - { - await Awaiting(async () => - await AwaitAssertAsync(() => Assert.Equal("foo", "bar"), TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(300))) - .Should().ThrowAsync(); - }); - } - - [Fact] - public async Task AwaitAssertAsync_with_async_delegate_must_throw_exception_when_assertion_is_invalid() + [Fact] + public void AwaitAssert_must_throw_exception_when_assertion_is_invalid() + { + Within(TimeSpan.FromMilliseconds(300), TimeSpan.FromSeconds(1), () => { - await WithinAsync(TimeSpan.FromMilliseconds(300), TimeSpan.FromSeconds(1), async () => - { - await Awaiting(async () => await AwaitAssertAsync(() => - { - Assert.Equal("foo", "bar"); - return Task.CompletedTask; - }, TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(300))) - .Should().ThrowAsync(); - }); - } - + Assert.Throws(() => + AwaitAssert(() => Assert.Equal("foo", "bar"), TimeSpan.FromMilliseconds(500), TimeSpan.FromMilliseconds(300))); + }); } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs index f6912650..5c8a149e 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs @@ -1,91 +1,79 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; -using FluentAssertions; +using Akka.Configuration; using Xunit; -using Xunit.Abstractions; using Xunit.Sdk; +using FluentAssertions; using static FluentAssertions.FluentActions; -namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests -{ - public class DilatedTests : HostingSpec - { - private const int TimeFactor = 4; - private const int Timeout = 1000; - private const int ExpectedTimeout = Timeout * TimeFactor; - private const int Margin = 1000; // margin for GC - private const int DiffDelta = 100; - - public DilatedTests(ITestOutputHelper output) : base(nameof(DilatedTests), output) - { - } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - builder.AddHocon($"akka.test.timefactor={TimeFactor}", HoconAddMode.Prepend); - } +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; - [Fact] - public void Dilates_correctly_using_timeFactor() - { - Assert.Equal(Dilated(TimeSpan.FromMilliseconds(Timeout)), TimeSpan.FromMilliseconds(ExpectedTimeout)); - } +public class DilatedTests : TestKit +{ + private const int TimeFactor = 4; + private const int Timeout = 1000; + private const int ExpectedTimeout = Timeout * TimeFactor; + private const int Margin = 1000; // margin for GC + private const int DiffDelta = 100; - [Fact] - public async Task AwaitConditionAsync_should_dilate_timeout() - { - var stopwatch = Stopwatch.StartNew(); - await Awaiting(() => AwaitConditionAsync(() => Task.FromResult(false), TimeSpan.FromMilliseconds(Timeout))) - .Should().ThrowAsync(); - stopwatch.Stop(); - AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); - } + protected override Config Config { get; } = $"akka.test.timefactor={TimeFactor}"; - [Fact] - public async Task ReceiveNAsync_should_dilate_timeout() - { - var stopwatch = Stopwatch.StartNew(); - await Awaiting(async () => await ReceiveNAsync(42, TimeSpan.FromMilliseconds(Timeout)).ToListAsync()) - .Should().ThrowAsync(); - stopwatch.Stop(); - AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); - } + [Fact] + public void Dilates_correctly_using_timeFactor() + { + Assert.Equal(Dilated(TimeSpan.FromMilliseconds(Timeout)), TimeSpan.FromMilliseconds(ExpectedTimeout)); + } - [Fact] - public async Task ExpectMsgAllOfAsync_should_dilate_timeout() - { - var stopwatch = Stopwatch.StartNew(); - await Awaiting(async () => await ExpectMsgAllOfAsync(TimeSpan.FromMilliseconds(Timeout), new []{ "1", "2" }).ToListAsync()) - .Should().ThrowAsync(); - stopwatch.Stop(); - AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); - } + [Fact] + public void AwaitCondition_should_dilate_timeout() + { + var stopwatch = Stopwatch.StartNew(); + Invoking(() => AwaitCondition(() => false, TimeSpan.FromMilliseconds(Timeout))) + .Should().Throw(); + stopwatch.Stop(); + AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); + } - [Fact] - public async Task FishForMessageAsync_should_dilate_timeout() - { - var stopwatch = Stopwatch.StartNew(); - await Awaiting(async () => await FishForMessageAsync(_=>false, TimeSpan.FromMilliseconds(Timeout))) - .Should().ThrowAsync(); - stopwatch.Stop(); - AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); - } + [Fact] + public void ReceiveN_should_dilate_timeout() + { + var stopwatch = Stopwatch.StartNew(); + Invoking(() => ReceiveN(42, TimeSpan.FromMilliseconds(Timeout))) + .Should().Throw(); + stopwatch.Stop(); + AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); + } - private static void AssertDilated(double diff, string message = null) - { - Assert.True(diff >= ExpectedTimeout - DiffDelta, message); - Assert.True(diff < ExpectedTimeout + Margin, message); // margin for GC - } + [Fact] + public void ExpectMsgAllOf_should_dilate_timeout() + { + var stopwatch = Stopwatch.StartNew(); + Invoking(() => ExpectMsgAllOf(TimeSpan.FromMilliseconds(Timeout), "1", "2")) + .Should().Throw(); + stopwatch.Stop(); + AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); + } + [Fact] + public void FishForMessage_should_dilate_timeout() + { + var stopwatch = Stopwatch.StartNew(); + Invoking(() => FishForMessage(_=>false, TimeSpan.FromMilliseconds(Timeout))) + .Should().Throw(); + stopwatch.Stop(); + AssertDilated(stopwatch.ElapsedMilliseconds, $"Expected the timeout to be {ExpectedTimeout} but in fact it was {stopwatch.ElapsedMilliseconds}."); } -} + private static void AssertDilated(double diff, string message = null) + { + Assert.True(diff >= ExpectedTimeout - DiffDelta, message); + Assert.True(diff < ExpectedTimeout + Margin, message); // margin for GC + } +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs index 04e685dd..454fdce9 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs @@ -1,76 +1,56 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; -using System.Linq; -using System.Threading.Tasks; +using Akka.Actor; using FluentAssertions; using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; -using Akka.Actor; using static FluentAssertions.FluentActions; -namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; + +public class ExpectTests : TestKit { - public class ExpectTests : HostingSpec + [Fact] + public void ExpectMsgAllOf_should_receive_correct_messages() { - public ExpectTests(ITestOutputHelper output) : base(nameof(ExpectTests), output) - { - } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - } - - [Fact] - public async Task ExpectMsgAllOfAsync_should_receive_correct_messages() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - TestActor.Tell("3"); - TestActor.Tell("4"); - var messages = await ExpectMsgAllOfAsync(new[] { "3", "1", "4", "2" }).ToListAsync(); - messages.Should().BeEquivalentTo(new[] { "1", "2", "3", "4" }, opt => opt.WithStrictOrdering()); - } - - [Fact] - public async Task ExpectMsgAllOfAsync_should_fail_when_receiving_unexpected() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - TestActor.Tell("Totally unexpected"); - TestActor.Tell("3"); - await Awaiting(async () => - { - await ExpectMsgAllOfAsync(new[] { "3", "1", "2" }).ToListAsync(); - }).Should().ThrowAsync().WithMessage("not found [*"); - } + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("3"); + TestActor.Tell("4"); + ExpectMsgAllOf("3", "1", "4", "2").Should() + .BeEquivalentTo(new[] { "1", "2", "3", "4" }, opt => opt.WithStrictOrdering()); + } - [Fact] - public async Task ExpectMsgAllOfAsync_should_timeout_when_not_receiving_any_messages() - { - await Awaiting(async () => - { - await ExpectMsgAllOfAsync(TimeSpan.FromMilliseconds(100), new[] { "3", "1", "2" }).ToListAsync(); - }).Should().ThrowAsync().WithMessage("Timeout (*"); - } + [Fact] + public void ExpectMsgAllOf_should_fail_when_receiving_unexpected() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("Totally unexpected"); + TestActor.Tell("3"); + Invoking(() => ExpectMsgAllOf("3", "1", "2")) + .Should().Throw(); + } - [Fact] - public async Task ExpectMsgAllOfAsync_should_timeout_if_to_few_messages() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - await Awaiting(async () => - { - await ExpectMsgAllOfAsync(TimeSpan.FromMilliseconds(100), new[] { "3", "1", "2" }).ToListAsync(); - }).Should().ThrowAsync().WithMessage("Timeout (*"); - } + [Fact] + public void ExpectMsgAllOf_should_timeout_when_not_receiving_any_messages() + { + Invoking(() => ExpectMsgAllOf(TimeSpan.FromMilliseconds(100), "3", "1", "2")) + .Should().Throw(); + } + [Fact] + public void ExpectMsgAllOf_should_timeout_if_to_few_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + Invoking(() => ExpectMsgAllOf(TimeSpan.FromMilliseconds(100), "3", "1", "2")) + .Should().Throw(); } -} +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs index f451f5c9..8d42bd0a 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs @@ -1,76 +1,62 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; -using System.Linq; -using System.Threading.Tasks; -using FluentAssertions; using Akka.Actor; +using FluentAssertions; using Xunit; -using Xunit.Abstractions; -namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; + +public class IgnoreMessagesTests : TestKit { - public class IgnoreMessagesTests : HostingSpec + public class IgnoredMessage { - private class IgnoredMessage + public IgnoredMessage(string ignoreMe = null) { - public IgnoredMessage(string ignoreMe = null) - { - IgnoreMe = ignoreMe; - } - - public string IgnoreMe { get; } + IgnoreMe = ignoreMe; } - public IgnoreMessagesTests(ITestOutputHelper output) : base(nameof(IgnoreMessagesTests), output) - { - } + public string IgnoreMe { get; } + } - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - } - - [Fact] - public async Task IgnoreMessages_should_ignore_messages() - { - IgnoreMessages(o => o is int i && i == 1); - TestActor.Tell(1); - TestActor.Tell("1"); - (await ReceiveOneAsync()).Should().Be("1"); - HasMessages.Should().BeFalse(); - } + [Fact] + public void IgnoreMessages_should_ignore_messages() + { + IgnoreMessages(o => o is int && (int)o == 1); + TestActor.Tell(1); + TestActor.Tell("1"); + string.Equals((string)ReceiveOne(), "1").Should().BeTrue(); + HasMessages.Should().BeFalse(); + } - [Fact] - public async Task IgnoreMessages_should_ignore_messages_T() - { - IgnoreMessages(); + [Fact] + public void IgnoreMessages_should_ignore_messages_T() + { + IgnoreMessages(); - TestActor.Tell("1"); - TestActor.Tell(new IgnoredMessage(), TestActor); - TestActor.Tell("2"); - var messages = await ReceiveNAsync(2).ToListAsync(); - messages.Should().BeEquivalentTo(new[] { "1", "2" }, opt => opt.WithStrictOrdering()); - HasMessages.Should().BeFalse(); - } + TestActor.Tell("1"); + TestActor.Tell(new IgnoredMessage(), TestActor); + TestActor.Tell("2"); + ReceiveN(2).Should().BeEquivalentTo(new[] { "1", "2" }, opt => opt.WithStrictOrdering()); + HasMessages.Should().BeFalse(); + } - [Fact] - public async Task IgnoreMessages_should_ignore_messages_T_with_Func() - { - IgnoreMessages(m => string.IsNullOrWhiteSpace(m.IgnoreMe)); + [Fact] + public void IgnoreMessages_should_ignore_messages_T_with_Func() + { + IgnoreMessages(m => String.IsNullOrWhiteSpace(m.IgnoreMe)); - var msg = new IgnoredMessage("not ignored!"); + var msg = new IgnoredMessage("not ignored!"); - TestActor.Tell("1"); - TestActor.Tell(msg, TestActor); - TestActor.Tell("2"); - var messages = await ReceiveNAsync(3).ToListAsync(); - messages.Should().BeEquivalentTo(new object[] { "1", msg, "2" }, opt => opt.WithStrictOrdering()); - HasMessages.Should().BeFalse(); - } + TestActor.Tell("1"); + TestActor.Tell(msg, TestActor); + TestActor.Tell("2"); + ReceiveN(3).Should().BeEquivalentTo(new object[] { "1", msg, "2" }, opt => opt.WithStrictOrdering()); + HasMessages.Should().BeFalse(); } -} +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs index c00b02a2..f05cc2cb 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs @@ -1,279 +1,278 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; using System.Collections; -using System.Linq; using System.Threading.Tasks; using Akka.Actor; using FluentAssertions; -using Microsoft.Extensions.Logging; using Xunit; -using Xunit.Abstractions; using Xunit.Sdk; using static FluentAssertions.FluentActions; -namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; + +public class ReceiveTests : TestKit { - public class ReceiveTests : HostingSpec + [Fact] + public void ReceiveN_should_receive_correct_number_of_messages() { - public ReceiveTests(ITestOutputHelper output) : base(nameof(ReceiveTests), output) - { - } + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("3"); + TestActor.Tell("4"); + ReceiveN(3).Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); + ReceiveN(1).Should().BeEquivalentTo(new[] { "4" }); + } - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - } - - [Fact] - public async Task ReceiveNAsync_should_receive_correct_number_of_messages() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - TestActor.Tell("3"); - TestActor.Tell("4"); - var messages = await ReceiveNAsync(3).ToListAsync(); - messages.Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); - messages = await ReceiveNAsync(1).ToListAsync(); - messages.Should().BeEquivalentTo(new[] { "4" }); - } + [Fact] + public void ReceiveN_should_timeout_if_no_messages() + { + Invoking(() => ReceiveN(3, TimeSpan.FromMilliseconds(10))) + .Should().Throw(); + } - [Fact] - public async Task ReceiveNAsync_should_timeout_if_no_messages() - { - await Awaiting(async () => await ReceiveNAsync(3, TimeSpan.FromMilliseconds(10)).ToListAsync()) - .Should().ThrowAsync(); - } + [Fact] + public void ReceiveN_should_timeout_if_to_few_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + Invoking(() => ReceiveN(3, TimeSpan.FromMilliseconds(100))) + .Should().Throw(); + } - [Fact] - public async Task ReceiveNAsync_should_timeout_if_to_few_messages() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - await Awaiting(async () => await ReceiveNAsync(3, TimeSpan.FromMilliseconds(100)).ToListAsync()) - .Should().ThrowAsync(); - } - [Fact] - public async Task FishForMessageAsync_should_return_matched_message() - { - TestActor.Tell(1); - TestActor.Tell(2); - TestActor.Tell(10); - TestActor.Tell(20); - (await FishForMessageAsync(i => i >= 10)).Should().Be(10); - } + [Fact] + public void FishForMessage_should_return_matched_message() + { + TestActor.Tell(1); + TestActor.Tell(2); + TestActor.Tell(10); + TestActor.Tell(20); + FishForMessage(i => i >= 10).Should().Be(10); + } - [Fact] - public async Task FishForMessageAsync_should_timeout_if_no_messages() - { - await Awaiting(async () => await FishForMessageAsync(_ => false, TimeSpan.FromMilliseconds(10))) - .Should().ThrowAsync(); - } + [Fact] + public void FishForMessage_should_timeout_if_no_messages() + { + Invoking(() => FishForMessage(_ => false, TimeSpan.FromMilliseconds(10))) + .Should().Throw(); + } - [Fact] - public async Task FishForMessageAsync_should_timeout_if_too_few_messages() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - await Awaiting(async () => await FishForMessageAsync(_ => false, TimeSpan.FromMilliseconds(100))) - .Should().ThrowAsync(); - } + [Fact] + public void FishForMessage_should_timeout_if_to_few_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + Invoking(() => FishForMessage(_ => false, TimeSpan.FromMilliseconds(100))) + .Should().Throw(); + } - [Fact] - public async Task FishForMessageAsync_should_fill_the_all_messages_param_if_not_null() + [Fact] + public async Task FishForMessage_should_fill_the_all_messages_param_if_not_null() + { + await Task.Run(delegate { - var probe = CreateTestProbe("probe"); + var probe = base.CreateTestProbe("probe"); probe.Tell("1"); probe.Tell(2); probe.Tell("3"); probe.Tell(4); var allMessages = new ArrayList(); - await probe.FishForMessageAsync(isMessage: s => s == "3", allMessages: allMessages); + probe.FishForMessage(isMessage: s => s == "3", allMessages: allMessages); allMessages.Should().BeEquivalentTo(new ArrayList { "1", 2 }); - } + }); + } - [Fact] - public async Task FishForMessageAsync_should_clear_the_all_messages_param_if_not_null_before_filling_it() + [Fact] + public async Task FishForMessage_should_clear_the_all_messages_param_if_not_null_before_filling_it() + { + await Task.Run(delegate { - var probe = CreateTestProbe("probe"); + var probe = base.CreateTestProbe("probe"); probe.Tell("1"); probe.Tell(2); probe.Tell("3"); probe.Tell(4); - var allMessages = new ArrayList { "pre filled data" }; - await probe.FishForMessageAsync(isMessage: x => x == "3", allMessages: allMessages); + var allMessages = new ArrayList() { "pre filled data" }; + probe.FishForMessage(isMessage: x => x == "3", allMessages: allMessages); allMessages.Should().BeEquivalentTo(new ArrayList { "1", 2 }); - } + }); + } - [Fact] - public async Task FishUntilMessageAsync_should_succeed_with_good_input() - { - var probe = CreateTestProbe("probe"); - probe.Ref.Tell(1d, TestActor); - await Awaiting(() => probe.FishUntilMessageAsync(max: TimeSpan.FromMilliseconds(10))) - .Should().NotThrowAsync(); - } + [Fact] + public async Task FishUntilMessageAsync_should_succeed_with_good_input() + { + var probe = CreateTestProbe("probe"); + probe.Ref.Tell(1d, TestActor); + await probe.FishUntilMessageAsync(max: TimeSpan.FromMilliseconds(10)); + } - [Fact] - public async Task FishUntilMessageAsync_should_fail_with_bad_input() - { - var probe = CreateTestProbe("probe"); - probe.Ref.Tell(3, TestActor); - await Awaiting(() => probe.FishUntilMessageAsync(max: TimeSpan.FromMilliseconds(10))) - .Should().ThrowAsync(); - } - [Fact] - public async Task WaitForRadioSilenceAsync_should_succeed_immediately_with_null_good_input() - { - var probe = CreateTestProbe("probe"); - var messages = await probe.WaitForRadioSilenceAsync(max: TimeSpan.FromMilliseconds(0)); - messages.Should().BeEquivalentTo(new ArrayList()); - } + [Fact] + public async Task FishUntilMessageAsync_should_fail_with_bad_input() + { + var probe = CreateTestProbe("probe"); + probe.Ref.Tell(3, TestActor); + Func func = () => probe.FishUntilMessageAsync(max: TimeSpan.FromMilliseconds(10)); + await func.Should().ThrowAsync(); + } - [Fact] - public async Task WaitForRadioSilenceAsync_should_succeed_immediately_with_good_pre_input() - { - var probe = CreateTestProbe("probe"); - probe.Ref.Tell(1, TestActor); - var messages = await probe.WaitForRadioSilenceAsync(max: TimeSpan.FromMilliseconds(0)); - messages.Should().BeEquivalentTo(new ArrayList { 1 }); - } + [Fact] + public async Task WaitForRadioSilenceAsync_should_succeed_immediately_with_null_good_input() + { + var probe = CreateTestProbe("probe"); + var messages = await probe.WaitForRadioSilenceAsync(max: TimeSpan.FromMilliseconds(0)); + messages.Should().BeEquivalentTo(new ArrayList()); + } - [Fact] - public async Task WaitForRadioSilenceAsync_should_succeed_later_with_good_post_input() - { - var probe = CreateTestProbe("probe"); - var task = probe.WaitForRadioSilenceAsync(); - probe.Ref.Tell(1, TestActor); - var messages = await task; - messages.Should().BeEquivalentTo(new ArrayList { 1 }); - } + [Fact] + public async Task WaitForRadioSilenceAsync_should_succeed_immediately_with_good_pre_input() + { + var probe = CreateTestProbe("probe"); + probe.Ref.Tell(1, TestActor); + var messages = await probe.WaitForRadioSilenceAsync(max: TimeSpan.FromMilliseconds(0)); + messages.Should().BeEquivalentTo(new ArrayList { 1 }); + } - [Fact] - public async Task WaitForRadioSilenceAsync_should_reset_timer_twice_only() - { - var probe = CreateTestProbe("probe"); - var max = TimeSpan.FromMilliseconds(3000); - var halfMax = TimeSpan.FromMilliseconds(max.TotalMilliseconds / 2); - var doubleMax = TimeSpan.FromMilliseconds(max.TotalMilliseconds * 2); - var task = probe.WaitForRadioSilenceAsync(max: max, maxMessages: 2); - await Task.Delay(halfMax); - probe.Ref.Tell(1, TestActor); - await Task.Delay(halfMax); - probe.Ref.Tell(2, TestActor); - await Task.Delay(doubleMax); - probe.Ref.Tell(3, TestActor); - var messages = await task; - messages.Should().BeEquivalentTo(new ArrayList { 1, 2 }); - } + [Fact] + public async Task WaitForRadioSilenceAsync_should_succeed_later_with_good_post_input() + { + var probe = CreateTestProbe("probe"); + var task = probe.WaitForRadioSilenceAsync(); + probe.Ref.Tell(1, TestActor); + var messages = await task; + messages.Should().BeEquivalentTo(new ArrayList { 1 }); + } - [Fact] - public async Task WaitForRadioSilenceAsync_should_fail_immediately_with_bad_input() - { - var probe = CreateTestProbe("probe"); - probe.Ref.Tell(3, TestActor); - await Awaiting(() => probe.WaitForRadioSilenceAsync(max: TimeSpan.FromMilliseconds(0), maxMessages: 0)) - .Should().ThrowAsync().WithMessage("maxMessages violated*"); - } + [Fact] + public async Task WaitForRadioSilenceAsync_should_reset_timer_twice_only() + { + var probe = CreateTestProbe("probe"); + var max = TimeSpan.FromMilliseconds(3000); + var halfMax = TimeSpan.FromMilliseconds(max.TotalMilliseconds / 2); + var doubleMax = TimeSpan.FromMilliseconds(max.TotalMilliseconds * 2); + var task = probe.WaitForRadioSilenceAsync(max: max, maxMessages: 2); + await Task.Delay(halfMax); + probe.Ref.Tell(1, TestActor); + await Task.Delay(halfMax); + probe.Ref.Tell(2, TestActor); + await Task.Delay(doubleMax); + probe.Ref.Tell(3, TestActor); + var messages = await task; + messages.Should().BeEquivalentTo(new ArrayList { 1, 2 }); + } - [Fact] - public async Task ReceiveWhileAsync_Filter_should_on_a_timeout_return_no_messages() + [Fact] + public async Task WaitForRadioSilenceAsync_should_fail_immediately_with_bad_input() + { + var probe = CreateTestProbe("probe"); + probe.Ref.Tell(3, TestActor); + try { - await using var e = ReceiveWhileAsync(_ => _, TimeSpan.FromMilliseconds(10)).GetAsyncEnumerator(); - (await e.MoveNextAsync()).Should().BeFalse(); + await probe.WaitForRadioSilenceAsync(max: TimeSpan.FromMilliseconds(0), maxMessages: 0); + Assert.True(false, "we should never get here"); } + catch (XunitException) { } + } - [Fact] - public async Task ReceiveWhileAsync_Filter_should_break_on_function_returning_null_and_return_correct_messages() - { - TestActor.Tell("1"); - TestActor.Tell(2); - TestActor.Tell("3"); - TestActor.Tell(99999.0); - TestActor.Tell(4); - var messages = await ReceiveWhileAsync(_ => _ is double ? null : _.ToString()).ToListAsync(); - messages.Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); - } + [Fact] + public void ReceiveWhile_Filter_should_on_a_timeout_return_no_messages() + { + ReceiveWhile(_ => _, TimeSpan.FromMilliseconds(10)).Count.Should().Be(0); + } - [Fact] - public async Task ReceiveWhileAsync_Filter_should_not_consume_last_message_that_didnt_match() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - TestActor.Tell(4711); - await ReceiveWhileAsync(_ => _ is string ? _ : null).ToListAsync(); - await ExpectMsgAsync(4711); - } + [Fact] + public void ReceiveWhile_Filter_should_break_on_function_returning_null_and_return_correct_messages() + { + TestActor.Tell("1"); + TestActor.Tell(2); + TestActor.Tell("3"); + TestActor.Tell(99999.0); + TestActor.Tell(4); + ReceiveWhile(_ => _ is double ? null : _.ToString()) + .Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); + } - [Fact] - public async Task ReceiveWhileAsync_Predicate_should_on_a_timeout_return_no_messages() - { - await using var e = ReceiveWhileAsync(_ => false, TimeSpan.FromMilliseconds(10)).GetAsyncEnumerator(); - (await e.MoveNextAsync()).Should().BeFalse(); - } + [Fact] + public void ReceiveWhile_Filter_should_not_consume_last_message_that_didnt_match() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell(4711); + ReceiveWhile(_ => _ is string ? _ : null); + ExpectMsg(4711); + } - [Fact] - public async Task ReceiveWhileAsync_Predicate_should_break_when_predicate_returns_false_and_return_correct_messages() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - TestActor.Tell("3"); - TestActor.Tell("-----------"); - TestActor.Tell("4"); - var messages = await ReceiveWhileAsync(s => s.Length == 1).ToListAsync(); - messages.Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); - } + [Fact] + public void ReceiveWhile_Predicate_should_on_a_timeout_return_no_messages() + { + ReceiveWhile(_ => false, TimeSpan.FromMilliseconds(10)).Count.Should().Be(0); + } - [Fact] - public async Task ReceiveWhileAsync_Predicate_should_break_when_type_is_wrong_and_we_dont_ignore_those_and_return_correct_messages() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - TestActor.Tell("3"); - TestActor.Tell(4); - TestActor.Tell("5"); - var messages = await ReceiveWhileAsync(s => s.Length == 1, shouldIgnoreOtherMessageTypes: false).ToListAsync(); - messages.Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); - } + [Fact] + public void ReceiveWhile_Predicate_should_break_when_predicate_returns_false_and_return_correct_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("3"); + TestActor.Tell("-----------"); + TestActor.Tell("4"); + ReceiveWhile(s => s.Length == 1) + .Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); + } - [Fact] - public async Task ReceiveWhileAsync_Predicate_should_continue_when_type_is_other_but_we_ignore_other_types_and_return_correct_messages() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - TestActor.Tell("3"); - TestActor.Tell(4); - TestActor.Tell("5"); - var messages = await ReceiveWhileAsync(s => s.Length == 1, shouldIgnoreOtherMessageTypes: true).ToListAsync(); - messages.Should().BeEquivalentTo(new[] { "1", "2", "3", "5" }, opt => opt.WithStrictOrdering()); - } + [Fact] + public void + ReceiveWhile_Predicate_should_break_when_type_is_wrong_and_we_dont_ignore_those_and_return_correct_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("3"); + TestActor.Tell(4); + TestActor.Tell("5"); + ReceiveWhile(s => s.Length == 1, shouldIgnoreOtherMessageTypes: false) + .Should().BeEquivalentTo(new[] { "1", "2", "3" }, opt => opt.WithStrictOrdering()); + } - [Fact] - public async Task ReceiveWhileAsync_Predicate_should_not_consume_last_message_that_didnt_match() - { - TestActor.Tell("1"); - TestActor.Tell("2"); - TestActor.Tell(4711); - TestActor.Tell("3"); - TestActor.Tell("4"); - TestActor.Tell("5"); - TestActor.Tell(6); - TestActor.Tell("7"); - TestActor.Tell("8"); + [Fact] + public void + ReceiveWhile_Predicate_should_continue_when_type_is_other_but_we_ignore_other_types_and_return_correct_messages() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell("3"); + TestActor.Tell(4); + TestActor.Tell("5"); + ReceiveWhile(s => s.Length == 1, shouldIgnoreOtherMessageTypes: true) + .Should().BeEquivalentTo(new[] { "1", "2", "3", "5" }, opt => opt.WithStrictOrdering()); + } - var messages = await ReceiveWhileAsync(_ => _ is string).ToListAsync(); - messages.Should().BeEquivalentTo(new[] { "1", "2" }, opt => opt.WithStrictOrdering()); - await ExpectMsgAsync(4711); + [Fact] + public void ReceiveWhile_Predicate_should_not_consume_last_message_that_didnt_match() + { + TestActor.Tell("1"); + TestActor.Tell("2"); + TestActor.Tell(4711); + TestActor.Tell("3"); + TestActor.Tell("4"); + TestActor.Tell("5"); + TestActor.Tell(6); + TestActor.Tell("7"); + TestActor.Tell("8"); - messages = await ReceiveWhileAsync(_ => _ is string).ToListAsync(); - messages.Should().BeEquivalentTo(new[] { "3", "4", "5" }, opt => opt.WithStrictOrdering()); - await ExpectMsgAsync(6); - } + var received = ReceiveWhile(_ => _ is string); + received.Should().BeEquivalentTo(new[] { "1", "2" }, opt => opt.WithStrictOrdering()); + + ExpectMsg(4711); + + received = ReceiveWhile(_ => _ is string); + received.Should().BeEquivalentTo(new[] { "3", "4", "5" }, opt => opt.WithStrictOrdering()); + + ExpectMsg(6); } -} +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs index 04ca971b..20fbbd9e 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs @@ -1,22 +1,20 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; using Xunit; -namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; + +public class RemainingTests : TestKit { - public class RemainingTests : Akka.TestKit.Xunit2.TestKit + [Fact] + public void Throw_if_remaining_is_called_outside_Within() { - [Fact] - public void Throw_if_remaining_is_called_outside_Within() - { - Assert.Throws(() => Remaining); - } + Assert.Throws(() => Remaining); } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs index 92b132c3..a96f635f 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs @@ -1,75 +1,20 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; -using System.Threading.Tasks; -using FluentAssertions; -using FluentAssertions.Extensions; using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; -using static FluentAssertions.FluentActions; -namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests +namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; + +public class WithinTests : TestKit { - public class WithinTests : HostingSpec + [Fact] + public void Within_should_increase_max_timeout_by_the_provided_epsilon_value() { - public WithinTests(ITestOutputHelper output) : base(nameof(WithinTests), output) - { - } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - } - - [Fact] - public void Within_should_increase_max_timeout_by_the_provided_epsilon_value() - { - Within(TimeSpan.FromSeconds(1), () => ExpectNoMsg(), TimeSpan.FromMilliseconds(50)); - } - - [Fact] - public void Within_should_respect_minimum_time() - { - Within(0.3.Seconds(), 1.Seconds(), () => ExpectNoMsg(0.4.Seconds()), "", 0.1.Seconds()); - } - - [Fact] - public async Task WithinAsync_should_respect_minimum_time() - { - await WithinAsync( - 0.3.Seconds(), - 1.Seconds(), - async () => await ExpectNoMsgAsync(0.4.Seconds()), - "", - 0.1.Seconds()); - } - - [Fact] - public void Within_should_throw_if_execution_is_shorter_than_minimum_time() - { - Invoking(() => - { - Within(0.5.Seconds(), 1.Seconds(), () => ExpectNoMsg(0.1.Seconds()), null, 0.1.Seconds()); - }).Should().Throw(); - } - - [Fact] - public async Task WithinAsync_should_throw_if_execution_is_shorter_than_minimum_time() - { - await Awaiting(async () => - { - await WithinAsync( - 0.5.Seconds(), - 1.Seconds(), - async () => await ExpectNoMsgAsync(0.1.Seconds()), - null, - 0.1.Seconds()); - }).Should().ThrowAsync(); - } + Within(TimeSpan.FromSeconds(1), () => ExpectNoMsg(), TimeSpan.FromMilliseconds(50)); } -} +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs b/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs index 49783f75..80dc8b01 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs @@ -1,7 +1,7 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- @@ -9,35 +9,24 @@ using System.Reflection; using Akka.TestKit; using FluentAssertions; -using Microsoft.Extensions.Logging; using Xunit; -using Xunit.Abstractions; -namespace Akka.Hosting.TestKit.Tests +namespace Akka.Hosting.TestKit.Tests; + +// ReSharper disable once InconsistentNaming +public class TestKit_Config_Tests : TestKit { - // ReSharper disable once InconsistentNaming - public class TestKit_Config_Tests : HostingSpec + [Fact] + public void DefaultValues_should_be_correct() { - public TestKit_Config_Tests(ITestOutputHelper output) : base("TestKitConfigTests", output) - { - } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { } - - [Fact] - public void DefaultValues_should_be_correct() - { - TestKitSettings.DefaultTimeout.Should().Be(TimeSpan.FromSeconds(5)); - TestKitSettings.SingleExpectDefault.Should().Be(TimeSpan.FromSeconds(3)); - TestKitSettings.TestEventFilterLeeway.Should().Be(TimeSpan.FromSeconds(3)); - TestKitSettings.TestTimeFactor.Should().Be(1); - var callingThreadDispatcherTypeName = typeof(CallingThreadDispatcherConfigurator).FullName + ", " + typeof(CallingThreadDispatcher).GetTypeInfo().Assembly.GetName().Name; - Assert.False(Sys.Settings.Config.IsEmpty); - Sys.Settings.Config.GetString("akka.test.calling-thread-dispatcher.type", null).Should().Be(callingThreadDispatcherTypeName); - Sys.Settings.Config.GetString("akka.test.test-actor.dispatcher.type", null).Should().Be(callingThreadDispatcherTypeName); - CallingThreadDispatcher.Id.Should().Be("akka.test.calling-thread-dispatcher"); - } + TestKitSettings.DefaultTimeout.Should().Be(TimeSpan.FromSeconds(5)); + TestKitSettings.SingleExpectDefault.Should().Be(TimeSpan.FromSeconds(3)); + TestKitSettings.TestEventFilterLeeway.Should().Be(TimeSpan.FromSeconds(3)); + TestKitSettings.TestTimeFactor.Should().Be(1); + var callingThreadDispatcherTypeName = typeof(CallingThreadDispatcherConfigurator).FullName + ", " + typeof(CallingThreadDispatcher).GetTypeInfo().Assembly.GetName().Name; + Assert.False(Sys.Settings.Config.IsEmpty); + Sys.Settings.Config.GetString("akka.test.calling-thread-dispatcher.type", null).Should().Be(callingThreadDispatcherTypeName); + Sys.Settings.Config.GetString("akka.test.test-actor.dispatcher.type", null).Should().Be(callingThreadDispatcherTypeName); + CallingThreadDispatcher.Id.Should().Be("akka.test.calling-thread-dispatcher"); } -} - +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs b/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs index 8692406f..632c4408 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs @@ -1,205 +1,198 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation +// Copyright (C) 2009-2021 Lightbend Inc. +// Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- using System; using System.Threading.Tasks; using Akka.Actor; +using Akka.Configuration; using Akka.TestKit; using Akka.TestKit.Configs; +using FluentAssertions; using Xunit; -using Xunit.Abstractions; -namespace Akka.Hosting.TestKit.Tests +namespace Akka.Hosting.TestKit.Tests; + +public class TestSchedulerTests : TestKit { - public class TestSchedulerTests : HostingSpec + private IActorRef _testReceiveActor; + + protected override async Task BeforeTestStart() { - private enum TestActorKey - { } - - private IActorRef _testReceiveActor; + await base.BeforeTestStart(); + _testReceiveActor = Sys.ActorOf(Props.Create(() => new TestReceiveActor()) + .WithDispatcher(CallingThreadDispatcher.Id)); + } - public TestSchedulerTests(ITestOutputHelper output) : base(nameof(TestSchedulerTests), output) - { - } - - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - builder.AddHocon(TestConfigs.TestSchedulerConfig); - builder.WithActors((system, registry) => - { - var actor = system.ActorOf(Props.Create(() => new TestReceiveActor()) - .WithDispatcher(CallingThreadDispatcher.Id)); - registry.Register(actor); - }); - } + protected override Config Config { get; } = TestConfigs.TestSchedulerConfig; - protected override async Task BeforeTestStart() - { - await base.BeforeTestStart(); - _testReceiveActor = ActorRegistry.Get(); - } + [Fact] + public void Delivers_message_when_scheduled_time_reached() + { + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1))); + _testReceiveActor.Ask(new Identify(null), RemainingOrDefault) + .Wait(RemainingOrDefault).Should().BeTrue(); // verify that the ActorCell has started - [Fact] - public async Task Delivers_message_when_scheduled_time_reached() - { - _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1))); - await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started + Scheduler.Advance(TimeSpan.FromSeconds(1)); + ExpectMsg(); + } - Scheduler.Advance(TimeSpan.FromSeconds(1)); - await ExpectMsgAsync(); - } + [Fact] + public void Does_not_deliver_message_prematurely() + { + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1))); + _testReceiveActor.Ask(new Identify(null), RemainingOrDefault) + .Wait(RemainingOrDefault).Should().BeTrue(); // verify that the ActorCell has started - [Fact] - public async Task Does_not_deliver_message_prematurely() - { - _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1))); - await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started + Scheduler.Advance(TimeSpan.FromMilliseconds(999)); + ExpectNoMsg(TimeSpan.FromMilliseconds(20)); + } - Scheduler.Advance(TimeSpan.FromMilliseconds(999)); - await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(20)); - } + [Fact] + public void Delivers_messages_scheduled_for_same_time_in_order_they_were_added() + { + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1), 1)); + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1), 2)); + _testReceiveActor.Ask(new Identify(null), RemainingOrDefault) + .Wait(RemainingOrDefault).Should().BeTrue(); // verify that the ActorCell has started + + Scheduler.Advance(TimeSpan.FromSeconds(1)); + var firstId = ExpectMsg().Id; + var secondId = ExpectMsg().Id; + Assert.Equal(1, firstId); + Assert.Equal(2, secondId); + } - [Fact] - public async Task Delivers_messages_scheduled_for_same_time_in_order_they_were_added() - { - _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1), 1)); - _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1), 2)); - await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started - - Scheduler.Advance(TimeSpan.FromSeconds(1)); - var firstId = (await ExpectMsgAsync()).Id; - var secondId = (await ExpectMsgAsync()).Id; - Assert.Equal(1, firstId); - Assert.Equal(2, secondId); - } + [Fact] + public void Keeps_delivering_rescheduled_message() + { + _testReceiveActor.Tell(new RescheduleMessage(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5))); + _testReceiveActor.Ask(new Identify(null), RemainingOrDefault) + .Wait(RemainingOrDefault).Should().BeTrue(); // verify that the ActorCell has started - [Fact] - public async Task Keeps_delivering_rescheduled_message() + for (int i = 0; i < 500; i ++) { - _testReceiveActor.Tell(new RescheduleMessage(TimeSpan.FromSeconds(5), TimeSpan.FromSeconds(5))); - await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started - - for (int i = 0; i < 500; i ++) - { - Scheduler.Advance(TimeSpan.FromSeconds(5)); - await ExpectMsgAsync(); - } + Scheduler.Advance(TimeSpan.FromSeconds(5)); + ExpectMsg(); } + } - [Fact] - public async Task Uses_initial_delay_to_schedule_first_rescheduled_message() - { - _testReceiveActor.Tell(new RescheduleMessage(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5))); - await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started + [Fact] + public void Uses_initial_delay_to_schedule_first_rescheduled_message() + { + _testReceiveActor.Tell(new RescheduleMessage(TimeSpan.FromSeconds(1), TimeSpan.FromSeconds(5))); + _testReceiveActor.Ask(new Identify(null), RemainingOrDefault) + .Wait(RemainingOrDefault).Should().BeTrue(); // verify that the ActorCell has started - Scheduler.Advance(TimeSpan.FromSeconds(1)); - await ExpectMsgAsync(); - } + Scheduler.Advance(TimeSpan.FromSeconds(1)); + ExpectMsg(); + } - [Fact] - public async Task Doesnt_reschedule_cancelled() - { - _testReceiveActor.Tell(new CancelableMessage(TimeSpan.FromSeconds(1))); - await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started - - Scheduler.Advance(TimeSpan.FromSeconds(1)); - await ExpectMsgAsync(); - _testReceiveActor.Tell(new CancelMessage()); - Scheduler.Advance(TimeSpan.FromSeconds(1)); - await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(20)); - } + [Fact] + public void Doesnt_reschedule_cancelled() + { + _testReceiveActor.Tell(new CancelableMessage(TimeSpan.FromSeconds(1))); + _testReceiveActor.Ask(new Identify(null), RemainingOrDefault) + .Wait(RemainingOrDefault).Should().BeTrue(); // verify that the ActorCell has started + + Scheduler.Advance(TimeSpan.FromSeconds(1)); + ExpectMsg(); + _testReceiveActor.Tell(new CancelMessage()); + Scheduler.Advance(TimeSpan.FromSeconds(1)); + ExpectNoMsg(TimeSpan.FromMilliseconds(20)); + } - [Fact] - public async Task Advance_to_takes_us_to_correct_time() - { - _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1), 1)); - _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(2), 2)); - _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(3), 3)); - await _testReceiveActor.Ask(new Identify(null), RemainingOrDefault); // verify that the ActorCell has started - - Scheduler.AdvanceTo(Scheduler.Now.AddSeconds(2)); - var firstId = (await ExpectMsgAsync()).Id; - var secondId = (await ExpectMsgAsync()).Id; - await ExpectNoMsgAsync(TimeSpan.FromMilliseconds(20)); - Assert.Equal(1, firstId); - Assert.Equal(2, secondId); - } + [Fact] + public void Advance_to_takes_us_to_correct_time() + { + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(1), 1)); + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(2), 2)); + _testReceiveActor.Tell(new ScheduleOnceMessage(TimeSpan.FromSeconds(3), 3)); + _testReceiveActor.Ask(new Identify(null), RemainingOrDefault) + .Wait(RemainingOrDefault).Should().BeTrue(); // verify that the ActorCell has started + + Scheduler.AdvanceTo(Scheduler.Now.AddSeconds(2)); + var firstId = ExpectMsg().Id; + var secondId = ExpectMsg().Id; + ExpectNoMsg(TimeSpan.FromMilliseconds(20)); + Assert.Equal(1, firstId); + Assert.Equal(2, secondId); + } - private class TestReceiveActor : ReceiveActor + private class TestReceiveActor : ReceiveActor + { + private Cancelable _cancelable; + + public TestReceiveActor() { - private Cancelable _cancelable; + Receive(x => + { + Context.System.Scheduler.ScheduleTellOnce(x.ScheduleOffset, Sender, x, Self); + }); - public TestReceiveActor() + Receive(x => { - Receive(x => - { - Context.System.Scheduler.ScheduleTellOnce(x.ScheduleOffset, Sender, x, Self); - }); - - Receive(x => - { - Context.System.Scheduler.ScheduleTellRepeatedly(x.InitialOffset, x.ScheduleOffset, Sender, x, Self); - }); - - Receive(x => - { - _cancelable = new Cancelable(Context.System.Scheduler); - Context.System.Scheduler.ScheduleTellRepeatedly(x.ScheduleOffset, x.ScheduleOffset, Sender, x, Self, _cancelable); - }); - - Receive(x => - { - _cancelable.Cancel(); - }); - - } - } + Context.System.Scheduler.ScheduleTellRepeatedly(x.InitialOffset, x.ScheduleOffset, Sender, x, Self); + }); - private class CancelableMessage - { - public TimeSpan ScheduleOffset { get; } - public int Id { get; } + Receive(x => + { + _cancelable = new Cancelable(Context.System.Scheduler); + Context.System.Scheduler.ScheduleTellRepeatedly(x.ScheduleOffset, x.ScheduleOffset, Sender, x, Self, _cancelable); + }); - public CancelableMessage(TimeSpan scheduleOffset, int id = 1) + Receive(_ => { - ScheduleOffset = scheduleOffset; - Id = id; - } + _cancelable.Cancel(); + }); + } + } - private class CancelMessage { } + private class CancelableMessage + { + public TimeSpan ScheduleOffset { get; } + public int Id { get; } - private class ScheduleOnceMessage + public CancelableMessage(TimeSpan scheduleOffset, int id = 1) { - public TimeSpan ScheduleOffset { get; } - public int Id { get; } - - public ScheduleOnceMessage(TimeSpan scheduleOffset, int id = 1) - { - ScheduleOffset = scheduleOffset; - Id = id; - } + ScheduleOffset = scheduleOffset; + Id = id; } + } - private class RescheduleMessage - { - public TimeSpan InitialOffset { get; } - public TimeSpan ScheduleOffset { get; } - public int Id { get; } + private class CancelMessage { } - public RescheduleMessage(TimeSpan initialOffset, TimeSpan scheduleOffset, int id = 1) - { - InitialOffset = initialOffset; - ScheduleOffset = scheduleOffset; - Id = id; - } + private class ScheduleOnceMessage + { + public TimeSpan ScheduleOffset { get; } + public int Id { get; } + + public ScheduleOnceMessage(TimeSpan scheduleOffset, int id = 1) + { + ScheduleOffset = scheduleOffset; + Id = id; } + } + + private class RescheduleMessage + { + public TimeSpan InitialOffset { get; } + public TimeSpan ScheduleOffset { get; } + public int Id { get; } - private TestScheduler Scheduler => (TestScheduler)Sys.Scheduler; + public RescheduleMessage(TimeSpan initialOffset, TimeSpan scheduleOffset, int id = 1) + { + InitialOffset = initialOffset; + ScheduleOffset = scheduleOffset; + Id = id; + } } -} + + + private TestScheduler Scheduler => (TestScheduler)Sys.Scheduler; +} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj b/src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj index 798876a8..ab6944ed 100644 --- a/src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj +++ b/src/Akka.Hosting.TestKit/Akka.Hosting.TestKit.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/Akka.Hosting.TestKit/HostingSpec.cs b/src/Akka.Hosting.TestKit/TestKit.cs similarity index 80% rename from src/Akka.Hosting.TestKit/HostingSpec.cs rename to src/Akka.Hosting.TestKit/TestKit.cs index 0f95c650..35045502 100644 --- a/src/Akka.Hosting.TestKit/HostingSpec.cs +++ b/src/Akka.Hosting.TestKit/TestKit.cs @@ -8,11 +8,12 @@ using System.Threading; using System.Threading.Tasks; using Akka.Actor; -using Akka.Actor.Internal; +using Akka.Actor.Setup; using Akka.Annotations; using Akka.Configuration; using Akka.Hosting.TestKit.Internals; using Akka.TestKit; +using Akka.TestKit.Xunit2; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -23,8 +24,13 @@ namespace Akka.Hosting.TestKit { - public abstract partial class HostingSpec: IAsyncLifetime + public abstract class TestKit: TestKitBase, IAsyncLifetime { + /// + /// Commonly used assertions used throughout the testkit. + /// + protected static XunitAssertions Assertions { get; } = new XunitAssertions(); + private IHost _host; public IHost Host { @@ -35,16 +41,6 @@ public IHost Host } } - private TestKitBaseUnWrapper _testKit; - public Akka.TestKit.Xunit2.TestKit TestKit - { - get - { - AssertNotNull(_testKit); - return _testKit; - } - } - public ActorRegistry ActorRegistry => Host.Services.GetRequiredService(); public TimeSpan StartupTimeout { get; } @@ -52,9 +48,10 @@ public Akka.TestKit.Xunit2.TestKit TestKit public ITestOutputHelper Output { get; } public LogLevel LogLevel { get; } - protected HostingSpec(string actorSystemName, ITestOutputHelper output = null, TimeSpan? startupTimeout = null, LogLevel logLevel = LogLevel.Information) + protected TestKit(string actorSystemName = null, ITestOutputHelper output = null, TimeSpan? startupTimeout = null, LogLevel logLevel = LogLevel.Information) + : base(Assertions) { - ActorSystemName = actorSystemName; + ActorSystemName = actorSystemName ?? "test"; Output = output; LogLevel = logLevel; StartupTimeout = startupTimeout ?? TimeSpan.FromSeconds(10); @@ -122,23 +119,17 @@ public async Task InitializeAsync() cts.Dispose(); } - _sys = _host.Services.GetRequiredService(); - _testKit = new TestKitBaseUnWrapper(_sys, Output); - - if (this is INoImplicitSender) - { - InternalCurrentActorCellKeeper.Current = null; - } - else - { - InternalCurrentActorCellKeeper.Current = (ActorCell)((ActorRefWithCell)_testKit.TestActor).Underlying; - } - SynchronizationContext.SetSynchronizationContext( - new ActorCellKeepingSynchronizationContext(InternalCurrentActorCellKeeper.Current)); + var sys = _host.Services.GetRequiredService(); + base.InitializeTest(sys, (ActorSystemSetup)null, null, null); await BeforeTestStart(); } + protected sealed override void InitializeTest(ActorSystem system, ActorSystemSetup config, string actorSystemName, string testActorName) + { + // no-op, deferring InitializeTest after Host have ran + } + protected virtual Task BeforeTestStart() { return Task.CompletedTask; @@ -152,9 +143,10 @@ protected virtual Task BeforeTestStart() /// to shut down the system. Otherwise a memory leak will occur. /// /// - protected virtual async Task AfterAllAsync() + protected virtual Task AfterAllAsync() { - await ShutdownAsync(); + Shutdown(); + return Task.CompletedTask; } public async Task DisposeAsync() @@ -163,7 +155,14 @@ public async Task DisposeAsync() if(_host != null) { await _host.StopAsync(); - _host.Dispose(); + if (_host is IAsyncDisposable asyncDisposable) + { + await asyncDisposable.DisposeAsync(); + } + else + { + _host.Dispose(); + } } } diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ActorOf.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ActorOf.cs deleted file mode 100644 index 357563d3..00000000 --- a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ActorOf.cs +++ /dev/null @@ -1,313 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation -// -// ----------------------------------------------------------------------- - -using System; -using System.Linq.Expressions; -using Akka.Actor; -using Akka.Actor.Dsl; -using Akka.TestKit; - -namespace Akka.Hosting.TestKit -{ - public abstract partial class HostingSpec - { - /// - /// Create a new actor as child of . - /// - /// The props configuration object - /// TBD - public IActorRef ActorOf(Props props) - => TestKit.ActorOf(props); - - /// - /// Create a new actor as child of . - /// - /// The props configuration object - /// The name of the actor. - /// TBD - public IActorRef ActorOf(Props props, string name) - => TestKit.ActorOf(props, name); - - /// - /// Create a new actor as child of . - /// - /// The type of the actor. It must have a parameterless public constructor - /// TBD - public IActorRef ActorOf() where TActor : ActorBase, new() - => TestKit.ActorOf(); - - /// - /// Create a new actor as child of . - /// - /// The type of the actor. It must have a parameterless public constructor - /// The name of the actor. - /// TBD - public IActorRef ActorOf(string name) where TActor : ActorBase, new() - => TestKit.ActorOf(name); - - /// - /// Create a new actor as child of using an expression that calls the constructor - /// of . - /// - /// ActorOf<MyActor>(()=>new MyActor("value", 4711)) - /// - /// - /// The type of the actor. - /// An expression that calls the constructor of - /// TBD - public IActorRef ActorOf(Expression> factory) where TActor : ActorBase - => TestKit.ActorOf(factory); - - /// - /// Create a new actor as child of using an expression that calls the constructor - /// of . - /// - /// ActorOf<MyActor>(()=>new MyActor("value", 4711), "test-actor") - /// - /// - /// The type of the actor. - /// An expression that calls the constructor of - /// The name of the actor. - /// TBD - public IActorRef ActorOf(Expression> factory, string name) where TActor : ActorBase - => TestKit.ActorOf(factory, name); - - /// - /// Creates a new actor by defining the behavior inside the action. - /// - /// - /// ActorOf(c => - /// { - /// c.Receive<string>((msg, ctx) => ctx.Sender.Tell("Hello " + msg)); - /// }); - /// - /// - /// - /// An action that configures the actor's behavior. - /// Optional: The name of the actor. - /// TBD - public IActorRef ActorOf(Action configure, string name = null) - => TestKit.ActorOf(configure, name); - - /// - /// Creates a new actor by defining the behavior inside the action. - /// - /// - /// ActorOf(c => - /// { - /// c.Receive<string>((msg, ctx) => ctx.Sender.Tell("Hello " + msg)); - /// }); - /// - /// - /// - /// An action that configures the actor's behavior. - /// Optional: The name of the actor. - /// TBD - public IActorRef ActorOf(Action configure, string name = null) - => TestKit.ActorOf(configure, name); - - /// - /// Creates an - /// - /// The path of the actor(s) we want to select. - /// An ActorSelection - public ActorSelection ActorSelection(ActorPath actorPath) - => TestKit.ActorSelection(actorPath); - - /// - /// Creates an - /// - /// The path of the actor(s) we want to select. - /// An ActorSelection - public ActorSelection ActorSelection(string actorPath) - => TestKit.ActorSelection(actorPath); - - /// - /// Creates an - /// - /// The base actor that anchors the . - /// The path of the actor(s) we want to select. - /// An ActorSelection - public ActorSelection ActorSelection(IActorRef anchorRef, string actorPath) - => TestKit.ActorSelection(anchorRef, actorPath); - - /// - /// Create a new actor as child of specified supervisor and returns it as - /// to enable access to the underlying actor instance via . - /// - /// The type of the actor. It must have a parameterless public constructor - /// The object - /// The supervisor - /// Optional: The name. - /// TBD - public TestActorRef ActorOfAsTestActorRef(Props props, IActorRef supervisor, string name = null) - where TActor : ActorBase - => TestKit.ActorOfAsTestActorRef(props, supervisor, name); - - /// - /// Create a new actor as child of and returns it as - /// to enable access to the underlying actor instance via . - /// - /// The type of the actor. It must have a parameterless public constructor - /// The object - /// Optional: The name. - /// TBD - public TestActorRef ActorOfAsTestActorRef(Props props, string name = null) where TActor : ActorBase - => TestKit.ActorOfAsTestActorRef(props, name); - - /// - /// Create a new actor as child of the specified supervisor and returns it as - /// to enable access to the underlying actor instance via . - /// Uses an expression that calls the constructor of . - /// - /// ActorOf<MyActor>(()=>new MyActor("value", 4711), "test-actor") - /// - /// - /// The type of the actor. - /// An expression that calls the constructor of - /// The supervisor - /// Optional: The name. - /// TBD - public TestActorRef ActorOfAsTestActorRef(Expression> factory, IActorRef supervisor, string name = null) where TActor : ActorBase - => TestKit.ActorOfAsTestActorRef(factory, supervisor, name); - - /// - /// Create a new actor as child of and returns it as - /// to enable access to the underlying actor instance via . - /// Uses an expression that calls the constructor of . - /// - /// ActorOf<MyActor>(()=>new MyActor("value", 4711), "test-actor") - /// - /// - /// The type of the actor. - /// An expression that calls the constructor of - /// Optional: The name. - /// TBD - public TestActorRef ActorOfAsTestActorRef(Expression> factory, string name = null) where TActor : ActorBase - => TestKit.ActorOfAsTestActorRef(factory, name); - - /// - /// Create a new actor as child of the specified supervisor and returns it as - /// to enable access to the underlying actor instance via . - /// - /// The type of the actor. It must have a parameterless public constructor - /// The supervisor - /// Optional: The name. - /// TBD - public TestActorRef ActorOfAsTestActorRef(IActorRef supervisor, string name = null) where TActor : ActorBase, new() - => TestKit.ActorOfAsTestActorRef(supervisor, name); - - /// - /// Create a new actor as child of and returns it as - /// to enable access to the underlying actor instance via . - /// - /// The type of the actor. It must have a parameterless public constructor - /// Optional: The name. - /// TBD - public TestActorRef ActorOfAsTestActorRef(string name = null) where TActor : ActorBase, new() - => TestKit.ActorOfAsTestActorRef(name); - - - /// - /// Create a new as child of the specified supervisor - /// and returns it as to enable inspecting and modifying the FSM directly. - /// - /// The type of the actor. It must be a - /// The type of state name - /// The type of state data - /// The object - /// The supervisor - /// Optional: The name. - /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. - /// TBD - public TestFSMRef ActorOfAsTestFSMRef(Props props, - IActorRef supervisor, string name = null, bool withLogging = false) - where TFsmActor : FSM - => TestKit.ActorOfAsTestFSMRef(props, supervisor, name, withLogging); - - /// - /// Create a new as child of - /// and returns it as to enable inspecting and modifying the FSM directly. - /// - /// The type of the actor. It must be a and have a public parameterless constructor - /// The type of state name - /// The type of state data - /// The object - /// Optional: The name. - /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. - /// TBD - public TestFSMRef ActorOfAsTestFSMRef(Props props, string name = null, bool withLogging = false) - where TFsmActor : FSM - => TestKit.ActorOfAsTestFSMRef(props, name, withLogging); - - - /// - /// Create a new as child of the specified supervisor - /// and returns it as to enable inspecting and modifying the FSM directly. - /// must have a public parameterless constructor. - /// - /// The type of the actor. It must have a parameterless public constructor - /// The type of state name - /// The type of state data - /// The supervisor - /// Optional: The name. - /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. - /// TBD - public TestFSMRef ActorOfAsTestFSMRef(IActorRef supervisor, string name = null, bool withLogging = false) - where TFsmActor : FSM, new() - => TestKit.ActorOfAsTestFSMRef(supervisor, name, withLogging); - - /// - /// Create a new as child of - /// and returns it as to enable inspecting and modifying the FSM directly. - /// must have a public parameterless constructor. - /// - /// The type of the actor. It must have a parameterless public constructor - /// The type of state name - /// The type of state data - /// Optional: The name. - /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. - /// TBD - public TestFSMRef ActorOfAsTestFSMRef(string name = null, bool withLogging = false) - where TFsmActor : FSM, new() - => TestKit.ActorOfAsTestFSMRef(name, withLogging); - - /// - /// Create a new as child of the specified supervisor - /// and returns it as to enable inspecting and modifying the FSM directly. - /// Uses an expression that calls the constructor of . - /// - /// The type of the actor. - /// The type of state name - /// The type of state data - /// An expression that calls the constructor of - /// The supervisor - /// Optional: The name. - /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. - /// TBD - public TestFSMRef ActorOfAsTestFSMRef(Expression> factory, IActorRef supervisor, string name = null, bool withLogging = false) - where TFsmActor : FSM - => TestKit.ActorOfAsTestFSMRef(factory, supervisor, name, withLogging); - - /// - /// Create a new as child of - /// and returns it as to enable inspecting and modifying the FSM directly. - /// Uses an expression that calls the constructor of . - /// - /// The type of the actor. - /// The type of state name - /// The type of state data - /// An expression that calls the constructor of - /// Optional: The name. - /// Optional: If set to true logs state changes of the FSM as Debug messages. Default is false. - /// TBD - public TestFSMRef ActorOfAsTestFSMRef(Expression> factory, string name = null, bool withLogging = false) - where TFsmActor : FSM - => TestKit.ActorOfAsTestFSMRef(factory, name, withLogging); - - } -} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitAssert.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitAssert.cs deleted file mode 100644 index ec7e0f58..00000000 --- a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitAssert.cs +++ /dev/null @@ -1,58 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation -// -// ----------------------------------------------------------------------- - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Akka.Hosting.TestKit -{ - public abstract partial class HostingSpec - { - /// - /// Await until the given assertion does not throw an exception or the timeout - /// expires, whichever comes first. If the timeout expires the last exception - /// is thrown. - /// The action is called, and if it throws an exception the thread sleeps - /// the specified interval before retrying. - /// If no timeout is given, take it from the innermost enclosing `within` - /// block. - /// Note that the timeout is scaled using , - /// which uses the configuration entry "akka.test.timefactor". - /// - /// The action. - /// The timeout. - /// The interval to wait between executing the assertion. - /// - public void AwaitAssert(Action assertion, TimeSpan? duration = null, TimeSpan? interval = null, - CancellationToken cancellationToken = default) - => TestKit.AwaitAssert(assertion, duration, interval, cancellationToken); - - /// - public Task AwaitAssertAsync(Action assertion, TimeSpan? duration=null, TimeSpan? interval=null, CancellationToken cancellationToken = default) - => TestKit.AwaitAssertAsync(assertion, duration, interval, cancellationToken); - - /// - /// Await until the given assertion does not throw an exception or the timeout - /// expires, whichever comes first. If the timeout expires the last exception - /// is thrown. - /// The action is called, and if it throws an exception the thread sleeps - /// the specified interval before retrying. - /// If no timeout is given, take it from the innermost enclosing `within` - /// block. - /// Note that the timeout is scaled using , - /// which uses the configuration entry "akka.test.timefactor". - /// - /// The action. - /// The timeout. - /// The interval to wait between executing the assertion. - /// - public Task AwaitAssertAsync(Func assertion, TimeSpan? duration=null, TimeSpan? interval=null, CancellationToken cancellationToken = default) - => TestKit.AwaitAssertAsync(assertion, duration, interval, cancellationToken); - - } -} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitCondition.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitCondition.cs deleted file mode 100644 index 6d36e234..00000000 --- a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_AwaitCondition.cs +++ /dev/null @@ -1,142 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation -// -// ----------------------------------------------------------------------- - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Akka.Hosting.TestKit -{ - public abstract partial class HostingSpec - { - /// - /// Await until the given condition evaluates to true or until a timeout - /// The timeout is taken from the innermost enclosing `within` - /// block (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". - /// The value is dilated, i.e. scaled by the factor - /// specified in config value "akka.test.timefactor".. - /// A call to is done immediately, then the threads sleep - /// for about a tenth of the timeout value, before it checks the condition again. This is repeated until - /// timeout or the condition evaluates to true. To specify another interval, use the overload - /// - /// - /// - /// The condition that must be fulfilled within the duration. - /// - public void AwaitCondition(Func conditionIsFulfilled, CancellationToken cancellationToken = default) - => TestKit.AwaitCondition(conditionIsFulfilled, cancellationToken); - - public Task AwaitConditionAsync(Func> conditionIsFulfilled, CancellationToken cancellationToken = default) - => TestKit.AwaitConditionAsync(conditionIsFulfilled, cancellationToken); - - /// - /// Await until the given condition evaluates to true or the timeout - /// expires, whichever comes first. - /// If no timeout is given, take it from the innermost enclosing `within` - /// block (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". - /// The value is dilated, i.e. scaled by the factor - /// specified in config value "akka.test.timefactor".. - /// A call to is done immediately, then the threads sleep - /// for about a tenth of the timeout value, before it checks the condition again. This is repeated until - /// timeout or the condition evaluates to true. To specify another interval, use the overload - /// - /// - /// - /// The condition that must be fulfilled within the duration. - /// The maximum duration. If undefined, uses the remaining time - /// (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". - /// The value is dilated, i.e. scaled by the factor - /// specified in config value "akka.test.timefactor". - /// - public void AwaitCondition(Func conditionIsFulfilled, TimeSpan? max, CancellationToken cancellationToken = default) - => TestKit.AwaitCondition(conditionIsFulfilled, max, cancellationToken); - - public Task AwaitConditionAsync(Func> conditionIsFulfilled, TimeSpan? max, CancellationToken cancellationToken = default) - => TestKit.AwaitConditionAsync(conditionIsFulfilled, max, cancellationToken); - - /// - /// Await until the given condition evaluates to true or the timeout - /// expires, whichever comes first. - /// If no timeout is given, take it from the innermost enclosing `within` - /// block (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". - /// The value is dilated, i.e. scaled by the factor - /// specified in config value "akka.test.timefactor".. - /// A call to is done immediately, then the threads sleep - /// for about a tenth of the timeout value, before it checks the condition again. This is repeated until - /// timeout or the condition evaluates to true. To specify another interval, use the overload - /// - /// - /// - /// The condition that must be fulfilled within the duration. - /// The maximum duration. If undefined, uses the remaining time - /// (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". - /// The value is dilated, i.e. scaled by the factor - /// specified in config value "akka.test.timefactor". - /// The message used if the timeout expires. - /// - public void AwaitCondition(Func conditionIsFulfilled, TimeSpan? max, string message, CancellationToken cancellationToken = default) - => TestKit.AwaitCondition(conditionIsFulfilled, max, message, cancellationToken); - - public Task AwaitConditionAsync(Func> conditionIsFulfilled, TimeSpan? max, string message, CancellationToken cancellationToken = default) - => TestKit.AwaitConditionAsync(conditionIsFulfilled, max, message, cancellationToken); - - /// - /// Await until the given condition evaluates to true or the timeout - /// expires, whichever comes first. - /// If no timeout is given, take it from the innermost enclosing `within` - /// block. - /// Note that the timeout is dilated, i.e. scaled by the factor - /// specified in config value "akka.test.timefactor". - /// The parameter specifies the time between calls to - /// Between calls the thread sleeps. If is undefined the thread only sleeps - /// one time, using the as duration, and then rechecks the condition and ultimately - /// succeeds or fails. - /// To make sure that tests run as fast as possible, make sure you do not leave this value as undefined, - /// instead set it to a relatively small value. - /// - /// The condition that must be fulfilled within the duration. - /// The maximum duration. If undefined, uses the remaining time - /// (if inside a `within` block) or the value specified in config value "akka.test.single-expect-default". - /// The value is dilated, i.e. scaled by the factor - /// specified in config value "akka.test.timefactor". - /// The time between calls to to check - /// if the condition is fulfilled. Between calls the thread sleeps. If undefined, negative or - /// the thread only sleeps one time, using the , - /// and then rechecks the condition and ultimately succeeds or fails. - /// To make sure that tests run as fast as possible, make sure you do not set this value as undefined, - /// instead set it to a relatively small value. - /// - /// The message used if the timeout expires. - /// - public void AwaitCondition(Func conditionIsFulfilled, TimeSpan? max, TimeSpan? interval, string message = null, CancellationToken cancellationToken = default) - => TestKit.AwaitCondition(conditionIsFulfilled, max, interval, message, cancellationToken); - - public Task AwaitConditionAsync(Func> conditionIsFulfilled, TimeSpan? max, TimeSpan? interval, string message = null, CancellationToken cancellationToken = default) - => TestKit.AwaitConditionAsync(conditionIsFulfilled, max, interval, message, cancellationToken); - - /// - /// Await until the given condition evaluates to true or the timeout - /// expires, whichever comes first. Returns true if the condition was fulfilled. - /// The parameter specifies the time between calls to - /// Between calls the thread sleeps. If is not specified or null 100 ms is used. - /// - /// The condition that must be fulfilled within the duration. - /// The maximum duration. - /// Optional. The time between calls to to check - /// if the condition is fulfilled. Between calls the thread sleeps. If undefined, 100 ms is used - /// - /// - /// TBD - public bool AwaitConditionNoThrow(Func conditionIsFulfilled, TimeSpan max, TimeSpan? interval = null, - CancellationToken cancellationToken = default) - => TestKit.AwaitConditionNoThrow(conditionIsFulfilled, max, interval, cancellationToken); - - public Task AwaitConditionNoThrowAsync(Func> conditionIsFulfilled, TimeSpan max, TimeSpan? interval = null, CancellationToken cancellationToken = default) - => TestKit.AwaitConditionNoThrowAsync(conditionIsFulfilled, max, interval, cancellationToken); - - } -} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Expect.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Expect.cs deleted file mode 100644 index 6a71fcc8..00000000 --- a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Expect.cs +++ /dev/null @@ -1,374 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation -// -// ----------------------------------------------------------------------- - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Akka.Actor; -using Akka.TestKit; -using Akka.TestKit.Internal; -using Akka.Util; - -namespace Akka.Hosting.TestKit -{ - public abstract partial class HostingSpec - { - /// - /// Receive one message from the test actor and assert that it is of the specified type. - /// Wait time is bounded by the given duration, if specified; otherwise - /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsg( - TimeSpan? duration = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsg(duration, hint, cancellationToken); - - /// - public ValueTask ExpectMsgAsync( - TimeSpan? duration = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAsync(duration, hint, cancellationToken); - - /// - /// Receive one message of the specified type from the test actor and assert that it - /// equals the . - /// Wait time is bounded by the given duration, if specified; otherwise - /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsg( - T message, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsg(message, timeout, hint, cancellationToken); - - /// - public ValueTask ExpectMsgAsync( - T message, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAsync(message, timeout, hint, cancellationToken); - - /// - /// Receive one message of the specified type from the test actor and assert that the given - /// predicate accepts it. - /// Use this variant to implement more complicated or conditional processing. - /// - /// Wait time is bounded by the given duration, if specified; otherwise - /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsg( - Predicate isMessage, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsg(isMessage, timeout, hint, cancellationToken); - - /// - public ValueTask ExpectMsgAsync( - Predicate isMessage, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAsync(isMessage, timeout, hint, cancellationToken); - - - /// - /// Receive one message of the specified type from the test actor and calls the - /// action that performs extra assertions. - /// Use this variant to implement more complicated or conditional processing. - /// - /// Wait time is bounded by the given duration, if specified; otherwise - /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsg( - Action assert, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsg(assert, timeout, hint, cancellationToken); - - /// - public ValueTask ExpectMsgAsync( - Action assert, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAsync(assert, timeout, hint, cancellationToken); - - /// - /// Receive one message of the specified type from the test actor and assert that the given - /// predicate accepts it. - /// Use this variant to implement more complicated or conditional processing. - /// - /// Wait time is bounded by the given duration, if specified; otherwise - /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsg( - Func isMessageAndSender, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsg(isMessageAndSender, timeout, hint, cancellationToken); - - /// - public ValueTask ExpectMsgAsync( - Func isMessageAndSender, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAsync(isMessageAndSender, timeout, hint, cancellationToken); - - /// - /// Receive one message of the specified type from the test actor calls the - /// action that performs extra assertions. - /// Use this variant to implement more complicated or conditional processing. - /// - /// Wait time is bounded by the given duration, if specified; otherwise - /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsg( - Action assertMessageAndSender, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsg(assertMessageAndSender, timeout, hint, cancellationToken); - - /// - public ValueTask ExpectMsgAsync( - Action assertMessageAndSender, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAsync(assertMessageAndSender, timeout, hint, cancellationToken); - - - /// - /// Receive one message from the test actor and assert that it is equal to the expected value, - /// according to the specified comparer function. - /// - /// Wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsg( - T expected, - Func comparer, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsg(expected, comparer, timeout, hint, cancellationToken); - - /// - public ValueTask ExpectMsgAsync( - T expected, - Func comparer, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAsync(expected, comparer, timeout, hint, cancellationToken); - - /// - /// Receive one message from the test actor and assert that it is the Terminated message of the given ActorRef. - /// - /// Wait time is bounded by the given duration, if specified; otherwise - /// wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - /// TBD - /// TBD - /// TBD - /// - /// TBD - public Terminated ExpectTerminated( - IActorRef target, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectTerminated(target, timeout, hint, cancellationToken); - - /// - public ValueTask ExpectTerminatedAsync( - IActorRef target, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectTerminatedAsync(target, timeout, hint, cancellationToken); - - /// - /// Assert that no message is received. - /// - /// Wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - public void ExpectNoMsg(CancellationToken cancellationToken = default) - => TestKit.ExpectNoMsg(cancellationToken); - - /// - public ValueTask ExpectNoMsgAsync(CancellationToken cancellationToken = default) - => TestKit.ExpectNoMsgAsync(cancellationToken); - - /// - /// Assert that no message is received for the specified time. - /// - /// TBD - /// - public void ExpectNoMsg(TimeSpan duration, CancellationToken cancellationToken = default) - => TestKit.ExpectNoMsg(duration, cancellationToken); - - /// - public ValueTask ExpectNoMsgAsync(TimeSpan duration, CancellationToken cancellationToken = default) - => TestKit.ExpectNoMsgAsync(duration, cancellationToken); - - /// - /// Assert that no message is received for the specified time in milliseconds. - /// - /// TBD - /// - public void ExpectNoMsg(int milliseconds, CancellationToken cancellationToken = default) - => TestKit.ExpectNoMsg(milliseconds, cancellationToken); - - /// - public ValueTask ExpectNoMsgAsync(int milliseconds, CancellationToken cancellationToken = default) - => TestKit.ExpectNoMsgAsync(milliseconds, cancellationToken); - - /// - /// Receive a message from the test actor and assert that it equals - /// one of the given . Wait time is bounded by - /// as duration, with an assertion exception being thrown in case of timeout. - /// - /// The type of the messages - /// The messages. - /// - /// The received messages in received order - public T ExpectMsgAnyOf(IEnumerable messages, CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAnyOf(messages, cancellationToken); - - public ValueTask ExpectMsgAnyOfAsync(IEnumerable messages, CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAnyOfAsync(messages, cancellationToken); - - /// - /// Receive a number of messages from the test actor matching the given - /// number of objects and assert that for each given object one is received - /// which equals it and vice versa. This construct is useful when the order in - /// which the objects are received is not fixed. Wait time is bounded by - /// as duration, with an assertion exception being thrown in case of timeout. - /// - /// - /// dispatcher.Tell(SomeWork1()) - /// dispatcher.Tell(SomeWork2()) - /// ExpectMsgAllOf(TimeSpan.FromSeconds(1), Result1(), Result2()) - /// - /// - /// The type of the messages - /// The messages. - /// - /// The received messages in received order - public IReadOnlyCollection ExpectMsgAllOf( - IReadOnlyCollection messages, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAllOf(messages, cancellationToken); - - public IAsyncEnumerable ExpectMsgAllOfAsync( - IReadOnlyCollection messages, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAllOfAsync(messages, cancellationToken); - - /// - /// Receive a number of messages from the test actor matching the given - /// number of objects and assert that for each given object one is received - /// which equals it and vice versa. This construct is useful when the order in - /// which the objects are received is not fixed. Wait time is bounded by the - /// given duration, with an assertion exception being thrown in case of timeout. - /// - /// - /// dispatcher.Tell(SomeWork1()) - /// dispatcher.Tell(SomeWork2()) - /// ExpectMsgAllOf(TimeSpan.FromSeconds(1), Result1(), Result2()) - /// - /// The deadline is scaled by "akka.test.timefactor" using . - /// - /// The type of the messages - /// The deadline. The deadline is scaled by "akka.test.timefactor" using . - /// The messages. - /// - /// The received messages in received order - public IReadOnlyCollection ExpectMsgAllOf( - TimeSpan max, - IReadOnlyCollection messages, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAllOf(max, messages, cancellationToken); - - public IAsyncEnumerable ExpectMsgAllOfAsync( - TimeSpan max, - IReadOnlyCollection messages, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgAllOfAsync(max, messages, cancellationToken); - } -} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ExpectMsgFrom.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ExpectMsgFrom.cs deleted file mode 100644 index 4501c4b3..00000000 --- a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_ExpectMsgFrom.cs +++ /dev/null @@ -1,204 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation -// -// ----------------------------------------------------------------------- - -using System; -using System.Threading; -using System.Threading.Tasks; -using Akka.Actor; - -namespace Akka.Hosting.TestKit -{ - public abstract partial class HostingSpec - { - /// - /// Receive one message from the test actor and assert that it is of the specified type - /// and was sent by the specified sender - /// Wait time is bounded by the given duration if specified. - /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsgFrom( - IActorRef sender, - TimeSpan? duration = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFrom(sender, duration, hint, cancellationToken); - - public ValueTask ExpectMsgFromAsync( - IActorRef sender, - TimeSpan? duration = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFromAsync(sender, duration, hint, cancellationToken); - - /// - /// Receive one message of the specified type from the test actor and assert that it - /// equals the and was sent by the specified sender - /// Wait time is bounded by the given duration if specified. - /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsgFrom( - IActorRef sender, - T message, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFrom(sender, message, timeout, hint, cancellationToken); - - public ValueTask ExpectMsgFromAsync( - IActorRef sender, - T message, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFromAsync(sender, message, timeout, hint, cancellationToken); - - /// - /// Receive one message of the specified type from the test actor and assert that the given - /// predicate accepts it and was sent by the specified sender - /// Wait time is bounded by the given duration if specified. - /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// Use this variant to implement more complicated or conditional processing. - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsgFrom( - IActorRef sender, - Predicate isMessage, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFrom(sender, isMessage, timeout, hint, cancellationToken); - - public ValueTask ExpectMsgFromAsync( - IActorRef sender, - Predicate isMessage, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFromAsync(sender, isMessage, timeout, hint, cancellationToken); - - /// - /// Receive one message of the specified type from the test actor and assert that the given - /// predicate accepts it and was sent by a sender that matches the predicate. - /// Wait time is bounded by the given duration if specified. - /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// Use this variant to implement more complicated or conditional processing. - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsgFrom( - Predicate isSender, - Predicate isMessage, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFrom(isSender, isMessage, timeout, hint, cancellationToken); - - public ValueTask ExpectMsgFromAsync( - Predicate isSender, - Predicate isMessage, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFromAsync(isSender, isMessage, timeout, hint, cancellationToken); - - /// - /// Receive one message of the specified type from the test actor, verifies that the sender is the specified - /// and calls the action that performs extra assertions. - /// Wait time is bounded by the given duration if specified. - /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// Use this variant to implement more complicated or conditional processing. - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsgFrom( - IActorRef sender, - Action assertMessage, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFrom(sender, assertMessage, timeout, hint, cancellationToken); - - public ValueTask ExpectMsgFromAsync( - IActorRef sender, - Action assertMessage, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFromAsync(sender, assertMessage, timeout, hint, cancellationToken); - - /// - /// Receive one message of the specified type from the test actor and calls the - /// action that performs extra assertions. - /// Wait time is bounded by the given duration if specified. - /// If not specified, wait time is bounded by remaining time for execution of the innermost enclosing 'within' - /// block, if inside a 'within' block; otherwise by the config value - /// "akka.test.single-expect-default". - /// Use this variant to implement more complicated or conditional processing. - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T ExpectMsgFrom( - Action assertSender, - Action assertMessage, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFrom(assertSender, assertMessage, timeout, hint, cancellationToken); - - public ValueTask ExpectMsgFromAsync( - Action assertSender, - Action assertMessage, - TimeSpan? timeout = null, - string hint = null, - CancellationToken cancellationToken = default) - => TestKit.ExpectMsgFromAsync(assertSender, assertMessage, timeout, hint, cancellationToken); - } -} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Receive.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Receive.cs deleted file mode 100644 index 31648f59..00000000 --- a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Receive.cs +++ /dev/null @@ -1,403 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation -// -// ----------------------------------------------------------------------- - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; -using System.Threading.Tasks; -using Akka.TestKit; -using Akka.TestKit.Internal; - -namespace Akka.Hosting.TestKit -{ - public abstract partial class HostingSpec - { - /// - /// Receives messages until returns true. - /// Use it to ignore certain messages while waiting for a specific message. - /// - /// The is message. - /// The maximum. - /// The hint. - /// - /// Returns the message that matched - public object FishForMessage( - Predicate isMessage, - TimeSpan? max = null, - string hint = "", - CancellationToken cancellationToken = default) - => TestKit.FishForMessage(isMessage, max, hint, cancellationToken); - - /// - public ValueTask FishForMessageAsync( - Predicate isMessage, - TimeSpan? max = null, - string hint = "", - CancellationToken cancellationToken = default) - => TestKit.FishForMessageAsync(isMessage, max, hint, cancellationToken); - - /// - /// Receives messages until returns true. - /// Use it to ignore certain messages while waiting for a specific message. - /// - /// The type of the expected message. Messages of other types are ignored. - /// The is message. - /// The maximum. - /// The hint. - /// - /// Returns the message that matched - public T FishForMessage( - Predicate isMessage, - TimeSpan? max = null, - string hint = "", - CancellationToken cancellationToken = default) - => TestKit.FishForMessage(isMessage, max, hint, cancellationToken); - - /// - public ValueTask FishForMessageAsync( - Predicate isMessage, - TimeSpan? max = null, - string hint = "", - CancellationToken cancellationToken = default) - => TestKit.FishForMessageAsync(isMessage, max, hint, cancellationToken); - - /// - /// Receives messages until returns true. - /// Use it to ignore certain messages while waiting for a specific message. - /// - /// The type of the expected message. Messages of other types are ignored. - /// The is message. - /// The maximum. - /// The hint. - /// - /// If null then will be ignored. If not null then will be initially cleared, then filled with all the messages until returns true - /// Returns the message that matched - public T FishForMessage( - Predicate isMessage, - ArrayList allMessages, - TimeSpan? max = null, - string hint = "", - CancellationToken cancellationToken = default) - => TestKit.FishForMessage(isMessage, allMessages, max, hint, cancellationToken); - - /// - public ValueTask FishForMessageAsync( - Predicate isMessage, - ArrayList allMessages, - TimeSpan? max = null, - string hint = "", - CancellationToken cancellationToken = default) - => TestKit.FishForMessageAsync(isMessage, allMessages, max, hint, cancellationToken); - - /// - /// Receives messages until . - /// - /// Ignores all messages except for a message of type . - /// Asserts that all messages are not of the of type . - /// - /// The type that the message is not supposed to be. - /// Optional. The maximum wait duration. Defaults to when unset. - /// - public Task FishUntilMessageAsync(TimeSpan? max = null, CancellationToken cancellationToken = default) - => TestKit.FishUntilMessageAsync(max, cancellationToken); - - /// - /// Waits for a period of 'radio-silence' limited to a number of . - /// Note: 'radio-silence' definition: period when no messages arrive at. - /// - /// A temporary period of 'radio-silence'. - /// The method asserts that is never reached. - /// - /// If set to null then this method will loop for an infinite number of periods. - /// NOTE: If set to null and radio-silence is never reached then this method will never return. - /// Returns all the messages encountered before 'radio-silence' was reached. - public Task WaitForRadioSilenceAsync(TimeSpan? max = null, uint? maxMessages = null, - CancellationToken cancellationToken = default) - => TestKit.WaitForRadioSilenceAsync(max, maxMessages, cancellationToken); - - /// - /// Receive one message from the internal queue of the TestActor. - /// This method blocks the specified duration or until a message - /// is received. If no message was received, null is returned. - /// This method does NOT automatically scale its Duration parameter using ! - /// - /// The maximum duration to wait. - /// If null the config value "akka.test.single-expect-default" is used as timeout. - /// If set to a negative value or , blocks forever. - /// This method does NOT automatically scale its Duration parameter using ! - /// - /// The message if one was received; null otherwise - public object ReceiveOne(TimeSpan? max = null, CancellationToken cancellationToken = default) - => TestKit.ReceiveOne(max, cancellationToken); - - /// - public ValueTask ReceiveOneAsync(TimeSpan? max = null, CancellationToken cancellationToken = default) - => TestKit.ReceiveOneAsync(max, cancellationToken); - - /// - /// Receive one message from the internal queue of the TestActor within - /// the specified duration. The method blocks the specified duration. - /// Note! that the returned - /// is a containing the sender and the message. - /// This method does NOT automatically scale its Duration parameter using ! - /// - /// The received envelope. - /// Optional: The maximum duration to wait. - /// If null the config value "akka.test.single-expect-default" is used as timeout. - /// If set to a negative value or , blocks forever. - /// This method does NOT automatically scale its Duration parameter using ! - /// - /// True if a message was received within the specified duration; false otherwise. - public bool TryReceiveOne( - out MessageEnvelope envelope, - TimeSpan? max = null, - CancellationToken cancellationToken = default) - => TestKit.TryReceiveOne(out envelope, max, cancellationToken); - - /// - public ValueTask<(bool success, MessageEnvelope envelope)> TryReceiveOneAsync( - TimeSpan? max, - CancellationToken cancellationToken = default) - => TestKit.TryReceiveOneAsync(max, cancellationToken); - - #region Peek methods - - /// - /// Peek one message from the head of the internal queue of the TestActor. - /// This method blocks the specified duration or until a message - /// is received. If no message was received, null is returned. - /// This method does NOT automatically scale its Duration parameter using ! - /// - /// The maximum duration to wait. - /// If null the config value "akka.test.single-expect-default" is used as timeout. - /// If set to a negative value or , blocks forever. - /// This method does NOT automatically scale its Duration parameter using ! - /// - /// The message if one was received; null otherwise - public object PeekOne(TimeSpan? max = null, CancellationToken cancellationToken = default) - => TestKit.PeekOne(max, cancellationToken); - - /// - public ValueTask PeekOneAsync(TimeSpan? max = null, CancellationToken cancellationToken = default) - => TestKit.PeekOneAsync(max, cancellationToken); - - /// - /// Peek one message from the head of the internal queue of the TestActor. - /// This method blocks until cancelled. - /// - /// A token used to cancel the operation - /// The message if one was received; null otherwise - public object PeekOne(CancellationToken cancellationToken) - => TestKit.PeekOne(cancellationToken); - - /// - public ValueTask PeekOneAsync(CancellationToken cancellationToken) - => TestKit.PeekOneAsync(cancellationToken); - - /// - /// Peek one message from the head of the internal queue of the TestActor within - /// the specified duration. - /// True is returned if a message existed, and the message - /// is returned in . The method blocks the - /// specified duration, and can be cancelled using the - /// . - /// - /// This method does NOT automatically scale its duration parameter using ! - /// - /// The received envelope. - /// The maximum duration to wait. - /// If null the config value "akka.test.single-expect-default" is used as timeout. - /// If set to , blocks forever (or until cancelled). - /// This method does NOT automatically scale its Duration parameter using ! - /// - /// A token used to cancel the operation. - /// True if a message was received within the specified duration; false otherwise. - public bool TryPeekOne(out MessageEnvelope envelope, TimeSpan? max, CancellationToken cancellationToken) - => TestKit.TryPeekOne(out envelope, max, cancellationToken); - - /// - public ValueTask<(bool success, MessageEnvelope envelope)> TryPeekOneAsync(TimeSpan? max, CancellationToken cancellationToken) - => TestKit.TryPeekOneAsync(max, cancellationToken); - - #endregion - - /// - /// Receive a series of messages until the function returns null or the overall - /// maximum duration is elapsed or expected messages count is reached. - /// Returns the sequence of messages. - /// - /// Note that it is not an error to hit the `max` duration in this case. - /// The max duration is scaled by - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public IReadOnlyList ReceiveWhile( - TimeSpan? max, - Func filter, - int msgs = int.MaxValue, - CancellationToken cancellationToken = default) - => TestKit.ReceiveWhile(max, filter, msgs, cancellationToken); - - /// - public IAsyncEnumerable ReceiveWhileAsync( - TimeSpan? max, - Func filter, - int msgs = int.MaxValue, - CancellationToken cancellationToken = default) - => TestKit.ReceiveWhileAsync(max, filter, msgs, cancellationToken); - - /// - /// Receive a series of messages until the function returns null or the idle - /// timeout is met or the overall maximum duration is elapsed or - /// expected messages count is reached. - /// Returns the sequence of messages. - /// - /// Note that it is not an error to hit the `max` duration in this case. - /// The max duration is scaled by - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public IReadOnlyList ReceiveWhile( - TimeSpan? max, - TimeSpan? idle, - Func filter, - int msgs = int.MaxValue, - CancellationToken cancellationToken = default) - => TestKit.ReceiveWhile(max, idle, filter, msgs, cancellationToken); - - /// - public IAsyncEnumerable ReceiveWhileAsync( - TimeSpan? max, - TimeSpan? idle, - Func filter, - int msgs = int.MaxValue, - CancellationToken cancellationToken = default) - => TestKit.ReceiveWhileAsync(max, idle, filter, msgs, cancellationToken); - - /// - /// Receive a series of messages until the function returns null or the idle - /// timeout is met (disabled by default) or the overall - /// maximum duration is elapsed or expected messages count is reached. - /// Returns the sequence of messages. - /// - /// Note that it is not an error to hit the `max` duration in this case. - /// The max duration is scaled by - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public IReadOnlyList ReceiveWhile( - Func filter, - TimeSpan? max = null, - TimeSpan? idle = null, - int msgs = int.MaxValue, - CancellationToken cancellationToken = default) - => TestKit.ReceiveWhile(filter, max, idle, msgs, cancellationToken); - - /// - public IAsyncEnumerable ReceiveWhileAsync( - Func filter, - TimeSpan? max = null, - TimeSpan? idle = null, - int msgs = int.MaxValue, - CancellationToken cancellationToken = default) - => TestKit.ReceiveWhileAsync(filter, max, idle, msgs, cancellationToken); - - /// - /// Receive a series of messages. - /// It will continue to receive messages until the predicate returns false or the idle - /// timeout is met (disabled by default) or the overall - /// maximum duration is elapsed or expected messages count is reached. - /// If a message that isn't of type the parameter - /// declares if the message should be ignored or not. - /// Returns the sequence of messages. - /// - /// Note that it is not an error to hit the `max` duration in this case. - /// The max duration is scaled by - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public IReadOnlyList ReceiveWhile( - Predicate shouldContinue, - TimeSpan? max = null, - TimeSpan? idle = null, - int msgs = int.MaxValue, - bool shouldIgnoreOtherMessageTypes = true, - CancellationToken cancellationToken = default) - => TestKit.ReceiveWhile(shouldContinue, max, idle, msgs, shouldIgnoreOtherMessageTypes, cancellationToken); - - /// - public IAsyncEnumerable ReceiveWhileAsync( - Predicate shouldContinue, - TimeSpan? max = null, - TimeSpan? idle = null, - int msgs = int.MaxValue, - bool shouldIgnoreOtherMessageTypes = true, - CancellationToken cancellationToken = default) - => TestKit.ReceiveWhileAsync(shouldContinue, max, idle, msgs, shouldIgnoreOtherMessageTypes, cancellationToken); - - /// - /// Receive the specified number of messages using as timeout. - /// - /// The number of messages. - /// - /// The received messages - public IReadOnlyCollection ReceiveN( - int numberOfMessages, - CancellationToken cancellationToken = default) - => TestKit.ReceiveN(numberOfMessages, cancellationToken); - - /// - public IAsyncEnumerable ReceiveNAsync( - int numberOfMessages, - CancellationToken cancellationToken = default) - => TestKit.ReceiveNAsync(numberOfMessages, cancellationToken); - - /// - /// Receive the specified number of messages in a row before the given deadline. - /// The deadline is scaled by "akka.test.timefactor" using . - /// - /// The number of messages. - /// The timeout scaled by "akka.test.timefactor" using . - /// - /// The received messages - public IReadOnlyCollection ReceiveN( - int numberOfMessages, - TimeSpan max, - CancellationToken cancellationToken = default) - => TestKit.ReceiveN(numberOfMessages, max, cancellationToken); - - /// - public IAsyncEnumerable ReceiveNAsync( - int numberOfMessages, - TimeSpan max, - CancellationToken cancellationToken = default) - => TestKit.ReceiveNAsync(numberOfMessages, max, cancellationToken); - } -} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_TestKit.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_TestKit.cs deleted file mode 100644 index b54f1bde..00000000 --- a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_TestKit.cs +++ /dev/null @@ -1,479 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation -// -// ----------------------------------------------------------------------- - -using System; -using System.Threading; -using System.Threading.Tasks; -using Akka.Actor; -using Akka.Actor.Setup; -using Akka.Configuration; -using Akka.Event; -using Akka.TestKit; -using Xunit.Abstractions; - -namespace Akka.Hosting.TestKit -{ - public abstract partial class HostingSpec - { - private ActorSystem _sys; - /// - /// The that is recreated and used for each test. - /// - protected ActorSystem Sys - { - get - { - AssertNotNull(_sys); - return _sys; - } - } - - /// - /// The settings for the testkit. - /// - public TestKitSettings TestKitSettings => TestKit.TestKitSettings; - - /// - /// The last to send a message to the . - /// - public IActorRef LastSender => TestKit.LastSender; - - /// - /// The default TestKit configuration. - /// - public static Config DefaultConfig => TestKitBase.DefaultConfig; - - /// - /// A full debugging configuration with all log settings enabled. - /// - public static Config FullDebugConfig => TestKitBase.FullDebugConfig; - - /// - /// The current time. - /// - public static TimeSpan Now => TimeSpan.FromTicks(DateTime.UtcNow.Ticks); - - private ILoggingAdapter _log; - - /// - /// The built-in used by . - /// - public ILoggingAdapter Log => _log ??= Event.Logging.GetLogger(Sys, GetType()); - - /// - /// The last message received by the . - /// - public object LastMessage => TestKit.LastMessage; - - /// - /// The default TestActor. The actor can be controlled by sending it - /// special control messages, see , - /// , . - /// You can also install an to drive the actor, see - /// . All other messages are forwarded to the queue - /// and can be retrieved with Receive and the ExpectMsg overloads. - /// - public IActorRef TestActor => TestKit.TestActor; - - /// - /// Filter sent to the system's . - /// In order to be able to filter the log the special logger - /// must be installed using the config - /// akka.loggers = ["Akka.TestKit.TestEventListener, Akka.TestKit"] - /// It is installed by default in testkit. - /// - public EventFilterFactory EventFilter => TestKit.EventFilter; - - /// - /// Creates a new event filter for the specified actor system. - /// - /// Actor system. - /// A new instance of . - public EventFilterFactory CreateEventFilter(ActorSystem system) - => new EventFilterFactory(TestKit, system); - - /// - /// Returns true if messages are available. - /// - /// - /// true if messages are available; otherwise, false. - /// - public bool HasMessages => TestKit.HasMessages; - - /// - /// Ignore all messages in the test actor for which the given function - /// returns true. - /// - /// Given a message, if the function returns - /// true the message will be ignored by . - public void IgnoreMessages(Func shouldIgnoreMessage) - => TestKit.IgnoreMessages(shouldIgnoreMessage); - - /// - /// Ignore all messages in the test actor of the given TMsg type for which the given function - /// returns true. - /// - /// The type of the message to ignore. - /// Given a message, if the function returns - /// true the message will be ignored by . - public void IgnoreMessages(Func shouldIgnoreMessage) - => TestKit.IgnoreMessages(shouldIgnoreMessage); - - /// - /// Ignore all messages in the test actor of the given TMsg type. - /// - /// The type of the message to ignore. - public void IgnoreMessages() => TestKit.IgnoreMessages(); - - /// Stop ignoring messages in the test actor. - public void IgnoreNoMessages() => TestKit.IgnoreNoMessages(); - - /// - /// Have the watch an actor and receive - /// messages when the actor terminates. - /// - /// The actor to watch. - /// The actor to watch, i.e. the parameter - public IActorRef Watch(IActorRef actorToWatch) => TestKit.Watch(actorToWatch); - - /// - /// Have the stop watching an actor. - /// - /// The actor to unwatch. - /// The actor to unwatch, i.e. the parameter - public IActorRef Unwatch(IActorRef actorToUnwatch) => TestKit.Unwatch(actorToUnwatch); - - /// - /// - /// Install an to drive the . - /// The will be run for each received message and can - /// be used to send or forward messages, etc. - /// - /// - /// Each invocation must return the AutoPilot for the next round. To reuse the - /// same return . - /// - /// - /// The pilot to install. - public void SetAutoPilot(AutoPilot pilot) => TestKit.SetAutoPilot(pilot); - - /// - /// - /// Retrieves the time remaining for execution of the innermost enclosing - /// Within block. - /// If missing that, then it returns the properly dilated default for this - /// case from settings (key: "akka.test.single-expect-default"). - /// - /// The returned value is always finite. - /// - public TimeSpan RemainingOrDefault => TestKit.RemainingOrDefault; - - /// - /// - /// Retrieves the time remaining for execution of the innermost enclosing - /// Within block. - /// - /// The returned value is always finite. - /// - /// - /// This exception is thrown when called from outside of `within`. - /// - public TimeSpan Remaining => TestKit.Remaining; - - /// - /// If inside a `within` block obtain time remaining for execution of the innermost enclosing `within` - /// block; otherwise returns the given duration. - /// - /// TBD - /// TBD - /// TBD - protected TimeSpan RemainingOr(TimeSpan duration) - { - AssertNotNull(_testKit); - return _testKit.RemainingOr(duration); - } - - /// - /// If is finite it is returned after it has been scaled using . - /// If is undefined, it returns the remaining time (if within a `within` block) or the properly dilated - /// default from settings (key "akka.test.single-expect-default"). - /// If is infinite, an is thrown. - /// The returned value is always finite. - /// - /// The maximum. - /// A finite properly dilated - /// Thrown if is infinite - public TimeSpan RemainingOrDilated(TimeSpan? duration) => TestKit.RemainingOrDilated(duration); - - - /// - /// Multiplies the duration with the , - /// i.e. the config value "akka.test.timefactor" - /// - /// TBD - /// TBD - public TimeSpan Dilated(TimeSpan duration) => TestKit.Dilated(duration); - - - /// - /// If is defined it is returned; otherwise - /// the config value "akka.test.single-expect-default" is returned. - /// - /// TBD - /// TBD - public TimeSpan GetTimeoutOrDefault(TimeSpan? timeout) => TestKit.GetTimeoutOrDefault(timeout); - - /// - /// Shuts down this system. - /// On failure debug output will be logged about the remaining actors in the system. - /// If verifySystemShutdown is true, then an exception will be thrown on failure. - /// - /// Optional. The duration to wait for shutdown. Default is 5 seconds multiplied with the config value "akka.test.timefactor". - /// if set to true an exception will be thrown on failure. - /// to cancel the operation - /// TBD - public virtual void Shutdown( - TimeSpan? duration = null, - bool verifySystemShutdown = false, - CancellationToken cancellationToken = default) - => TestKit.Shutdown(duration, verifySystemShutdown, cancellationToken); - - /// - /// Shuts down this system. - /// On failure debug output will be logged about the remaining actors in the system. - /// If verifySystemShutdown is true, then an exception will be thrown on failure. - /// - /// Optional. The duration to wait for shutdown. Default is 5 seconds multiplied with the config value "akka.test.timefactor". - /// if set to true an exception will be thrown on failure. - /// to cancel the operation - /// TBD - public virtual Task ShutdownAsync( - TimeSpan? duration = null, - bool verifySystemShutdown = false, - CancellationToken cancellationToken = default) - => TestKit.ShutdownAsync(duration, verifySystemShutdown, cancellationToken); - - /// - /// Shuts down the specified system. - /// On failure debug output will be logged about the remaining actors in the system. - /// If verifySystemShutdown is true, then an exception will be thrown on failure. - /// - /// The system to shutdown. - /// The duration to wait for shutdown. Default is 5 seconds multiplied with the config value "akka.test.timefactor" - /// if set to true an exception will be thrown on failure. - /// to cancel the operation - /// TBD - protected virtual void Shutdown( - ActorSystem system, - TimeSpan? duration = null, - bool verifySystemShutdown = false, - CancellationToken cancellationToken = default) - { - AssertNotNull(_testKit); - _testKit.Shutdown(system, duration, verifySystemShutdown, cancellationToken); - } - - /// - /// Shuts down the specified system. - /// On failure debug output will be logged about the remaining actors in the system. - /// If verifySystemShutdown is true, then an exception will be thrown on failure. - /// - /// The system to shutdown. - /// The duration to wait for shutdown. Default is 5 seconds multiplied with the config value "akka.test.timefactor" - /// if set to true an exception will be thrown on failure. - /// to cancel the operation - /// TBD - protected virtual Task ShutdownAsync( - ActorSystem system, - TimeSpan? duration = null, - bool verifySystemShutdown = false, - CancellationToken cancellationToken = default) - { - AssertNotNull(_testKit); - return _testKit.ShutdownAsync(system, duration, verifySystemShutdown, cancellationToken); - } - - /// - /// Spawns an actor as a child of this test actor, and returns the child's IActorRef - /// - /// Child actor props - /// Child actor name - /// Supervisor strategy for the child actor - /// to cancel the operation - /// - public IActorRef ChildActorOf( - Props props, - string name, - SupervisorStrategy supervisorStrategy, - CancellationToken cancellationToken = default) - => TestKit.ChildActorOf(props, name, supervisorStrategy, cancellationToken); - - /// - /// Spawns an actor as a child of this test actor, and returns the child's IActorRef - /// - /// Child actor props - /// Child actor name - /// Supervisor strategy for the child actor - /// to cancel the operation - /// - public Task ChildActorOfAsync( - Props props, - string name, - SupervisorStrategy supervisorStrategy, - CancellationToken cancellationToken = default) - => TestKit.ChildActorOfAsync(props, name, supervisorStrategy, cancellationToken); - - /// - /// Spawns an actor as a child of this test actor with an auto-generated name, and returns the child's ActorRef. - /// - /// Child actor props - /// Supervisor strategy for the child actor - /// to cancel the operation - /// - public IActorRef ChildActorOf( - Props props, SupervisorStrategy supervisorStrategy, CancellationToken cancellationToken = default) - => TestKit.ChildActorOf(props, supervisorStrategy, cancellationToken); - - /// - /// Spawns an actor as a child of this test actor with an auto-generated name, and returns the child's ActorRef. - /// - /// Child actor props - /// Supervisor strategy for the child actor - /// to cancel the operation - /// - public Task ChildActorOfAsync( - Props props, SupervisorStrategy supervisorStrategy, CancellationToken cancellationToken = default) - => TestKit.ChildActorOfAsync(props, supervisorStrategy, cancellationToken); - - /// - /// Spawns an actor as a child of this test actor with a stopping supervisor strategy, and returns the child's ActorRef. - /// - /// Child actor props - /// Child actor name - /// to cancel the operation - /// - public IActorRef ChildActorOf(Props props, string name, CancellationToken cancellationToken = default) - => TestKit.ChildActorOf(props, name, cancellationToken); - - /// - /// Spawns an actor as a child of this test actor with a stopping supervisor strategy, and returns the child's ActorRef. - /// - /// Child actor props - /// Child actor name - /// to cancel the operation - /// - public Task ChildActorOfAsync( - Props props, string name, CancellationToken cancellationToken = default) - => TestKit.ChildActorOfAsync(props, name, cancellationToken); - - /// - /// Spawns an actor as a child of this test actor with an auto-generated name and stopping supervisor strategy, returning the child's ActorRef. - /// - /// Child actor props - /// to cancel the operation - /// - public IActorRef ChildActorOf(Props props, CancellationToken cancellationToken = default) - => TestKit.ChildActorOf(props, cancellationToken); - - /// - /// Spawns an actor as a child of this test actor with an auto-generated name and stopping supervisor strategy, returning the child's ActorRef. - /// - /// Child actor props - /// to cancel the operation - /// - public Task ChildActorOfAsync(Props props, CancellationToken cancellationToken = default) - => TestKit.ChildActorOfAsync(props, cancellationToken); - - /// - /// Creates a test actor with the specified name. The actor can be controlled by sending it - /// special control messages, see , - /// , , - /// . All other messages are forwarded to the queue - /// and can be retrieved with Receive and the ExpectMsg overloads. - /// The default test actor can be retrieved from the property - /// - /// The name of the new actor. - /// TBD - public IActorRef CreateTestActor(string name) - => TestKit.CreateTestActor(name); - - /// - /// Creates a new . - /// - /// Optional: The name of the probe. - /// A new instance. - public virtual TestProbe CreateTestProbe(string name = null) - => TestKit.CreateTestProbe(name); - - /// - /// Creates a new . - /// - /// For multi-actor system tests, you can specify which system the node is for. - /// Optional: The name of the probe. - /// TBD - public virtual TestProbe CreateTestProbe(ActorSystem system, string name = null) - => TestKit.CreateTestProbe(system, name); - - /// - /// Creates a Countdown latch wrapper for use in testing. - /// - /// It uses a timeout when waiting and timeouts are specified as durations. - /// There's a default timeout of 5 seconds and the default count is 1. - /// Timeouts will always throw an exception. - /// - /// Optional. The count. Default: 1 - /// A new - public virtual TestLatch CreateTestLatch(int count = 1) - => TestKit.CreateTestLatch(count); - - /// - /// Wraps a for use in testing. - /// It always uses a timeout when waiting. - /// Timeouts will always throw an exception. The default timeout is 5 seconds. - /// - /// TBD - /// TBD - public TestBarrier CreateTestBarrier(int count) - => TestKit.CreateTestBarrier(count); - } - - internal class TestKitBaseUnWrapper : Akka.TestKit.Xunit2.TestKit - { - public TestKitBaseUnWrapper(ActorSystem system = null, ITestOutputHelper output = null) : base(system, output) - { - } - - public TestKitBaseUnWrapper(ActorSystemSetup config, string actorSystemName = null, ITestOutputHelper output = null) : base(config, actorSystemName, output) - { - } - - public TestKitBaseUnWrapper(Config config, string actorSystemName = null, ITestOutputHelper output = null) : base(config, actorSystemName, output) - { - } - - public TestKitBaseUnWrapper(string config, ITestOutputHelper output = null) : base(config, output) - { - } - - public new void Shutdown( - ActorSystem system, - TimeSpan? duration = null, - bool verifySystemShutdown = false, - CancellationToken cancellationToken = default) - => base.Shutdown(system, duration, verifySystemShutdown, cancellationToken); - - public new Task ShutdownAsync( - ActorSystem system, - TimeSpan? duration = null, - bool verifySystemShutdown = false, - CancellationToken cancellationToken = default) - => base.ShutdownAsync(system, duration, verifySystemShutdown, cancellationToken); - - public new TimeSpan RemainingOr(TimeSpan duration) - => base.RemainingOr(duration); - } -} \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Within.cs b/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Within.cs deleted file mode 100644 index 0599470f..00000000 --- a/src/Akka.Hosting.TestKit/Wrapper/HostingSpec_Within.cs +++ /dev/null @@ -1,177 +0,0 @@ -// ----------------------------------------------------------------------- -// -// Copyright (C) 2009-2022 Lightbend Inc. -// Copyright (C) 2013-2022 .NET Foundation -// -// ----------------------------------------------------------------------- - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Akka.Hosting.TestKit -{ - public abstract partial class HostingSpec - { - /// - /// Execute code block while bounding its execution time between 0 seconds and . - /// `within` blocks may be nested. All methods in this class which take maximum wait times - /// are available in a version which implicitly uses the remaining time governed by - /// the innermost enclosing `within` block. - /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor" - /// - /// TBD - /// TBD - /// TBD - /// - public void Within( - TimeSpan max, - Action action, - TimeSpan? epsilonValue = null, - CancellationToken cancellationToken = default) - => TestKit.Within(max, action, epsilonValue, cancellationToken); - - /// - /// Async version of - /// that takes a instead of an - /// - public Task WithinAsync( - TimeSpan max, - Func actionAsync, - TimeSpan? epsilonValue = null, - CancellationToken cancellationToken = default) - => TestKit.WithinAsync(max, actionAsync, epsilonValue, cancellationToken); - - /// - /// Execute code block while bounding its execution time between and . - /// `within` blocks may be nested. All methods in this class which take maximum wait times - /// are available in a version which implicitly uses the remaining time governed by - /// the innermost enclosing `within` block. - /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor" - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - public void Within( - TimeSpan min, - TimeSpan max, - Action action, - string hint = null, - TimeSpan? epsilonValue = null, - CancellationToken cancellationToken = default) - => TestKit.Within(min, max, action, hint, epsilonValue, cancellationToken); - - /// - /// Async version of - /// that takes a instead of an - /// - public Task WithinAsync( - TimeSpan min, - TimeSpan max, - Func actionAsync, - string hint = null, - TimeSpan? epsilonValue = null, - CancellationToken cancellationToken = default) - => TestKit.WithinAsync(min, max, actionAsync, hint, epsilonValue, cancellationToken); - - /// - /// Execute code block while bounding its execution time between 0 seconds and . - /// `within` blocks may be nested. All methods in this class which take maximum wait times - /// are available in a version which implicitly uses the remaining time governed by - /// the innermost enclosing `within` block. - /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor" - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T Within( - TimeSpan max, - Func function, - TimeSpan? epsilonValue = null, - CancellationToken cancellationToken = default) - => TestKit.Within(max, function, epsilonValue, cancellationToken); - - /// - /// Execute code block while bounding its execution time between 0 seconds and . - /// `within` blocks may be nested. All methods in this class which take maximum wait times - /// are available in a version which implicitly uses the remaining time governed by - /// the innermost enclosing `within` block. - /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor" - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public Task WithinAsync( - TimeSpan max, - Func> function, - TimeSpan? epsilonValue = null, - CancellationToken cancellationToken = default) - => TestKit.WithinAsync(max, function, epsilonValue, cancellationToken); - - /// - /// Execute code block while bounding its execution time between and . - /// `within` blocks may be nested. All methods in this class which take maximum wait times - /// are available in a version which implicitly uses the remaining time governed by - /// the innermost enclosing `within` block. - /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor" - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public T Within( - TimeSpan min, - TimeSpan max, - Func function, - string hint = null, - TimeSpan? epsilonValue = null, - CancellationToken cancellationToken = default) - => TestKit.Within(min, max, function, hint, epsilonValue, cancellationToken); - - /// - /// Execute code block while bounding its execution time between and . - /// `within` blocks may be nested. All methods in this class which take maximum wait times - /// are available in a version which implicitly uses the remaining time governed by - /// the innermost enclosing `within` block. - /// - /// - /// Note that the max duration is scaled using which uses the config value "akka.test.timefactor". - /// - /// - /// Note that due to how asynchronous Task is executed in managed code, there is no way to stop a running Task. - /// If this assertion fails in any way, the Task might still be running in the - /// background and might not be stopped/disposed until the unit test is over. - /// - /// - /// - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// TBD - /// - /// TBD - public Task WithinAsync( - TimeSpan min, - TimeSpan max, - Func> function, - string hint = null, - TimeSpan? epsilonValue = null, - CancellationToken cancellationToken = default) - => TestKit.WithinAsync(min, max, function, hint, epsilonValue, cancellationToken); - } -} \ No newline at end of file From 5d54f429033d4adc9ca0aeb84fbbad6c2d03036b Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Sat, 24 Sep 2022 03:51:12 +0700 Subject: [PATCH 6/9] Fix TestKit, test should start only after TestKit is initialized --- src/Akka.Hosting.TestKit/TestKit.cs | 66 ++++++++++++++++++++--------- 1 file changed, 46 insertions(+), 20 deletions(-) diff --git a/src/Akka.Hosting.TestKit/TestKit.cs b/src/Akka.Hosting.TestKit/TestKit.cs index 35045502..42986efa 100644 --- a/src/Akka.Hosting.TestKit/TestKit.cs +++ b/src/Akka.Hosting.TestKit/TestKit.cs @@ -11,9 +11,12 @@ using Akka.Actor.Setup; using Akka.Annotations; using Akka.Configuration; +using Akka.Event; +using Akka.Hosting.Logging; using Akka.Hosting.TestKit.Internals; using Akka.TestKit; using Akka.TestKit.Xunit2; +using Akka.TestKit.Xunit2.Internals; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -21,7 +24,9 @@ using Xunit; using Xunit.Abstractions; using Xunit.Sdk; +using LogLevel = Microsoft.Extensions.Logging.LogLevel; +#nullable enable namespace Akka.Hosting.TestKit { public abstract class TestKit: TestKitBase, IAsyncLifetime @@ -31,12 +36,13 @@ public abstract class TestKit: TestKitBase, IAsyncLifetime /// protected static XunitAssertions Assertions { get; } = new XunitAssertions(); - private IHost _host; + private IHost? _host; public IHost Host { get { - AssertNotNull(_host); + if(_host is null) + throw new XunitException("Test has not been initialized yet"); return _host; } } @@ -45,10 +51,12 @@ public IHost Host public TimeSpan StartupTimeout { get; } public string ActorSystemName { get; } - public ITestOutputHelper Output { get; } + public ITestOutputHelper? Output { get; } public LogLevel LogLevel { get; } - protected TestKit(string actorSystemName = null, ITestOutputHelper output = null, TimeSpan? startupTimeout = null, LogLevel logLevel = LogLevel.Information) + private TaskCompletionSource _initialized = new TaskCompletionSource(); + + protected TestKit(string? actorSystemName = null, ITestOutputHelper? output = null, TimeSpan? startupTimeout = null, LogLevel logLevel = LogLevel.Information) : base(Assertions) { ActorSystemName = actorSystemName ?? "test"; @@ -70,23 +78,48 @@ private void InternalConfigureServices(HostBuilderContext context, IServiceColle { ConfigureServices(context, services); - services.AddAkka(ActorSystemName, (builder, provider) => + services.AddAkka(ActorSystemName, async (builder, provider) => { - builder.AddHocon( - Config != null ? Config.WithFallback(TestKitBase.DefaultConfig) : TestKitBase.DefaultConfig, - HoconAddMode.Prepend); + builder.AddHocon(DefaultConfig, HoconAddMode.Prepend); + if (Config is { }) + builder.AddHocon(Config, HoconAddMode.Prepend); - ConfigureAkka(builder, provider); + builder.ConfigureLoggers(logger => + { + logger.LogLevel = ToAkkaLogLevel(LogLevel); + logger.ClearLoggers(); + logger.AddLogger(); + }); + + if (Output is { }) + { + builder.StartActors(async (system, registry) => + { + var extSystem = (ExtendedActorSystem)system; + var logger = extSystem.SystemActorOf(Props.Create(() => new LoggerFactoryLogger()), "log-test"); + await logger.Ask(new InitializeLogger(system.EventStream)); + }); + } + + await ConfigureAkka(builder, provider); + + builder.AddStartup((system, registry) => + { + base.InitializeTest(system, (ActorSystemSetup)null!, null, null); + _initialized.SetResult(Done.Instance); + }); }); } - protected virtual Config Config { get; } = null; + protected virtual Config? Config { get; } = null; protected virtual void ConfigureLogging(ILoggingBuilder builder) { } - protected virtual void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { } + protected virtual Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } [InternalApi] public async Task InitializeAsync() @@ -119,9 +152,7 @@ public async Task InitializeAsync() cts.Dispose(); } - var sys = _host.Services.GetRequiredService(); - base.InitializeTest(sys, (ActorSystemSetup)null, null, null); - + await _initialized.Task; await BeforeTestStart(); } @@ -178,11 +209,6 @@ private static Event.LogLevel ToAkkaLogLevel(LogLevel logLevel) _ => Event.LogLevel.ErrorLevel }; - private static void AssertNotNull(object obj) - { - if(obj is null) - throw new XunitException("Test has not been initialized yet"); - } } } From 69dd73c948f9fdf934983c7a4058ba72e280bf77 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Sat, 24 Sep 2022 03:51:41 +0700 Subject: [PATCH 7/9] Fix TestEventListenerTest --- .../HostingSpecSpec.cs | 13 +++++++++-- .../AllTestForEventFilterBase.cs | 5 ++-- .../AllTestForEventFilterBase_Instances.cs | 17 +++++++------- .../CustomEventFilterTests.cs | 2 +- .../DeadLettersEventFilterTests.cs | 2 +- .../EventFilterTestBase.cs | 23 +++++++++++++++---- .../ExceptionEventFilterTests.cs | 2 +- 7 files changed, 43 insertions(+), 21 deletions(-) diff --git a/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs b/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs index 50d00107..46248177 100644 --- a/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/HostingSpecSpec.cs @@ -6,7 +6,9 @@ // ----------------------------------------------------------------------- using System; +using System.Threading.Tasks; using Akka.Actor; +using Akka.Event; using Akka.TestKit.TestActors; using Xunit; using Xunit.Abstractions; @@ -24,13 +26,14 @@ public HostingSpecSpec(ITestOutputHelper output) { } - protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) { builder.WithActors((system, registry) => { var echo = system.ActorOf(Props.Create(() => new SimpleEchoActor())); registry.Register(echo); }); + return Task.CompletedTask; } [Fact] @@ -48,7 +51,13 @@ private class SimpleEchoActor : ReceiveActor { public SimpleEchoActor() { - ReceiveAny(Sender.Tell); + var log = Context.GetLogger(); + + ReceiveAny(msg => + { + log.Info($"Received {msg}"); + Sender.Tell(msg); + }); } } } diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs index 98699273..dd4878c4 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase.cs @@ -11,6 +11,7 @@ using Akka.TestKit; using FluentAssertions; using Xunit; +using Xunit.Abstractions; using Xunit.Sdk; using static FluentAssertions.FluentActions; @@ -21,8 +22,8 @@ public abstract class AllTestForEventFilterBase : EventFilterTestBase // ReSharper disable ConvertToLambdaExpression private EventFilterFactory _testingEventFilter; - protected AllTestForEventFilterBase(string config) - : base(config) + protected AllTestForEventFilterBase(LogLevel logLevel, ITestOutputHelper output = null) + : base(logLevel, output) { } diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs index fcbd4aa3..0aa33b9e 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/AllTestForEventFilterBase_Instances.cs @@ -1,6 +1,5 @@ //----------------------------------------------------------------------- // -// Copyright (C) 2009-2021 Lightbend Inc. // Copyright (C) 2013-2021 .NET Foundation // //----------------------------------------------------------------------- @@ -12,7 +11,7 @@ namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests; public class EventFilterDebugTests : AllTestForEventFilterBase { - public EventFilterDebugTests() : base("akka.loglevel=DEBUG"){} + public EventFilterDebugTests() : base(LogLevel.DebugLevel){} protected override EventFilterFactory CreateTestingEventFilter() { @@ -27,7 +26,7 @@ protected override void PublishMessage(object message, string source) public class CustomEventFilterDebugTests : AllTestForEventFilterBase { - public CustomEventFilterDebugTests() : base("akka.loglevel=DEBUG") { } + public CustomEventFilterDebugTests() : base(LogLevel.DebugLevel) { } protected override EventFilterFactory CreateTestingEventFilter() { @@ -42,7 +41,7 @@ protected override void PublishMessage(object message, string source) public class EventFilterInfoTests : AllTestForEventFilterBase { - public EventFilterInfoTests() : base("akka.loglevel=INFO") { } + public EventFilterInfoTests() : base(LogLevel.InfoLevel) { } protected override EventFilterFactory CreateTestingEventFilter() { @@ -57,7 +56,7 @@ protected override void PublishMessage(object message, string source) public class CustomEventFilterInfoTests : AllTestForEventFilterBase { - public CustomEventFilterInfoTests() : base("akka.loglevel=INFO") { } + public CustomEventFilterInfoTests() : base(LogLevel.InfoLevel) { } protected override EventFilterFactory CreateTestingEventFilter() { @@ -73,7 +72,7 @@ protected override void PublishMessage(object message, string source) public class EventFilterWarningTests : AllTestForEventFilterBase { - public EventFilterWarningTests() : base("akka.loglevel=WARNING") { } + public EventFilterWarningTests() : base(LogLevel.WarningLevel) { } protected override EventFilterFactory CreateTestingEventFilter() { @@ -88,7 +87,7 @@ protected override void PublishMessage(object message, string source) public class CustomEventFilterWarningTests : AllTestForEventFilterBase { - public CustomEventFilterWarningTests() : base("akka.loglevel=WARNING") { } + public CustomEventFilterWarningTests() : base(LogLevel.WarningLevel) { } protected override EventFilterFactory CreateTestingEventFilter() { @@ -103,7 +102,7 @@ protected override void PublishMessage(object message, string source) public class EventFilterErrorTests : AllTestForEventFilterBase { - public EventFilterErrorTests() : base("akka.loglevel=ERROR") { } + public EventFilterErrorTests() : base(LogLevel.ErrorLevel) { } protected override EventFilterFactory CreateTestingEventFilter() { @@ -118,7 +117,7 @@ protected override void PublishMessage(object message, string source) public class CustomEventFilterErrorTests : AllTestForEventFilterBase { - public CustomEventFilterErrorTests() : base("akka.loglevel=ERROR") { } + public CustomEventFilterErrorTests() : base(LogLevel.ErrorLevel) { } protected override EventFilterFactory CreateTestingEventFilter() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs index 79ccee4e..0e1a1f63 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/CustomEventFilterTests.cs @@ -14,7 +14,7 @@ namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests; public abstract class CustomEventFilterTestsBase : EventFilterTestBase { // ReSharper disable ConvertToLambdaExpression - public CustomEventFilterTestsBase() : base("akka.loglevel=ERROR") { } + public CustomEventFilterTestsBase() : base(Event.LogLevel.ErrorLevel) { } protected override void SendRawLogEventMessage(object message) { diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs index 20643513..86f0416a 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/DeadLettersEventFilterTests.cs @@ -19,7 +19,7 @@ public abstract class DeadLettersEventFilterTestsBase : EventFilterTestBase private IActorRef _deadActor; // ReSharper disable ConvertToLambdaExpression - protected DeadLettersEventFilterTestsBase() : base("akka.loglevel=ERROR") + protected DeadLettersEventFilterTestsBase() : base(Event.LogLevel.ErrorLevel) { } diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs index 612914dc..fb6ba1d0 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs @@ -5,28 +5,41 @@ // //----------------------------------------------------------------------- +using System; using System.Threading.Tasks; -using Akka.Configuration; using Akka.Event; +using Xunit.Abstractions; namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests { public abstract class EventFilterTestBase : TestKit { + private readonly LogLevel _logLevel; + /// /// Used to signal that the test was successful and that we should ensure no more messages were logged /// protected bool TestSuccessful; - protected EventFilterTestBase(string config) + protected EventFilterTestBase(LogLevel logLevel, ITestOutputHelper output = null) : base(output: output) { - Config = $"akka.loggers = [\"{typeof(ForwardAllEventsTestEventListener).AssemblyQualifiedName}\"], {(string.IsNullOrEmpty(config) ? "" : config)}"; + _logLevel = logLevel; } - protected override Config Config { get; } - protected abstract void SendRawLogEventMessage(object message); + protected override async Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + builder.ConfigureLoggers(logger => + { + logger.LogLevel = _logLevel; + logger.ClearLoggers(); + logger.AddLogger(); + }); + + await base.ConfigureAkka(builder, provider); + } + protected override async Task BeforeTestStart() { await base.BeforeTestStart(); diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs index 0abbd878..b4d2b12f 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ExceptionEventFilterTests.cs @@ -18,7 +18,7 @@ namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests; public class ExceptionEventFilterTests : EventFilterTestBase { public ExceptionEventFilterTests() - : base("akka.logLevel=ERROR") + : base(Event.LogLevel.ErrorLevel) { } From f62069a378d82aa8854baa57974341bbb792019b Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Tue, 4 Oct 2022 04:19:44 +0700 Subject: [PATCH 8/9] Change `ConfigureAkka` from virtual to abstract --- .../NoImplicitSenderSpec.cs | 12 ++++++++++++ .../TestActorRefTests/TestActorRefSpec.cs | 5 +++++ .../TestActorRefTests/TestProbeSpec.cs | 6 ++++++ .../TestEventListenerTests/ConfigTests.cs | 7 +++++++ .../TestEventListenerTests/EventFilterTestBase.cs | 4 ++-- .../TestFSMRefTests/TestFSMRefSpec.cs | 6 ++++++ .../TestKitBaseTests/AwaitAssertTests.cs | 6 ++++++ .../TestKitBaseTests/DilatedTests.cs | 6 ++++++ .../TestKitBaseTests/ExpectTests.cs | 6 ++++++ .../TestKitBaseTests/IgnoreMessagesTests.cs | 6 ++++++ .../TestKitBaseTests/ReceiveTests.cs | 5 +++++ .../TestKitBaseTests/RemainingTests.cs | 6 ++++++ .../TestKitBaseTests/WithinTests.cs | 6 ++++++ .../TestKit_Config_Tests.cs | 6 ++++++ src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs | 5 +++++ src/Akka.Hosting.TestKit/TestKit.cs | 5 +---- 16 files changed, 91 insertions(+), 6 deletions(-) diff --git a/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs b/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs index 7f94995a..23192787 100644 --- a/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/NoImplicitSenderSpec.cs @@ -5,6 +5,8 @@ // //----------------------------------------------------------------------- +using System; +using System.Threading.Tasks; using Akka.Actor; using Akka.Actor.Dsl; using Akka.TestKit; @@ -15,6 +17,11 @@ namespace Akka.Hosting.TestKit.Tests; public class NoImplicitSenderSpec : TestKit, INoImplicitSender { + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void When_Not_ImplicitSender_then_testActor_is_not_sender() { @@ -28,6 +35,11 @@ public void When_Not_ImplicitSender_then_testActor_is_not_sender() public class ImplicitSenderSpec : TestKit { + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void ImplicitSender_should_have_testActor_as_sender() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs index 845199ec..2be76e17 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestActorRefSpec.cs @@ -38,6 +38,11 @@ private void AssertThread() Assert.True(OtherThread == null || OtherThread == Thread, "Thread"); } + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + protected override async Task BeforeTestStart() { await base.BeforeTestStart(); diff --git a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs index ac8113f6..ce64f64a 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestActorRefTests/TestProbeSpec.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; +using System.Threading.Tasks; using Akka.Actor; using Akka.TestKit; using Akka.TestKit.TestActors; @@ -28,6 +29,11 @@ public void TestProbe_should_equal_underlying_Ref() hs.Count.Should().Be(1); } + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + /// /// Should be able to receive a message from a /// if we're deathwatching it and it terminates. diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs index 98e18356..6d5bfef0 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/ConfigTests.cs @@ -5,7 +5,9 @@ // //----------------------------------------------------------------------- +using System; using System.Linq; +using System.Threading.Tasks; using FluentAssertions; using Xunit; @@ -13,6 +15,11 @@ namespace Akka.Hosting.TestKit.Tests.TestEventListenerTests { public class ConfigTests : TestKit { + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void TestEventListener_is_in_config_by_default() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs index fb6ba1d0..ce5fdac8 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestEventListenerTests/EventFilterTestBase.cs @@ -28,7 +28,7 @@ protected EventFilterTestBase(LogLevel logLevel, ITestOutputHelper output = null protected abstract void SendRawLogEventMessage(object message); - protected override async Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) { builder.ConfigureLoggers(logger => { @@ -37,7 +37,7 @@ protected override async Task ConfigureAkka(AkkaConfigurationBuilder builder, IS logger.AddLogger(); }); - await base.ConfigureAkka(builder, provider); + return Task.CompletedTask; } protected override async Task BeforeTestStart() diff --git a/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs b/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs index 42df6ff3..8f099a43 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestFSMRefTests/TestFSMRefSpec.cs @@ -6,6 +6,7 @@ //----------------------------------------------------------------------- using System; +using System.Threading.Tasks; using Akka.Actor; using FluentAssertions; using Xunit; @@ -14,6 +15,11 @@ namespace Akka.Hosting.TestKit.Tests.TestFSMRefTests; public class TestFSMRefSpec : TestKit { + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void A_TestFSMRef_must_allow_access_to_internal_state() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs index 19b22962..32b1a12b 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/AwaitAssertTests.cs @@ -6,6 +6,7 @@ //----------------------------------------------------------------------- using System; +using System.Threading.Tasks; using Akka.Configuration; using Xunit; using Xunit.Sdk; @@ -16,6 +17,11 @@ public class AwaitAssertTests : TestKit { protected override Config Config { get; } = "akka.test.timefactor=2"; + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void AwaitAssert_must_not_throw_any_exception_when_assertion_is_valid() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs index 5c8a149e..54ec0a3c 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/DilatedTests.cs @@ -7,6 +7,7 @@ using System; using System.Diagnostics; +using System.Threading.Tasks; using Akka.Configuration; using Xunit; using Xunit.Sdk; @@ -25,6 +26,11 @@ public class DilatedTests : TestKit protected override Config Config { get; } = $"akka.test.timefactor={TimeFactor}"; + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void Dilates_correctly_using_timeFactor() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs index 454fdce9..0ca717ac 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ExpectTests.cs @@ -6,6 +6,7 @@ //----------------------------------------------------------------------- using System; +using System.Threading.Tasks; using Akka.Actor; using FluentAssertions; using Xunit; @@ -15,6 +16,11 @@ namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; public class ExpectTests : TestKit { + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void ExpectMsgAllOf_should_receive_correct_messages() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs index 8d42bd0a..ad22590d 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/IgnoreMessagesTests.cs @@ -6,6 +6,7 @@ //----------------------------------------------------------------------- using System; +using System.Threading.Tasks; using Akka.Actor; using FluentAssertions; using Xunit; @@ -24,6 +25,11 @@ public IgnoredMessage(string ignoreMe = null) public string IgnoreMe { get; } } + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void IgnoreMessages_should_ignore_messages() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs index f05cc2cb..2bd7c342 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/ReceiveTests.cs @@ -18,6 +18,11 @@ namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; public class ReceiveTests : TestKit { + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void ReceiveN_should_receive_correct_number_of_messages() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs index 20fbbd9e..3efd2ff4 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/RemainingTests.cs @@ -6,12 +6,18 @@ //----------------------------------------------------------------------- using System; +using System.Threading.Tasks; using Xunit; namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; public class RemainingTests : TestKit { + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void Throw_if_remaining_is_called_outside_Within() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs index a96f635f..2a927f0a 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKitBaseTests/WithinTests.cs @@ -6,12 +6,18 @@ //----------------------------------------------------------------------- using System; +using System.Threading.Tasks; using Xunit; namespace Akka.Hosting.TestKit.Tests.TestKitBaseTests; public class WithinTests : TestKit { + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void Within_should_increase_max_timeout_by_the_provided_epsilon_value() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs b/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs index 80dc8b01..c0f4cf74 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestKit_Config_Tests.cs @@ -7,6 +7,7 @@ using System; using System.Reflection; +using System.Threading.Tasks; using Akka.TestKit; using FluentAssertions; using Xunit; @@ -16,6 +17,11 @@ namespace Akka.Hosting.TestKit.Tests; // ReSharper disable once InconsistentNaming public class TestKit_Config_Tests : TestKit { + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + [Fact] public void DefaultValues_should_be_correct() { diff --git a/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs b/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs index 632c4408..973bd119 100644 --- a/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs +++ b/src/Akka.Hosting.TestKit.Tests/TestSchedulerTests.cs @@ -20,6 +20,11 @@ public class TestSchedulerTests : TestKit { private IActorRef _testReceiveActor; + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + return Task.CompletedTask; + } + protected override async Task BeforeTestStart() { await base.BeforeTestStart(); diff --git a/src/Akka.Hosting.TestKit/TestKit.cs b/src/Akka.Hosting.TestKit/TestKit.cs index 42986efa..15515ccc 100644 --- a/src/Akka.Hosting.TestKit/TestKit.cs +++ b/src/Akka.Hosting.TestKit/TestKit.cs @@ -116,10 +116,7 @@ private void InternalConfigureServices(HostBuilderContext context, IServiceColle protected virtual void ConfigureLogging(ILoggingBuilder builder) { } - protected virtual Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) - { - return Task.CompletedTask; - } + protected abstract Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider); [InternalApi] public async Task InitializeAsync() From f15c1cdab3b32e10b9a2efc6a19912ddc8a985ab Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Tue, 4 Oct 2022 04:31:56 +0700 Subject: [PATCH 9/9] Refactor Akka.Persistence.Hosting.Tests to use Akka.Hosting.TestKit --- .../Akka.Persistence.Hosting.Tests.csproj | 1 + .../EventAdapterSpecs.cs | 34 ++++++++-------- .../InMemoryPersistenceSpecs.cs | 40 +++++++++---------- 3 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/Akka.Persistence.Hosting.Tests/Akka.Persistence.Hosting.Tests.csproj b/src/Akka.Persistence.Hosting.Tests/Akka.Persistence.Hosting.Tests.csproj index b06789e6..d503b8db 100644 --- a/src/Akka.Persistence.Hosting.Tests/Akka.Persistence.Hosting.Tests.csproj +++ b/src/Akka.Persistence.Hosting.Tests/Akka.Persistence.Hosting.Tests.csproj @@ -16,6 +16,7 @@ + diff --git a/src/Akka.Persistence.Hosting.Tests/EventAdapterSpecs.cs b/src/Akka.Persistence.Hosting.Tests/EventAdapterSpecs.cs index 6ed7a5ee..4dee92a4 100644 --- a/src/Akka.Persistence.Hosting.Tests/EventAdapterSpecs.cs +++ b/src/Akka.Persistence.Hosting.Tests/EventAdapterSpecs.cs @@ -11,7 +11,7 @@ namespace Akka.Persistence.Hosting.Tests; -public class EventAdapterSpecs +public class EventAdapterSpecs: Akka.Hosting.TestKit.TestKit { public static async Task StartHost(Action testSetup) { @@ -79,25 +79,25 @@ public IEventSequence FromJournal(object evt, string manifest) } } - [Fact] - public async Task Should_use_correct_EventAdapter_bindings() + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) { - // arrange - using var host = await StartHost(collection => collection.AddAkka("MySys", builder => + builder.WithJournal("sql-server", journalBuilder => { - builder.WithJournal("sql-server", journalBuilder => - { - journalBuilder.AddWriteEventAdapter("mapper1", new Type[] { typeof(Event1) }); - journalBuilder.AddReadEventAdapter("reader1", new Type[] { typeof(Event1) }); - journalBuilder.AddEventAdapter("combo", boundTypes: new Type[] { typeof(Event2) }); - journalBuilder.AddWriteEventAdapter("tagger", - boundTypes: new Type[] { typeof(Event1), typeof(Event2) }); - }); - })); - + journalBuilder.AddWriteEventAdapter("mapper1", new Type[] { typeof(Event1) }); + journalBuilder.AddReadEventAdapter("reader1", new Type[] { typeof(Event1) }); + journalBuilder.AddEventAdapter("combo", boundTypes: new Type[] { typeof(Event2) }); + journalBuilder.AddWriteEventAdapter("tagger", + boundTypes: new Type[] { typeof(Event1), typeof(Event2) }); + }); + + return Task.CompletedTask; + } + + [Fact] + public void Should_use_correct_EventAdapter_bindings() + { // act - var sys = host.Services.GetRequiredService(); - var config = sys.Settings.Config; + var config = Sys.Settings.Config; var sqlPersistenceJournal = config.GetConfig("akka.persistence.journal.sql-server"); // assert diff --git a/src/Akka.Persistence.Hosting.Tests/InMemoryPersistenceSpecs.cs b/src/Akka.Persistence.Hosting.Tests/InMemoryPersistenceSpecs.cs index c20dac63..f770da67 100644 --- a/src/Akka.Persistence.Hosting.Tests/InMemoryPersistenceSpecs.cs +++ b/src/Akka.Persistence.Hosting.Tests/InMemoryPersistenceSpecs.cs @@ -13,7 +13,7 @@ namespace Akka.Persistence.Hosting.Tests { - public class InMemoryPersistenceSpecs + public class InMemoryPersistenceSpecs: Akka.Hosting.TestKit.TestKit { private readonly ITestOutputHelper _output; @@ -76,30 +76,26 @@ public static async Task StartHost(Action testSetup) await host.StartAsync(); return host; } + + protected override Task ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + builder + .WithInMemoryJournal() + .WithInMemorySnapshotStore() + .StartActors((system, registry) => + { + var myActor = system.ActorOf(Props.Create(() => new MyPersistenceActor("ac1")), "actor1"); + registry.Register(myActor); + }); + + return Task.CompletedTask; + } [Fact] public async Task Should_Start_ActorSystem_wth_InMemory_Persistence() { // arrange - using var host = await StartHost(collection => collection.AddAkka("MySys", builder => - { - builder.WithInMemoryJournal().WithInMemorySnapshotStore() - .StartActors((system, registry) => - { - var myActor = system.ActorOf(Props.Create(() => new MyPersistenceActor("ac1")), "actor1"); - registry.Register(myActor); - }) - .WithActors((system, registry) => - { - var extSystem = (ExtendedActorSystem)system; - var logger = extSystem.SystemActorOf(Props.Create(() => new TestOutputLogger(_output)), "log-test"); - logger.Tell(new InitializeLogger(system.EventStream)); - });; - })); - - var actorSystem = host.Services.GetRequiredService(); - var actorRegistry = host.Services.GetRequiredService(); - var myPersistentActor = actorRegistry.Get(); + var myPersistentActor = ActorRegistry.Get(); // act var resp1 = await myPersistentActor.Ask(1, TimeSpan.FromSeconds(3)); @@ -111,13 +107,13 @@ public async Task Should_Start_ActorSystem_wth_InMemory_Persistence() // kill + recreate actor with same PersistentId await myPersistentActor.GracefulStop(TimeSpan.FromSeconds(3)); - var myPersistentActor2 = actorSystem.ActorOf(Props.Create(() => new MyPersistenceActor("ac1")), "actor1a"); + var myPersistentActor2 = Sys.ActorOf(Props.Create(() => new MyPersistenceActor("ac1")), "actor1a"); var snapshot2 = await myPersistentActor2.Ask("getall", TimeSpan.FromSeconds(3)); snapshot2.Should().BeEquivalentTo(new[] {1, 2}); // validate configs - var config = actorSystem.Settings.Config; + var config = Sys.Settings.Config; config.GetString("akka.persistence.journal.plugin").Should().Be("akka.persistence.journal.inmem"); config.GetString("akka.persistence.snapshot-store.plugin").Should().Be("akka.persistence.snapshot-store.inmem"); }