mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-12 03:24:14 +00:00
Retire source-text tests
- Delete 22 test classes whose assertions only read production .java text and check call ordering or literal presence - Remove 77 source-text assertion methods from 34 test classes that keep real behavioural coverage - Replace the core process-stream source grep with an ASM gate over compiled core classes in CoreProcessStreamPurityGateTest - Replace the packs-root source grep in PacksFolderContractTest with an ASM call-site gate over compiled core classes - Replace the IrisData registered-root regex in PackTypeBukkitPurityGateTest with an ASM scan of the registerLoader class constants - Trim six mixed tests down to their behavioural assertions and drop their source-text tails - Drop the eight iris.*Source and three iris.moddedCommonSources test system properties now that nothing reads them
This commit is contained in:
@@ -59,12 +59,4 @@ tasks.named('jar', Jar).configure {
|
||||
}
|
||||
|
||||
tasks.named('test').configure {
|
||||
systemProperty('iris.pregeneratorJobSource', rootProject.file('core/src/main/java/art/arcane/iris/core/gui/PregeneratorJob.java').absolutePath)
|
||||
systemProperty('iris.bukkitEnginePlatformHooksSource', file('src/main/java/art/arcane/iris/core/runtime/BukkitEnginePlatformHooks.java').absolutePath)
|
||||
systemProperty('iris.engineSvcSource', file('src/main/java/art/arcane/iris/core/service/IrisEngineSVC.java').absolutePath)
|
||||
systemProperty('iris.terrainSvcSource', file('src/main/java/art/arcane/iris/core/service/IrisTerrainSVC.java').absolutePath)
|
||||
systemProperty('iris.apiEventSvcSource', file('src/main/java/art/arcane/iris/core/service/IrisApiEventSVC.java').absolutePath)
|
||||
systemProperty('iris.worldInfoFactorySource', file('src/main/java/art/arcane/iris/core/service/terrain/IrisWorldInfoFactory.java').absolutePath)
|
||||
systemProperty('iris.startupSource', file('src/main/java/art/arcane/iris/Iris.java').absolutePath)
|
||||
systemProperty('iris.readmeSource', rootProject.file('README.md').absolutePath)
|
||||
}
|
||||
|
||||
@@ -3,14 +3,10 @@ package art.arcane.iris;
|
||||
import art.arcane.iris.spi.LogLevel;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.logging.Level;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Core states a severity on every message it logs and the modded adapters honour it. Bukkit must preserve
|
||||
@@ -33,58 +29,4 @@ public class IrisDiagnosticLogLevelTest {
|
||||
public void lifecycleNoticesReachThePluginLoggerAtInfo() {
|
||||
assertEquals(Level.INFO, Iris.diagnosticLevel(LogLevel.NOTICE));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void adapterSideWarningsCarryTheSameSeverityAsCoreWarnings() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/Iris.java")).replace("\r\n", "\n");
|
||||
|
||||
String warn = method(source, "public static void warn(String format, Object... objs)");
|
||||
assertTrue(warn, warn.contains("diagnostic(Level.WARNING"));
|
||||
assertFalse(warn, warn.contains("msg("));
|
||||
|
||||
String error = method(source, "public static void error(String format, Object... objs)");
|
||||
assertTrue(error, error.contains("diagnostic(Level.SEVERE"));
|
||||
assertFalse(error, error.contains("msg("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theCoreLogBridgeRoutesDiagnosticsToThePluginLogger() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/Iris.java")).replace("\r\n", "\n");
|
||||
String bridge = method(source, "private static void bridgeLog(LogLevel level, String message)");
|
||||
|
||||
assertTrue(bridge, bridge.contains("diagnosticLevel(target)"));
|
||||
assertTrue(bridge, bridge.contains("diagnostic(diagnostic, message)"));
|
||||
String diagnostic = method(source, "private static void diagnostic(Level level, String message)");
|
||||
assertTrue(diagnostic, diagnostic.contains("ComponentLog.log("));
|
||||
assertTrue(diagnostic, diagnostic.contains("ComponentText.literal(line)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void informationalMessagesUseTheSharedComponentLogger() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/Iris.java")).replace("\r\n", "\n");
|
||||
String message = method(source, "public static void msg(String string)");
|
||||
|
||||
assertTrue(message, message.contains("ComponentLog.logMarkup("));
|
||||
assertTrue(message, message.contains("logPrefix(plugin)"));
|
||||
assertFalse(message, message.contains("getSender().sendMessage"));
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue("method not found: " + signature, start >= 0);
|
||||
int open = source.indexOf('{', start);
|
||||
int depth = 0;
|
||||
for (int i = open; i < source.length(); i++) {
|
||||
char c = source.charAt(i);
|
||||
if (c == '{') {
|
||||
depth++;
|
||||
} else if (c == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(start, i + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new AssertionError("unterminated method: " + signature);
|
||||
}
|
||||
}
|
||||
|
||||
-72
@@ -1,72 +0,0 @@
|
||||
package art.arcane.iris.core;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisStartupOrderingTest {
|
||||
@Test
|
||||
public void admissionGateIsRegisteredBeforeStartupValidationBegins() throws Exception {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.startupSource")));
|
||||
String onEnable = section(source, "public void onEnable()", "public void onDisable()");
|
||||
|
||||
assertOrdered(onEnable,
|
||||
"IrisStartupValidation.begin();",
|
||||
"registerEvents(new IrisStartupAdmissionListener(), this);",
|
||||
"enable();");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void externalDatapacksValidateBeforeDimensionPacks() throws Exception {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.startupSource")));
|
||||
String enable = section(source, "private boolean enable()", "public void addShutdownHook()");
|
||||
|
||||
assertOrdered(enable,
|
||||
"DatapackIngestService.validateOnStartup();",
|
||||
"generatorResolver.validateAllPacks();");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void externalProvidersEnableBeforePackValidation() throws Exception {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.startupSource")));
|
||||
String enable = section(source, "private boolean enable()", "public void addShutdownHook()");
|
||||
|
||||
assertOrdered(enable,
|
||||
"service.onEnable();",
|
||||
"setContentChangeListener(generatorResolver::requestExternalContentRefresh)",
|
||||
"generatorResolver.validateAllPacks();");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void restartRequiredStartupTerminatesAfterPluginInitialization() throws Exception {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.startupSource")));
|
||||
String onEnable = section(source, "public void onEnable()", "public void onDisable()");
|
||||
|
||||
assertOrdered(onEnable,
|
||||
"BukkitGuiHost.install();",
|
||||
"super.onEnable();",
|
||||
"IrisStartupValidation.isRestartRequired()",
|
||||
"ServerConfigurator.restartAtStartupBoundary(restartReason);");
|
||||
}
|
||||
|
||||
private static String section(String source, String startMarker, String endMarker) {
|
||||
int start = source.indexOf(startMarker);
|
||||
int end = source.indexOf(endMarker, start);
|
||||
assertTrue("Missing source section starting with " + startMarker, start >= 0);
|
||||
assertTrue("Missing source section ending with " + endMarker, end > start);
|
||||
return source.substring(start, end);
|
||||
}
|
||||
|
||||
private static void assertOrdered(String source, String... markers) {
|
||||
int previous = -1;
|
||||
for (String marker : markers) {
|
||||
int current = source.indexOf(marker);
|
||||
assertTrue("Missing source marker " + marker, current >= 0);
|
||||
assertTrue("Source marker is out of order: " + marker, current > previous);
|
||||
previous = current;
|
||||
}
|
||||
}
|
||||
}
|
||||
-98
@@ -140,23 +140,6 @@ public class IrisWorldGeneratorResolverTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startupValidationPublishesFingerprintBoundExactRootResults() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/IrisWorldGeneratorResolver.java")).replace("\r\n", "\n");
|
||||
int validateAll = source.indexOf("public void validateAllPacks()");
|
||||
int snapshot = source.indexOf("ServerConfigurator.computePackContentSnapshot(packsRoot)", validateAll);
|
||||
int perPackFingerprint = source.indexOf("contentSnapshot.packContents()", snapshot);
|
||||
int exactRootPublish = source.indexOf(
|
||||
"PackValidationRegistry.publish(packDirectory.toPath(), result, packFingerprint)",
|
||||
perPackFingerprint);
|
||||
|
||||
assertTrue(validateAll >= 0);
|
||||
assertTrue(snapshot > validateAll);
|
||||
assertTrue(perPackFingerprint > snapshot);
|
||||
assertTrue(exactRootPublish > perPackFingerprint);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paperStartupAliasResolvesToCanonicalRuntimeKey() throws Exception {
|
||||
File levelRoot = ownedLevelRoot("startup-alias", "moon");
|
||||
@@ -251,87 +234,6 @@ public class IrisWorldGeneratorResolverTest {
|
||||
return levelRoot;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void configuredWorldResolutionUsesOnlyActiveImmutableGenerationPack() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/IrisWorldGeneratorResolver.java")).replace("\r\n", "\n");
|
||||
int resolverStart = source.indexOf("private ChunkGenerator resolveFrozenWorldGenerator(");
|
||||
int resolverEnd = source.indexOf("private record FreshValidation", resolverStart);
|
||||
String resolver = source.substring(resolverStart, resolverEnd);
|
||||
|
||||
int dimensionRoot = resolver.indexOf("IrisWorldStorage.requireFrozenDimensionRoot(");
|
||||
int currentPlatformRoot = resolver.indexOf("WorldCreatorCompat.persistentDimensionRoot(worldKey)");
|
||||
int layoutRefusal = resolver.indexOf(
|
||||
"Frozen Iris world storage does not match the current platform layout",
|
||||
currentPlatformRoot
|
||||
);
|
||||
int history = resolver.indexOf("requireGenerationHistory(dimensionRoot, id, worldSeed)");
|
||||
int snapshotRoot = resolver.indexOf("requireActivePack(history)", history);
|
||||
int validation = resolver.indexOf("requireSnapshotLoadable(snapshotRoot)");
|
||||
int exactLoad = resolver.indexOf("requireHistoricalDimension(history, snapshotRoot, id)");
|
||||
int canonicalIdentity = resolver.indexOf(".platformIdentity(worldKey.toString())");
|
||||
int historicalSeed = resolver.indexOf(".seed(history.activeEpoch().worldSeed())");
|
||||
int resolvedStorage = resolver.indexOf(".worldFolder(dimensionRoot)");
|
||||
|
||||
assertTrue(dimensionRoot >= 0);
|
||||
assertTrue(currentPlatformRoot > dimensionRoot);
|
||||
assertTrue(layoutRefusal > currentPlatformRoot);
|
||||
assertTrue(history > layoutRefusal);
|
||||
assertTrue(snapshotRoot > history);
|
||||
assertTrue(validation > snapshotRoot);
|
||||
assertTrue(exactLoad > validation);
|
||||
assertTrue(canonicalIdentity > exactLoad);
|
||||
assertTrue(historicalSeed > canonicalIdentity);
|
||||
assertTrue(resolvedStorage > historicalSeed);
|
||||
assertFalse(resolver.contains("loadDimension("));
|
||||
assertFalse(resolver.contains("loadAnyDimension("));
|
||||
assertFalse(resolver.contains("replaceIntoWorld("));
|
||||
assertFalse(resolver.contains("installIntoWorld("));
|
||||
assertFalse(resolver.contains(".seed(1337)"));
|
||||
assertTrue(resolver.contains("GenerationHistory.openIfPresent(root, worldSeed)"));
|
||||
assertTrue(resolver.contains("GenerationHistory.adoptLegacyPack("));
|
||||
assertTrue(resolver.contains("GenerationRegistryContractFactory.captureRequiredDefinitions("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ownedWorldSnapshotFailureStopsStartupAndRethrows() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/IrisWorldGeneratorResolver.java")).replace("\r\n", "\n");
|
||||
int resolverStart = source.indexOf("public ChunkGenerator resolveDefaultWorldGenerator(");
|
||||
int resolverEnd = source.indexOf("private ChunkGenerator resolveFrozenWorldGenerator(", resolverStart);
|
||||
String resolver = source.substring(resolverStart, resolverEnd);
|
||||
|
||||
int plotSquaredProbe = resolver.indexOf("isPlotSquaredGeneratorDiscoveryProbe(worldName, id)");
|
||||
int probe = resolver.indexOf("isGeneratorDiscoveryProbe(worldName, id)", plotSquaredProbe);
|
||||
int denial = resolver.indexOf("IrisStartupValidation.denialReason()", probe);
|
||||
int failClosed = resolver.indexOf("IrisFailClosedChunkGenerator.startupLock(", denial);
|
||||
int staged = resolver.indexOf("WorldLifecycleStaging.consumeGenerator(worldName)", failClosed);
|
||||
int duplicateGuard = resolver.indexOf("requireWorldKeyAvailable(worldName, worldKey)");
|
||||
int ownership = resolver.indexOf("requireOwnedWorld(worldName, levelRoot, worldKey)");
|
||||
int frozen = resolver.indexOf("return resolveFrozenWorldGenerator(", ownership);
|
||||
int failureCapture = resolver.indexOf("catch (RuntimeException failure)", frozen);
|
||||
int report = resolver.indexOf("Iris.reportError(", failureCapture);
|
||||
int shutdown = resolver.indexOf("Bukkit.shutdown()", report);
|
||||
int rethrow = resolver.indexOf("throw failure", shutdown);
|
||||
|
||||
assertTrue(plotSquaredProbe >= 0);
|
||||
assertTrue(probe > plotSquaredProbe);
|
||||
assertTrue(denial > probe);
|
||||
assertTrue(failClosed > denial);
|
||||
assertTrue(staged > failClosed);
|
||||
assertTrue(duplicateGuard > staged);
|
||||
assertTrue(ownership > duplicateGuard);
|
||||
assertTrue(frozen > ownership);
|
||||
assertTrue(failureCapture > frozen);
|
||||
assertTrue(report > failureCapture);
|
||||
assertTrue(shutdown > report);
|
||||
assertTrue(rethrow > shutdown);
|
||||
|
||||
String shutdownScope = "the fail-fast shutdown must stay scoped to the frozen snapshot of an owned world";
|
||||
assertEquals(shutdownScope, resolverStart + shutdown, source.indexOf("Bukkit.shutdown()"));
|
||||
assertEquals(shutdownScope, resolverStart + shutdown, source.lastIndexOf("Bukkit.shutdown()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void plotSquaredDiscoveryProbeIsIgnoredQuietly() {
|
||||
try (MockedStatic<Bukkit> bukkit = mockStatic(Bukkit.class);
|
||||
|
||||
-26
@@ -1,26 +0,0 @@
|
||||
package art.arcane.iris.core.commands;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class CommandDeveloperGenerationHistoryTest {
|
||||
@Test
|
||||
public void updateWorldStagesThePackWithTheLiveWorldSeed() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandDeveloper.java"
|
||||
)).replace("\r\n", "\n");
|
||||
int updateStart = source.indexOf("public void updateWorld(");
|
||||
int updateEnd = source.indexOf("@Director(description = \"Test\"", updateStart);
|
||||
String update = source.substring(updateStart, updateEnd);
|
||||
|
||||
assertTrue(update.contains(
|
||||
"replaceIntoWorld(sender(), pack, folder, world.getSeed())"
|
||||
));
|
||||
assertFalse(update.contains("iris/pack"));
|
||||
}
|
||||
}
|
||||
-24
@@ -7,8 +7,6 @@ import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -73,26 +71,4 @@ public class CommandIrisCreateOverwriteContractTest {
|
||||
assertEquals(Long.valueOf(Long.MAX_VALUE), handler.parse(Long.toString(Long.MAX_VALUE), false));
|
||||
assertThrows(DirectorParsingException.class, () -> handler.parse("9223372036854775808", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void createAndReplaceUseLocalizedStyledMissingDimensionFeedback() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandIris.java"
|
||||
)).replace("\r\n", "\n");
|
||||
|
||||
assertEquals(2, occurrences(source, "COMMAND_IRIS_DIMENSION_NOT_FOUND"));
|
||||
assertEquals(2, occurrences(source, "COMMAND_IRIS_INSTALL_PACK_AND_RESTART"));
|
||||
assertFalse(source.contains("sendMessage(\"Could not find dimension"));
|
||||
assertFalse(source.contains("sendMessage(\"Install its pack with"));
|
||||
}
|
||||
|
||||
private static int occurrences(String value, String match) {
|
||||
int count = 0;
|
||||
int offset = 0;
|
||||
while ((offset = value.indexOf(match, offset)) >= 0) {
|
||||
count++;
|
||||
offset += match.length();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
-17
@@ -5,13 +5,10 @@ import org.junit.Test;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -47,20 +44,6 @@ public class CommandIrisDownloadContractTest {
|
||||
assertThrows(Exception.class, () -> handler.parse("custom", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commandDelegatesAcceptedDownloadsWithoutRawPreamble() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandIris.java"
|
||||
)).replace("\r\n", "\n");
|
||||
String download = method(source, "public void download(");
|
||||
|
||||
assertTrue(download.contains("downloadBuiltIn(sender(), builtInPack)"));
|
||||
assertTrue(download.contains("downloadUrl(sender(), directLink)"));
|
||||
assertFalse(download.contains("Downloading built-in Iris pack"));
|
||||
assertFalse(download.contains("Downloading Iris pack from"));
|
||||
assertFalse(download.contains("sendMessage(directLink"));
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue(start >= 0);
|
||||
|
||||
-48
@@ -115,21 +115,6 @@ public class CommandJigsawContractTest {
|
||||
new IllegalStateException("Unexpected Studio failure")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void committedActivationStartsInitialEvaluationBeforePlayerBinding() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandJigsaw.java")).replace("\r\n", "\n");
|
||||
int commit = source.indexOf("JigsawStudioActivation.commit(staged)");
|
||||
int evaluation = source.indexOf(
|
||||
"studioService.activationCommitted(world, request.requestId())",
|
||||
commit);
|
||||
int binding = source.indexOf("PLAYER_PACKS.put", evaluation);
|
||||
|
||||
assertTrue(commit >= 0);
|
||||
assertTrue(evaluation > commit);
|
||||
assertTrue(binding > evaluation);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void convertIsAddOnlyWorkflowWithAliasesAndDefaults() throws Exception {
|
||||
Method convert = CommandJigsaw.class.getDeclaredMethod(
|
||||
@@ -358,20 +343,6 @@ public class CommandJigsawContractTest {
|
||||
.getAnnotation(Director.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pieceCreateUsesResolvedWorkcellCapacityInsteadOfLayoutDefault() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandJigsaw.java")).replace("\r\n", "\n");
|
||||
int pieceCommands = source.indexOf("public static class CommandJigsawPiece");
|
||||
int create = source.indexOf("public void create(", pieceCommands);
|
||||
int add = source.indexOf("public void add(", create);
|
||||
String createSource = source.substring(create, add);
|
||||
|
||||
assertTrue(createSource.contains("targetWorkcell = contextual;"));
|
||||
assertTrue(createSource.contains("JigsawStudioCellDimensions dimensions = targetWorkcell.capacity();"));
|
||||
assertFalse(createSource.contains("layout().cellDimensions()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pieceAddUsesCanonicalAxesForRotatedRectangularPlanarWorkcells() {
|
||||
JigsawStudioLayout layout = nonuniformPlanarLayout();
|
||||
@@ -510,25 +481,6 @@ public class CommandJigsawContractTest {
|
||||
assertEquals(1, releases.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void exportSourceAcquiresStudioLeaseBeforeStaticLeaseAndReleasesBothPaths() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandJigsaw.java")).replace("\r\n", "\n");
|
||||
int serviceLease = source.indexOf("studioService.tryBeginExport(requestId, playerId)");
|
||||
int staticLease = source.indexOf("beginExport(playerId, destination)", serviceLease);
|
||||
int dispatch = source.indexOf("J.a(() -> runExport(operation))", staticLease);
|
||||
int schedulingRelease = source.indexOf("operation.lease().release()", dispatch);
|
||||
int exporter = source.indexOf("new VanillaJigsawDatapackExporter().export(operation.request())", dispatch);
|
||||
int completionRelease = source.indexOf("operation.lease().release()", exporter);
|
||||
|
||||
assertTrue(serviceLease >= 0);
|
||||
assertTrue(staticLease > serviceLease);
|
||||
assertTrue(dispatch > staticLease);
|
||||
assertTrue(schedulingRelease > dispatch);
|
||||
assertTrue(exporter > schedulingRelease);
|
||||
assertTrue(completionRelease > exporter);
|
||||
}
|
||||
|
||||
private static void assertCommand(String name, Class<?>... parameterTypes) throws Exception {
|
||||
Method method = CommandJigsaw.class.getDeclaredMethod(name, parameterTypes);
|
||||
assertNotNull(method.getAnnotation(Director.class));
|
||||
|
||||
-16
@@ -7,8 +7,6 @@ import org.bukkit.OfflinePlayer;
|
||||
import org.bukkit.World;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.UUID;
|
||||
@@ -212,20 +210,6 @@ public class IrisPapiExpansionTest {
|
||||
assertTrue(expansion.persist());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theReadmeDocumentsEveryPublishedKeyAndEveryRetiredOne() throws Exception {
|
||||
String readme = Files.readString(Path.of(System.getProperty("iris.readmeSource")));
|
||||
|
||||
for (String key : PUBLISHED_KEYS) {
|
||||
assertTrue("README must document %iris_" + key + "%", readme.contains("%iris_" + key + "%"));
|
||||
}
|
||||
|
||||
for (String retired : RETIRED_KEYS) {
|
||||
assertTrue("README must carry the migration row for %iris_" + retired + "%",
|
||||
readme.contains("%iris_" + retired + "%"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theTerrainServiceIsTheOnlyDoorIntoIris() {
|
||||
IrisTerrainService service = new FakeIrisTerrainService();
|
||||
|
||||
-137
@@ -1,137 +0,0 @@
|
||||
package art.arcane.iris.core.link;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisPapiLifecycleTest {
|
||||
private static final Path PLUGIN_SOURCE = Path.of("src/main/java/art/arcane/iris/Iris.java");
|
||||
private static final String SETUP = "private void setupPapi() {";
|
||||
private static final String TEARDOWN = "private void teardownPapi() {";
|
||||
|
||||
private static String source() throws Exception {
|
||||
// The assertions below match against literals written with \n, so a CRLF checkout has to
|
||||
// be folded first or every multi-line expectation misses.
|
||||
return Files.readString(PLUGIN_SOURCE).replace("\r\n", "\n");
|
||||
}
|
||||
|
||||
private static String body(String declaration) throws Exception {
|
||||
String source = source();
|
||||
int start = source.indexOf(declaration);
|
||||
|
||||
assertTrue("Iris.java must declare " + declaration, start >= 0);
|
||||
|
||||
int open = source.indexOf('{', start);
|
||||
int depth = 0;
|
||||
|
||||
for (int index = open; index < source.length(); index++) {
|
||||
char character = source.charAt(index);
|
||||
|
||||
if (character == '{') {
|
||||
depth++;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (character != '}') {
|
||||
continue;
|
||||
}
|
||||
|
||||
depth--;
|
||||
|
||||
if (depth == 0) {
|
||||
return source.substring(open + 1, index);
|
||||
}
|
||||
}
|
||||
|
||||
throw new AssertionError(declaration + " is not brace balanced");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registrationIsGatedOnPlaceholderApiBeingEnabled() throws Exception {
|
||||
assertTrue("setupPapi must bail out when PlaceholderAPI is absent",
|
||||
body(SETUP).contains("if (!PlaceholderRegistration.isPlaceholderApiEnabled()) {"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theRegistrationTheListenerAndTheStateAreAllRetained() throws Exception {
|
||||
String source = source();
|
||||
|
||||
assertTrue(source.contains("private volatile PlaceholderRegistration papiRegistration;"));
|
||||
assertTrue(source.contains("private volatile IrisPapiListener papiListener;"));
|
||||
assertTrue(source.contains("private volatile IrisPapiState papiState;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void teardownUnregistersTheExpansionTheListenerAndClearsTheState() throws Exception {
|
||||
String teardown = body(TEARDOWN);
|
||||
|
||||
assertTrue("the retained registration must be unregistered inside teardownPapi",
|
||||
teardown.contains("registration.unregister();"));
|
||||
assertTrue("the retained listener must be detached inside teardownPapi",
|
||||
teardown.contains("HandlerList.unregisterAll(listener);"));
|
||||
assertTrue("the retained state must drop every held world reference inside teardownPapi",
|
||||
teardown.contains("state.clear();"));
|
||||
assertTrue("teardownPapi must drop the retained listener", teardown.contains("papiListener = null;"));
|
||||
assertTrue("teardownPapi must drop the retained registration", teardown.contains("papiRegistration = null;"));
|
||||
assertTrue("teardownPapi must drop the retained state", teardown.contains("papiState = null;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bothDisablePathsTearThePlaceholderSurfaceDown() throws Exception {
|
||||
String disable = body("public void onDisable() {");
|
||||
String preUnload = body("public void onPreUnload(ReloadAware.PreUnloadReason reason) {");
|
||||
int disableTeardown = disable.indexOf("teardownPapi();");
|
||||
int preUnloadTeardown = preUnload.indexOf("teardownPapi();");
|
||||
|
||||
assertTrue("onDisable must tear the expansion down before branching into runtime shutdown",
|
||||
disableTeardown >= 0 && disableTeardown < disable.indexOf("IrisToolbelt.isServerStopping()"));
|
||||
assertTrue("the BileTools pre-unload hook must tear the expansion down before any shutdown branch",
|
||||
preUnloadTeardown >= 0 && preUnloadTeardown < preUnload.indexOf("IrisToolbelt.isServerStopping()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aFailedListenerAttachDoesNotLeaveTheExpansionRegistered() throws Exception {
|
||||
String setup = body(SETUP);
|
||||
int attach = setup.indexOf("registerEvents(listener, this)");
|
||||
|
||||
assertTrue("the listener must be attached inside setupPapi", attach >= 0);
|
||||
|
||||
int rescue = setup.indexOf("} catch (Throwable failure) {", attach);
|
||||
|
||||
assertTrue("the attach must be guarded inside setupPapi", rescue > attach);
|
||||
|
||||
int rollback = setup.indexOf("registration.unregister();", rescue);
|
||||
|
||||
assertTrue("a failed attach must roll the registration back inside setupPapi", rollback > rescue);
|
||||
|
||||
int bail = setup.indexOf("return;", rollback);
|
||||
|
||||
assertTrue("a failed attach must leave setupPapi", bail > rollback);
|
||||
|
||||
int retained = setup.indexOf("papiRegistration = registration;");
|
||||
|
||||
assertTrue("setupPapi must retain the registration", retained > 0);
|
||||
assertTrue("the registration may only be retained after a successful attach", retained > bail);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theExpansionIsNeverConstructedFromAPluginStatic() throws Exception {
|
||||
String setup = body(SETUP);
|
||||
|
||||
assertFalse("the plugin class must not construct the expansion: that forces PlaceholderExpansion to load during enable and crashes a server without PlaceholderAPI",
|
||||
setup.contains("new IrisPapiExpansion("));
|
||||
assertTrue("the expansion takes its state as a constructor argument, built inside the installer",
|
||||
installerSource().contains("new IrisPapiExpansion(state, logger)"));
|
||||
assertFalse("the hand rolled PlaceholderAPI presence check must be gone",
|
||||
source().contains("isPluginEnabled(\"PlaceholderAPI\")"));
|
||||
}
|
||||
|
||||
private static String installerSource() throws Exception {
|
||||
return java.nio.file.Files.readString(java.nio.file.Path.of(
|
||||
"src/main/java/art/arcane/iris/core/link/IrisPapiInstaller.java")).replace("\r\n", "\n");
|
||||
}
|
||||
}
|
||||
-53
@@ -1,53 +0,0 @@
|
||||
package art.arcane.iris.core.runtime;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class StudioPlayerModeContractTest {
|
||||
@Test
|
||||
public void autoStartedStudioUsesSpectatorMode() throws IOException {
|
||||
String plugin = Files.readString(Path.of("src/main/java/art/arcane/iris/Iris.java")).replace("\r\n", "\n");
|
||||
String commands = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandStudio.java")).replace("\r\n", "\n");
|
||||
|
||||
assertFalse(plugin.contains("GameMode.CREATIVE"));
|
||||
assertFalse(commands.contains("GameMode.CREATIVE"));
|
||||
assertTrue(plugin.contains("GameMode.SPECTATOR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void tpStudioUsesThePreparedCoordinatorEntry() throws IOException {
|
||||
String commands = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandStudio.java")).replace("\r\n", "\n");
|
||||
int methodStart = commands.indexOf("public void tpstudio()");
|
||||
int methodEnd = commands.indexOf("\n @Director", methodStart);
|
||||
String method = commands.substring(methodStart, methodEnd);
|
||||
|
||||
assertTrue(method.contains("StudioSVC studioService = Iris.service(StudioSVC.class)"));
|
||||
assertTrue(method.contains("studioService.teleportToActiveProject(player)"));
|
||||
assertFalse(method.contains("setGameMode("));
|
||||
assertFalse(method.contains("getActiveProject()"));
|
||||
assertFalse(method.contains("BukkitPlatform.teleportAsync"));
|
||||
assertFalse(method.contains("BukkitWorldBinding.spawnLocation"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void editingStudiosRemainCreative() throws IOException {
|
||||
String objectCommands = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandObject.java")).replace("\r\n", "\n");
|
||||
String jigsawCommands = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/commands/CommandJigsaw.java")).replace("\r\n", "\n");
|
||||
|
||||
assertTrue(objectCommands.contains("GameMode.CREATIVE"));
|
||||
assertTrue(objectCommands.contains("StudioOpenCoordinator.StudioOpenKind.OBJECT"));
|
||||
assertTrue(jigsawCommands.contains("GameMode.CREATIVE"));
|
||||
assertFalse(objectCommands.contains("GameMode.SPECTATOR"));
|
||||
assertFalse(jigsawCommands.contains("GameMode.SPECTATOR"));
|
||||
}
|
||||
}
|
||||
-195
@@ -1,195 +0,0 @@
|
||||
package art.arcane.iris.core.service;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisApiWiringContractTest {
|
||||
@Test
|
||||
public void theTerrainServiceRegistersOnBothRegistriesAndReleasesBoth() throws IOException {
|
||||
String source = source("iris.terrainSvcSource");
|
||||
String onEnable = method(source, "public void onEnable()");
|
||||
String onDisable = method(source, "public void onDisable()");
|
||||
|
||||
assertTrue(onEnable.contains("Bukkit.getServicesManager().register("));
|
||||
assertTrue(onEnable.contains("IrisTerrainService.class"));
|
||||
assertTrue(onEnable.contains("IrisServices.register(IrisTerrainService.class, this)"));
|
||||
assertTrue(onDisable.contains("Bukkit.getServicesManager().unregister(IrisTerrainService.class, this)"));
|
||||
assertTrue(onDisable.contains("IrisServices.remove(IrisTerrainService.class)"));
|
||||
assertBefore(onDisable, "serviceEnabled.set(false)", "Bukkit.getServicesManager().unregister(");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theTerrainServiceNeverForcesEngineInitialisationOrLoadsTheMantle() throws IOException {
|
||||
String source = source("iris.terrainSvcSource");
|
||||
|
||||
assertFalse("isIrisWorld must not touch the generator", source.contains("IrisToolbelt"));
|
||||
assertFalse("isIrisWorld must not touch the generator", source.contains(".touch("));
|
||||
assertFalse("terrain queries must not load mantle chunks", source.contains("getMantle()"));
|
||||
assertFalse("terrain queries must not load mantle chunks", source.contains("getObjectsAt("));
|
||||
assertFalse("terrain queries must not load mantle chunks", source.contains("getPOIsAt("));
|
||||
assertFalse("terrain queries must not load mantle chunks", source.contains("getCaveOrMantleBiome("));
|
||||
assertFalse("terrain queries must never block", source.contains(".join()"));
|
||||
assertFalse("terrain queries must never block", source.contains("synchronized"));
|
||||
assertFalse("terrain queries must never block", source.contains("J.sfut("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void sampleColumnsBoundsTheQueryBeforeItWalksAndTreatsAThrowingSinkAsARefusal() throws IOException {
|
||||
String sampleColumns = method(source("iris.terrainSvcSource"),
|
||||
"public boolean sampleColumns(World world, IrisColumnQuery query, IrisColumnSink sink)");
|
||||
|
||||
assertBefore(sampleColumns, "IrisSampleLimits.withinLimits(", "IrisColumnWalk.walk(");
|
||||
assertBefore(sampleColumns, "IrisColumnWalk.walk(", "catch (Throwable error)");
|
||||
assertTrue(sampleColumns.contains("reportSinkFault(world, error)"));
|
||||
assertTrue(sampleColumns.contains("return false;"));
|
||||
assertTrue("the walk must abort when the engine closes underneath it",
|
||||
sampleColumns.contains("engine.isClosed()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thePregeneratorDispatchesPhasesFromItsExistingTickAndNeverBlocksOnTheSink() throws IOException {
|
||||
String source = source("iris.pregeneratorJobSource");
|
||||
String dispatch = method(source, "private void dispatchApiPhases(List<PregenApiPhase> phases)");
|
||||
|
||||
assertTrue(dispatch.contains("IrisServices.getOrNull(PregenApiSink.class)"));
|
||||
assertTrue(dispatch.contains("catch (Throwable error)"));
|
||||
assertBefore(dispatch, "sink.pregen(phase, progress)", "catch (Throwable error)");
|
||||
assertFalse(dispatch.contains(".join()"));
|
||||
|
||||
assertTrue(method(source, "public void onTick(").contains("dispatchApiPhases(apiPhases.onTick(paused()))"));
|
||||
assertTrue(method(source, "public void onSaving()").contains("dispatchApiPhases(apiPhases.onSaving())"));
|
||||
assertTrue(method(source, "public void onClose()").contains("dispatchApiPhases(apiPhases.onClose(reachedTotal()))"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void worldPhasesAreFiredOutsideTheRegistrationLock() throws IOException {
|
||||
String source = source("iris.engineSvcSource");
|
||||
|
||||
String add = method(source, "private void add(World world)");
|
||||
assertBefore(add, "catch (RejectedExecutionException exception)", "phases.ready(world)");
|
||||
assertBefore(add, "registered = true;", "phases.ready(world)");
|
||||
|
||||
String remove = method(source,
|
||||
"private boolean remove(World world, CompletionStage<Boolean> unloadBoundary)");
|
||||
assertBefore(remove, "registered = worlds.remove(world)", "phases.closing(world)");
|
||||
assertBefore(remove, "phases.closing(world)",
|
||||
"deferCloseUntilWorldUnload(world, registered, closing, unloadBoundary)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void everyRegisteredWorldIsAnnouncedClosingWhenTheServiceShutsDown() throws IOException {
|
||||
String onDisable = method(source("iris.engineSvcSource"), "public void onDisable()");
|
||||
|
||||
assertTrue("service shutdown must announce ENGINE_CLOSING for every registered world",
|
||||
onDisable.contains("phases.closing(teardown.world())"));
|
||||
assertBefore(onDisable, "worlds.clear()", "phases.closing(teardown.world())");
|
||||
assertBefore(onDisable, "phases.closing(teardown.world())", "shutdownAndDrain(activeService)");
|
||||
assertBefore(onDisable, "phases.closing(teardown.world())",
|
||||
"startClose(teardown.registered(), teardown.closing())");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aReplacedEngineIsAnnouncedClosingBeforeTheRetryReRegistersTheWorld() throws IOException {
|
||||
String add = method(source("iris.engineSvcSource"), "private void add(World world)");
|
||||
|
||||
assertBefore(add, "catch (RejectedExecutionException exception)", "phases.closing(world)");
|
||||
assertBefore(add, "phases.closing(world)", "retryRegistrationAfterClose(world, retryAfter)");
|
||||
assertBefore(add, "phases.closing(world)", "startClose(replaced, replacementClose)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aWorldPhaseIsNeverConditionalOnAnotherServicesRegistration() throws IOException {
|
||||
String source = source("iris.apiEventSvcSource");
|
||||
String fire = method(source, "public static void fireWorldPhase(World world, IrisWorldPhase phase)");
|
||||
|
||||
assertFalse("world lifecycle must not resolve a swappable service to build its payload",
|
||||
fire.contains("IrisServices"));
|
||||
assertFalse("world lifecycle must not resolve a swappable service to build its payload",
|
||||
fire.contains("IrisTerrainService"));
|
||||
assertTrue(fire.contains("deliver(new IrisWorldEngineEvent(world, phase, describe(world, phase)))"));
|
||||
|
||||
String describe = method(source, "private static IrisWorldInfo describe(World world, IrisWorldPhase phase)");
|
||||
assertTrue(describe.contains("IrisWorldInfoFactory.forWorld(world)"));
|
||||
assertTrue("an undescribable world must be reported, not swallowed",
|
||||
describe.contains("IrisLogging.reportError("));
|
||||
assertTrue("an undescribable world must still deliver the phase", describe.contains("return null;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aWorldPhaseRaisedFromAServerThreadIsDeliveredBeforeThatThreadMovesOn() throws IOException {
|
||||
String deliver = method(source("iris.apiEventSvcSource"), "private static void deliver(Event event)");
|
||||
|
||||
assertBefore(deliver, "Bukkit.isPrimaryThread()", "Bukkit.getPluginManager().callEvent(event)");
|
||||
assertBefore(deliver, "Bukkit.getPluginManager().callEvent(event)", "Iris.callEvent(event)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theWorldInfoFactoryNeverForcesEngineInitialisationOrLoadsTheMantle() throws IOException {
|
||||
String source = source("iris.worldInfoFactorySource");
|
||||
|
||||
assertFalse("the factory must not touch the generator", source.contains("IrisToolbelt"));
|
||||
assertFalse("the factory must not touch the generator", source.contains(".touch("));
|
||||
assertFalse("the factory must not load mantle chunks", source.contains("getMantle()"));
|
||||
assertFalse("the factory must never block", source.contains(".join()"));
|
||||
assertFalse("the factory must never block", source.contains("synchronized"));
|
||||
assertTrue("the factory must refuse a closed engine", source.contains("engine.isClosed()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theHotloadHookStillFiresTheLegacyEventAlongsideTheApiEvent() throws IOException {
|
||||
String hook = method(source("iris.bukkitEnginePlatformHooksSource"), "public void fireHotloadEvent(Engine engine)");
|
||||
|
||||
assertBefore(hook, "new IrisEngineHotloadEvent(engine)",
|
||||
"IrisApiEventSVC.fireWorldPhase(BukkitWorldBinding.world(engine.getWorld()), IrisWorldPhase.ENGINE_HOTLOADED)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theEventServiceNeverLetsAThirdPartyFailureEscapeALifecyclePath() throws IOException {
|
||||
String fire = method(source("iris.apiEventSvcSource"),
|
||||
"public static void fireWorldPhase(World world, IrisWorldPhase phase)");
|
||||
|
||||
assertTrue(fire.contains("catch (Throwable error)"));
|
||||
assertTrue(fire.contains("IrisLogging.reportError("));
|
||||
assertFalse(fire.contains("printStackTrace"));
|
||||
}
|
||||
|
||||
private static String source(String property) throws IOException {
|
||||
String path = System.getProperty(property);
|
||||
assertTrue("missing source property " + property, path != null && !path.isBlank());
|
||||
return Files.readString(Path.of(path));
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = source.indexOf(first);
|
||||
int secondIndex = source.indexOf(second);
|
||||
assertTrue("Missing source contract token: " + first, firstIndex >= 0);
|
||||
assertTrue("Missing source contract token: " + second, secondIndex >= 0);
|
||||
assertTrue(first + " must occur before " + second, firstIndex < secondIndex);
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue("Missing source contract signature: " + signature, start >= 0);
|
||||
int openBrace = source.indexOf('{', start);
|
||||
assertTrue("Missing source contract method body: " + signature, openBrace >= 0);
|
||||
int depth = 0;
|
||||
for (int index = openBrace; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source contract method: " + signature);
|
||||
}
|
||||
}
|
||||
@@ -207,7 +207,6 @@ tasks.named('compileJava', JavaCompile).configure {
|
||||
}
|
||||
|
||||
tasks.named('test').configure {
|
||||
systemProperty('iris.moddedCommonSources', file('../modded-common/src/main/java').absolutePath)
|
||||
}
|
||||
|
||||
loom {
|
||||
|
||||
@@ -240,7 +240,6 @@ tasks.named('compileJava', JavaCompile).configure {
|
||||
}
|
||||
|
||||
tasks.named('test').configure {
|
||||
systemProperty('iris.moddedCommonSources', file('../modded-common/src/main/java').absolutePath)
|
||||
}
|
||||
|
||||
processResources {
|
||||
|
||||
+3
-9
@@ -73,15 +73,9 @@ public class IrisModLanguageAssetsTest {
|
||||
|
||||
private Set<String> resourceFiles() throws Exception {
|
||||
URL resource = IrisModLanguageAssetsTest.class.getClassLoader().getResource(ROOT);
|
||||
Path directory;
|
||||
if (resource != null && "file".equals(resource.getProtocol())) {
|
||||
directory = Path.of(resource.toURI());
|
||||
} else {
|
||||
String sources = System.getProperty("iris.moddedCommonSources");
|
||||
assertNotNull("Missing mod language resource directory and source root", sources);
|
||||
directory = Path.of(sources).getParent()
|
||||
.resolve("resources").resolve(ROOT);
|
||||
}
|
||||
assertNotNull("Missing mod language resource directory: " + ROOT, resource);
|
||||
assertEquals("Mod language resources must resolve to a directory on disk", "file", resource.getProtocol());
|
||||
Path directory = Path.of(resource.toURI());
|
||||
try (Stream<Path> paths = Files.list(directory)) {
|
||||
return paths
|
||||
.filter(Files::isRegularFile)
|
||||
|
||||
-21
@@ -9,13 +9,9 @@ import net.minecraft.world.level.biome.MobSpawnSettings;
|
||||
import org.junit.Test;
|
||||
import org.junit.BeforeClass;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisModdedChunkGeneratorSpawnTest {
|
||||
@BeforeClass
|
||||
@@ -48,21 +44,4 @@ public class IrisModdedChunkGeneratorSpawnTest {
|
||||
assertEquals(explicitCow, entries.get(2).value());
|
||||
assertEquals(3, entries.get(2).weight());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vanillaChunkGenerationMobsUseTheVisibleBiomesVanillaDerivative() throws IOException {
|
||||
Path sourcePath = Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedChunkGenerator.java");
|
||||
String source = Files.readString(sourcePath);
|
||||
int spawnStart = source.indexOf("public void spawnOriginalMobs");
|
||||
int spawnEnd = source.indexOf("@Override", spawnStart + 1);
|
||||
String spawn = source.substring(spawnStart, spawnEnd);
|
||||
|
||||
assertTrue(spawn.contains("spawnTables.initializeVanillaSpawnBiomes(registry)"));
|
||||
assertTrue(spawn.contains("structureBiomeSource.getVisibleSurfaceBiome("));
|
||||
assertTrue(spawn.contains("spawnTables.vanillaSpawnBiome(visibleBiome.value())"));
|
||||
assertTrue(spawn.contains("NaturalSpawner.spawnMobsForChunkGeneration("));
|
||||
assertTrue(spawn.contains("new LegacyRandomSource(RandomSupport.generateUniqueSeed())"));
|
||||
assertTrue(spawn.contains("random.setDecorationSeed(region.getSeed()"));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-153
@@ -14,9 +14,6 @@ import net.minecraft.server.Bootstrap;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
@@ -64,16 +61,10 @@ public class IrisModdedStructureParityTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void strongholdRingSearchSamplesOneQuartColumnPerChunk() throws IOException {
|
||||
public void strongholdRingSearchSamplesOneQuartColumnPerChunk() {
|
||||
assertEquals(4, IrisModdedBiomeSource.horizontalBiomeSearchQuartStep(0, 112));
|
||||
assertEquals(1, IrisModdedBiomeSource.horizontalBiomeSearchQuartStep(1, 112));
|
||||
assertEquals(1, IrisModdedBiomeSource.horizontalBiomeSearchQuartStep(0, 111));
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
assertTrue(source.contains("x, y, z, searchRadius, quartStep, allowed, random, false, sampler"));
|
||||
assertTrue(source.contains(
|
||||
"return super.findBiomeHorizontal(x, y, z, searchRadius, allowed, random, sampler)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -130,126 +121,12 @@ public class IrisModdedStructureParityTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void visibleBiomeResolutionSelectsStackOwnershipBeforeHostCaves() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
int resolutionStart = source.indexOf("private BiomeResolution resolveBiomeResolution(");
|
||||
int resolutionEnd = source.indexOf("private Holder<Biome> resolveRequiredStructureBiome(", resolutionStart);
|
||||
|
||||
assertTrue(resolutionStart >= 0);
|
||||
assertTrue(resolutionEnd > resolutionStart);
|
||||
String resolution = source.substring(resolutionStart, resolutionEnd);
|
||||
|
||||
assertTrue(resolution.contains("resolveDimensionStackLayer("));
|
||||
assertTrue(resolution.contains("!stackLayer.terrainContext().isSelfReferencing()"));
|
||||
assertTrue(resolution.indexOf("resolveDimensionStackLayer(")
|
||||
< resolution.indexOf("boolean underground"));
|
||||
assertTrue(resolution.contains("includeDimensionStack"));
|
||||
assertTrue(resolution.contains(
|
||||
"return resolveBiomeResolution(engine, quartX, quartY, quartZ, false)"));
|
||||
assertTrue(source.contains("resolution.packName(), resolution.dimension(), customBiome.getId()"));
|
||||
int helperStart = source.indexOf("private static IrisBiome resolveSurfaceStructureBiome(");
|
||||
int helperEnd = source.indexOf("private static Set<String> registeredBiomeKeys(", helperStart);
|
||||
String helper = source.substring(helperStart, helperEnd);
|
||||
assertTrue(helper.contains("engine.getComplex().getTrueBiomeStream().get(blockX, blockZ)"));
|
||||
assertFalse(helper.contains("getDimensionStackContext()"));
|
||||
assertFalse(helper.contains("getSurfaceBiome("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stackBiomesAreVisibleButExcludedFromStructureReachability() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
|
||||
assertTrue(source.contains("return biomeKeySets().structureRequired()"));
|
||||
assertTrue(source.contains("generatedBiomeKeys.addAll(keys.visibleRequired())"));
|
||||
assertTrue(source.contains("collectStructureBiomeKeys("));
|
||||
assertTrue(source.contains("dimension.getReachableBiomes(() -> data)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void biomeLocatorSearchesTheVisibleStack() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
int start = source.indexOf("public Pair<BlockPos, Holder<Biome>> findClosestBiome3d(");
|
||||
int end = source.indexOf("private Holder<Biome> getNoiseBiome(", start);
|
||||
String method = source.substring(start, end);
|
||||
|
||||
assertTrue(method.contains("modded_locate_visible_biome"));
|
||||
assertTrue(method.contains("return super.findClosestBiome3d("));
|
||||
assertTrue(method.contains("collectVisibleBiomeHolders()"));
|
||||
assertTrue(method.contains("QuartPos.toBlock(quartX), QuartPos.toBlock(quartZ)"));
|
||||
assertTrue(method.contains("resolveVisibleBiome("));
|
||||
assertFalse(method.contains("Holder<Biome> structureBiome = getNoiseBiome("));
|
||||
assertTrue(method.indexOf("tryAcquireGenerationLease(engine, \"modded_locate_visible_biome\")")
|
||||
< method.indexOf("engine.getDimensionStackContext()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void surfaceSpawnBiomeUsesTheVisibleQuartCell() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
int start = source.indexOf("Holder<Biome> getVisibleSurfaceBiome(");
|
||||
int end = source.indexOf("boolean isStructureReachable(", start);
|
||||
String method = source.substring(start, end);
|
||||
|
||||
assertTrue(method.contains("QuartPos.fromBlock(blockX)"));
|
||||
assertTrue(method.contains("QuartPos.toBlock(quartX)"));
|
||||
assertTrue(method.contains("QuartPos.fromBlock(internalY + engine.getMinHeight())"));
|
||||
assertTrue(method.contains("stackContext.getLayout(sampleX, sampleZ).surfaceLayer()"));
|
||||
assertTrue(method.contains("visibleBiomeCache"));
|
||||
assertTrue(method.contains("isBiomeCacheable(engine, sampleX, sampleZ)"));
|
||||
assertTrue(method.indexOf("boolean cacheable = isBiomeCacheable(engine, sampleX, sampleZ)")
|
||||
< method.indexOf("Engine.hostHeight(engine, sampleX, sampleZ, true)"));
|
||||
assertCacheGuarded(method, "visibleBiomeCache");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void temporaryNaturalBiomeAnswersAreNotMemoized() throws IOException {
|
||||
public void temporaryNaturalBiomeAnswersAreNotMemoized() {
|
||||
Engine engine = mock(Engine.class);
|
||||
when(engine.answersFromNaturalTerrain(12, -8)).thenReturn(true);
|
||||
assertFalse(IrisModdedBiomeSource.isBiomeCacheable(engine, 12, -8));
|
||||
when(engine.answersFromNaturalTerrain(12, -8)).thenReturn(false);
|
||||
assertTrue(IrisModdedBiomeSource.isBiomeCacheable(engine, 12, -8));
|
||||
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/IrisModdedBiomeSource.java"));
|
||||
int structureStart = source.indexOf("private Holder<Biome> getNoiseBiome(Engine engine");
|
||||
int structureEnd = source.indexOf("Holder<Biome> getVisibleNoiseBiome(", structureStart);
|
||||
String structure = source.substring(structureStart, structureEnd);
|
||||
assertTrue(structure.contains("QuartPos.toBlock(quartX)"));
|
||||
assertTrue(structure.contains("QuartPos.toBlock(quartZ)"));
|
||||
assertCacheGuarded(structure, "structureBiomeCache");
|
||||
|
||||
int visibleStart = source.indexOf(
|
||||
"private Holder<Biome> getVisibleNoiseBiomeWithActiveGenerationLease(");
|
||||
int visibleEnd = source.indexOf("Holder<Biome> getVisibleSurfaceBiome(", visibleStart);
|
||||
String visible = source.substring(visibleStart, visibleEnd);
|
||||
assertTrue(visible.contains("QuartPos.toBlock(quartX)"));
|
||||
assertTrue(visible.contains("QuartPos.toBlock(quartZ)"));
|
||||
assertCacheGuarded(visible, "visibleBiomeCache");
|
||||
|
||||
int predicateStart = source.indexOf("static boolean isBiomeCacheable(");
|
||||
int predicateEnd = source.indexOf("static boolean isMonumentSurfaceBiomeQuery(", predicateStart);
|
||||
String predicate = source.substring(predicateStart, predicateEnd);
|
||||
assertTrue(predicate.contains("return !engine.answersFromNaturalTerrain(blockX, blockZ)"));
|
||||
assertFalse(predicate.contains("getDimensionStackContext()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void importedFeaturesCollectOnlyHostOwnedStackCells() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedImportedFeatureStage.java"));
|
||||
|
||||
assertTrue(source.contains("stackContext.getLayerAt("));
|
||||
assertTrue(source.contains("layer.terrainContext().isSelfReferencing()"));
|
||||
assertTrue(source.contains("section.getBiomes().get(quartX, quartY, quartZ)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -281,17 +158,6 @@ public class IrisModdedStructureParityTest {
|
||||
assertTrue(recursiveKeys.contains("layers:sky/aurora"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void biomeWriterDerivativeFallbackMatchesCanonicalRecursiveKeys() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedBiomeWriter.java"));
|
||||
|
||||
assertTrue(source.contains(
|
||||
"key.equalsIgnoreCase(dimension.getCustomBiomeKey(custom.getId()))"));
|
||||
assertFalse(source.contains("String dimensionLoadKey = key.substring(0, colon)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureStateRejectsBiomesOutsideThePackContract() {
|
||||
Set<String> generated = Set.of("minecraft:deep_ocean", "minecraft:dark_forest");
|
||||
@@ -410,21 +276,4 @@ public class IrisModdedStructureParityTest {
|
||||
public void initialEntitySpawnsUseThePaperCompletionMarker() {
|
||||
assertSame(MantleFlag.INITIAL_SPAWNED_MARKER, ModdedWorldManager.INITIAL_SPAWN_COMPLETION_FLAG);
|
||||
}
|
||||
|
||||
private static void assertCacheGuarded(String method, String cacheName) {
|
||||
int predicate = method.indexOf("boolean cacheable = isBiomeCacheable(");
|
||||
int cache = method.indexOf("BiomeHolderTable cache = " + cacheName, predicate);
|
||||
int read = method.indexOf("cache.get(", predicate);
|
||||
int readGuard = method.lastIndexOf("if (cacheable)", read);
|
||||
int write = method.indexOf("cache.put(", read);
|
||||
int writeGuard = method.lastIndexOf("if (cacheable)", write);
|
||||
|
||||
assertTrue(predicate >= 0);
|
||||
assertTrue(cache >= 0);
|
||||
assertTrue(read > predicate);
|
||||
assertTrue(readGuard >= predicate);
|
||||
assertTrue(write > read);
|
||||
assertTrue(writeGuard > read);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
-91
@@ -1,91 +0,0 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedDynamicLevelLifecycleContractTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void sharedManagerPublishesDynamicLoadAndUnloadAroundRegistration() throws IOException {
|
||||
String manager = commonSource("ModdedDimensionManager.java");
|
||||
String loader = commonSource("ModdedLoader.java");
|
||||
String injection = method(manager, "private static Handle inject(");
|
||||
String removal = method(manager, "public static boolean remove(");
|
||||
String injectionRollback = method(manager, "private static void rollbackInjection(");
|
||||
String removalRollback = method(manager, "private static void rollbackRemoval(");
|
||||
|
||||
assertTrue(loader.contains("void fireDynamicLevelLoad(MinecraftServer server, ServerLevel level);"));
|
||||
assertTrue(loader.contains("void fireDynamicLevelUnload(MinecraftServer server, ServerLevel level);"));
|
||||
assertBefore(injection, "serverAccess.putLevelIfAbsent(server, key, level);",
|
||||
"fireDynamicLevelLoad(server, level);");
|
||||
assertBefore(removal, "fireDynamicLevelUnload(server, level);",
|
||||
"serverAccess.removeLevel(server, key);");
|
||||
assertTrue(injectionRollback.contains("fireDynamicLevelUnload(server, level);"));
|
||||
assertTrue(removalRollback.contains("fireDynamicLevelLoad(server, level);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void everyLoaderUsesItsNativeLifecycleBus() throws IOException {
|
||||
String fabric = loaderSource("fabric", "art/arcane/iris/fabric/FabricModdedLoader.java");
|
||||
String forge = loaderSource("forge", "art/arcane/iris/forge/ForgeModdedLoader.java");
|
||||
String neoForge = loaderSource("neoforge", "art/arcane/iris/neoforge/NeoForgeModdedLoader.java");
|
||||
|
||||
assertTrue(fabric.contains("ServerLevelEvents.LOAD.invoker().onLevelLoad(server, level);"));
|
||||
assertTrue(fabric.contains("ServerLevelEvents.UNLOAD.invoker().onLevelUnload(server, level);"));
|
||||
assertTrue(forge.contains("LevelEvent.Load.BUS.post(new LevelEvent.Load(level));"));
|
||||
assertTrue(forge.contains("LevelEvent.Unload.BUS.post(new LevelEvent.Unload(level));"));
|
||||
assertTrue(neoForge.contains("NeoForge.EVENT_BUS.post(new LevelEvent.Load(level));"));
|
||||
assertTrue(neoForge.contains("NeoForge.EVENT_BUS.post(new LevelEvent.Unload(level));"));
|
||||
}
|
||||
|
||||
private static String commonSource(String file) throws IOException {
|
||||
return Files.readString(commonRoot().resolve("art/arcane/iris/modded").resolve(file))
|
||||
.replace("\r\n", "\n");
|
||||
}
|
||||
|
||||
private static String loaderSource(String loader, String relative) throws IOException {
|
||||
Path adaptersRoot = commonRoot().getParent().getParent().getParent().getParent();
|
||||
return Files.readString(adaptersRoot.resolve(loader).resolve("src/main/java").resolve(relative))
|
||||
.replace("\r\n", "\n");
|
||||
}
|
||||
|
||||
private static Path commonRoot() {
|
||||
String root = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
if (root == null || root.isBlank()) {
|
||||
throw new IllegalStateException("Missing test property " + SOURCE_ROOT_PROPERTY);
|
||||
}
|
||||
return Path.of(root);
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
if (start < 0) {
|
||||
throw new IllegalArgumentException("Missing source method " + signature);
|
||||
}
|
||||
int open = source.indexOf('{', start);
|
||||
int depth = 0;
|
||||
for (int index = open; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}' && --depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source method " + signature);
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = source.indexOf(first);
|
||||
int secondIndex = source.indexOf(second);
|
||||
assertTrue("Missing source token " + first, firstIndex >= 0);
|
||||
assertTrue("Missing source token " + second, secondIndex >= 0);
|
||||
assertTrue(first + " must precede " + second, firstIndex < secondIndex);
|
||||
}
|
||||
}
|
||||
-64
@@ -7,12 +7,8 @@ import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.chunk.ChunkGenerator;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
@@ -24,8 +20,6 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class ModdedGenerationBootstrapBindingTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void publishedLevelRequiresCurrentGeneratorIdentity() {
|
||||
IrisModdedChunkGenerator generator = mock(IrisModdedChunkGenerator.class, CALLS_REAL_METHODS);
|
||||
@@ -89,62 +83,4 @@ public class ModdedGenerationBootstrapBindingTest {
|
||||
assertSame(dynamicLevel, generator.resolveBoundLevel(server, List.of(dynamicLevel)));
|
||||
verify(server, never()).getLevel(Level.OVERWORLD);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstGenerationStagesProvideAuthoritativeLevelContext() throws IOException {
|
||||
String source = source("IrisModdedChunkGenerator.java");
|
||||
String structures = method(source, "public void createStructures(");
|
||||
String references = method(source, "public void createReferences(");
|
||||
assertTrue(structures.contains("Engine current = engine(levelKey);"));
|
||||
assertTrue(references.contains("Engine current = engine(level.getLevel());"));
|
||||
|
||||
String publishedBinding = method(source, "private Engine engine(ResourceKey<Level> levelKey)");
|
||||
assertTrue(publishedBinding.contains("requirePublishedLevel(ModdedEngineBootstrap.currentServer(), levelKey)"));
|
||||
assertTrue(publishedBinding.contains("return bindGenerationLevel(level);"));
|
||||
assertFalse(publishedBinding.contains("ModdedServerLevels.level("));
|
||||
|
||||
String fullBinding = method(source, "private Engine bindGenerationLevel(ServerLevel level)");
|
||||
assertTrue(fullBinding.contains("bindLevel(level);"));
|
||||
assertTrue(fullBinding.contains("Engine bound = readyEngine();"));
|
||||
|
||||
String genericBinding = method(source, "Engine engine()");
|
||||
assertTrue(genericBinding.contains("return bindGenerationLevel(level);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void constructorMixinIsNotPartOfGenerationBootstrap() throws IOException {
|
||||
String sourceRoot = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
assertTrue(sourceRoot != null && !sourceRoot.isBlank());
|
||||
Path root = Path.of(sourceRoot);
|
||||
assertFalse(Files.exists(root.resolve(Path.of("art", "arcane", "iris", "modded", "mixin",
|
||||
"ServerLevelBindingMixin.java"))));
|
||||
String mixinConfig = Files.readString(root.getParent().resolve("resources/irisworldgen.entity.mixins.json"));
|
||||
assertFalse(mixinConfig.contains("ServerLevelBindingMixin"));
|
||||
}
|
||||
|
||||
private String source(String fileName) throws IOException {
|
||||
String sourceRoot = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
assertTrue(sourceRoot != null && !sourceRoot.isBlank());
|
||||
return Files.readString(Path.of(sourceRoot, "art", "arcane", "iris", "modded", fileName));
|
||||
}
|
||||
|
||||
private String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue(start >= 0);
|
||||
int body = source.indexOf('{', start);
|
||||
assertTrue(body >= 0);
|
||||
int depth = 0;
|
||||
for (int index = body; index < source.length(); index++) {
|
||||
char token = source.charAt(index);
|
||||
if (token == '{') {
|
||||
depth++;
|
||||
} else if (token == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source contract method: " + signature);
|
||||
}
|
||||
}
|
||||
|
||||
-203
@@ -1,203 +0,0 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedGenerationHistoryRoutingContractTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void startupPublishesOnlyAnImmutableHistoryBoundRuntime() throws IOException {
|
||||
String source = source("ModdedWorldEngines.java");
|
||||
String create = method(source, "private static Engine create(");
|
||||
String createHistoryEngine = method(source, "private static Engine createHistoryEngine(");
|
||||
String buildEngine = method(source, "private static IrisEngine buildEngine(");
|
||||
|
||||
assertTrue(create.contains("ModdedGenerationHistoryStorage.openOrAdopt("));
|
||||
assertFalse(create.contains("resolvePack("));
|
||||
assertTrue(createHistoryEngine.contains("candidate.attachGenerationHistory("));
|
||||
assertTrue(createHistoryEngine.contains("IrisBoundarySignatureSampler.INSTANCE"));
|
||||
assertTrue(createHistoryEngine.contains("getGenerationTransitionWidthBlocks()"));
|
||||
assertTrue(createHistoryEngine.contains("router.preloadActiveRuntimes();"));
|
||||
assertBefore(createHistoryEngine, "history.prepareCurrentGenerator(",
|
||||
"opened = ModdedGenerationHistoryStorage.resolveActive(history)");
|
||||
assertBefore(createHistoryEngine, "opened = ModdedGenerationHistoryStorage.resolveActive(history)",
|
||||
"IrisEngine candidate = buildEngine(level, seed, opened)");
|
||||
String promoted = method(createHistoryEngine,
|
||||
"if (history.activeActivation().activationId() != openedActivationId)");
|
||||
assertBefore(promoted, "close(candidate);", "ModdedGenerationHistoryStorage.resolveActive(history)");
|
||||
assertBefore(promoted, "ModdedGenerationHistoryStorage.resolveActive(history)",
|
||||
"candidate = buildEngine(level, seed, promoted)");
|
||||
assertTrue(createHistoryEngine.contains("GenerationHistoryRuntimeRouter.attach("));
|
||||
assertTrue(buildEngine.contains("active.packRoot().toFile()"));
|
||||
assertTrue(buildEngine.contains("history.paths().activationMantleRoot(activation.activationId())"));
|
||||
assertTrue(buildEngine.contains("epoch.kernelVersion()"));
|
||||
assertTrue(buildEngine.contains("transitionPlan"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyExplicitTransientStudioEnginesBypassHistory() throws IOException {
|
||||
String manager = source("ModdedDimensionManager.java");
|
||||
String transientCreate = method(manager, "public static Handle createTransientStudio(");
|
||||
String persistentCreate = method(manager, "public static Handle createPersistent(");
|
||||
String engines = source("ModdedWorldEngines.java");
|
||||
String studio = method(engines, "private static IrisEngine createTransientStudioEngine(");
|
||||
String generator = source("IrisModdedChunkGenerator.java");
|
||||
String bypass = method(generator, "boolean allowsGenerationHistoryBypass(");
|
||||
String route = method(generator, "private GenerationHistoryRuntimeRouter.RuntimeRoute openHistoryRoute(");
|
||||
|
||||
assertTrue(transientCreate.contains("ModdedGenerationMode.TRANSIENT_STUDIO"));
|
||||
assertTrue(persistentCreate.contains("ModdedGenerationMode.PERSISTENT_CREATE"));
|
||||
assertTrue(studio.contains("IrisData.openRuntime(packDir)"));
|
||||
assertTrue(studio.contains("IrisEngine.InitializationMode.STUDIO"));
|
||||
assertFalse(studio.contains("ModdedGenerationHistoryStorage"));
|
||||
assertFalse(studio.contains("attachGenerationHistory("));
|
||||
assertTrue(bypass.contains("generationMode != ModdedGenerationMode.TRANSIENT_STUDIO"));
|
||||
assertTrue(bypass.contains("irisEngine.isStudio()"));
|
||||
assertTrue(bypass.contains("Only an explicitly transient Iris Studio engine"));
|
||||
assertBefore(route, "allowsGenerationHistoryBypass(current)", "requireHistoryRouter(current, operation)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void everyCoordinateBearingChunkStageOpensItsRoutedRuntime() throws IOException {
|
||||
String source = source("IrisModdedChunkGenerator.java");
|
||||
|
||||
assertRuntimeRoute(source, "public Pair<BlockPos, Holder<Structure>> findNearestMapStructure(");
|
||||
assertCoordinateRoute(source, "public WeightedList<MobSpawnSettings.SpawnerData> getMobsAt(");
|
||||
assertRuntimeRoute(source, "public CompletableFuture<ChunkAccess> createBiomes(");
|
||||
assertRuntimeRoute(source, "public void applyCarvers(");
|
||||
assertRuntimeRoute(source, "public void buildSurface(");
|
||||
assertRuntimeRoute(source, "public void applyBiomeDecoration(");
|
||||
assertRuntimeRoute(source, "public void createStructures(");
|
||||
assertRuntimeRoute(source, "public void createReferences(");
|
||||
assertRuntimeRoute(source, "public void spawnOriginalMobs(");
|
||||
assertCoordinateRoute(source, "public int getBaseHeight(");
|
||||
assertCoordinateRoute(source, "public NoiseColumn getBaseColumn(");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncTerrainKeepsTheRouteUntilDurableSemanticClaim() throws IOException {
|
||||
String source = source("IrisModdedChunkGenerator.java");
|
||||
String fill = method(source, "public CompletableFuture<ChunkAccess> fillFromNoise(");
|
||||
String terrain = method(source, "private ChunkAccess generateTerrain(");
|
||||
String completion = method(source, "private static CompletableFuture<ChunkAccess> closeRouteOnCompletion(");
|
||||
|
||||
assertBefore(fill, "openHistoryRoute(", "CompletableFuture.supplyAsync(");
|
||||
assertTrue(fill.contains("generateTerrain(chunk, generationEngine, pos, air, route)"));
|
||||
assertTrue(fill.contains("return closeRouteOnCompletion(pipeline, route);"));
|
||||
assertTrue(terrain.contains("openHistoryRuntimeScope(route)"));
|
||||
assertBefore(terrain, "generationEngine.generate(", "writeBlocks(chunk, blocks, dimMinY, height)");
|
||||
assertBefore(terrain, "writeBlocks(chunk, blocks, dimMinY, height)", "route.claimGeneratedSemantics(");
|
||||
assertTrue(terrain.contains("chunk.getBlockState(position.set(x, dimMinY + y, z))"));
|
||||
assertTrue(terrain.contains("state.isAir() || state.liquid()"));
|
||||
assertBefore(terrain, "route.claimGeneratedSemantics(", "ModdedNativeTerrainReceipts.persist(chunk, route)");
|
||||
assertBefore(terrain, "ModdedNativeTerrainReceipts.persist(chunk, route)", "return chunk;");
|
||||
assertTrue(completion.contains("pipeline.whenComplete("));
|
||||
assertTrue(completion.contains("route.close();"));
|
||||
assertBefore(completion, "pipeline.whenComplete(", "route.detachThread()");
|
||||
assertBefore(completion, "route.detachThread()", "return completion;");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void biomeQueriesRouteEachCoordinateAndKeepRuntimeSpecificCaches() throws IOException {
|
||||
String source = source("IrisModdedBiomeSource.java");
|
||||
String structure = method(source, "public Holder<Biome> getNoiseBiome(");
|
||||
String visible = method(source, "Holder<Biome> getVisibleNoiseBiome(");
|
||||
String batch = method(source, "public Set<Holder<Biome>> getBiomesWithin(");
|
||||
String resolution = method(source, "private Holder<Biome> resolveVisibleBiome(");
|
||||
|
||||
assertTrue(structure.contains("openHistoryCoordinateScope("));
|
||||
assertTrue(visible.contains("openHistoryCoordinateScope("));
|
||||
assertTrue(batch.contains("quartX << 2, quartZ << 2"));
|
||||
assertTrue(source.contains("int runtimeIdentity = engine.getCacheID();"));
|
||||
assertTrue(source.contains("cache.get(runtimeIdentity, key)"));
|
||||
assertTrue(source.contains("cache.put(runtimeIdentity, key, resolved)"));
|
||||
assertBefore(resolution, "historicalPhysicalBiomeKeyAt(", "ModdedWorldgenIds.biomeRef(engine");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pendingNativeStagesPreserveSavedStartsAndGuardNewFeatureFootprints() throws IOException {
|
||||
String generator = source("IrisModdedChunkGenerator.java");
|
||||
String discrete = method(generator, "private boolean allowsRoutedDiscreteGeneration(");
|
||||
String decoration = method(generator, "public void applyBiomeDecoration(");
|
||||
String structures = method(generator, "public void createStructures(");
|
||||
String mobs = method(generator, "public void spawnOriginalMobs(");
|
||||
String nativeStage = source("ModdedNativeStructureStage.java");
|
||||
String adjust = method(nativeStage, "void adjustGeneratedStructures(");
|
||||
String place = method(nativeStage, "void placeVanillaStructures(");
|
||||
String savedStart = method(nativeStage, "private boolean isHistoricalStructureStart(");
|
||||
|
||||
assertTrue(discrete.contains("chunk.getPersistedStatus().isOrAfter(stage)"));
|
||||
assertFalse(discrete.contains("router.history().activeActivation().activationId()"));
|
||||
assertFalse(discrete.contains("return true;"));
|
||||
assertTrue(discrete.contains("allowsNewGenerationChunk("));
|
||||
assertTrue(discrete.contains("NativeGenerationWriteGuard.allowsPendingStage(current, chunk, stage)"));
|
||||
assertTrue(decoration.contains("ChunkStatus.FEATURES"));
|
||||
assertTrue(structures.contains("ChunkStatus.STRUCTURE_STARTS"));
|
||||
assertTrue(mobs.contains("ChunkStatus.SPAWN"));
|
||||
assertBefore(decoration, "chunk.getPersistedStatus().isOrAfter(ChunkStatus.FEATURES)",
|
||||
"nativeStructures.placeVanillaStructures(");
|
||||
assertBefore(decoration, "nativeStructures.placeVanillaStructures(", "allowsRoutedDiscreteGeneration(");
|
||||
assertBefore(decoration, "allowsRoutedDiscreteGeneration(", "importedFeatures.run(");
|
||||
assertBefore(decoration, "NativeGenerationWriteGuard.allowsDecoration(", "importedFeatures.run(");
|
||||
assertBefore(structures, "allowsRoutedDiscreteGeneration(", "super.createStructures(");
|
||||
assertBefore(mobs, "allowsRoutedDiscreteGeneration(", "NaturalSpawner.spawnMobsForChunkGeneration(");
|
||||
assertBefore(adjust, "allowsNewGenerationFootprint(", "recordWorldCheckStructureShift(");
|
||||
assertBefore(place, "!isHistoricalStructureStart(current, world, start)", "allowsNewGenerationFootprint(");
|
||||
assertTrue(savedStart.contains("world.getChunk(origin.x(), origin.z(), ChunkStatus.EMPTY, false)"));
|
||||
assertTrue(savedStart.contains("ModdedNativeTerrainReceipts.structureActivation(source)"));
|
||||
assertBefore(structures, "super.createStructures(", "ModdedNativeTerrainReceipts.persistStructureActivation(");
|
||||
}
|
||||
|
||||
private static void assertRuntimeRoute(String source, String signature) {
|
||||
String body = method(source, signature);
|
||||
assertTrue(signature + " must open a generation-history route", body.contains("openHistoryRoute("));
|
||||
assertTrue(signature + " must open its routed runtime", body.contains("openHistoryRuntimeScope(route)"));
|
||||
}
|
||||
|
||||
private static void assertCoordinateRoute(String source, String signature) {
|
||||
String body = method(source, signature);
|
||||
assertTrue(signature + " must route its coordinate", body.contains("openHistoryCoordinateScope("));
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = source.indexOf(first);
|
||||
int secondIndex = source.indexOf(second);
|
||||
assertTrue("Missing source contract token: " + first, firstIndex >= 0);
|
||||
assertTrue("Missing source contract token: " + second, secondIndex >= 0);
|
||||
assertTrue(first + " must occur before " + second, firstIndex < secondIndex);
|
||||
}
|
||||
|
||||
private static String source(String fileName) throws IOException {
|
||||
String sourceRoot = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
assertTrue("Missing system property " + SOURCE_ROOT_PROPERTY,
|
||||
sourceRoot != null && !sourceRoot.isBlank());
|
||||
return Files.readString(Path.of(sourceRoot, "art", "arcane", "iris", "modded", fileName));
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue("Missing source contract signature: " + signature, start >= 0);
|
||||
int openBrace = source.indexOf('{', start);
|
||||
assertTrue("Missing source contract method body: " + signature, openBrace >= 0);
|
||||
int depth = 0;
|
||||
for (int index = openBrace; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source contract method: " + signature);
|
||||
}
|
||||
}
|
||||
-244
@@ -1,244 +0,0 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedGenerationLeaseContractTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void biomeRuntimeReadsAreGenerationLeased() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/IrisModdedBiomeSource.java");
|
||||
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_structure_biome\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_visible_biome\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_biomes_within\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_structure_reachability\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_structure_state_biome\")"));
|
||||
assertTrue(source.contains("tryAcquireGenerationLease(engine, \"modded_structure_biome_keys\")"));
|
||||
assertTrue(source.contains("engine == null || engine.isClosed()"));
|
||||
assertTrue(source.contains("catch (GenerationSessionException e)"));
|
||||
assertTrue(source.contains("e.isExpectedTeardown()"));
|
||||
assertTrue(source.contains("Iris structure biome key lookup was rejected during an engine transition"));
|
||||
assertTrue(source.contains("int runtimeIdentity = engine.getCacheID();"));
|
||||
assertTrue(source.contains("cache.get(runtimeIdentity, key)"));
|
||||
assertTrue(source.contains("cache.put(runtimeIdentity, key, resolved)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void terrainAndHeightQueriesShareGenerationSessions() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/IrisModdedChunkGenerator.java");
|
||||
|
||||
assertTrue(source.contains("generationEngine.acquireGenerationLease(\"modded_chunk_pipeline\")"));
|
||||
assertTrue(source.contains("current.acquireGenerationLease(\"modded_base_height\")"));
|
||||
assertTrue(source.contains("current.acquireGenerationLease(\"modded_base_column\")"));
|
||||
assertTrue(source.contains("current.acquireGenerationLease(\"modded_configured_biome_keys\")"));
|
||||
assertTrue(source.contains("generationEngine.isClosing() || e.isExpectedTeardown()"));
|
||||
String terrain = method(source, "private ChunkAccess generateTerrain(");
|
||||
int sessionFailure = terrain.indexOf("catch (GenerationSessionException e)");
|
||||
int generalFailure = terrain.indexOf("catch (Throwable e)", sessionFailure);
|
||||
String sessionHandler = terrain.substring(sessionFailure, generalFailure);
|
||||
assertTrue(sessionHandler.contains("throw new IllegalStateException"));
|
||||
assertFalse(sessionHandler.contains("return chunk;"));
|
||||
|
||||
String references = method(source, "public void createReferences(");
|
||||
assertTrue(references.contains("requireGenerationLease(current, \"modded_create_references\")"));
|
||||
assertTrue(references.contains("IrisContext.open(current, lease.sessionId(), null)"));
|
||||
assertTrue(references.contains("NativeStructureReferenceRepair.createReferences("));
|
||||
assertFalse(references.contains("super.createReferences(level, structureManager, chunk);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void preservationDereferencesSharedPackData() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/service/ModdedPreservationService.java");
|
||||
int dereference = source.indexOf("public void dereference()");
|
||||
int irisData = source.indexOf("IrisData.dereference();", dereference);
|
||||
int trackedResources = source.indexOf("threads.removeIf", dereference);
|
||||
|
||||
assertTrue(dereference >= 0);
|
||||
assertTrue(irisData > dereference);
|
||||
assertTrue(trackedResources > irisData);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hotloadShutdownOnlyTargetsItsOwnWorld() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/service/ModdedStudioHotloadService.java");
|
||||
int shutdown = source.indexOf("public void shutdownPregenerator(Engine engine)");
|
||||
int nextMethod = source.indexOf("private boolean throttled", shutdown);
|
||||
String method = source.substring(shutdown, nextMethod);
|
||||
|
||||
assertTrue(method.contains("ModdedPregenJob.shutdownForWorld(world.identity());"));
|
||||
assertFalse(method.contains("PregeneratorJob.shutdownInstance();"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void maintenanceOwnsOneGenerationSessionUntilWorkCompletes() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/service/ModdedEngineMaintenanceService.java");
|
||||
String maintain = method(source, "private void maintain(Engine engine, long scheduledAt)");
|
||||
|
||||
assertTrue(maintain.contains("engine.acquireGenerationLease(\"modded_engine_maintenance\")"));
|
||||
assertTrue(maintain.contains("IrisContext.open(engine, lease.sessionId(), null)"));
|
||||
assertTrue(maintain.contains("exception.isExpectedTeardown()"));
|
||||
assertTrue(source.contains("active.awaitTermination(INTERRUPT_DRAIN_TIMEOUT_SECONDS, TimeUnit.SECONDS)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packDownloadAdmissionPrecedesAsyncDispatch() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/command/IrisModdedCommands.java");
|
||||
String download = method(source, "static int download(CommandSourceStack source, String rawRequest)");
|
||||
int admission = download.indexOf("LifecycleOperationCoordinator.get().acquire(");
|
||||
int executionTracking = download.indexOf("new PackDownloadExecution(");
|
||||
int dispatch = download.indexOf("scheduler.asyncIfRunning(execution, execution::cancel)");
|
||||
|
||||
assertTrue(admission >= 0);
|
||||
assertTrue(executionTracking > admission);
|
||||
assertTrue(dispatch > admission);
|
||||
assertTrue(download.contains("execution.cancel();"));
|
||||
|
||||
String execution = method(source, "private static void executeDownload(");
|
||||
assertTrue(execution.contains("PackDownloader.DownloadCancellation cancellation"));
|
||||
assertTrue(execution.contains("catch (PackDownloader.PackDownloadCancelledException error)"));
|
||||
assertTrue(execution.contains("dispatchDownloadFeedback(source,"));
|
||||
assertFalse(execution.contains("scheduler.global("));
|
||||
assertFalse(execution.contains("lease.close();"));
|
||||
|
||||
String feedback = method(source, "private static void dispatchDownloadFeedback(");
|
||||
assertTrue(feedback.contains("source.getServer().execute(feedback);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packDownloadsDrainBeforeModdedSchedulerShutdown() throws IOException {
|
||||
String commands = source("art/arcane/iris/modded/command/IrisModdedCommands.java");
|
||||
String shutdownDownloads = method(commands, "public static void shutdownDownloads()");
|
||||
assertTrue(shutdownDownloads.contains("downloadAdmissionOpen = false;"));
|
||||
assertTrue(shutdownDownloads.contains("execution.cancel();"));
|
||||
assertTrue(shutdownDownloads.contains("execution.await("));
|
||||
|
||||
String bootstrap = source("art/arcane/iris/modded/ModdedEngineBootstrap.java");
|
||||
String stop = method(bootstrap, "public static void stop()");
|
||||
int downloads = stop.indexOf("IrisModdedCommands::shutdownDownloads");
|
||||
int scheduler = stop.indexOf("scheduler::shutdown");
|
||||
assertTrue(downloads >= 0);
|
||||
assertTrue(scheduler > downloads);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moddedSchedulerRejectsAndCancelsAbandonedDownloadSubmissions() throws IOException {
|
||||
String scheduler = source("art/arcane/iris/modded/ModdedScheduler.java");
|
||||
String dispatch = method(scheduler, "public boolean asyncIfRunning(Runnable task, Runnable rejection)");
|
||||
assertTrue(dispatch.contains("RejectionAwareTask"));
|
||||
assertTrue(dispatch.contains("Objects.requireNonNull(rejection"));
|
||||
|
||||
String shutdown = method(scheduler, "public void shutdown()");
|
||||
assertTrue(shutdown.contains("asyncExecutor.shutdownNow()"));
|
||||
assertTrue(shutdown.contains("rejectAbandonedTasks(abandonedTasks);"));
|
||||
|
||||
String reset = method(scheduler, "public void reset()");
|
||||
assertTrue(reset.contains("asyncExecutor.getQueue().drainTo(abandonedTasks);"));
|
||||
assertTrue(reset.contains("rejectAbandonedTasks(abandonedTasks);"));
|
||||
|
||||
String rejection = method(scheduler, "private static void rejectAbandonedTasks(");
|
||||
assertTrue(rejection.contains("rejectionAwareTask.reject();"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockingPregenShutdownDefersItsFinalSaveToTheServerThread() throws IOException {
|
||||
String jobSource = source("art/arcane/iris/modded/command/ModdedPregenJob.java");
|
||||
String shutdown = method(jobSource, "private static boolean shutdownAndSave(");
|
||||
assertTrue(shutdown.contains("deferFinalSaveToServerThread()"));
|
||||
assertTrue(shutdown.contains("completeDeferredFinalSave()"));
|
||||
assertTrue(shutdown.contains("cancelDeferredFinalSave()"));
|
||||
|
||||
String methodSource = source("art/arcane/iris/modded/command/ModdedPregenMethod.java");
|
||||
String close = method(methodSource, "public void close()");
|
||||
assertTrue(close.contains("deferFinalSaveIfRequested()"));
|
||||
|
||||
String await = method(methodSource, "private void awaitFinalSave(");
|
||||
assertTrue(await.contains("FINAL_SAVE_POLL_MILLIS"));
|
||||
assertTrue(await.contains("deferFinalSaveIfRequested()"));
|
||||
|
||||
String complete = method(methodSource, "void completeDeferredFinalSave()");
|
||||
int cancel = complete.indexOf("cancelQueuedFinalSave();");
|
||||
int directSave = complete.indexOf("saveLevelOnServerThread();");
|
||||
assertTrue(cancel >= 0);
|
||||
assertTrue(directSave > cancel);
|
||||
|
||||
String queuedSave = method(methodSource, "private void executeFinalSave(");
|
||||
assertTrue(queuedSave.contains("if (!request.claim())"));
|
||||
String cancelRequest = method(methodSource, "private void cancelQueuedFinalSave()");
|
||||
assertTrue(cancelRequest.contains("request.cancel();"));
|
||||
String pending = method(methodSource, "boolean hasPendingFinalSave()");
|
||||
assertTrue(pending.contains("queuedFinalSave.get() != null"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void firstPregenChunkFailureRetainsItsFullCause() throws IOException {
|
||||
String methodSource = source("art/arcane/iris/modded/command/ModdedPregenMethod.java");
|
||||
String logFailure = method(methodSource, "private void logChunkFailure(");
|
||||
|
||||
assertTrue(methodSource.contains("AtomicBoolean failureDetailLogged"));
|
||||
assertTrue(logFailure.contains("unwrap(failure)"));
|
||||
assertTrue(logFailure.contains("failureDetailLogged.compareAndSet(false, true)"));
|
||||
assertTrue(logFailure.contains("x, z, cause);"));
|
||||
assertTrue(logFailure.contains("cause.toString()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pregenReportsOnlyCleanupCallbacksFromCoveredTargets() throws IOException {
|
||||
String source = source("art/arcane/iris/modded/command/ModdedPregenMethod.java");
|
||||
String cleanup = method(source, "private void cleanupMantleChunksCoveredBy(");
|
||||
|
||||
assertTrue(source.contains("cleanupMantleChunksCoveredBy(x, z, listener);"));
|
||||
assertFalse(source.contains("cleanupMantleChunk(x, z);"));
|
||||
assertTrue(cleanup.contains("cleanupChunksCoveredBy(x, z, true, listener::onChunkCleaned)"));
|
||||
assertFalse(cleanup.contains("forceCleanupChunk"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void invalidPregenBoundsFailBeforeRuntimeMutation() throws IOException {
|
||||
String jobSource = source("art/arcane/iris/modded/command/ModdedPregenJob.java");
|
||||
String start = method(jobSource, "public static boolean start(");
|
||||
int taskConstruction = start.indexOf("PregenTask task = PregenTask.builder()");
|
||||
int profileMutation = start.indexOf("PregenPerformanceProfile.apply(engine)");
|
||||
|
||||
assertTrue(taskConstruction >= 0);
|
||||
assertTrue(profileMutation > taskConstruction);
|
||||
|
||||
String commandSource = source("art/arcane/iris/modded/command/ModdedPregenCommands.java");
|
||||
String command = method(commandSource, "static int pregenStart(");
|
||||
assertTrue(command.contains("catch (IllegalArgumentException failure)"));
|
||||
assertTrue(command.contains("IrisModdedCommands.fail(source, failure.getMessage())"));
|
||||
}
|
||||
|
||||
private static String source(String relativePath) throws IOException {
|
||||
String root = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
assertTrue("Missing system property " + SOURCE_ROOT_PROPERTY, root != null && !root.isBlank());
|
||||
return Files.readString(Path.of(root, relativePath));
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue("Missing source contract signature: " + signature, start >= 0);
|
||||
int openBrace = source.indexOf('{', start);
|
||||
assertTrue("Missing source contract method body: " + signature, openBrace >= 0);
|
||||
int depth = 0;
|
||||
for (int index = openBrace; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source contract method: " + signature);
|
||||
}
|
||||
}
|
||||
-58
@@ -2,32 +2,9 @@ package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* The level router is a switch statement, which the compiler does not check for exhaustiveness. A level added
|
||||
* to {@link art.arcane.iris.spi.LogLevel} with no arm here would compile and then drop every message at that
|
||||
* level without a trace, so the fallback arm is what keeps a new level visible on the modded loaders.
|
||||
*/
|
||||
public class ModdedIrisLogLevelCoverageTest {
|
||||
@Test
|
||||
public void everyLogLevelReachesTheLoggerIncludingOnesAddedLater() throws IOException {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedIrisLog.java"));
|
||||
int router = source.indexOf("public static void log(LogLevel level, String message)");
|
||||
assertTrue("log(LogLevel, String) not found", router >= 0);
|
||||
|
||||
String body = source.substring(router, source.indexOf("public static void debug(", router));
|
||||
assertTrue(body, body.contains("default -> info(message);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void slf4jStyleArgumentsAndTrailingThrowableArePreserved() {
|
||||
RuntimeException failure = new RuntimeException("broken");
|
||||
@@ -37,39 +14,4 @@ public class ModdedIrisLogLevelCoverageTest {
|
||||
assertEquals("chunk 4,9 failed", rendered.message());
|
||||
assertEquals(failure, rendered.error());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void formattedDebugThrowableUsesTheVisibleDebugRouteWhenEnabled() throws IOException {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedIrisLog.java"));
|
||||
int start = source.indexOf("public static void debug(String format, Object... arguments)");
|
||||
int end = source.indexOf("public static void info(String message)", start);
|
||||
assertTrue("formatted debug overload not found", start >= 0);
|
||||
assertTrue("formatted debug overload boundary not found", end > start);
|
||||
String body = source.substring(start, end);
|
||||
|
||||
assertTrue(body, body.contains("if (!debugEnabled())"));
|
||||
assertTrue(body, body.contains("LOGGER.info(\"[Iris/DEBUG] \" + clean(rendered.message()), rendered.error())"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void moddedProductionUsesTheIrisLogFrontDoor() throws IOException {
|
||||
Path root = Path.of(System.getProperty("iris.moddedCommonSources"));
|
||||
try (Stream<Path> files = Files.walk(root)) {
|
||||
List<Path> bypasses = files
|
||||
.filter(path -> path.toString().endsWith(".java"))
|
||||
.filter(path -> !path.getFileName().toString().equals("ModdedIrisLog.java"))
|
||||
.filter(path -> {
|
||||
try {
|
||||
String source = Files.readString(path);
|
||||
return source.contains("LoggerFactory.getLogger")
|
||||
|| source.contains("private static final Logger LOGGER");
|
||||
} catch (IOException unreadable) {
|
||||
throw new IllegalStateException(unreadable);
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
assertTrue(bypasses.toString(), bypasses.isEmpty());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-23
@@ -2,10 +2,6 @@ package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
@@ -48,25 +44,6 @@ public class ModdedLevelDataAttachmentTest {
|
||||
assertNull(failure.getCause());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void initializesLevelDataBeforeBindingAndPublication() throws IOException {
|
||||
String source = Files.readString(sourcePath("ModdedDimensionManager.java"));
|
||||
int construction = source.indexOf("ServerLevel level = new ServerLevel(");
|
||||
int initialization = source.indexOf("serverAccess.initializeLevelData(server, level);", construction);
|
||||
int binding = source.indexOf("generator.bindLevel(level);", initialization);
|
||||
int publication = source.indexOf("serverAccess.putLevelIfAbsent(server, key, level);", binding);
|
||||
|
||||
assertTrue(construction >= 0);
|
||||
assertTrue(initialization > construction);
|
||||
assertTrue(binding > initialization);
|
||||
assertTrue(publication > binding);
|
||||
}
|
||||
|
||||
private static Path sourcePath(String fileName) {
|
||||
return Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded", fileName);
|
||||
}
|
||||
|
||||
public static final class AttachedLevelData {
|
||||
private RootData rootData;
|
||||
private DimensionKey dimensionKey;
|
||||
|
||||
-436
@@ -1,436 +0,0 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedLifecycleFailureContractTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void existingDimensionStagesWithoutReplacingTheLiveEngine() throws IOException {
|
||||
String managerSource = source("ModdedDimensionManager.java");
|
||||
String create = method(managerSource, "private static Handle create(");
|
||||
int existingStart = requiredIndex(create, "if (existing != null");
|
||||
int presentStart = requiredIndex(create, "if (serverAccess.hasLevel(server, key))", existingStart + 1);
|
||||
int injectionStart = requiredIndex(create, "try {", presentStart);
|
||||
|
||||
assertFalse(create.substring(existingStart, presentStart).contains("repointAndBind("));
|
||||
assertFalse(create.substring(presentStart, injectionStart).contains("repointAndBind("));
|
||||
assertBefore(create, "ModdedGenerationHistoryStorage.createOrStage(", "if (existing != null");
|
||||
|
||||
String generatorSource = source("IrisModdedChunkGenerator.java");
|
||||
String repointAndBind = method(generatorSource, "void repointAndBind(");
|
||||
assertBefore(repointAndBind, "ModdedWorldEngines.prepareReplacement(",
|
||||
"ModdedWorldEngines.installReplacement(");
|
||||
assertBefore(repointAndBind, "ModdedWorldEngines.installReplacement(", "this.activePack = pack;");
|
||||
String failure = catchBlock(repointAndBind);
|
||||
assertTrue(failure.contains("ModdedWorldEngines.closeUnregistered(replacement);"));
|
||||
assertTrue(failure.contains("addSuppressed("));
|
||||
|
||||
String worldEnginesSource = source("ModdedWorldEngines.java");
|
||||
String installReplacement = method(worldEnginesSource, "static void installReplacement(");
|
||||
assertTrue(installReplacement.contains("ENGINES.compute("));
|
||||
assertBefore(installReplacement, "close(current);", "return activeReplacement;");
|
||||
assertFalse(installReplacement.contains("catch ("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentPackUpdatesUseAnExplicitRestartBoundPath() throws IOException {
|
||||
String managerSource = source("ModdedDimensionManager.java");
|
||||
String createPersistent = method(managerSource, "public static Handle createPersistent(");
|
||||
String update = method(managerSource, "public static UpdateResult stagePersistentUpdate(");
|
||||
|
||||
assertBefore(createPersistent, "if (previous != null)", "ModdedDimensionRegistryStore.put(");
|
||||
assertTrue(createPersistent.contains("Use the Iris world update command"));
|
||||
assertTrue(update.contains("ModdedGenerationHistoryStorage.createOrStage("));
|
||||
assertTrue(update.contains("active.history().pendingActivation()"));
|
||||
assertTrue(update.contains("restartRequired()"));
|
||||
assertFalse(update.contains("inject("));
|
||||
assertFalse(update.contains("repointAndBind("));
|
||||
assertBefore(update, "ModdedDimensionRegistryStore.put(server, updated);",
|
||||
"ModdedGenerationHistoryStorage.createOrStage(");
|
||||
assertTrue(update.contains("ModdedDimensionRegistryStore.put(server, previous);"));
|
||||
|
||||
String commandsSource = source("command/ModdedWorldCommands.java");
|
||||
assertTrue(commandsSource.contains("root.then(updateTree());"));
|
||||
String command = method(commandsSource, "private static int update(");
|
||||
assertTrue(command.contains("ModdedDimensionManager.stagePersistentUpdate("));
|
||||
assertTrue(command.contains("Restart the server to activate it"));
|
||||
assertTrue(command.contains("running world remains on its current pack"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void injectionBindsBeforeRegistrationAndRollsBackEveryFailure() throws IOException {
|
||||
String source = source("ModdedDimensionManager.java");
|
||||
String injection = method(source, "private static Handle inject(");
|
||||
|
||||
assertBefore(injection, "generator.bindLevel(level);", "serverAccess.putLevelIfAbsent(server, key, level);");
|
||||
assertBefore(injection, "serverAccess.putLevelIfAbsent(server, key, level);",
|
||||
"server.getPlayerList().addWorldborderListener(level);");
|
||||
assertFalse(injection.contains("serverAccess.putLevel(server, key, previous);"));
|
||||
String failure = catchBlock(injection);
|
||||
assertBefore(failure, "rollbackInjection(", "throw ");
|
||||
|
||||
String rollback = method(source, "void rollbackInjection(");
|
||||
assertBefore(rollback, "serverAccess.hasLevel(server, key)", "serverAccess.removeLevel(server, key);");
|
||||
assertTrue(rollback.contains("generator.unbindEngine("));
|
||||
assertTrue(rollback.contains("level.close();"));
|
||||
assertTrue(rollback.contains("addSuppressed("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentReinjectionQuarantinesBrokenEntriesAndContinues() throws IOException {
|
||||
String source = source("ModdedStartup.java");
|
||||
String reinjection = method(source, "private static void reinjectPersistentDimensions(");
|
||||
String failure = catchBlock(reinjection);
|
||||
|
||||
assertTrue(failure.contains("ModdedIrisLog.error("));
|
||||
assertFalse(failure.contains("e.toString()"));
|
||||
assertFalse(failure.contains("throw new IllegalStateException("));
|
||||
assertTrue(reinjection.contains("injected++;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentReinjectionWaitsForPlayerServicesAndServerStarted() throws IOException {
|
||||
String bootstrapSource = source("ModdedEngineBootstrap.java");
|
||||
String start = method(bootstrapSource, "public static void start(");
|
||||
String serverAboutToStart = method(bootstrapSource, "public static void serverAboutToStart(");
|
||||
String serverStarted = method(bootstrapSource, "public static void serverStarted(");
|
||||
|
||||
assertFalse(start.contains("ModdedStartup.runOnce(server);"));
|
||||
assertFalse(serverAboutToStart.contains("ModdedStartup.runOnce(server);"));
|
||||
assertEquals(1, occurrences(serverStarted, "ModdedStartup.runOnce(server);"));
|
||||
assertBefore(serverStarted, "bindWorldGenerators(server);", "ModdedStartup.runOnce(server);");
|
||||
assertBefore(serverStarted, "ModdedStartup.runOnce(server);", "reconcileSpawn(server);");
|
||||
|
||||
String startupSource = source("ModdedStartup.java");
|
||||
String runOnce = method(startupSource, "public static void runOnce(");
|
||||
assertBefore(runOnce, "server.getPlayerList() == null", "STARTED.compareAndSet(false, true)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dynamicLevelMutationsInvalidateLoaderTickCaches() throws IOException {
|
||||
String levelsSource = source("ModdedServerLevels.java");
|
||||
String put = method(levelsSource, "public ServerLevel putLevel(");
|
||||
String putIfAbsent = method(levelsSource, "public ServerLevel putLevelIfAbsent(");
|
||||
String remove = method(levelsSource, "public ServerLevel removeLevel(");
|
||||
|
||||
assertBefore(put, "server.levels.put(key, level);", "levelCacheInvalidator.accept(server);");
|
||||
assertTrue(put.contains("if (previous != level)"));
|
||||
assertBefore(putIfAbsent, "server.levels.putIfAbsent(key, level);", "levelCacheInvalidator.accept(server);");
|
||||
assertTrue(putIfAbsent.contains("if (previous == null)"));
|
||||
assertBefore(remove, "server.levels.remove(key);", "levelCacheInvalidator.accept(server);");
|
||||
assertTrue(remove.contains("if (removed != null)"));
|
||||
|
||||
String loaderSource = source("ModdedLoader.java");
|
||||
assertTrue(loaderSource.contains("void invalidateLevelCache(MinecraftServer server);"));
|
||||
String bootstrapSource = source("ModdedEngineBootstrap.java");
|
||||
assertTrue(bootstrapSource.contains("new ModdedServerLevels(boundLoader::invalidateLevelCache)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentRestoreOpensHistoryBeforeConsultingTheGlobalPack() throws IOException {
|
||||
String source = source("ModdedGenerationHistoryStorage.java");
|
||||
String restore = method(source, "static GenerationHistory restoreOrAdopt(");
|
||||
|
||||
assertBefore(restore, "GenerationHistory.openIfPresent(", "sourceSupplier\").get();");
|
||||
assertTrue(restore.contains("if (existing.isPresent())"));
|
||||
assertBefore(restore, "requireRegistryDefinitions(existing.get()", "return existing.get();");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void productionPackResolutionNeverUsesTheParityProbeAsData() throws IOException {
|
||||
String source = source("ModdedWorldEngines.java");
|
||||
String resolvePack = method(source, "static File resolvePack(");
|
||||
|
||||
assertFalse(source.contains("System.getProperty(\"iris.parity\")"));
|
||||
assertFalse(source.contains("parityPack"));
|
||||
assertFalse(source.contains("falling back to parity pack"));
|
||||
assertTrue(resolvePack.contains("throw new IllegalStateException("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void engineBootstrapResetsStartupExactlyOnceDuringStop() throws IOException {
|
||||
String source = source("ModdedEngineBootstrap.java");
|
||||
String stop = method(source, "public static void stop(");
|
||||
|
||||
assertEquals(1, occurrences(stop, "ModdedStartup::reset"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void engineBootstrapAttemptsEveryShutdownStageAndAggregatesFailures() throws IOException {
|
||||
String source = source("ModdedEngineBootstrap.java");
|
||||
String stop = method(source, "public static void stop(");
|
||||
|
||||
assertBefore(stop, "\"services\"", "\"world engines\"");
|
||||
assertBefore(stop, "\"world engines\"", "\"dimension manager\"");
|
||||
assertBefore(stop, "\"server state\"", "if (failure != null)");
|
||||
assertFalse(stop.contains("throw"));
|
||||
assertTrue(stop.contains("ModdedIrisLog.error(\"Iris modded shutdown completed with failures\", failure);"));
|
||||
|
||||
String runStage = method(source, "private static Throwable runStopStage(");
|
||||
assertTrue(runStage.contains("catch (Throwable stageFailure)"));
|
||||
assertTrue(runStage.contains("failure.addSuppressed(stageFailure);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void worldShutdownRemovesOnlySuccessfullyClosedMappings() throws IOException {
|
||||
String source = source("ModdedWorldEngines.java");
|
||||
String shutdown = method(source, "public static void shutdown()");
|
||||
|
||||
assertTrue(shutdown.contains("new ArrayList<>(ENGINES.entrySet())"));
|
||||
assertFalse(shutdown.contains("ENGINES.clear()"));
|
||||
// Bound generators must go through unbindEngine (latch unloading, then strict evict) so a
|
||||
// late chunk-system drain cannot rebuild an engine nothing closes again.
|
||||
assertTrue(shutdown.contains("generator.unbindEngine(level);"));
|
||||
assertBefore(shutdown, "close(engine);", "ENGINES.remove(level, engine)");
|
||||
assertTrue(shutdown.contains("ENGINES.containsKey(level)"));
|
||||
assertTrue(shutdown.contains("failure.addSuppressed(e);"));
|
||||
assertTrue(shutdown.contains("failed mappings were retained"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void engineEvictionRetainsTheMappingUntilCloseSucceeds() throws IOException {
|
||||
String source = source("ModdedWorldEngines.java");
|
||||
String eviction = method(source, "static void evictOrThrow(");
|
||||
|
||||
assertTrue(eviction.contains("ENGINES.computeIfPresent("));
|
||||
assertFalse(eviction.contains("ENGINES.remove("));
|
||||
assertBefore(eviction, "close(current);", "return null;");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatorUnbindRetainsOwnershipWhenEvictionFails() throws IOException {
|
||||
String source = source("IrisModdedChunkGenerator.java");
|
||||
String unbind = method(source, "synchronized void unbindEngine(ServerLevel level)");
|
||||
|
||||
assertFalse(unbind.contains("finally"));
|
||||
assertBefore(unbind, "unloading = true;", "ModdedWorldEngines.evictOrThrow(level);");
|
||||
assertBefore(unbind, "ModdedWorldEngines.evictOrThrow(level);", "clearEngineBinding();");
|
||||
String binding = method(source, "private Engine bindEngine(ServerLevel level)");
|
||||
assertTrue(binding.contains("requireBindingAllowed();"));
|
||||
assertTrue(binding.contains("requireCompletedShutdown(cached);"));
|
||||
String bindLevel = method(source, "synchronized void bindLevel(ServerLevel level)");
|
||||
assertBefore(bindLevel, "requireCompletedShutdown(engine);", "unloading = false;");
|
||||
String boundEngine = method(source, "public Engine engineIfBound()");
|
||||
assertTrue(boundEngine.contains("unloading || current == null || current.isClosing() || current.isClosed()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loaderUnloadSurfacesCloseFailureAndDynamicRemovalUsesStrictEviction() throws IOException {
|
||||
String bootstrapSource = source("ModdedEngineBootstrap.java");
|
||||
String unload = method(bootstrapSource, "public static void levelUnloaded(ServerLevel level)");
|
||||
String failure = catchBlock(unload);
|
||||
assertTrue(failure.contains("ModdedIrisLog.error("));
|
||||
assertTrue(failure.contains("throw "));
|
||||
|
||||
String managerSource = source("ModdedDimensionManager.java");
|
||||
String remove = method(managerSource, "public static boolean remove(MinecraftServer server, String dimensionId, boolean wipeStorage)");
|
||||
assertTrue(remove.contains("ModdedWorldEngines.evictOrThrow(level);"));
|
||||
assertFalse(remove.contains("ModdedWorldEngines.evict(level);"));
|
||||
assertTrue(remove.contains("unloadEventStarted = true;"));
|
||||
assertTrue(remove.contains("rollbackRemoval(server, serverAccess, key, level, generator, unloadEventStarted, e);"));
|
||||
|
||||
String rollback = method(managerSource, "private static void rollbackRemoval(");
|
||||
assertTrue(rollback.contains("serverAccess.hasLevel(server, key)"));
|
||||
assertTrue(rollback.contains("generator.bindLevel(level);"));
|
||||
assertTrue(rollback.contains("failure.addSuppressed(rollbackFailure);"));
|
||||
assertTrue(rollback.contains("ModdedIrisLog.error("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void engineBootstrapRollsBackEveryPublishedBindingBeforeRethrowing() throws IOException {
|
||||
String source = source("ModdedEngineBootstrap.java");
|
||||
String bind = method(source, "public static ModdedPlatform bind(");
|
||||
String failure = catchBlock(bind);
|
||||
|
||||
assertBefore(bind, "createdServices.enableAll();", "runtime = new BoundRuntime(");
|
||||
assertTrue(bind.contains("rollback.add(() -> GuiHost.suppressDesktop("));
|
||||
assertTrue(bind.contains("rollback.add(() -> restorePlatform("));
|
||||
assertTrue(bind.contains("rollback.add(() -> ModdedDimensionManager.restoreAccess("));
|
||||
assertTrue(bind.contains("rollback.add(() -> IrisObjectRotation.restorePlatformRotator("));
|
||||
assertTrue(bind.contains("rollback.add(() -> BlockDataMergeSupport.restorePlatformMerger("));
|
||||
assertTrue(bind.contains("rollback.add(() -> TileData.restorePlatformReader("));
|
||||
assertTrue(bind.contains("rollback.add(() -> TileData.restorePlatformFactory("));
|
||||
assertTrue(bind.contains("rollback.add(() -> GuiHost.set("));
|
||||
assertTrue(bind.contains("rollback.add(() -> DecoratorPlatformHooks.restore("));
|
||||
assertTrue(bind.contains("bindService(PreservationRegistry.class"));
|
||||
assertTrue(bind.contains("bindService(EngineEffectsProvider.class"));
|
||||
assertTrue(bind.contains("bindService(EnginePlatformHooks.class"));
|
||||
assertTrue(bind.contains("bindService(EngineWorldManagerProvider.class"));
|
||||
assertTrue(bind.contains("rollback.add(customContentDiscovery::rollback);"));
|
||||
assertBefore(failure, "createdServices.rollback(failure);", "rollback.restore(failure);");
|
||||
assertBefore(failure, "rollback.restore(failure);", "throw ");
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = requiredIndex(source, first);
|
||||
int secondIndex = requiredIndex(source, second);
|
||||
assertTrue(first + " must occur before " + second, firstIndex < secondIndex);
|
||||
}
|
||||
|
||||
private static int requiredIndex(String source, String needle) {
|
||||
return requiredIndex(source, needle, 0);
|
||||
}
|
||||
|
||||
private static int requiredIndex(String source, String needle, int start) {
|
||||
int index = source.indexOf(needle, start);
|
||||
assertTrue("Missing source contract token: " + needle, index >= 0);
|
||||
return index;
|
||||
}
|
||||
|
||||
private static String source(String fileName) throws IOException {
|
||||
String sourceRoot = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
assertTrue("Missing system property " + SOURCE_ROOT_PROPERTY,
|
||||
sourceRoot != null && !sourceRoot.isBlank());
|
||||
Path sourcePath = Path.of(sourceRoot, "art", "arcane", "iris", "modded", fileName);
|
||||
return Files.readString(sourcePath);
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = requiredIndex(source, signature);
|
||||
int openBrace = openingBrace(source, start);
|
||||
return source.substring(start, matchingBrace(source, openBrace) + 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the block of the shallowest catch in the method, i.e. the one attached to the
|
||||
* method's outermost try. Textual first-match would return a nested catch instead, and a
|
||||
* nested catch handles a different failure than the one these contracts pin.
|
||||
*/
|
||||
private static String catchBlock(String source) {
|
||||
int bodyStart = openingBrace(source, 0);
|
||||
int depth = 0;
|
||||
int bestDepth = Integer.MAX_VALUE;
|
||||
int catchIndex = -1;
|
||||
int i = bodyStart;
|
||||
while (i < source.length()) {
|
||||
int skipped = skipNonCode(source, i);
|
||||
if (skipped > i) {
|
||||
i = skipped;
|
||||
continue;
|
||||
}
|
||||
char current = source.charAt(i);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
break;
|
||||
}
|
||||
} else if (current == 'c' && depth < bestDepth && source.startsWith("catch (", i)) {
|
||||
bestDepth = depth;
|
||||
catchIndex = i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
assertTrue("Missing source contract token: catch (", catchIndex >= 0);
|
||||
return blockAt(source, openingBrace(source, catchIndex));
|
||||
}
|
||||
|
||||
private static String blockAt(String source, int openBrace) {
|
||||
return source.substring(openBrace, matchingBrace(source, openBrace) + 1);
|
||||
}
|
||||
|
||||
private static int openingBrace(String source, int start) {
|
||||
int i = start;
|
||||
while (i < source.length()) {
|
||||
int skipped = skipNonCode(source, i);
|
||||
if (skipped > i) {
|
||||
i = skipped;
|
||||
continue;
|
||||
}
|
||||
if (source.charAt(i) == '{') {
|
||||
return i;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
throw new IllegalArgumentException("No source block after offset " + start);
|
||||
}
|
||||
|
||||
private static int matchingBrace(String source, int openBrace) {
|
||||
int depth = 0;
|
||||
int i = openBrace;
|
||||
while (i < source.length()) {
|
||||
int skipped = skipNonCode(source, i);
|
||||
if (skipped > i) {
|
||||
i = skipped;
|
||||
continue;
|
||||
}
|
||||
char current = source.charAt(i);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
i++;
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source block");
|
||||
}
|
||||
|
||||
/**
|
||||
* Advances past a comment, string, char or text block starting at the index, otherwise returns
|
||||
* the index unchanged. Comments must be skipped: an apostrophe in prose ("generator's") would
|
||||
* otherwise open a char literal and swallow the rest of the file.
|
||||
*/
|
||||
private static int skipNonCode(String source, int index) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '/' && index + 1 < source.length()) {
|
||||
char next = source.charAt(index + 1);
|
||||
if (next == '/') {
|
||||
int end = source.indexOf('\n', index);
|
||||
return end < 0 ? source.length() : end + 1;
|
||||
}
|
||||
if (next == '*') {
|
||||
int end = source.indexOf("*/", index + 2);
|
||||
return end < 0 ? source.length() : end + 2;
|
||||
}
|
||||
return index;
|
||||
}
|
||||
if (current == '"' && source.startsWith("\"\"\"", index)) {
|
||||
int end = source.indexOf("\"\"\"", index + 3);
|
||||
return end < 0 ? source.length() : end + 3;
|
||||
}
|
||||
if (current == '"' || current == '\'') {
|
||||
return endOfLiteral(source, index, current);
|
||||
}
|
||||
return index;
|
||||
}
|
||||
|
||||
private static int endOfLiteral(String source, int index, char terminator) {
|
||||
for (int i = index + 1; i < source.length(); i++) {
|
||||
char current = source.charAt(i);
|
||||
if (current == '\\') {
|
||||
i++;
|
||||
continue;
|
||||
}
|
||||
if (current == terminator) {
|
||||
return i + 1;
|
||||
}
|
||||
}
|
||||
return source.length();
|
||||
}
|
||||
|
||||
private static int occurrences(String source, String needle) {
|
||||
int count = 0;
|
||||
int offset = 0;
|
||||
while (true) {
|
||||
int index = source.indexOf(needle, offset);
|
||||
if (index < 0) {
|
||||
return count;
|
||||
}
|
||||
count++;
|
||||
offset = index + needle.length();
|
||||
}
|
||||
}
|
||||
}
|
||||
-37
@@ -5,13 +5,9 @@ import art.arcane.iris.engine.object.IrisEntitySpawn;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedSpawnerRarityParityTest {
|
||||
private static IrisEntitySpawn spawn(int rarity) {
|
||||
@@ -39,37 +35,4 @@ public class ModdedSpawnerRarityParityTest {
|
||||
assertEquals(1, IRare.get(spawn(-5)));
|
||||
assertEquals(3, IRare.get(spawn(3)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void perPositionSpawnLoopsDoNotRerollEntryRarity() throws Exception {
|
||||
String source = Files.readString(Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedWorldManager.java"));
|
||||
|
||||
for (String method : List.of("private int spawnEntry(", "private int spawnEntryAt(")) {
|
||||
String body = methodBody(source, method);
|
||||
assertFalse(method + " must not re-roll rarity per position; rarityPick already weighted the pool",
|
||||
body.contains("getRarity()"));
|
||||
assertTrue(method + " must keep the min/max spawn-count roll",
|
||||
body.contains("LootResolver.inclusive("));
|
||||
}
|
||||
}
|
||||
|
||||
private static String methodBody(String source, String declaration) {
|
||||
int start = source.indexOf(declaration);
|
||||
assertTrue("ModdedWorldManager must declare " + declaration, start >= 0);
|
||||
int open = source.indexOf('{', start);
|
||||
int depth = 0;
|
||||
for (int index = open; index < source.length(); index++) {
|
||||
char character = source.charAt(index);
|
||||
if (character == '{') {
|
||||
depth++;
|
||||
} else if (character == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(open + 1, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new AssertionError(declaration + " is not brace balanced");
|
||||
}
|
||||
}
|
||||
|
||||
-30
@@ -1,30 +0,0 @@
|
||||
package art.arcane.iris.modded;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedStructurePlacementFailureContractTest {
|
||||
@Test
|
||||
public void structurePlacementPropagatesRuntimeFailuresWithContext() throws IOException {
|
||||
Path sourcePath = Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art/arcane/iris/modded/ModdedStructureHooks.java");
|
||||
String source = Files.readString(sourcePath);
|
||||
int methodStart = source.indexOf("public int[] placeStructure(");
|
||||
int methodEnd = source.indexOf("\n @Override", methodStart + 1);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
int catchStart = method.indexOf("catch (RuntimeException error)");
|
||||
|
||||
assertTrue(catchStart >= 0);
|
||||
String failurePath = method.substring(catchStart);
|
||||
assertTrue(failurePath.contains("throw NativeStructureGenerationException.failure("));
|
||||
assertTrue(failurePath.contains("\"capture placement\", structureKey, chunkX, chunkZ, error"));
|
||||
assertFalse(failurePath.contains("return null;"));
|
||||
assertFalse(failurePath.contains("catch (Throwable"));
|
||||
}
|
||||
}
|
||||
+4
-11
@@ -4,8 +4,6 @@ import org.junit.Test;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
@@ -64,14 +62,9 @@ public class ModdedStructureTemplateCacheTest {
|
||||
config = new String(input.readAllBytes(), StandardCharsets.UTF_8);
|
||||
}
|
||||
assertTrue(config.contains("\"StructureTemplatePaletteConcurrencyMixin\""));
|
||||
|
||||
Path source = Path.of(System.getProperty("iris.moddedCommonSources"))
|
||||
.resolve("art/arcane/iris/modded/mixin/StructureTemplatePaletteConcurrencyMixin.java");
|
||||
String mixin = Files.readString(source);
|
||||
assertTrue(mixin.contains("@Mixin(StructureTemplate.Palette.class)"));
|
||||
assertTrue(mixin.contains("@Shadow\n @Final\n @Mutable"));
|
||||
assertTrue(mixin.contains("private Map<Block, List<StructureTemplate.StructureBlockInfo>> cache;"));
|
||||
assertTrue(mixin.contains("@Inject(method = \"<init>(Ljava/util/List;)V\", at = @At(\"RETURN\"))"));
|
||||
assertTrue(mixin.contains("cache = new ConcurrentHashMap<>();"));
|
||||
assertNotNull(Class.forName(
|
||||
"art.arcane.iris.modded.mixin.StructureTemplatePaletteConcurrencyMixin",
|
||||
false,
|
||||
ModdedStructureTemplateCacheTest.class.getClassLoader()));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,9 +5,6 @@ import net.minecraft.world.level.ChunkPos;
|
||||
import net.minecraft.world.level.levelgen.structure.BoundingBox;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
@@ -26,32 +23,6 @@ public class ModdedWorldCheckTest {
|
||||
assertTrue(thread.isDaemon());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void poiAuditRunsInASecondServerTaskAfterVillageGeneration() throws IOException {
|
||||
Path sourceRoot = Path.of(System.getProperty("iris.moddedCommonSources"));
|
||||
String source = Files.readString(sourceRoot.resolve("art/arcane/iris/modded/ModdedWorldCheck.java"));
|
||||
String auditSource = Files.readString(
|
||||
sourceRoot.resolve("art/arcane/iris/modded/WorldCheckStructureAudit.java"));
|
||||
int preparationSubmit = source.indexOf(
|
||||
"WorldCheckPreparation preparation = serverRef.submit(() -> run(serverRef))");
|
||||
int completionSubmit = source.indexOf(
|
||||
"exitCode = serverRef.submit(() -> runAndRequestStop(", preparationSubmit);
|
||||
int completionMethod = source.indexOf("private static boolean completeWorldCheck");
|
||||
int deferredAudit = source.indexOf(
|
||||
"PoiAudit poi = WorldCheckStructureAudit.auditStructurePois", completionMethod);
|
||||
int structureMethod = auditSource.indexOf("private static StructureCheckResult checkNativeStructure");
|
||||
int structureMethodEnd = auditSource.indexOf("private static StructureStart resolveStructureStart",
|
||||
structureMethod);
|
||||
String structureSource = auditSource.substring(structureMethod, structureMethodEnd);
|
||||
|
||||
assertTrue(preparationSubmit >= 0);
|
||||
assertTrue(completionSubmit > preparationSubmit);
|
||||
assertTrue(deferredAudit > completionMethod);
|
||||
assertFalse(structureSource.contains("auditStructurePois"));
|
||||
assertFalse(source.contains("prepareDeferredAudits"));
|
||||
assertFalse(auditSource.contains("prepareDeferredAudits"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void validStructureStartIsGenerationEvidence() {
|
||||
assertTrue(WorldCheckPredicates.hasNativeStructureEvidence(true, 0));
|
||||
|
||||
-90
@@ -6,11 +6,6 @@ import net.minecraft.server.Bootstrap;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -22,25 +17,6 @@ public class NativeStructureFailureContractTest {
|
||||
Bootstrap.bootStrap();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureLocateDoesNotCatchAndFallThroughToAnotherImplementation() throws IOException {
|
||||
String source = moddedSource("IrisModdedChunkGenerator.java");
|
||||
int locateStart = source.indexOf("public Pair<BlockPos, Holder<Structure>> findNearestMapStructure");
|
||||
int locateEnd = source.indexOf("public boolean isNativeStructureReachable", locateStart);
|
||||
String locate = source.substring(locateStart, locateEnd);
|
||||
String stage = moddedSource("ModdedNativeStructureStage.java");
|
||||
int filterStart = stage.indexOf("HolderSet<Structure> filterReachableNativeStructures");
|
||||
int filterEnd = stage.indexOf("void adjustGeneratedStructures", filterStart);
|
||||
String filter = stage.substring(filterStart, filterEnd);
|
||||
|
||||
assertTrue(locate.contains("Engine current = engine();"));
|
||||
assertFalse(locate.contains("catch (Throwable"));
|
||||
assertFalse(locate.contains("return null;\n } catch"));
|
||||
assertTrue(filter.contains("unregistered structure holder"));
|
||||
assertFalse(filter.contains("catch (Throwable"));
|
||||
assertFalse(filter.contains("failed closed"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void globalStructureDisableRefusesGeneratorBinding() {
|
||||
IrisModdedChunkGenerator.requireGlobalStructureGeneration(true, "overworld:overworld");
|
||||
@@ -56,66 +32,6 @@ public class NativeStructureFailureContractTest {
|
||||
assertTrue(error.getMessage().contains("importedStructures.disabledExact"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureVegetationCleanupPrecedesTerrainPreparationAndPlacement() throws IOException {
|
||||
String source = moddedSource("ModdedNativeStructureStage.java");
|
||||
int placementStart = source.indexOf("void placeVanillaStructures");
|
||||
int placementEnd = source.indexOf("private static String nativeStructureBatchContext", placementStart);
|
||||
String placement = source.substring(placementStart, placementEnd);
|
||||
|
||||
assertTrue(placement.contains("\"terrain integration\""));
|
||||
assertTrue(placement.contains("\"terrain preparation\""));
|
||||
assertTrue(placement.contains("\"foundation repair\""));
|
||||
assertFalse(placement.contains("\"terrain carving\""));
|
||||
assertTrue(placement.contains("visitExistingPois(chunk"));
|
||||
assertTrue(placement.contains("level.updatePOIOnBlockStateChange("));
|
||||
assertTrue(placement.contains("Blocks.AIR.defaultBlockState(), state"));
|
||||
assertTrue(placement.indexOf("visitExistingPois(chunk")
|
||||
< placement.indexOf("WorldgenTerrainHeightmaps.primeStructurePlacement("));
|
||||
assertTrue(placement.contains("prepareSurfaceStructures"));
|
||||
assertTrue(placement.contains("clearIntersectingVegetation"));
|
||||
assertTrue(placement.indexOf("clearIntersectingVegetation")
|
||||
< placement.indexOf("prepareSurfaceStructures"));
|
||||
assertTrue(placement.indexOf("prepareSurfaceStructures")
|
||||
< placement.indexOf("for (NativePlacementGroup group"));
|
||||
assertTrue(placement.indexOf("repairVacuumFoundations")
|
||||
> placement.indexOf("for (NativePlacementGroup group"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structurePlacementPrimesNeighbourWorldgenHeightmapsBeforeTerrainPreparation() throws IOException {
|
||||
String source = moddedSource("ModdedNativeStructureStage.java");
|
||||
int placementStart = source.indexOf("void placeVanillaStructures");
|
||||
int placementEnd = source.indexOf("private static String nativeStructureBatchContext", placementStart);
|
||||
String placement = source.substring(placementStart, placementEnd);
|
||||
|
||||
assertTrue(source.contains("import art.arcane.iris.nativegen.WorldgenTerrainHeightmaps;"));
|
||||
assertTrue(placement.contains("WorldgenTerrainHeightmaps.primeStructurePlacement("));
|
||||
assertTrue(placement.contains("\"heightmap priming\""));
|
||||
assertTrue(placement.contains("heightmapStarts.add(start);"));
|
||||
assertTrue(placement.indexOf("WorldgenTerrainHeightmaps.primeStructurePlacement(")
|
||||
< placement.indexOf("prepareSurfaceStructures"));
|
||||
assertTrue(source.contains("Engine.hostHeight(generationEngine, x, z, false) + runtimeMinY + 1"));
|
||||
assertTrue(source.contains("Engine.hostHeight(generationEngine, x, z, true) + runtimeMinY + 1"));
|
||||
assertTrue(placement.contains("current.getDimensionStackContext() != null"));
|
||||
assertTrue(source.contains("int minY = chunk.getMinY() + 1;"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeStructurePostProcessingUsesBoundedWorldgenAccess() throws IOException {
|
||||
String source = moddedSource("ModdedNativeStructureStage.java");
|
||||
int placementStart = source.indexOf("void placeVanillaStructures");
|
||||
int placementEnd = source.indexOf("private List<List<Structure>> structuresByStep", placementStart);
|
||||
String placement = source.substring(placementStart, placementEnd);
|
||||
|
||||
assertTrue(placement.contains("ModdedNativeStructureWorldgenAccess.create("));
|
||||
assertTrue(placement.contains("nativeStructureProtection(current, staticObjects)"));
|
||||
assertTrue(placement.contains("Engine.hostHeight(current, x, z, true)"));
|
||||
assertTrue(placement.contains("placeVanillaStructure(boundedWorld, structureManager"));
|
||||
assertFalse(placement.contains("NativeStructurePostProcessor.place(world,"));
|
||||
assertTrue(placement.contains("world.setCurrentlyGenerating(null);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureFailurePreservesPhaseIdentityChunkAndCause() {
|
||||
IllegalArgumentException cause = new IllegalArgumentException("broken placement");
|
||||
@@ -128,10 +44,4 @@ public class NativeStructureFailureContractTest {
|
||||
assertTrue(error.getMessage().contains("12,-8"));
|
||||
assertTrue(error.getMessage().contains("aborted"));
|
||||
}
|
||||
|
||||
private static String moddedSource(String fileName) throws IOException {
|
||||
Path sourcePath = Path.of(System.getProperty("iris.moddedCommonSources"),
|
||||
"art", "arcane", "iris", "modded", fileName);
|
||||
return Files.readString(sourcePath);
|
||||
}
|
||||
}
|
||||
|
||||
-183
@@ -7,9 +7,6 @@ import com.mojang.datafixers.util.Pair;
|
||||
import net.minecraft.core.BlockPos;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
@@ -48,30 +45,6 @@ public class IrisModdedStructureCommandTest {
|
||||
assertEquals(1, nativeReferences.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void gotoStructureSupportsIrisAndNativeRegistryTargets() throws IOException {
|
||||
String source = source("ModdedLocateCommands.java");
|
||||
String suggestions = source("ModdedCommandSuggestions.java");
|
||||
|
||||
assertTrue(source.contains("IrisStructureLocator.hasLocatableEditablePlacement(engine, key)"));
|
||||
assertTrue(source.contains("registry.get(identifier)"));
|
||||
assertTrue(source.contains("getPlacementsForStructure(holder)"));
|
||||
assertTrue(source.contains("generator.findNearestMapStructure("));
|
||||
assertTrue(source.contains("NATIVE_STRUCTURE_LOCATE_RADIUS = 100"));
|
||||
assertTrue(source.contains("HolderSet.direct(target.holder())"));
|
||||
assertFalse(source.contains("NativeStructureLocateCapability"));
|
||||
assertTrue(source.contains("boolean teleported = player.teleportTo("));
|
||||
assertTrue(suggestions.contains("combineStructureKeys(unregisteredIrisKeys, nativeKeys)"));
|
||||
assertTrue(source.contains("irisGenerator.isNativeStructureReachable(holder)"));
|
||||
assertTrue(source.contains("LocateStatus.SEARCH_LIMIT_REACHED"));
|
||||
assertTrue(source.contains("IRIS_MODDED_COMMANDS_UNABLE_LOCATE_IRIS_PLACED_STRUCTURE_DENSITY_SEARCH_SAFETY_LIMIT_WAS"));
|
||||
assertTrue(source.contains("int targetX = result.originX()"));
|
||||
assertTrue(source.contains("int targetY = result.baseY() + 2"));
|
||||
assertTrue(source.contains("int targetZ = result.originZ()"));
|
||||
assertFalse(source.contains("at[0] + 8"));
|
||||
assertFalse(source.contains("at[2] + 8"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registeredStructureEligibilityMatchesGotoAndSuggestions() {
|
||||
IrisNativeStructureDecision nativeDecision = decision(NativeStructureGenerationStatus.GENERATE_NATIVE);
|
||||
@@ -175,162 +148,6 @@ public class IrisModdedStructureCommandTest {
|
||||
.contains("non-positive density or a Y band outside"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unregisteredCommandUsesGotoEligibilityAndPrintsToConsole() throws IOException {
|
||||
String command = source("ModdedUnregisteredStructures.java");
|
||||
String tree = source("ModdedCommandTree.java");
|
||||
|
||||
assertTrue(tree.contains("Commands.literal(\"unregistered\")"));
|
||||
assertTrue(tree.contains("ModdedUnregisteredStructures.print(context.getSource())"));
|
||||
assertTrue(command.contains("ModdedCommandSuggestions.isEligibleRegisteredStructure("));
|
||||
assertTrue(command.contains("ModdedLocateCommands.registeredStructureUnavailableMessage("));
|
||||
assertTrue(command.contains("engine.getData().getStructureLoader().getPossibleKeys()"));
|
||||
assertTrue(command.contains("IrisStructureLocator.hasLocatableEditablePlacement(engine, key)"));
|
||||
assertTrue(command.contains("ModdedIrisLog.info(\"[Iris goto unregistered] [{}] {} - {}\""));
|
||||
assertTrue(command.contains("UNREGISTERED(\"unregistered\")"));
|
||||
assertFalse(command.contains("DatapackIngestService"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureSuggestionsUseActiveLevelAvailability() throws IOException {
|
||||
String suggestions = source("ModdedCommandSuggestions.java");
|
||||
int methodStart = suggestions.indexOf("static CompletableFuture<Suggestions> suggestStructureKeys(");
|
||||
int methodEnd = suggestions.indexOf("static boolean isEligibleRegisteredStructure(", methodStart);
|
||||
String method = suggestions.substring(methodStart, methodEnd);
|
||||
|
||||
assertTrue(method.contains("ServerLevel level = source.getLevel()"));
|
||||
assertTrue(method.contains("Engine engine = IrisModdedCommands.engineFor(level)"));
|
||||
assertFalse(method.contains("IrisStructureLocator.locatableKeys(engine)"));
|
||||
assertTrue(method.contains("IrisStructureLocator.locatableEditableKeys(engine)"));
|
||||
assertTrue(method.contains("StructureReachability.reachableKeys(engine)"));
|
||||
assertTrue(method.contains("NativeStructureGenerationPolicy.resolve(engine, key, false)"));
|
||||
assertTrue(method.contains("IrisStructureLocator.hasLocatableNativePlacement(engine, key)"));
|
||||
assertTrue(method.contains("IrisStructureLocator.hasLocatableEditablePlacement(engine, key)"));
|
||||
assertTrue(method.contains("reachableNativeKeys.contains(normalizeKey(key))"));
|
||||
assertTrue(method.contains("nativeGenerationEnabled"));
|
||||
assertTrue(method.contains("eligibleUnregisteredEditableKeys("));
|
||||
assertTrue(method.contains("IrisStructureLocator.hasNativePlacement(engine, candidate)"));
|
||||
assertFalse(method.contains("NativeStructureGenerationKeys.active(level)"));
|
||||
assertFalse(method.contains("getPlacementsForStructure"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generatorLocateUsesEveryIrisPlacedNativeStructure() throws IOException {
|
||||
String source = moddedSource("ModdedNativeStructureStage.java");
|
||||
int methodStart = source.indexOf("Pair<BlockPos, Holder<Structure>> findNearestIrisStructure(");
|
||||
int methodEnd = source.indexOf("HolderSet<Structure> filterReachableNativeStructures(", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
int registryLookup = method.indexOf("level.registryAccess().lookupOrThrow(Registries.STRUCTURE)");
|
||||
int placedCheck = method.indexOf(
|
||||
"if (!IrisStructureLocator.hasNativePlacement(current, structureId))");
|
||||
int irisLocate = method.indexOf("NativeStructureLocatePersistence.search(", placedCheck);
|
||||
int nearestSelection = method.indexOf(
|
||||
"NativeStructureLocateResults.nearest(pos, predicted, nativeLocated)", irisLocate);
|
||||
int selectedVerification = method.indexOf("bestSearch.search().verify(bestResult)", nearestSelection);
|
||||
int selectedReference = method.indexOf(
|
||||
"selectedSearch.search().reference(selectedStart)", selectedVerification);
|
||||
|
||||
assertTrue(registryLookup >= 0);
|
||||
assertTrue(placedCheck > registryLookup);
|
||||
assertTrue(irisLocate > placedCheck);
|
||||
assertTrue(nearestSelection > irisLocate);
|
||||
assertTrue(selectedVerification > nearestSelection);
|
||||
assertTrue(selectedReference > selectedVerification);
|
||||
assertTrue(method.contains("NativeStructureLocatePersistence.probe("));
|
||||
assertTrue(method.contains("findUnexplored"));
|
||||
assertTrue(method.contains("LocateStatus.SEARCH_LIMIT_REACHED"));
|
||||
assertTrue(method.contains("verified.ownership().locatorY()"));
|
||||
assertTrue(method.contains("selectedSearch.search().reference(selectedStart)"));
|
||||
assertTrue(method.contains("NativeStructureLocateResults.selectAndReference("));
|
||||
assertFalse(method.contains("NativeStructureLocateCapability"));
|
||||
assertTrue(source.contains("structureBiomeSource.isStructureReachable(holder)"));
|
||||
assertFalse(source.contains("isPaperUnavailable"));
|
||||
String generator = moddedSource("IrisModdedChunkGenerator.java");
|
||||
assertTrue(generator.contains("NativeStructureVanillaLocator.predict("));
|
||||
assertFalse(generator.contains("super.findNearestMapStructure(level, reachable"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void commandResolvesNativePolicyBeforeAnyVanillaAliasLookup() throws IOException {
|
||||
String source = source("ModdedLocateCommands.java");
|
||||
int methodStart = source.indexOf("static int gotoStructure(");
|
||||
int methodEnd = source.indexOf("private static void locateIrisStructure(", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
int nativeResolution = method.indexOf("resolveNativeStructure(source, level, engine, key)");
|
||||
int editableIrisLookup = method.indexOf(
|
||||
"IrisStructureLocator.hasLocatableEditablePlacement(engine, key)", nativeResolution);
|
||||
int policyResolution = method.indexOf(
|
||||
"NativeStructureGenerationPolicy.resolve(engine, target.key(), false)", editableIrisLookup);
|
||||
int eligibilityCheck = method.indexOf(
|
||||
"ModdedCommandSuggestions.isEligibleRegisteredStructure(", policyResolution);
|
||||
int replacementLocate = method.indexOf(
|
||||
"locateIrisStructure(source, level, engine, player, target.key())", eligibilityCheck);
|
||||
int nativeLocate = method.indexOf("runNativeStructureLocate(source, level, player, target)",
|
||||
replacementLocate);
|
||||
|
||||
assertTrue(nativeResolution >= 0);
|
||||
assertTrue(editableIrisLookup > nativeResolution);
|
||||
assertTrue(policyResolution > editableIrisLookup);
|
||||
assertTrue(eligibilityCheck > policyResolution);
|
||||
assertTrue(replacementLocate > eligibilityCheck);
|
||||
assertTrue(nativeLocate > replacementLocate);
|
||||
assertTrue(method.contains("IrisStructureLocator.hasLocatableNativePlacement(engine, target.key())"));
|
||||
assertTrue(method.contains("IrisStructureLocator.hasLocatableEditablePlacement(engine, target.key())"));
|
||||
assertTrue(method.contains("!IrisStructureLocator.hasNativePlacement(engine, key)"));
|
||||
assertTrue(method.contains("StructureReachability.isReachable(engine, target.key())"));
|
||||
assertTrue(method.contains("getWorldGenSettings().options().generateStructures()"));
|
||||
assertFalse(method.contains("IrisStructureLocator.isPlaced(engine, key)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void verifyResolvesRegisteredNativeBeforeGenericIrisAliases() throws IOException {
|
||||
String source = source("ModdedLocateCommands.java");
|
||||
int methodStart = source.indexOf("private static int verifyStructure(");
|
||||
int methodEnd = source.indexOf("private static Optional<NativeStructureTarget> resolveNativeStructure(",
|
||||
methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
int nativeResolution = method.indexOf("resolveNativeStructure(source, level, engine, key)");
|
||||
int genericIrisLookup = method.indexOf("IrisStructureLocator.isPlaced(engine, key)", nativeResolution);
|
||||
|
||||
assertTrue(nativeResolution >= 0);
|
||||
assertTrue(genericIrisLookup > nativeResolution);
|
||||
assertTrue(method.contains("NativeStructureAvailability.IRIS_SUPPRESSED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureVerificationNoLongerClaimsNativeGenerationIsMissing() throws IOException {
|
||||
String source = source("ModdedStructureCommands.java");
|
||||
|
||||
assertTrue(source.contains("verifyTree(\"verify\")"));
|
||||
assertTrue(source.contains("verifyTree(\"locateall\")"));
|
||||
assertTrue(source.contains("IrisModdedCommands.verifyStructures("));
|
||||
assertFalse(source.contains("Vanilla structure locate is meaningless here"));
|
||||
}
|
||||
|
||||
private String source(String fileName) throws IOException {
|
||||
Path source = commonSourceRoot().resolve("art/arcane/iris/modded/command/")
|
||||
.resolve(fileName)
|
||||
.normalize();
|
||||
return Files.readString(source);
|
||||
}
|
||||
|
||||
private String moddedSource(String fileName) throws IOException {
|
||||
Path source = commonSourceRoot().resolve("art/arcane/iris/modded/")
|
||||
.resolve(fileName)
|
||||
.normalize();
|
||||
return Files.readString(source);
|
||||
}
|
||||
|
||||
private Path commonSourceRoot() {
|
||||
String configuredRoot = System.getProperty("iris.moddedCommonSources");
|
||||
if (configuredRoot != null && !configuredRoot.isBlank()) {
|
||||
return Path.of(configuredRoot);
|
||||
}
|
||||
return Path.of(System.getProperty("user.dir"))
|
||||
.resolve("../modded-common/src/main/java")
|
||||
.normalize();
|
||||
}
|
||||
|
||||
private IrisNativeStructureDecision decision(NativeStructureGenerationStatus status) {
|
||||
return new IrisNativeStructureDecision(status, 0, null, false, null, null);
|
||||
}
|
||||
|
||||
-28
@@ -1,28 +0,0 @@
|
||||
package art.arcane.iris.modded.command;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class ModdedRiverLocateContractTest {
|
||||
@Test
|
||||
public void riverLookupMergesRecordedAndEligibleActiveFeatures() throws IOException {
|
||||
Path sourcePath = Path.of(System.getProperty("iris.moddedCommonSources"))
|
||||
.resolve("art/arcane/iris/modded/command/ModdedLocateCommands.java");
|
||||
String source = Files.readString(sourcePath);
|
||||
int methodStart = source.indexOf("static int gotoRiver(");
|
||||
int methodEnd = source.indexOf("static int gotoStructure(", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
|
||||
assertTrue(method.contains("GenerationSemanticQueries.nearestRiver("));
|
||||
assertTrue(method.contains("runtime == null"));
|
||||
assertTrue(method.contains("feature.y() + 2"));
|
||||
assertFalse(method.contains("runtime.nearestFeature("));
|
||||
assertFalse(method.contains("feature.y() + engine.getDimension().getMinHeight()"));
|
||||
}
|
||||
}
|
||||
-34
@@ -1,34 +0,0 @@
|
||||
package art.arcane.iris.modded.command;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public final class ModdedStructureHistoryContractTest {
|
||||
@Test
|
||||
public void retainedStructureNamesReachTheSemanticSearchOnBothAdmissionBranches() throws IOException {
|
||||
Path root = Path.of(System.getProperty("iris.moddedCommonSources"));
|
||||
String source = Files.readString(root.resolve("art/arcane/iris/modded/command/ModdedLocateCommands.java"));
|
||||
int start = source.indexOf("static int gotoStructure(");
|
||||
int end = source.indexOf("static String registeredStructureUnavailableMessage(", start);
|
||||
String admission = source.substring(start, end);
|
||||
String retainedCheck = "GenerationFindCatalog.hasRetainedStructurePlacement(engine, key)";
|
||||
int firstCheck = admission.indexOf(retainedCheck);
|
||||
assertTrue(firstCheck >= 0);
|
||||
assertTrue(admission.indexOf(retainedCheck, firstCheck + retainedCheck.length()) >= 0);
|
||||
|
||||
int locateStart = source.indexOf("private static void locateIrisStructure(");
|
||||
int locateEnd = source.indexOf("private static void runNativeStructureLocate(", locateStart);
|
||||
String locate = source.substring(locateStart, locateEnd);
|
||||
assertTrue(locate.contains("GenerationSemanticQueries.nearestStructure("));
|
||||
assertFalse(locate.contains("IrisStructureLocator.locate("));
|
||||
|
||||
String suggestions = Files.readString(root.resolve("art/arcane/iris/modded/command/ModdedCommandSuggestions.java"));
|
||||
assertTrue(suggestions.contains("GenerationFindCatalog.retainedStructureKeys(engine)"));
|
||||
}
|
||||
}
|
||||
-89
@@ -1,89 +0,0 @@
|
||||
package art.arcane.iris.modded.command;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ModdedStudioTeleportContractTest {
|
||||
private static final String SOURCE_ROOT_PROPERTY = "iris.moddedCommonSources";
|
||||
|
||||
@Test
|
||||
public void studioOpenSerializesReplacementAndWaitsForTheNativePath() throws IOException {
|
||||
String source = source("command/ModdedStudioCommands.java");
|
||||
String open = method(source, "private static int open(");
|
||||
String execute = method(source, "private static void executeStudioOpen(");
|
||||
|
||||
assertTrue(open.contains("TRANSITIONS.submit("));
|
||||
assertFalse(open.contains("orTimeout("));
|
||||
assertFalse(open.contains("deadlineNanos"));
|
||||
assertBefore(execute, "replaceExistingStudio(", "ModdedDimensionManager.createTransientStudio(");
|
||||
assertBefore(execute, "ModdedDimensionManager.createTransientStudio(", "ModdedDimensionManager.teleportAsync(");
|
||||
assertFalse(execute.contains("deadlineNanos"));
|
||||
assertFalse(source.contains("player.teleportTo(studio"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void studioTeleportAndVisionShareWarmFutureSemantics() throws IOException {
|
||||
String studio = source("command/ModdedStudioCommands.java");
|
||||
String vision = source("command/ModdedVisionOverlay.java");
|
||||
String dimensions = source("ModdedDimensionManager.java");
|
||||
|
||||
assertTrue(studio.contains("private static CompletableFuture<Void> teleportToStudio("));
|
||||
assertTrue(studio.contains("ModdedDimensionManager.teleportAsync("));
|
||||
assertTrue(dimensions.contains("TELEPORT_WARM_RADIUS = 0"));
|
||||
assertTrue(dimensions.contains("addTicketAndLoadWithRadius(\n"
|
||||
+ " TELEPORT_WARM_TICKET,\n"
|
||||
+ " chunkPos,\n"
|
||||
+ " TELEPORT_WARM_RADIUS)"));
|
||||
assertTrue(dimensions.contains("removeTicketWithRadius(\n"
|
||||
+ " TELEPORT_WARM_TICKET,\n"
|
||||
+ " chunkPos,\n"
|
||||
+ " TELEPORT_WARM_RADIUS)"));
|
||||
assertTrue(vision.contains("Math.floor(worldX)"));
|
||||
assertTrue(vision.contains("Math.floor(worldZ)"));
|
||||
assertTrue(vision.contains("if (opener != null)"));
|
||||
assertTrue(vision.contains("ModdedDimensionManager.teleportAsync("));
|
||||
assertFalse(vision.contains("level.getHeight("));
|
||||
assertFalse(vision.contains("player.teleportTo("));
|
||||
}
|
||||
|
||||
private static String source(String relative) throws IOException {
|
||||
String root = System.getProperty(SOURCE_ROOT_PROPERTY);
|
||||
if (root == null || root.isBlank()) {
|
||||
throw new IllegalStateException("Missing test property " + SOURCE_ROOT_PROPERTY);
|
||||
}
|
||||
return Files.readString(Path.of(root).resolve("art/arcane/iris/modded").resolve(relative))
|
||||
.replace("\r\n", "\n");
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
if (start < 0) {
|
||||
throw new IllegalArgumentException("Missing source method " + signature);
|
||||
}
|
||||
int open = source.indexOf('{', start);
|
||||
int depth = 0;
|
||||
for (int index = open; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}' && --depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source method " + signature);
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = source.indexOf(first);
|
||||
int secondIndex = source.indexOf(second);
|
||||
assertTrue("Missing source token " + first, firstIndex >= 0);
|
||||
assertTrue("Missing source token " + second, secondIndex >= 0);
|
||||
assertTrue(first + " must precede " + second, firstIndex < secondIndex);
|
||||
}
|
||||
}
|
||||
@@ -222,7 +222,6 @@ tasks.named('compileJava', JavaCompile).configure {
|
||||
|
||||
tasks.named('test').configure {
|
||||
useJUnitPlatform()
|
||||
systemProperty('iris.moddedCommonSources', file('../modded-common/src/main/java').absolutePath)
|
||||
}
|
||||
|
||||
processResources {
|
||||
|
||||
@@ -1,182 +0,0 @@
|
||||
package art.arcane.iris.core;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
/**
|
||||
* Every core diagnostic keeps its severity through to the server log, so the level a call site picks is the
|
||||
* level an operator scanning for WARN and SEVERE sees. This pins the two ends of that decision: conditions an
|
||||
* operator must act on stay at WARN or ERROR, and conditions that repeat per block, per sample or per chunk -
|
||||
* or that report designed backpressure, a probe on an optional dependency, or a success - do not.
|
||||
* <p>
|
||||
* The pin is on the source rather than on a live engine because most of these call sites need a running world
|
||||
* to reach, and what is being fixed is the choice of level, not the condition that triggers it.
|
||||
*/
|
||||
public class DiagnosticSeverityPolicyTest {
|
||||
@Test
|
||||
public void perBlockAndPerSampleConditionsDoNotReachTheServerLogAtWarnLevel() {
|
||||
assertLoggedWith("util/project/hunk/Hunk.java", "OUT OF BOUNDS ", "debug");
|
||||
assertLoggedWith("engine/mantle/MantleWriter.java", "No set? ", "debug");
|
||||
assertLoggedWith("engine/mantle/MantleWriter.java", "Mantle Writer Accessed chunk out of bounds", "debug");
|
||||
assertLoggedWith("engine/object/IrisDecorator.java", "Empty Block Data for ", "warnOnce");
|
||||
assertLoggedWith("engine/object/IrisCompat.java", "Can't find block data for ", "warnOnce");
|
||||
assertLoggedWith("engine/data/cache/AtomicCache.java", "Atomic cache supplier failed: %s: %s", "warnOnce");
|
||||
assertLoggedWith("engine/IrisComplex.java", "Failed to sample interpolated biome bounds", "warnOnce");
|
||||
assertLoggedWith("engine/framework/placer/WorldObjectPlacer.java", "Tried to place custom block at", "warnOnce");
|
||||
}
|
||||
|
||||
/**
|
||||
* Backpressure pausing generation is the design working. It is reported every five seconds for the whole
|
||||
* duration of a large pregen, so at WARN it would be the only thing a log scan finds.
|
||||
*/
|
||||
@Test
|
||||
public void designedBackpressureIsReportedWithoutRaisingAWarning() {
|
||||
assertLoggedWith("core/pregenerator/PregenMantleBackpressure.java", "Pregen mantle backpressure: ", "info");
|
||||
assertLoggedWith("core/pregenerator/PregenMantleBackpressure.java", "Pregen heap pressure: pausing generation", "info");
|
||||
assertLoggedWith("core/pregenerator/MantleHeapPressure.java", "Iris heap remained at", "info");
|
||||
assertLoggedWith("core/pregenerator/methods/AsyncPregenMethod.java", "is still pending after", "debug");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void generationTracingStaysAtDebugLevel() {
|
||||
assertLoggedWith("engine/mantle/components/MantleObjectComponent.java", "Regen object layer start:", "debug");
|
||||
assertLoggedWith("engine/mantle/components/MantleObjectComponent.java", "Goldendebug object attempt:", "debug");
|
||||
assertLoggedWith("engine/mantle/components/GoldenDebugObjectPlacer.java", "Goldendebug query:", "debug");
|
||||
assertLoggedWith("engine/mantle/components/IrisStructureComponent.java", "[StructTrace] ORIGIN", "debug");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coreProductionDoesNotWriteDirectlyToProcessStreams() throws IOException {
|
||||
try (Stream<Path> files = Files.walk(Path.of("src/main/java"))) {
|
||||
List<Path> bypasses = files
|
||||
.filter(path -> path.toString().endsWith(".java"))
|
||||
.filter(path -> {
|
||||
try {
|
||||
String source = Files.readString(path);
|
||||
return source.contains("System.out")
|
||||
|| source.contains("System.err")
|
||||
|| source.contains(".printStackTrace();");
|
||||
} catch (IOException unreadable) {
|
||||
throw new IllegalStateException(unreadable);
|
||||
}
|
||||
})
|
||||
.toList();
|
||||
assertTrue(bypasses.toString(), bypasses.isEmpty());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void probesOnOptionalDependenciesAreNotWarnings() throws IOException {
|
||||
assertLoggedWith("core/link/MultiverseCoreLink.java", "is not reachable; Iris world settings", "info");
|
||||
assertLoggedWith("core/link/MultiverseCoreLink.java", "Multiverse will record the live name", "info");
|
||||
assertLoggedWith("core/gui/GuiHost.java", "Unable to install the Iris desktop quit guard", "info");
|
||||
String slimJar = read("util/common/misc/SlimJar.java");
|
||||
assertTrue(slimJar.contains("debug(plugin, \"Failed to inject the library loader"));
|
||||
assertTrue(slimJar.contains("if (DEBUG)"));
|
||||
assertTrue(slimJar.contains("plugin.getLogger().info(\"[DEBUG] \" + message)"));
|
||||
}
|
||||
|
||||
/**
|
||||
* A refused destructive command is the guard working, and the admin who typed it is told directly. The
|
||||
* console record of it is not a warning about the server.
|
||||
*/
|
||||
@Test
|
||||
public void aRefusedMultiverseCommandIsRecordedWithoutRaisingAWarning() throws IOException {
|
||||
String guard = read("core/link/MultiverseGuardListener.java");
|
||||
|
||||
assertTrue("the console record of a refusal is kept", guard.contains("IrisLogging.info(\"%s %s\", reason, remedy)"));
|
||||
assertFalse("the refusal is not a warning about the server", guard.contains("IrisLogging.warn(\"%s %s\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void decorationAndBlankLinesAreNotEmittedAtAnySeverity() throws IOException {
|
||||
String safeguard = read("core/safeguard/IrisSafeguard.java");
|
||||
assertFalse("a blank record renders as an empty [WARN] line", safeguard.contains("IrisLogging.warn(\"\")"));
|
||||
assertFalse("a blank record renders as an empty [SEVERE] line", safeguard.contains("IrisLogging.error(\"\")"));
|
||||
assertFalse("a blank record renders as an empty [INFO] line", safeguard.contains("IrisLogging.info(\"\")"));
|
||||
assertFalse("a separator rule is not a diagnostic", safeguard.contains("--==<"));
|
||||
|
||||
String configurator = read("core/ServerConfigurator.java");
|
||||
assertFalse("a separator rule is not a diagnostic", configurator.contains("IrisLogging.error(\"===="));
|
||||
assertFalse("a separator rule is not a diagnostic", configurator.contains("IrisLogging.error(\"----"));
|
||||
}
|
||||
|
||||
/**
|
||||
* A written debug image is a success, and the method that wrote it had no callers at all.
|
||||
*/
|
||||
@Test
|
||||
public void theUnusedDebugImageWriterIsGone() throws IOException {
|
||||
assertFalse(read("engine/object/IrisImage.java").contains("writeDebug"));
|
||||
}
|
||||
|
||||
/**
|
||||
* The other end of the policy. These are the conditions the promotion exists for.
|
||||
*/
|
||||
@Test
|
||||
public void operatorActionableConditionsKeepTheirSeverity() {
|
||||
assertLoggedWith("core/lifecycle/VanishedWorldStorage.java", "Iris world storage is gone at", "error");
|
||||
assertLoggedWith("core/IrisWorlds.java", "has unusable world storage and is excluded", "error");
|
||||
assertLoggedWith("engine/EngineDataStore.java", "Failed to setup Engine Data", "error");
|
||||
assertLoggedWith("engine/IrisEngineMantle.java", "Failed to read chunk section, skipping it.", "error");
|
||||
assertLoggedWith("engine/IrisEngineMantle.java", "Failed to read chunk, creating a new chunk instead.", "error");
|
||||
assertLoggedWith("util/common/reflect/WrappedField.java", "Failed to created WrappedField", "error");
|
||||
assertLoggedWith("core/datapack/DatapackIngestService.java", "Repairing modified or corrupt Iris-managed datapack", "warn");
|
||||
assertLoggedWith("core/pregenerator/cache/PregenCacheImpl.java", "Pregen cache storage is gone at", "warn");
|
||||
assertLoggedWith("core/pregenerator/PregenMantleBackpressure.java", "Pregen mantle backpressure exceeded ", "warn");
|
||||
assertLoggedWith("core/pregenerator/PregenMantleBackpressure.java", "Pregen heap pressure wait exceeded ", "warn");
|
||||
assertLoggedWith("core/pregenerator/IrisPregenerator.java", "failed chunk(s); failures are not cached", "warn");
|
||||
assertLoggedWith("core/service/MultiverseSVC.java", "Multiverse world events are not bindable", "warn");
|
||||
}
|
||||
|
||||
/**
|
||||
* A handful of lifecycle lines an operator reads logs/latest.log to find. NOTICE is the level adapters
|
||||
* route to the server's own logger rather than to the console sender.
|
||||
*/
|
||||
@Test
|
||||
public void lifecycleLinesAreRaisedAtNoticeLevel() {
|
||||
assertLoggedWith("engine/IrisEngine.java", "Engine init: ", "notice");
|
||||
assertLoggedWith("core/link/MultiverseCoreLink.java", "Adopted %d live Iris world", "notice");
|
||||
assertLoggedWith("core/pregenerator/IrisPregenerator.java", "Pregen finished: ", "notice");
|
||||
}
|
||||
|
||||
private static void assertLoggedWith(String relativePath, String fragment, String expectedMethod) {
|
||||
String source;
|
||||
try {
|
||||
source = read(relativePath);
|
||||
} catch (IOException unreadable) {
|
||||
throw new AssertionError("Cannot read " + relativePath, unreadable);
|
||||
}
|
||||
|
||||
int occurrences = 0;
|
||||
for (int at = source.indexOf(fragment); at >= 0; at = source.indexOf(fragment, at + 1)) {
|
||||
occurrences++;
|
||||
assertEquals(relativePath + " @ \"" + fragment + "\" occurrence " + occurrences,
|
||||
expectedMethod, loggingMethodBefore(source, at, relativePath, fragment));
|
||||
}
|
||||
assertTrue(relativePath + " no longer contains \"" + fragment + "\"", occurrences > 0);
|
||||
}
|
||||
|
||||
private static String loggingMethodBefore(String source, int at, String relativePath, String fragment) {
|
||||
int call = source.lastIndexOf("IrisLogging.", at);
|
||||
assertTrue(relativePath + " @ \"" + fragment + "\" is not logged through IrisLogging", call >= 0);
|
||||
int start = call + "IrisLogging.".length();
|
||||
int end = start;
|
||||
while (end < source.length() && Character.isJavaIdentifierPart(source.charAt(end))) {
|
||||
end++;
|
||||
}
|
||||
return source.substring(start, end);
|
||||
}
|
||||
|
||||
private static String read(String relativePath) throws IOException {
|
||||
return Files.readString(Path.of("src/main/java/art/arcane/iris").resolve(relativePath)).replace("\r\n", "\n");
|
||||
}
|
||||
}
|
||||
@@ -95,28 +95,6 @@ public class IrisWorldsTest {
|
||||
* asserted against the source: one unusable world folder used to throw out of {@code clean()}, through
|
||||
* the constructor and into the cache, which returned null and NPE'd every caller.
|
||||
*/
|
||||
@Test
|
||||
public void oneUnusableWorldIsExcludedInsteadOfFailingTheWholeRegistry() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/IrisWorlds.java")).replace("\r\n", "\n");
|
||||
|
||||
int accessor = source.indexOf("public static IrisWorlds get()");
|
||||
int accessorEnd = source.indexOf("public synchronized void put(", accessor);
|
||||
assertTrue("get() must not swallow a failure into a null return",
|
||||
source.substring(accessor, accessorEnd).contains("cache.aquireOnceOrThrow("));
|
||||
|
||||
int clean = source.indexOf("public synchronized void clean()");
|
||||
int cleanEnd = source.indexOf("public synchronized void save()", clean);
|
||||
String cleanBody = source.substring(clean, cleanEnd);
|
||||
assertTrue("clean() must isolate an unusable entry",
|
||||
cleanBody.contains("catch (IllegalStateException e)"));
|
||||
assertTrue("an unusable entry stays in the registry so /iris remove can still find it",
|
||||
cleanBody.contains("warnUnusableStorage(entry.getKey(), e);"));
|
||||
|
||||
int loadDimension = source.indexOf("private IrisDimension loadDimension(");
|
||||
assertTrue("loadDimension must exclude an unusable world rather than propagate",
|
||||
source.substring(loadDimension).contains("catch (IllegalStateException unusableStorage)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bukkitWorldFilteringRecognizesCurrentCraftBukkitConfiguredStorage() throws Exception {
|
||||
Path worldContainer = temporaryFolder.newFolder("configured-server").toPath();
|
||||
|
||||
@@ -3,9 +3,6 @@ package art.arcane.iris.core;
|
||||
import art.arcane.iris.platform.bukkit.BukkitPlatform;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
@@ -28,55 +25,6 @@ public class ProgressBossBarToggleTest {
|
||||
() -> BukkitPlatform.showProgressLane(null, "iris:job", "Working", 0.5D, 4000L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void studioOpenBossBarIsGatedBySettings() throws Exception {
|
||||
assertGated("core/project/StudioOpenProgressReporter.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void worldCreationAlwaysUsesTheActionBarInsteadOfABossBar() throws Exception {
|
||||
String source = source("core/tools/WorldCreationProgressReporter.java");
|
||||
|
||||
assertFalse(source.contains("isProgressBossBar()"));
|
||||
assertFalse(source.contains("Bukkit.createBossBar("));
|
||||
assertTrue(source.contains("WORLD_CREATE_LIFECYCLE_ACTION"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void chunkJobBossBarIsGatedBySettings() throws Exception {
|
||||
assertGated("core/runtime/ChunkJobReporter.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packDownloadLaneIsGatedWhileTheActionBarSurvives() throws Exception {
|
||||
String source = source("core/service/PackDownloadProgressReporter.java");
|
||||
|
||||
assertTrue(source.contains("isProgressBossBar()"));
|
||||
assertTrue("the action bar must keep reporting when boss bars are off",
|
||||
source.contains("sender.sendAction(snapshot.line())"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pregenLaneIsGatedWhileTheActionBarSurvives() throws Exception {
|
||||
String source = source("core/tools/IrisCreator.java");
|
||||
int lane = source.indexOf("\"iris:pregen\"");
|
||||
int gate = source.lastIndexOf("isProgressBossBar()", lane);
|
||||
|
||||
assertTrue("the pregen boss bar lane must sit behind general.progressBossBar",
|
||||
gate > 0 && lane - gate < 200);
|
||||
assertTrue("the action bar must keep reporting when boss bars are off",
|
||||
source.contains("RuntimeProgressMessages.WORLD_PREGEN_ACTION"));
|
||||
}
|
||||
|
||||
private void assertGated(String relativePath) throws Exception {
|
||||
assertTrue(relativePath + " must consult general.progressBossBar before creating a boss bar",
|
||||
source(relativePath).contains("isProgressBossBar()"));
|
||||
}
|
||||
|
||||
private String source(String relativePath) throws Exception {
|
||||
return Files.readString(Path.of("src/main/java/art/arcane/iris").resolve(relativePath)).replace("\r\n", "\n");
|
||||
}
|
||||
|
||||
private void withProgressBossBar(boolean enabled, Runnable body) {
|
||||
IrisSettings previous = IrisSettings.settings;
|
||||
try {
|
||||
|
||||
-37
@@ -384,43 +384,6 @@ public class ServerConfiguratorDatapackFingerprintTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void recoveryRunsBeforeRestoredFingerprintReuseHashFallbackAndCompilation() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/ServerConfigurator.java")).replace("\r\n", "\n");
|
||||
int installIfChanged = source.indexOf("installDataPacksIfChanged(boolean fullInstall)");
|
||||
int recovery = source.indexOf("DatapackIngestService.reapplyFromStaging", installIfChanged);
|
||||
int restored = source.indexOf("restoredCompilerInputFingerprint()", recovery);
|
||||
int fingerprint = source.indexOf("computeCurrentDatapackCompilerInputFingerprint", restored);
|
||||
int earlyReturn = source.indexOf("resultForUnchangedFingerprint", fingerprint);
|
||||
int compile = source.indexOf("compileDataPacksLocked(", earlyReturn);
|
||||
int cache = source.indexOf("writeCompilerInputFingerprintCache(cacheFile.toPath(), current)", compile);
|
||||
|
||||
assertTrue(recovery >= 0);
|
||||
assertTrue(restored > recovery);
|
||||
assertTrue(fingerprint > restored);
|
||||
assertTrue(earlyReturn > fingerprint);
|
||||
assertTrue(compile > earlyReturn);
|
||||
assertTrue(cache > compile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void studioTimingSeparatesRecoveryFingerprintCompilationAndTotal() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/ServerConfigurator.java")).replace("\r\n", "\n");
|
||||
int timedInstall = source.indexOf("BiConsumer<String, Long> timingConsumer");
|
||||
int recovery = source.indexOf("\"datapack_external_recovery\"", timedInstall);
|
||||
int fingerprint = source.indexOf("\"datapack_compiler_input_fingerprint\"", recovery);
|
||||
int compile = source.indexOf("\"datapack_compile_publish\"", fingerprint);
|
||||
int total = source.indexOf("\"datapack_install_if_changed_total\"", compile);
|
||||
|
||||
assertTrue(timedInstall >= 0);
|
||||
assertTrue(recovery > timedInstall);
|
||||
assertTrue(fingerprint > recovery);
|
||||
assertTrue(compile > fingerprint);
|
||||
assertTrue(total > compile);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void loadedRuntimeReuseRequiresAnExactPinnedCompilerInputFingerprint() {
|
||||
assertTrue(ServerConfigurator.reusableRuntimeFingerprint("abc", "abc"));
|
||||
|
||||
@@ -9,8 +9,6 @@ import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -92,30 +90,6 @@ public class ServerConfiguratorStartupRestartTest {
|
||||
assertFalse(directRestartInvocation.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startupRestartPathsPromoteTheValidationStateAndBypassTickQueues() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/ServerConfigurator.java"));
|
||||
String configure = section(
|
||||
source,
|
||||
"public static void configure()",
|
||||
"public static boolean isLoadedDatapackRuntimeReady");
|
||||
String startupRestart = section(
|
||||
source,
|
||||
"public static void restartAtStartupBoundary",
|
||||
"public static boolean verifyDataPackInstalled");
|
||||
|
||||
int restartResult = configure.indexOf("if (result.restartRequired())");
|
||||
int validationRestart = configure.indexOf("requireDatapackRestart();", restartResult);
|
||||
assertTrue(restartResult >= 0);
|
||||
assertTrue(validationRestart > restartResult);
|
||||
assertTrue(startupRestart.indexOf("invokeImmediateRestartIfSupported(Bukkit.class)")
|
||||
< startupRestart.indexOf("Bukkit.shutdown();"));
|
||||
assertFalse(startupRestart.contains("Bukkit.restart"));
|
||||
assertFalse(startupRestart.contains("J.s("));
|
||||
assertFalse(startupRestart.contains("dispatchCommand"));
|
||||
}
|
||||
|
||||
public static final class RestartCapableApi {
|
||||
private static boolean restarted;
|
||||
|
||||
@@ -126,12 +100,4 @@ public class ServerConfiguratorStartupRestartTest {
|
||||
|
||||
public static final class ShutdownOnlyApi {
|
||||
}
|
||||
|
||||
private static String section(String source, String startMarker, String endMarker) {
|
||||
int start = source.indexOf(startMarker);
|
||||
int end = source.indexOf(endMarker, start);
|
||||
assertTrue(start >= 0);
|
||||
assertTrue(end > start);
|
||||
return source.substring(start, end);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,7 +15,6 @@ import java.io.File;
|
||||
import java.io.PrintStream;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
@@ -255,23 +254,6 @@ public class SettingsHotloadWatchTest {
|
||||
assertEquals("Active " + PERMISSION, permissionMessage());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bukkitAndModdedUseTheSameCoreCoordinator() throws Exception {
|
||||
String bukkit = Files.readString(Path.of(
|
||||
"../adapters/bukkit/plugin/src/main/java/art/arcane/iris/Iris.java"
|
||||
));
|
||||
String modded = Files.readString(Path.of(
|
||||
"../adapters/modded-common/src/main/java/art/arcane/iris/modded/service/ModdedSettingsHotloadService.java"
|
||||
));
|
||||
|
||||
assertTrue(bukkit.contains("new SettingsHotloadWatch("));
|
||||
assertTrue(modded.contains("new SettingsHotloadWatch("));
|
||||
assertFalse(bukkit.contains("new ConfigHotloadEngine("));
|
||||
assertFalse(modded.contains("new ConfigHotloadEngine("));
|
||||
assertFalse(bukkit.contains("IrisLanguage.update()"));
|
||||
assertFalse(modded.contains("IrisLanguage.update()"));
|
||||
}
|
||||
|
||||
private void awaitPermissionMessage(String expected) throws Exception {
|
||||
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(6L);
|
||||
while (System.nanoTime() < deadline) {
|
||||
|
||||
-51
@@ -1,51 +0,0 @@
|
||||
package art.arcane.iris.core.datapack;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class DatapackIngestRuntimeInvalidationTest {
|
||||
@Test
|
||||
public void everyPublicMutationInvalidatesLoadedRuntimeBeforeLocking() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/datapack/DatapackIngestService.java")).replace("\r\n", "\n");
|
||||
|
||||
assertInvalidatesBeforeLock(source, "public static Report ingest(");
|
||||
assertInvalidatesBeforeLock(source, "public static ReapplyOutcome reapplyFromStaging(");
|
||||
assertInvalidatesBeforeLock(source, "public static boolean remove(");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noChangeChecksRestoreWhileChangedIngestRemainsRestartBound() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/datapack/DatapackIngestService.java")).replace("\r\n", "\n");
|
||||
int ingest = source.indexOf("public static Report ingest(");
|
||||
int unlock = source.indexOf("TRANSACTION_LOCK.unlock();", ingest);
|
||||
int restore = source.indexOf(
|
||||
"ServerConfigurator.restoreLoadedDatapackRuntimeIfUnchanged(invalidation);",
|
||||
unlock);
|
||||
int changed = source.indexOf("else if (report.changed())", restore);
|
||||
int restart = source.indexOf("ServerConfigurator.restart();", changed);
|
||||
int restartRequired = source.indexOf("ServerConfigurator.requireDatapackRestart();", restart);
|
||||
|
||||
assertTrue(ingest >= 0);
|
||||
assertTrue(unlock > ingest);
|
||||
assertTrue(restore > unlock);
|
||||
assertTrue(changed > restore);
|
||||
assertTrue(restart > changed);
|
||||
assertTrue(restartRequired > restart);
|
||||
}
|
||||
|
||||
private void assertInvalidatesBeforeLock(String source, String signature) {
|
||||
int method = source.indexOf(signature);
|
||||
int invalidation = source.indexOf("ServerConfigurator.invalidateLoadedDatapackRuntime();", method);
|
||||
int lock = source.indexOf("TRANSACTION_LOCK.lock();", method);
|
||||
|
||||
assertTrue("Missing datapack mutation entry point " + signature, method >= 0);
|
||||
assertTrue("Loaded runtime must be invalidated before " + signature, invalidation > method);
|
||||
assertTrue("Loaded runtime invalidation must precede the transaction lock", invalidation < lock);
|
||||
}
|
||||
}
|
||||
@@ -246,39 +246,6 @@ public class DatapackIngestServiceTest {
|
||||
validated, "26.2", 4000, true, true, validated.urls));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startupChecksCheapCacheContextBeforeHashingManagedDatapacks() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/datapack/DatapackIngestService.java")).replace("\r\n", "\n");
|
||||
int validation = source.indexOf("public static StartupValidationOutcome validateOnStartup()");
|
||||
int cacheRead = source.indexOf("readStartupValidationCache", validation);
|
||||
int contextCheck = source.indexOf("startupValidationContextMatches(", cacheRead);
|
||||
int fingerprint = source.indexOf("startupValidationFingerprint(", contextCheck);
|
||||
int fullValidation = source.indexOf("if (autoIngest && !configured.isEmpty())", fingerprint);
|
||||
|
||||
assertTrue(validation >= 0);
|
||||
assertTrue(cacheRead > validation);
|
||||
assertTrue(contextCheck > cacheRead);
|
||||
assertTrue(fingerprint > contextCheck);
|
||||
assertTrue(fullValidation > fingerprint);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unchangedPostStartupMaintenanceReturnsBeforeFingerprinting() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/datapack/DatapackIngestService.java")).replace("\r\n", "\n");
|
||||
int refresh = source.indexOf(
|
||||
"refreshStartupValidationAfterMaintenance(boolean maintenanceChanged)");
|
||||
int unchangedGuard = source.indexOf("if (!maintenanceChanged)", refresh);
|
||||
int validatedState = source.indexOf("StartupValidationCache validated", refresh);
|
||||
int fingerprint = source.indexOf("startupValidationFingerprint(", refresh);
|
||||
|
||||
assertTrue(refresh >= 0);
|
||||
assertTrue(unchangedGuard > refresh);
|
||||
assertTrue(validatedState > unchangedGuard);
|
||||
assertTrue(fingerprint > unchangedGuard);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packMetadataMustContainAValidPackContract() throws Exception {
|
||||
File valid = temporaryFolder.newFolder("valid");
|
||||
@@ -894,27 +861,6 @@ public class DatapackIngestServiceTest {
|
||||
assertEquals(expected, ownershipHash(managed));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void directoryHashRestatsAttributesAndVolumeBeforeOpeningEachFile() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/datapack/DatapackIngestService.java")).replace("\r\n", "\n");
|
||||
int method = source.indexOf("private static String directoryHash(File root)");
|
||||
int entries = source.indexOf("List<Path> entries = new ArrayList<>()", method);
|
||||
int loop = source.indexOf("for (Path entry : entries)", entries);
|
||||
int attributes = source.indexOf("BasicFileAttributes attributes = Files.readAttributes(", loop);
|
||||
int fileStore = source.indexOf("Files.getFileStore(entry)", attributes);
|
||||
int open = source.indexOf("Files.newInputStream(", fileStore);
|
||||
int digest = source.indexOf("return hex(digest.digest())", open);
|
||||
|
||||
assertTrue(method >= 0);
|
||||
assertTrue(entries > method);
|
||||
assertTrue(loop > entries);
|
||||
assertTrue(attributes > loop);
|
||||
assertTrue(fileStore > attributes);
|
||||
assertTrue(open > fileStore);
|
||||
assertTrue(digest > open);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failedUpdateStagingCannotBeAdoptedByTheCommittedManifest() throws Exception {
|
||||
File staging = datapackDirectory("candidate-staging");
|
||||
@@ -3880,8 +3826,6 @@ public class DatapackIngestServiceTest {
|
||||
}
|
||||
|
||||
private static final class DeleteAttemptFile extends File {
|
||||
private static final long serialVersionUID = 1L;
|
||||
|
||||
private final int successfulAttempt;
|
||||
private int deleteAttempts;
|
||||
|
||||
|
||||
@@ -10,8 +10,6 @@ import org.junit.Test;
|
||||
import org.mockito.MockedStatic;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.CompletionException;
|
||||
import java.util.concurrent.CompletionStage;
|
||||
@@ -59,40 +57,6 @@ public class WorldLifecycleUnloadAsyncTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void paperLikeManualUnloadDetachesBeforeOffThreadChunkDrain() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/lifecycle/WorldLifecycleSupport.java"))
|
||||
.replace("\r\n", "\n");
|
||||
int unloadStart = source.indexOf("private static CompletableFuture<Boolean> unloadWorldWithoutAsyncApi(");
|
||||
int unloadEnd = source.indexOf("static boolean announceManualWorldUnload", unloadStart);
|
||||
String unloadMethod = source.substring(unloadStart, unloadEnd);
|
||||
int capabilityFallback = unloadMethod.indexOf(
|
||||
"capabilities.minecraftServer() == null || capabilities.removeLevelMethod() == null");
|
||||
int bukkitUnload = unloadMethod.indexOf("Bukkit.unloadWorld(world, save)", capabilityFallback);
|
||||
int detach = unloadMethod.indexOf("detachServerLevelAsync(capabilities, serverLevel, world)");
|
||||
int drain = unloadMethod.indexOf("drainChunkTasksAsync(world, serverLevel)");
|
||||
int close = unloadMethod.indexOf("closeServerLevelAsync(world, serverLevel)");
|
||||
int closeStart = source.indexOf("private static CompletableFuture<Void> closeServerLevelAsync(");
|
||||
int closeEnd = source.indexOf("private static CompletableFuture<Void> drainChunkTasksAsync(", closeStart);
|
||||
String closeMethod = source.substring(closeStart, closeEnd);
|
||||
int drainStart = closeEnd;
|
||||
int drainEnd = source.indexOf("private static void removeWorldFromCraftServerMap", drainStart);
|
||||
String drainMethod = source.substring(drainStart, drainEnd);
|
||||
|
||||
assertTrue(capabilityFallback >= 0);
|
||||
assertTrue(bukkitUnload > capabilityFallback);
|
||||
assertTrue(detach > bukkitUnload);
|
||||
assertTrue(drain > detach);
|
||||
assertTrue(close > drain);
|
||||
assertTrue(drainMethod.contains("return J.afut(() ->"));
|
||||
assertTrue(drainMethod.contains("\"moonrise$getChunkTaskScheduler\""));
|
||||
assertTrue(drainMethod.contains("\"halt\""));
|
||||
assertTrue(closeMethod.contains("return runGlobalAsync(closeTask)"));
|
||||
assertFalse(closeMethod.contains("J.afut("));
|
||||
assertFalse(closeMethod.contains("J.runRegion("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reflectedAsyncUnloadWaitsForTrueCallback() throws Exception {
|
||||
CallbackServer server = new CallbackServer();
|
||||
|
||||
@@ -18,8 +18,6 @@ import org.mvplugins.multiverse.external.vavr.control.Option;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -53,16 +51,6 @@ public class MultiverseCoreLinkTest {
|
||||
&& method.getParameterCount() == 2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registrationDoesNotRewriteTheRecordedMultiverseName() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/link/MultiverseCoreLink.java")).replace("\r\n", "\n");
|
||||
|
||||
assertFalse("worlds are created under the startup name, so the recorded name needs no correction",
|
||||
source.contains("setLegacyWorldName"));
|
||||
assertTrue("Multiverse still has to be told the live world name it will record",
|
||||
source.contains("ImportWorldOptions.worldName(world.getName())"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void lookupNamesAddTheIrisKeyAndRuntimeNameForConfiguredStartupNames() {
|
||||
assertEquals(
|
||||
|
||||
@@ -8,8 +8,6 @@ import art.arcane.iris.engine.object.IrisStaticObject;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@@ -102,47 +100,4 @@ public class PackExportClosureTest {
|
||||
* Source guard: both packagers must export the ambient-spawning graph. Spawner and marker
|
||||
* folders were silently omitted from exports, leaving dangling entitySpawners references.
|
||||
*/
|
||||
@Test
|
||||
public void bothPackagersExportSpawnersMarkersAndTheFullEntityGraph() throws Exception {
|
||||
String bukkit = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/project/IrisPackageCompiler.java")).replace("\r\n", "\n");
|
||||
assertTrue("Bukkit packager must write spawners/", bukkit.contains("\"spawners/\""));
|
||||
assertTrue("Bukkit packager must write markers/", bukkit.contains("\"markers/\""));
|
||||
assertTrue("Bukkit packager must export region objects alongside biome objects",
|
||||
bukkit.contains("regions.forEach((r) -> allPlacements.addAll(r.getObjects()))"));
|
||||
assertTrue("Bukkit packager must export static objects",
|
||||
bukkit.contains("exportedDimension.getStaticObjects()"));
|
||||
assertTrue("Bukkit obfuscation must rewrite static object references",
|
||||
bukkit.contains("placement.setObject(renameObjects.get(placement.getObject()))"));
|
||||
assertTrue("Bukkit packager must export entity loot tables", bukkit.contains("getLoot().getTables()"));
|
||||
assertTrue("Bukkit packager must include dimension-stack resources",
|
||||
bukkit.contains("PackExportClosure.collectDimensionKeys(dimension)"));
|
||||
assertTrue("Bukkit packager must export expression-backed stack blend styles",
|
||||
bukkit.contains("dm.getExpressionLoader().getPossibleKeys()")
|
||||
&& bukkit.contains("\"expressions/\""));
|
||||
int bukkitValidation = bukkit.indexOf("PackValidator.validateForPackaging(project.getPath())");
|
||||
assertTrue("Bukkit packager must validate before opening or mutating package state",
|
||||
bukkitValidation >= 0
|
||||
&& bukkitValidation < bukkit.indexOf("IrisData.openRuntime(project.getPath())")
|
||||
&& bukkitValidation < bukkit.indexOf("IO.delete(folder)"));
|
||||
|
||||
String modded = Files.readString(Path.of(
|
||||
"../adapters/modded-common/src/main/java/art/arcane/iris/modded/command/ModdedStudioCommands.java")).replace("\r\n", "\n");
|
||||
assertTrue("modded packager must write spawners/", modded.contains("\"spawners\""));
|
||||
assertTrue("modded packager must write markers/", modded.contains("\"markers\""));
|
||||
assertTrue("modded packager must include initial spawns", modded.contains("getInitialSpawns"));
|
||||
assertTrue("modded packager must export region objects", modded.contains("region.getObjects()"));
|
||||
assertTrue("modded packager must export static objects",
|
||||
modded.contains("PackExportClosure.collectStaticObjectKeys(exportedDimension.getStaticObjects())"));
|
||||
assertTrue("modded packager must include dimension-stack resources",
|
||||
modded.contains("PackExportClosure.collectDimensionKeys(dimension)"));
|
||||
assertTrue("modded packager must export expression-backed stack blend styles",
|
||||
modded.contains("dm.getExpressionLoader().getPossibleKeys()")
|
||||
&& modded.contains("\"expressions\""));
|
||||
assertTrue("modded packager must retain referenced style snippets",
|
||||
modded.contains("copyStyleSnippets(packFolder, folder)"));
|
||||
int moddedValidation = modded.indexOf("PackValidator.validateForPackaging(packFolder)");
|
||||
assertTrue("modded packager must validate before mutating package state",
|
||||
moddedValidation >= 0 && moddedValidation < modded.indexOf("IO.delete(folder)"));
|
||||
}
|
||||
}
|
||||
|
||||
-144
@@ -1,144 +0,0 @@
|
||||
package art.arcane.iris.core.pregenerator.methods;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URISyntaxException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PregenMantleCleanupContractTest {
|
||||
@Test
|
||||
public void normalEngineCleanupRetriesCandidatesCoveredByTheNewestRealChunk() throws IOException {
|
||||
String source = read("art/arcane/iris/engine/framework/Engine.java");
|
||||
String cleanup = method(source, "default void cleanupMantleChunk(int x, int z)");
|
||||
|
||||
assertTrue(cleanup.contains("cleanupChunksCoveredBy("));
|
||||
assertTrue(cleanup.contains("EngineMantle.ChunkCleanupCallback.NONE"));
|
||||
assertFalse(cleanup.contains(".cleanupChunk(x, z)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void asyncPregenReportsOnlyCleanupCallbacksFromCoveredTargets() throws IOException {
|
||||
String source = read("art/arcane/iris/core/pregenerator/methods/AsyncPregenMethod.java");
|
||||
String completion = method(source, "private void completeChunk(");
|
||||
String cleanup = method(source, "private void cleanupMantleChunksCoveredBy(");
|
||||
|
||||
assertTrue(completion.contains("cleanupMantleChunksCoveredBy(x, z, listener)"));
|
||||
assertFalse(completion.contains("listener.onChunkCleaned"));
|
||||
assertTrue(cleanup.contains("cleanupChunksCoveredBy(x, z, true, listener::onChunkCleaned)"));
|
||||
assertFalse(cleanup.contains("forceCleanupChunk"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void finalChunkFlushUsesTargetedPersistenceWithoutFullWorldSave() throws IOException {
|
||||
String source = read("art/arcane/iris/core/pregenerator/methods/AsyncPregenMethod.java");
|
||||
String flush = method(source, "private void flushAllRemainingChunks()");
|
||||
String medievalSource = read("art/arcane/iris/core/pregenerator/methods/MedievalPregenMethod.java");
|
||||
String medievalFlush = method(medievalSource, "private void unloadAndSaveAllChunks(");
|
||||
|
||||
assertTrue(flush.contains("evictRegion(regionKey)"));
|
||||
assertTrue(flush.contains("INMS.get().flushChunkIO(world)"));
|
||||
assertFalse(flush.contains("world.save()"));
|
||||
assertTrue(medievalFlush.contains("i.unload(true)"));
|
||||
assertTrue(medievalFlush.contains("INMS.get().flushChunkIO(world)"));
|
||||
assertFalse(medievalFlush.contains("world.save()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void foliaFinalEvictionUsesAwaitableRegionOwnedTasks() throws IOException {
|
||||
String source = read("art/arcane/iris/core/pregenerator/methods/AsyncPregenMethod.java");
|
||||
String eviction = method(source, "private CompletableFuture<Void> evictFoliaRegion(");
|
||||
|
||||
assertTrue(eviction.contains("J.runRegionFuture("));
|
||||
assertTrue(eviction.contains("CompletableFuture.allOf("));
|
||||
assertFalse(eviction.contains("J.runRegion("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shutdownReclaimsMantleBeforeReleasingTheJob() throws IOException {
|
||||
String source = read("art/arcane/iris/core/pregenerator/IrisPregenerator.java");
|
||||
String shutdown = method(source, "private void shutdown()");
|
||||
int generator = shutdown.indexOf("shutdownStep(\"generator\"");
|
||||
int interruptBoundary = shutdown.indexOf("Thread.interrupted()", generator);
|
||||
int mantle = shutdown.indexOf("shutdownStep(\"mantle\"");
|
||||
int protocol = shutdown.indexOf("shutdownStep(\"protocol\"");
|
||||
int listener = shutdown.indexOf("shutdownStep(\"listener\"");
|
||||
|
||||
assertTrue(generator >= 0);
|
||||
assertTrue(interruptBoundary > generator);
|
||||
assertTrue(mantle > interruptBoundary);
|
||||
assertTrue(protocol > mantle);
|
||||
assertTrue(listener > protocol);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void directStopDoesNotReleaseTheActiveJobBeforeWorkerCleanup() throws IOException {
|
||||
String source = read("art/arcane/iris/core/gui/PregeneratorJob.java");
|
||||
String stop = method(source, "public void stop()");
|
||||
String close = method(source, "public void onClose()");
|
||||
|
||||
assertTrue(stop.contains("requestStop()"));
|
||||
assertFalse(stop.contains("instance.compareAndSet"));
|
||||
assertTrue(close.contains("instance.compareAndSet(this, null)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void performanceProfilePreparationRunsOnThePregenWorker() throws IOException {
|
||||
String source = read("art/arcane/iris/core/gui/PregeneratorJob.java");
|
||||
String constructor = method(source, "public PregeneratorJob(Configuration configuration)");
|
||||
String worker = method(source, "private void runWorker(Runnable preparation)");
|
||||
|
||||
assertTrue(constructor.contains("runWorker(configuration.preparation())"));
|
||||
assertBefore(worker, "preparation.run()", "pregenerator.start()");
|
||||
}
|
||||
|
||||
private static String read(String relativePath) throws IOException {
|
||||
Path current;
|
||||
try {
|
||||
current = Path.of(PregenMantleCleanupContractTest.class.getProtectionDomain()
|
||||
.getCodeSource()
|
||||
.getLocation()
|
||||
.toURI());
|
||||
} catch (URISyntaxException e) {
|
||||
throw new IOException("Unable to resolve the core source root", e);
|
||||
}
|
||||
while (current != null && !Files.isDirectory(current.resolve("src/main/java"))) {
|
||||
current = current.getParent();
|
||||
}
|
||||
if (current == null) {
|
||||
throw new IOException("Unable to locate the core source root");
|
||||
}
|
||||
return Files.readString(current.resolve("src/main/java").resolve(relativePath));
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue("Missing source contract signature: " + signature, start >= 0);
|
||||
int openBrace = source.indexOf('{', start);
|
||||
assertTrue("Missing source contract method body: " + signature, openBrace >= 0);
|
||||
int depth = 0;
|
||||
for (int index = openBrace; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source contract method: " + signature);
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = source.indexOf(first);
|
||||
int secondIndex = source.indexOf(second);
|
||||
assertTrue(firstIndex >= 0);
|
||||
assertTrue(secondIndex > firstIndex);
|
||||
}
|
||||
}
|
||||
@@ -4,9 +4,6 @@ import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.iris.core.service.ObjectStudioSaveService;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -18,32 +15,6 @@ public class StudioEntitySpawningTest {
|
||||
assertTrue(new IrisSettings.IrisSettingsStudio().isEntitySpawning());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void studioWorldRulesEnableVanillaMobSpawning() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/WorldRuntimeControlService.java")).replace("\r\n", "\n");
|
||||
int helperStart = source.indexOf("static void enableStudioEntitySpawning");
|
||||
int helperEnd = source.indexOf("public boolean applyNoonTimeLock", helperStart);
|
||||
String helper = source.substring(helperStart, helperEnd);
|
||||
|
||||
assertTrue(source.contains("enableStudioEntitySpawning(world);"));
|
||||
assertTrue(helper.contains("setBooleanGameRule(world, true, \"DO_MOB_SPAWNING\""));
|
||||
assertTrue(helper.contains("setBooleanGameRule(world, true, \"DO_TRADER_SPAWNING\""));
|
||||
assertTrue(helper.contains("setBooleanGameRule(world, true, \"DO_PATROL_SPAWNING\""));
|
||||
assertTrue(helper.contains("setBooleanGameRule(world, true, \"DO_INSOMNIA\""));
|
||||
assertTrue(helper.contains("setBooleanGameRule(world, true, \"DO_WARDEN_SPAWNING\""));
|
||||
assertFalse(helper.contains("setBooleanGameRule(world, false, \"DO_MOB_SPAWNING\""));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardStudioDefaultsToSpectatorMode() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java")).replace("\r\n", "\n");
|
||||
|
||||
assertTrue(source.contains("request.openKind().teleportThroughStandardEntry()"));
|
||||
assertTrue(source.contains("GameMode.SPECTATOR"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void objectStudioDoesNotCancelEntitySpawns() {
|
||||
boolean hasSpawnCanceller = Arrays.stream(ObjectStudioSaveService.class.getDeclaredMethods())
|
||||
|
||||
-209
@@ -5,9 +5,6 @@ import art.arcane.iris.core.tools.IrisCreator;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
@@ -86,210 +83,4 @@ public class StudioOpenCoordinatorOpenKindTest {
|
||||
assertEquals(StudioOpenCoordinator.StudioOpenKind.JIGSAW, request.openKind());
|
||||
assertTrue(request.requestedAtNanos() > 0L);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeTeleportWaitsForCompletionWithoutAnArrivalDeadline() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java"))
|
||||
.replace("\r\n", "\n");
|
||||
int methodStart = source.indexOf("private void executeOpen(");
|
||||
int delegation = source.indexOf("WorldRuntimeControlService.get().teleportInMode(", methodStart);
|
||||
int completion = source.indexOf("nativeTeleportFuture.get();", delegation);
|
||||
|
||||
assertTrue(delegation >= 0);
|
||||
assertTrue(completion > delegation);
|
||||
assertFalse(source.substring(methodStart, completion).contains("orTimeout("));
|
||||
assertFalse(source.substring(methodStart, completion).contains("deadlineNanos"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void activeStudioTeleportIsSerializedWithoutAnArrivalDeadline() throws Exception {
|
||||
String coordinator = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java"))
|
||||
.replace("\r\n", "\n");
|
||||
String service = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/service/StudioSVC.java"))
|
||||
.replace("\r\n", "\n");
|
||||
int coordinatorStart = coordinator.indexOf(
|
||||
"public CompletableFuture<Boolean> teleportPlayerToProject(");
|
||||
int coordinatorEnd = coordinator.indexOf("private void executeOpen", coordinatorStart);
|
||||
String coordinatorMethod = coordinator.substring(coordinatorStart, coordinatorEnd);
|
||||
int serviceStart = service.indexOf(
|
||||
"public CompletableFuture<Boolean> teleportToActiveProject(Player player)");
|
||||
int serviceEnd = service.indexOf("public void open(VolmitSender", serviceStart);
|
||||
String serviceMethod = service.substring(serviceStart, serviceEnd);
|
||||
int transitionAdmission = serviceMethod.indexOf("studioTransitions.submit(() ->");
|
||||
int projectCapture = serviceMethod.indexOf("IrisProject project = activeProject");
|
||||
int nativeDelegation = coordinatorMethod.indexOf(
|
||||
"WorldRuntimeControlService.get().teleportInMode(player, entry, GameMode.SPECTATOR)");
|
||||
|
||||
assertTrue(transitionAdmission >= 0);
|
||||
assertTrue(projectCapture > transitionAdmission);
|
||||
assertFalse(serviceMethod.contains("orTimeout("));
|
||||
assertFalse(serviceMethod.contains("deadlineNanos"));
|
||||
assertTrue(coordinatorMethod.contains(
|
||||
"WorldRuntimeControlService.get().resolveEntryAnchor(world, provider)"));
|
||||
assertTrue(coordinatorMethod.contains(
|
||||
"project.getActiveOpenKind().teleportThroughStandardEntry()"));
|
||||
assertFalse(coordinatorMethod.contains("requestChunkAsync("));
|
||||
assertFalse(coordinatorMethod.contains("getHighestBlockYAt("));
|
||||
assertTrue(nativeDelegation >= 0);
|
||||
assertFalse(coordinatorMethod.contains("orTimeout("));
|
||||
assertFalse(coordinatorMethod.contains("deadlineNanos"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardEntryDelegatesWithoutAnExplicitChunkOrSurfaceLookup() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java"))
|
||||
.replace("\r\n", "\n");
|
||||
|
||||
assertTrue(source.contains("Location entryLocation = entryAnchor;"));
|
||||
assertFalse(source.contains("prepareStudioEntryChunks("));
|
||||
assertFalse(source.contains("findStudioEntryLocation"));
|
||||
assertFalse(source.contains("EntryChunkResolution"));
|
||||
assertFalse(source.contains("resolveSafeEntry(world, entryAnchor)"));
|
||||
assertFalse(source.contains("getHighestBlockYAt("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void standardEntryBindsSpectatorToTheNativeTeleport() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java"))
|
||||
.replace("\r\n", "\n");
|
||||
String runtime = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/WorldRuntimeControlService.java"))
|
||||
.replace("\r\n", "\n");
|
||||
int mode = runtime.indexOf("modeRestore.apply(gameMode)");
|
||||
int teleport = runtime.indexOf("teleporter.teleport(player, location)", mode);
|
||||
|
||||
assertTrue(source.contains("WorldRuntimeControlService.get().teleportInMode("));
|
||||
int openKind = source.indexOf("request.project().setActiveOpenKind(request.openKind())");
|
||||
int provider = source.indexOf("request.project().setActiveProvider(provider)", openKind);
|
||||
assertTrue(openKind >= 0);
|
||||
assertTrue(provider > openKind);
|
||||
assertTrue(source.contains("project.setActiveOpenKind(null)"));
|
||||
assertTrue(mode >= 0);
|
||||
assertTrue(teleport > mode);
|
||||
assertTrue(runtime.contains("modeRestore.restore()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void studioTimingSeparatesOrderedLifecyclePhases() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java")).replace("\r\n", "\n");
|
||||
int previous = -1;
|
||||
for (String phase : List.of(
|
||||
"resolve_dimension_and_cleanup",
|
||||
"create_world_total",
|
||||
"apply_world_rules",
|
||||
"prepare_generator",
|
||||
"resolve_entry_anchor",
|
||||
"prepare_structure_rings",
|
||||
"prepare_generation_caches",
|
||||
"teleport_standard_entry",
|
||||
"finalize_open")) {
|
||||
int current = source.indexOf("\"" + phase + "\"", previous + 1);
|
||||
assertTrue("Missing or out-of-order Studio timing phase " + phase, current > previous);
|
||||
previous = current;
|
||||
}
|
||||
assertTrue(source.contains("long openStart = System.nanoTime();"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void structureStateCompletesBeforeImmediateNativeTeleport() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java")).replace("\r\n", "\n");
|
||||
int executeOpen = source.indexOf("private void executeOpen(");
|
||||
int completionCall = source.indexOf("endStudioEntryBootstrap(entryWorld, entryProvider)", executeOpen);
|
||||
int completionAwait = source.indexOf("entryBootstrap.get(", completionCall);
|
||||
int cacheAwait = source.indexOf("irisEngine.awaitGenerationCacheWarm()", completionAwait);
|
||||
int teleportStart = source.indexOf("WorldRuntimeControlService.get().teleportInMode(", cacheAwait);
|
||||
int finalizeOpen = source.indexOf(
|
||||
"updateStage(request, \"finalize_open\", 1.00D)", teleportStart);
|
||||
int futureComplete = source.indexOf(
|
||||
"future.complete(new StudioOpenResult(world, entryLocation))", finalizeOpen);
|
||||
int methodStart = source.indexOf(
|
||||
"private void endStudioEntryBootstrap(World world, PlatformChunkGenerator provider)");
|
||||
int methodEnd = source.indexOf("private void abandonStudioEntryBootstrap", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
int scheduled = method.indexOf("J.sfut(() ->");
|
||||
int claim = method.indexOf("activationClaim.compareAndSet(true, false)");
|
||||
int activation = method.indexOf("INMS.get().completeStudioStructureBootstrap(world)");
|
||||
int gateRelease = method.indexOf("bukkitGenerator::endStudioEntryBootstrap");
|
||||
|
||||
assertTrue(completionCall >= 0);
|
||||
assertTrue(completionAwait > completionCall);
|
||||
assertTrue(cacheAwait > completionAwait);
|
||||
assertTrue(teleportStart > cacheAwait);
|
||||
assertTrue(finalizeOpen > teleportStart);
|
||||
assertTrue(futureComplete > finalizeOpen);
|
||||
assertFalse(source.contains("requestChunkAsync("));
|
||||
assertTrue(scheduled >= 0);
|
||||
assertTrue(claim > scheduled);
|
||||
assertTrue(activation > claim);
|
||||
int ringCompletion = method.indexOf("thenCompose(nativeActivation -> nativeActivation)");
|
||||
assertTrue(ringCompletion > activation);
|
||||
assertTrue(gateRelease > ringCompletion);
|
||||
assertTrue(source.contains("abandonStudioEntryBootstrap(world, e);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failedOpenMarshalsRetainedStateAbandonmentToTheServerScheduler() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java")).replace("\r\n", "\n");
|
||||
int methodStart = source.indexOf(
|
||||
"private void abandonStudioEntryBootstrap(World world, Throwable failure)");
|
||||
int methodEnd = source.indexOf("private CompletableFuture<Void> cleanupFailedOpen", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
|
||||
assertTrue(method.contains("if (J.isPrimaryThread())"));
|
||||
assertTrue(method.contains("CompletableFuture<Void> abandonment = J.sfut("));
|
||||
assertTrue(method.contains("abandonment.get(STUDIO_STRUCTURE_ACTIVATION_TIMEOUT_SECONDS"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void queuedRestartDefersFailedOpenCleanupWithoutAcquiringALiveCloseLease() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java")).replace("\r\n", "\n");
|
||||
int openCatch = source.indexOf("} catch (Throwable e) {");
|
||||
int restartCheck = source.indexOf(
|
||||
".active(LifecycleOperationCoordinator.Domain.SERVER_LIFECYCLE)",
|
||||
openCatch);
|
||||
int restartDeferral = source.indexOf(
|
||||
"deferFailedOpenCleanupToRestart(",
|
||||
restartCheck);
|
||||
int liveCleanup = source.indexOf("cleanupFailedOpen(", restartDeferral);
|
||||
int methodStart = source.indexOf(
|
||||
"private void deferFailedOpenCleanupToRestart(",
|
||||
restartDeferral);
|
||||
int methodEnd = source.indexOf("private boolean transientWorldStorageExists", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
|
||||
assertTrue(restartCheck > openCatch);
|
||||
assertTrue(restartDeferral > restartCheck);
|
||||
assertTrue(liveCleanup > restartDeferral);
|
||||
assertTrue(method.contains("queueStartupCleanup("));
|
||||
assertFalse(method.contains("closeWorldCoordinated("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void openFinalizerReturnsToTheServerThreadBeforeCompletion() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java")).replace("\r\n", "\n");
|
||||
int finalizerCall = source.indexOf("runOpenFinalizer(request.onDone(), world);");
|
||||
int futureCompletion = source.indexOf(
|
||||
"future.complete(new StudioOpenResult(world, entryLocation))", finalizerCall);
|
||||
int methodStart = source.indexOf(
|
||||
"private void runOpenFinalizer(Consumer<World> finalizer, World world)");
|
||||
int methodEnd = source.indexOf("private long elapsedMillis", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
|
||||
assertTrue(finalizerCall >= 0);
|
||||
assertTrue(futureCompletion > finalizerCall);
|
||||
assertTrue(method.contains("if (J.isPrimaryThread())"));
|
||||
assertTrue(method.contains("J.sfut(() -> finalizer.accept(world))"));
|
||||
assertTrue(method.contains(
|
||||
"completion.get(STUDIO_STRUCTURE_ACTIVATION_TIMEOUT_SECONDS, TimeUnit.SECONDS)"));
|
||||
}
|
||||
}
|
||||
|
||||
-24
@@ -2,12 +2,9 @@ package art.arcane.iris.core.runtime;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.Arrays;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class StudioOpenCoordinatorSpawnStuckRegressionTest {
|
||||
@Test
|
||||
@@ -30,25 +27,4 @@ public class StudioOpenCoordinatorSpawnStuckRegressionTest {
|
||||
.anyMatch(m -> m.getName().equals("waitForEntryChunk"));
|
||||
assertFalse("waitForEntryChunk retry loop must be removed", found);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void entryTeleportDoesNotIssueASecondChunkRequest() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java")).replace("\r\n", "\n");
|
||||
|
||||
assertTrue(source.contains("WorldRuntimeControlService.get().teleportInMode("));
|
||||
assertFalse(source.contains("prepareStudioEntryChunks("));
|
||||
assertFalse(source.contains("requestChunkAsync("));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void foliaEntryPathNeverReadsTerrainOnARegionThread() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/runtime/StudioOpenCoordinator.java")).replace("\r\n", "\n");
|
||||
|
||||
assertTrue(source.contains("resolveEntryAnchor(world, provider)"));
|
||||
assertFalse(source.contains("findStudioEntryLocation"));
|
||||
assertFalse(source.contains("getHighestBlockYAt("));
|
||||
assertFalse(source.contains("resolveSafeEntry(world, entryAnchor)"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,9 +23,6 @@ import org.mockito.MockedStatic;
|
||||
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
@@ -84,23 +81,6 @@ public class JigsawStudioLifecycleTest {
|
||||
CreatureSpawnEvent.SpawnReason.SPAWNER));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void committedStudioActivationDisablesNaturalMobSpawning() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/service/JigsawStudioService.java")).replace("\r\n", "\n");
|
||||
int registerStart = source.indexOf("public void register(");
|
||||
int commitStart = source.indexOf("public void activationCommitted(", registerStart);
|
||||
int commitEnd = source.indexOf("public void markChunkGenerated(", commitStart);
|
||||
int helperStart = source.indexOf("static void disableNaturalStudioSpawning(", commitEnd);
|
||||
String register = source.substring(registerStart, commitStart);
|
||||
String commit = source.substring(commitStart, commitEnd);
|
||||
String helper = source.substring(helperStart);
|
||||
|
||||
assertFalse(register.contains("disableNaturalStudioSpawning(world)"));
|
||||
assertTrue(commit.contains("disableNaturalStudioSpawning(world)"));
|
||||
assertTrue(helper.contains("setGameRule(GameRules.SPAWN_MOBS, false)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void closeAuthorizationChecksOwnerDirtyStateAndSaveBarrierAtomically() {
|
||||
assertTrue(JigsawStudioActivation.tryBeginOpen(OWNER));
|
||||
|
||||
@@ -7,8 +7,6 @@ import art.arcane.volmlib.util.format.Form;
|
||||
import org.junit.Test;
|
||||
import org.mockito.ArgumentCaptor;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
@@ -182,24 +180,6 @@ public class PackDownloadProgressReporterTest {
|
||||
verify(sender, times(1)).sendMessage(anyString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playerHudAlwaysShowsLoaderLaneAndActionBarWithCleanup() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/service/PackDownloadProgressReporter.java"
|
||||
)).replace("\r\n", "\n");
|
||||
|
||||
assertFalse(source.contains("HudSlotRequest"));
|
||||
assertFalse(source.contains("HudSurface"));
|
||||
assertTrue(source.contains("J.ar(this::pulseHud, HUD_PULSE_TICKS)"));
|
||||
assertTrue(source.contains("BukkitPlatform.hudLanes().show("));
|
||||
assertTrue(source.contains("sender.sendAction(snapshot.line())"));
|
||||
assertTrue(source.contains("HUD_TERMINAL_TICKS, retiredCleanup"));
|
||||
assertTrue(source.contains("BukkitPlatform.hudLanes().retire(playerId, hudLaneId)"));
|
||||
assertTrue(source.contains("BukkitPlatform.hudLanes().hide(player, hudLaneId)"));
|
||||
assertFalse(source.contains("J.runGlobal(cleanup)"));
|
||||
assertTrue(source.contains("J.car(activeTaskId);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void allDownloadPhasesHaveLocalizedLabels() {
|
||||
for (PackDownloader.DownloadPhase phase : PackDownloader.DownloadPhase.values()) {
|
||||
|
||||
-87
@@ -5,76 +5,9 @@ import art.arcane.iris.core.localization.IrisLanguage;
|
||||
import art.arcane.iris.core.localization.PackDownloadMessages;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class StudioSVCPackDownloadContractTest {
|
||||
@Test
|
||||
public void downloadsUseReporterCompletionWithoutMutatingLiveDatapacks() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/service/StudioSVC.java")).replace("\r\n", "\n");
|
||||
|
||||
assertTrue(source.contains("reporter.succeed(result);"));
|
||||
assertFalse(method(source, "public void downloadBuiltIn(VolmitSender sender, String key)")
|
||||
.contains("ServerConfigurator.restart()"));
|
||||
assertFalse(method(source, "public void downloadUrl(VolmitSender sender, String url)")
|
||||
.contains("installDataPacksIfChanged"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void downloadLeaseIsAcquiredBeforeUnconditionalIoDispatch() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/service/StudioSVC.java")).replace("\r\n", "\n");
|
||||
int mutationStart = source.indexOf("private void runPackMutation(");
|
||||
int mutationEnd = source.indexOf("private void executePackMutation(", mutationStart);
|
||||
String runPackMutation = source.substring(mutationStart, mutationEnd);
|
||||
int leaseAcquisition = runPackMutation.indexOf("LifecycleOperationCoordinator.get().acquire(");
|
||||
int executionTracking = runPackMutation.indexOf("new PackDownloadExecution(");
|
||||
int ioDispatch = runPackMutation.indexOf("MultiBurst.ioBurst.submit(");
|
||||
|
||||
assertTrue(leaseAcquisition >= 0);
|
||||
assertTrue(executionTracking > leaseAcquisition);
|
||||
assertTrue(ioDispatch > leaseAcquisition);
|
||||
assertTrue(runPackMutation.contains("execution.bind(future);"));
|
||||
assertTrue(runPackMutation.contains("execution.cancel();"));
|
||||
assertTrue(runPackMutation.contains("finally"));
|
||||
assertTrue(runPackMutation.contains("reporter.start();"));
|
||||
assertTrue(runPackMutation.contains("reporter.executionComplete();"));
|
||||
assertFalse(runPackMutation.contains("runOffPrimaryThread"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void acceptedDownloadsRouteFeedbackProgressAndTerminalStatesThroughReporter() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/service/StudioSVC.java")).replace("\r\n", "\n");
|
||||
String builtIn = method(source, "public void downloadBuiltIn(VolmitSender sender, String key)");
|
||||
String remote = method(source, "public void downloadUrl(VolmitSender sender, String url)");
|
||||
String execute = method(source, "private void executePackMutation(");
|
||||
|
||||
assertTrue(builtIn.contains("PackDownloadProgressReporter reporter"));
|
||||
assertTrue(remote.contains("PackDownloadProgressReporter reporter"));
|
||||
assertTrue(remote.contains("reporter::detail"));
|
||||
assertTrue(remote.contains("\"remote-zip\", reporter"));
|
||||
assertTrue(remote.contains("cancellation,"));
|
||||
assertTrue(remote.contains("reporter"));
|
||||
assertTrue(execute.contains("reporter.cancel();"));
|
||||
assertTrue(execute.contains("reporter.fail(e);"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shutdownClosesAdmissionAndDrainsTrackedDownloadBeforeServiceTeardown() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/service/StudioSVC.java")).replace("\r\n", "\n");
|
||||
String onDisable = method(source, "public void onDisable()");
|
||||
String quiesce = method(source, "public void quiesceDownloadsForShutdown()");
|
||||
|
||||
assertTrue(onDisable.contains("quiesceDownloadsForShutdown();"));
|
||||
assertTrue(quiesce.contains("downloadAdmissionOpen = false;"));
|
||||
assertTrue(quiesce.contains("execution.cancel();"));
|
||||
assertTrue(quiesce.contains("execution.await("));
|
||||
assertTrue(quiesce.contains("while (!execution.isComplete())"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stackedDownloadUsesLocalizedBusyMessage() {
|
||||
LifecycleOperationCoordinator.ActiveOperation download = new LifecycleOperationCoordinator.ActiveOperation(
|
||||
@@ -99,24 +32,4 @@ public class StudioSVCPackDownloadContractTest {
|
||||
StudioSVC.packMutationBusyMessage(worldCreation)
|
||||
);
|
||||
}
|
||||
|
||||
private static String method(String source, String signature) {
|
||||
int start = source.indexOf(signature);
|
||||
assertTrue("Missing source contract signature: " + signature, start >= 0);
|
||||
int openBrace = source.indexOf('{', start);
|
||||
assertTrue("Missing source contract method body: " + signature, openBrace >= 0);
|
||||
int depth = 0;
|
||||
for (int index = openBrace; index < source.length(); index++) {
|
||||
char current = source.charAt(index);
|
||||
if (current == '{') {
|
||||
depth++;
|
||||
} else if (current == '}') {
|
||||
depth--;
|
||||
if (depth == 0) {
|
||||
return source.substring(start, index + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
throw new IllegalArgumentException("Unclosed source contract method: " + signature);
|
||||
}
|
||||
}
|
||||
|
||||
-35
@@ -1,35 +0,0 @@
|
||||
package art.arcane.iris.core.service;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
|
||||
public class StudioSVCWorldPackFeedbackContractTest {
|
||||
@Test
|
||||
public void persistentPackCopyUsesLifecycleProgressWithoutLegacySnapshotFeedback() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/service/StudioSVC.java"
|
||||
)).replace("\r\n", "\n");
|
||||
|
||||
assertEquals(2, occurrences(source, "STUDIO_S_V_C_PACK_COPY_REQUIRES_ASYNC_THREAD"));
|
||||
assertEquals(3, occurrences(source, "STUDIO_S_V_C_PACK_INSTALL_FAILED"));
|
||||
assertFalse(source.contains("STUDIO_S_V_C_INSTALLING_PACKAGE"));
|
||||
assertFalse(source.contains("Publishing snapshot"));
|
||||
assertFalse(source.contains("sender.sendMessage(\"Iris refused to copy a pack"));
|
||||
assertFalse(source.contains("sender.sendMessage(\"Failed to install studio pack"));
|
||||
}
|
||||
|
||||
private static int occurrences(String value, String match) {
|
||||
int count = 0;
|
||||
int offset = 0;
|
||||
while ((offset = value.indexOf(match, offset)) >= 0) {
|
||||
count++;
|
||||
offset += match.length();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -172,27 +172,6 @@ public class StudioSVCWorldPackPublishTest {
|
||||
|
||||
@Test
|
||||
public void replaceExistingRejectsSymbolicTargetBeforePublication() throws Exception {
|
||||
String sourceCode = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/service/StudioSVC.java")).replace("\r\n", "\n");
|
||||
int install = sourceCode.indexOf("private IrisDimension installIntoDirectory(");
|
||||
int initialTargetSafety = sourceCode.indexOf(
|
||||
"requireSafePublicationTarget(target, replaceExisting)",
|
||||
install);
|
||||
int beginMutation = sourceCode.indexOf(
|
||||
"PackValidationRegistry.beginRootMutation(target)",
|
||||
initialTargetSafety);
|
||||
int finalTargetSafety = sourceCode.indexOf(
|
||||
"requireSafePublicationTarget(target, replaceExisting)",
|
||||
beginMutation);
|
||||
int publish = sourceCode.indexOf(
|
||||
"AtomicDirectoryPublisher.publish(stage, target)",
|
||||
finalTargetSafety);
|
||||
assertTrue(install >= 0);
|
||||
assertTrue(initialTargetSafety > install);
|
||||
assertTrue(beginMutation > initialTargetSafety);
|
||||
assertTrue(finalTargetSafety > beginMutation);
|
||||
assertTrue(publish > finalTargetSafety);
|
||||
|
||||
Path root = temporaryFolder.newFolder("symbolic-replacement-target").toPath();
|
||||
Path outside = root.resolve("outside-pack");
|
||||
Path target = root.resolve("world/iris/pack");
|
||||
@@ -305,27 +284,6 @@ public class StudioSVCWorldPackPublishTest {
|
||||
|
||||
@Test
|
||||
public void replacementKeepsTargetUnauthorizedThroughPublishedFingerprintWindow() throws Exception {
|
||||
String sourceCode = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/service/StudioSVC.java")).replace("\r\n", "\n");
|
||||
int install = sourceCode.indexOf("private IrisDimension installIntoDirectory(");
|
||||
int beginMutation = sourceCode.indexOf("PackValidationRegistry.beginRootMutation(target)", install);
|
||||
int publish = sourceCode.indexOf("AtomicDirectoryPublisher.publish(stage, target)", beginMutation);
|
||||
int fingerprint = sourceCode.indexOf(
|
||||
"ServerConfigurator.computePackTreeFingerprint(target.toFile())",
|
||||
publish);
|
||||
int stageValidation = sourceCode.indexOf(
|
||||
"validatePublishedPack(target, source, copiedFingerprint, validationMutation)",
|
||||
fingerprint);
|
||||
int publishCommit = sourceCode.indexOf("publication.commit()", stageValidation);
|
||||
int validationCommit = sourceCode.indexOf("validationMutation.commit()", publishCommit);
|
||||
assertTrue(install >= 0);
|
||||
assertTrue(beginMutation > install);
|
||||
assertTrue(publish > beginMutation);
|
||||
assertTrue(fingerprint > publish);
|
||||
assertTrue(stageValidation > fingerprint);
|
||||
assertTrue(publishCommit > stageValidation);
|
||||
assertTrue(validationCommit > publishCommit);
|
||||
|
||||
Path root = temporaryFolder.newFolder("validation-publication-window").toPath();
|
||||
Path sourcePack = root.resolve("source");
|
||||
Path target = root.resolve("world/iris/pack");
|
||||
@@ -454,26 +412,6 @@ public class StudioSVCWorldPackPublishTest {
|
||||
assertEquals("second", second.join());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void productionWorldPublicationUsesImmutableGenerationHistory() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/service/StudioSVC.java"
|
||||
)).replace("\r\n", "\n");
|
||||
int productionStart = source.indexOf("private IrisDimension publishGenerationHistory(");
|
||||
int transientStart = source.indexOf("private IrisDimension installIntoDirectory(", productionStart);
|
||||
String production = source.substring(productionStart, transientStart);
|
||||
|
||||
assertTrue(production.contains("GenerationHistory.create("));
|
||||
assertTrue(production.contains("GenerationHistory.openIfPresent(dimensionRoot, worldSeed)"));
|
||||
assertTrue(production.contains("GenerationHistory.adoptLegacyPack("));
|
||||
assertTrue(production.contains("history.stageUpdate("));
|
||||
assertTrue(production.contains("getGenerationTransitionWidthBlocks()"));
|
||||
assertTrue(production.contains("history.activePackRoot()"));
|
||||
assertFalse(production.contains("AtomicDirectoryPublisher.publish("));
|
||||
assertTrue(source.contains("installIntoTransientWorld("));
|
||||
assertTrue(source.contains("new File(dimensionRoot, \"iris/pack\")"));
|
||||
}
|
||||
|
||||
private static void writeValidPack(Path packRoot) throws Exception {
|
||||
Files.createDirectories(packRoot.resolve("dimensions"));
|
||||
Files.createDirectories(packRoot.resolve("regions"));
|
||||
|
||||
-75
@@ -1,75 +0,0 @@
|
||||
package art.arcane.iris.core.tools;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisPersistentWorldCreationContractTest {
|
||||
@Test
|
||||
public void productionCreateFreezesIntoCurrentPlatformStorageBeforeWorldCreation() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/tools/IrisCreator.java")).replace("\r\n", "\n");
|
||||
int createStart = source.indexOf("private World createReserved(");
|
||||
int createEnd = source.indexOf("static Player createTeleportTarget(", createStart);
|
||||
String create = source.substring(createStart, createEnd);
|
||||
|
||||
int dimensionRoot = create.indexOf("WorldCreatorCompat.persistentDimensionRoot(worldKey)");
|
||||
int levelRoot = create.indexOf("WorldCreatorCompat.persistentLevelRoot(worldKey)");
|
||||
int existingStorage = create.indexOf("Files.exists(storageRoot.toPath())");
|
||||
int freezePack = create.indexOf("studioService.installIntoWorld(sender, resolvedDimension, dimensionRoot, seed)");
|
||||
int persistentCreator = create.indexOf(".persistent(!studio && !benchmark)");
|
||||
int bukkitCreate = create.indexOf("INMS.get().createWorldAsync(wc, request)");
|
||||
int rollbackStorage = create.indexOf(
|
||||
"rollbackWorldCreation(worldKey, world, stagedGenerator, storageRoot, bukkitRegistered, failure)"
|
||||
);
|
||||
|
||||
assertTrue(dimensionRoot >= 0);
|
||||
assertTrue(levelRoot > dimensionRoot);
|
||||
assertTrue(existingStorage > levelRoot);
|
||||
assertTrue(freezePack > existingStorage);
|
||||
assertTrue(persistentCreator > freezePack);
|
||||
assertTrue(bukkitCreate > persistentCreator);
|
||||
assertTrue(rollbackStorage > bukkitCreate);
|
||||
assertFalse(create.contains("copySeed"));
|
||||
assertFalse(create.contains("level.dat"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistentCreatorPreservesConfiguredNameAndCanonicalIdentity() throws Exception {
|
||||
String source = Files.readString(Path.of("src/main/java/art/arcane/iris/core/tools/IrisWorldCreator.java")).replace("\r\n", "\n");
|
||||
int createStart = source.indexOf("public WorldCreator create()");
|
||||
int createEnd = source.indexOf("private World.Environment findEnvironment()", createStart);
|
||||
String create = source.substring(createStart, createEnd);
|
||||
|
||||
int persistentCreator = create.indexOf("WorldCreatorCompat.ofPersistentKey(worldKey)");
|
||||
int persistentStorage = create.indexOf("WorldCreatorCompat.persistentDimensionRoot(worldKey)");
|
||||
int canonicalIdentity = create.indexOf(".platformIdentity(worldKey.toString())");
|
||||
int configuredName = create.indexOf(".name(creator.name())");
|
||||
int history = create.indexOf("requireGenerationHistory(w.worldFolder(), seed)");
|
||||
int exactPack = create.indexOf("requireActivePack(generationHistory)");
|
||||
|
||||
assertTrue(persistentCreator >= 0);
|
||||
assertTrue(persistentStorage > persistentCreator);
|
||||
assertTrue(canonicalIdentity > persistentStorage);
|
||||
assertTrue(configuredName > canonicalIdentity);
|
||||
assertTrue(history > configuredName);
|
||||
assertTrue(exactPack > history);
|
||||
assertTrue(create.contains("new File(w.worldFolder(), \"iris/pack\")"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void publicBukkitBackendRebuildKeepsExactFallbackWorldName() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/lifecycle/WorldLifecycleRequest.java"
|
||||
)).replace("\r\n", "\n");
|
||||
int rebuildStart = source.indexOf("public WorldCreator toWorldCreator()");
|
||||
int rebuildEnd = source.indexOf("return creator;", rebuildStart);
|
||||
String rebuild = source.substring(rebuildStart, rebuildEnd);
|
||||
|
||||
assertTrue(rebuild.contains("WorldCreatorCompat.ofKey(worldKey, worldName)"));
|
||||
assertFalse(rebuild.contains("WorldCreatorCompat.ofKey(worldKey)"));
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,7 @@ import art.arcane.iris.core.localization.RuntimeProgressMessages;
|
||||
import art.arcane.iris.util.common.format.C;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
@@ -79,20 +75,6 @@ public class WorldCreationProgressReporterTest {
|
||||
assertTrue(failed.contains("FAILED | Entering world"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void playerProgressUsesTheEntityScheduledActionBarWithoutABossBar() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/core/tools/WorldCreationProgressReporter.java"
|
||||
)).replace("\r\n", "\n");
|
||||
|
||||
assertFalse(source.contains("Bukkit.createBossBar("));
|
||||
assertFalse(source.contains("WORLD_CREATE_BOSSBAR_"));
|
||||
assertFalse(source.contains("isProgressBossBar()"));
|
||||
assertTrue(source.contains("RuntimeProgressMessages.WORLD_CREATE_LIFECYCLE_ACTION"));
|
||||
assertTrue(source.contains("J.runEntity(sender.player(), guardedRender)"));
|
||||
assertTrue(source.contains("J.runEntity(sender.player(), render)"));
|
||||
}
|
||||
|
||||
private static int occurrences(String value, String match) {
|
||||
int count = 0;
|
||||
int offset = 0;
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
package art.arcane.iris.engine;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class EngineRuntimePublicationContractTest {
|
||||
@Test
|
||||
public void worldManagerStartsOnlyAfterTheRuntimeSessionIsReady() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineRuntimeBuilder.java")).replace("\r\n", "\n");
|
||||
int publishStart = source.indexOf("void publishRuntime(");
|
||||
int publishEnd = source.indexOf("private void scheduleRuntimeTasks", publishStart);
|
||||
String publish = source.substring(publishStart, publishEnd);
|
||||
|
||||
assertBefore(publish, "engine.runtime = next", "next.worldManager().start()");
|
||||
assertBefore(publish, "activateNextSession()", "next.worldManager().start()");
|
||||
assertBefore(publish, "engine.lifecycleState = LifecycleState.RUNNING", "next.worldManager().start()");
|
||||
assertBefore(publish, "engine.getClosing().set(false)", "next.worldManager().start()");
|
||||
assertBefore(publish, "openBackgroundTaskAdmission()", "next.worldManager().start()");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failedManagerStartClosesAdmissionBeforeRuntimeCleanup() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineRuntimeBuilder.java")).replace("\r\n", "\n");
|
||||
int publishStart = source.indexOf("void publishRuntime(");
|
||||
int publishEnd = source.indexOf("private void scheduleRuntimeTasks", publishStart);
|
||||
String publish = source.substring(publishStart, publishEnd);
|
||||
|
||||
assertBefore(publish, "engine.getClosing().set(true)", "cleanupFailure = engine.shutdownSequence.closeRuntime(next, null)");
|
||||
assertBefore(publish, "closeBackgroundTaskAdmission()", "cleanupFailure = engine.shutdownSequence.closeRuntime(next, null)");
|
||||
assertBefore(publish, "sealAndAwait(", "cleanupFailure = engine.shutdownSequence.closeRuntime(next, null)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void dimensionStackRegistrationRunsAfterHostStaticObjects() throws IOException {
|
||||
String builderSource = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineRuntimeBuilder.java")).replace("\r\n", "\n");
|
||||
int buildStart = builderSource.indexOf("private EngineRuntime buildRuntime(");
|
||||
int buildEnd = builderSource.indexOf("GenerationRuntime buildDetachedGenerationRuntime(", buildStart);
|
||||
String build = builderSource.substring(buildStart, buildEnd);
|
||||
|
||||
assertBefore(build, "assembly.runtimeKernel.registerStaticObjects(engine, assembly.mode)",
|
||||
"assembly.runtimeKernel.registerDimensionStack(");
|
||||
|
||||
String kernelSource = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/history/GenerationKernelV1.java")).replace("\r\n", "\n");
|
||||
assertBefore(kernelSource, "staticObjects.apply(engine, x, z, blocks)",
|
||||
"dimensionStack.actuate(x, z, blocks, multicore, chunkContext)");
|
||||
assertBefore(kernelSource, "dimensionStack.actuate(x, z, blocks, multicore, chunkContext)",
|
||||
"stackCustom.modify(x, z, blocks, multicore, chunkContext)");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detachedRuntimeBuildDoesNotCreateWorldOwnedServices() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineRuntimeBuilder.java")).replace("\r\n", "\n");
|
||||
int buildStart = source.indexOf("GenerationRuntime buildDetachedGenerationRuntime(");
|
||||
int buildEnd = source.indexOf("void publishRuntime(", buildStart);
|
||||
String build = source.substring(buildStart, buildEnd);
|
||||
|
||||
assertTrue(build.contains("assembly.runtimeKernel.createComplex(engine, assembly.transitionPlan)"));
|
||||
assertTrue(build.contains("assembly.runtimeKernel.createMantle(engine, assembly.mantleStorageDirectory)"));
|
||||
assertTrue(build.contains("assembly.mantle.hotload()"));
|
||||
assertTrue(build.contains("assembly.runtimeKernel.createMode(engine)"));
|
||||
assertTrue(build.contains("assembly.runtimeKernel.createUpperContext(engine)"));
|
||||
assertTrue(build.contains("assembly.runtimeKernel.registerStaticObjects(engine, assembly.mode)"));
|
||||
assertFalse(build.contains("EngineEffectsProvider"));
|
||||
assertFalse(build.contains("EngineWorldManagerProvider"));
|
||||
assertFalse(build.contains("worldManager().start()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runtimePublicationTransfersAnIdenticalMantleWithoutClosingIt() throws IOException {
|
||||
String builderSource = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineRuntimeBuilder.java")).replace("\r\n", "\n");
|
||||
String shutdownSource = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineShutdownSequence.java")).replace("\r\n", "\n");
|
||||
|
||||
assertTrue(builderSource.contains("next.generation().mantle()"));
|
||||
assertTrue(shutdownSource.contains("generationRuntime.mantle() != retainedMantle"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void engineAndDetachedRuntimeAcceptExplicitMantleDirectories() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/IrisEngine.java")).replace("\r\n", "\n");
|
||||
|
||||
assertTrue(source.contains("InitializationMode initializationMode,\n Path mantleStorageDirectory"));
|
||||
assertTrue(source.contains("EngineTarget runtimeTarget,\n Path mantleStorageDirectory"));
|
||||
assertTrue(source.contains("exclusive mantle storage directory"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mantleIoPinsTheRuntimeDataAcrossWorkerThreads() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/IrisEngineMantle.java")).replace("\r\n", "\n");
|
||||
|
||||
assertTrue(source.contains("runtimeData::get"));
|
||||
assertTrue(source.contains("runtimeData.set(Objects.requireNonNull(engine.getData()"));
|
||||
assertFalse(source.contains("createDataAdapter(engine::getData)"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failedDetachedBuildReleasesTransferredTargetOwnership() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/IrisEngine.java")).replace("\r\n", "\n");
|
||||
int buildStart = source.indexOf("public GenerationRuntimeBinding buildDetachedGenerationRuntime(");
|
||||
int buildEnd = source.indexOf("public void closeDetachedGenerationRuntime(", buildStart);
|
||||
String build = source.substring(buildStart, buildEnd);
|
||||
|
||||
assertTrue(build.contains("detachedData.registerEngine(this)"));
|
||||
assertTrue(build.contains("shutdownSequence.closeDetachedTarget(requiredTarget, null)"));
|
||||
assertTrue(build.contains("shutdownSequence.closeDetachedGenerationRuntime(detached, null)"));
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = source.indexOf(first);
|
||||
int secondIndex = source.indexOf(second);
|
||||
assertTrue("Missing source contract token: " + first, firstIndex >= 0);
|
||||
assertTrue("Missing source contract token: " + second, secondIndex >= 0);
|
||||
assertTrue(first + " must occur before " + second, firstIndex < secondIndex);
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package art.arcane.iris.engine;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class EngineShutdownOwnershipContractTest {
|
||||
@Test
|
||||
public void attachedHistoryRouterClosesBeforeRuntimeOwnershipReleases() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineShutdownSequence.java")).replace("\r\n", "\n");
|
||||
int closeStart = source.indexOf("void close()");
|
||||
int routerClose = source.indexOf("engine::closeAttachedGenerationHistoryRuntimeRouter", closeStart);
|
||||
int runtimeRelease = source.indexOf("releaseRuntime(failure)", routerClose);
|
||||
|
||||
assertTrue(routerClose > closeStart);
|
||||
assertTrue(runtimeRelease > routerClose);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ownershipCloseMustSucceedBeforeRuntimeRelease() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineShutdownSequence.java")).replace("\r\n", "\n");
|
||||
int ownershipClose = source.indexOf("NativeStructureOwnershipStore.close(engine)");
|
||||
int ownershipGate = source.indexOf("if (ownershipFailure == null)", ownershipClose);
|
||||
int runtimeRelease = source.indexOf("releaseRuntime(failure)", ownershipGate);
|
||||
|
||||
assertTrue(ownershipClose >= 0);
|
||||
assertTrue(ownershipGate > ownershipClose);
|
||||
assertTrue(runtimeRelease > ownershipGate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failedConstructionKeepsMantleOpenWhileOwnershipWritesRemain() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineShutdownSequence.java")).replace("\r\n", "\n");
|
||||
int cleanupStart = source.indexOf("void cleanupFailedConstruction");
|
||||
int cleanupEnd = source.indexOf("Throwable closeAssembly", cleanupStart);
|
||||
String cleanup = source.substring(cleanupStart, cleanupEnd);
|
||||
int ownershipClose = cleanup.indexOf("NativeStructureOwnershipStore.close(engine)");
|
||||
int ownershipGate = cleanup.indexOf("if (ownershipFailure == null)", ownershipClose);
|
||||
int runtimeRelease = cleanup.indexOf("releaseRuntime(cleanupFailure)", ownershipGate);
|
||||
int targetRelease = cleanup.indexOf("releaseTarget(cleanupFailure)", runtimeRelease);
|
||||
int closedPublication = cleanup.indexOf("engine.closed = true", ownershipGate);
|
||||
|
||||
assertTrue(ownershipClose >= 0);
|
||||
assertTrue(ownershipGate > ownershipClose);
|
||||
assertTrue(runtimeRelease > ownershipGate);
|
||||
assertTrue(targetRelease > runtimeRelease);
|
||||
assertTrue(closedPublication > ownershipGate);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detachedRuntimesCloseBeforeTheBaseRuntimeAndTarget() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineShutdownSequence.java")).replace("\r\n", "\n");
|
||||
int releaseStart = source.indexOf("private Throwable releaseRuntime(");
|
||||
int releaseEnd = source.indexOf("private Throwable closeDetachedGenerationRuntimes(", releaseStart);
|
||||
String release = source.substring(releaseStart, releaseEnd);
|
||||
int detachedRelease = release.indexOf("closeDetachedGenerationRuntimes(null)");
|
||||
int detachedGate = release.indexOf("if (detachedFailure != null)", detachedRelease);
|
||||
int baseRelease = release.indexOf("closeRuntime(engine.runtime, null)", detachedGate);
|
||||
int targetRelease = source.indexOf("engine.publishedTarget::close", releaseEnd);
|
||||
|
||||
assertTrue(detachedRelease >= 0);
|
||||
assertTrue(detachedGate > detachedRelease);
|
||||
assertTrue(baseRelease > detachedGate);
|
||||
assertTrue(targetRelease > releaseEnd);
|
||||
}
|
||||
}
|
||||
-107
@@ -1,107 +0,0 @@
|
||||
package art.arcane.iris.engine;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class NativeStructureBootstrapLifecycleContractTest {
|
||||
@Test
|
||||
public void closeWaitsBeforeLifecycleMutationAndGenerationSeal() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineShutdownSequence.java")).replace("\r\n", "\n");
|
||||
int closeStart = source.indexOf("void close()");
|
||||
int closeEnd = source.indexOf("void cleanupFailedConstruction", closeStart);
|
||||
String close = source.substring(closeStart, closeEnd);
|
||||
|
||||
assertBefore(close,
|
||||
"engine.awaitNativeStructureBootstrap(\"close\")",
|
||||
"engine.lifecycleState = LifecycleState.CLOSING");
|
||||
assertBefore(close,
|
||||
"engine.awaitNativeStructureBootstrap(\"close\")",
|
||||
"sealAndAwait(\"close\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void fullAndComplexHotloadWaitBeforeLifecycleMutationAndGenerationSeal() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/EngineHotloader.java")).replace("\r\n", "\n");
|
||||
int complexStart = source.indexOf("void hotloadComplex()");
|
||||
int fullStart = source.indexOf("void hotloadSilently()", complexStart);
|
||||
String complex = source.substring(complexStart, fullStart);
|
||||
int fullEnd = source.indexOf("private void broadcastStudioHotload", fullStart);
|
||||
String full = source.substring(fullStart, fullEnd);
|
||||
|
||||
assertBefore(complex,
|
||||
"engine.awaitNativeStructureBootstrap(\"complex hotload\")",
|
||||
"engine.lifecycleState = LifecycleState.HOTLOADING");
|
||||
assertBefore(complex,
|
||||
"engine.awaitNativeStructureBootstrap(\"complex hotload\")",
|
||||
"engine.sealForTransition(\"complex hotload\"");
|
||||
assertBefore(full,
|
||||
"engine.awaitNativeStructureBootstrap(\"hotload\")",
|
||||
"engine.lifecycleState = LifecycleState.HOTLOADING");
|
||||
assertBefore(full,
|
||||
"engine.awaitNativeStructureBootstrap(\"hotload\")",
|
||||
"engine.sealForTransition(\"hotload\"");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void ringBootstrapStartsUnderTheSameLifecycleLock() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/IrisEngine.java")).replace("\r\n", "\n");
|
||||
int start = source.indexOf("public CompletableFuture<Void> startNativeStructureBootstrap(");
|
||||
int end = source.indexOf("void awaitNativeStructureBootstrap", start);
|
||||
String method = source.substring(start, end);
|
||||
|
||||
assertBefore(method, "synchronized (lifecycleLock)",
|
||||
"nativeStructureBootstrapBarrier.start(claim, starter)");
|
||||
assertBefore(method, "requireRunning(\"prepare native structure placements\")",
|
||||
"nativeStructureBootstrapBarrier.start(claim, starter)");
|
||||
assertBefore(method, "nativeStructureBootstrapBarrier.start(claim, starter)",
|
||||
"activation.run()");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void studioVolumeQueriesStayOutsideCachesUntilTheBootstrapGateReleases() throws IOException {
|
||||
String engineSource = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/IrisEngine.java")).replace("\r\n", "\n");
|
||||
int queryStart = engineSource.indexOf(
|
||||
"public KList<NativeStructureVolume> getNativeStructureVolumes(");
|
||||
int queryEnd = engineSource.indexOf(
|
||||
"public void setNativeStructureVolumeQueriesEnabled", queryStart);
|
||||
String query = engineSource.substring(queryStart, queryEnd);
|
||||
int setterEnd = engineSource.indexOf("public IrisEngineData getEngineData()", queryEnd);
|
||||
String setter = engineSource.substring(queryEnd, setterEnd);
|
||||
String generatorSource = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/platform/BukkitChunkGenerator.java")).replace("\r\n", "\n");
|
||||
int releaseStart = generatorSource.indexOf("public void endStudioEntryBootstrap()");
|
||||
int releaseEnd = generatorSource.indexOf(
|
||||
"public boolean isStudioEntryBootstrapActive()", releaseStart);
|
||||
String release = generatorSource.substring(releaseStart, releaseEnd);
|
||||
|
||||
assertTrue(engineSource.contains(
|
||||
"new AtomicBoolean(!requiredMode.studio())"));
|
||||
assertBefore(query,
|
||||
"!nativeStructureVolumeQueriesEnabled.get()",
|
||||
"nativeStructureVolumeMemo.volumes(");
|
||||
assertBefore(setter,
|
||||
"nativeStructureVolumeMemo.clear()",
|
||||
"nativeStructureVolumeQueriesEnabled.set(true)");
|
||||
assertBefore(release,
|
||||
"irisEngine.setNativeStructureVolumeQueriesEnabled(shouldGenerateNativeStructures(",
|
||||
"studioEntryBootstrapActive.set(false)");
|
||||
}
|
||||
|
||||
private static void assertBefore(String source, String first, String second) {
|
||||
int firstIndex = source.indexOf(first);
|
||||
int secondIndex = source.indexOf(second);
|
||||
assertTrue("Missing source contract token: " + first, firstIndex >= 0);
|
||||
assertTrue("Missing source contract token: " + second, secondIndex >= 0);
|
||||
assertTrue(first + " must occur before " + second, firstIndex < secondIndex);
|
||||
}
|
||||
}
|
||||
-17
@@ -11,11 +11,8 @@ import art.arcane.iris.engine.object.StudioMode;
|
||||
import art.arcane.iris.engine.object.IrisDimension;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Method;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
@@ -108,19 +105,6 @@ public class BukkitChunkGeneratorInitializationModeTest {
|
||||
assertEquals(-64, BukkitChunkGenerator.authoringBaseHeight(-64, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void authoringStartupDoesNotPrefetchTerrainHydrology() throws IOException {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/engine/platform/BukkitChunkGenerator.java"));
|
||||
int start = source.indexOf("private CompletableFuture<Void> prefetchSpawnHydrology(");
|
||||
int end = source.indexOf("private void updateSpawnLocation(", start);
|
||||
String prefetch = source.substring(start, end);
|
||||
|
||||
assertTrue(prefetch.indexOf("if (usesFlatStudioTerrain())") >= 0);
|
||||
assertTrue(prefetch.indexOf("if (usesFlatStudioTerrain())")
|
||||
< prefetch.indexOf("engine.getComplex()"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void runtimeAndOrdinaryStudioWarmGenerationCaches() {
|
||||
IrisEngine.InitializationMode runtime =
|
||||
@@ -180,5 +164,4 @@ public class BukkitChunkGeneratorInitializationModeTest {
|
||||
assertFalse(BukkitChunkGenerator.shouldPersistWorldRegistration(true));
|
||||
assertTrue(BukkitChunkGenerator.shouldPersistWorldRegistration(false));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package art.arcane.iris.purity;
|
||||
|
||||
import art.arcane.iris.core.loader.IrisData;
|
||||
import org.junit.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class CoreProcessStreamPurityGateTest {
|
||||
private static final String SYSTEM_OWNER = "java/lang/System";
|
||||
private static final String STACK_TRACE_METHOD = "printStackTrace";
|
||||
|
||||
@Test
|
||||
public void coreProductionBytecodeNeverTouchesTheProcessStreams() throws Exception {
|
||||
Path classes = Path.of(IrisData.class.getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||
assertTrue("core classes must be a directory to scan, got " + classes, Files.isDirectory(classes));
|
||||
List<String> violations = new ArrayList<>();
|
||||
int scanned = 0;
|
||||
|
||||
try (Stream<Path> tree = Files.walk(classes)) {
|
||||
for (Path file : tree.filter(candidate -> candidate.toString().endsWith(".class")).toList()) {
|
||||
collectViolations(file, violations);
|
||||
scanned++;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue("the gate scanned only " + scanned + " core classes, so it proves nothing", scanned > 1000);
|
||||
|
||||
assertTrue("Core production code must report through IrisLogging, never the process streams."
|
||||
+ " printStackTrace(PrintStream)/printStackTrace(PrintWriter) write to a caller-supplied"
|
||||
+ " sink and stay allowed:" + String.join("", violations), violations.isEmpty());
|
||||
}
|
||||
|
||||
private static void collectViolations(Path classFile, List<String> violations) throws Exception {
|
||||
ClassReader reader;
|
||||
try (InputStream input = Files.newInputStream(classFile)) {
|
||||
reader = new ClassReader(input);
|
||||
}
|
||||
String owner = reader.getClassName();
|
||||
|
||||
reader.accept(new ClassVisitor(Opcodes.ASM9) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(
|
||||
int access,
|
||||
String name,
|
||||
String descriptor,
|
||||
String signature,
|
||||
String[] exceptions
|
||||
) {
|
||||
return new MethodVisitor(Opcodes.ASM9) {
|
||||
@Override
|
||||
public void visitFieldInsn(int opcode, String fieldOwner, String fieldName, String fieldDescriptor) {
|
||||
if (opcode != Opcodes.GETSTATIC || !SYSTEM_OWNER.equals(fieldOwner)) {
|
||||
return;
|
||||
}
|
||||
if ("out".equals(fieldName) || "err".equals(fieldName)) {
|
||||
violations.add(report(owner, name, "System." + fieldName));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(
|
||||
int opcode,
|
||||
String methodOwner,
|
||||
String methodName,
|
||||
String methodDescriptor,
|
||||
boolean isInterface
|
||||
) {
|
||||
if (STACK_TRACE_METHOD.equals(methodName) && "()V".equals(methodDescriptor)) {
|
||||
violations.add(report(owner, name, methodOwner.replace('/', '.') + "#printStackTrace()"));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
}
|
||||
|
||||
private static String report(String owner, String method, String detail) {
|
||||
return "\n " + owner.replace('/', '.') + "#" + method + " -> " + detail;
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,11 @@ package art.arcane.iris.purity;
|
||||
|
||||
import art.arcane.iris.engine.object.annotations.Snippet;
|
||||
import org.junit.Test;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
import org.objectweb.asm.Type;
|
||||
|
||||
import java.io.File;
|
||||
import java.lang.annotation.Annotation;
|
||||
@@ -10,7 +15,6 @@ import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.nio.file.Paths;
|
||||
@@ -22,8 +26,6 @@ import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
@@ -74,13 +76,12 @@ public class PackTypeBukkitPurityGateTest {
|
||||
/** See {@link #theGateStillCatchesAClassThatNeedsBukkit()}. */
|
||||
private static final String BUKKIT_DEPENDENT_CANARY = "art.arcane.iris.engine.object.IrisObjectRotation$Faces";
|
||||
private static final String OBJECT_PACKAGE = "art/arcane/iris/engine/object";
|
||||
private static final Path IRIS_DATA_SOURCE =
|
||||
Paths.get("src", "main", "java", "art", "arcane", "iris", "core", "loader", "IrisData.java");
|
||||
private static final String IRIS_DATA = "art.arcane.iris.core.loader.IrisData";
|
||||
|
||||
/**
|
||||
* Every type handed to {@code registerLoader} in {@code IrisData.hotloaded()} - i.e. every root
|
||||
* type Gson deserializes from a pack. Kept literal on purpose; {@link
|
||||
* #registeredRootListMatchesIrisData()} fails if the source drifts away from it.
|
||||
* #registeredRootListMatchesIrisData()} fails if the compiled registrations drift away from it.
|
||||
*/
|
||||
private static final List<String> GSON_REGISTERED_ROOTS = List.of(
|
||||
"art.arcane.iris.engine.object.IrisLootTable",
|
||||
@@ -140,21 +141,52 @@ public class PackTypeBukkitPurityGateTest {
|
||||
|
||||
@Test
|
||||
public void registeredRootListMatchesIrisData() throws Exception {
|
||||
if (!Files.isRegularFile(IRIS_DATA_SOURCE)) {
|
||||
return;
|
||||
}
|
||||
String source = Files.readString(IRIS_DATA_SOURCE, StandardCharsets.UTF_8);
|
||||
Matcher matcher = Pattern.compile("registerLoader\\((\\w+)\\.class").matcher(source);
|
||||
Set<String> inSource = new TreeSet<>();
|
||||
while (matcher.find()) {
|
||||
inSource.add(matcher.group(1));
|
||||
}
|
||||
byte[] bytes = BukkitHidingClassLoader.readClassBytes(getClass().getClassLoader(), IRIS_DATA);
|
||||
assertNotNull("no class bytes for " + IRIS_DATA, bytes);
|
||||
|
||||
Set<String> registered = new TreeSet<>();
|
||||
new ClassReader(bytes).accept(new ClassVisitor(Opcodes.ASM9) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(
|
||||
int access,
|
||||
String name,
|
||||
String descriptor,
|
||||
String signature,
|
||||
String[] exceptions
|
||||
) {
|
||||
return new MethodVisitor(Opcodes.ASM9) {
|
||||
private String pendingRegistrant;
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object value) {
|
||||
if (value instanceof Type type && type.getSort() == Type.OBJECT) {
|
||||
pendingRegistrant = type.getClassName();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(
|
||||
int opcode,
|
||||
String owner,
|
||||
String methodName,
|
||||
String methodDescriptor,
|
||||
boolean isInterface
|
||||
) {
|
||||
if ("registerLoader".equals(methodName) && pendingRegistrant != null) {
|
||||
registered.add(pendingRegistrant.substring(pendingRegistrant.lastIndexOf('.') + 1));
|
||||
}
|
||||
pendingRegistrant = null;
|
||||
}
|
||||
};
|
||||
}
|
||||
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
|
||||
Set<String> inTest = new TreeSet<>();
|
||||
for (String name : GSON_REGISTERED_ROOTS) {
|
||||
inTest.add(name.substring(name.lastIndexOf('.') + 1));
|
||||
}
|
||||
assertEquals("IrisData registers a different set of pack roots than this gate covers - "
|
||||
+ "add the new type to GSON_REGISTERED_ROOTS", inTest, inSource);
|
||||
+ "add the new type to GSON_REGISTERED_ROOTS", inTest, registered);
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -1,21 +1,32 @@
|
||||
package art.arcane.iris.spi;
|
||||
|
||||
import art.arcane.iris.core.service.StudioSVC;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
import org.objectweb.asm.ClassReader;
|
||||
import org.objectweb.asm.ClassVisitor;
|
||||
import org.objectweb.asm.MethodVisitor;
|
||||
import org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.InputStream;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Answers.CALLS_REAL_METHODS;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class PacksFolderContractTest {
|
||||
private static final String PLATFORM_OWNER = "art/arcane/iris/spi/IrisPlatform";
|
||||
private static final String PACKS_ROOT_NAME = StudioSVC.WORKSPACE_NAME;
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@@ -29,24 +40,92 @@ public class PacksFolderContractTest {
|
||||
assertEquals(new File(data, "packs" + File.separator + "overworld"), platform.packsFolderNoCreate("overworld"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Source guard: nothing outside the SPI defaults and platform overrides may spell the packs
|
||||
* root by hand. New code goes through packsFolder()/packsFolderNoCreate().
|
||||
*/
|
||||
@Test
|
||||
public void noCallSiteResolvesThePacksRootByHand() throws Exception {
|
||||
List<Path> roots = List.of(
|
||||
Path.of("src/main/java"),
|
||||
Path.of("../adapters/bukkit/plugin/src/main/java"));
|
||||
for (Path root : roots) {
|
||||
try (var stream = Files.walk(root)) {
|
||||
for (Path file : stream.filter(p -> p.toString().endsWith(".java")).toList()) {
|
||||
String text = Files.readString(file);
|
||||
assertFalse(file + " must use packsFolder() instead of dataFolder(\"packs\")",
|
||||
text.contains("dataFolder(\"packs\")"));
|
||||
assertFalse(file + " must use packsFolderNoCreate() instead of the WORKSPACE_NAME indirection",
|
||||
text.contains("dataFolderNoCreate(StudioSVC.WORKSPACE_NAME"));
|
||||
}
|
||||
public void noCompiledCallSiteResolvesThePacksRootByHand() throws Exception {
|
||||
Path classes = Path.of(StudioSVC.class.getProtectionDomain().getCodeSource().getLocation().toURI());
|
||||
assertTrue("core classes must be a directory to scan, got " + classes, Files.isDirectory(classes));
|
||||
List<String> violations = new ArrayList<>();
|
||||
int scanned = 0;
|
||||
|
||||
try (Stream<Path> tree = Files.walk(classes)) {
|
||||
for (Path file : tree.filter(candidate -> candidate.toString().endsWith(".class")).toList()) {
|
||||
collectViolations(file, violations);
|
||||
scanned++;
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue("the gate scanned only " + scanned + " core classes, so it proves nothing", scanned > 1000);
|
||||
|
||||
assertTrue("These methods spell the packs root by hand instead of going through"
|
||||
+ " IrisPlatform.packsFolder()/packsFolderNoCreate():" + String.join("", violations),
|
||||
violations.isEmpty());
|
||||
}
|
||||
|
||||
private static void collectViolations(Path classFile, List<String> violations) throws Exception {
|
||||
ClassReader reader;
|
||||
try (InputStream input = Files.newInputStream(classFile)) {
|
||||
reader = new ClassReader(input);
|
||||
}
|
||||
String owner = reader.getClassName();
|
||||
if (owner.equals(PLATFORM_OWNER)) {
|
||||
return;
|
||||
}
|
||||
|
||||
reader.accept(new ClassVisitor(Opcodes.ASM9) {
|
||||
@Override
|
||||
public MethodVisitor visitMethod(
|
||||
int access,
|
||||
String name,
|
||||
String descriptor,
|
||||
String signature,
|
||||
String[] exceptions
|
||||
) {
|
||||
return new PacksRootCallSite(owner, name, violations);
|
||||
}
|
||||
}, ClassReader.SKIP_DEBUG | ClassReader.SKIP_FRAMES);
|
||||
}
|
||||
|
||||
private static final class PacksRootCallSite extends MethodVisitor {
|
||||
private final String owner;
|
||||
private final String method;
|
||||
private final List<String> violations;
|
||||
private boolean loadsPacksRootName;
|
||||
private boolean resolvesDataFolder;
|
||||
|
||||
private PacksRootCallSite(String owner, String method, List<String> violations) {
|
||||
super(Opcodes.ASM9);
|
||||
this.owner = owner;
|
||||
this.method = method;
|
||||
this.violations = violations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitLdcInsn(Object value) {
|
||||
if (PACKS_ROOT_NAME.equals(value)) {
|
||||
loadsPacksRootName = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitMethodInsn(
|
||||
int opcode,
|
||||
String methodOwner,
|
||||
String methodName,
|
||||
String methodDescriptor,
|
||||
boolean isInterface
|
||||
) {
|
||||
if (!PLATFORM_OWNER.equals(methodOwner)) {
|
||||
return;
|
||||
}
|
||||
if ("dataFolder".equals(methodName) || "dataFolderNoCreate".equals(methodName)) {
|
||||
resolvesDataFolder = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitEnd() {
|
||||
if (loadsPacksRootName && resolvesDataFolder) {
|
||||
violations.add("\n " + owner.replace('/', '.') + "#" + method);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
-31
@@ -21,9 +21,6 @@ import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.director.exceptions.DirectorParsingException;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@@ -231,24 +228,6 @@ public class StructureHandlerTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void completionUsesOneReachabilitySnapshotAndLocatableKeys() throws IOException {
|
||||
Path sourcePath = Path.of(
|
||||
"src/main/java/art/arcane/iris/util/common/director/specialhandlers/StructureHandler.java");
|
||||
String source = Files.readString(sourcePath).replace("\r\n", "\n");
|
||||
int methodStart = source.indexOf("public KList<String> getPossibilities()");
|
||||
int methodEnd = source.indexOf("@Override\n public String toString", methodStart);
|
||||
String method = source.substring(methodStart, methodEnd);
|
||||
|
||||
assertEquals(1, occurrences(method, "StructureReachability.reachableKeys(activeEngine)"));
|
||||
assertEquals(0, occurrences(method, "IrisStructureLocator.locatableKeys(activeEngine)"));
|
||||
assertEquals(1, occurrences(method, "IrisStructureLocator.locatableEditableKeys(activeEngine)"));
|
||||
assertTrue(method.indexOf("if (activeEngine == null)")
|
||||
< method.indexOf("IrisPlatforms.get().structureHooks()"));
|
||||
assertFalse(method.contains("INMS"));
|
||||
assertFalse(method.contains("catch ("));
|
||||
}
|
||||
|
||||
private static IrisNativeStructureDecision decision(NativeStructureGenerationStatus status) {
|
||||
return new IrisNativeStructureDecision(status, 0, null, false, null, null);
|
||||
}
|
||||
@@ -268,16 +247,6 @@ public class StructureHandlerTest {
|
||||
return placement;
|
||||
}
|
||||
|
||||
private static int occurrences(String source, String target) {
|
||||
int count = 0;
|
||||
int offset = 0;
|
||||
while ((offset = source.indexOf(target, offset)) >= 0) {
|
||||
count++;
|
||||
offset += target.length();
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
private static final class TestStructureHandler extends StructureHandler {
|
||||
private final Engine activeEngine;
|
||||
private final boolean nativeGenerationEnabled;
|
||||
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
package art.arcane.iris.util.common.scheduling;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class JRegionFutureContractTest {
|
||||
@Test
|
||||
public void regionFutureSettlesEverySchedulerPath() throws Exception {
|
||||
String source = Files.readString(Path.of(
|
||||
"src/main/java/art/arcane/iris/util/common/scheduling/J.java"))
|
||||
.replace("\r\n", "\n");
|
||||
int start = source.indexOf("public static CompletableFuture<Void> runRegionFuture(");
|
||||
int end = source.indexOf("public static boolean runGlobal(", start);
|
||||
String method = source.substring(start, end);
|
||||
|
||||
assertTrue(method.contains("settle(future, runnable)"));
|
||||
assertTrue(method.contains("future.completeExceptionally("));
|
||||
assertTrue(method.contains("return sfut(runnable);"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user