Skip to content

Ported Cluster and Archive client unit tests from Java#148

Merged
mikeb01 merged 1 commit into
masterfrom
ebowden/port-cluster-and-archive-client-tests
May 11, 2026
Merged

Ported Cluster and Archive client unit tests from Java#148
mikeb01 merged 1 commit into
masterfrom
ebowden/port-cluster-and-archive-client-tests

Conversation

@strangelydim
Copy link
Copy Markdown
Contributor

Added a new "Unsealer.Fody" Fody addin to allow tests to subclass sealed classes (Debug builds only).

Changed EgressPoller.OnFragment to skip over unknown schemaIds and continue to match Java behavior

Added ArchiveException.ErrorCodeAsString, made AeronArchive.QuietClose public, and added MessageRetryAttempts config option for parity with Java

Changed serialization of booleans in URIs to be lowercase "true/false" rather than .NET's default "True/False"

Changed AeronArchive.AsyncConnect to match Java's version and state machine

Fixed wrong decoder used in EgressAdapter.OnFragment's new leader event case

@strangelydim strangelydim requested a review from JPWatson May 4, 2026 22:49
@strangelydim strangelydim force-pushed the ebowden/port-cluster-and-archive-client-tests branch 2 times, most recently from fb5f03a to 24a1484 Compare May 8, 2026 19:07
var actualException = Assert.Throws<InvalidOperationException>(() => AeronArchive.ConnectAsync(ctx));
Assert.AreSame(error, actualException);

A.CallTo(() => ctx.Conclude()).MustHaveHappened().Then(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting. We should break the link on the ., e.g:

A.CallTo(() => ctx.Conclude()).MustHaveHappened()
    .Then(A.CallTo(() => ctx.AeronClient()).MustHaveHappened())
    .Then(A.CallTo(() => ctx.ControlResponseChannel()).MustHaveHappened())
    .Then(A.CallTo(() => ctx.ControlResponseStreamId()).MustHaveHappened())
    .Then(A.CallTo(() => aeron.AsyncAddSubscription(
        responseChannel, responseStreamId, A<AvailableImageHandler>._, A<UnavailableImageHandler>._))
        .MustHaveHappened())
    .Then(A.CallTo(() => ctx.Dispose()).MustHaveHappened());

There's are a few other instance similar to this.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

var actualException = Assert.Throws<IndexOutOfRangeException>(() => AeronArchive.ConnectAsync(ctx));
Assert.AreSame(error, actualException);

A.CallTo(() => ctx.Conclude()).MustHaveHappened().Then(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

Comment thread src/Adaptive.Archiver/AeronArchive.cs Outdated
private long controlSessionId = Aeron.Aeron.NULL_VALUE;
private byte[] encodedCredentialsFromChallenge = null;
private AsyncConnectState state = AsyncConnectState.ADD_PUBLICATION;
private AsyncConnectState state = AsyncConnectState.AWAIT_SUBSCRIPTION;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Java code moves this code into the constructor, while functionally equivalent (on the happy path). I think it is clearer if this gets moved to a similar location to the Java version.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Moved!

Comment thread src/Adaptive.Archiver/AeronArchive.cs Outdated
@@ -3548,8 +3602,11 @@ public void Dispose()
{
if (null != controlResponsePoller)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Java implementation is doing a check for ownsAeronClient, is that required here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably... added.

Comment thread src/Adaptive.Archiver/AeronArchive.cs Outdated
CheckDeadline();
ctx.RunInvokers();

if (AsyncConnectState.AWAIT_SUBSCRIPTION == state)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Java implementation uses a switch statement here instead a lot of ifs. It may be out of scope for this PR, but we should look at aligning the implementation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to switch.

object publication = null != archiveProxy ? (object)archiveProxy.Pub() : ctx.ControlRequestChannel();
object subscription = null != controlResponsePoller
? (object)controlResponsePoller.Subscription()
: ctx.ControlResponseChannel();
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting, should be:

throw new TimeoutException(
    "Archive connect timeout: step=" + state +
    " publication=" + publication +
    " subscription=" + subscription);

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!

{
public int DisposeCount { get; private set; }

public override void Dispose()
Copy link
Copy Markdown
Contributor

@mikeb01 mikeb01 May 11, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This requires that AeronCluster.Context.Dispose to be declared virtual. If there is an alternative to this implementation, then that would be better than a derived class. This looks like it is a replacement for a spy, which the .Net mocking library doesn't seem to support. If making the method virtual is the only solution, then I guess we need to go with that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turns out there was an alternative using Fody... so used that instead, Dispose is back to non-virtual.


asyncConnect.Dispose();

A.CallTo(() => _aeron.Ctx).MustHaveHappened().Then(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!


asyncConnect.Dispose();

A.CallTo(() => _aeron.Ctx).MustHaveHappened().Then(
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Formatting.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed!


[SetUp]
public void SetUp()
{
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A lot of this logic could move to field initialisation to better follow the Java implemementation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mostly fixed... some couldn't be moved due to .NET rules.

@strangelydim strangelydim force-pushed the ebowden/port-cluster-and-archive-client-tests branch from 24a1484 to 90dbec7 Compare May 11, 2026 18:16
Added a new "Unsealer.Fody" Fody addin to allow tests to subclass sealed classes (Debug builds only).

Changed EgressPoller.OnFragment to skip over unknown schemaIds and continue to match Java behavior

Added ArchiveException.ErrorCodeAsString, made AeronArchive.QuietClose public, and added MessageRetryAttempts config option for parity with Java

Changed serialization of booleans in URIs to be lowercase "true/false" rather than .NET's default "True/False"

Changed AeronArchive.AsyncConnect to match Java's version and state machine

Fixed wrong decoder used in EgressAdapter.OnFragment's new leader event case
@strangelydim strangelydim force-pushed the ebowden/port-cluster-and-archive-client-tests branch from 90dbec7 to 1752af2 Compare May 11, 2026 18:30
@strangelydim strangelydim requested a review from mikeb01 May 11, 2026 18:33
private EgressPoller _egressPoller;

[SetUp]
public void SetUp()
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Initialisation can happen on the field and this setup method can be removed.

@mikeb01 mikeb01 merged commit fdb43ed into master May 11, 2026
4 checks passed
@mikeb01 mikeb01 deleted the ebowden/port-cluster-and-archive-client-tests branch May 11, 2026 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants