mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-11 19:14:11 +00:00
Adapter patches
(tests)
This commit is contained in:
+6
@@ -14,6 +14,7 @@ import net.minecraft.SharedConstants;
|
|||||||
import net.minecraft.server.Bootstrap;
|
import net.minecraft.server.Bootstrap;
|
||||||
import net.minecraft.world.level.LevelHeightAccessor;
|
import net.minecraft.world.level.LevelHeightAccessor;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.levelgen.Heightmap;
|
||||||
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -45,6 +46,11 @@ public final class IrisChunkGeneratorHeightCacheTest {
|
|||||||
NativeBlockTags.bindHeightmapFixtures();
|
NativeBlockTags.bindHeightmapFixtures();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void restoreBlockTags() {
|
||||||
|
NativeBlockTags.restoreTags();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void repeatedNativeHeightUsesLiveHeightQueriesAcrossRuntimeChanges() throws Exception {
|
public void repeatedNativeHeightUsesLiveHeightQueriesAcrossRuntimeChanges() throws Exception {
|
||||||
Fixture fixture = fixture();
|
Fixture fixture = fixture();
|
||||||
|
|||||||
+6
@@ -20,6 +20,7 @@ import net.minecraft.world.level.block.Blocks;
|
|||||||
import net.minecraft.world.level.block.RotatedPillarBlock;
|
import net.minecraft.world.level.block.RotatedPillarBlock;
|
||||||
import net.minecraft.world.level.block.LiquidBlock;
|
import net.minecraft.world.level.block.LiquidBlock;
|
||||||
import net.minecraft.world.level.levelgen.Heightmap;
|
import net.minecraft.world.level.levelgen.Heightmap;
|
||||||
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -46,6 +47,11 @@ public final class NativeTransitionColumnTest {
|
|||||||
NativeBlockTags.bindHeightmapFixtures();
|
NativeBlockTags.bindHeightmapFixtures();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void restoreBlockTags() {
|
||||||
|
NativeBlockTags.restoreTags();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void customKeysUseCarrierForNativeColumnsAndHeightmaps() {
|
public void customKeysUseCarrierForNativeColumnsAndHeightmaps() {
|
||||||
String key = "itemsadder:rocks/ruby_ore";
|
String key = "itemsadder:rocks/ruby_ore";
|
||||||
|
|||||||
+17
@@ -1,21 +1,30 @@
|
|||||||
package art.arcane.iris.nativegen.v26_3_R1;
|
package art.arcane.iris.nativegen.v26_3_R1;
|
||||||
|
|
||||||
import net.minecraft.core.Holder;
|
import net.minecraft.core.Holder;
|
||||||
|
import net.minecraft.core.HolderSet;
|
||||||
import net.minecraft.core.registries.BuiltInRegistries;
|
import net.minecraft.core.registries.BuiltInRegistries;
|
||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import net.minecraft.tags.BlockTags;
|
import net.minecraft.tags.BlockTags;
|
||||||
import net.minecraft.tags.TagLoader;
|
import net.minecraft.tags.TagLoader;
|
||||||
|
import net.minecraft.tags.TagKey;
|
||||||
import net.minecraft.world.level.block.Block;
|
import net.minecraft.world.level.block.Block;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
public final class NativeBlockTags {
|
public final class NativeBlockTags {
|
||||||
|
private static Map<TagKey<Block>, List<Holder<Block>>> originalTags;
|
||||||
|
|
||||||
private NativeBlockTags() {
|
private NativeBlockTags() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void bindHeightmapFixtures() {
|
public static void bindHeightmapFixtures() {
|
||||||
|
originalTags = new HashMap<>();
|
||||||
|
for (HolderSet.Named<Block> tag : BuiltInRegistries.BLOCK.getTags().toList()) {
|
||||||
|
originalTags.put(tag.key(), tag.stream().toList());
|
||||||
|
}
|
||||||
List<Holder<Block>> solid = List.of(Blocks.STONE.builtInRegistryHolder(),
|
List<Holder<Block>> solid = List.of(Blocks.STONE.builtInRegistryHolder(),
|
||||||
Blocks.OAK_LOG.builtInRegistryHolder(), Blocks.SPRUCE_LOG.builtInRegistryHolder());
|
Blocks.OAK_LOG.builtInRegistryHolder(), Blocks.SPRUCE_LOG.builtInRegistryHolder());
|
||||||
List<Holder<Block>> canopy = List.of(Blocks.STONE.builtInRegistryHolder(),
|
List<Holder<Block>> canopy = List.of(Blocks.STONE.builtInRegistryHolder(),
|
||||||
@@ -25,4 +34,12 @@ public final class NativeBlockTags {
|
|||||||
BlockTags.BLOCKS_MOTION_IN_HEIGHTMAP, canopy,
|
BlockTags.BLOCKS_MOTION_IN_HEIGHTMAP, canopy,
|
||||||
BlockTags.BLOCKS_MOTION_IN_HEIGHTMAP_NO_LEAVES, solid))).apply();
|
BlockTags.BLOCKS_MOTION_IN_HEIGHTMAP_NO_LEAVES, solid))).apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void restoreTags() {
|
||||||
|
if (originalTags == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
BuiltInRegistries.BLOCK.prepareTagReload(new TagLoader.LoadResult<>(Registries.BLOCK, originalTags)).apply();
|
||||||
|
originalTags = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
@@ -34,6 +34,7 @@ import net.minecraft.world.level.levelgen.structure.pieces.PiecesContainer;
|
|||||||
import net.minecraft.world.level.levelgen.structure.structures.IglooPieces;
|
import net.minecraft.world.level.levelgen.structure.structures.IglooPieces;
|
||||||
import net.minecraft.world.level.levelgen.structure.structures.SwampHutPiece;
|
import net.minecraft.world.level.levelgen.structure.structures.SwampHutPiece;
|
||||||
import net.minecraft.world.level.levelgen.structure.structures.SwampHutStructure;
|
import net.minecraft.world.level.levelgen.structure.structures.SwampHutStructure;
|
||||||
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -68,6 +69,11 @@ public class WorldgenTerrainHeightmapsTest {
|
|||||||
NativeBlockTags.bindHeightmapFixtures();
|
NativeBlockTags.bindHeightmapFixtures();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void restoreBlockTags() {
|
||||||
|
NativeBlockTags.restoreTags();
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void authoringHeightmapsReadPlacedObjectsAndFluidsWithoutTerrainResolvers() {
|
public void authoringHeightmapsReadPlacedObjectsAndFluidsWithoutTerrainResolvers() {
|
||||||
ProtoChunk chunk = terrainChunk(new ChunkPos(0, 0));
|
ProtoChunk chunk = terrainChunk(new ChunkPos(0, 0));
|
||||||
|
|||||||
+4
@@ -13,6 +13,7 @@ import com.google.gson.JsonParser;
|
|||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@@ -26,6 +27,9 @@ import static org.junit.Assert.assertNull;
|
|||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
public final class BareMobSpawnHistoryTest {
|
public final class BareMobSpawnHistoryTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
private static final String LEGACY_RENDERER_IDENTITY = "bukkit-v26_3_R1-generated-registry-json-v1";
|
private static final String LEGACY_RENDERER_IDENTITY = "bukkit-v26_3_R1-generated-registry-json-v1";
|
||||||
private static final String NATURAL_MOB_SPAWNS = "minecraft:gameplay/natural_mob_spawns";
|
private static final String NATURAL_MOB_SPAWNS = "minecraft:gameplay/natural_mob_spawns";
|
||||||
private static final GenerationRegistryContract.PhysicalResourceKey INSTALLED =
|
private static final GenerationRegistryContract.PhysicalResourceKey INSTALLED =
|
||||||
|
|||||||
+4
@@ -29,6 +29,7 @@ import net.minecraft.world.level.levelgen.structure.StructureStart;
|
|||||||
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplateManager;
|
||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
|
import org.bukkit.craftbukkit.generator.CustomChunkGenerator;
|
||||||
@@ -49,6 +50,9 @@ import static org.mockito.Mockito.spy;
|
|||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
|
|
||||||
public class HeadlessChunkGeneratorTest {
|
public class HeadlessChunkGeneratorTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public TemporaryFolder temporary = new TemporaryFolder();
|
public TemporaryFolder temporary = new TemporaryFolder();
|
||||||
|
|
||||||
|
|||||||
+4
@@ -31,6 +31,7 @@ import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemp
|
|||||||
import net.minecraft.world.level.storage.LevelStorageSource;
|
import net.minecraft.world.level.storage.LevelStorageSource;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
@@ -48,6 +49,9 @@ import static org.junit.Assert.assertThrows;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public final class HeadlessChunkLightingTest {
|
public final class HeadlessChunkLightingTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public TemporaryFolder temporary = new TemporaryFolder();
|
public TemporaryFolder temporary = new TemporaryFolder();
|
||||||
|
|
||||||
|
|||||||
+4
@@ -38,6 +38,7 @@ import net.minecraft.world.ticks.ScheduledTick;
|
|||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
@@ -54,6 +55,9 @@ import static org.mockito.Mockito.mock;
|
|||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class HeadlessChunkSerializationTest {
|
public class HeadlessChunkSerializationTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public TemporaryFolder temporary = new TemporaryFolder();
|
public TemporaryFolder temporary = new TemporaryFolder();
|
||||||
|
|
||||||
|
|||||||
+4
@@ -9,6 +9,7 @@ import net.minecraft.world.item.Items;
|
|||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import net.minecraft.world.level.block.Blocks;
|
import net.minecraft.world.level.block.Blocks;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
|
|
||||||
@@ -18,6 +19,9 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public final class HeadlessNativeBootstrapTest {
|
public final class HeadlessNativeBootstrapTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void bindsNativeItemComponentsWithoutStartingServer() throws Exception {
|
public void bindsNativeItemComponentsWithoutStartingServer() throws Exception {
|
||||||
HeadlessNativeBootstrap.bindComponents(HeadlessNativeTestRegistries.get());
|
HeadlessNativeBootstrap.bindComponents(HeadlessNativeTestRegistries.get());
|
||||||
|
|||||||
+4
@@ -7,12 +7,16 @@ import net.minecraft.resources.Identifier;
|
|||||||
import net.minecraft.resources.ResourceKey;
|
import net.minecraft.resources.ResourceKey;
|
||||||
import net.minecraft.world.level.biome.Biome;
|
import net.minecraft.world.level.biome.Biome;
|
||||||
import net.minecraft.world.level.levelgen.structure.Structure;
|
import net.minecraft.world.level.levelgen.structure.Structure;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public final class HeadlessNativeRegistriesTest {
|
public final class HeadlessNativeRegistriesTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void nativeStructureBiomeTagsUseTheLoadedBiomeHolders() throws Exception {
|
public void nativeStructureBiomeTagsUseTheLoadedBiomeHolders() throws Exception {
|
||||||
RegistryAccess registries = HeadlessNativeTestRegistries.get();
|
RegistryAccess registries = HeadlessNativeTestRegistries.get();
|
||||||
|
|||||||
+4
@@ -4,6 +4,7 @@ import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeRegistryAccess;
|
|||||||
import net.minecraft.core.registries.Registries;
|
import net.minecraft.core.registries.Registries;
|
||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -17,6 +18,9 @@ import static org.junit.Assert.assertFalse;
|
|||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
public final class HeadlessNativeRegistryTest {
|
public final class HeadlessNativeRegistryTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void bootstrap() {
|
public static void bootstrap() {
|
||||||
HeadlessNativeBootstrap.initialize();
|
HeadlessNativeBootstrap.initialize();
|
||||||
|
|||||||
+4
@@ -11,6 +11,7 @@ import art.arcane.volmlib.nativelib.minecraft26_2.modded.NativeTileReader;
|
|||||||
import net.minecraft.nbt.CompoundTag;
|
import net.minecraft.nbt.CompoundTag;
|
||||||
import net.minecraft.data.registries.VanillaRegistries;
|
import net.minecraft.data.registries.VanillaRegistries;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
import java.io.ByteArrayInputStream;
|
||||||
@@ -23,6 +24,9 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertThrows;
|
import static org.junit.Assert.assertThrows;
|
||||||
|
|
||||||
public final class HeadlessNativeTileTest {
|
public final class HeadlessNativeTileTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void bootstrap() {
|
public static void bootstrap() {
|
||||||
HeadlessNativeBootstrap.initialize();
|
HeadlessNativeBootstrap.initialize();
|
||||||
|
|||||||
+4
@@ -17,6 +17,7 @@ import org.bukkit.Bukkit;
|
|||||||
import org.bukkit.NamespacedKey;
|
import org.bukkit.NamespacedKey;
|
||||||
import org.bukkit.persistence.PersistentDataType;
|
import org.bukkit.persistence.PersistentDataType;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.spigotmc.SpigotWorldConfig;
|
import org.spigotmc.SpigotWorldConfig;
|
||||||
@@ -36,6 +37,9 @@ import static org.junit.Assert.assertSame;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public final class HeadlessRegionTerrainTest {
|
public final class HeadlessRegionTerrainTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public TemporaryFolder temporary = new TemporaryFolder();
|
public TemporaryFolder temporary = new TemporaryFolder();
|
||||||
|
|
||||||
|
|||||||
+4
@@ -24,6 +24,7 @@ import net.minecraft.world.level.block.state.BlockState;
|
|||||||
import net.minecraft.world.level.chunk.storage.RegionFile;
|
import net.minecraft.world.level.chunk.storage.RegionFile;
|
||||||
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
|
import net.minecraft.world.level.chunk.storage.RegionStorageInfo;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.DataInputStream;
|
import java.io.DataInputStream;
|
||||||
@@ -38,6 +39,9 @@ import static org.junit.Assert.assertThrows;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public final class NativeRegionTerrainWriterTest {
|
public final class NativeRegionTerrainWriterTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void bootstrap() {
|
public static void bootstrap() {
|
||||||
SharedConstants.tryDetectVersion();
|
SharedConstants.tryDetectVersion();
|
||||||
|
|||||||
+4
@@ -26,6 +26,7 @@ import net.minecraft.world.level.material.Fluids;
|
|||||||
import org.bukkit.Bukkit;
|
import org.bukkit.Bukkit;
|
||||||
import org.junit.AfterClass;
|
import org.junit.AfterClass;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -40,6 +41,9 @@ import static org.junit.Assert.assertThrows;
|
|||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public final class OfflineNativeFeatureFeasibilityTest {
|
public final class OfflineNativeFeatureFeasibilityTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
private static HeadlessNativeRegistries loaded;
|
private static HeadlessNativeRegistries loaded;
|
||||||
private static RegistryAccess registries;
|
private static RegistryAccess registries;
|
||||||
private static DimensionType dimension;
|
private static DimensionType dimension;
|
||||||
|
|||||||
+4
@@ -1,6 +1,7 @@
|
|||||||
package art.arcane.iris.probe;
|
package art.arcane.iris.probe;
|
||||||
|
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
|
import org.junit.ClassRule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.rules.TemporaryFolder;
|
import org.junit.rules.TemporaryFolder;
|
||||||
import org.mockito.MockedStatic;
|
import org.mockito.MockedStatic;
|
||||||
@@ -22,6 +23,9 @@ import static org.mockito.Mockito.mock;
|
|||||||
import static org.mockito.Mockito.mockStatic;
|
import static org.mockito.Mockito.mockStatic;
|
||||||
|
|
||||||
public final class OfflineRegionPublicationTest {
|
public final class OfflineRegionPublicationTest {
|
||||||
|
@ClassRule
|
||||||
|
public static final PaperRegistryIsolation registryIsolation = new PaperRegistryIsolation();
|
||||||
|
|
||||||
@Rule
|
@Rule
|
||||||
public TemporaryFolder temporary = new TemporaryFolder();
|
public TemporaryFolder temporary = new TemporaryFolder();
|
||||||
|
|
||||||
|
|||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package art.arcane.iris.probe;
|
||||||
|
|
||||||
|
import io.papermc.paper.registry.PaperRegistryAccess;
|
||||||
|
import org.junit.rules.TestRule;
|
||||||
|
import org.junit.runner.Description;
|
||||||
|
import org.junit.runners.model.Statement;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public final class PaperRegistryIsolation implements TestRule {
|
||||||
|
@Override
|
||||||
|
public Statement apply(Statement base, Description description) {
|
||||||
|
return new Statement() {
|
||||||
|
@Override
|
||||||
|
public void evaluate() throws Throwable {
|
||||||
|
Map<Object, Object> registries = registries();
|
||||||
|
Map<Object, Object> previous = new HashMap<>(registries);
|
||||||
|
registries.clear();
|
||||||
|
try {
|
||||||
|
base.evaluate();
|
||||||
|
} finally {
|
||||||
|
registries.clear();
|
||||||
|
registries.putAll(previous);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Map<Object, Object> registries() throws ReflectiveOperationException {
|
||||||
|
Field field = PaperRegistryAccess.class.getDeclaredField("registries");
|
||||||
|
field.setAccessible(true);
|
||||||
|
return (Map<Object, Object>) field.get(PaperRegistryAccess.instance());
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
-4
@@ -96,7 +96,7 @@ String forgeArtifactName = irisArtifactName('Forge', "${minecraftVersion}+${load
|
|||||||
String neoForgeArtifactName = irisArtifactName('NeoForge', "${minecraftVersion}+${loaderDisplayVersion(neoForgeVersion)}")
|
String neoForgeArtifactName = irisArtifactName('NeoForge', "${minecraftVersion}+${loaderDisplayVersion(neoForgeVersion)}")
|
||||||
Map<String, String> runtimeDownloadRelocations = (Map<String, String>) ext.runtimeDownloadRelocations
|
Map<String, String> runtimeDownloadRelocations = (Map<String, String>) ext.runtimeDownloadRelocations
|
||||||
List<String> runtimeDownloadedPrefixes = (List<String>) ext.runtimeDownloadedPrefixes
|
List<String> runtimeDownloadedPrefixes = (List<String>) ext.runtimeDownloadedPrefixes
|
||||||
File pluginOutDirectory = layout.projectDirectory.dir('../PluginOuts').asFile
|
File pluginOutDirectory = file(providers.gradleProperty('pluginOutDirectory').getOrElse('../PluginOuts'))
|
||||||
File legacyBukkitJar = layout.buildDirectory.file("libs/Iris-${project.version}.jar").get().asFile
|
File legacyBukkitJar = layout.buildDirectory.file("libs/Iris-${project.version}.jar").get().asFile
|
||||||
File bukkitArtifact = layout.buildDirectory.file("libs/${bukkitArtifactName}").get().asFile
|
File bukkitArtifact = layout.buildDirectory.file("libs/${bukkitArtifactName}").get().asFile
|
||||||
File consumerBukkitArtifact = layout.buildDirectory.file("consumer/${bukkitArtifactName}").get().asFile
|
File consumerBukkitArtifact = layout.buildDirectory.file("consumer/${bukkitArtifactName}").get().asFile
|
||||||
@@ -327,6 +327,9 @@ if (localVolmLibCheckout != null) {
|
|||||||
// moves. Without a local checkout the Exec tasks always run and the nested build decides what is stale.
|
// moves. Without a local checkout the Exec tasks always run and the nested build decides what is stale.
|
||||||
Closure<Void> declareVolmLibInputs = { Exec task ->
|
Closure<Void> declareVolmLibInputs = { Exec task ->
|
||||||
task.inputs.property('volmLibStamp', volmLibStamp)
|
task.inputs.property('volmLibStamp', volmLibStamp)
|
||||||
|
String nativeBillow = providers.gradleProperty('includeNativeBillow').getOrElse('false')
|
||||||
|
task.inputs.property('includeNativeBillow', nativeBillow)
|
||||||
|
task.environment('ORG_GRADLE_PROJECT_includeNativeBillow', nativeBillow)
|
||||||
if (localVolmLibCheckout == null) {
|
if (localVolmLibCheckout == null) {
|
||||||
task.outputs.upToDateWhen { false }
|
task.outputs.upToDateWhen { false }
|
||||||
}
|
}
|
||||||
@@ -781,9 +784,20 @@ tasks.register('irisDev', Copy) {
|
|||||||
dependsOn(':core:javadocJar')
|
dependsOn(':core:javadocJar')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int defaultTestForks = Math.min(8, Math.max(1, Runtime.runtime.availableProcessors().intdiv(2)))
|
||||||
|
int testForks = Math.max(1, Integer.parseInt(providers.gradleProperty('testForks')
|
||||||
|
.getOrElse(String.valueOf(defaultTestForks))))
|
||||||
|
|
||||||
allprojects {
|
allprojects {
|
||||||
apply plugin: 'java'
|
apply plugin: 'java'
|
||||||
|
|
||||||
|
tasks.withType(Test).configureEach {
|
||||||
|
int moduleForks = project.path.startsWith(':adapters:bukkit:') ? Math.min(2, testForks)
|
||||||
|
: project.path == ':probe' ? Math.min(4, testForks) : testForks
|
||||||
|
maxParallelForks = Math.min(gradle.startParameter.maxWorkerCount, moduleForks)
|
||||||
|
jvmArgs('-XX:ParallelGCThreads=2', '-XX:ConcGCThreads=1', '-XX:CICompilerCount=2')
|
||||||
|
}
|
||||||
|
|
||||||
java {
|
java {
|
||||||
toolchain {
|
toolchain {
|
||||||
languageVersion = JavaLanguageVersion.of(25)
|
languageVersion = JavaLanguageVersion.of(25)
|
||||||
@@ -979,9 +993,6 @@ project(':adapters:bukkit:nms:v26_3_R1') {
|
|||||||
}
|
}
|
||||||
sourceSets.test.compileClasspath += sourceSets.offline.output
|
sourceSets.test.compileClasspath += sourceSets.offline.output
|
||||||
sourceSets.test.runtimeClasspath += sourceSets.offline.output
|
sourceSets.test.runtimeClasspath += sourceSets.offline.output
|
||||||
tasks.named('test', Test).configure {
|
|
||||||
forkEvery = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
TaskProvider<Jar> offlineJar = tasks.register('offlineJar', Jar) {
|
TaskProvider<Jar> offlineJar = tasks.register('offlineJar', Jar) {
|
||||||
dependsOn(offlineClasses)
|
dependsOn(offlineClasses)
|
||||||
|
|||||||
@@ -170,12 +170,8 @@ dependencies {
|
|||||||
testImplementation(libs.multiverseCore)
|
testImplementation(libs.multiverseCore)
|
||||||
}
|
}
|
||||||
|
|
||||||
int coreTestForks = Math.max(1, Integer.parseInt(providers.gradleProperty('testForks')
|
|
||||||
.getOrElse(String.valueOf(Math.max(1, (int) (Runtime.runtime.availableProcessors() / 2))))))
|
|
||||||
|
|
||||||
tasks.named('test').configure {
|
tasks.named('test').configure {
|
||||||
maxHeapSize = '1g'
|
maxHeapSize = '1g'
|
||||||
maxParallelForks = coreTestForks
|
|
||||||
jvmArgs('-XX:+ExitOnOutOfMemoryError')
|
jvmArgs('-XX:+ExitOnOutOfMemoryError')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package art.arcane.iris.generation.decoration;
|
package art.arcane.iris.generation.decoration;
|
||||||
|
|
||||||
import art.arcane.iris.generation.block.IrisBlockData;
|
import art.arcane.iris.generation.block.IrisBlockData;
|
||||||
|
import art.arcane.iris.pack.loading.IrisData;
|
||||||
|
import art.arcane.iris.testsupport.KeyedBlockState;
|
||||||
import art.arcane.iris.generation.noise.IrisGeneratorStyle;
|
import art.arcane.iris.generation.noise.IrisGeneratorStyle;
|
||||||
import art.arcane.iris.generation.noise.NoiseStyle;
|
import art.arcane.iris.generation.noise.NoiseStyle;
|
||||||
import art.arcane.iris.generation.terrain.IrisMaterialPalette;
|
import art.arcane.iris.generation.terrain.IrisMaterialPalette;
|
||||||
@@ -17,11 +19,6 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertNull;
|
import static org.junit.Assert.assertNull;
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyDouble;
|
|
||||||
import static org.mockito.ArgumentMatchers.isNull;
|
|
||||||
import static org.mockito.Mockito.mock;
|
|
||||||
import static org.mockito.Mockito.when;
|
|
||||||
|
|
||||||
public class IrisOreBandsTest {
|
public class IrisOreBandsTest {
|
||||||
private static final RNG RNG_SOURCE = new RNG(77L);
|
private static final RNG RNG_SOURCE = new RNG(77L);
|
||||||
@@ -69,10 +66,8 @@ public class IrisOreBandsTest {
|
|||||||
@Test
|
@Test
|
||||||
public void constantChanceMatchesSamplingTheFlatField() {
|
public void constantChanceMatchesSamplingTheFlatField() {
|
||||||
IrisOreGenerator flat = new IrisOreGenerator();
|
IrisOreGenerator flat = new IrisOreGenerator();
|
||||||
IrisMaterialPalette palette = mock(IrisMaterialPalette.class);
|
NativeBlockState ore = new KeyedBlockState("minecraft:iron_ore");
|
||||||
NativeBlockState ore = mock(NativeBlockState.class);
|
IrisMaterialPalette palette = new FixedPalette(ore);
|
||||||
when(palette.getPalette()).thenReturn(new KList<>(mock(IrisBlockData.class)));
|
|
||||||
when(palette.get(any(), anyDouble(), anyDouble(), anyDouble(), isNull())).thenReturn(ore);
|
|
||||||
IrisGeneratorStyle style = new IrisGeneratorStyle(NoiseStyle.FLAT).setZoom(2.5D).setExponent(1.3D)
|
IrisGeneratorStyle style = new IrisGeneratorStyle(NoiseStyle.FLAT).setZoom(2.5D).setExponent(1.3D)
|
||||||
.setFracture(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).setMultiplier(9D));
|
.setFracture(new IrisGeneratorStyle(NoiseStyle.SIMPLEX).setMultiplier(9D));
|
||||||
flat.setPalette(palette).setChanceStyle(style).setRange(new IrisRange(0, 100));
|
flat.setPalette(palette).setChanceStyle(style).setRange(new IrisRange(0, 100));
|
||||||
@@ -101,20 +96,9 @@ public class IrisOreBandsTest {
|
|||||||
|
|
||||||
private static IrisOreGenerator generator(double min, double max, double threshold, long seed) {
|
private static IrisOreGenerator generator(double min, double max, double threshold, long seed) {
|
||||||
IrisOreGenerator generator = new IrisOreGenerator();
|
IrisOreGenerator generator = new IrisOreGenerator();
|
||||||
IrisMaterialPalette palette = mock(IrisMaterialPalette.class);
|
NativeBlockState state = new KeyedBlockState("minecraft:iron_ore");
|
||||||
NativeBlockState state = mock(NativeBlockState.class);
|
IrisMaterialPalette palette = new FixedPalette(state);
|
||||||
when(palette.getPalette()).thenReturn(new KList<>(mock(IrisBlockData.class)));
|
IrisGeneratorStyle style = new FixedChanceStyle(new CoordinateChance(seed));
|
||||||
when(palette.get(any(), anyDouble(), anyDouble(), anyDouble(), isNull())).thenReturn(state);
|
|
||||||
CNG chance = mock(CNG.class);
|
|
||||||
when(chance.noise(anyDouble(), anyDouble(), anyDouble())).thenAnswer(invocation -> {
|
|
||||||
long hash = seed;
|
|
||||||
hash = hash * 31 + Double.doubleToLongBits(invocation.getArgument(0));
|
|
||||||
hash = hash * 31 + Double.doubleToLongBits(invocation.getArgument(1));
|
|
||||||
hash = hash * 31 + Double.doubleToLongBits(invocation.getArgument(2));
|
|
||||||
return new Random(hash).nextDouble();
|
|
||||||
});
|
|
||||||
IrisGeneratorStyle style = mock(IrisGeneratorStyle.class);
|
|
||||||
when(style.create(any(), isNull())).thenReturn(chance);
|
|
||||||
return generator.setPalette(palette).setChanceStyle(style).setThreshold(threshold)
|
return generator.setPalette(palette).setChanceStyle(style).setThreshold(threshold)
|
||||||
.setRange(new IrisRange(min, max));
|
.setRange(new IrisRange(min, max));
|
||||||
}
|
}
|
||||||
@@ -131,4 +115,49 @@ public class IrisOreBandsTest {
|
|||||||
assertSame(scan(ores, 0, 58, 0), bands.generate(0, 58, 0, RNG_SOURCE, null));
|
assertSame(scan(ores, 0, 58, 0), bands.generate(0, 58, 0, RNG_SOURCE, null));
|
||||||
assertNull(bands.contains(59) ? bands.generate(0, 59, 0, RNG_SOURCE, null) : null);
|
assertNull(bands.contains(59) ? bands.generate(0, 59, 0, RNG_SOURCE, null) : null);
|
||||||
}
|
}
|
||||||
|
private static final class FixedPalette extends IrisMaterialPalette {
|
||||||
|
private final NativeBlockState state;
|
||||||
|
|
||||||
|
private FixedPalette(NativeBlockState state) {
|
||||||
|
this.state = state;
|
||||||
|
setPalette(new KList<>(new IrisBlockData()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NativeBlockState get(RNG rng, double x, double y, double z, IrisData data) {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class FixedChanceStyle extends IrisGeneratorStyle {
|
||||||
|
private final CNG chance;
|
||||||
|
|
||||||
|
private FixedChanceStyle(CNG chance) {
|
||||||
|
this.chance = chance;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public CNG create(RNG rng, IrisData data) {
|
||||||
|
return chance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CoordinateChance extends CNG {
|
||||||
|
private final long seed;
|
||||||
|
|
||||||
|
private CoordinateChance(long seed) {
|
||||||
|
super(new RNG(seed));
|
||||||
|
this.seed = seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double noise(double x, double y, double z) {
|
||||||
|
long hash = seed;
|
||||||
|
hash = hash * 31 + Double.doubleToLongBits(x);
|
||||||
|
hash = hash * 31 + Double.doubleToLongBits(y);
|
||||||
|
hash = hash * 31 + Double.doubleToLongBits(z);
|
||||||
|
return new Random(hash).nextDouble();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+28
-18
@@ -11,6 +11,7 @@ import java.security.NoSuchAlgorithmException;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.HexFormat;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -27,7 +28,7 @@ public class HydrologyPlanHashTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void fullTilePlansAreByteIdentical() {
|
public void fullTilePlansAreByteIdentical() {
|
||||||
StringBuilder canonical = new StringBuilder();
|
MessageDigest canonical = sha256();
|
||||||
int courses = 0;
|
int courses = 0;
|
||||||
int columns = 0;
|
int columns = 0;
|
||||||
for (long seed : new long[]{77L, 19L}) {
|
for (long seed : new long[]{77L, 19L}) {
|
||||||
@@ -37,16 +38,16 @@ public class HydrologyPlanHashTest {
|
|||||||
HydrologyTile tile = planner.plan(key);
|
HydrologyTile tile = planner.plan(key);
|
||||||
courses += tile.courses().size();
|
courses += tile.courses().size();
|
||||||
columns += tile.footprint().columns().size();
|
columns += tile.footprint().columns().size();
|
||||||
canonical.append(render(tile));
|
render(tile, canonical);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
assertTrue("planned courses: " + courses, courses > 0);
|
assertTrue("planned courses: " + courses, courses > 0);
|
||||||
assertTrue("planned columns: " + columns, columns > 0);
|
assertTrue("planned columns: " + columns, columns > 0);
|
||||||
assertEquals(EXPECTED_PLAN_DIGEST, digest(canonical.toString()));
|
assertEquals(EXPECTED_PLAN_DIGEST, HexFormat.of().formatHex(canonical.digest()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String render(HydrologyTile tile) {
|
private static void render(HydrologyTile tile, MessageDigest digest) {
|
||||||
StringBuilder out = new StringBuilder();
|
CanonicalDigest out = new CanonicalDigest(digest);
|
||||||
out.append("tile ").append(tile.key()).append('\n');
|
out.append("tile ").append(tile.key()).append('\n');
|
||||||
out.append("seed ").append(tile.worldSeed()).append('\n');
|
out.append("seed ").append(tile.worldSeed()).append('\n');
|
||||||
out.append("fingerprint ").append(tile.settingsFingerprint()).append('\n');
|
out.append("fingerprint ").append(tile.settingsFingerprint()).append('\n');
|
||||||
@@ -78,23 +79,14 @@ public class HydrologyPlanHashTest {
|
|||||||
for (Map.Entry<Long, HydrologyColumnSample> column : columns) {
|
for (Map.Entry<Long, HydrologyColumnSample> column : columns) {
|
||||||
out.append("column ").append(column.getKey()).append(' ').append(column.getValue()).append('\n');
|
out.append("column ").append(column.getKey()).append(' ').append(column.getValue()).append('\n');
|
||||||
}
|
}
|
||||||
return out.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String digest(String value) {
|
private static MessageDigest sha256() {
|
||||||
MessageDigest sha256;
|
|
||||||
try {
|
try {
|
||||||
sha256 = MessageDigest.getInstance("SHA-256");
|
return MessageDigest.getInstance("SHA-256");
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException failure) {
|
||||||
throw new IllegalStateException(e);
|
throw new IllegalStateException(failure);
|
||||||
}
|
}
|
||||||
byte[] hash = sha256.digest(value.getBytes(StandardCharsets.UTF_8));
|
|
||||||
StringBuilder hex = new StringBuilder(hash.length * 2);
|
|
||||||
for (byte b : hash) {
|
|
||||||
hex.append(Character.forDigit((b >> 4) & 0xF, 16));
|
|
||||||
hex.append(Character.forDigit(b & 0xF, 16));
|
|
||||||
}
|
|
||||||
return hex.toString();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static HydrologyTerrainSampler rollingCoast(int coastX) {
|
private static HydrologyTerrainSampler rollingCoast(int coastX) {
|
||||||
@@ -230,4 +222,22 @@ public class HydrologyPlanHashTest {
|
|||||||
HydrologyPlannerSettings.SurfacePolicyBounds.NONE
|
HydrologyPlannerSettings.SurfacePolicyBounds.NONE
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class CanonicalDigest {
|
||||||
|
private final MessageDigest digest;
|
||||||
|
|
||||||
|
private CanonicalDigest(MessageDigest digest) {
|
||||||
|
this.digest = digest;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CanonicalDigest append(Object value) {
|
||||||
|
digest.update(String.valueOf(value).getBytes(StandardCharsets.UTF_8));
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private CanonicalDigest append(char value) {
|
||||||
|
digest.update((byte) value);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import static org.junit.Assert.assertTrue;
|
|||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.Mockito.withSettings;
|
||||||
|
|
||||||
public class CaveTerrainSnapshotTest {
|
public class CaveTerrainSnapshotTest {
|
||||||
private static final int SECTIONS = 5;
|
private static final int SECTIONS = 5;
|
||||||
@@ -176,7 +177,7 @@ public class CaveTerrainSnapshotTest {
|
|||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private static MantleChunk<Matter> chunk(Matter[] sections) {
|
private static MantleChunk<Matter> chunk(Matter[] sections) {
|
||||||
MantleChunk<Matter> chunk = mock(MantleChunk.class);
|
MantleChunk<Matter> chunk = mock(MantleChunk.class, withSettings().stubOnly());
|
||||||
when(chunk.sectionCount()).thenReturn(sections.length);
|
when(chunk.sectionCount()).thenReturn(sections.length);
|
||||||
when(chunk.exists(anyInt())).thenAnswer(call -> {
|
when(chunk.exists(anyInt())).thenAnswer(call -> {
|
||||||
int section = call.getArgument(0);
|
int section = call.getArgument(0);
|
||||||
|
|||||||
+41
-46
@@ -20,6 +20,7 @@ import art.arcane.volmlib.util.math.RNG;
|
|||||||
import art.arcane.volmlib.util.matter.Matter;
|
import art.arcane.volmlib.util.matter.Matter;
|
||||||
import art.arcane.volmlib.util.matter.MatterCavern;
|
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||||
import art.arcane.volmlib.util.matter.MatterSlice;
|
import art.arcane.volmlib.util.matter.MatterSlice;
|
||||||
|
import art.arcane.volmlib.util.matter.slices.CavernMatter;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
@@ -52,6 +53,7 @@ import static org.mockito.ArgumentMatchers.anyDouble;
|
|||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.withSettings;
|
||||||
|
|
||||||
public class IrisCaveCarver3DNearParityTest {
|
public class IrisCaveCarver3DNearParityTest {
|
||||||
private static final long CARVE_SEED = -54_777_863_784_492_918L;
|
private static final long CARVE_SEED = -54_777_863_784_492_918L;
|
||||||
@@ -136,7 +138,7 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
createProfile(false, false).setDetailWeight(0D).setAllowFluid(false));
|
createProfile(false, false).setDetailWeight(0D).setAllowFluid(false));
|
||||||
AtomicInteger calls = new AtomicInteger();
|
AtomicInteger calls = new AtomicInteger();
|
||||||
Set<Integer> sampledY = new HashSet<>();
|
Set<Integer> sampledY = new HashSet<>();
|
||||||
CNG density = mock(CNG.class);
|
CNG density = mock(CNG.class, withSettings().stubOnly());
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
calls.incrementAndGet();
|
calls.incrementAndGet();
|
||||||
double x = invocation.getArgument(0);
|
double x = invocation.getArgument(0);
|
||||||
@@ -193,14 +195,14 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void expressionStylesKeepTheOriginalSamplingAndCarvingBehavior() throws Exception {
|
public void expressionStylesKeepTheOriginalSamplingAndCarvingBehavior() throws Exception {
|
||||||
CNG density = mock(CNG.class);
|
CNG density = mock(CNG.class, withSettings().stubOnly());
|
||||||
AtomicInteger calls = new AtomicInteger();
|
AtomicInteger calls = new AtomicInteger();
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
calls.incrementAndGet();
|
calls.incrementAndGet();
|
||||||
double y = invocation.getArgument(1);
|
double y = invocation.getArgument(1);
|
||||||
return Math.sin(y * 0.2D);
|
return Math.sin(y * 0.2D);
|
||||||
}).when(density).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
|
}).when(density).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
|
||||||
IrisGeneratorStyle style = mock(IrisGeneratorStyle.class);
|
IrisGeneratorStyle style = mock(IrisGeneratorStyle.class, withSettings().stubOnly());
|
||||||
doReturn("density_expression").when(style).getExpression();
|
doReturn("density_expression").when(style).getExpression();
|
||||||
doReturn(density).when(style).create(any(RNG.class), any(IrisData.class));
|
doReturn(density).when(style).create(any(RNG.class), any(IrisData.class));
|
||||||
IrisCaveProfile profile = createProfile(false, false).setBaseDensityStyle(style)
|
IrisCaveProfile profile = createProfile(false, false).setBaseDensityStyle(style)
|
||||||
@@ -240,10 +242,10 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
.setModules(new KList<>(new IrisCaveFieldModule(new IrisGeneratorStyle(NoiseStyle.FLAT),
|
.setModules(new KList<>(new IrisCaveFieldModule(new IrisGeneratorStyle(NoiseStyle.FLAT),
|
||||||
1D, 0D, new IrisRange(5D, 6D), false)));
|
1D, 0D, new IrisRange(5D, 6D), false)));
|
||||||
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile);
|
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile);
|
||||||
CNG base = mock(CNG.class);
|
CNG base = mock(CNG.class, withSettings().stubOnly());
|
||||||
doReturn(1D).when(base).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
|
doReturn(1D).when(base).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
|
||||||
baseDensityField.set(carver, base);
|
baseDensityField.set(carver, base);
|
||||||
CNG module = mock(CNG.class);
|
CNG module = mock(CNG.class, withSettings().stubOnly());
|
||||||
doReturn(-1D).when(module).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
|
doReturn(-1D).when(module).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
|
||||||
moduleDensityField.set(((CaveFieldModuleState[]) modulesField.get(carver))[0], module);
|
moduleDensityField.set(((CaveFieldModuleState[]) modulesField.get(carver))[0], module);
|
||||||
WriterCapture capture = createWriterCapture(128);
|
WriterCapture capture = createWriterCapture(128);
|
||||||
@@ -307,7 +309,7 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
WriterCapture capture = createWriterCapture(128);
|
WriterCapture capture = createWriterCapture(128);
|
||||||
if (fail) {
|
if (fail) {
|
||||||
AtomicInteger samples = new AtomicInteger();
|
AtomicInteger samples = new AtomicInteger();
|
||||||
CNG density = mock(CNG.class);
|
CNG density = mock(CNG.class, withSettings().stubOnly());
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
if (samples.incrementAndGet() > 300) {
|
if (samples.incrementAndGet() > 300) {
|
||||||
throw new IllegalStateException("Density sample failed");
|
throw new IllegalStateException("Density sample failed");
|
||||||
@@ -380,7 +382,7 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
.setDetailWeight(0D)
|
.setDetailWeight(0D)
|
||||||
.setDensityThreshold(new IrisStyledRange(-2D, -2D, new IrisGeneratorStyle(NoiseStyle.FLAT)));
|
.setDensityThreshold(new IrisStyledRange(-2D, -2D, new IrisGeneratorStyle(NoiseStyle.FLAT)));
|
||||||
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile);
|
IrisCaveCarver3D carver = new IrisCaveCarver3D(engine, profile);
|
||||||
CNG detail = mock(CNG.class);
|
CNG detail = mock(CNG.class, withSettings().stubOnly());
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
throw new AssertionError("Rejected voxel sampled an aquifer");
|
throw new AssertionError("Rejected voxel sampled an aquifer");
|
||||||
}).when(detail).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
|
}).when(detail).noiseFastSigned3D(anyDouble(), anyDouble(), anyDouble());
|
||||||
@@ -1343,10 +1345,10 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Engine createEngine(int worldHeight, int sampledHeight) {
|
private Engine createEngine(int worldHeight, int sampledHeight) {
|
||||||
Engine engine = mock(Engine.class);
|
Engine engine = mock(Engine.class, withSettings().stubOnly());
|
||||||
IrisData data = mock(IrisData.class);
|
IrisData data = mock(IrisData.class, withSettings().stubOnly());
|
||||||
IrisDimension dimension = mock(IrisDimension.class);
|
IrisDimension dimension = mock(IrisDimension.class, withSettings().stubOnly());
|
||||||
SeedManager seedManager = mock(SeedManager.class);
|
SeedManager seedManager = mock(SeedManager.class, withSettings().stubOnly());
|
||||||
EngineMetrics metrics = new EngineMetrics(16);
|
EngineMetrics metrics = new EngineMetrics(16);
|
||||||
IrisWorld world = IrisWorld.builder().minHeight(0).maxHeight(worldHeight).build();
|
IrisWorld world = IrisWorld.builder().minHeight(0).maxHeight(worldHeight).build();
|
||||||
|
|
||||||
@@ -1467,15 +1469,15 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private WriterCapture createWriterCapture(int worldHeight) {
|
private WriterCapture createWriterCapture(int worldHeight) {
|
||||||
MantleWriter writer = mock(MantleWriter.class);
|
MantleWriter writer = mock(MantleWriter.class, withSettings().stubOnly());
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
Mantle<Matter> mantle = mock(Mantle.class);
|
Mantle<Matter> mantle = mock(Mantle.class, withSettings().stubOnly());
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
MantleChunk<Matter> chunk = mock(MantleChunk.class);
|
MantleChunk<Matter> chunk = mock(MantleChunk.class, withSettings().stubOnly());
|
||||||
Map<Integer, Matter> sections = new HashMap<>();
|
Map<Integer, Matter> sections = new HashMap<>();
|
||||||
Map<Integer, Map<Integer, MatterCavern>> sectionCells = new HashMap<>();
|
|
||||||
Set<String> carvedCells = new HashSet<>();
|
Set<String> carvedCells = new HashSet<>();
|
||||||
Map<String, Byte> carvedLiquids = new HashMap<>();
|
Map<String, Byte> carvedLiquids = new HashMap<>();
|
||||||
|
WriterCapture capture = new WriterCapture(writer, carvedCells, carvedLiquids);
|
||||||
|
|
||||||
doReturn(mantle).when(writer).getMantle();
|
doReturn(mantle).when(writer).getMantle();
|
||||||
doReturn(worldHeight).when(mantle).getWorldHeight();
|
doReturn(worldHeight).when(mantle).getWorldHeight();
|
||||||
@@ -1488,41 +1490,19 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
return section;
|
return section;
|
||||||
}
|
}
|
||||||
|
|
||||||
Matter created = createSection(sectionIndex, sectionCells, carvedCells, carvedLiquids);
|
Matter created = createSection(sectionIndex, capture);
|
||||||
sections.put(sectionIndex, created);
|
sections.put(sectionIndex, created);
|
||||||
return created;
|
return created;
|
||||||
}).when(chunk).getOrCreate(anyInt());
|
}).when(chunk).getOrCreate(anyInt());
|
||||||
|
|
||||||
return new WriterCapture(writer, carvedCells, carvedLiquids);
|
return capture;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Matter createSection(int sectionIndex, Map<Integer, Map<Integer, MatterCavern>> sectionCells, Set<String> carvedCells, Map<String, Byte> carvedLiquids) {
|
private Matter createSection(int sectionIndex, WriterCapture capture) {
|
||||||
Matter matter = mock(Matter.class);
|
Matter matter = mock(Matter.class, withSettings().stubOnly());
|
||||||
@SuppressWarnings("unchecked")
|
MatterSlice<MatterCavern> slice = new CapturingCavernMatter(sectionIndex, capture);
|
||||||
MatterSlice<MatterCavern> slice = mock(MatterSlice.class);
|
|
||||||
Map<Integer, MatterCavern> localCells = sectionCells.computeIfAbsent(sectionIndex, key -> new HashMap<>());
|
|
||||||
|
|
||||||
doReturn(slice).when(matter).slice(MatterCavern.class);
|
doReturn(slice).when(matter).slice(MatterCavern.class);
|
||||||
doReturn(slice).when(matter).getSlice(MatterCavern.class);
|
doReturn(slice).when(matter).getSlice(MatterCavern.class);
|
||||||
doAnswer(invocation -> {
|
|
||||||
int localX = invocation.getArgument(0);
|
|
||||||
int localY = invocation.getArgument(1);
|
|
||||||
int localZ = invocation.getArgument(2);
|
|
||||||
return localCells.get(packLocal(localX, localY, localZ));
|
|
||||||
}).when(slice).get(anyInt(), anyInt(), anyInt());
|
|
||||||
doAnswer(invocation -> {
|
|
||||||
int localX = invocation.getArgument(0);
|
|
||||||
int localY = invocation.getArgument(1);
|
|
||||||
int localZ = invocation.getArgument(2);
|
|
||||||
MatterCavern value = invocation.getArgument(3);
|
|
||||||
localCells.put(packLocal(localX, localY, localZ), value);
|
|
||||||
int worldY = (sectionIndex << 4) + localY;
|
|
||||||
String cellKey = cellKey(localX, worldY, localZ);
|
|
||||||
carvedCells.add(cellKey);
|
|
||||||
carvedLiquids.put(cellKey, value.getLiquid());
|
|
||||||
return null;
|
|
||||||
}).when(slice).set(anyInt(), anyInt(), anyInt(), any(MatterCavern.class));
|
|
||||||
|
|
||||||
return matter;
|
return matter;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1649,10 +1629,6 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
return (value * 2D) - 1D;
|
return (value * 2D) - 1D;
|
||||||
}
|
}
|
||||||
|
|
||||||
private int packLocal(int x, int y, int z) {
|
|
||||||
return (x << 8) | (y << 4) | z;
|
|
||||||
}
|
|
||||||
|
|
||||||
private String cellKey(int x, int y, int z) {
|
private String cellKey(int x, int y, int z) {
|
||||||
return x + ":" + y + ":" + z;
|
return x + ":" + y + ":" + z;
|
||||||
}
|
}
|
||||||
@@ -1752,6 +1728,25 @@ public class IrisCaveCarver3DNearParityTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private final class CapturingCavernMatter extends CavernMatter {
|
||||||
|
private final int sectionIndex;
|
||||||
|
private final WriterCapture capture;
|
||||||
|
|
||||||
|
private CapturingCavernMatter(int sectionIndex, WriterCapture capture) {
|
||||||
|
super(16, 16, 16);
|
||||||
|
this.sectionIndex = sectionIndex;
|
||||||
|
this.capture = capture;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(int x, int y, int z, MatterCavern value) {
|
||||||
|
super.set(x, y, z, value);
|
||||||
|
String cell = cellKey(x, (sectionIndex << 4) + y, z);
|
||||||
|
capture.carvedCells.add(cell);
|
||||||
|
capture.carvedLiquids.put(cell, value.getLiquid());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class WriterCapture {
|
private static final class WriterCapture {
|
||||||
private final MantleWriter writer;
|
private final MantleWriter writer;
|
||||||
private final Set<String> carvedCells;
|
private final Set<String> carvedCells;
|
||||||
|
|||||||
@@ -15,39 +15,43 @@ import art.arcane.volmlib.util.collection.KList;
|
|||||||
import art.arcane.volmlib.util.collection.KMap;
|
import art.arcane.volmlib.util.collection.KMap;
|
||||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||||
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
||||||
|
import art.arcane.volmlib.util.mantle.runtime.MantleDataAdapter;
|
||||||
|
import art.arcane.volmlib.util.math.Position2;
|
||||||
import art.arcane.volmlib.util.matter.Matter;
|
import art.arcane.volmlib.util.matter.Matter;
|
||||||
import art.arcane.volmlib.util.matter.MatterCavern;
|
import art.arcane.volmlib.util.matter.MatterCavern;
|
||||||
import art.arcane.volmlib.util.stream.ProceduralStream;
|
import art.arcane.volmlib.util.stream.ProceduralStream;
|
||||||
|
import art.arcane.volmlib.util.stream.interpolation.Interpolated;
|
||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.MockedStatic;
|
import org.mockito.MockedStatic;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.lang.reflect.InvocationHandler;
|
||||||
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Random;
|
import java.util.Random;
|
||||||
|
|
||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyBoolean;
|
|
||||||
import static org.mockito.ArgumentMatchers.anyDouble;
|
import static org.mockito.ArgumentMatchers.anyDouble;
|
||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
import static org.mockito.ArgumentMatchers.anyString;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.CALLS_REAL_METHODS;
|
|
||||||
import static org.mockito.Mockito.doAnswer;
|
|
||||||
import static org.mockito.Mockito.doReturn;
|
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.mockStatic;
|
import static org.mockito.Mockito.mockStatic;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.Mockito.withSettings;
|
||||||
|
|
||||||
public class EngineBiomeColumnTest {
|
public class EngineBiomeColumnTest {
|
||||||
private static final int SAMPLES = 96;
|
private static final int SAMPLES = 96;
|
||||||
|
private static MantleDataAdapter<Matter> chunkAdapter;
|
||||||
|
private static final EnginePlatformHooks PLATFORM_HOOKS = new EnginePlatformHooks() {};
|
||||||
|
|
||||||
@BeforeClass
|
@BeforeClass
|
||||||
public static void initializeMantleBlockState() throws Exception {
|
public static void initializeMantleBlockState() throws Exception {
|
||||||
NativeBlockState air = mock(NativeBlockState.class);
|
NativeBlockState air = mock(NativeBlockState.class, withSettings().stubOnly());
|
||||||
try (MockedStatic<B> blocks = mockStatic(B.class)) {
|
try (MockedStatic<B> blocks = mockStatic(B.class)) {
|
||||||
blocks.when(() -> B.getState("AIR")).thenReturn(air);
|
blocks.when(() -> B.getState("AIR")).thenReturn(air);
|
||||||
Class.forName(EngineMantle.class.getName());
|
Class.forName(EngineMantle.class.getName());
|
||||||
|
chunkAdapter = IrisEngineMantle.createRuntimeDataAdapter(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,20 +83,18 @@ public class EngineBiomeColumnTest {
|
|||||||
IrisBiome custom = biome("iris:custom_cave", 0);
|
IrisBiome custom = biome("iris:custom_cave", 0);
|
||||||
IrisBiome configured = biome("iris:configured_cave", 0);
|
IrisBiome configured = biome("iris:configured_cave", 0);
|
||||||
|
|
||||||
ProceduralStream<IrisBiome> surfaceStream = mock(ProceduralStream.class);
|
Interpolated<IrisBiome> biomeInterpolation = Interpolated.of(biome -> 0D, value -> null);
|
||||||
when(surfaceStream.get(anyDouble(), anyDouble())).thenAnswer(call ->
|
Interpolated<IrisRegion> regionInterpolation = Interpolated.of(region -> 0D, value -> null);
|
||||||
surfaces[pick(salt, call.getArgument(0), call.getArgument(1), 1, surfaces.length)]);
|
ProceduralStream<IrisBiome> surfaceStream = ProceduralStream.of((x, z) ->
|
||||||
ProceduralStream<IrisBiome> caveStream = mock(ProceduralStream.class);
|
surfaces[pick(salt, x, z, 1, surfaces.length)], biomeInterpolation);
|
||||||
when(caveStream.get(anyDouble(), anyDouble())).thenAnswer(call ->
|
ProceduralStream<IrisBiome> caveStream = ProceduralStream.of((x, z) ->
|
||||||
caves[pick(salt, call.getArgument(0), call.getArgument(1), 2, caves.length)]);
|
caves[pick(salt, x, z, 2, caves.length)], biomeInterpolation);
|
||||||
ProceduralStream<IrisRegion> regionStream = mock(ProceduralStream.class);
|
ProceduralStream<IrisRegion> regionStream = ProceduralStream.of((x, z) ->
|
||||||
when(regionStream.get(anyDouble(), anyDouble())).thenAnswer(call ->
|
regions[pick(salt, x, z, 3, regions.length)], regionInterpolation);
|
||||||
regions[pick(salt, call.getArgument(0), call.getArgument(1), 3, regions.length)]);
|
ProceduralStream<Double> heightStream = ProceduralStream.of((x, z) ->
|
||||||
ProceduralStream<Double> heightStream = mock(ProceduralStream.class);
|
(double) (40 + pick(salt, x, z, 4, 300)), Interpolated.DOUBLE);
|
||||||
when(heightStream.get(anyDouble(), anyDouble())).thenAnswer(call ->
|
|
||||||
(double) (40 + pick(salt, call.getArgument(0), call.getArgument(1), 4, 300)));
|
|
||||||
|
|
||||||
IrisComplex complex = mock(IrisComplex.class);
|
IrisComplex complex = mock(IrisComplex.class, withSettings().stubOnly());
|
||||||
when(complex.getTrueBiomeStream()).thenReturn(surfaceStream);
|
when(complex.getTrueBiomeStream()).thenReturn(surfaceStream);
|
||||||
when(complex.getCaveBiomeStream()).thenReturn(caveStream);
|
when(complex.getCaveBiomeStream()).thenReturn(caveStream);
|
||||||
when(complex.getRegionStream()).thenReturn(regionStream);
|
when(complex.getRegionStream()).thenReturn(regionStream);
|
||||||
@@ -100,27 +102,23 @@ public class EngineBiomeColumnTest {
|
|||||||
when(complex.isTerrain3DSurface(anyInt(), anyInt(), anyInt())).thenAnswer(call ->
|
when(complex.isTerrain3DSurface(anyInt(), anyInt(), anyInt())).thenAnswer(call ->
|
||||||
pick(salt, (int) call.getArgument(0) * 31 + (int) call.getArgument(1), (int) call.getArgument(2), 5, 9) == 0);
|
pick(salt, (int) call.getArgument(0) * 31 + (int) call.getArgument(1), (int) call.getArgument(2), 5, 9) == 0);
|
||||||
|
|
||||||
Mantle<Matter> mantle = mock(Mantle.class);
|
Mantle<Matter> mantle = mock(Mantle.class, withSettings().stubOnly());
|
||||||
when(mantle.getWorldHeight()).thenReturn(384);
|
when(mantle.getWorldHeight()).thenReturn(384);
|
||||||
when(mantle.getLoadedRegions()).thenReturn(new KMap<>());
|
when(mantle.getLoadedRegions()).thenReturn(new KMap<>());
|
||||||
when(mantle.hasTectonicPlate(anyInt(), anyInt())).thenReturn(true);
|
when(mantle.hasTectonicPlate(anyInt(), anyInt())).thenReturn(true);
|
||||||
when(mantle.get(anyInt(), anyInt(), anyInt(), eq(MatterCavern.class))).thenAnswer(call ->
|
when(mantle.get(anyInt(), anyInt(), anyInt(), eq(MatterCavern.class))).thenAnswer(call ->
|
||||||
cavern(salt, call.getArgument(0), call.getArgument(1), call.getArgument(2)));
|
cavern(salt, call.getArgument(0), call.getArgument(1), call.getArgument(2)));
|
||||||
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call -> {
|
when(mantle.useChunk(anyInt(), anyInt())).thenAnswer(call ->
|
||||||
int chunkX = call.getArgument(0);
|
new CoordinateChunk(salt, new Position2(call.getArgument(0), call.getArgument(1))).use());
|
||||||
int chunkZ = call.getArgument(1);
|
EngineMantle engineMantle = (EngineMantle) Proxy.newProxyInstance(EngineMantle.class.getClassLoader(),
|
||||||
MantleChunk<Matter> chunk = mock(MantleChunk.class);
|
new Class<?>[]{EngineMantle.class}, (proxy, method, arguments) -> switch (method.getName()) {
|
||||||
when(chunk.get(anyInt(), anyInt(), anyInt(), eq(MatterCavern.class))).thenAnswer(read -> cavern(salt,
|
case "getMantle" -> mantle;
|
||||||
chunkX * 16 + (int) read.getArgument(0), read.getArgument(1), chunkZ * 16 + (int) read.getArgument(2)));
|
case "getHighest" -> 20 + pick(salt, (int) arguments[0], (int) arguments[1], 7, 330);
|
||||||
return chunk;
|
default -> throw new UnsupportedOperationException(method.getName());
|
||||||
});
|
});
|
||||||
EngineMantle engineMantle = mock(EngineMantle.class);
|
|
||||||
when(engineMantle.getMantle()).thenReturn(mantle);
|
|
||||||
doAnswer(call -> 20 + pick(salt, (int) call.getArgument(0), (int) call.getArgument(1), 7, 330))
|
|
||||||
.when(engineMantle).getHighest(anyInt(), anyInt(), any(IrisData.class), anyBoolean());
|
|
||||||
|
|
||||||
IrisData data = mock(IrisData.class);
|
IrisData data = mock(IrisData.class, withSettings().stubOnly());
|
||||||
ResourceLoader<IrisBiome> loader = mock(ResourceLoader.class);
|
ResourceLoader<IrisBiome> loader = mock(ResourceLoader.class, withSettings().stubOnly());
|
||||||
when(data.getBiomeLoader()).thenReturn(loader);
|
when(data.getBiomeLoader()).thenReturn(loader);
|
||||||
when(loader.load(anyString())).thenReturn(null);
|
when(loader.load(anyString())).thenReturn(null);
|
||||||
when(loader.load("iris:custom_cave")).thenReturn(custom);
|
when(loader.load("iris:custom_cave")).thenReturn(custom);
|
||||||
@@ -136,15 +134,35 @@ public class EngineBiomeColumnTest {
|
|||||||
dimension.setCarving(new KList<>(List.of(root)));
|
dimension.setCarving(new KList<>(List.of(root)));
|
||||||
}
|
}
|
||||||
|
|
||||||
EnginePlatformHooks hooks = mock(EnginePlatformHooks.class);
|
IrisWorld world = IrisWorld.builder().minHeight(-64).maxHeight(320).build();
|
||||||
Engine engine = mock(Engine.class, CALLS_REAL_METHODS);
|
return (Engine) Proxy.newProxyInstance(Engine.class.getClassLoader(), new Class<?>[]{Engine.class},
|
||||||
doReturn(complex).when(engine).getComplex();
|
(proxy, method, arguments) -> switch (method.getName()) {
|
||||||
doReturn(engineMantle).when(engine).getMantle();
|
case "getComplex" -> complex;
|
||||||
doReturn(hooks).when(engine).getPlatformHooks();
|
case "getMantle" -> engineMantle;
|
||||||
doReturn(data).when(engine).getData();
|
case "getPlatformHooks" -> PLATFORM_HOOKS;
|
||||||
doReturn(dimension).when(engine).getDimension();
|
case "getData" -> data;
|
||||||
doReturn(IrisWorld.builder().minHeight(-64).maxHeight(320).build()).when(engine).getWorld();
|
case "getDimension" -> dimension;
|
||||||
return engine;
|
case "getWorld" -> world;
|
||||||
|
default -> InvocationHandler.invokeDefault(proxy, method, arguments);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final class CoordinateChunk extends MantleChunk<Matter> {
|
||||||
|
private final long salt;
|
||||||
|
private final Position2 coordinates;
|
||||||
|
|
||||||
|
private CoordinateChunk(long salt, Position2 coordinates) {
|
||||||
|
super(24, coordinates.getX(), coordinates.getZ(), chunkAdapter, null);
|
||||||
|
this.salt = salt;
|
||||||
|
this.coordinates = coordinates;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public <T> T get(int x, int y, int z, Class<T> type) {
|
||||||
|
return type == MatterCavern.class
|
||||||
|
? type.cast(cavern(salt, coordinates.getX() * 16 + x, y, coordinates.getZ() * 16 + z))
|
||||||
|
: null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static MatterCavern cavern(long salt, int x, int y, int z) {
|
private static MatterCavern cavern(long salt, int x, int y, int z) {
|
||||||
|
|||||||
+24
-10
@@ -3,6 +3,8 @@ package art.arcane.iris.generation.stage;
|
|||||||
import art.arcane.iris.testsupport.KeyedBlockState;
|
import art.arcane.iris.testsupport.KeyedBlockState;
|
||||||
|
|
||||||
import art.arcane.iris.generation.runtime.Engine;
|
import art.arcane.iris.generation.runtime.Engine;
|
||||||
|
import art.arcane.iris.generation.biome.IrisBiome;
|
||||||
|
import art.arcane.iris.generation.terrain.IrisRegion;
|
||||||
import art.arcane.iris.generation.decoration.IrisDepositGenerator;
|
import art.arcane.iris.generation.decoration.IrisDepositGenerator;
|
||||||
import art.arcane.iris.generation.decoration.IrisDepositHeightDistribution;
|
import art.arcane.iris.generation.decoration.IrisDepositHeightDistribution;
|
||||||
import art.arcane.iris.generation.decoration.IrisDepositPlacementScope;
|
import art.arcane.iris.generation.decoration.IrisDepositPlacementScope;
|
||||||
@@ -17,6 +19,9 @@ import art.arcane.iris.generation.geometry.IrisBlockVector;
|
|||||||
import art.arcane.iris.generation.concurrent.BurstExecutor;
|
import art.arcane.iris.generation.concurrent.BurstExecutor;
|
||||||
import art.arcane.iris.generation.concurrent.MultiBurst;
|
import art.arcane.iris.generation.concurrent.MultiBurst;
|
||||||
import art.arcane.iris.generation.context.ChunkContext;
|
import art.arcane.iris.generation.context.ChunkContext;
|
||||||
|
import art.arcane.iris.generation.context.ChunkedDataCache;
|
||||||
|
import art.arcane.volmlib.util.stream.ProceduralStream;
|
||||||
|
import art.arcane.volmlib.util.stream.interpolation.Interpolated;
|
||||||
import art.arcane.volmlib.util.hunk.Hunk;
|
import art.arcane.volmlib.util.hunk.Hunk;
|
||||||
import art.arcane.volmlib.util.collection.KList;
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
import art.arcane.volmlib.util.mantle.runtime.MantleChunk;
|
||||||
@@ -50,6 +55,7 @@ import static org.mockito.Mockito.mockStatic;
|
|||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.verify;
|
import static org.mockito.Mockito.verify;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.Mockito.withSettings;
|
||||||
|
|
||||||
public class IrisDepositModifierOrderingTest {
|
public class IrisDepositModifierOrderingTest {
|
||||||
@ClassRule
|
@ClassRule
|
||||||
@@ -58,10 +64,10 @@ public class IrisDepositModifierOrderingTest {
|
|||||||
@Before
|
@Before
|
||||||
public void bindPlatform() {
|
public void bindPlatform() {
|
||||||
IrisPlatforms.unbind();
|
IrisPlatforms.unbind();
|
||||||
PlatformRegistries registries = mock(PlatformRegistries.class);
|
PlatformRegistries registries = mock(PlatformRegistries.class, withSettings().stubOnly());
|
||||||
NativeBlockState stone = state("stone");
|
NativeBlockState stone = state("stone");
|
||||||
when(registries.block(anyString())).thenReturn(stone);
|
when(registries.block(anyString())).thenReturn(stone);
|
||||||
IrisPlatform platform = mock(IrisPlatform.class);
|
IrisPlatform platform = mock(IrisPlatform.class, withSettings().stubOnly());
|
||||||
when(platform.registries()).thenReturn(registries);
|
when(platform.registries()).thenReturn(registries);
|
||||||
IrisPlatforms.bind(platform);
|
IrisPlatforms.bind(platform);
|
||||||
}
|
}
|
||||||
@@ -274,8 +280,8 @@ public class IrisDepositModifierOrderingTest {
|
|||||||
private final List<String> samples = Collections.synchronizedList(new ArrayList<>());
|
private final List<String> samples = Collections.synchronizedList(new ArrayList<>());
|
||||||
private final CountDownLatch biomeReady = new CountDownLatch(1);
|
private final CountDownLatch biomeReady = new CountDownLatch(1);
|
||||||
private final CountDownLatch regionReady = new CountDownLatch(1);
|
private final CountDownLatch regionReady = new CountDownLatch(1);
|
||||||
private final Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS);
|
private final Engine engine = mock(Engine.class, withSettings().stubOnly().defaultAnswer(RETURNS_DEEP_STUBS));
|
||||||
private final ChunkContext context = mock(ChunkContext.class, RETURNS_DEEP_STUBS);
|
private final ChunkContext context = mock(ChunkContext.class, withSettings().stubOnly().defaultAnswer(RETURNS_DEEP_STUBS));
|
||||||
private final NativeBlockState stone = state("stone");
|
private final NativeBlockState stone = state("stone");
|
||||||
private final NativeBlockState granite = state("granite");
|
private final NativeBlockState granite = state("granite");
|
||||||
private final NativeBlockState diorite = state("diorite");
|
private final NativeBlockState diorite = state("diorite");
|
||||||
@@ -289,15 +295,23 @@ public class IrisDepositModifierOrderingTest {
|
|||||||
private Fixture(boolean parallel, int attempts) {
|
private Fixture(boolean parallel, int attempts) {
|
||||||
this.parallel = parallel;
|
this.parallel = parallel;
|
||||||
this.attempts = attempts;
|
this.attempts = attempts;
|
||||||
|
IrisBiome biome = mock(IrisBiome.class, withSettings().stubOnly());
|
||||||
|
IrisRegion region = mock(IrisRegion.class, withSettings().stubOnly());
|
||||||
|
ChunkedDataCache<IrisBiome> biomes = new ChunkedDataCache<>(
|
||||||
|
ProceduralStream.of((x, z) -> biome, Interpolated.of(value -> 0D, value -> null)), 0, 0);
|
||||||
|
ChunkedDataCache<IrisRegion> regions = new ChunkedDataCache<>(
|
||||||
|
ProceduralStream.of((x, z) -> region, Interpolated.of(value -> 0D, value -> null)), 0, 0);
|
||||||
|
when(context.getBiome()).thenReturn(biomes);
|
||||||
|
when(context.getRegion()).thenReturn(regions);
|
||||||
when(engine.getHeight()).thenReturn(16);
|
when(engine.getHeight()).thenReturn(16);
|
||||||
when(engine.getCaveBiome(anyInt(), anyInt(), anyInt(), any())).thenReturn(null);
|
when(engine.getCaveBiome(anyInt(), anyInt(), anyInt(), any())).thenReturn(null);
|
||||||
when(engine.getDimension().getDepositVariants()).thenReturn(new KList<>());
|
when(engine.getDimension().getDepositVariants()).thenReturn(new KList<>());
|
||||||
when(context.getRegion().get(anyInt(), anyInt()).getDepositVariants()).thenReturn(new KList<>());
|
when(region.getDepositVariants()).thenReturn(new KList<>());
|
||||||
when(context.getGenerationSessionId()).thenReturn(11L);
|
when(context.getGenerationSessionId()).thenReturn(11L);
|
||||||
chunk = mock(MantleChunk.class);
|
chunk = mock(MantleChunk.class);
|
||||||
Mantle<Matter> mantle = engine.getMantle().getMantle();
|
Mantle<Matter> mantle = engine.getMantle().getMantle();
|
||||||
doReturn(chunk).when(mantle).useChunk(0, 0);
|
doReturn(chunk).when(mantle).useChunk(0, 0);
|
||||||
MultiBurst pool = mock(MultiBurst.class);
|
MultiBurst pool = mock(MultiBurst.class, withSettings().stubOnly());
|
||||||
BurstExecutor burst = new BurstExecutor(executor, 3);
|
BurstExecutor burst = new BurstExecutor(executor, 3);
|
||||||
burst.setMulticore(parallel);
|
burst.setMulticore(parallel);
|
||||||
when(engine.burst()).thenReturn(pool);
|
when(engine.burst()).thenReturn(pool);
|
||||||
@@ -306,8 +320,8 @@ public class IrisDepositModifierOrderingTest {
|
|||||||
IrisDepositGenerator regionDeposit = generator("region", granite, diorite);
|
IrisDepositGenerator regionDeposit = generator("region", granite, diorite);
|
||||||
IrisDepositGenerator biomeDeposit = generator("biome", diorite, diamond);
|
IrisDepositGenerator biomeDeposit = generator("biome", diorite, diamond);
|
||||||
when(engine.getDimension().getDeposits()).thenReturn(new KList<>(dimensionDeposit));
|
when(engine.getDimension().getDeposits()).thenReturn(new KList<>(dimensionDeposit));
|
||||||
when(context.getRegion().get(7, 7).getDeposits()).thenReturn(new KList<>(regionDeposit));
|
when(region.getDeposits()).thenReturn(new KList<>(regionDeposit));
|
||||||
when(context.getBiome().get(7, 7).getDeposits()).thenReturn(new KList<>(biomeDeposit));
|
when(biome.getDeposits()).thenReturn(new KList<>(biomeDeposit));
|
||||||
for (int x = 0; x < 16; x++) {
|
for (int x = 0; x < 16; x++) {
|
||||||
for (int z = 0; z < 16; z++) {
|
for (int z = 0; z < 16; z++) {
|
||||||
output.set(x, 4, z, stone);
|
output.set(x, 4, z, stone);
|
||||||
@@ -316,7 +330,7 @@ public class IrisDepositModifierOrderingTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private IrisDepositGenerator generator(String name, NativeBlockState requiredHost, NativeBlockState replacement) {
|
private IrisDepositGenerator generator(String name, NativeBlockState requiredHost, NativeBlockState replacement) {
|
||||||
IrisDepositGenerator generator = mock(IrisDepositGenerator.class);
|
IrisDepositGenerator generator = mock(IrisDepositGenerator.class, withSettings().stubOnly());
|
||||||
when(generator.getSpawnChance()).thenReturn(1D);
|
when(generator.getSpawnChance()).thenReturn(1D);
|
||||||
when(generator.getPerClumpSpawnChance()).thenReturn(1D);
|
when(generator.getPerClumpSpawnChance()).thenReturn(1D);
|
||||||
when(generator.getMinPerChunk()).thenReturn(attempts);
|
when(generator.getMinPerChunk()).thenReturn(attempts);
|
||||||
@@ -355,7 +369,7 @@ public class IrisDepositModifierOrderingTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void generate() {
|
private void generate() {
|
||||||
try (MockedStatic<B> blocks = mockStatic(B.class, CALLS_REAL_METHODS)) {
|
try (MockedStatic<B> blocks = mockStatic(B.class, withSettings().stubOnly().defaultAnswer(CALLS_REAL_METHODS))) {
|
||||||
blocks.when(() -> B.toDeepSlateOre(any(), any())).thenAnswer(invocation -> invocation.getArgument(1));
|
blocks.when(() -> B.toDeepSlateOre(any(), any())).thenAnswer(invocation -> invocation.getArgument(1));
|
||||||
new IrisDepositModifier(engine).generateDeposits(output, 0, 0, parallel, context);
|
new IrisDepositModifier(engine).generateDeposits(output, 0, 0, parallel, context);
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-11
@@ -21,6 +21,9 @@ import art.arcane.iris.testsupport.PlatformLeakGuard;
|
|||||||
import art.arcane.iris.generation.block.B;
|
import art.arcane.iris.generation.block.B;
|
||||||
import art.arcane.iris.generation.concurrent.MultiBurst;
|
import art.arcane.iris.generation.concurrent.MultiBurst;
|
||||||
import art.arcane.iris.generation.context.ChunkContext;
|
import art.arcane.iris.generation.context.ChunkContext;
|
||||||
|
import art.arcane.iris.generation.context.ChunkedDataCache;
|
||||||
|
import art.arcane.volmlib.util.stream.ProceduralStream;
|
||||||
|
import art.arcane.volmlib.util.stream.interpolation.Interpolated;
|
||||||
import art.arcane.volmlib.util.hunk.Hunk;
|
import art.arcane.volmlib.util.hunk.Hunk;
|
||||||
import art.arcane.volmlib.util.collection.KList;
|
import art.arcane.volmlib.util.collection.KList;
|
||||||
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
import art.arcane.volmlib.util.mantle.runtime.Mantle;
|
||||||
@@ -53,6 +56,7 @@ import static org.mockito.Mockito.doReturn;
|
|||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.mockStatic;
|
import static org.mockito.Mockito.mockStatic;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
import static org.mockito.Mockito.withSettings;
|
||||||
|
|
||||||
public class IrisDepositModifierParityTest {
|
public class IrisDepositModifierParityTest {
|
||||||
@ClassRule
|
@ClassRule
|
||||||
@@ -63,9 +67,9 @@ public class IrisDepositModifierParityTest {
|
|||||||
@Before
|
@Before
|
||||||
public void bindPlatform() {
|
public void bindPlatform() {
|
||||||
IrisPlatforms.unbind();
|
IrisPlatforms.unbind();
|
||||||
PlatformRegistries registries = mock(PlatformRegistries.class);
|
PlatformRegistries registries = mock(PlatformRegistries.class, withSettings().stubOnly());
|
||||||
when(registries.block(anyString())).thenAnswer(invocation -> state(invocation.getArgument(0)));
|
when(registries.block(anyString())).thenAnswer(invocation -> state(invocation.getArgument(0)));
|
||||||
IrisPlatform platform = mock(IrisPlatform.class);
|
IrisPlatform platform = mock(IrisPlatform.class, withSettings().stubOnly());
|
||||||
when(platform.registries()).thenReturn(registries);
|
when(platform.registries()).thenReturn(registries);
|
||||||
IrisPlatforms.bind(platform);
|
IrisPlatforms.bind(platform);
|
||||||
}
|
}
|
||||||
@@ -125,8 +129,8 @@ public class IrisDepositModifierParityTest {
|
|||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
private long[] generateConfigured(long seed, boolean multicore, boolean caveVariant, boolean enumerablePack,
|
private long[] generateConfigured(long seed, boolean multicore, boolean caveVariant, boolean enumerablePack,
|
||||||
AtomicInteger caveBiomeLookups, AtomicInteger unmatchableClumps) {
|
AtomicInteger caveBiomeLookups, AtomicInteger unmatchableClumps) {
|
||||||
Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS);
|
Engine engine = mock(Engine.class, withSettings().stubOnly().defaultAnswer(RETURNS_DEEP_STUBS));
|
||||||
ChunkContext context = mock(ChunkContext.class, RETURNS_DEEP_STUBS);
|
ChunkContext context = mock(ChunkContext.class, withSettings().stubOnly().defaultAnswer(RETURNS_DEEP_STUBS));
|
||||||
IrisDimension dimension = new IrisDimension();
|
IrisDimension dimension = new IrisDimension();
|
||||||
IrisRegion region = new IrisRegion();
|
IrisRegion region = new IrisRegion();
|
||||||
IrisBiome surface = new IrisBiome();
|
IrisBiome surface = new IrisBiome();
|
||||||
@@ -170,9 +174,9 @@ public class IrisDepositModifierParityTest {
|
|||||||
when(engine.getSeedManager().getDeposit()).thenReturn(seed);
|
when(engine.getSeedManager().getDeposit()).thenReturn(seed);
|
||||||
when(engine.getDimension()).thenReturn(dimension);
|
when(engine.getDimension()).thenReturn(dimension);
|
||||||
if (enumerablePack) {
|
if (enumerablePack) {
|
||||||
IrisData data = mock(IrisData.class);
|
IrisData data = mock(IrisData.class, withSettings().stubOnly());
|
||||||
ResourceLoader<IrisBiome> biomes = mock(ResourceLoader.class);
|
ResourceLoader<IrisBiome> biomes = mock(ResourceLoader.class, withSettings().stubOnly());
|
||||||
ResourceLoader<IrisRegion> regions = mock(ResourceLoader.class);
|
ResourceLoader<IrisRegion> regions = mock(ResourceLoader.class, withSettings().stubOnly());
|
||||||
when(data.getBiomeLoader()).thenReturn(biomes);
|
when(data.getBiomeLoader()).thenReturn(biomes);
|
||||||
when(data.getRegionLoader()).thenReturn(regions);
|
when(data.getRegionLoader()).thenReturn(regions);
|
||||||
when(biomes.getPossibleKeys()).thenReturn(new String[]{"allowed", "denied", "surface"});
|
when(biomes.getPossibleKeys()).thenReturn(new String[]{"allowed", "denied", "surface"});
|
||||||
@@ -192,9 +196,13 @@ public class IrisDepositModifierParityTest {
|
|||||||
return ((Math.floorDiv(x, 8) + Math.floorDiv(z, 8) + Math.floorDiv(y, 8)) & 1) == 0 ? cave : denied;
|
return ((Math.floorDiv(x, 8) + Math.floorDiv(z, 8) + Math.floorDiv(y, 8)) & 1) == 0 ? cave : denied;
|
||||||
});
|
});
|
||||||
when(context.getRoundedHeight(anyInt(), anyInt())).thenReturn(40);
|
when(context.getRoundedHeight(anyInt(), anyInt())).thenReturn(40);
|
||||||
when(context.getBiome().get(anyInt(), anyInt())).thenReturn(surface);
|
ChunkedDataCache<IrisBiome> biomes = new ChunkedDataCache<>(
|
||||||
when(context.getRegion().get(anyInt(), anyInt())).thenReturn(region);
|
ProceduralStream.of((x, z) -> surface, Interpolated.of(value -> 0D, value -> null)), 0, 0);
|
||||||
MantleChunk<Matter> chunk = mock(MantleChunk.class);
|
ChunkedDataCache<IrisRegion> regions = new ChunkedDataCache<>(
|
||||||
|
ProceduralStream.of((x, z) -> region, Interpolated.of(value -> 0D, value -> null)), 0, 0);
|
||||||
|
when(context.getBiome()).thenReturn(biomes);
|
||||||
|
when(context.getRegion()).thenReturn(regions);
|
||||||
|
MantleChunk<Matter> chunk = mock(MantleChunk.class, withSettings().stubOnly());
|
||||||
Mantle<Matter> mantle = engine.getMantle().getMantle();
|
Mantle<Matter> mantle = engine.getMantle().getMantle();
|
||||||
doReturn(chunk).when(mantle).useChunk(-3, 5);
|
doReturn(chunk).when(mantle).useChunk(-3, 5);
|
||||||
MatterCavern cavern = new MatterCavern(true, "allowed", (byte) 0);
|
MatterCavern cavern = new MatterCavern(true, "allowed", (byte) 0);
|
||||||
@@ -215,7 +223,7 @@ public class IrisDepositModifierParityTest {
|
|||||||
}
|
}
|
||||||
MultiBurst burst = new MultiBurst("Deposit parity", () -> 4);
|
MultiBurst burst = new MultiBurst("Deposit parity", () -> 4);
|
||||||
when(engine.burst()).thenReturn(burst);
|
when(engine.burst()).thenReturn(burst);
|
||||||
try (MockedStatic<B> blocks = mockStatic(B.class, CALLS_REAL_METHODS)) {
|
try (MockedStatic<B> blocks = mockStatic(B.class, withSettings().stubOnly().defaultAnswer(CALLS_REAL_METHODS))) {
|
||||||
blocks.when(() -> B.getStateOrNull(anyString(), anyBoolean()))
|
blocks.when(() -> B.getStateOrNull(anyString(), anyBoolean()))
|
||||||
.thenAnswer(invocation -> state(invocation.getArgument(0)));
|
.thenAnswer(invocation -> state(invocation.getArgument(0)));
|
||||||
blocks.when(() -> B.toDeepSlateOre(any(), any())).thenAnswer(invocation -> {
|
blocks.when(() -> B.toDeepSlateOre(any(), any())).thenAnswer(invocation -> {
|
||||||
|
|||||||
+139
-17
@@ -30,7 +30,6 @@ import static org.junit.Assert.assertEquals;
|
|||||||
import static org.junit.Assert.assertSame;
|
import static org.junit.Assert.assertSame;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.mockito.ArgumentMatchers.any;
|
||||||
import static org.mockito.ArgumentMatchers.anyString;
|
|
||||||
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
@@ -245,27 +244,150 @@ public class IrisPerfectionModifierColumnTest {
|
|||||||
if (existing != null) {
|
if (existing != null) {
|
||||||
return existing;
|
return existing;
|
||||||
}
|
}
|
||||||
NativeBlockState block = mock(NativeBlockState.class, withSettings().stubOnly());
|
NativeBlockState block = new TestBlockState(this, key);
|
||||||
states.put(key, block);
|
states.put(key, block);
|
||||||
when(block.key()).thenReturn(key);
|
|
||||||
when(block.isAir()).thenReturn(key.equals("minecraft:air"));
|
|
||||||
when(block.isFluid()).thenReturn(key.equals("minecraft:water"));
|
|
||||||
when(block.isWater()).thenReturn(key.equals("minecraft:water"));
|
|
||||||
when(block.isWaterLogged()).thenReturn(key.contains("waterlogged=true"));
|
|
||||||
when(block.isDecorant()).thenReturn(key.equals("minecraft:poppy") || key.startsWith("minecraft:tall_grass"));
|
|
||||||
when(block.canPlaceOnto(any())).thenAnswer(call -> "minecraft:tall_grass[half=lower]".equals(call.<NativeBlockState>getArgument(0).key()));
|
|
||||||
BlockData handle = mock(BlockData.class, withSettings().stubOnly());
|
|
||||||
when(block.nativeHandle()).thenReturn(handle);
|
|
||||||
when(handle.isFaceSturdy(any(), any())).thenReturn(key.equals("minecraft:stone"));
|
|
||||||
when(block.withProperty(anyString(), anyString())).thenAnswer(call -> {
|
|
||||||
String property = call.getArgument(0);
|
|
||||||
String value = call.getArgument(1);
|
|
||||||
return state(key.replaceAll(property + "=[^,\\]]+", property + "=" + value));
|
|
||||||
});
|
|
||||||
return block;
|
return block;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static final class TestBlockState implements NativeBlockState {
|
||||||
|
private final Fixture fixture;
|
||||||
|
private final String key;
|
||||||
|
private final BlockData handle;
|
||||||
|
|
||||||
|
private TestBlockState(Fixture fixture, String key) {
|
||||||
|
this.fixture = fixture;
|
||||||
|
this.key = key;
|
||||||
|
handle = mock(BlockData.class, withSettings().stubOnly());
|
||||||
|
when(handle.isFaceSturdy(any(), any())).thenReturn(key.equals("minecraft:stone"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String key() {
|
||||||
|
return key;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String namespace() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isAir() {
|
||||||
|
return key.equals("minecraft:air");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFluid() {
|
||||||
|
return key.equals("minecraft:water");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWater() {
|
||||||
|
return key.equals("minecraft:water");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isWaterLogged() {
|
||||||
|
return key.contains("waterlogged=true");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDecorant() {
|
||||||
|
return key.equals("minecraft:poppy") || key.startsWith("minecraft:tall_grass");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean canPlaceOnto(NativeBlockState onto) {
|
||||||
|
return "minecraft:tall_grass[half=lower]".equals(onto.key());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object nativeHandle() {
|
||||||
|
return handle;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NativeBlockState withProperty(String property, String value) {
|
||||||
|
return fixture.state(key.replaceAll(property + "=[^,\\]]+", property + "=" + value));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isSolid() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOccluding() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isCustom() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isLit() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isUpdatable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFoliage() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isTreeBlock() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isFoliagePlantable() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStorage() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isStorageChest() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isOre() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isDeepSlate() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isVineBlock() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean hasTileEntity() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean matches(NativeBlockState state) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static final class CountingHunk extends ArrayHunk<NativeBlockState> {
|
private static final class CountingHunk extends ArrayHunk<NativeBlockState> {
|
||||||
private int cleanColumnReads;
|
private int cleanColumnReads;
|
||||||
|
|
||||||
|
|||||||
+5
-3
@@ -18,6 +18,7 @@ import art.arcane.volmlib.util.collection.KList;
|
|||||||
import org.junit.BeforeClass;
|
import org.junit.BeforeClass;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.mockito.invocation.InvocationOnMock;
|
import org.mockito.invocation.InvocationOnMock;
|
||||||
|
import org.mockito.MockMakers;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
@@ -47,6 +48,7 @@ import static org.mockito.Mockito.doAnswer;
|
|||||||
import static org.mockito.Mockito.clearInvocations;
|
import static org.mockito.Mockito.clearInvocations;
|
||||||
import static org.mockito.Mockito.doReturn;
|
import static org.mockito.Mockito.doReturn;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.withSettings;
|
||||||
import static org.mockito.Mockito.never;
|
import static org.mockito.Mockito.never;
|
||||||
import static org.mockito.Mockito.spy;
|
import static org.mockito.Mockito.spy;
|
||||||
import static org.mockito.Mockito.times;
|
import static org.mockito.Mockito.times;
|
||||||
@@ -348,7 +350,7 @@ public class IrisDimensionCarvingResolverParityTest {
|
|||||||
Fixture replacement = createMixedDepthFixture();
|
Fixture replacement = createMixedDepthFixture();
|
||||||
AtomicReference<IrisDimension> dimension = new AtomicReference<>(first.engine.getDimension());
|
AtomicReference<IrisDimension> dimension = new AtomicReference<>(first.engine.getDimension());
|
||||||
AtomicReference<IrisData> data = new AtomicReference<>(first.engine.getData());
|
AtomicReference<IrisData> data = new AtomicReference<>(first.engine.getData());
|
||||||
Engine engine = mock(Engine.class, CALLS_REAL_METHODS);
|
Engine engine = mock(Engine.class, withSettings().defaultAnswer(CALLS_REAL_METHODS).mockMaker(MockMakers.PROXY));
|
||||||
doAnswer((InvocationOnMock invocation) -> dimension.get()).when(engine).getDimension();
|
doAnswer((InvocationOnMock invocation) -> dimension.get()).when(engine).getDimension();
|
||||||
doAnswer((InvocationOnMock invocation) -> data.get()).when(engine).getData();
|
doAnswer((InvocationOnMock invocation) -> data.get()).when(engine).getData();
|
||||||
|
|
||||||
@@ -601,7 +603,7 @@ public class IrisDimensionCarvingResolverParityTest {
|
|||||||
doReturn(carvingEntries).when(dimension).getCarving();
|
doReturn(carvingEntries).when(dimension).getCarving();
|
||||||
doReturn(index).when(dimension).getCarvingEntryIndex();
|
doReturn(index).when(dimension).getCarvingEntryIndex();
|
||||||
|
|
||||||
Engine engine = mock(Engine.class, CALLS_REAL_METHODS);
|
Engine engine = mock(Engine.class, withSettings().defaultAnswer(CALLS_REAL_METHODS).mockMaker(MockMakers.PROXY));
|
||||||
doReturn(dimension).when(engine).getDimension();
|
doReturn(dimension).when(engine).getDimension();
|
||||||
doReturn(data).when(engine).getData();
|
doReturn(data).when(engine).getData();
|
||||||
doReturn(new SeedManager(913_531_771L)).when(engine).getSeedManager();
|
doReturn(new SeedManager(913_531_771L)).when(engine).getSeedManager();
|
||||||
@@ -689,7 +691,7 @@ public class IrisDimensionCarvingResolverParityTest {
|
|||||||
doReturn(carvingEntries).when(dimension).getCarving();
|
doReturn(carvingEntries).when(dimension).getCarving();
|
||||||
doReturn(index).when(dimension).getCarvingEntryIndex();
|
doReturn(index).when(dimension).getCarvingEntryIndex();
|
||||||
|
|
||||||
Engine engine = mock(Engine.class, CALLS_REAL_METHODS);
|
Engine engine = mock(Engine.class, withSettings().defaultAnswer(CALLS_REAL_METHODS).mockMaker(MockMakers.PROXY));
|
||||||
doReturn(dimension).when(engine).getDimension();
|
doReturn(dimension).when(engine).getDimension();
|
||||||
doReturn(data).when(engine).getData();
|
doReturn(data).when(engine).getData();
|
||||||
doReturn(new SeedManager(4_627_991_643L)).when(engine).getSeedManager();
|
doReturn(new SeedManager(4_627_991_643L)).when(engine).getSeedManager();
|
||||||
|
|||||||
+14
-1
@@ -2,6 +2,7 @@ package art.arcane.iris.studio.view;
|
|||||||
|
|
||||||
import art.arcane.iris.configuration.IrisSettings;
|
import art.arcane.iris.configuration.IrisSettings;
|
||||||
import art.arcane.iris.world.pregen.PregenApiPhase;
|
import art.arcane.iris.world.pregen.PregenApiPhase;
|
||||||
|
import art.arcane.iris.world.pregen.MantleHeapPressure;
|
||||||
import art.arcane.iris.world.pregen.PregenApiSink;
|
import art.arcane.iris.world.pregen.PregenApiSink;
|
||||||
import art.arcane.iris.world.pregen.PregenListener;
|
import art.arcane.iris.world.pregen.PregenListener;
|
||||||
import art.arcane.iris.world.pregen.PregenTask;
|
import art.arcane.iris.world.pregen.PregenTask;
|
||||||
@@ -9,11 +10,13 @@ import art.arcane.iris.world.pregen.PregeneratorMethod;
|
|||||||
import art.arcane.iris.spi.IrisServices;
|
import art.arcane.iris.spi.IrisServices;
|
||||||
import art.arcane.volmlib.util.math.Position2;
|
import art.arcane.volmlib.util.math.Position2;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.MockedStatic;
|
||||||
|
|
||||||
import java.lang.reflect.Field;
|
import java.lang.reflect.Field;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -23,6 +26,7 @@ import static org.mockito.ArgumentMatchers.any;
|
|||||||
import static org.mockito.ArgumentMatchers.anyInt;
|
import static org.mockito.ArgumentMatchers.anyInt;
|
||||||
import static org.mockito.Mockito.doAnswer;
|
import static org.mockito.Mockito.doAnswer;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.mockStatic;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
public class PregeneratorJobTerminalProgressTest {
|
public class PregeneratorJobTerminalProgressTest {
|
||||||
@@ -89,6 +93,7 @@ public class PregeneratorJobTerminalProgressTest {
|
|||||||
PregeneratorMethod method = mock(PregeneratorMethod.class);
|
PregeneratorMethod method = mock(PregeneratorMethod.class);
|
||||||
when(method.getMethod(anyInt(), anyInt())).thenReturn("terminal-progress-test");
|
when(method.getMethod(anyInt(), anyInt())).thenReturn("terminal-progress-test");
|
||||||
ArrayList<PregenListener> pending = new ArrayList<>();
|
ArrayList<PregenListener> pending = new ArrayList<>();
|
||||||
|
AtomicReference<MockedStatic<MantleHeapPressure>> heapPressure = new AtomicReference<>();
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
pending.add(invocation.getArgument(2));
|
pending.add(invocation.getArgument(2));
|
||||||
if (completion.cancel()) {
|
if (completion.cancel()) {
|
||||||
@@ -97,6 +102,7 @@ public class PregeneratorJobTerminalProgressTest {
|
|||||||
return null;
|
return null;
|
||||||
}).when(method).generateChunk(anyInt(), anyInt(), any(PregenListener.class));
|
}).when(method).generateChunk(anyInt(), anyInt(), any(PregenListener.class));
|
||||||
doAnswer(invocation -> {
|
doAnswer(invocation -> {
|
||||||
|
try {
|
||||||
for (int index = 0; index < pending.size(); index++) {
|
for (int index = 0; index < pending.size(); index++) {
|
||||||
PregenListener listener = pending.get(index);
|
PregenListener listener = pending.get(index);
|
||||||
if (completion.failLast() && index == pending.size() - 1) {
|
if (completion.failLast() && index == pending.size() - 1) {
|
||||||
@@ -105,6 +111,12 @@ public class PregeneratorJobTerminalProgressTest {
|
|||||||
listener.onChunkGenerated(0, 0, completion.cached());
|
listener.onChunkGenerated(0, 0, completion.cached());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} finally {
|
||||||
|
MockedStatic<MantleHeapPressure> pressure = heapPressure.getAndSet(null);
|
||||||
|
if (pressure != null) {
|
||||||
|
pressure.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}).when(method).close();
|
}).when(method).close();
|
||||||
PregenTask task = PregenTask.builder().center(new Position2(0, 0))
|
PregenTask task = PregenTask.builder().center(new Position2(0, 0))
|
||||||
@@ -112,7 +124,8 @@ public class PregeneratorJobTerminalProgressTest {
|
|||||||
Thread worker = null;
|
Thread worker = null;
|
||||||
try {
|
try {
|
||||||
assertNull(PregeneratorJob.getInstance());
|
assertNull(PregeneratorJob.getInstance());
|
||||||
PregeneratorJob job = new PregeneratorJob(new PregeneratorJob.Configuration(task, method, null, () -> {}));
|
PregeneratorJob job = new PregeneratorJob(new PregeneratorJob.Configuration(task, method, null,
|
||||||
|
() -> heapPressure.set(mockStatic(MantleHeapPressure.class))));
|
||||||
Field workerField = PregeneratorJob.class.getDeclaredField("worker");
|
Field workerField = PregeneratorJob.class.getDeclaredField("worker");
|
||||||
workerField.setAccessible(true);
|
workerField.setAccessible(true);
|
||||||
worker = (Thread) workerField.get(job);
|
worker = (Thread) workerField.get(job);
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package art.arcane.iris.world.history;
|
package art.arcane.iris.world.history;
|
||||||
|
|
||||||
import art.arcane.iris.generation.runtime.Engine;
|
import art.arcane.iris.generation.runtime.Engine;
|
||||||
|
import art.arcane.iris.generation.runtime.IrisComplex;
|
||||||
|
import art.arcane.volmlib.util.stream.ProceduralStream;
|
||||||
import art.arcane.iris.generation.runtime.DimensionStackContext;
|
import art.arcane.iris.generation.runtime.DimensionStackContext;
|
||||||
import art.arcane.iris.generation.runtime.DimensionStackLayout;
|
import art.arcane.iris.generation.runtime.DimensionStackLayout;
|
||||||
import art.arcane.iris.generation.subterrain.SubterrainCell;
|
import art.arcane.iris.generation.subterrain.SubterrainCell;
|
||||||
@@ -87,7 +89,7 @@ public class SavedBiomeCaptureTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void retainsFloatingChildIdentityWhenItSharesTheHostDerivative() throws Exception {
|
public void retainsFloatingChildIdentityWhenItSharesTheHostDerivative() throws Exception {
|
||||||
Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS);
|
Engine engine = captureEngine();
|
||||||
when(engine.getComplex().getSubterrainPlanner()).thenReturn(null);
|
when(engine.getComplex().getSubterrainPlanner()).thenReturn(null);
|
||||||
GenerationHistory.GenerationStage stage = mock(GenerationHistory.GenerationStage.class);
|
GenerationHistory.GenerationStage stage = mock(GenerationHistory.GenerationStage.class);
|
||||||
SavedBiomeRuntime historical = mock(SavedBiomeRuntime.class);
|
SavedBiomeRuntime historical = mock(SavedBiomeRuntime.class);
|
||||||
@@ -129,7 +131,7 @@ public class SavedBiomeCaptureTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void recordsExactSurfaceColumnsAndIndependentCaveAndVerticalIdentities() throws Exception {
|
public void recordsExactSurfaceColumnsAndIndependentCaveAndVerticalIdentities() throws Exception {
|
||||||
Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS);
|
Engine engine = captureEngine();
|
||||||
when(engine.getComplex().getSubterrainPlanner()).thenReturn(null);
|
when(engine.getComplex().getSubterrainPlanner()).thenReturn(null);
|
||||||
GenerationHistory.GenerationStage stage = mock(GenerationHistory.GenerationStage.class);
|
GenerationHistory.GenerationStage stage = mock(GenerationHistory.GenerationStage.class);
|
||||||
SavedBiomeRuntime historical = mock(SavedBiomeRuntime.class);
|
SavedBiomeRuntime historical = mock(SavedBiomeRuntime.class);
|
||||||
@@ -170,15 +172,15 @@ public class SavedBiomeCaptureTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void preservesHostFeatureBiomeAndRegionAgainstStackAndFloatingOverlay() throws Exception {
|
public void preservesHostFeatureBiomeAndRegionAgainstStackAndFloatingOverlay() throws Exception {
|
||||||
Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS);
|
Engine engine = captureEngine();
|
||||||
when(engine.getComplex().getSubterrainPlanner()).thenReturn(null);
|
when(engine.getComplex().getSubterrainPlanner()).thenReturn(null);
|
||||||
IrisBiome upper = biome("upper-biome");
|
IrisBiome upper = biome("upper-biome");
|
||||||
IrisBiome authored = biome("authored-biome");
|
IrisBiome authored = biome("authored-biome");
|
||||||
IrisRegion upperRegion = region("upper-region");
|
IrisRegion upperRegion = region("upper-region");
|
||||||
IrisRegion hostRegion = region("host-region");
|
IrisRegion hostRegion = region("host-region");
|
||||||
DimensionStackContext stack = mock(DimensionStackContext.class);
|
DimensionStackContext stack = mock(DimensionStackContext.class, withSettings().stubOnly());
|
||||||
DimensionStackLayout layout = mock(DimensionStackLayout.class);
|
DimensionStackLayout layout = mock(DimensionStackLayout.class, withSettings().stubOnly());
|
||||||
DimensionStackLayout.Layer layer = mock(DimensionStackLayout.Layer.class);
|
DimensionStackLayout.Layer layer = mock(DimensionStackLayout.Layer.class, withSettings().stubOnly());
|
||||||
when(engine.getDimensionStackContext()).thenReturn(stack);
|
when(engine.getDimensionStackContext()).thenReturn(stack);
|
||||||
when(stack.getLayout(anyInt(), anyInt())).thenReturn(layout);
|
when(stack.getLayout(anyInt(), anyInt())).thenReturn(layout);
|
||||||
when(layout.layerAt(anyInt())).thenReturn(layer);
|
when(layout.layerAt(anyInt())).thenReturn(layer);
|
||||||
@@ -220,15 +222,14 @@ public class SavedBiomeCaptureTest {
|
|||||||
}
|
}
|
||||||
int chunkX = Math.floorDiv(edgeX, 16);
|
int chunkX = Math.floorDiv(edgeX, 16);
|
||||||
int chunkZ = Math.floorDiv(anchor.z(), 16);
|
int chunkZ = Math.floorDiv(anchor.z(), 16);
|
||||||
Engine engine = mock(Engine.class, withSettings().defaultAnswer(RETURNS_DEEP_STUBS)
|
Engine engine = captureEngine();
|
||||||
.mockMaker(MockMakers.SUBCLASS));
|
|
||||||
IrisBiome fallback = biome("base-biome");
|
IrisBiome fallback = biome("base-biome");
|
||||||
IrisBiome authored = biome("authored-biome");
|
IrisBiome authored = biome("authored-biome");
|
||||||
IrisRegion host = region("host-region");
|
IrisRegion host = region("host-region");
|
||||||
IrisRegion stacked = region("stacked-region");
|
IrisRegion stacked = region("stacked-region");
|
||||||
DimensionStackContext stack = mock(DimensionStackContext.class);
|
DimensionStackContext stack = mock(DimensionStackContext.class, withSettings().stubOnly());
|
||||||
DimensionStackLayout layout = mock(DimensionStackLayout.class);
|
DimensionStackLayout layout = mock(DimensionStackLayout.class, withSettings().stubOnly());
|
||||||
DimensionStackLayout.Layer layer = mock(DimensionStackLayout.Layer.class);
|
DimensionStackLayout.Layer layer = mock(DimensionStackLayout.Layer.class, withSettings().stubOnly());
|
||||||
when(engine.getDimensionStackContext()).thenReturn(stack);
|
when(engine.getDimensionStackContext()).thenReturn(stack);
|
||||||
when(stack.getLayout(anyInt(), anyInt())).thenReturn(layout);
|
when(stack.getLayout(anyInt(), anyInt())).thenReturn(layout);
|
||||||
when(layout.layerAt(anyInt())).thenReturn(layer);
|
when(layout.layerAt(anyInt())).thenReturn(layer);
|
||||||
@@ -272,8 +273,21 @@ public class SavedBiomeCaptureTest {
|
|||||||
assertTrue(outsideOccupiedVolume);
|
assertTrue(outsideOccupiedVolume);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private static Engine captureEngine() {
|
||||||
|
Engine engine = mock(Engine.class, withSettings().stubOnly().mockMaker(MockMakers.PROXY));
|
||||||
|
IrisComplex complex = mock(IrisComplex.class, withSettings().stubOnly().defaultAnswer(RETURNS_DEEP_STUBS));
|
||||||
|
ProceduralStream<IrisRegion> regions = mock(ProceduralStream.class, withSettings().stubOnly());
|
||||||
|
ProceduralStream<IrisBiome> caves = mock(ProceduralStream.class, withSettings().stubOnly());
|
||||||
|
when(engine.getComplex()).thenReturn(complex);
|
||||||
|
when(engine.getSubterrainCell(anyInt(), anyInt(), anyInt())).thenReturn(SubterrainCell.OUTSIDE);
|
||||||
|
when(complex.getRegionStream()).thenReturn(regions);
|
||||||
|
when(complex.getCaveBiomeStream()).thenReturn(caves);
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
private static Engine caveCaptureEngine(IrisBiome surface, IrisBiome cave) {
|
private static Engine caveCaptureEngine(IrisBiome surface, IrisBiome cave) {
|
||||||
Engine engine = mock(Engine.class, RETURNS_DEEP_STUBS);
|
Engine engine = captureEngine();
|
||||||
when(engine.getComplex().getSubterrainPlanner()).thenReturn(null);
|
when(engine.getComplex().getSubterrainPlanner()).thenReturn(null);
|
||||||
IrisRegion region = region("flat-region");
|
IrisRegion region = region("flat-region");
|
||||||
when(engine.getMinHeight()).thenReturn(-64);
|
when(engine.getMinHeight()).thenReturn(-64);
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ import java.util.concurrent.TimeoutException;
|
|||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||||
|
import java.util.concurrent.locks.LockSupport;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
@@ -589,8 +590,14 @@ public class SavedBiomeRuntimeTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static void awaitIdle(SavedBiomeRuntime runtime) {
|
private static void awaitIdle(SavedBiomeRuntime runtime) {
|
||||||
Await.reached("the runtime to drain its pending queries", Duration.ofSeconds(5L),
|
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5L);
|
||||||
() -> runtime.pendingQueryCount() == 0);
|
while (runtime.pendingQueryCount() != 0 && System.nanoTime() - deadline < 0L) {
|
||||||
|
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1L));
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new AssertionError("Interrupted while waiting for the runtime to drain its pending queries");
|
||||||
|
}
|
||||||
|
}
|
||||||
assertEquals(0, runtime.pendingQueryCount());
|
assertEquals(0, runtime.pendingQueryCount());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package art.arcane.iris.generation.runtime;
|
||||||
|
|
||||||
|
public final class ProbeBackgroundTasks {
|
||||||
|
private ProbeBackgroundTasks() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void await(Engine engine) {
|
||||||
|
if (!(engine instanceof IrisEngine iris)) {
|
||||||
|
throw new IllegalArgumentException("Probe background barrier requires an Iris engine");
|
||||||
|
}
|
||||||
|
await(iris.backgroundTasks);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void await(EngineBackgroundTasks tasks) {
|
||||||
|
tasks.drainBackgroundTasks("probe completion").requireComplete("probe completion");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -414,7 +414,7 @@ public final class GenerationOrderProbe {
|
|||||||
? generateParallel(session.engine(), scheduled, configuration)
|
? generateParallel(session.engine(), scheduled, configuration)
|
||||||
: generateSequential(session.engine(), scheduled, configuration);
|
: generateSequential(session.engine(), scheduled, configuration);
|
||||||
long generationNanos = System.nanoTime() - started;
|
long generationNanos = System.nanoTime() - started;
|
||||||
List<Throwable> reports = RealPackProbeSupport.settleAndDrain();
|
List<Throwable> reports = RealPackProbeSupport.settleAndDrain(session.engine());
|
||||||
if (!reports.isEmpty()) {
|
if (!reports.isEmpty()) {
|
||||||
RealPackProbeSupport.printReports(LOG_PREFIX, order + " generation reports", reports);
|
RealPackProbeSupport.printReports(LOG_PREFIX, order + " generation reports", reports);
|
||||||
throw reportedFailure(order + " generation", reports);
|
throw reportedFailure(order + " generation", reports);
|
||||||
|
|||||||
@@ -3156,7 +3156,7 @@ public final class HydrologyPackProbe {
|
|||||||
}
|
}
|
||||||
hydrologySeed = engine.getSeedManager().getBodies();
|
hydrologySeed = engine.getSeedManager().getBodies();
|
||||||
engineReadyNanos = session.readyNanos();
|
engineReadyNanos = session.readyNanos();
|
||||||
List<Throwable> reports = RealPackProbeSupport.settleAndDrain();
|
List<Throwable> reports = RealPackProbeSupport.settleAndDrain(engine);
|
||||||
if (!reports.isEmpty()) {
|
if (!reports.isEmpty()) {
|
||||||
RealPackProbeSupport.printReports(LOG_PREFIX, "seed " + seed + " planning reports", reports);
|
RealPackProbeSupport.printReports(LOG_PREFIX, "seed " + seed + " planning reports", reports);
|
||||||
throw reportedFailure("Hydrology coverage scan", reports);
|
throw reportedFailure("Hydrology coverage scan", reports);
|
||||||
@@ -4153,7 +4153,7 @@ public final class HydrologyPackProbe {
|
|||||||
System.out.println(new GeneratedProcessResult(
|
System.out.println(new GeneratedProcessResult(
|
||||||
witness.selector(), verification).machineLine());
|
witness.selector(), verification).machineLine());
|
||||||
}
|
}
|
||||||
List<Throwable> reports = RealPackProbeSupport.settleAndDrain();
|
List<Throwable> reports = RealPackProbeSupport.settleAndDrain(engine);
|
||||||
if (!reports.isEmpty()) {
|
if (!reports.isEmpty()) {
|
||||||
RealPackProbeSupport.printReports(LOG_PREFIX, "generated chunk reports", reports);
|
RealPackProbeSupport.printReports(LOG_PREFIX, "generated chunk reports", reports);
|
||||||
throw reportedFailure("Hydrology generated chunk process", reports);
|
throw reportedFailure("Hydrology generated chunk process", reports);
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package art.arcane.iris.probe;
|
package art.arcane.iris.probe;
|
||||||
|
|
||||||
import art.arcane.iris.pack.loading.IrisData;
|
import art.arcane.iris.pack.loading.IrisData;
|
||||||
|
import art.arcane.iris.generation.concurrent.MultiBurst;
|
||||||
import art.arcane.iris.pack.PackValidationResult;
|
import art.arcane.iris.pack.PackValidationResult;
|
||||||
import art.arcane.iris.pack.PackValidator;
|
import art.arcane.iris.pack.PackValidator;
|
||||||
import art.arcane.iris.generation.runtime.IrisEngine;
|
import art.arcane.iris.generation.runtime.IrisEngine;
|
||||||
|
import art.arcane.iris.generation.runtime.ProbeBackgroundTasks;
|
||||||
import art.arcane.iris.generation.runtime.Engine;
|
import art.arcane.iris.generation.runtime.Engine;
|
||||||
import art.arcane.iris.generation.runtime.EngineAssignedComponent;
|
import art.arcane.iris.generation.runtime.EngineAssignedComponent;
|
||||||
import art.arcane.iris.generation.runtime.EngineEffects;
|
import art.arcane.iris.generation.runtime.EngineEffects;
|
||||||
@@ -39,6 +41,7 @@ import java.util.EnumSet;
|
|||||||
import java.nio.file.Path;
|
import java.nio.file.Path;
|
||||||
import java.nio.file.StandardCopyOption;
|
import java.nio.file.StandardCopyOption;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
@@ -46,6 +49,13 @@ import java.util.List;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ExecutorService;
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Callable;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ForkJoinPool;
|
||||||
|
import java.util.concurrent.ForkJoinTask;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.concurrent.locks.LockSupport;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
final class RealPackProbeSupport {
|
final class RealPackProbeSupport {
|
||||||
@@ -79,6 +89,7 @@ final class RealPackProbeSupport {
|
|||||||
validatePack(pack, logPrefix);
|
validatePack(pack, logPrefix);
|
||||||
return new Workspace(workRoot, pack, dimensionKey, logPrefix, false, bindings);
|
return new Workspace(workRoot, pack, dimensionKey, logPrefix, false, bindings);
|
||||||
} catch (Throwable failure) {
|
} catch (Throwable failure) {
|
||||||
|
clearRuntime();
|
||||||
Throwable cleanupFailure = deleteWorkRoot(workRoot);
|
Throwable cleanupFailure = deleteWorkRoot(workRoot);
|
||||||
if (cleanupFailure != null) {
|
if (cleanupFailure != null) {
|
||||||
failure.addSuppressed(cleanupFailure);
|
failure.addSuppressed(cleanupFailure);
|
||||||
@@ -103,6 +114,7 @@ final class RealPackProbeSupport {
|
|||||||
configureRuntime(new File(workRoot, "platform-validation"), hydrologyGeneratedVerification, null);
|
configureRuntime(new File(workRoot, "platform-validation"), hydrologyGeneratedVerification, null);
|
||||||
return new Workspace(workRoot, pack, dimensionKey, logPrefix, hydrologyGeneratedVerification, null);
|
return new Workspace(workRoot, pack, dimensionKey, logPrefix, hydrologyGeneratedVerification, null);
|
||||||
} catch (Throwable failure) {
|
} catch (Throwable failure) {
|
||||||
|
clearRuntime();
|
||||||
Throwable cleanupFailure = deleteWorkRoot(workRoot);
|
Throwable cleanupFailure = deleteWorkRoot(workRoot);
|
||||||
if (cleanupFailure != null) {
|
if (cleanupFailure != null) {
|
||||||
failure.addSuppressed(cleanupFailure);
|
failure.addSuppressed(cleanupFailure);
|
||||||
@@ -120,7 +132,12 @@ final class RealPackProbeSupport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static List<Throwable> settleAndDrain() throws InterruptedException {
|
static List<Throwable> settleAndDrain(Engine engine) throws InterruptedException {
|
||||||
|
if (IrisPlatforms.isBound() && IrisPlatforms.get() instanceof StubPlatform) {
|
||||||
|
ProbeBackgroundTasks.await(engine);
|
||||||
|
awaitGenerationWorkers();
|
||||||
|
return drainReported();
|
||||||
|
}
|
||||||
List<Throwable> drained = new ArrayList<>();
|
List<Throwable> drained = new ArrayList<>();
|
||||||
long quietSince = System.currentTimeMillis();
|
long quietSince = System.currentTimeMillis();
|
||||||
long start = quietSince;
|
long start = quietSince;
|
||||||
@@ -139,6 +156,54 @@ final class RealPackProbeSupport {
|
|||||||
return drained;
|
return drained;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void awaitGenerationWorkers() throws InterruptedException {
|
||||||
|
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(15);
|
||||||
|
List<ForkJoinPool> pools = new ArrayList<>(3);
|
||||||
|
for (MultiBurst burst : List.of(MultiBurst.hydrology, MultiBurst.ioBurst, MultiBurst.burst)) {
|
||||||
|
try {
|
||||||
|
ForkJoinPool pool = burst.submit((Callable<ForkJoinPool>) ForkJoinTask::getPool)
|
||||||
|
.get(Math.max(1L, deadline - System.nanoTime()), TimeUnit.NANOSECONDS);
|
||||||
|
if (pool != null) {
|
||||||
|
pools.add(pool);
|
||||||
|
}
|
||||||
|
} catch (ExecutionException | TimeoutException failure) {
|
||||||
|
throw new IllegalStateException("Probe generation worker barrier failed", failure);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
long[] previous = quietWorkerSnapshot(pools, deadline);
|
||||||
|
while (true) {
|
||||||
|
long[] current = quietWorkerSnapshot(pools, deadline);
|
||||||
|
if (Arrays.equals(previous, current)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
previous = current;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static long[] quietWorkerSnapshot(List<ForkJoinPool> pools, long deadline) throws InterruptedException {
|
||||||
|
long[] counts = new long[pools.size()];
|
||||||
|
for (int index = 0; index < pools.size(); index++) {
|
||||||
|
ForkJoinPool pool = pools.get(index);
|
||||||
|
while (true) {
|
||||||
|
if (Thread.interrupted()) {
|
||||||
|
throw new InterruptedException("Probe generation worker barrier interrupted");
|
||||||
|
}
|
||||||
|
if (System.nanoTime() >= deadline) {
|
||||||
|
throw new IllegalStateException("Probe generation workers did not settle within 15 seconds");
|
||||||
|
}
|
||||||
|
long count = pool.getStealCount();
|
||||||
|
boolean quiet = pool.isQuiescent();
|
||||||
|
long settledCount = pool.getStealCount();
|
||||||
|
if (quiet && count == settledCount) {
|
||||||
|
counts[index] = settledCount;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
LockSupport.parkNanos(TimeUnit.MICROSECONDS.toNanos(100));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return counts;
|
||||||
|
}
|
||||||
|
|
||||||
static void printReports(String logPrefix, String label, List<Throwable> reports) {
|
static void printReports(String logPrefix, String label, List<Throwable> reports) {
|
||||||
if (reports.isEmpty()) {
|
if (reports.isEmpty()) {
|
||||||
return;
|
return;
|
||||||
@@ -305,7 +370,7 @@ final class RealPackProbeSupport {
|
|||||||
router.preloadActiveRuntimes();
|
router.preloadActiveRuntimes();
|
||||||
}
|
}
|
||||||
long readyNanos = System.nanoTime() - started;
|
long readyNanos = System.nanoTime() - started;
|
||||||
List<Throwable> initializationReports = settleAndDrain();
|
List<Throwable> initializationReports = settleAndDrain(engine);
|
||||||
printReports(logPrefix, runLabel + " engine-init reports", initializationReports);
|
printReports(logPrefix, runLabel + " engine-init reports", initializationReports);
|
||||||
if (bindings != null && !initializationReports.isEmpty()) {
|
if (bindings != null && !initializationReports.isEmpty()) {
|
||||||
IllegalStateException failure = new IllegalStateException("Native engine initialization reported failures");
|
IllegalStateException failure = new IllegalStateException("Native engine initialization reported failures");
|
||||||
@@ -317,6 +382,7 @@ final class RealPackProbeSupport {
|
|||||||
return new EngineSession(this, data, engine, readyNanos);
|
return new EngineSession(this, data, engine, readyNanos);
|
||||||
} catch (Throwable failure) {
|
} catch (Throwable failure) {
|
||||||
Throwable cleanupFailure = closeResources(engine, data);
|
Throwable cleanupFailure = closeResources(engine, data);
|
||||||
|
clearRuntime();
|
||||||
sessionOpen = false;
|
sessionOpen = false;
|
||||||
if (cleanupFailure != null) {
|
if (cleanupFailure != null) {
|
||||||
failure.addSuppressed(cleanupFailure);
|
failure.addSuppressed(cleanupFailure);
|
||||||
@@ -347,8 +413,7 @@ final class RealPackProbeSupport {
|
|||||||
failure = bindingFailure;
|
failure = bindingFailure;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
IrisPlatforms.unbind();
|
clearRuntime();
|
||||||
StubPlatform.errorSink(null);
|
|
||||||
Throwable cleanupFailure = deleteWorkRoot(workRoot);
|
Throwable cleanupFailure = deleteWorkRoot(workRoot);
|
||||||
if (cleanupFailure != null) {
|
if (cleanupFailure != null) {
|
||||||
failure = appendFailure(failure, cleanupFailure);
|
failure = appendFailure(failure, cleanupFailure);
|
||||||
@@ -494,6 +559,15 @@ final class RealPackProbeSupport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void clearRuntime() {
|
||||||
|
IrisPlatforms.unbind();
|
||||||
|
IrisServices.remove(PreservationRegistry.class);
|
||||||
|
IrisServices.remove(EngineWorldManagerProvider.class);
|
||||||
|
IrisServices.remove(EngineEffectsProvider.class);
|
||||||
|
IrisServices.remove(EnginePlatformHooks.class);
|
||||||
|
StubPlatform.errorSink(null);
|
||||||
|
}
|
||||||
|
|
||||||
private static void configureRuntime(File platformRoot, boolean hydrologyGeneratedVerification, RuntimeBindings bindings) {
|
private static void configureRuntime(File platformRoot, boolean hydrologyGeneratedVerification, RuntimeBindings bindings) {
|
||||||
REPORTED.clear();
|
REPORTED.clear();
|
||||||
StubPlatform.bindGenerationStateHandlers();
|
StubPlatform.bindGenerationStateHandlers();
|
||||||
|
|||||||
@@ -328,7 +328,7 @@ public final class Terrain3DProbe {
|
|||||||
sections.addAll(renderer.write());
|
sections.addAll(renderer.write());
|
||||||
}
|
}
|
||||||
topology = terrainTopology.inspect();
|
topology = terrainTopology.inspect();
|
||||||
failReported("generation", RealPackProbeSupport.settleAndDrain());
|
failReported("generation", RealPackProbeSupport.settleAndDrain(engine));
|
||||||
}
|
}
|
||||||
List<String> failures = coverageFailures(evidence, configuration.requiredBiomes(), configuration.strictGeometry());
|
List<String> failures = coverageFailures(evidence, configuration.requiredBiomes(), configuration.strictGeometry());
|
||||||
Result result = new Result(failures.isEmpty() ? "PASS" : "FAIL", configuration, generatedChunks,
|
Result result = new Result(failures.isEmpty() ? "PASS" : "FAIL", configuration, generatedChunks,
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ public final class TransitionProbe {
|
|||||||
}
|
}
|
||||||
require(historicalBiomes.equals(router.biomes().snapshot(0, 0).orElseThrow()),
|
require(historicalBiomes.equals(router.biomes().snapshot(0, 0).orElseThrow()),
|
||||||
"Historical biome data changed after generation");
|
"Historical biome data changed after generation");
|
||||||
for (Throwable failure : RealPackProbeSupport.settleAndDrain()) {
|
for (Throwable failure : RealPackProbeSupport.settleAndDrain(engine)) {
|
||||||
throw new IllegalStateException("Generation reported a failure", failure);
|
throw new IllegalStateException("Generation reported a failure", failure);
|
||||||
}
|
}
|
||||||
require(bindings.captures > 0, "Promotion did not capture historical terrain");
|
require(bindings.captures > 0, "Promotion did not capture historical terrain");
|
||||||
|
|||||||
@@ -0,0 +1,131 @@
|
|||||||
|
package art.arcane.iris.generation.runtime;
|
||||||
|
|
||||||
|
import art.arcane.iris.probe.StubPlatform;
|
||||||
|
import art.arcane.iris.spi.IrisPlatforms;
|
||||||
|
import art.arcane.iris.testsupport.IrisRuntimeState;
|
||||||
|
import org.junit.After;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Rule;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.rules.TemporaryFolder;
|
||||||
|
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.ExecutionException;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
import java.util.concurrent.Future;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.TimeoutException;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.concurrent.locks.LockSupport;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertThrows;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public final class ProbeBackgroundTasksTest {
|
||||||
|
@Rule
|
||||||
|
public final TemporaryFolder temporary = new TemporaryFolder();
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void bindRuntime() {
|
||||||
|
IrisPlatforms.bind(new StubPlatform(temporary.getRoot()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@After
|
||||||
|
public void resetRuntime() {
|
||||||
|
IrisRuntimeState.reset();
|
||||||
|
StubPlatform.errorSink(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 15_000L)
|
||||||
|
public void waitsForTrackedContinuationBeforeReturning() throws Exception {
|
||||||
|
EngineBackgroundTasks tasks = new EngineBackgroundTasks();
|
||||||
|
tasks.openBackgroundTaskAdmission();
|
||||||
|
CountDownLatch firstEntered = new CountDownLatch(1);
|
||||||
|
CountDownLatch firstRelease = new CountDownLatch(1);
|
||||||
|
CountDownLatch continuationEntered = new CountDownLatch(1);
|
||||||
|
CountDownLatch continuationRelease = new CountDownLatch(1);
|
||||||
|
try (ExecutorService closer = Executors.newSingleThreadExecutor()) {
|
||||||
|
try {
|
||||||
|
tasks.scheduleTrackedTask(() -> {
|
||||||
|
awaitRelease(firstEntered, firstRelease);
|
||||||
|
tasks.scheduleTrackedTask(() -> awaitRelease(continuationEntered, continuationRelease));
|
||||||
|
});
|
||||||
|
assertTrue(firstEntered.await(5, TimeUnit.SECONDS));
|
||||||
|
Future<?> completion = startDrain(closer, tasks);
|
||||||
|
assertThrows(TimeoutException.class, () -> completion.get(100, TimeUnit.MILLISECONDS));
|
||||||
|
firstRelease.countDown();
|
||||||
|
assertTrue(continuationEntered.await(5, TimeUnit.SECONDS));
|
||||||
|
assertThrows(TimeoutException.class, () -> completion.get(100, TimeUnit.MILLISECONDS));
|
||||||
|
continuationRelease.countDown();
|
||||||
|
completion.get(5, TimeUnit.SECONDS);
|
||||||
|
} finally {
|
||||||
|
firstRelease.countDown();
|
||||||
|
continuationRelease.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 15_000L)
|
||||||
|
public void propagatesAnAsynchronousTrackedFailure() throws Exception {
|
||||||
|
EngineBackgroundTasks tasks = new EngineBackgroundTasks();
|
||||||
|
tasks.openBackgroundTaskAdmission();
|
||||||
|
CountDownLatch entered = new CountDownLatch(1);
|
||||||
|
CountDownLatch release = new CountDownLatch(1);
|
||||||
|
IllegalStateException failure = new IllegalStateException("tracked generation failure");
|
||||||
|
CountDownLatch reported = new CountDownLatch(1);
|
||||||
|
StubPlatform.errorSink(error -> reported.countDown());
|
||||||
|
try (ExecutorService closer = Executors.newSingleThreadExecutor()) {
|
||||||
|
try {
|
||||||
|
tasks.scheduleTrackedTask(() -> {
|
||||||
|
awaitRelease(entered, release);
|
||||||
|
throw failure;
|
||||||
|
});
|
||||||
|
assertTrue(entered.await(5, TimeUnit.SECONDS));
|
||||||
|
Future<?> completion = startDrain(closer, tasks);
|
||||||
|
assertThrows(TimeoutException.class, () -> completion.get(100, TimeUnit.MILLISECONDS));
|
||||||
|
release.countDown();
|
||||||
|
ExecutionException observed = assertThrows(ExecutionException.class,
|
||||||
|
() -> completion.get(5, TimeUnit.SECONDS));
|
||||||
|
assertSame(failure, observed.getCause().getCause().getCause());
|
||||||
|
assertTrue(reported.await(5, TimeUnit.SECONDS));
|
||||||
|
} finally {
|
||||||
|
release.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Future<?> startDrain(ExecutorService closer, EngineBackgroundTasks tasks) {
|
||||||
|
AtomicReference<Thread> drainingThread = new AtomicReference<>();
|
||||||
|
Future<?> completion = closer.submit(() -> {
|
||||||
|
drainingThread.set(Thread.currentThread());
|
||||||
|
ProbeBackgroundTasks.await(tasks);
|
||||||
|
});
|
||||||
|
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(5);
|
||||||
|
while (System.nanoTime() < deadline) {
|
||||||
|
Thread thread = drainingThread.get();
|
||||||
|
if (thread != null && thread.getState() == Thread.State.TIMED_WAITING) {
|
||||||
|
for (StackTraceElement frame : thread.getStackTrace()) {
|
||||||
|
if (frame.getClassName().equals(EngineBackgroundTasks.class.getName())
|
||||||
|
&& frame.getMethodName().equals("drainBackgroundTasks")) {
|
||||||
|
return completion;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LockSupport.parkNanos(TimeUnit.MILLISECONDS.toNanos(1));
|
||||||
|
}
|
||||||
|
throw new AssertionError("Probe barrier did not await its initial task");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void awaitRelease(CountDownLatch entered, CountDownLatch release) {
|
||||||
|
entered.countDown();
|
||||||
|
try {
|
||||||
|
assertTrue(release.await(5, TimeUnit.SECONDS));
|
||||||
|
} catch (InterruptedException interruption) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IllegalStateException(interruption);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -211,7 +211,7 @@ public final class ObjectContinuationProbeTest {
|
|||||||
assertArrayEquals(oldReceipt, SavedTerrainChunk.readReceipt(world.worldFolder().toPath(), 0, 0));
|
assertArrayEquals(oldReceipt, SavedTerrainChunk.readReceipt(world.worldFolder().toPath(), 0, 0));
|
||||||
assertEquals(originalActivation, reopened.resolveActivation(0, 0).activationId());
|
assertEquals(originalActivation, reopened.resolveActivation(0, 0).activationId());
|
||||||
assertTrue(reopened.semantics(1, 0).orElseThrow().objectKeys().contains(fixture.objectKey()));
|
assertTrue(reopened.semantics(1, 0).orElseThrow().objectKeys().contains(fixture.objectKey()));
|
||||||
assertTrue("Generation must not report swallowed errors", RealPackProbeSupport.settleAndDrain().isEmpty());
|
assertTrue("Generation must not report swallowed errors", RealPackProbeSupport.settleAndDrain(engine).isEmpty());
|
||||||
} finally {
|
} finally {
|
||||||
engine.close();
|
engine.close();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,101 @@
|
|||||||
|
package art.arcane.iris.probe;
|
||||||
|
|
||||||
|
import art.arcane.iris.generation.concurrent.MultiBurst;
|
||||||
|
import art.arcane.iris.generation.runtime.EngineEffectsProvider;
|
||||||
|
import art.arcane.iris.generation.runtime.EnginePlatformHooks;
|
||||||
|
import art.arcane.iris.generation.runtime.EngineWorldManagerProvider;
|
||||||
|
import art.arcane.iris.generation.runtime.PreservationRegistry;
|
||||||
|
import art.arcane.iris.spi.IrisPlatforms;
|
||||||
|
import art.arcane.iris.spi.IrisServices;
|
||||||
|
import art.arcane.iris.testsupport.IrisRuntimeState;
|
||||||
|
import art.arcane.iris.testsupport.PlatformLeakGuard;
|
||||||
|
import art.arcane.iris.world.task.J;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
import static org.junit.Assert.assertNotNull;
|
||||||
|
import static org.junit.Assert.assertNull;
|
||||||
|
import static org.junit.Assert.assertFalse;
|
||||||
|
import static org.junit.Assert.assertSame;
|
||||||
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
|
public final class RealPackProbeSupportBarrierTest {
|
||||||
|
@AfterClass
|
||||||
|
public static void resetRuntime() {
|
||||||
|
IrisRuntimeState.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void closesOwnedRuntimeServicesAndPreservesUnrelatedServices() throws Exception {
|
||||||
|
URL resource = RealPackProbeSupportBarrierTest.class.getResource("/seed-cohesion");
|
||||||
|
assertNotNull(resource);
|
||||||
|
IrisServices.register(String.class, "unrelated");
|
||||||
|
try {
|
||||||
|
try (RealPackProbeSupport.Workspace workspace = RealPackProbeSupport.openWorkspace(
|
||||||
|
new File(resource.toURI()), "cohesion", "[workspace-cleanup]")) {
|
||||||
|
assertNotNull(IrisServices.getOrNull(EngineEffectsProvider.class));
|
||||||
|
}
|
||||||
|
assertFalse(IrisPlatforms.isBound());
|
||||||
|
assertNull(IrisServices.getOrNull(PreservationRegistry.class));
|
||||||
|
assertNull(IrisServices.getOrNull(EngineWorldManagerProvider.class));
|
||||||
|
assertNull(IrisServices.getOrNull(EngineEffectsProvider.class));
|
||||||
|
assertNull(IrisServices.getOrNull(EnginePlatformHooks.class));
|
||||||
|
assertEquals("unrelated", IrisServices.get(String.class));
|
||||||
|
} finally {
|
||||||
|
IrisServices.remove(String.class);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(timeout = 30_000L)
|
||||||
|
public void waitsForCrossPoolContinuationsAndCollectsTheirErrors() throws Exception {
|
||||||
|
URL resource = RealPackProbeSupportBarrierTest.class.getResource("/seed-cohesion");
|
||||||
|
assertNotNull(resource);
|
||||||
|
try (RealPackProbeSupport.Workspace workspace = RealPackProbeSupport.openWorkspace(
|
||||||
|
new File(resource.toURI()), "cohesion", "[worker-barrier]");
|
||||||
|
RealPackProbeSupport.EngineSession session = workspace.openEngine(1337L, false, "barrier")) {
|
||||||
|
for (int repeat = 0; repeat < 40; repeat++) {
|
||||||
|
verifyCrossPoolContinuation(session, MultiBurst.ioBurst, MultiBurst.hydrology);
|
||||||
|
verifyCrossPoolContinuation(session, MultiBurst.hydrology, MultiBurst.ioBurst);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PlatformLeakGuard.requireClean("closed real-pack workspace", "end");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void verifyCrossPoolContinuation(RealPackProbeSupport.EngineSession session,
|
||||||
|
MultiBurst first, MultiBurst second) throws Exception {
|
||||||
|
IllegalStateException failure = new IllegalStateException("nested generation failure");
|
||||||
|
CountDownLatch entered = new CountDownLatch(1);
|
||||||
|
CountDownLatch release = new CountDownLatch(1);
|
||||||
|
try {
|
||||||
|
first.lazy(() -> {
|
||||||
|
entered.countDown();
|
||||||
|
try {
|
||||||
|
assertTrue(release.await(5, TimeUnit.SECONDS));
|
||||||
|
} catch (InterruptedException interruption) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
|
throw new IllegalStateException(interruption);
|
||||||
|
}
|
||||||
|
Runnable failedTask = () -> {
|
||||||
|
throw failure;
|
||||||
|
};
|
||||||
|
second.lazy(() -> first.lazy(() -> second.lazy(() -> J.a(failedTask))));
|
||||||
|
});
|
||||||
|
assertTrue(entered.await(5, TimeUnit.SECONDS));
|
||||||
|
release.countDown();
|
||||||
|
List<Throwable> reports = RealPackProbeSupport.settleAndDrain(session.engine());
|
||||||
|
assertEquals(1, reports.size());
|
||||||
|
assertSame(failure, reports.getFirst());
|
||||||
|
assertTrue(RealPackProbeSupport.drainReported().isEmpty());
|
||||||
|
} finally {
|
||||||
|
release.countDown();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user