fix(test): tie preference writer hold to shutdown phase
Release / release (push) Failing after 30m13s

This commit is contained in:
dmg committed 2026-10-04 17:24:40 -04:00
1 parent e54896cb38
commit 9f0c18bcf2
1 file changed
+32 -7
@@ -80,6 +80,14 @@ class PreferenceLifecycleNativeTest {
}
@Test void registeredAuraExportQueuedAtShutdownMustBeAcknowledgedBeforeRestart() throws Exception {
queuedAuraShutdown(false);
}
@Test void realQueuedAuraWriterStaysHeldPastFormerDeadlineUntilActualClose() throws Exception {
queuedAuraShutdown(true);
}
private void queuedAuraShutdown(boolean delayClosing) throws Exception {
try (var f = new FantasyStickLifecycleNativeTest.Fixture(data)) {
f.plugin.setEnabled(true);
var actor = new FantasyStickLifecycleNativeTest.Actor(data);
@@ -93,10 +101,12 @@ class PreferenceLifecycleNativeTest {
var release = new java.util.concurrent.CountDownLatch(1);
var blocked = worker.submit(() -> {
entered.countDown();
try { assertTrue(release.await(5, TimeUnit.SECONDS)); }
try { release.await(); }
catch (InterruptedException failure) { Thread.currentThread().interrupt(); throw new AssertionError(failure); }
});
CompletableFuture<Void> releasing = null;
var cleanupRequested = new java.util.concurrent.atomic.AtomicBoolean();
Throwable primary = null;
try {
assertTrue(entered.await(5, TimeUnit.SECONDS));
assertTrue(f.playerCommand.getExecutor().onCommand(actor.player, f.playerCommand,
@@ -108,14 +118,20 @@ class PreferenceLifecycleNativeTest {
var original = preferences.pendingEvents().getFirst();
assertTrue(changes().isEmpty(), "The real writer is held before export, not a fabricated sink result");
assertArrayEquals(inventory, actor.core.snapshot());
if (delayClosing) {
assertThrows(java.util.concurrent.TimeoutException.class, () -> blocked.get(6, TimeUnit.SECONDS),
"The real queued writer must remain held before any close request, even past the former five-second test deadline");
assertFalse((Boolean) FantasyStickLifecycleNativeTest.Fixture.field(log, "closing"));
assertTrue(changes().isEmpty()); assertFalse(preferences.pendingEvents().isEmpty());
}
releasing = CompletableFuture.runAsync(() -> {
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5);
while (!(Boolean) FantasyStickLifecycleNativeTest.Fixture.field(log, "closing")
&& System.nanoTime() < deadline) java.util.concurrent.locks.LockSupport.parkNanos(100_000);
assertTrue((Boolean) FantasyStickLifecycleNativeTest.Fixture.field(log, "closing"));
release.countDown();
&& !cleanupRequested.get()) java.util.concurrent.locks.LockSupport.parkNanos(100_000);
if (!cleanupRequested.get()) release.countDown();
});
f.plugin.setEnabled(false);
assertTrue((Boolean) FantasyStickLifecycleNativeTest.Fixture.field(log, "closing"),
"Actual orderly native shutdown must request writer close before its queued acknowledgement");
releasing.get(5, TimeUnit.SECONDS); blocked.get(5, TimeUnit.SECONDS);
preferences.drained().toCompletableFuture().get(5, TimeUnit.SECONDS);
assertEquals(1, changes().size(), "Orderly writer shutdown exports the queued accepted native change");
@@ -134,9 +150,18 @@ class PreferenceLifecycleNativeTest {
f.plugin.setEnabled(false); f.awaitWorkers();
assertEquals(java.util.List.of(exported), changes(),
"Orderly restart cannot create a second export or recapture the acknowledged original payload");
} catch (Exception | AssertionError failure) {
primary = failure; throw failure;
} finally {
release.countDown(); blocked.get(5, TimeUnit.SECONDS);
if (releasing != null) releasing.get(5, TimeUnit.SECONDS);
cleanupRequested.set(true); release.countDown();
var completions = new java.util.ArrayList<java.util.concurrent.Future<?>>(); completions.add(blocked);
if (releasing != null) completions.add(releasing);
Exception cleanupFailure = null;
for (var completion : completions) {
try { completion.get(5, TimeUnit.SECONDS); }
catch (Exception failure) { if (cleanupFailure == null) cleanupFailure = failure; else cleanupFailure.addSuppressed(failure); }
}
if (cleanupFailure != null) { if (primary == null) throw cleanupFailure; primary.addSuppressed(cleanupFailure); }
}
}
}