diff --git a/Directory.Build.props b/Directory.Build.props index 57ba700d..50381c5f 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -31,7 +31,7 @@ 6.0.3 3.1.5 3.1.5 - 1.5.67 + 1.5.68 [9.0.0,) [9.0.0,) diff --git a/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveTestKit.verified.txt b/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveTestKit.verified.txt index 1baf252f..d77b749e 100644 --- a/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveTestKit.verified.txt +++ b/src/Akka.Hosting.API.Tests/verify/CoreApiSpec.ApproveTestKit.verified.txt @@ -43,6 +43,7 @@ namespace Akka.Hosting.TestKit public System.Threading.Tasks.Task WithSnapshotSave(System.Func behaviorSelector, System.Action execution) { } public System.Threading.Tasks.Task WithSnapshotSave(System.Func behaviorSelector, System.Func execution) { } } + [Akka.TestKit.Xunit.Attributes.AkkaCleanAmbientContext] public abstract class TestKit : Akka.TestKit.TestKitBase, System.IAsyncDisposable, Xunit.IAsyncLifetime { protected TestKit(string? actorSystemName = null, Xunit.ITestOutputHelper? output = null, System.TimeSpan? startupTimeout = default, Microsoft.Extensions.Logging.LogLevel logLevel = 2) { } diff --git a/src/Akka.Hosting.TestKit.Tests/ParallelAmbientContextSpec.cs b/src/Akka.Hosting.TestKit.Tests/ParallelAmbientContextSpec.cs new file mode 100644 index 00000000..0842fba6 --- /dev/null +++ b/src/Akka.Hosting.TestKit.Tests/ParallelAmbientContextSpec.cs @@ -0,0 +1,83 @@ +// ----------------------------------------------------------------------- +// +// Copyright (C) 2013-2022 .NET Foundation +// +// ----------------------------------------------------------------------- + +using System; +using System.Threading.Tasks; +using Akka.Actor; +using Akka.Actor.Internal; +using Akka.TestKit; +using Xunit; + +namespace Akka.Hosting.TestKit.Tests; + +public abstract class ParallelAmbientContextSpecBase : TestKit +{ + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + } + + [Fact] + public async Task Implicit_sender_should_resolve_to_own_TestActor() + { + TestActor.Tell("ping"); + await ExpectMsgAsync( + "ping", + TimeSpan.FromSeconds(5), + cancellationToken: TestContext.Current.CancellationToken); + Assert.Equal(TestActor, LastSender); + + await Task.Yield(); + TestActor.Tell("ping-after-yield"); + await ExpectMsgAsync( + "ping-after-yield", + TimeSpan.FromSeconds(5), + cancellationToken: TestContext.Current.CancellationToken); + Assert.Equal(TestActor, LastSender); + } +} + +public class ParallelAmbientContextSpec01 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec02 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec03 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec04 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec05 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec06 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec07 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec08 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec09 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec10 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec11 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec12 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec13 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec14 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec15 : ParallelAmbientContextSpecBase { } +public class ParallelAmbientContextSpec16 : ParallelAmbientContextSpecBase { } + +public abstract class ParallelNoImplicitSenderSpecBase : TestKit, INoImplicitSender +{ + protected override void ConfigureAkka(AkkaConfigurationBuilder builder, IServiceProvider provider) + { + } + + [Fact] + public async Task Current_should_be_null_both_pre_and_post_await() + { + Assert.Null(InternalCurrentActorCellKeeper.Current); + await Task.Yield(); + Assert.Null(InternalCurrentActorCellKeeper.Current); + await Task.Yield(); + Assert.Null(InternalCurrentActorCellKeeper.Current); + } +} + +public class ParallelNoImplicitSenderSpec01 : ParallelNoImplicitSenderSpecBase { } +public class ParallelNoImplicitSenderSpec02 : ParallelNoImplicitSenderSpecBase { } +public class ParallelNoImplicitSenderSpec03 : ParallelNoImplicitSenderSpecBase { } +public class ParallelNoImplicitSenderSpec04 : ParallelNoImplicitSenderSpecBase { } +public class ParallelNoImplicitSenderSpec05 : ParallelNoImplicitSenderSpecBase { } +public class ParallelNoImplicitSenderSpec06 : ParallelNoImplicitSenderSpecBase { } +public class ParallelNoImplicitSenderSpec07 : ParallelNoImplicitSenderSpecBase { } +public class ParallelNoImplicitSenderSpec08 : ParallelNoImplicitSenderSpecBase { } diff --git a/src/Akka.Hosting.TestKit.Tests/Properties/AssemblyInfo.cs b/src/Akka.Hosting.TestKit.Tests/Properties/AssemblyInfo.cs index e2336be8..d7439b5d 100644 --- a/src/Akka.Hosting.TestKit.Tests/Properties/AssemblyInfo.cs +++ b/src/Akka.Hosting.TestKit.Tests/Properties/AssemblyInfo.cs @@ -32,4 +32,4 @@ // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: CollectionBehavior(CollectionBehavior.CollectionPerAssembly, DisableTestParallelization = true)] +[assembly: CollectionBehavior(CollectionBehavior.CollectionPerClass)] diff --git a/src/Akka.Hosting.TestKit.Tests/xunit.runner.json b/src/Akka.Hosting.TestKit.Tests/xunit.runner.json index 4a73b1e5..4ba486b0 100644 --- a/src/Akka.Hosting.TestKit.Tests/xunit.runner.json +++ b/src/Akka.Hosting.TestKit.Tests/xunit.runner.json @@ -1,6 +1,6 @@ { "$schema": "https://xunit.github.io/schema/current/xunit.runner.schema.json", "longRunningTestSeconds": 60, - "parallelizeAssembly": false, - "parallelizeTestCollections": false + "parallelizeAssembly": true, + "parallelizeTestCollections": true } \ No newline at end of file diff --git a/src/Akka.Hosting.TestKit/TestKit.Shared.cs b/src/Akka.Hosting.TestKit/TestKit.Shared.cs index 940ba95e..11f421e1 100644 --- a/src/Akka.Hosting.TestKit/TestKit.Shared.cs +++ b/src/Akka.Hosting.TestKit/TestKit.Shared.cs @@ -54,7 +54,7 @@ public IHost Host /// private void EnsureImplicitSender() { - if (this is not INoImplicitSender && TestActor != null) + if (this is not INoImplicitSender && InternalCurrentActorCellKeeper.Current == null && TestActor != null) InternalCurrentActorCellKeeper.Current = (ActorCell)((ActorRefWithCell)TestActor).Underlying; } @@ -192,12 +192,6 @@ private async Task InitializeAsyncCore() // TestActor initialization and registration now happens in AddStartup // before user actors are created, preventing race conditions - // ALWAYS set the implicit sender context on the current thread after initialization - // This ensures it's available on the thread where tests will run - // This is critical for tests using DI-created actors - if (this is not INoImplicitSender && TestActor != null) - InternalCurrentActorCellKeeper.Current = (ActorCell)((ActorRefWithCell)TestActor).Underlying; - await BeforeTestStart(); } @@ -221,11 +215,6 @@ protected sealed override void InitializeTest(ActorSystem system, ActorSystemSet protected virtual Task BeforeTestStart() { - // Ensure the implicit sender is set on the current thread before each test - // This is critical because tests may run on different threads than initialization - if (this is not INoImplicitSender) - InternalCurrentActorCellKeeper.Current = (ActorCell)((ActorRefWithCell)TestActor).Underlying; - return Task.CompletedTask; } diff --git a/src/Akka.Hosting.TestKit/TestKit.cs b/src/Akka.Hosting.TestKit/TestKit.cs index 922ee6df..a0a28b05 100644 --- a/src/Akka.Hosting.TestKit/TestKit.cs +++ b/src/Akka.Hosting.TestKit/TestKit.cs @@ -7,10 +7,12 @@ using System; using System.Threading.Tasks; using Akka.Annotations; +using Akka.TestKit.Xunit.Attributes; using Xunit; namespace Akka.Hosting.TestKit { + [AkkaCleanAmbientContext] public abstract partial class TestKit : IAsyncLifetime, IAsyncDisposable { [InternalApi]