mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-22 08:07:42 +00:00
d
This commit is contained in:
43 files changed
+1308
-279
No files matched your search
@@ -6,7 +6,10 @@ import org.junit.Test;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNotSame;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
|
||||
public class IrisSettingsDefaultsTest {
|
||||
@Test
|
||||
@@ -34,4 +37,64 @@ public class IrisSettingsDefaultsTest {
|
||||
assertTrue(settings.isAutoIngestDatapacks());
|
||||
assertFalse(settings.isAutoImportDatapackStructures());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void hotloadSnapshotIsValidatedBeforeReplacingLiveSettings() {
|
||||
IrisSettings previous = IrisSettings.settings;
|
||||
IrisSettings live = new IrisSettings();
|
||||
IrisSettings.settings = live;
|
||||
try {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> IrisSettings.installHotloadSnapshot("{\"general\":"));
|
||||
assertSame(live, IrisSettings.settings);
|
||||
|
||||
IrisSettings installed = IrisSettings.installHotloadSnapshot(
|
||||
"{\"general\":{\"language\":\"fr_FR\"},\"world\":null}");
|
||||
assertSame(installed, IrisSettings.settings);
|
||||
assertEquals("fr_FR", installed.getGeneral().getLanguage());
|
||||
assertNotNull(installed.getWorld());
|
||||
} finally {
|
||||
IrisSettings.settings = previous;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectedHotloadActivationKeepsPreviousLiveSettings() {
|
||||
IrisSettings previous = IrisSettings.settings;
|
||||
IrisSettings live = new IrisSettings();
|
||||
live.getGeneral().setLanguage("en_US");
|
||||
IrisSettings.settings = live;
|
||||
try {
|
||||
boolean applied = IrisSettings.applyHotloadSnapshot(
|
||||
"{\"general\":{\"language\":\"de_DE\"}}",
|
||||
candidate -> false
|
||||
);
|
||||
|
||||
assertFalse(applied);
|
||||
assertSame(live, IrisSettings.settings);
|
||||
assertEquals("en_US", IrisSettings.settings.getGeneral().getLanguage());
|
||||
} finally {
|
||||
IrisSettings.settings = previous;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void successfulHotloadActivationPublishesCandidateSettings() {
|
||||
IrisSettings previous = IrisSettings.settings;
|
||||
IrisSettings live = new IrisSettings();
|
||||
IrisSettings.settings = live;
|
||||
try {
|
||||
boolean applied = IrisSettings.applyHotloadSnapshot(
|
||||
"{\"general\":{\"language\":\"de_DE\"},\"world\":null}",
|
||||
candidate -> "de_DE".equals(candidate.getGeneral().getLanguage())
|
||||
);
|
||||
|
||||
assertTrue(applied);
|
||||
assertNotSame(live, IrisSettings.settings);
|
||||
assertEquals("de_DE", IrisSettings.settings.getGeneral().getLanguage());
|
||||
assertNotNull(IrisSettings.settings.getWorld());
|
||||
} finally {
|
||||
IrisSettings.settings = previous;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
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;
|
||||
|
||||
public class ProgressBossBarToggleTest {
|
||||
@Test
|
||||
public void progressBossBarsAreEnabledByDefault() {
|
||||
assertTrue(new IrisSettings.IrisSettingsGeneral().isProgressBossBar());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void showProgressLaneNeverTouchesTheLaneServiceWhenDisabled() {
|
||||
withProgressBossBar(false, () ->
|
||||
BukkitPlatform.showProgressLane(null, "iris:job", "Working", 0.5D, 4000L));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void showProgressLaneStillReachesTheLaneServiceWhenEnabled() {
|
||||
withProgressBossBar(true, () -> assertThrows(IllegalStateException.class,
|
||||
() -> BukkitPlatform.showProgressLane(null, "iris:job", "Working", 0.5D, 4000L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void studioOpenBossBarIsGatedBySettings() throws Exception {
|
||||
assertGated("core/project/StudioOpenProgressReporter.java");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void worldCreationBossBarIsGatedBySettings() throws Exception {
|
||||
assertGated("core/tools/WorldCreationProgressReporter.java");
|
||||
}
|
||||
|
||||
@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));
|
||||
}
|
||||
|
||||
private void withProgressBossBar(boolean enabled, Runnable body) {
|
||||
IrisSettings previous = IrisSettings.settings;
|
||||
try {
|
||||
IrisSettings live = new IrisSettings();
|
||||
live.getGeneral().setProgressBossBar(enabled);
|
||||
IrisSettings.settings = live;
|
||||
assertFalse("another test hosted a HUD; this test needs an unhosted platform",
|
||||
BukkitPlatform.hasHud());
|
||||
body.run();
|
||||
} finally {
|
||||
IrisSettings.settings = previous;
|
||||
}
|
||||
}
|
||||
}
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
package art.arcane.iris.core.localization;
|
||||
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import java.io.File;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.attribute.FileTime;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisLanguageHotloadSnapshotTest {
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Test
|
||||
public void capturesMissingOverrideAsStableTombstone() throws Exception {
|
||||
File override = new File(temporaryFolder.getRoot(), "languages/overrides/en_US.json");
|
||||
|
||||
LocaleHotloadSnapshot snapshot = IrisLanguage.captureHotloadSnapshot(override, "en_US");
|
||||
|
||||
assertTrue(snapshot.missing());
|
||||
assertEquals("missing", snapshot.sha256());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void detectsSameMetadataContentReplacementBySha256() throws Exception {
|
||||
File override = new File(temporaryFolder.getRoot(), "languages/overrides/en_US.json");
|
||||
Files.createDirectories(override.toPath().getParent());
|
||||
String firstContent = "{\"messages\":{\"a\":\"1\"}}";
|
||||
String secondContent = "{\"messages\":{\"a\":\"2\"}}";
|
||||
Files.writeString(override.toPath(), firstContent, StandardCharsets.UTF_8);
|
||||
FileTime fixedTime = FileTime.fromMillis(10_000L);
|
||||
Files.setLastModifiedTime(override.toPath(), fixedTime);
|
||||
LocaleHotloadSnapshot first = IrisLanguage.captureHotloadSnapshot(override, "en_US");
|
||||
|
||||
Files.writeString(override.toPath(), secondContent, StandardCharsets.UTF_8);
|
||||
Files.setLastModifiedTime(override.toPath(), fixedTime);
|
||||
LocaleHotloadSnapshot second = IrisLanguage.captureHotloadSnapshot(override, "en_US");
|
||||
|
||||
assertFalse(first.missing());
|
||||
assertFalse(second.missing());
|
||||
assertEquals(firstContent.length(), secondContent.length());
|
||||
assertEquals(first.file(), second.file());
|
||||
assertEquals(firstContent, first.content());
|
||||
assertEquals(secondContent, second.content());
|
||||
assertNotEquals(first.sha256(), second.sha256());
|
||||
assertNotEquals(first, second);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
package art.arcane.iris.core.localization;
|
||||
|
||||
import art.arcane.iris.core.IrisSettings;
|
||||
import art.arcane.volmlib.util.localization.MessageArgument;
|
||||
import art.arcane.volmlib.util.localization.LocaleOverlay;
|
||||
import art.arcane.volmlib.util.localization.LocalizationValidationResult;
|
||||
@@ -35,6 +36,7 @@ import java.util.stream.Stream;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisLanguageTest {
|
||||
@@ -237,6 +239,26 @@ public class IrisLanguageTest {
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rejectedCandidateLocaleLeavesPreviousSettingsAndLanguageActive() {
|
||||
IrisSettings previous = IrisSettings.settings;
|
||||
IrisSettings live = new IrisSettings();
|
||||
live.getGeneral().setLanguage("en_US");
|
||||
IrisSettings.settings = live;
|
||||
try {
|
||||
boolean applied = IrisSettings.applyHotloadSnapshot(
|
||||
"{\"general\":{\"language\":\"../invalid\"}}",
|
||||
IrisLanguage::reload
|
||||
);
|
||||
|
||||
assertFalse(applied);
|
||||
assertSame(live, IrisSettings.settings);
|
||||
assertEquals("en_US", IrisLanguage.activeLocale());
|
||||
} finally {
|
||||
IrisSettings.settings = previous;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void untrustedArgumentsCannotInjectLegacyOrMiniMessageFormatting() throws Exception {
|
||||
writeOverride("de_DE", """
|
||||
|
||||
@@ -0,0 +1,159 @@
|
||||
package art.arcane.iris.core.localization;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class LocaleHotloadGateTest {
|
||||
@Test
|
||||
public void anchorsCooldownAtCompletedApplication() {
|
||||
LocaleHotloadGate gate = gate();
|
||||
LocaleHotloadSnapshot initial = snapshot("initial");
|
||||
LocaleHotloadSnapshot first = snapshot("first");
|
||||
LocaleHotloadSnapshot second = snapshot("second");
|
||||
gate.reset(initial);
|
||||
|
||||
assertNull(gate.observe(first, 0L));
|
||||
LocaleHotloadGate.Attempt firstAttempt = gate.observe(first, 100L);
|
||||
assertNotNull(firstAttempt);
|
||||
gate.complete(firstAttempt, 500L, true);
|
||||
|
||||
assertNull(gate.observe(second, 600L));
|
||||
assertNull(gate.observe(second, 700L));
|
||||
assertNull(gate.observe(second, 3_499L));
|
||||
LocaleHotloadGate.Attempt secondAttempt = gate.observe(second, 3_500L);
|
||||
|
||||
assertNotNull(secondAttempt);
|
||||
assertEquals(second, secondAttempt.snapshot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void coalescesCooldownBurstToLatestStableSnapshot() {
|
||||
LocaleHotloadGate gate = gate();
|
||||
LocaleHotloadSnapshot first = snapshot("first");
|
||||
LocaleHotloadSnapshot intermediate = snapshot("intermediate");
|
||||
LocaleHotloadSnapshot latest = snapshot("latest");
|
||||
gate.reset(snapshot("initial"));
|
||||
|
||||
assertNull(gate.observe(first, 0L));
|
||||
LocaleHotloadGate.Attempt firstAttempt = gate.observe(first, 100L);
|
||||
assertNotNull(firstAttempt);
|
||||
gate.complete(firstAttempt, 200L, true);
|
||||
|
||||
assertNull(gate.observe(intermediate, 250L));
|
||||
assertNull(gate.observe(intermediate, 350L));
|
||||
assertNull(gate.observe(latest, 400L));
|
||||
assertNull(gate.observe(latest, 500L));
|
||||
assertNull(gate.observe(latest, 3_199L));
|
||||
LocaleHotloadGate.Attempt latestAttempt = gate.observe(latest, 3_200L);
|
||||
|
||||
assertNotNull(latestAttempt);
|
||||
assertEquals(latest, latestAttempt.snapshot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void requiresDeletionGraceAndCancelsTransientMissingSnapshot() {
|
||||
LocaleHotloadGate gate = gate();
|
||||
LocaleHotloadSnapshot initial = snapshot("initial");
|
||||
LocaleHotloadSnapshot replacement = snapshot("replacement");
|
||||
LocaleHotloadSnapshot missing = LocaleHotloadSnapshot.missing(initial.file(), "en_US");
|
||||
gate.reset(initial);
|
||||
|
||||
assertNull(gate.observe(missing, 0L));
|
||||
assertNull(gate.observe(missing, 499L));
|
||||
assertNull(gate.observe(replacement, 500L));
|
||||
LocaleHotloadGate.Attempt replacementAttempt = gate.observe(replacement, 600L);
|
||||
assertNotNull(replacementAttempt);
|
||||
assertEquals(replacement, replacementAttempt.snapshot());
|
||||
gate.complete(replacementAttempt, 600L, true);
|
||||
|
||||
assertNull(gate.observe(missing, 700L));
|
||||
assertNull(gate.observe(missing, 1_199L));
|
||||
assertNull(gate.observe(missing, 1_200L));
|
||||
LocaleHotloadGate.Attempt deletionAttempt = gate.observe(missing, 3_600L);
|
||||
|
||||
assertNotNull(deletionAttempt);
|
||||
assertEquals(missing, deletionAttempt.snapshot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unavailableReadBlocksStalePendingSnapshot() {
|
||||
LocaleHotloadGate gate = gate();
|
||||
LocaleHotloadSnapshot first = snapshot("first");
|
||||
LocaleHotloadSnapshot pending = snapshot("pending");
|
||||
gate.reset(snapshot("initial"));
|
||||
|
||||
assertNull(gate.observe(first, 0L));
|
||||
LocaleHotloadGate.Attempt firstAttempt = gate.observe(first, 100L);
|
||||
assertNotNull(firstAttempt);
|
||||
gate.complete(firstAttempt, 200L, true);
|
||||
assertNull(gate.observe(pending, 300L));
|
||||
assertNull(gate.observe(pending, 400L));
|
||||
|
||||
gate.unavailable();
|
||||
assertNull(gate.observe(pending, 3_200L));
|
||||
LocaleHotloadGate.Attempt recoveredAttempt = gate.observe(pending, 3_300L);
|
||||
|
||||
assertNotNull(recoveredAttempt);
|
||||
assertEquals(pending, recoveredAttempt.snapshot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void failedApplicationRetriesOnlyAfterCooldown() {
|
||||
LocaleHotloadGate gate = gate();
|
||||
LocaleHotloadSnapshot changed = snapshot("changed");
|
||||
gate.reset(snapshot("initial"));
|
||||
|
||||
assertNull(gate.observe(changed, 0L));
|
||||
LocaleHotloadGate.Attempt failedAttempt = gate.observe(changed, 100L);
|
||||
assertNotNull(failedAttempt);
|
||||
gate.complete(failedAttempt, 500L, false);
|
||||
|
||||
assertNull(gate.observe(changed, 3_499L));
|
||||
LocaleHotloadGate.Attempt retryAttempt = gate.observe(changed, 3_500L);
|
||||
|
||||
assertNotNull(retryAttempt);
|
||||
assertEquals(changed, retryAttempt.snapshot());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void manualResetInvalidatesInFlightAutomaticAttempt() {
|
||||
LocaleHotloadGate gate = gate();
|
||||
LocaleHotloadSnapshot automatic = snapshot("automatic");
|
||||
LocaleHotloadSnapshot manual = snapshot("manual");
|
||||
LocaleHotloadSnapshot changed = snapshot("changed");
|
||||
gate.reset(snapshot("initial"));
|
||||
|
||||
assertNull(gate.observe(automatic, 0L));
|
||||
LocaleHotloadGate.Attempt staleAttempt = gate.observe(automatic, 100L);
|
||||
assertNotNull(staleAttempt);
|
||||
gate.reset(manual);
|
||||
gate.complete(staleAttempt, 200L, true);
|
||||
|
||||
assertNull(gate.observe(manual, 200L));
|
||||
assertNull(gate.observe(changed, 300L));
|
||||
LocaleHotloadGate.Attempt currentAttempt = gate.observe(changed, 400L);
|
||||
|
||||
assertNotNull(currentAttempt);
|
||||
assertNotEquals(staleAttempt.generation(), currentAttempt.generation());
|
||||
assertEquals(changed, currentAttempt.snapshot());
|
||||
}
|
||||
|
||||
private LocaleHotloadGate gate() {
|
||||
return new LocaleHotloadGate(new LocaleHotloadGate.Timing(100L, 500L, 3_000L));
|
||||
}
|
||||
|
||||
private LocaleHotloadSnapshot snapshot(String content) {
|
||||
return LocaleHotloadSnapshot.present(
|
||||
new File("locale.json"),
|
||||
"en_US",
|
||||
content,
|
||||
content
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package art.arcane.iris.core.service;
|
||||
|
||||
import art.arcane.iris.core.pack.BrokenPackException;
|
||||
import art.arcane.iris.core.pack.PackValidationResult;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class StudioSVCPackAdmissionTest {
|
||||
@Test
|
||||
public void loadablePackIsAdmitted() {
|
||||
PackValidationResult validation = new PackValidationResult(
|
||||
"overworld", List.of(), List.of(), 1L);
|
||||
|
||||
assertNull(StudioSVC.resolvePackAdmissionFailure(
|
||||
"overworld", Optional.empty(), validation));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void missingValidationFailsClosedWithTheResolvedPackName() {
|
||||
BrokenPackException failure = StudioSVC.resolvePackAdmissionFailure(
|
||||
"overworld-pack", Optional.empty(), null);
|
||||
|
||||
assertEquals("overworld-pack", failure.getPackName());
|
||||
assertEquals(List.of(
|
||||
"Required pack validation has not completed. Studio creation fails closed until validation succeeds."),
|
||||
failure.getReasons());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockingValidationPreservesEveryReasonInOrder() {
|
||||
List<String> reasons = List.of(
|
||||
"Biome 'broken' has no resolvable regions.",
|
||||
"Structure 'castle' references missing pool 'castle/start'.");
|
||||
PackValidationResult validation = new PackValidationResult(
|
||||
"overworld", reasons, List.of(), 1L);
|
||||
|
||||
BrokenPackException failure = StudioSVC.resolvePackAdmissionFailure(
|
||||
"overworld", Optional.empty(), validation);
|
||||
|
||||
assertEquals("overworld", failure.getPackName());
|
||||
assertEquals(reasons, failure.getReasons());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void startupDenialTakesPriorityOverCachedPackValidation() {
|
||||
String denial = "Restart the server after changing external datapacks.";
|
||||
PackValidationResult validation = new PackValidationResult(
|
||||
"overworld", List.of(), List.of(), 1L);
|
||||
|
||||
BrokenPackException failure = StudioSVC.resolvePackAdmissionFailure(
|
||||
"overworld", Optional.of(denial), validation);
|
||||
|
||||
assertEquals(List.of(denial), failure.getReasons());
|
||||
}
|
||||
}
|
||||
@@ -112,9 +112,9 @@ public class VillageImporterBundleTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nativeMaximumDistanceControlsIrisAssemblyRadius() {
|
||||
public void registeredJigsawMetadataIncludesAncientCitySourceAndAssemblyContract() {
|
||||
Map<String, Object> structure = VillageImporter.structureJson(
|
||||
"minecraft:village_plains",
|
||||
"minecraft:ancient_city",
|
||||
"village/pool/minecraft/start",
|
||||
6,
|
||||
81
|
||||
@@ -122,8 +122,10 @@ public class VillageImporterBundleTest {
|
||||
|
||||
assertEquals(6, structure.get("maxDepth"));
|
||||
assertEquals(6, structure.get("maxSizeChunks"));
|
||||
assertEquals("STRUCTURE_PIECE", structure.get("placeMode"));
|
||||
assertEquals(IrisJigsawBranchFailurePolicy.TERMINATE_BRANCH.name(),
|
||||
structure.get("branchFailurePolicy"));
|
||||
assertEquals("minecraft:ancient_city", structure.get("vanillaSource"));
|
||||
}
|
||||
|
||||
private StructureResourceBundle bundle(String objectContent) {
|
||||
|
||||
Reference in new issue
Block a user