Skip to content

[#137] Wait for cleanup in RequestDistributorTest.testFailedRequest - #140

Open
vharseko wants to merge 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:request-distributor-failed-request-wait
Open

vharseko wants to merge 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:request-distributor-failed-request-wait

Conversation

@vharseko

Copy link
Copy Markdown
Member

Summary

Fixes the intermittent RequestDistributorTest.testSimpleRequest CI failure reported in #137 by closing the gap in testFailedRequest, the one test method in the class that did not wait for its own request to be unregistered before returning.

Root cause

RemoteRequest.getSendFunction() registers the map cleanup as a promise completion listener:

promise.thenOnResultOrException(new Runnable() {
    public void run() { completionCallback.complete(RemoteRequest.this); }
});

which is what runs remoteRequests.remove(request.getRequestId()) (RemoteConnectionGroup.allocateRequest).

In PromiseImpl.setState() the waiter is woken before the listener queue is drained:

synchronized (this) {
    state = newState;
    notifyAll();          // unblocks getOrThrowUninterruptibly()
}                          // monitor released here
while ((listener = listeners.poll()) != null) {
    handleCompletion(listener, newState);   // only now is the map entry removed
}

So returning from getOrThrowUninterruptibly() does not guarantee the entry has left remoteRequests. The window is normally microseconds, which is why this only shows up on a loaded CI runner.

testSimpleRequest (3 s sleep), testCallbackRequest, testBlockingCallbackRequest and testCancelRequest (5x1 s retry loops) all absorb that window before they return. testFailedRequest did not:

request.getPromise().getOrThrowUninterruptibly();
Assert.assertTrue(client.getRemoteRequests().isEmpty());   // unreachable
Assert.assertTrue(server.getLocalRequests().isEmpty());    // unreachable

The call throws RuntimeException("Unknown Test case number") (set via getExceptionHandler().handleException(...) in TestRemoteRequest.handleIncomingMessage, i.e. the HAS_EXCEPTION path), so both assertions were dead code and the method went straight to finally { connection.close(); } with no wait at all. Since every one of these methods only declares dependsOnMethods = { "testNoConnectionRequest" }, TestNG is free to schedule testFailedRequest right before testSimpleRequest or testCallbackRequest, whose entry assertion on client.getRemoteRequests().isEmpty() then races the pending listener — exactly the failure at line 147 in the report.

The server side is not affected: LocalRequest.handleException() calls removeRequest() synchronously before the error message is even sent, which is consistent with the CI log failing on getRemoteRequests() rather than getLocalRequests().

Change

testFailedRequest now catches the expected exception explicitly (so the expectedExceptions annotation is no longer needed and the assertions become reachable) and waits for remoteRequests/localRequests to drain with the same retry loop the sibling test methods use.

Test plan

  • mvn -pl OpenICF-java-framework/connector-framework-rpc test -Dtest=RequestDistributorTest — 6/6 green
  • 5 consecutive runs of the same command, all green
  • The expected failure path is still asserted: Assert.fail("Not Failed") if no exception, plus a message check on the caught RuntimeException

Fixes #137

….testFailedRequest

testFailedRequest returned as soon as getOrThrowUninterruptibly() threw,
without waiting for the promise completion listener to remove the request
from remoteRequests/localRequests. Since sibling test methods only depend
on testNoConnectionRequest, TestNG may run any of them right after
testFailedRequest, so their entry assertions could observe the stale entry
and fail intermittently.

The assertions placed after getOrThrowUninterruptibly() were also
unreachable: the call threw before reaching them, so the test never
verified its own cleanup. Catch the expected exception explicitly and wait
for the maps to drain, as the sibling test methods already do.
@vharseko vharseko added bug Something isn't working concurrency Races, locking and thread-safety fixes framework OpenICF-java-framework tests Test additions or fixes java Pull requests that update java code labels Sep 19, 2026
vharseko added a commit to vharseko/OpenICF that referenced this pull request Sep 19, 2026
…ty answer

The Docker jobs fetched releases/latest anonymously; the 60 req/h limit
is per runner IP, so a rate-limited answer silently left release_version
empty, metadata-action produced no tag and buildx failed two steps later
with "tag is needed when pushing to registry" (PR OpenIdentityPlatform#140, run 35429703030).
Use the tag rather than the release title: it is what the Dockerfile's
releases/download URL is built from, and it is never empty.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working concurrency Races, locking and thread-safety fixes framework OpenICF-java-framework java Pull requests that update java code tests Test additions or fixes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Flaky RequestDistributorTest.testSimpleRequest: stale entry in client.getRemoteRequests() on CI

1 participant