Skip to content

Commit 6afdd48

Browse files
Merge branch 'dev' into optional-snapshots
2 parents 0db3ecf + 26383ec commit 6afdd48

File tree

10 files changed

+173
-34
lines changed

10 files changed

+173
-34
lines changed

src/Akka.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClusterToolsExample.Seed",
288288
EndProject
289289
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ClusterToolsExample.Node", "examples\Cluster\ClusterTools\ClusterToolsExample.Node\ClusterToolsExample.Node.csproj", "{337A85B5-4A7C-4883-8634-46E7E52A765F}"
290290
EndProject
291+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Akka.TestKit.Xunit2.Tests", "contrib\testkits\Akka.TestKit.Xunit2.Tests\Akka.TestKit.Xunit2.Tests.csproj", "{95017C99-E960-44E5-83AD-BF21461DF06F}"
292+
EndProject
291293
Global
292294
GlobalSection(SolutionConfigurationPlatforms) = preSolution
293295
Debug|Any CPU = Debug|Any CPU
@@ -1343,6 +1345,18 @@ Global
13431345
{337A85B5-4A7C-4883-8634-46E7E52A765F}.Release|x64.Build.0 = Release|Any CPU
13441346
{337A85B5-4A7C-4883-8634-46E7E52A765F}.Release|x86.ActiveCfg = Release|Any CPU
13451347
{337A85B5-4A7C-4883-8634-46E7E52A765F}.Release|x86.Build.0 = Release|Any CPU
1348+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
1349+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Debug|Any CPU.Build.0 = Debug|Any CPU
1350+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Debug|x64.ActiveCfg = Debug|Any CPU
1351+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Debug|x64.Build.0 = Debug|Any CPU
1352+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Debug|x86.ActiveCfg = Debug|Any CPU
1353+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Debug|x86.Build.0 = Debug|Any CPU
1354+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Release|Any CPU.ActiveCfg = Release|Any CPU
1355+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Release|Any CPU.Build.0 = Release|Any CPU
1356+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Release|x64.ActiveCfg = Release|Any CPU
1357+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Release|x64.Build.0 = Release|Any CPU
1358+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Release|x86.ActiveCfg = Release|Any CPU
1359+
{95017C99-E960-44E5-83AD-BF21461DF06F}.Release|x86.Build.0 = Release|Any CPU
13461360
EndGlobalSection
13471361
GlobalSection(SolutionProperties) = preSolution
13481362
HideSolutionNode = FALSE
@@ -1469,6 +1483,7 @@ Global
14691483
{88D7D845-2F50-4D37-9026-B0A8353D0E8D} = {7735F35A-E7B7-44DE-B6FB-C770B53EB69C}
14701484
{ED00E6F4-2B5C-4F16-ADE4-45E4A73C17B8} = {7735F35A-E7B7-44DE-B6FB-C770B53EB69C}
14711485
{337A85B5-4A7C-4883-8634-46E7E52A765F} = {7735F35A-E7B7-44DE-B6FB-C770B53EB69C}
1486+
{95017C99-E960-44E5-83AD-BF21461DF06F} = {7625FD95-4B2C-4A5B-BDD5-94B1493FAC8E}
14721487
EndGlobalSection
14731488
GlobalSection(ExtensibilityGlobals) = postSolution
14741489
SolutionGuid = {03AD8E21-7507-4E68-A4E9-F4A7E7273164}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<Import Project="../../../xunitSettings.props" />
4+
5+
<PropertyGroup>
6+
<TargetFrameworks>$(NetFrameworkTestVersion);$(NetTestVersion)</TargetFrameworks>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
11+
<PackageReference Include="xunit" Version="$(XunitVersion)" />
12+
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitVersion)" />
13+
<ProjectReference Include="../Akka.TestKit.Xunit2/Akka.TestKit.Xunit2.csproj" />
14+
</ItemGroup>
15+
16+
</Project>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="AkkaEqualException.cs" company="Akka.NET Project">
3+
// Copyright (C) 2009-2025 Lightbend Inc. <http://www.lightbend.com>
4+
// Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
5+
// </copyright>
6+
// -----------------------------------------------------------------------
7+
8+
using Akka.TestKit.Xunit2.Internals;
9+
using Xunit;
10+
11+
namespace Akka.TestKit.Xunit2.Tests.Internals;
12+
13+
public static class AkkaEqualExceptionSpec
14+
{
15+
#if NETFRAMEWORK
16+
[Fact]
17+
public static void Constructor_deserializes_message()
18+
{
19+
var originalException = new AkkaEqualException("Test message");
20+
21+
AkkaEqualException deserializedException;
22+
using (var memoryStream = new System.IO.MemoryStream())
23+
{
24+
var formatter = new System.Runtime.Serialization.Formatters.Binary.BinaryFormatter();
25+
formatter.Serialize(memoryStream, originalException);
26+
memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
27+
deserializedException = (AkkaEqualException)formatter.Deserialize(memoryStream);
28+
}
29+
30+
Assert.Equal(originalException.Message, deserializedException.Message);
31+
}
32+
#endif
33+
}
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="XunitAssertionsTests.cs" company="Akka.NET Project">
3+
// Copyright (C) 2009-2025 Lightbend Inc. <http://www.lightbend.com>
4+
// Copyright (C) 2013-2025 .NET Foundation <https://github.com/akkadotnet/akka.net>
5+
// </copyright>
6+
// -----------------------------------------------------------------------
7+
8+
using Xunit;
9+
using Xunit.Sdk;
10+
11+
namespace Akka.TestKit.Xunit2.Tests;
12+
13+
public class XunitAssertionsSpec
14+
{
15+
private readonly XunitAssertions _assertions = new();
16+
17+
[Fact]
18+
public void Assert_does_not_format_message_when_no_arguments_are_specified()
19+
{
20+
const string testMessage = "{Value} with different format placeholders {0}";
21+
22+
var exception = Assert.ThrowsAny<XunitException>(() => _assertions.Fail(testMessage));
23+
Assert.Contains(testMessage, exception.Message);
24+
25+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertTrue(false, testMessage));
26+
Assert.Contains(testMessage, exception.Message);
27+
28+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertFalse(true, testMessage));
29+
Assert.Contains(testMessage, exception.Message);
30+
31+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, testMessage));
32+
Assert.Contains(testMessage, exception.Message);
33+
34+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, (_, _) => false, testMessage));
35+
Assert.Contains(testMessage, exception.Message);
36+
}
37+
38+
[Fact]
39+
public void Assert_formats_message_when_arguments_are_specified()
40+
{
41+
const string testMessage = "Meaning: {0}";
42+
const string expectedMessage = "Meaning: 42";
43+
44+
var exception = Assert.ThrowsAny<XunitException>(() => _assertions.Fail(testMessage, 42));
45+
Assert.Contains(expectedMessage, exception.Message);
46+
47+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertTrue(false, testMessage, 42));
48+
Assert.Contains(expectedMessage, exception.Message);
49+
50+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertFalse(true, testMessage, 42));
51+
Assert.Contains(expectedMessage, exception.Message);
52+
53+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, testMessage, 42));
54+
Assert.Contains(expectedMessage, exception.Message);
55+
56+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, (_, _) => false, testMessage, 42));
57+
Assert.Contains(expectedMessage, exception.Message);
58+
}
59+
60+
[Fact]
61+
public void Assert_catches_format_exceptions()
62+
{
63+
const string testMessage = "Meaning: {0} {1}";
64+
const string expectedMessage = "Could not string.Format";
65+
66+
var exception = Assert.ThrowsAny<XunitException>(() => _assertions.Fail(testMessage, 42));
67+
Assert.Contains(expectedMessage, exception.Message);
68+
69+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertTrue(false, testMessage, 42));
70+
Assert.Contains(expectedMessage, exception.Message);
71+
72+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertFalse(true, testMessage, 42));
73+
Assert.Contains(expectedMessage, exception.Message);
74+
75+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, testMessage, 42));
76+
Assert.Contains(expectedMessage, exception.Message);
77+
78+
exception = Assert.ThrowsAny<XunitException>(() => _assertions.AssertEqual(4, 2, (_, _) => false, testMessage, 42));
79+
Assert.Contains(expectedMessage, exception.Message);
80+
}
81+
}

src/contrib/testkits/Akka.TestKit.Xunit2/Internals/AkkaEqualException.cs

Lines changed: 23 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,12 @@ namespace Akka.TestKit.Xunit2.Internals
1515
/// <summary>
1616
/// TBD
1717
/// </summary>
18+
[Serializable]
1819
public class AkkaEqualException : XunitException
1920
{
2021
// Length of "Expected: " and "Actual: "
2122
private static readonly string NewLineAndIndent = Environment.NewLine + new string(' ', 10);
2223

23-
private readonly string? _format;
24-
private readonly object[] _args = Array.Empty<object>();
25-
2624
public static AkkaEqualException ForMismatchedValues(
2725
object? expected,
2826
object? actual,
@@ -46,48 +44,46 @@ public static AkkaEqualException ForMismatchedValues(
4644
args
4745
);
4846
}
49-
47+
5048
/// <summary>
5149
/// Initializes a new instance of the <see cref="AkkaEqualException"/> class.
5250
/// </summary>
5351
/// <param name="format">A template string that describes the error.</param>
5452
/// <param name="args">An optional object array that contains zero or more objects to format.</param>
55-
public AkkaEqualException(string format = "", params object[] args): base(null)
56-
{
57-
_format = format;
58-
_args = args;
59-
}
53+
public AkkaEqualException(string format = "", params object[] args)
54+
: base(BuildAssertionMessage(format, args)) { }
6055

6156
/// <summary>
6257
/// Initializes a new instance of the <see cref="AkkaEqualException"/> class.
6358
/// </summary>
6459
/// <param name="info">The <see cref="SerializationInfo"/> that holds the serialized object data about the exception being thrown.</param>
6560
/// <param name="context">The <see cref="StreamingContext"/> that contains contextual information about the source or destination.</param>
66-
protected AkkaEqualException(SerializationInfo info, StreamingContext context): base(null)
67-
{
68-
}
61+
protected AkkaEqualException(SerializationInfo info, StreamingContext context)
62+
: base(info.GetString("Message")) { }
6963

7064
/// <summary>
71-
/// The message that describes the error.
65+
/// Builds assertion message by applying specified arguments to the format string.
66+
/// When no arguments are specified, format string is returned as-is.
7267
/// </summary>
73-
public override string Message
68+
internal static string? BuildAssertionMessage(string format, object[] args)
7469
{
75-
get
70+
if (string.IsNullOrEmpty(format))
7671
{
77-
if(string.IsNullOrEmpty(_format))
78-
return base.Message;
72+
return null;
73+
}
7974

80-
string message;
81-
try
82-
{
83-
message = string.Format(_format!, _args);
84-
}
85-
catch(Exception)
86-
{
87-
message = $@"[Could not string.Format(""{_format}"", {string.Join(", ", _args)})]";
88-
}
75+
if (args is not { Length: > 0 })
76+
{
77+
return format;
78+
}
8979

90-
return base.Message is not null ? $"{base.Message} {message}" : message;
80+
try
81+
{
82+
return string.Format(format, args);
83+
}
84+
catch (Exception)
85+
{
86+
return $"""[Could not string.Format("{format}", {string.Join(", ", args)})]""";
9187
}
9288
}
9389
}

src/contrib/testkits/Akka.TestKit.Xunit2/XunitAssertions.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ public class XunitAssertions : ITestKitAssertions
2323
/// <param name="args">An optional object array that contains zero or more objects to format.</param>
2424
public void Fail(string format = "", params object[] args)
2525
{
26-
Assert.Fail(string.Format(format, args));
26+
Assert.Fail(AkkaEqualException.BuildAssertionMessage(format, args));
2727
}
2828

2929
/// <summary>
@@ -34,7 +34,7 @@ public void Fail(string format = "", params object[] args)
3434
/// <param name="args">An optional object array that contains zero or more objects to format.</param>
3535
public void AssertTrue(bool condition, string format = "", params object[] args)
3636
{
37-
Assert.True(condition, string.Format(format, args));
37+
Assert.True(condition, AkkaEqualException.BuildAssertionMessage(format, args));
3838
}
3939

4040
/// <summary>
@@ -45,7 +45,7 @@ public void AssertTrue(bool condition, string format = "", params object[] args)
4545
/// <param name="args">An optional object array that contains zero or more objects to format.</param>
4646
public void AssertFalse(bool condition, string format = "", params object[] args)
4747
{
48-
Assert.False(condition, string.Format(format, args));
48+
Assert.False(condition, AkkaEqualException.BuildAssertionMessage(format, args));
4949
}
5050

5151
/// <summary>

src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveTestKitXunit2.DotNet.verified.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ namespace Akka.TestKit.Xunit2.Internals
3838
{
3939
public AkkaEqualException(string format = "", params object[] args) { }
4040
protected AkkaEqualException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { }
41-
public override string Message { get; }
4241
[return: System.Runtime.CompilerServices.NullableAttribute(1)]
4342
public static Akka.TestKit.Xunit2.Internals.AkkaEqualException ForMismatchedValues(object expected, object actual, string format = null, [System.Runtime.CompilerServices.NullableAttribute(1)] params object[] args) { }
4443
}

src/core/Akka.API.Tests/verify/CoreAPISpec.ApproveTestKitXunit2.Net.verified.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,6 @@ namespace Akka.TestKit.Xunit2.Internals
3838
{
3939
public AkkaEqualException(string format = "", params object[] args) { }
4040
protected AkkaEqualException(System.Runtime.Serialization.SerializationInfo info, System.Runtime.Serialization.StreamingContext context) { }
41-
public override string Message { get; }
4241
[return: System.Runtime.CompilerServices.NullableAttribute(1)]
4342
public static Akka.TestKit.Xunit2.Internals.AkkaEqualException ForMismatchedValues(object expected, object actual, string format = null, [System.Runtime.CompilerServices.NullableAttribute(1)] params object[] args) { }
4443
}

src/core/Akka.Persistence.TestKit.Tests/JournalInterceptorsSpecs.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ public async Task cancelable_delay_must_call_next_interceptor_immediately_after_
7373

7474
var startedAt = DateTime.Now;
7575
var task = delay.InterceptAsync(null);
76-
await Task.Delay(delayDuration - epsilon);
76+
await Task.Delay(delayDuration);
7777

7878
probe.WasCalled.Should().BeFalse();
7979
cts.Cancel();

src/core/Akka.Persistence.TestKit.Tests/SnapshotStoreInterceptorsSpec.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ public async Task cancelable_delay_must_call_next_interceptor_immediately_after_
6363

6464
var startedAt = DateTime.Now;
6565
var task = delay.InterceptAsync(null, null);
66-
await Task.Delay(delayDuration - epsilon);
66+
await Task.Delay(delayDuration);
6767

6868
probe.WasCalled.Should().BeFalse();
6969
cts.Cancel();

0 commit comments

Comments
 (0)