mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-12 03:24:14 +00:00
Cover untested contract surfaces
- Add a test source set to spi with bind, service registry and platform value contracts - Prove the DataType probe contract, the ExternalDataProvider defaults and the string-only provider namespace claims - Prove Identifier parsing and rendering - Cover the data version, primitive and vector director handlers - Cover the vanilla export identifier, block state, orientation, request and settings values - Cover the structure adoption limits, diagnostics and result invariants - Cover the pregen mantle backpressure, heap reclaim urgency, empty cache, listener defaults and diagnostic descriptions
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
package art.arcane.iris.core.link;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class IdentifierTest {
|
||||
@Test
|
||||
public void abareKeyIsQualifiedIntoTheMinecraftNamespace() {
|
||||
Identifier identifier = Identifier.fromString("stone");
|
||||
|
||||
assertEquals("minecraft", identifier.namespace());
|
||||
assertEquals("stone", identifier.key());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aQualifiedKeyKeepsBothHalves() {
|
||||
Identifier identifier = Identifier.fromString("oraxen:ruby_ore");
|
||||
|
||||
assertEquals("oraxen", identifier.namespace());
|
||||
assertEquals("ruby_ore", identifier.key());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyTheFirstSeparatorSplitsTheIdentifier() {
|
||||
Identifier identifier = Identifier.fromString("itemsadder:rocks/ruby:ore");
|
||||
|
||||
assertEquals("itemsadder", identifier.namespace());
|
||||
assertEquals("rocks/ruby:ore", identifier.key());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aLeadingSeparatorProducesAnEmptyNamespaceRatherThanADefault() {
|
||||
Identifier identifier = Identifier.fromString(":ruby_ore");
|
||||
|
||||
assertEquals("", identifier.namespace());
|
||||
assertEquals("ruby_ore", identifier.key());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theRenderedFormRoundTripsThroughTheParser() {
|
||||
Identifier identifier = new Identifier("nexo", "village/lamp");
|
||||
|
||||
assertEquals("nexo:village/lamp", identifier.toString());
|
||||
assertEquals(identifier, Identifier.fromString(identifier.toString()));
|
||||
assertEquals(Identifier.fromString("stone"), Identifier.fromString("minecraft:stone"));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
package art.arcane.iris.core.link.data;
|
||||
|
||||
import art.arcane.iris.core.link.ExternalDataProvider;
|
||||
import art.arcane.iris.core.link.Identifier;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.MissingResourceException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class DataTypeProbeContractTest {
|
||||
private static final Identifier RUBY = new Identifier("testplugin", "ruby_ore");
|
||||
|
||||
@Test
|
||||
public void anUnclaimedIdentifierIsRejectedWithoutProbingTheProvider() {
|
||||
RecordingProvider provider = new RecordingProvider(false, true, true);
|
||||
|
||||
assertFalse(DataType.BLOCK.test(provider, RUBY));
|
||||
assertFalse(DataType.ITEM.test(provider, RUBY));
|
||||
assertFalse(DataType.ENTITY.test(provider, RUBY));
|
||||
assertEquals(List.of(), provider.probes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aClaimedBlockIsProbedThroughTheStatelessBlockLookup() {
|
||||
RecordingProvider provider = new RecordingProvider(true, true, true);
|
||||
|
||||
assertTrue(DataType.BLOCK.test(provider, RUBY));
|
||||
assertEquals(List.of("block"), provider.probes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aClaimedItemIsProbedThroughTheStatelessItemLookup() {
|
||||
RecordingProvider provider = new RecordingProvider(true, true, true);
|
||||
|
||||
assertTrue(DataType.ITEM.test(provider, RUBY));
|
||||
assertEquals(List.of("item"), provider.probes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aClaimedEntityIsAcceptedOnTheClaimAlone() {
|
||||
RecordingProvider provider = new RecordingProvider(true, false, false);
|
||||
|
||||
assertTrue(DataType.ENTITY.test(provider, RUBY));
|
||||
assertEquals(List.of(), provider.probes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aMissingResourceDowngradesTheClaimInsteadOfFailing() {
|
||||
RecordingProvider provider = new RecordingProvider(true, false, false);
|
||||
|
||||
assertFalse(DataType.BLOCK.test(provider, RUBY));
|
||||
assertFalse(DataType.ITEM.test(provider, RUBY));
|
||||
assertEquals(List.of("block", "item"), provider.probes);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnexpectedProviderFailureIsNotSwallowed() {
|
||||
ExplodingProvider provider = new ExplodingProvider();
|
||||
|
||||
assertThrows(IllegalStateException.class, () -> DataType.BLOCK.test(provider, RUBY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thePredicateFormFiltersEveryElementThroughTheSameProvider() {
|
||||
RecordingProvider provider = new RecordingProvider(true, true, true);
|
||||
List<Identifier> claimed = List.of(RUBY, new Identifier("other", "quartz"));
|
||||
|
||||
List<Identifier> filtered = claimed.stream().filter(DataType.BLOCK.asPredicate(provider)).toList();
|
||||
|
||||
assertEquals(claimed, filtered);
|
||||
assertEquals(List.of("block", "block"), provider.probes);
|
||||
}
|
||||
|
||||
private static final class RecordingProvider extends ExternalDataProvider {
|
||||
private final List<String> probes = new ArrayList<>();
|
||||
private final boolean claims;
|
||||
private final boolean resolvesBlocks;
|
||||
private final boolean resolvesItems;
|
||||
|
||||
private RecordingProvider(boolean claims, boolean resolvesBlocks, boolean resolvesItems) {
|
||||
super("TestPlugin");
|
||||
this.claims = claims;
|
||||
this.resolvesBlocks = resolvesBlocks;
|
||||
this.resolvesItems = resolvesItems;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap<String, String> state) {
|
||||
probes.add("block");
|
||||
if (!resolvesBlocks) {
|
||||
throw new MissingResourceException("no block", blockId.namespace(), blockId.key());
|
||||
}
|
||||
return mock(BlockData.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap<String, Object> customNbt) {
|
||||
probes.add("item");
|
||||
if (!resolvesItems) {
|
||||
throw new MissingResourceException("no item", itemId.namespace(), itemId.key());
|
||||
}
|
||||
return mock(ItemStack.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidProvider(@NotNull Identifier id, DataType dataType) {
|
||||
return claims;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ExplodingProvider extends ExternalDataProvider {
|
||||
private ExplodingProvider() {
|
||||
super("TestPlugin");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap<String, String> state) {
|
||||
throw new IllegalStateException("the provider backend is broken");
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidProvider(@NotNull Identifier id, DataType dataType) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
+110
@@ -0,0 +1,110 @@
|
||||
package art.arcane.iris.core.link.data;
|
||||
|
||||
import art.arcane.iris.core.link.ExternalDataProvider;
|
||||
import art.arcane.iris.core.link.Identifier;
|
||||
import art.arcane.volmlib.util.collection.KMap;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.block.data.BlockData;
|
||||
import org.bukkit.inventory.ItemStack;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.MissingResourceException;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class ExternalDataProviderDefaultsTest {
|
||||
private static final Identifier RUBY = new Identifier("testplugin", "ruby_ore");
|
||||
|
||||
@Test
|
||||
public void theProviderRemembersThePluginItSpeaksFor() {
|
||||
assertEquals("TestPlugin", new StatelessProvider().getPluginId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aProviderWithoutAPluginIdIsRejected() {
|
||||
assertThrows(NullPointerException.class, () -> new StatelessProvider(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnimplementedProviderRefusesEveryResourceKind() {
|
||||
StatelessProvider provider = new StatelessProvider();
|
||||
|
||||
MissingResourceException blockFailure =
|
||||
assertThrows(MissingResourceException.class, () -> provider.getBlockData(RUBY));
|
||||
MissingResourceException itemFailure =
|
||||
assertThrows(MissingResourceException.class, () -> provider.getItemStack(RUBY));
|
||||
|
||||
assertEquals("testplugin", blockFailure.getClassName());
|
||||
assertEquals("ruby_ore", blockFailure.getKey());
|
||||
assertEquals("testplugin", itemFailure.getClassName());
|
||||
assertEquals("ruby_ore", itemFailure.getKey());
|
||||
assertThrows(MissingResourceException.class, () -> provider.spawnMob(mock(Location.class), RUBY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnimplementedProviderExposesNoBlockProperties() {
|
||||
assertTrue(new StatelessProvider().getBlockProperties(RUBY).isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theStatelessLookupsDelegateWithAnEmptyStateMap() {
|
||||
DelegationProvider provider = new DelegationProvider();
|
||||
|
||||
provider.getBlockData(RUBY);
|
||||
provider.getItemStack(RUBY);
|
||||
|
||||
assertEquals(List.of("block:0", "item:0"), provider.calls);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void deferredPlacementIsANoOpUntilAProviderImplementsIt() {
|
||||
new StatelessProvider().processUpdate(null, null, RUBY);
|
||||
}
|
||||
|
||||
private static class StatelessProvider extends ExternalDataProvider {
|
||||
private StatelessProvider() {
|
||||
super("TestPlugin");
|
||||
}
|
||||
|
||||
private StatelessProvider(String pluginId) {
|
||||
super(pluginId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull Collection<@NotNull Identifier> getTypes(@NotNull DataType dataType) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isValidProvider(@NotNull Identifier id, DataType dataType) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class DelegationProvider extends StatelessProvider {
|
||||
private final List<String> calls = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public @NotNull BlockData getBlockData(@NotNull Identifier blockId, @NotNull KMap<String, String> state) {
|
||||
calls.add("block:" + state.size());
|
||||
return mock(BlockData.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NotNull ItemStack getItemStack(@NotNull Identifier itemId, @NotNull KMap<String, Object> customNbt) {
|
||||
calls.add("item:" + customNbt.size());
|
||||
return mock(ItemStack.class);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
package art.arcane.iris.core.link.data;
|
||||
|
||||
import art.arcane.iris.core.link.Identifier;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class ProviderNamespaceClaimTest {
|
||||
@Test
|
||||
public void ecoItemsClaimsOnlyItemsInItsOwnNamespace() {
|
||||
EcoItemsDataProvider provider = new EcoItemsDataProvider();
|
||||
|
||||
assertTrue(provider.isValidProvider(new Identifier("ecoitems", "ruby"), DataType.ITEM));
|
||||
assertTrue(provider.isValidProvider(new Identifier("EcoItems", "ruby"), DataType.ITEM));
|
||||
assertFalse(provider.isValidProvider(new Identifier("ecoitems", "ruby"), DataType.BLOCK));
|
||||
assertFalse(provider.isValidProvider(new Identifier("ecoitems", "ruby"), DataType.ENTITY));
|
||||
assertFalse(provider.isValidProvider(new Identifier("oraxen", "ruby"), DataType.ITEM));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void executableItemsClaimsOnlyItemsInItsOwnNamespace() {
|
||||
ExecutableItemsDataProvider provider = new ExecutableItemsDataProvider();
|
||||
|
||||
assertTrue(provider.isValidProvider(new Identifier("executable_items", "wand"), DataType.ITEM));
|
||||
assertFalse(provider.isValidProvider(new Identifier("executable_items", "wand"), DataType.BLOCK));
|
||||
assertFalse(provider.isValidProvider(new Identifier("executableitems", "wand"), DataType.ITEM));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void kgeneratorsClaimsItemsWithoutConsultingTheGeneratorRegistry() {
|
||||
KGeneratorsDataProvider provider = new KGeneratorsDataProvider();
|
||||
|
||||
assertTrue(provider.isValidProvider(new Identifier("kgenerators", "cobble"), DataType.ITEM));
|
||||
assertFalse(provider.isValidProvider(new Identifier("kgenerators", "cobble"), DataType.ENTITY));
|
||||
assertFalse(provider.isValidProvider(new Identifier("oraxen", "cobble"), DataType.ITEM));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mmoItemsEncodesItsItemTypeInTheNamespace() {
|
||||
MMOItemsDataProvider provider = new MMOItemsDataProvider();
|
||||
|
||||
assertTrue(provider.isValidProvider(new Identifier("mmoitems_sword", "excalibur"), DataType.ITEM));
|
||||
assertFalse(provider.isValidProvider(new Identifier("mmoitems", "excalibur"), DataType.ITEM));
|
||||
assertFalse(provider.isValidProvider(new Identifier("mmoitems_sword", "excalibur"), DataType.ENTITY));
|
||||
assertFalse(provider.isValidProvider(new Identifier("MMOITEMS", "1"), DataType.BLOCK));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void mythicCrucibleClaimsItemsWithoutConsultingItsItemManager() {
|
||||
MythicCrucibleDataProvider provider = new MythicCrucibleDataProvider();
|
||||
|
||||
assertTrue(provider.isValidProvider(new Identifier("crucible", "ruby"), DataType.ITEM));
|
||||
assertTrue(provider.isValidProvider(new Identifier("Crucible", "ruby"), DataType.ITEM));
|
||||
assertFalse(provider.isValidProvider(new Identifier("crucible", "ruby"), DataType.ENTITY));
|
||||
assertFalse(provider.isValidProvider(new Identifier("oraxen", "ruby"), DataType.ITEM));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oraxenRefusesForeignNamespacesBeforeTouchingItsApi() {
|
||||
OraxenDataProvider provider = new OraxenDataProvider();
|
||||
|
||||
assertFalse(provider.isValidProvider(new Identifier("nexo", "ruby_ore"), DataType.BLOCK));
|
||||
assertFalse(provider.isValidProvider(new Identifier("nexo", "ruby_ore"), DataType.ITEM));
|
||||
assertEquals(List.of(), provider.getTypes(DataType.ENTITY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nexoRefusesForeignNamespacesBeforeTouchingItsApi() {
|
||||
NexoDataProvider provider = new NexoDataProvider();
|
||||
|
||||
assertFalse(provider.isValidProvider(new Identifier("oraxen", "lamp"), DataType.BLOCK));
|
||||
assertFalse(provider.isValidProvider(new Identifier("oraxen", "lamp"), DataType.ITEM));
|
||||
assertEquals(List.of(), provider.getTypes(DataType.ENTITY));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void itemsAdderClaimsNothingBeforeItsRegistryHasLoaded() {
|
||||
ItemAdderDataProvider provider = new ItemAdderDataProvider();
|
||||
|
||||
assertFalse(provider.isValidProvider(new Identifier("rocks", "ruby_ore"), DataType.ITEM));
|
||||
assertFalse(provider.isValidProvider(new Identifier("rocks", "ruby_ore"), DataType.BLOCK));
|
||||
assertFalse(provider.isValidProvider(new Identifier("rocks", "ruby_ore"), DataType.ENTITY));
|
||||
assertTrue(provider.getTypes(DataType.ITEM).isEmpty());
|
||||
assertTrue(provider.getTypes(DataType.BLOCK).isEmpty());
|
||||
}
|
||||
}
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
package art.arcane.iris.core.pregenerator;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class MantleHeapPressureUrgencyTest {
|
||||
@Test
|
||||
public void headroomBelowTheLowWaterMarkAsksForNothing() {
|
||||
assertEquals(0D, MantleHeapPressure.reclaimUrgency(0D), 0D);
|
||||
assertEquals(0D, MantleHeapPressure.reclaimUrgency(0.5D), 0D);
|
||||
assertEquals(0D, MantleHeapPressure.reclaimUrgency(0.82D), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pressureAtOrAboveTheHighWaterMarkIsFullyUrgent() {
|
||||
assertEquals(1D, MantleHeapPressure.reclaimUrgency(0.92D), 0D);
|
||||
assertEquals(1D, MantleHeapPressure.reclaimUrgency(0.99D), 0D);
|
||||
assertEquals(1D, MantleHeapPressure.reclaimUrgency(4D), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pressureBetweenTheMarksRampsLinearly() {
|
||||
assertEquals(0.25D, MantleHeapPressure.reclaimUrgency(0.845D), 1.0E-9D);
|
||||
assertEquals(0.5D, MantleHeapPressure.reclaimUrgency(0.87D), 1.0E-9D);
|
||||
assertEquals(0.75D, MantleHeapPressure.reclaimUrgency(0.895D), 1.0E-9D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void urgencyNeverDecreasesAsPressureRises() {
|
||||
double previous = -1D;
|
||||
|
||||
for (int step = 0; step <= 100; step++) {
|
||||
double urgency = MantleHeapPressure.reclaimUrgency(step / 100D);
|
||||
assertTrue("urgency dropped at " + step, urgency >= previous);
|
||||
previous = urgency;
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unmeasurableHeapPressureAsksForNothing() {
|
||||
assertEquals(0D, MantleHeapPressure.reclaimUrgency(Double.NaN), 0D);
|
||||
assertEquals(0D, MantleHeapPressure.reclaimUrgency(Double.POSITIVE_INFINITY), 0D);
|
||||
assertEquals(0D, MantleHeapPressure.reclaimUrgency(Double.NEGATIVE_INFINITY), 0D);
|
||||
}
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
package art.arcane.iris.core.pregenerator;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class PregenDiagnosticsDescriptionTest {
|
||||
@Test
|
||||
public void anUnnamedProbeStillReadsAsAProbe() {
|
||||
assertEquals("unnamed", PregenDiagnostics.describeProbe(null));
|
||||
assertEquals("unnamed", PregenDiagnostics.describeProbe(""));
|
||||
assertEquals("unnamed", PregenDiagnostics.describeProbe(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aNamedProbeIsReportedVerbatim() {
|
||||
assertEquals("mantle handle for backpressure",
|
||||
PregenDiagnostics.describeProbe("mantle handle for backpressure"));
|
||||
assertEquals("%s throughput", PregenDiagnostics.describeProbe("%s throughput"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anAbsentThrowableIsCalledOutInsteadOfPrintingNull() {
|
||||
assertEquals("no throwable reported", PregenDiagnostics.describeFailure(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aThrowableWithoutAMessageIsIdentifiedByItsType() {
|
||||
assertEquals("java.lang.IllegalStateException",
|
||||
PregenDiagnostics.describeFailure(new IllegalStateException()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aThrowableWithAMessageCarriesBothTypeAndMessage() {
|
||||
assertEquals("java.lang.IllegalStateException: mantle is gone",
|
||||
PregenDiagnostics.describeFailure(new IllegalStateException("mantle is gone")));
|
||||
assertEquals("java.lang.RuntimeException: ",
|
||||
PregenDiagnostics.describeFailure(new RuntimeException("")));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package art.arcane.iris.core.pregenerator;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PregenListenerDefaultsTest {
|
||||
@Test
|
||||
public void anUnqualifiedChunkIsReportedAsFreshlyGenerated() {
|
||||
RecordingListener listener = new RecordingListener();
|
||||
|
||||
listener.onChunkGenerated(4, -9);
|
||||
|
||||
assertEquals(List.of("4/-9/false"), listener.generated);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anExplicitCacheFlagIsPassedThroughUnchanged() {
|
||||
RecordingListener listener = new RecordingListener();
|
||||
|
||||
listener.onChunkGenerated(1, 2, true);
|
||||
listener.onChunkGenerated(3, 4, false);
|
||||
|
||||
assertEquals(List.of("1/2/true", "3/4/false"), listener.generated);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aFailedChunkIsIgnoredByListenersThatDoNotCare() {
|
||||
RecordingListener listener = new RecordingListener();
|
||||
|
||||
listener.onChunkFailed(7, 7);
|
||||
|
||||
assertTrue(listener.generated.isEmpty());
|
||||
}
|
||||
|
||||
private static final class RecordingListener implements PregenListener {
|
||||
private final List<String> generated = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public void onTick(double chunksPerSecond, double chunksPerMinute, double regionsPerMinute, double percent, long generatedChunks, long totalChunks, long chunksRemaining, long eta, long elapsed, String method, boolean cached) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkGenerating(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkGenerated(int x, int z, boolean cached) {
|
||||
generated.add(x + "/" + z + "/" + cached);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegionGenerated(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegionGenerating(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkCleaned(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRegionSkipped(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkStarted(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkFailed(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkReclaim(int revert) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkGeneratedChunk(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onNetworkDownloaded(int x, int z) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClose() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSaving() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onChunkExistsInRegionGen(int x, int z) {
|
||||
}
|
||||
}
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
package art.arcane.iris.core.pregenerator;
|
||||
|
||||
import art.arcane.iris.testsupport.Await;
|
||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.mockito.Mockito.doThrow;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.never;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class PregenMantleBackpressureTest {
|
||||
@Test
|
||||
public void aDisabledPlateCapNeverAsksForAMantle() {
|
||||
AtomicInteger supplierCalls = new AtomicInteger();
|
||||
Supplier<Mantle> supplier = () -> {
|
||||
supplierCalls.incrementAndGet();
|
||||
return null;
|
||||
};
|
||||
|
||||
backpressure(supplier, 0, 1_000L, () -> {
|
||||
}).enforceMantleBudget();
|
||||
|
||||
assertEquals(0, supplierCalls.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aFailingMantleProbeDoesNotPropagate() {
|
||||
AtomicInteger timeouts = new AtomicInteger();
|
||||
|
||||
backpressure(() -> {
|
||||
throw new IllegalStateException("no mantle here");
|
||||
}, 4, 1_000L, timeouts::incrementAndGet).enforceMantleBudget();
|
||||
|
||||
assertEquals(0, timeouts.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void residencyAtTwiceThePlateCapIsNotTrimmed() {
|
||||
Mantle mantle = mock(Mantle.class);
|
||||
when(mantle.getLoadedRegionCount()).thenReturn(8);
|
||||
|
||||
backpressure(() -> mantle, 4, 1_000L, () -> {
|
||||
}).enforceMantleBudget();
|
||||
|
||||
verify(mantle, never()).trim(0L, 0);
|
||||
verify(mantle, never()).unloadTectonicPlate(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void residencyOverTheHardCapIsTrimmedUntilItFits() {
|
||||
Mantle mantle = mock(Mantle.class);
|
||||
when(mantle.getLoadedRegionCount()).thenReturn(20, 20, 3);
|
||||
|
||||
backpressure(() -> mantle, 4, 1_000L, () -> {
|
||||
}).enforceMantleBudget();
|
||||
|
||||
verify(mantle, times(1)).trim(0L, 0);
|
||||
verify(mantle, times(1)).unloadTectonicPlate(0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aTrimFailureEndsTheWaitInsteadOfSpinning() {
|
||||
Mantle mantle = mock(Mantle.class);
|
||||
when(mantle.getLoadedRegionCount()).thenReturn(20);
|
||||
doThrow(new IllegalStateException("mantle is gone")).when(mantle).trim(0L, 0);
|
||||
AtomicInteger timeouts = new AtomicInteger();
|
||||
|
||||
backpressure(() -> mantle, 4, 1_000L, timeouts::incrementAndGet).enforceMantleBudget();
|
||||
|
||||
verify(mantle, never()).unloadTectonicPlate(0);
|
||||
assertEquals(0, timeouts.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anExhaustedBudgetReportsTheTimeoutOnceAndProceeds() {
|
||||
Mantle mantle = mock(Mantle.class);
|
||||
when(mantle.getLoadedRegionCount()).thenReturn(20);
|
||||
AtomicInteger timeouts = new AtomicInteger();
|
||||
|
||||
backpressure(() -> mantle, 4, 0L, timeouts::incrementAndGet).enforceMantleBudget();
|
||||
|
||||
assertEquals(1, timeouts.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cancellationStopsTheWaitWithoutReportingATimeout() {
|
||||
Mantle mantle = mock(Mantle.class);
|
||||
when(mantle.getLoadedRegionCount()).thenReturn(20);
|
||||
AtomicInteger timeouts = new AtomicInteger();
|
||||
PregenMantleBackpressure backpressure = new PregenMantleBackpressure(
|
||||
() -> mantle, 4, 5, 0L, timeouts::incrementAndGet, () -> "cancelled", () -> true);
|
||||
|
||||
backpressure.enforceMantleBudget();
|
||||
|
||||
verify(mantle, never()).trim(0L, 0);
|
||||
verify(mantle, never()).unloadTectonicPlate(0);
|
||||
assertEquals(0, timeouts.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aFailingCancellationProbeIsTreatedAsRunning() {
|
||||
Mantle mantle = mock(Mantle.class);
|
||||
when(mantle.getLoadedRegionCount()).thenReturn(20);
|
||||
AtomicInteger timeouts = new AtomicInteger();
|
||||
PregenMantleBackpressure backpressure = new PregenMantleBackpressure(
|
||||
() -> mantle, 4, 5, 0L, timeouts::incrementAndGet, () -> "probing", () -> {
|
||||
throw new IllegalStateException("cancellation probe exploded");
|
||||
});
|
||||
|
||||
backpressure.enforceMantleBudget();
|
||||
|
||||
assertEquals(1, timeouts.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void signalledProgressReleasesTheWaitAheadOfThePollInterval() throws Exception {
|
||||
Mantle mantle = mock(Mantle.class);
|
||||
when(mantle.getLoadedRegionCount()).thenReturn(20, 20, 20, 3);
|
||||
PregenMantleBackpressure backpressure = new PregenMantleBackpressure(
|
||||
() -> mantle, 4, 600_000, 600_000L, () -> {
|
||||
}, () -> "waiting");
|
||||
Thread waiter = new Thread(backpressure::enforceMantleBudget, "pregen-backpressure-waiter");
|
||||
|
||||
waiter.start();
|
||||
Await.until("the parked backpressure wait to be released", Duration.ofSeconds(10L), () -> {
|
||||
backpressure.signalProgress();
|
||||
return !waiter.isAlive();
|
||||
});
|
||||
waiter.join(1_000L);
|
||||
|
||||
assertFalse(waiter.isAlive());
|
||||
}
|
||||
|
||||
private static PregenMantleBackpressure backpressure(
|
||||
Supplier<Mantle> mantleSupplier,
|
||||
int maxResidentTectonicPlates,
|
||||
long timeoutMs,
|
||||
Runnable onBudgetTimeout
|
||||
) {
|
||||
return new PregenMantleBackpressure(
|
||||
mantleSupplier, maxResidentTectonicPlates, 5, timeoutMs, onBudgetTimeout, () -> "test");
|
||||
}
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package art.arcane.iris.core.pregenerator.cache;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
||||
public class PregenCacheDefaultsTest {
|
||||
@Test
|
||||
public void anAbsentDirectoryResolvesToTheSharedEmptyCache() {
|
||||
assertSame(PregenCache.EMPTY, PregenCache.create(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theEmptyCacheNeverClaimsAChunkItWasToldToCache() {
|
||||
PregenCache.EMPTY.cacheChunk(3, -7);
|
||||
PregenCache.EMPTY.cacheChunk(0, 0);
|
||||
|
||||
assertFalse(PregenCache.EMPTY.isChunkCached(3, -7));
|
||||
assertFalse(PregenCache.EMPTY.isChunkCached(0, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theEmptyCacheNeverClaimsARegionItWasToldToCache() {
|
||||
PregenCache.EMPTY.cacheRegion(-2, 5);
|
||||
|
||||
assertFalse(PregenCache.EMPTY.isRegionCached(-2, 5));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theEmptyCacheSurvivesMaintenanceCalls() {
|
||||
PregenCache.EMPTY.write();
|
||||
PregenCache.EMPTY.trim(0L);
|
||||
PregenCache.EMPTY.trim(60_000L);
|
||||
|
||||
assertFalse(PregenCache.EMPTY.isChunkCached(0, 0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aCacheIsItsOwnSynchronousView() {
|
||||
assertSame(PregenCache.EMPTY, PregenCache.EMPTY.sync());
|
||||
}
|
||||
}
|
||||
+187
@@ -0,0 +1,187 @@
|
||||
package art.arcane.iris.core.structure.conversion;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.time.Duration;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisStructureAdoptionValueTest {
|
||||
@Test
|
||||
public void theDefaultLimitsAreTheOnesTheServiceShipsWith() {
|
||||
IrisStructureAdoptionLimits limits = IrisStructureAdoptionLimits.defaults();
|
||||
|
||||
assertEquals(10_000, limits.maxResources());
|
||||
assertEquals(8 * 1024 * 1024, limits.maxJsonBytes());
|
||||
assertEquals(64 * 1024 * 1024, limits.maxBinaryBytes());
|
||||
assertEquals(1024L * 1024L * 1024L, limits.maxTotalBytes());
|
||||
assertEquals(10_000, limits.maxStructuresScanned());
|
||||
assertEquals(1_000, limits.maxDiagnostics());
|
||||
assertEquals(1_024, limits.maxActivePlans());
|
||||
assertEquals(Duration.ofMinutes(15L), limits.planTtl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void everyLimitRejectsBothEndsOfItsRange() {
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(0, 1, 1, 1L, 1, 1, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(100_001, 1, 1, 1L, 1, 1, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 0, 1, 1L, 1, 1, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 67_108_865, 1, 1L, 1, 1, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 0, 1L, 1, 1, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 268_435_457, 1L, 1, 1, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 1, 0L, 1, 1, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 1, 1_073_741_825L, 1, 1, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 1, 1L, 0, 1, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 1, 1L, 1, 0, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 1, 1L, 1, 10_001, 1, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 1, 1L, 1, 1, 0, Duration.ofMinutes(1L)));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 1, 1L, 1, 1, 10_001, Duration.ofMinutes(1L)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void thePlanTtlMustBePositiveAndWithinADay() {
|
||||
assertThrows(NullPointerException.class, () -> limits(1, 1, 1, 1L, 1, 1, 1, null));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 1, 1L, 1, 1, 1, Duration.ZERO));
|
||||
assertThrows(IllegalArgumentException.class, () -> limits(1, 1, 1, 1L, 1, 1, 1, Duration.ofSeconds(-1L)));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> limits(1, 1, 1, 1L, 1, 1, 1, Duration.ofHours(24L).plusNanos(1L)));
|
||||
|
||||
assertEquals(Duration.ofHours(24L), limits(1, 1, 1, 1L, 1, 1, 1, Duration.ofHours(24L)).planTtl());
|
||||
assertEquals(Duration.ofNanos(1L), limits(1, 1, 1, 1L, 1, 1, 1, Duration.ofNanos(1L)).planTtl());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aDiagnosticRendersItsSeverityCodeResourceAndAdvice() {
|
||||
IrisStructureAdoptionDiagnostic diagnostic = new IrisStructureAdoptionDiagnostic(
|
||||
IrisStructureAdoptionDiagnostic.Severity.ERROR,
|
||||
IrisStructureAdoptionDiagnostic.Code.TARGET_RESOURCE_EXISTS,
|
||||
" structures/village.json ",
|
||||
"the target already exists",
|
||||
" pick another target ");
|
||||
|
||||
assertEquals("structures/village.json", diagnostic.resource());
|
||||
assertEquals("ERROR TARGET_RESOURCE_EXISTS [structures/village.json]: "
|
||||
+ "the target already exists pick another target", diagnostic.summary());
|
||||
assertTrue(diagnostic.blocking());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aDiagnosticWithoutAResourceOrAdviceStillReadsCleanly() {
|
||||
IrisStructureAdoptionDiagnostic diagnostic = new IrisStructureAdoptionDiagnostic(
|
||||
IrisStructureAdoptionDiagnostic.Severity.INFO,
|
||||
IrisStructureAdoptionDiagnostic.Code.IN_PLACE_AVAILABLE,
|
||||
null,
|
||||
"the source can be claimed in place",
|
||||
null);
|
||||
|
||||
assertEquals("INFO IN_PLACE_AVAILABLE: the source can be claimed in place", diagnostic.summary());
|
||||
assertFalse(diagnostic.blocking());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aDiagnosticWithoutADetailIsRejected() {
|
||||
IllegalArgumentException failure = assertThrows(IllegalArgumentException.class,
|
||||
() -> new IrisStructureAdoptionDiagnostic(
|
||||
IrisStructureAdoptionDiagnostic.Severity.WARNING,
|
||||
IrisStructureAdoptionDiagnostic.Code.SHARED_DEPENDENCY,
|
||||
"structures/village.json",
|
||||
" ",
|
||||
null));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("detail cannot be blank"));
|
||||
assertThrows(NullPointerException.class, () -> new IrisStructureAdoptionDiagnostic(
|
||||
null,
|
||||
IrisStructureAdoptionDiagnostic.Code.SHARED_DEPENDENCY,
|
||||
null,
|
||||
"detail",
|
||||
null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void diagnosticsSortErrorsAheadOfWarningsAndInfo() {
|
||||
IrisStructureAdoptionDiagnostic error = diagnostic(
|
||||
IrisStructureAdoptionDiagnostic.Severity.ERROR,
|
||||
IrisStructureAdoptionDiagnostic.Code.PLAN_BLOCKED, "b", "second");
|
||||
IrisStructureAdoptionDiagnostic warning = diagnostic(
|
||||
IrisStructureAdoptionDiagnostic.Severity.WARNING,
|
||||
IrisStructureAdoptionDiagnostic.Code.SHARED_DEPENDENCY, "a", "first");
|
||||
IrisStructureAdoptionDiagnostic sameSeverityEarlierCode = diagnostic(
|
||||
IrisStructureAdoptionDiagnostic.Severity.ERROR,
|
||||
IrisStructureAdoptionDiagnostic.Code.SOURCE_GRAPH_INVALID, "z", "third");
|
||||
|
||||
assertTrue(error.compareTo(warning) < 0);
|
||||
assertTrue(sameSeverityEarlierCode.compareTo(error) < 0);
|
||||
assertTrue(diagnostic(IrisStructureAdoptionDiagnostic.Severity.ERROR,
|
||||
IrisStructureAdoptionDiagnostic.Code.PLAN_BLOCKED, "a", "x")
|
||||
.compareTo(error) < 0);
|
||||
assertEquals(0, error.compareTo(diagnostic(
|
||||
IrisStructureAdoptionDiagnostic.Severity.ERROR,
|
||||
IrisStructureAdoptionDiagnostic.Code.PLAN_BLOCKED, "b", "second")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnappliedResultCarriesItsDiagnosticsAndNoReceipt() {
|
||||
UUID planId = UUID.randomUUID();
|
||||
IrisStructureAdoptionResult result = new IrisStructureAdoptionResult(
|
||||
IrisStructureAdoptionResult.Status.BLOCKED,
|
||||
planId,
|
||||
List.of(diagnostic(IrisStructureAdoptionDiagnostic.Severity.ERROR,
|
||||
IrisStructureAdoptionDiagnostic.Code.PLAN_BLOCKED, "structures/village.json", "blocked")),
|
||||
Optional.empty(),
|
||||
Optional.empty());
|
||||
|
||||
assertFalse(result.successful());
|
||||
assertEquals(List.of(
|
||||
"Adoption plan " + planId + ": BLOCKED",
|
||||
"ERROR PLAN_BLOCKED [structures/village.json]: blocked"), result.summaryLines());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anAppliedResultWithoutAReceiptIsRejected() {
|
||||
IllegalArgumentException failure = assertThrows(IllegalArgumentException.class,
|
||||
() -> new IrisStructureAdoptionResult(
|
||||
IrisStructureAdoptionResult.Status.APPLIED,
|
||||
UUID.randomUUID(),
|
||||
List.of(),
|
||||
Optional.empty(),
|
||||
Optional.empty()));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("requires a receipt"));
|
||||
}
|
||||
|
||||
private static IrisStructureAdoptionDiagnostic diagnostic(
|
||||
IrisStructureAdoptionDiagnostic.Severity severity,
|
||||
IrisStructureAdoptionDiagnostic.Code code,
|
||||
String resource,
|
||||
String detail
|
||||
) {
|
||||
return new IrisStructureAdoptionDiagnostic(severity, code, resource, detail, null);
|
||||
}
|
||||
|
||||
private static IrisStructureAdoptionLimits limits(
|
||||
int maxResources,
|
||||
int maxJsonBytes,
|
||||
int maxBinaryBytes,
|
||||
long maxTotalBytes,
|
||||
int maxStructuresScanned,
|
||||
int maxDiagnostics,
|
||||
int maxActivePlans,
|
||||
Duration planTtl
|
||||
) {
|
||||
return new IrisStructureAdoptionLimits(
|
||||
maxResources,
|
||||
maxJsonBytes,
|
||||
maxBinaryBytes,
|
||||
maxTotalBytes,
|
||||
maxStructuresScanned,
|
||||
maxDiagnostics,
|
||||
maxActivePlans,
|
||||
planTtl);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
package art.arcane.iris.core.structure.export;
|
||||
|
||||
import art.arcane.volmlib.util.nbt.tag.CompoundTag;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class VanillaBlockStateTest {
|
||||
@Test
|
||||
public void aBareBlockKeepsItsNameAndNoProperties() {
|
||||
VanillaBlockState state = VanillaBlockState.parse(" minecraft:stone ");
|
||||
|
||||
assertEquals("minecraft:stone", state.name());
|
||||
assertTrue(state.properties().isEmpty());
|
||||
assertEquals("minecraft:stone", state.canonical());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void propertiesAreCanonicalizedIntoAlphabeticalOrder() {
|
||||
VanillaBlockState state = VanillaBlockState.parse("minecraft:oak_stairs[waterlogged=false,facing=north,half=top]");
|
||||
|
||||
assertEquals("minecraft:oak_stairs[facing=north,half=top,waterlogged=false]", state.canonical());
|
||||
assertEquals("north", state.properties().get("facing"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theCanonicalFormRoundTripsThroughTheParser() {
|
||||
String canonical = VanillaBlockState.parse("minecraft:oak_log[axis=z]").canonical();
|
||||
|
||||
assertEquals(canonical, VanillaBlockState.parse(canonical).canonical());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theNbtFormCarriesTheNameAndOnlyRealProperties() {
|
||||
CompoundTag bare = VanillaBlockState.parse("minecraft:air").toNbt();
|
||||
CompoundTag stairs = VanillaBlockState.parse("minecraft:oak_stairs[facing=north]").toNbt();
|
||||
|
||||
assertEquals("minecraft:air", bare.getString("Name"));
|
||||
assertNull(bare.get("Properties"));
|
||||
assertEquals("minecraft:oak_stairs", stairs.getString("Name"));
|
||||
assertEquals("north", stairs.getCompoundTag("Properties").getString("facing"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void nonMinecraftBlocksAreRejectedBeforeAnythingIsWritten() {
|
||||
IllegalArgumentException failure =
|
||||
assertThrows(IllegalArgumentException.class, () -> VanillaBlockState.parse("iris:custom_block"));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("non-Minecraft block"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void malformedPropertyBlocksAreRejected() {
|
||||
assertThrows(IllegalArgumentException.class, () -> VanillaBlockState.parse(null));
|
||||
assertThrows(IllegalArgumentException.class, () -> VanillaBlockState.parse("minecraft:stone[facing=north"));
|
||||
assertThrows(IllegalArgumentException.class, () -> VanillaBlockState.parse("minecraft:stone[]"));
|
||||
assertThrows(IllegalArgumentException.class, () -> VanillaBlockState.parse("minecraft:stone[facing]"));
|
||||
assertThrows(IllegalArgumentException.class, () -> VanillaBlockState.parse("minecraft:stone[facing=]"));
|
||||
assertThrows(IllegalArgumentException.class, () -> VanillaBlockState.parse("minecraft:stone[=north]"));
|
||||
assertThrows(IllegalArgumentException.class, () -> VanillaBlockState.parse("minecraft:stone[a=1][b=2]"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aRepeatedPropertyIsRejectedInsteadOfSilentlyWinning() {
|
||||
IllegalArgumentException failure = assertThrows(IllegalArgumentException.class,
|
||||
() -> VanillaBlockState.parse("minecraft:stone[facing=north,facing=south]"));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("Duplicate block property"));
|
||||
}
|
||||
}
|
||||
+124
@@ -0,0 +1,124 @@
|
||||
package art.arcane.iris.core.structure.export;
|
||||
|
||||
import art.arcane.iris.engine.framework.structure.StructureGraphResolver;
|
||||
import art.arcane.iris.engine.object.IrisStructure;
|
||||
import org.junit.Test;
|
||||
|
||||
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.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
public class VanillaJigsawExportRequestTest {
|
||||
@Test
|
||||
public void aStructureSourceKeepsTheSuppliedGraphInsteadOfLoadingOne() {
|
||||
IrisStructure structure = mock(IrisStructure.class);
|
||||
StructureGraphResolver resolver = mock(StructureGraphResolver.class);
|
||||
|
||||
VanillaJigsawExportSource source = VanillaJigsawExportSource.forStructure(" village ", structure, resolver);
|
||||
|
||||
assertEquals("village", source.structureKey());
|
||||
assertSame(structure, source.loadStructure());
|
||||
assertSame(resolver, source.resolver());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aBlankStructureKeyIsRejected() {
|
||||
IllegalArgumentException failure = assertThrows(IllegalArgumentException.class,
|
||||
() -> VanillaJigsawExportSource.forStructure(" ", mock(IrisStructure.class), mock(StructureGraphResolver.class)));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("must not be blank"));
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> VanillaJigsawExportSource.forStructure(null, mock(IrisStructure.class), mock(StructureGraphResolver.class)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theResourcePathDefaultsToTheStructureKey() {
|
||||
VanillaJigsawExportRequest request = request().build();
|
||||
|
||||
assertEquals("village", request.resourcePath());
|
||||
assertEquals("iris", request.namespace());
|
||||
assertEquals(VanillaJigsawExportFormat.DIRECTORY, request.format());
|
||||
assertFalse(request.replaceExisting());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theOutputPathIsStoredAbsoluteAndNormalized() {
|
||||
VanillaJigsawExportRequest request = VanillaJigsawExportRequest
|
||||
.builder(source(), Path.of("build", "exports", "..", "datapack"))
|
||||
.build();
|
||||
|
||||
assertTrue(request.output().isAbsolute());
|
||||
assertEquals("datapack", request.output().getFileName().toString());
|
||||
assertFalse(request.output().toString().contains(".."));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void identityFieldsAreTrimmedButTheDescriptionIsNot() {
|
||||
VanillaJigsawExportRequest request = request()
|
||||
.namespace(" mypack ")
|
||||
.resourcePath(" village/plains ")
|
||||
.description(" a description ")
|
||||
.format(VanillaJigsawExportFormat.ZIP)
|
||||
.replaceExisting(true)
|
||||
.build();
|
||||
|
||||
assertEquals("mypack", request.namespace());
|
||||
assertEquals("village/plains", request.resourcePath());
|
||||
assertEquals(" a description ", request.description());
|
||||
assertEquals(VanillaJigsawExportFormat.ZIP, request.format());
|
||||
assertTrue(request.replaceExisting());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theDefaultSettingsDescribeAVanillaSurfaceStructure() {
|
||||
VanillaJigsawExportSettings settings = VanillaJigsawExportSettings.defaults();
|
||||
|
||||
assertEquals(List.of("minecraft:plains"), settings.biomes());
|
||||
assertEquals(0, settings.startHeight());
|
||||
assertEquals(4064, settings.maxDistanceVertical());
|
||||
assertEquals(32, settings.spacing());
|
||||
assertEquals(8, settings.separation());
|
||||
assertEquals(0, settings.salt());
|
||||
assertEquals(1.0F, settings.frequency(), 0F);
|
||||
assertFalse(settings.expansionHack());
|
||||
assertEquals(VanillaJigsawExportSettings.ProjectHeightmap.WORLD_SURFACE_WG, settings.projectHeightmap());
|
||||
assertEquals(VanillaJigsawExportSettings.GenerationStep.SURFACE_STRUCTURES, settings.generationStep());
|
||||
assertEquals(VanillaJigsawExportSettings.TerrainAdaptation.NONE, settings.terrainAdaptation());
|
||||
assertEquals(VanillaJigsawExportSettings.SpreadType.LINEAR, settings.spreadType());
|
||||
assertNull(VanillaJigsawExportSettings.ProjectHeightmap.NONE.serializedName());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theSettingsBuilderReplacesTheBiomeListRatherThanAppendingToIt() {
|
||||
VanillaJigsawExportSettings settings = VanillaJigsawExportSettings.builder()
|
||||
.biomes(List.of("minecraft:desert", "minecraft:savanna"))
|
||||
.biomes(List.of("minecraft:taiga"))
|
||||
.startHeight(64)
|
||||
.randomSpread(48, 12, 7)
|
||||
.frequency(0.5F)
|
||||
.build();
|
||||
|
||||
assertEquals(List.of("minecraft:taiga"), settings.biomes());
|
||||
assertEquals(64, settings.startHeight());
|
||||
assertEquals(48, settings.spacing());
|
||||
assertEquals(12, settings.separation());
|
||||
assertEquals(7, settings.salt());
|
||||
assertEquals(0.5F, settings.frequency(), 0F);
|
||||
}
|
||||
|
||||
private static VanillaJigsawExportSource source() {
|
||||
return VanillaJigsawExportSource.forStructure(
|
||||
"village", mock(IrisStructure.class), mock(StructureGraphResolver.class));
|
||||
}
|
||||
|
||||
private static VanillaJigsawExportRequest.Builder request() {
|
||||
return VanillaJigsawExportRequest.builder(source(), Path.of("build", "datapack"));
|
||||
}
|
||||
}
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
package art.arcane.iris.core.structure.export;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class VanillaResourceIdentifierTest {
|
||||
@Test
|
||||
public void namespacesAcceptOnlyTheVanillaCharacterSet() {
|
||||
assertTrue(VanillaResourceIdentifier.validNamespace("minecraft"));
|
||||
assertTrue(VanillaResourceIdentifier.validNamespace("iris_pack-1.0"));
|
||||
assertFalse(VanillaResourceIdentifier.validNamespace(null));
|
||||
assertFalse(VanillaResourceIdentifier.validNamespace(""));
|
||||
assertFalse(VanillaResourceIdentifier.validNamespace("Minecraft"));
|
||||
assertFalse(VanillaResourceIdentifier.validNamespace("iris/pack"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void pathsAllowNestingButNeverTraversal() {
|
||||
assertTrue(VanillaResourceIdentifier.validPath("village/plains/houses"));
|
||||
assertTrue(VanillaResourceIdentifier.validPath("empty"));
|
||||
assertFalse(VanillaResourceIdentifier.validPath("/village"));
|
||||
assertFalse(VanillaResourceIdentifier.validPath("village/"));
|
||||
assertFalse(VanillaResourceIdentifier.validPath("village//houses"));
|
||||
assertFalse(VanillaResourceIdentifier.validPath("village/../secrets"));
|
||||
assertFalse(VanillaResourceIdentifier.validPath("village/./houses"));
|
||||
assertFalse(VanillaResourceIdentifier.validPath("Village"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void identifiersRequireExactlyOneSeparatorWithBothHalvesPresent() {
|
||||
assertTrue(VanillaResourceIdentifier.validIdentifier("minecraft:village/plains"));
|
||||
assertFalse(VanillaResourceIdentifier.validIdentifier("village"));
|
||||
assertFalse(VanillaResourceIdentifier.validIdentifier(":village"));
|
||||
assertFalse(VanillaResourceIdentifier.validIdentifier("minecraft:"));
|
||||
assertFalse(VanillaResourceIdentifier.validIdentifier("minecraft:village:plains"));
|
||||
assertFalse(VanillaResourceIdentifier.validIdentifier(null));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anAbsentConnectorTargetBecomesTheVanillaEmptyPool() {
|
||||
assertEquals("minecraft:empty", VanillaResourceIdentifier.normalizeConnectorIdentifier(null));
|
||||
assertEquals("minecraft:empty", VanillaResourceIdentifier.normalizeConnectorIdentifier(""));
|
||||
assertEquals("minecraft:empty", VanillaResourceIdentifier.normalizeConnectorIdentifier(" "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void abareConnectorNameIsQualifiedIntoTheMinecraftNamespace() {
|
||||
assertEquals("minecraft:bottom", VanillaResourceIdentifier.normalizeConnectorIdentifier("bottom"));
|
||||
assertEquals("minecraft:bottom", VanillaResourceIdentifier.normalizeConnectorIdentifier(" bottom "));
|
||||
assertEquals("iris:side", VanillaResourceIdentifier.normalizeConnectorIdentifier("iris:side"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aMalformedConnectorTargetIsRejectedWithItsOriginalText() {
|
||||
IllegalArgumentException failure = assertThrows(IllegalArgumentException.class,
|
||||
() -> VanillaResourceIdentifier.normalizeConnectorIdentifier("Iris:Side"));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("Iris:Side"));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> VanillaResourceIdentifier.normalizeConnectorIdentifier("iris:../escape"));
|
||||
}
|
||||
}
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
package art.arcane.iris.core.structure.export;
|
||||
|
||||
import art.arcane.iris.engine.object.IrisDirection;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class VanillaStructureTemplateOrientationTest {
|
||||
@Test
|
||||
public void aHorizontalFrontIsRenderedAgainstAnUpwardTop() {
|
||||
assertEquals("north_up", VanillaStructureTemplateEncoder.orientation(
|
||||
IrisDirection.NORTH_NEGATIVE_Z, IrisDirection.UP_POSITIVE_Y));
|
||||
assertEquals("south_up", VanillaStructureTemplateEncoder.orientation(
|
||||
IrisDirection.SOUTH_POSITIVE_Z, IrisDirection.UP_POSITIVE_Y));
|
||||
assertEquals("east_up", VanillaStructureTemplateEncoder.orientation(
|
||||
IrisDirection.EAST_POSITIVE_X, IrisDirection.UP_POSITIVE_Y));
|
||||
assertEquals("west_up", VanillaStructureTemplateEncoder.orientation(
|
||||
IrisDirection.WEST_NEGATIVE_X, IrisDirection.UP_POSITIVE_Y));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void averticalFrontIsRenderedAgainstItsHorizontalTop() {
|
||||
assertEquals("up_north", VanillaStructureTemplateEncoder.orientation(
|
||||
IrisDirection.UP_POSITIVE_Y, IrisDirection.NORTH_NEGATIVE_Z));
|
||||
assertEquals("down_east", VanillaStructureTemplateEncoder.orientation(
|
||||
IrisDirection.DOWN_NEGATIVE_Y, IrisDirection.EAST_POSITIVE_X));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void averticalFrontCannotTakeAVerticalTop() {
|
||||
IllegalArgumentException failure = assertThrows(IllegalArgumentException.class,
|
||||
() -> VanillaStructureTemplateEncoder.orientation(
|
||||
IrisDirection.UP_POSITIVE_Y, IrisDirection.UP_POSITIVE_Y));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("horizontal top direction"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aHorizontalFrontCannotTakeANonUpwardTop() {
|
||||
IllegalArgumentException failure = assertThrows(IllegalArgumentException.class,
|
||||
() -> VanillaStructureTemplateEncoder.orientation(
|
||||
IrisDirection.NORTH_NEGATIVE_Z, IrisDirection.SOUTH_POSITIVE_Z));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("upward top direction"));
|
||||
assertThrows(IllegalArgumentException.class, () -> VanillaStructureTemplateEncoder.orientation(
|
||||
IrisDirection.EAST_POSITIVE_X, IrisDirection.DOWN_NEGATIVE_Y));
|
||||
}
|
||||
}
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
package art.arcane.iris.util.common.director.handlers;
|
||||
|
||||
import art.arcane.iris.core.nms.datapack.DataVersion;
|
||||
import art.arcane.volmlib.util.collection.KList;
|
||||
import art.arcane.volmlib.util.director.exceptions.DirectorParsingException;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class DataVersionHandlerTest {
|
||||
private final DataVersionHandler handler = new DataVersionHandler();
|
||||
|
||||
@Test
|
||||
public void latestResolvesToTheNewestShippedDatapackVersion() throws DirectorParsingException {
|
||||
assertSame(DataVersion.getLatest(), handler.parse("latest", false));
|
||||
assertSame(DataVersion.getLatest(), handler.parse("LATEST", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anExactVersionStringResolvesRegardlessOfCase() throws DirectorParsingException {
|
||||
assertSame(DataVersion.V26_2, handler.parse("26.2", false));
|
||||
assertSame(DataVersion.V26_1_2, handler.parse("26.1.2", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theUnsupportedSentinelIsHiddenFromSuggestionsButStillParses() throws DirectorParsingException {
|
||||
KList<DataVersion> possibilities = handler.getPossibilities();
|
||||
|
||||
assertFalse(possibilities.contains(DataVersion.UNSUPPORTED));
|
||||
assertTrue(possibilities.contains(DataVersion.getLatest()));
|
||||
assertSame(DataVersion.UNSUPPORTED, handler.parse(DataVersion.UNSUPPORTED.getVersion(), false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnknownVersionNamesTheOffendingInput() {
|
||||
DirectorParsingException failure =
|
||||
assertThrows(DirectorParsingException.class, () -> handler.parse("nope", false));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("nope"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theRenderedFormIsTheVersionString() {
|
||||
assertSame(DataVersion.V26_2.getVersion(), handler.toString(DataVersion.V26_2));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyDataVersionParametersAreClaimed() {
|
||||
assertTrue(handler.supports(DataVersion.class));
|
||||
assertFalse(handler.supports(String.class));
|
||||
assertFalse(handler.supports(Integer.class));
|
||||
}
|
||||
}
|
||||
+94
@@ -0,0 +1,94 @@
|
||||
package art.arcane.iris.util.common.director.handlers;
|
||||
|
||||
import art.arcane.volmlib.util.director.exceptions.DirectorParsingException;
|
||||
import org.junit.Test;
|
||||
|
||||
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;
|
||||
|
||||
public class PrimitiveHandlerParsingTest {
|
||||
@Test
|
||||
public void integerSuffixesScaleTheParsedValue() throws DirectorParsingException {
|
||||
IntegerHandler handler = new IntegerHandler();
|
||||
|
||||
assertEquals(Integer.valueOf(5), handler.parse("5", false));
|
||||
assertEquals(Integer.valueOf(64), handler.parse("4c", false));
|
||||
assertEquals(Integer.valueOf(1_000), handler.parse("1k", false));
|
||||
assertEquals(Integer.valueOf(200), handler.parse("2h", false));
|
||||
assertEquals(Integer.valueOf(512), handler.parse("1r", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stackedIntegerSuffixesMultiplyTogether() throws DirectorParsingException {
|
||||
assertEquals(Integer.valueOf(2_000_000), new IntegerHandler().parse("2kk", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnparseableIntegerNamesTheOffendingInput() {
|
||||
DirectorParsingException failure =
|
||||
assertThrows(DirectorParsingException.class, () -> new IntegerHandler().parse("abc", false));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("abc"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnscaledLongKeepsFullPrecision() throws DirectorParsingException {
|
||||
LongHandler handler = new LongHandler();
|
||||
|
||||
assertEquals(Long.valueOf(9_007_199_254_740_993L), handler.parse("9007199254740993", false));
|
||||
assertEquals(Long.valueOf(1_000L), handler.parse("1k", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shortAndFloatingPointHandlersShareTheSuffixTable() throws DirectorParsingException {
|
||||
assertEquals(Short.valueOf((short) 32), new ShortHandler().parse("2c", false));
|
||||
assertEquals(1_500F, new FloatHandler().parse("1.5k", false), 0F);
|
||||
assertEquals(1_500D, new DoubleHandler().parse("1.5k", false), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void byteParsingHasNoSuffixTable() throws DirectorParsingException {
|
||||
ByteHandler handler = new ByteHandler();
|
||||
|
||||
assertEquals(Byte.valueOf((byte) 12), handler.parse("12", false));
|
||||
assertThrows(DirectorParsingException.class, () -> handler.parse("1k", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void booleanSentinelsAreCaseSensitiveAndEverythingElseFallsBackToFalse() throws DirectorParsingException {
|
||||
BooleanHandler handler = new BooleanHandler();
|
||||
|
||||
assertNull(handler.parse("null", false));
|
||||
assertNull(handler.parse("other", false));
|
||||
assertNull(handler.parse("flip", false));
|
||||
assertEquals(Boolean.TRUE, handler.parse("TRUE", false));
|
||||
assertEquals(Boolean.FALSE, handler.parse("yes", false));
|
||||
assertEquals(Boolean.FALSE, handler.parse("NULL", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stringParsingIsVerbatim() throws DirectorParsingException {
|
||||
StringHandler handler = new StringHandler();
|
||||
|
||||
assertEquals(" spaced ", handler.parse(" spaced ", false));
|
||||
assertEquals("", handler.parse("", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void eachHandlerClaimsOnlyItsOwnParameterType() {
|
||||
assertTrue(new IntegerHandler().supports(int.class));
|
||||
assertTrue(new IntegerHandler().supports(Integer.class));
|
||||
assertFalse(new IntegerHandler().supports(Long.class));
|
||||
assertTrue(new LongHandler().supports(long.class));
|
||||
assertTrue(new ShortHandler().supports(short.class));
|
||||
assertTrue(new ByteHandler().supports(byte.class));
|
||||
assertTrue(new FloatHandler().supports(float.class));
|
||||
assertTrue(new DoubleHandler().supports(double.class));
|
||||
assertTrue(new BooleanHandler().supports(boolean.class));
|
||||
assertTrue(new StringHandler().supports(String.class));
|
||||
assertFalse(new StringHandler().supports(Object.class));
|
||||
}
|
||||
}
|
||||
+144
@@ -0,0 +1,144 @@
|
||||
package art.arcane.iris.util.common.director.handlers;
|
||||
|
||||
import art.arcane.volmlib.util.director.exceptions.DirectorParsingException;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
public class VectorHandlerParsingTest {
|
||||
@Test
|
||||
public void threeComponentsMapToTheirAxes() throws DirectorParsingException {
|
||||
Vector parsed = console().parse("1, 2 ,3", false);
|
||||
|
||||
assertEquals(1D, parsed.getX(), 0D);
|
||||
assertEquals(2D, parsed.getY(), 0D);
|
||||
assertEquals(3D, parsed.getZ(), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void twoComponentsAreTreatedAsAHorizontalPair() throws DirectorParsingException {
|
||||
Vector parsed = console().parse("4,6", false);
|
||||
|
||||
assertEquals(4D, parsed.getX(), 0D);
|
||||
assertEquals(0D, parsed.getY(), 0D);
|
||||
assertEquals(6D, parsed.getZ(), 0D);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anyOtherComponentCountIsRejectedWithItsArity() {
|
||||
DirectorParsingException failure =
|
||||
assertThrows(DirectorParsingException.class, () -> console().parse("1,2,3,4", false));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("4 components"));
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("Expected 2 or 3"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionalKeywordsAreRefusedForAConsoleSender() {
|
||||
VectorHandler handler = console();
|
||||
|
||||
assertTrue(assertThrows(DirectorParsingException.class, () -> handler.parse("here", false))
|
||||
.getMessage().contains("as a console"));
|
||||
assertTrue(assertThrows(DirectorParsingException.class, () -> handler.parse("SELF", false))
|
||||
.getMessage().contains("as a console"));
|
||||
assertTrue(assertThrows(DirectorParsingException.class, () -> handler.parse("look", false))
|
||||
.getMessage().contains("as a console"));
|
||||
assertTrue(assertThrows(DirectorParsingException.class, () -> handler.parse("Crosshair", false))
|
||||
.getMessage().contains("as a console"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void positionalKeywordsResolveForAPlayerSender() throws DirectorParsingException {
|
||||
VectorHandler handler = new TestVectorHandler(true, new Vector(10, 20, 30), new Vector(-1, -2, -3), List.of());
|
||||
|
||||
assertEquals(new Vector(10, 20, 30), handler.parse("me", false));
|
||||
assertEquals(new Vector(-1, -2, -3), handler.parse("cursor", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aNamedPlayerResolvesToThatPlayersPosition() throws DirectorParsingException {
|
||||
Player player = mock(Player.class);
|
||||
when(player.getLocation()).thenReturn(new Location(null, 7D, 8D, 9D));
|
||||
VectorHandler handler = new TestVectorHandler(false, null, null, List.of(player));
|
||||
|
||||
assertEquals(new Vector(7, 8, 9), handler.parse("player:steve", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnmatchedPlayerQueryNamesTheQuery() {
|
||||
VectorHandler handler = new TestVectorHandler(false, null, null, List.of());
|
||||
|
||||
DirectorParsingException failure =
|
||||
assertThrows(DirectorParsingException.class, () -> handler.parse("player:steve", false));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("steve"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anUnrecognizedTokenResolvesToNothingRatherThanFailing() throws DirectorParsingException {
|
||||
assertNull(console().parse("garbage", false));
|
||||
assertNull(console().parse("", false));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void theRenderedFormOmitsAZeroHeight() {
|
||||
VectorHandler handler = console();
|
||||
|
||||
assertEquals(2, handler.toString(new Vector(1, 0, 3)).split(",").length);
|
||||
assertEquals(3, handler.toString(new Vector(1, 2, 3)).split(",").length);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onlyVectorParametersAreClaimed() {
|
||||
assertTrue(console().supports(Vector.class));
|
||||
assertEquals(false, console().supports(Location.class));
|
||||
}
|
||||
|
||||
private static VectorHandler console() {
|
||||
return new TestVectorHandler(false, null, null, List.of());
|
||||
}
|
||||
|
||||
private static final class TestVectorHandler extends VectorHandler {
|
||||
private final boolean senderIsPlayer;
|
||||
private final Vector senderVector;
|
||||
private final Vector lookVector;
|
||||
private final List<?> players;
|
||||
|
||||
private TestVectorHandler(boolean senderIsPlayer, Vector senderVector, Vector lookVector, List<?> players) {
|
||||
this.senderIsPlayer = senderIsPlayer;
|
||||
this.senderVector = senderVector;
|
||||
this.lookVector = lookVector;
|
||||
this.players = players;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isSenderPlayer() {
|
||||
return senderIsPlayer;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vector getSenderVector() {
|
||||
return senderVector;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Vector getLookVector() {
|
||||
return lookVector;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<?> playerPossibilities(String query) {
|
||||
return players;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,10 @@ java {
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
testImplementation('junit:junit:4.13.2')
|
||||
}
|
||||
|
||||
tasks.named('jar', Jar).configure {
|
||||
archiveBaseName.set('iris-spi')
|
||||
}
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
package art.arcane.iris.spi;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisPlatformsBindContractTest {
|
||||
@Before
|
||||
public void requireUnbound() {
|
||||
IrisPlatforms.unbind();
|
||||
}
|
||||
|
||||
@After
|
||||
public void releaseBinding() {
|
||||
IrisPlatforms.unbind();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unboundLookupsReportAbsenceInsteadOfImprovising() {
|
||||
assertFalse(IrisPlatforms.isBound());
|
||||
assertNull(IrisPlatforms.getOrNull());
|
||||
|
||||
IllegalStateException failure = assertThrows(IllegalStateException.class, IrisPlatforms::get);
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("No Iris platform is bound"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void bindingIsVisibleThroughEveryAccessor() {
|
||||
IrisPlatform platform = new StubPlatform();
|
||||
|
||||
IrisPlatforms.bind(platform);
|
||||
|
||||
assertTrue(IrisPlatforms.isBound());
|
||||
assertSame(platform, IrisPlatforms.get());
|
||||
assertSame(platform, IrisPlatforms.getOrNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void rebindingTheSameInstanceIsAccepted() {
|
||||
IrisPlatform platform = new StubPlatform();
|
||||
|
||||
IrisPlatforms.bind(platform);
|
||||
IrisPlatforms.bind(platform);
|
||||
|
||||
assertSame(platform, IrisPlatforms.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aSecondPlatformCannotDisplaceTheBoundOne() {
|
||||
IrisPlatform first = new StubPlatform();
|
||||
IrisPlatforms.bind(first);
|
||||
|
||||
IllegalStateException failure = assertThrows(
|
||||
IllegalStateException.class, () -> IrisPlatforms.bind(new StubPlatform()));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains("already bound"));
|
||||
assertSame(first, IrisPlatforms.get());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unbindIsIdempotentAndClearsTheBinding() {
|
||||
IrisPlatforms.bind(new StubPlatform());
|
||||
|
||||
IrisPlatforms.unbind();
|
||||
IrisPlatforms.unbind();
|
||||
|
||||
assertFalse(IrisPlatforms.isBound());
|
||||
assertNull(IrisPlatforms.getOrNull());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unbindingClearsTheOnceKeysSoLaterRunsWarnAgain() {
|
||||
IrisPlatforms.bind(new StubPlatform());
|
||||
assertTrue(IrisLogging.warnOnce("spi-contract-once", "first"));
|
||||
assertFalse(IrisLogging.warnOnce("spi-contract-once", "second"));
|
||||
|
||||
IrisPlatforms.unbind();
|
||||
|
||||
assertTrue(IrisLogging.warnOnce("spi-contract-once", "third"));
|
||||
}
|
||||
|
||||
private static final class StubPlatform implements IrisPlatform {
|
||||
@Override
|
||||
public String platformName() {
|
||||
return "stub";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String minecraftVersion() {
|
||||
return "26.2";
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformRegistries registries() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformScheduler scheduler() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformStructureHooks structureHooks() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlatformBiomeWriter biomeWriter() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File dataFolder() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File dataFile(String... path) {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public File pluginJar() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int irisVersionNumber() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int minecraftVersionNumber() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void callEvent(Object event) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void dispatchConsoleCommand(String command) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean spawnEntity(PlatformWorld world, String entityKey, double x, double y, double z) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(LogLevel level, String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void msg(String message) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void reportError(Throwable error) {
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package art.arcane.iris.spi;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class IrisServicesRegistryContractTest {
|
||||
@Before
|
||||
public void requireEmptyRegistry() {
|
||||
IrisServices.clear();
|
||||
}
|
||||
|
||||
@After
|
||||
public void releaseRegistry() {
|
||||
IrisServices.clear();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void registeredServicesResolveThroughBothAccessors() {
|
||||
Greeter greeter = new Greeter();
|
||||
|
||||
IrisServices.register(Greeter.class, greeter);
|
||||
|
||||
assertSame(greeter, IrisServices.get(Greeter.class));
|
||||
assertSame(greeter, IrisServices.getOrNull(Greeter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void anAbsentServiceNamesItselfInTheFailure() {
|
||||
assertNull(IrisServices.getOrNull(Greeter.class));
|
||||
|
||||
IllegalStateException failure = assertThrows(IllegalStateException.class, () -> IrisServices.get(Greeter.class));
|
||||
|
||||
assertTrue(failure.getMessage(), failure.getMessage().contains(Greeter.class.getName()));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void reregisteringATypeReplacesThePreviousImplementation() {
|
||||
Greeter first = new Greeter();
|
||||
Greeter second = new Greeter();
|
||||
|
||||
IrisServices.register(Greeter.class, first);
|
||||
IrisServices.register(Greeter.class, second);
|
||||
|
||||
assertSame(second, IrisServices.get(Greeter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void removingAServiceLeavesTheRegistryUsable() {
|
||||
IrisServices.register(Greeter.class, new Greeter());
|
||||
|
||||
IrisServices.remove(Greeter.class);
|
||||
IrisServices.remove(Greeter.class);
|
||||
|
||||
assertNull(IrisServices.getOrNull(Greeter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearDropsEveryRegistration() {
|
||||
IrisServices.register(Greeter.class, new Greeter());
|
||||
IrisServices.register(Counter.class, new Counter());
|
||||
|
||||
IrisServices.clear();
|
||||
|
||||
assertNull(IrisServices.getOrNull(Greeter.class));
|
||||
assertNull(IrisServices.getOrNull(Counter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aMismatchedImplementationIsRejectedBeforeItEntersTheRegistry() {
|
||||
assertThrows(ClassCastException.class, () -> IrisServices.register(Greeter.class, new Counter()));
|
||||
|
||||
assertNull(IrisServices.getOrNull(Greeter.class));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aNullImplementationIsRejectedBeforeItEntersTheRegistry() {
|
||||
assertThrows(NullPointerException.class, () -> IrisServices.register(Greeter.class, null));
|
||||
|
||||
assertNull(IrisServices.getOrNull(Greeter.class));
|
||||
}
|
||||
|
||||
private static final class Greeter {
|
||||
}
|
||||
|
||||
private static final class Counter {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,177 @@
|
||||
package art.arcane.iris.spi;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertThrows;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class PlatformValueContractTest {
|
||||
private static final String FINGERPRINT = "a".repeat(64);
|
||||
|
||||
@Test
|
||||
public void jigsawMetadataAcceptsTheWholeDocumentedRange() {
|
||||
PlatformStructureHooks.JigsawSourceMetadata smallest =
|
||||
new PlatformStructureHooks.JigsawSourceMetadata(1, 0, 0);
|
||||
PlatformStructureHooks.JigsawSourceMetadata largest =
|
||||
new PlatformStructureHooks.JigsawSourceMetadata(128, 7, 96);
|
||||
|
||||
assertEquals(1, smallest.maxDistanceHorizontal());
|
||||
assertEquals(128, largest.maxDistanceHorizontal());
|
||||
assertEquals(7, largest.referenceExpansion());
|
||||
assertEquals(96, largest.maxStartElementHorizontalSpan());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jigsawMetadataDefaultsTheStartElementSpanToZero() {
|
||||
PlatformStructureHooks.JigsawSourceMetadata metadata =
|
||||
new PlatformStructureHooks.JigsawSourceMetadata(64, 3);
|
||||
|
||||
assertEquals(0, metadata.maxStartElementHorizontalSpan());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void jigsawMetadataRejectsOutOfRangeGeometry() {
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new PlatformStructureHooks.JigsawSourceMetadata(0, 0, 0));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new PlatformStructureHooks.JigsawSourceMetadata(129, 0, 0));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new PlatformStructureHooks.JigsawSourceMetadata(64, -1, 0));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new PlatformStructureHooks.JigsawSourceMetadata(64, 0, -1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void unimplementedStructureHookDefaultsRefuseInsteadOfReturningNothing() {
|
||||
PlatformStructureHooks hooks = new MinimalStructureHooks();
|
||||
|
||||
assertEquals(List.of(), hooks.jigsawStructureKeys());
|
||||
assertEquals(List.of(), hooks.templatePoolKeys());
|
||||
assertThrows(UnsupportedOperationException.class, () -> hooks.jigsawSourceMetadata("iris:village"));
|
||||
assertThrows(UnsupportedOperationException.class, () -> hooks.templatePoolHorizontalSpan("iris:pool"));
|
||||
assertThrows(UnsupportedOperationException.class,
|
||||
() -> hooks.jigsawStartPoolHorizontalSpan("iris:village", "iris:pool"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void definitionFactoriesCarryTheirEncoding() {
|
||||
PlatformGenerationRegistry.Definition json = PlatformGenerationRegistry.Definition.exactJson("{}");
|
||||
PlatformGenerationRegistry.Definition identity =
|
||||
PlatformGenerationRegistry.Definition.conservativeIdentity("abc");
|
||||
|
||||
assertEquals(PlatformGenerationRegistry.Encoding.JSON, json.encoding());
|
||||
assertEquals("{}", json.value());
|
||||
assertEquals(PlatformGenerationRegistry.Encoding.CONSERVATIVE_IDENTITY, identity.encoding());
|
||||
assertEquals("abc", identity.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void resourceIdentityTrimsItsPartsIntoAStableKey() {
|
||||
PlatformGenerationRegistry.Definition definition =
|
||||
PlatformGenerationRegistry.Definition.resourceIdentity(" worldgen/biome ", " iris:plains ");
|
||||
|
||||
assertEquals(PlatformGenerationRegistry.Encoding.CONSERVATIVE_IDENTITY, definition.encoding());
|
||||
assertEquals("registry-resource-key-v1|worldgen/biome|iris:plains", definition.value());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void definitionsRejectMissingAndBlankValues() {
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> new PlatformGenerationRegistry.Definition(null, "value"));
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> new PlatformGenerationRegistry.Definition(PlatformGenerationRegistry.Encoding.JSON, null));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> new PlatformGenerationRegistry.Definition(PlatformGenerationRegistry.Encoding.JSON, " "));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> PlatformGenerationRegistry.Definition.resourceIdentity(" ", "iris:plains"));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> PlatformGenerationRegistry.Definition.resourceIdentity("worldgen/biome", " "));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customBiomeResourceKeysAreContentAddressed() {
|
||||
assertEquals("iris:biomes/" + FINGERPRINT,
|
||||
PlatformGenerationRegistry.contentAddressedCustomBiomeResourceKey(FINGERPRINT));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void customBiomeResourceKeysRejectAnythingButALowercaseSha256() {
|
||||
assertThrows(NullPointerException.class,
|
||||
() -> PlatformGenerationRegistry.contentAddressedCustomBiomeResourceKey(null));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> PlatformGenerationRegistry.contentAddressedCustomBiomeResourceKey(FINGERPRINT.toUpperCase()));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> PlatformGenerationRegistry.contentAddressedCustomBiomeResourceKey("a".repeat(63)));
|
||||
assertThrows(IllegalArgumentException.class,
|
||||
() -> PlatformGenerationRegistry.contentAddressedCustomBiomeResourceKey("z".repeat(64)));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void blockPropertiesReportWhetherTheyCarryANumericRange() {
|
||||
PlatformNumericRange range = new PlatformNumericRange(0D, 15D, false, true);
|
||||
PlatformBlockProperty numeric =
|
||||
new PlatformBlockProperty("level", "integer", 0, List.of(0, 15), range);
|
||||
PlatformBlockProperty enumerated =
|
||||
new PlatformBlockProperty("axis", "string", "y", List.of("x", "y", "z"), null);
|
||||
|
||||
assertTrue(numeric.hasNumericRange());
|
||||
assertEquals(range, numeric.numericRange());
|
||||
assertEquals(0D, range.minimum(), 0D);
|
||||
assertEquals(15D, range.maximum(), 0D);
|
||||
assertFalse(range.exclusiveMinimum());
|
||||
assertTrue(range.exclusiveMaximum());
|
||||
assertFalse(enumerated.hasNumericRange());
|
||||
assertEquals(List.of("x", "y", "z"), enumerated.allowedValues());
|
||||
}
|
||||
|
||||
private static final class MinimalStructureHooks implements PlatformStructureHooks {
|
||||
@Override
|
||||
public List<String> structureKeys() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> structureSetKeys() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> structureBiomeKeys(String structureKey) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> objectFeatureKeys() {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> reachableStructureKeys(PlatformWorld world) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> possibleBiomeKeys(PlatformWorld world) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean placeFeature(PlatformWorld world, int x, int y, int z, String featureKey, long seed) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int[] placeStructure(PlatformWorld world, int chunkX, int chunkZ, String structureKey, long seed, int maxSpan) {
|
||||
return new int[0];
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean supportsStructurePlacement() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user