Adapter patches

(tests)
This commit is contained in:
Brian Neumann-Fopiano committed 2026-10-04 13:08:41 -04:00
1 parent 4b4b418c61
commit a6149b27c9
40 files changed
+894 -211

No files matched your search

@@ -0,0 +1,131 @@
package art.arcane.iris.generation.runtime;
import art.arcane.iris.probe.StubPlatform;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.testsupport.IrisRuntimeState;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.LockSupport;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
public final class ProbeBackgroundTasksTest {
@Rule
public final TemporaryFolder temporary = new TemporaryFolder();
@Before
public void bindRuntime() {
IrisPlatforms.bind(new StubPlatform(temporary.getRoot()));
}
@After
public void resetRuntime() {
IrisRuntimeState.reset();
StubPlatform.errorSink(null);
}
@Test(timeout = 15_000L)
public void waitsForTrackedContinuationBeforeReturning() throws Exception {
EngineBackgroundTasks tasks = new EngineBackgroundTasks();
tasks.openBackgroundTaskAdmission();
CountDownLatch firstEntered = new CountDownLatch(1);
CountDownLatch firstRelease = new CountDownLatch(1);
CountDownLatch continuationEntered = new CountDownLatch(1);
CountDownLatch continuationRelease = new CountDownLatch(1);
try (ExecutorService closer = Executors.newSingleThreadExecutor()) {
try {
tasks.scheduleTrackedTask(() -> {
awaitRelease(firstEntered, firstRelease);
tasks.scheduleTrackedTask(() -> awaitRelease(continuationEntered, continuationRelease));
});
assertTrue(firstEntered.await(5, TimeUnit.SECONDS));
Future<?> completion = startDrain(closer, tasks);
assertThrows(TimeoutException.class, () -> completion.get(100, TimeUnit.MILLISECONDS));
firstRelease.countDown();
assertTrue(continuationEntered.await(5, TimeUnit.SECONDS));
assertThrows(TimeoutException.class, () -> completion.get(100, TimeUnit.MILLISECONDS));
continuationRelease.countDown();
completion.get(5, TimeUnit.SECONDS);
} finally {
firstRelease.countDown();
continuationRelease.countDown();
}
}
}
@Test(timeout = 15_000L)
public void propagatesAnAsynchronousTrackedFailure() throws Exception {
EngineBackgroundTasks tasks = new EngineBackgroundTasks();
tasks.openBackgroundTaskAdmission();
CountDownLatch entered = new CountDownLatch(1);
CountDownLatch release = new CountDownLatch(1);
IllegalStateException failure = new IllegalStateException("tracked generation failure");
CountDownLatch reported = new CountDownLatch(1);
StubPlatform.errorSink(error -> reported.countDown());
try (ExecutorService closer = Executors.newSingleThreadExecutor()) {
try {
tasks.scheduleTrackedTask(() -> {
awaitRelease(entered, release);
throw failure;
});
assertTrue(entered.await(5, TimeUnit.SECONDS));
Future<?> completion = startDrain(closer, tasks);
assertThrows(TimeoutException.class, () -> completion.get(100, TimeUnit.MILLISECONDS));
release.countDown();
ExecutionException observed = assertThrows(ExecutionException.class,
() -> completion.get(5, TimeUnit.SECONDS));
assertSame(failure, observed.getCause().getCause().getCause());
assertTrue(reported.await(5, TimeUnit.SECONDS));
} finally {
release.countDown();
}
}
}
private static Future<?> startDrain(ExecutorService closer, EngineBackgroundTasks tasks) {
AtomicReference<Thread> drainingThread = new AtomicReference<>();
Future<?> completion = closer.submit(() -> {
drainingThread.set(Thread.currentThread());
ProbeBackgroundTasks.await(tasks);
});
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5);
while (System.nanoTime() < deadline) {
Thread thread = drainingThread.get();
if (thread != null && thread.getState() == Thread.State.TIMED_WAITING) {
for (StackTraceElement frame : thread.getStackTrace()) {
if (frame.getClassName().equals(EngineBackgroundTasks.class.getName())
&& frame.getMethodName().equals("drainBackgroundTasks")) {
return completion;
}
}
}
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1));
}
throw new AssertionError("Probe barrier did not await its initial task");
}
private static void awaitRelease(CountDownLatch entered, CountDownLatch release) {
entered.countDown();
try {
assertTrue(release.await(5, TimeUnit.SECONDS));
} catch (InterruptedException interruption) {
Thread.currentThread().interrupt();
throw new IllegalStateException(interruption);
}
}
}
@@ -211,7 +211,7 @@ public final class ObjectContinuationProbeTest {
assertArrayEquals(oldReceipt, SavedTerrainChunk.readReceipt(world.worldFolder().toPath(), 0, 0));
assertEquals(originalActivation, reopened.resolveActivation(0, 0).activationId());
assertTrue(reopened.semantics(1, 0).orElseThrow().objectKeys().contains(fixture.objectKey()));
assertTrue("Generation must not report swallowed errors", RealPackProbeSupport.settleAndDrain().isEmpty());
assertTrue("Generation must not report swallowed errors", RealPackProbeSupport.settleAndDrain(engine).isEmpty());
} finally {
engine.close();
}
@@ -0,0 +1,101 @@
package art.arcane.iris.probe;
import art.arcane.iris.generation.concurrent.MultiBurst;
import art.arcane.iris.generation.runtime.EngineEffectsProvider;
import art.arcane.iris.generation.runtime.EnginePlatformHooks;
import art.arcane.iris.generation.runtime.EngineWorldManagerProvider;
import art.arcane.iris.generation.runtime.PreservationRegistry;
import art.arcane.iris.spi.IrisPlatforms;
import art.arcane.iris.spi.IrisServices;
import art.arcane.iris.testsupport.IrisRuntimeState;
import art.arcane.iris.testsupport.PlatformLeakGuard;
import art.arcane.iris.world.task.J;
import org.junit.AfterClass;
import org.junit.Test;
import java.io.File;
import java.net.URL;
import java.util.List;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
public final class RealPackProbeSupportBarrierTest {
@AfterClass
public static void resetRuntime() {
IrisRuntimeState.reset();
}
@Test
public void closesOwnedRuntimeServicesAndPreservesUnrelatedServices() throws Exception {
URL resource = RealPackProbeSupportBarrierTest.class.getResource("/seed-cohesion");
assertNotNull(resource);
IrisServices.register(String.class, "unrelated");
try {
try (RealPackProbeSupport.Workspace workspace = RealPackProbeSupport.openWorkspace(
new File(resource.toURI()), "cohesion", "[workspace-cleanup]")) {
assertNotNull(IrisServices.getOrNull(EngineEffectsProvider.class));
}
assertFalse(IrisPlatforms.isBound());
assertNull(IrisServices.getOrNull(PreservationRegistry.class));
assertNull(IrisServices.getOrNull(EngineWorldManagerProvider.class));
assertNull(IrisServices.getOrNull(EngineEffectsProvider.class));
assertNull(IrisServices.getOrNull(EnginePlatformHooks.class));
assertEquals("unrelated", IrisServices.get(String.class));
} finally {
IrisServices.remove(String.class);
}
}
@Test(timeout = 30_000L)
public void waitsForCrossPoolContinuationsAndCollectsTheirErrors() throws Exception {
URL resource = RealPackProbeSupportBarrierTest.class.getResource("/seed-cohesion");
assertNotNull(resource);
try (RealPackProbeSupport.Workspace workspace = RealPackProbeSupport.openWorkspace(
new File(resource.toURI()), "cohesion", "[worker-barrier]");
RealPackProbeSupport.EngineSession session = workspace.openEngine(1337L, false, "barrier")) {
for (int repeat = 0; repeat < 40; repeat++) {
verifyCrossPoolContinuation(session, MultiBurst.ioBurst, MultiBurst.hydrology);
verifyCrossPoolContinuation(session, MultiBurst.hydrology, MultiBurst.ioBurst);
}
}
PlatformLeakGuard.requireClean("closed real-pack workspace", "end");
}
private static void verifyCrossPoolContinuation(RealPackProbeSupport.EngineSession session,
MultiBurst first, MultiBurst second) throws Exception {
IllegalStateException failure = new IllegalStateException("nested generation failure");
CountDownLatch entered = new CountDownLatch(1);
CountDownLatch release = new CountDownLatch(1);
try {
first.lazy(() -> {
entered.countDown();
try {
assertTrue(release.await(5, TimeUnit.SECONDS));
} catch (InterruptedException interruption) {
Thread.currentThread().interrupt();
throw new IllegalStateException(interruption);
}
Runnable failedTask = () -> {
throw failure;
};
second.lazy(() -> first.lazy(() -> second.lazy(() -> J.a(failedTask))));
});
assertTrue(entered.await(5, TimeUnit.SECONDS));
release.countDown();
List<Throwable> reports = RealPackProbeSupport.settleAndDrain(session.engine());
assertEquals(1, reports.size());
assertSame(failure, reports.getFirst());
assertTrue(RealPackProbeSupport.drainReported().isEmpty());
} finally {
release.countDown();
}
}
}