diff --git a/src/core/Akka.Tests/Actor/ActorLifeCycleSpec.cs b/src/core/Akka.Tests/Actor/ActorLifeCycleSpec.cs index 0505eeb73d0..e3c8a8faf52 100644 --- a/src/core/Akka.Tests/Actor/ActorLifeCycleSpec.cs +++ b/src/core/Akka.Tests/Actor/ActorLifeCycleSpec.cs @@ -213,31 +213,21 @@ public class Become } public class BecomeActor : UntypedActor { - private IActorRef testActor; + private readonly IActorRef _testActor; public BecomeActor(IActorRef testActor) { - this.testActor = testActor; - } - - protected override void PreRestart(Exception cause, object message) - { - base.PreRestart(cause, message); - } - - protected override void PostRestart(Exception cause) - { - base.PostRestart(cause); + _testActor = testActor; } protected override void OnReceive(object message) { if (message is Become) { Context.Become(OnBecome); - testActor.Tell("ok"); + _testActor.Tell("ok"); } else { - testActor.Tell(42); + _testActor.Tell(42); } } @@ -249,7 +239,7 @@ protected void OnBecome(object message) } else { - testActor.Tell(43); + _testActor.Tell(43); } } } @@ -266,7 +256,12 @@ public async Task Clear_behavior_stack_upon_restart() a.Tell("hello"); await ExpectMsgAsync(43); - await EventFilter.Exception("buh").ExpectOneAsync(async () => a.Tell("fail")); + await EventFilter.Exception("buh").ExpectOneAsync(async () => + { + a.Tell("fail"); + await Task.CompletedTask; + }); + a.Tell("hello"); await ExpectMsgAsync(42); } diff --git a/src/core/Akka.Tests/Actor/GracefulStopSpecs.cs b/src/core/Akka.Tests/Actor/GracefulStopSpecs.cs new file mode 100644 index 00000000000..810eb4388fe --- /dev/null +++ b/src/core/Akka.Tests/Actor/GracefulStopSpecs.cs @@ -0,0 +1,71 @@ +//----------------------------------------------------------------------- +// +// Copyright (C) 2009-2022 Lightbend Inc. +// Copyright (C) 2013-2022 .NET Foundation +// +//----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Actor.Dsl; +using Akka.TestKit; +using Akka.TestKit.TestActors; +using Xunit; +using FluentAssertions; + +namespace Akka.Tests.Actor +{ + public class GracefulStopSpecs : AkkaSpec + { + [Fact(DisplayName = "GracefulStop should terminate target actor on-time")] + public async Task GracefulStopShouldTerminateOnTime() + { + // arrange + var actor = Sys.ActorOf(BlackHoleActor.Props); + Watch(actor); + + // act + var stopped = await actor.GracefulStop(TimeSpan.FromSeconds(3)); + await ExpectTerminatedAsync(actor); + + // assert + stopped.Should().BeTrue(); + } + + [Fact(DisplayName = "GracefulStop should return true for an already terminated actor")] + public async Task GracefulStopShouldReturnTrueForAlreadyDeadActor() + { + // arrange + var actor = Sys.ActorOf(BlackHoleActor.Props); + Watch(actor); + + // act + Sys.Stop(actor); + await ExpectTerminatedAsync(actor); + var stopped = await actor.GracefulStop(TimeSpan.FromSeconds(3)); + + // assert + stopped.Should().BeTrue(); + } + + private class CustomShutdown{} + + [Fact(DisplayName = "GracefulStop should return false if shutdown goes overtime", Skip = "GracefulStop currently throws a TaskCancellationException, which seems wrong")] + public async Task GracefulStopShouldThrowIfShutdownGoesOvertime() + { + // arrange + var actor = Sys.ActorOf(act => act.ReceiveAsync(async (a, ctx) => + { + await Task.Delay(200); + ctx.Stop(ctx.Self); + })); + + // act + var stopped = await actor.GracefulStop(TimeSpan.FromMilliseconds(10), new CustomShutdown()); + + // assert + stopped.Should().BeFalse(); + } + } +} \ No newline at end of file