From 84fc0464ba81acc8270d6850ccdfee88676254ea Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Fri, 29 Apr 2022 02:33:14 +0700 Subject: [PATCH 1/2] Convert Akka.Remote.Tests to async - Transport.ThrottlerTransportAdapterSpec --- .../Akka.TestKit.Xunit2.csproj | 3 +- .../Extensions/TaskExtensions.cs | 82 +++++++++++++++++++ .../ThrottlerTransportAdapterSpec.cs | 78 ++++++++---------- 3 files changed, 116 insertions(+), 47 deletions(-) create mode 100644 src/contrib/testkits/Akka.TestKit.Xunit2/Extensions/TaskExtensions.cs diff --git a/src/contrib/testkits/Akka.TestKit.Xunit2/Akka.TestKit.Xunit2.csproj b/src/contrib/testkits/Akka.TestKit.Xunit2/Akka.TestKit.Xunit2.csproj index e51a05c42f8..94bc9fb6ade 100644 --- a/src/contrib/testkits/Akka.TestKit.Xunit2/Akka.TestKit.Xunit2.csproj +++ b/src/contrib/testkits/Akka.TestKit.Xunit2/Akka.TestKit.Xunit2.csproj @@ -4,7 +4,7 @@ Akka.TestKit.Xunit2 TestKit for writing tests for Akka.NET using xUnit. - $(NetStandardLibVersion) + $(NetStandardLibVersion) $(AkkaPackageTags);testkit;xunit true @@ -12,6 +12,7 @@ + diff --git a/src/contrib/testkits/Akka.TestKit.Xunit2/Extensions/TaskExtensions.cs b/src/contrib/testkits/Akka.TestKit.Xunit2/Extensions/TaskExtensions.cs new file mode 100644 index 00000000000..cf2ced4f3ea --- /dev/null +++ b/src/contrib/testkits/Akka.TestKit.Xunit2/Extensions/TaskExtensions.cs @@ -0,0 +1,82 @@ +// //----------------------------------------------------------------------- +// // +// // Copyright (C) 2009-2022 Lightbend Inc. +// // Copyright (C) 2013-2022 .NET Foundation +// // +// //----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using FluentAssertions; +using static FluentAssertions.FluentActions; + +namespace Akka.TestKit.Xunit2.Extensions +{ + public static class TaskExtensions + { + /// + /// Guard a with a timeout and checks to see if + /// the matches the provided expected value. + /// + /// The Task to be guarded + /// The expected Task.Result + /// The allowed time span for the operation. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + /// + public static async Task ShouldCompleteWithin( + this Task task, T expected, TimeSpan timeout, string because = "", params object[] becauseArgs) + { + await Awaiting(async () => + { + var result = await task; + result.Should().Be(expected); + }).Should().CompleteWithinAsync(timeout, because, becauseArgs); + } + + /// + /// Guard a with a timeout and returns the . + /// + /// The Task to be guarded + /// The allowed time span for the operation. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + /// + public static async Task ShouldCompleteWithin( + this Task task, TimeSpan timeout, string because = "", params object[] becauseArgs) + { + return (await Awaiting(async () => await task).Should().CompleteWithinAsync(timeout), because, becauseArgs) + .Item1.Subject; + } + + /// + /// Guard a with a timeout. + /// + /// The Task to be guarded + /// The allowed time span for the operation. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + /// + public static async Task ShouldCompleteWithin( + this Task task, TimeSpan timeout, string because = "", params object[] becauseArgs) + { + await Awaiting(async () => await task).Should().CompleteWithinAsync(timeout, because, becauseArgs); + } + + } +} \ No newline at end of file diff --git a/src/core/Akka.Remote.Tests/Transport/ThrottlerTransportAdapterSpec.cs b/src/core/Akka.Remote.Tests/Transport/ThrottlerTransportAdapterSpec.cs index c8cd4be9a71..0dc10727b77 100644 --- a/src/core/Akka.Remote.Tests/Transport/ThrottlerTransportAdapterSpec.cs +++ b/src/core/Akka.Remote.Tests/Transport/ThrottlerTransportAdapterSpec.cs @@ -6,7 +6,6 @@ //----------------------------------------------------------------------- using System; -using System.Runtime.CompilerServices; using System.Text.RegularExpressions; using System.Threading.Tasks; using Akka.Actor; @@ -16,12 +15,13 @@ using Akka.TestKit.Internal; using Akka.TestKit.Internal.StringMatcher; using Akka.TestKit.TestEvent; +using Akka.TestKit.Xunit2.Extensions; using Akka.Util; using Akka.Util.Internal; using FluentAssertions; +using FluentAssertions.Extensions; using Xunit; using Xunit.Abstractions; -using static FluentAssertions.FluentActions; namespace Akka.Remote.Tests.Transport { @@ -29,7 +29,7 @@ public class ThrottlerTransportAdapterSpec : AkkaSpec { #region Setup / Config - public static Config ThrottlerTransportAdapterSpecConfig + private static Config ThrottlerTransportAdapterSpecConfig { get { @@ -53,12 +53,12 @@ public static Config ThrottlerTransportAdapterSpecConfig private const int PingPacketSize = 350; private const int MessageCount = 15; private const int BytesPerSecond = 700; - private static readonly long TotalTime = (MessageCount * PingPacketSize) / BytesPerSecond; + private const long TotalTime = (MessageCount * PingPacketSize) / BytesPerSecond; public class ThrottlingTester : ReceiveActor { - private IActorRef _remoteRef; - private IActorRef _controller; + private readonly IActorRef _remoteRef; + private readonly IActorRef _controller; private int _received = 0; private int _messageCount = MessageCount; @@ -110,7 +110,7 @@ public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) return true; - return obj is Lost && Equals((Lost) obj); + return obj is Lost lost && Equals(lost); } public override int GetHashCode() @@ -140,14 +140,15 @@ protected override void OnReceive(object message) private readonly ActorSystem _systemB; private readonly IActorRef _remote; + private TimeSpan DefaultTimeout => Dilated(TestKitSettings.DefaultTimeout); + private RootActorPath RootB - { - get { return new RootActorPath(_systemB.AsInstanceOf().Provider.DefaultAddress); } - } + => new RootActorPath(_systemB.AsInstanceOf().Provider.DefaultAddress); private async Task Here() { - var identity = await Sys.ActorSelection(RootB / "user" / "echo").Ask(new Identify(null)); + var identity = await Sys.ActorSelection(RootB / "user" / "echo").Ask(new Identify(null)) + .ShouldCompleteWithin(DefaultTimeout); return identity.Subject; } @@ -157,7 +158,8 @@ private async Task Throttle(ThrottleTransportAdapter.Direction direction, var transport = Sys.AsInstanceOf().Provider.AsInstanceOf().Transport; - return await transport.ManagementCommand(new SetThrottle(rootBAddress, direction, mode)); + return await transport.ManagementCommand(new SetThrottle(rootBAddress, direction, mode)) + .ShouldCompleteWithin(DefaultTimeout); } private async Task Disassociate() @@ -166,7 +168,8 @@ private async Task Disassociate() var transport = Sys.AsInstanceOf().Provider.AsInstanceOf().Transport; - return await transport.ManagementCommand(new ForceDisassociate(rootBAddress)); + return await transport.ManagementCommand(new ForceDisassociate(rootBAddress)) + .ShouldCompleteWithin(DefaultTimeout); } #endregion @@ -183,23 +186,21 @@ public ThrottlerTransportAdapterSpec(ITestOutputHelper output) [Fact] public async Task ThrottlerTransportAdapter_must_maintain_average_message_rate() { - await ShouldCompleteWithin( - () => Throttle( + await Throttle( ThrottleTransportAdapter.Direction.Send, - new Remote.Transport.TokenBucket(PingPacketSize * 4, BytesPerSecond, 0, 0)), - true, TimeSpan.FromSeconds(3)); - + new Remote.Transport.TokenBucket(PingPacketSize * 4, BytesPerSecond, 0, 0)) + .ShouldCompleteWithin(true, TimeSpan.FromSeconds(3)); + var here = await Here(); var tester = Sys.ActorOf(Props.Create(() => new ThrottlingTester(here, TestActor))); tester.Tell("start"); - var time = TimeSpan.FromTicks(ExpectMsg(TimeSpan.FromSeconds(TotalTime + 12))).TotalSeconds; + var time = TimeSpan.FromTicks(await ExpectMsgAsync(TimeSpan.FromSeconds(TotalTime + 12))).TotalSeconds; Log.Warning("Total time of transmission: {0}", time); time.Should().BeGreaterThan(TotalTime - 12); - - await ShouldCompleteWithin( - () => Throttle(ThrottleTransportAdapter.Direction.Send, Unthrottled.Instance), - true, TimeSpan.FromSeconds(3)); + + await Throttle(ThrottleTransportAdapter.Direction.Send, Unthrottled.Instance) + .ShouldCompleteWithin(true, TimeSpan.FromSeconds(3)); } [Fact] @@ -208,25 +209,21 @@ public async Task ThrottlerTransportAdapter_must_survive_blackholing() var here = await Here(); here.Tell(new ThrottlingTester.Lost("BlackHole 1")); - ExpectMsg(new ThrottlingTester.Lost("BlackHole 1")); + await ExpectMsgAsync(new ThrottlingTester.Lost("BlackHole 1")); MuteDeadLetters(typeof(ThrottlingTester.Lost)); MuteDeadLetters(_systemB, typeof(ThrottlingTester.Lost)); - await ShouldCompleteWithin( - func: () => Throttle(ThrottleTransportAdapter.Direction.Both, Blackhole.Instance), - expected: true, - timeout: TimeSpan.FromSeconds(3)); + await Throttle(ThrottleTransportAdapter.Direction.Both, Blackhole.Instance) + .ShouldCompleteWithin(true, 3.Seconds()); here.Tell(new ThrottlingTester.Lost("BlackHole 2")); await ExpectNoMsgAsync(TimeSpan.FromSeconds(1)); - await ShouldCompleteWithin(Disassociate, true, TimeSpan.FromSeconds(3)); + await Disassociate().ShouldCompleteWithin(true, TimeSpan.FromSeconds(3)); await ExpectNoMsgAsync(TimeSpan.FromSeconds(1)); - await ShouldCompleteWithin( - func: () => Throttle(ThrottleTransportAdapter.Direction.Both, Unthrottled.Instance), - expected: true, - timeout: TimeSpan.FromSeconds(3)); + await Throttle(ThrottleTransportAdapter.Direction.Both, Unthrottled.Instance) + .ShouldCompleteWithin(true, TimeSpan.FromSeconds(3)); // after we remove the Blackhole we can't be certain of the state // of the connection, repeat until success @@ -246,17 +243,6 @@ await AwaitConditionAsync(async () => await FishForMessageAsync(o => o.Equals("Cleanup"), TimeSpan.FromSeconds(5)); } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private static async Task ShouldCompleteWithin(Func> func, T expected, TimeSpan timeout) - { - T result = default; - await Awaiting(async () => - { - result = await func(); - }).Should().CompleteWithinAsync(timeout); - result.Should().Be(expected); - } - #endregion #region Cleanup @@ -271,10 +257,10 @@ protected override async Task BeforeTerminationAsync() await base.BeforeTerminationAsync(); } - protected override async Task AfterTerminationAsync() + protected override async Task AfterAllAsync() { + await base.AfterAllAsync(); await ShutdownAsync(_systemB); - await base.AfterTerminationAsync(); } #endregion From c8757de2c369df7e72526a9e5df62e8f456421e3 Mon Sep 17 00:00:00 2001 From: Gregorius Soedharmo Date: Sat, 30 Apr 2022 05:14:03 +0700 Subject: [PATCH 2/2] Refactor FluentAssertion to TestKit --- .../Akka.TestKit.Xunit2.csproj | 1 - .../Extensions/TaskExtensions.cs | 82 ------------------- .../ThrottlerTransportAdapterSpec.cs | 2 +- src/core/Akka.TestKit/Akka.TestKit.csproj | 1 + .../Akka.TestKit/Extensions/TaskExtensions.cs | 65 +++++++++++++++ 5 files changed, 67 insertions(+), 84 deletions(-) delete mode 100644 src/contrib/testkits/Akka.TestKit.Xunit2/Extensions/TaskExtensions.cs diff --git a/src/contrib/testkits/Akka.TestKit.Xunit2/Akka.TestKit.Xunit2.csproj b/src/contrib/testkits/Akka.TestKit.Xunit2/Akka.TestKit.Xunit2.csproj index 94bc9fb6ade..91d1b9900d8 100644 --- a/src/contrib/testkits/Akka.TestKit.Xunit2/Akka.TestKit.Xunit2.csproj +++ b/src/contrib/testkits/Akka.TestKit.Xunit2/Akka.TestKit.Xunit2.csproj @@ -12,7 +12,6 @@ - diff --git a/src/contrib/testkits/Akka.TestKit.Xunit2/Extensions/TaskExtensions.cs b/src/contrib/testkits/Akka.TestKit.Xunit2/Extensions/TaskExtensions.cs deleted file mode 100644 index cf2ced4f3ea..00000000000 --- a/src/contrib/testkits/Akka.TestKit.Xunit2/Extensions/TaskExtensions.cs +++ /dev/null @@ -1,82 +0,0 @@ -// //----------------------------------------------------------------------- -// // -// // Copyright (C) 2009-2022 Lightbend Inc. -// // Copyright (C) 2013-2022 .NET Foundation -// // -// //----------------------------------------------------------------------- - -using System; -using System.Threading.Tasks; -using FluentAssertions; -using static FluentAssertions.FluentActions; - -namespace Akka.TestKit.Xunit2.Extensions -{ - public static class TaskExtensions - { - /// - /// Guard a with a timeout and checks to see if - /// the matches the provided expected value. - /// - /// The Task to be guarded - /// The expected Task.Result - /// The allowed time span for the operation. - /// - /// A formatted phrase as is supported by explaining why the assertion - /// is needed. If the phrase does not start with the word because, it is prepended automatically. - /// - /// - /// Zero or more objects to format using the placeholders in . - /// - /// - public static async Task ShouldCompleteWithin( - this Task task, T expected, TimeSpan timeout, string because = "", params object[] becauseArgs) - { - await Awaiting(async () => - { - var result = await task; - result.Should().Be(expected); - }).Should().CompleteWithinAsync(timeout, because, becauseArgs); - } - - /// - /// Guard a with a timeout and returns the . - /// - /// The Task to be guarded - /// The allowed time span for the operation. - /// - /// A formatted phrase as is supported by explaining why the assertion - /// is needed. If the phrase does not start with the word because, it is prepended automatically. - /// - /// - /// Zero or more objects to format using the placeholders in . - /// - /// - public static async Task ShouldCompleteWithin( - this Task task, TimeSpan timeout, string because = "", params object[] becauseArgs) - { - return (await Awaiting(async () => await task).Should().CompleteWithinAsync(timeout), because, becauseArgs) - .Item1.Subject; - } - - /// - /// Guard a with a timeout. - /// - /// The Task to be guarded - /// The allowed time span for the operation. - /// - /// A formatted phrase as is supported by explaining why the assertion - /// is needed. If the phrase does not start with the word because, it is prepended automatically. - /// - /// - /// Zero or more objects to format using the placeholders in . - /// - /// - public static async Task ShouldCompleteWithin( - this Task task, TimeSpan timeout, string because = "", params object[] becauseArgs) - { - await Awaiting(async () => await task).Should().CompleteWithinAsync(timeout, because, becauseArgs); - } - - } -} \ No newline at end of file diff --git a/src/core/Akka.Remote.Tests/Transport/ThrottlerTransportAdapterSpec.cs b/src/core/Akka.Remote.Tests/Transport/ThrottlerTransportAdapterSpec.cs index 0dc10727b77..a4052153bda 100644 --- a/src/core/Akka.Remote.Tests/Transport/ThrottlerTransportAdapterSpec.cs +++ b/src/core/Akka.Remote.Tests/Transport/ThrottlerTransportAdapterSpec.cs @@ -12,10 +12,10 @@ using Akka.Configuration; using Akka.Remote.Transport; using Akka.TestKit; +using Akka.TestKit.Extensions; using Akka.TestKit.Internal; using Akka.TestKit.Internal.StringMatcher; using Akka.TestKit.TestEvent; -using Akka.TestKit.Xunit2.Extensions; using Akka.Util; using Akka.Util.Internal; using FluentAssertions; diff --git a/src/core/Akka.TestKit/Akka.TestKit.csproj b/src/core/Akka.TestKit/Akka.TestKit.csproj index f56c969e28c..bfb849e635d 100644 --- a/src/core/Akka.TestKit/Akka.TestKit.csproj +++ b/src/core/Akka.TestKit/Akka.TestKit.csproj @@ -32,6 +32,7 @@ + diff --git a/src/core/Akka.TestKit/Extensions/TaskExtensions.cs b/src/core/Akka.TestKit/Extensions/TaskExtensions.cs index 9da72e2f4f8..f40eec166ca 100644 --- a/src/core/Akka.TestKit/Extensions/TaskExtensions.cs +++ b/src/core/Akka.TestKit/Extensions/TaskExtensions.cs @@ -2,6 +2,8 @@ using System.Runtime.ExceptionServices; using System.Threading; using System.Threading.Tasks; +using FluentAssertions; +using static FluentAssertions.FluentActions; namespace Akka.TestKit.Extensions { @@ -64,5 +66,68 @@ public static async Task WithTimeout(this Task parentTask, TimeSpan tim } } + /// + /// Guard a with a timeout and checks to see if + /// the matches the provided expected value. + /// + /// The Task to be guarded + /// The expected Task.Result + /// The allowed time span for the operation. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + /// + public static async Task ShouldCompleteWithin( + this Task task, T expected, TimeSpan timeout, string because = "", params object[] becauseArgs) + { + await Awaiting(async () => + { + var result = await task; + result.Should().Be(expected); + }).Should().CompleteWithinAsync(timeout, because, becauseArgs); + } + + /// + /// Guard a with a timeout and returns the . + /// + /// The Task to be guarded + /// The allowed time span for the operation. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + /// + public static async Task ShouldCompleteWithin( + this Task task, TimeSpan timeout, string because = "", params object[] becauseArgs) + { + return (await Awaiting(async () => await task).Should().CompleteWithinAsync(timeout), because, becauseArgs) + .Item1.Subject; + } + + /// + /// Guard a with a timeout. + /// + /// The Task to be guarded + /// The allowed time span for the operation. + /// + /// A formatted phrase as is supported by explaining why the assertion + /// is needed. If the phrase does not start with the word because, it is prepended automatically. + /// + /// + /// Zero or more objects to format using the placeholders in . + /// + /// + public static async Task ShouldCompleteWithin( + this Task task, TimeSpan timeout, string because = "", params object[] becauseArgs) + { + await Awaiting(async () => await task).Should().CompleteWithinAsync(timeout, because, becauseArgs); + } } }