This commit is contained in:
Brian Neumann-Fopiano committed 2026-07-18 19:16:14 -04:00
1 parent ad41b6795c
commit 1d194e880b
12 files changed
+1208 -128

No files matched your search

@@ -153,9 +153,20 @@ public class SchemaBuilderParityTest {
.getJSONObject(stilt.getString("$ref").substring("#/definitions/".length()));
JSONObject stiltProperties = stiltDefinition.getJSONObject("properties");
JSONObject maxDepth = stiltProperties.getJSONObject("maxDepth");
JSONObject overboreShape = properties.getJSONObject("overboreShape");
JSONObject overboreErosionStrength = properties.getJSONObject("overboreErosionStrength");
JSONObject overboreErosionFrequency = properties.getJSONObject("overboreErosionFrequency");
String overboreShapeDefinition = overboreShape.getString("$ref")
.substring("#/definitions/".length());
assertTrue(properties.has("structures"));
assertTrue(properties.has("distribution"));
assertEquals(List.of("BOX", "ROUNDED", "ERODED"), oneOfValues(
schema.getJSONObject("definitions"), overboreShapeDefinition));
assertEquals(0D, overboreErosionStrength.getDouble("minimum"), 0D);
assertEquals(1D, overboreErosionStrength.getDouble("maximum"), 0D);
assertEquals(0.001D, overboreErosionFrequency.getDouble("minimum"), 0D);
assertEquals(1D, overboreErosionFrequency.getDouble("maximum"), 0D);
assertEquals("object", stilt.getString("type"));
assertTrue(stiltProperties.has("palette"));
assertTrue(stiltProperties.has("supportNonOccluding"));
@@ -190,6 +201,15 @@ public class SchemaBuilderParityTest {
return values;
}
private static List<String> oneOfValues(JSONObject definitions, String key) {
JSONArray array = definitions.getJSONObject(key).getJSONArray("oneOf");
List<String> values = new ArrayList<>(array.length());
for (int index = 0; index < array.length(); index++) {
values.add(array.getJSONObject(index).getString("const"));
}
return values;
}
@Desc("Registry dependent model.")
public static class RegistryModel {
@Desc("Potion field.")
@@ -0,0 +1,102 @@
package art.arcane.iris.core.tools;
import art.arcane.iris.core.runtime.WorldRuntimeControlService;
import art.arcane.iris.util.common.plugin.VolmitSender;
import org.bukkit.Chunk;
import org.bukkit.Location;
import org.bukkit.World;
import org.bukkit.entity.Player;
import org.junit.Test;
import org.mockito.InOrder;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CompletionException;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.inOrder;
import static org.mockito.Mockito.mock;
public class IrisCreatorTeleportTest {
@Test
public void createTeleportTarget_onlySelectsProductionPlayerSender() {
Player player = mock(Player.class);
VolmitSender playerSender = mock(VolmitSender.class);
VolmitSender consoleSender = mock(VolmitSender.class);
doReturn(true).when(playerSender).isPlayer();
doReturn(player).when(playerSender).player();
doReturn(false).when(consoleSender).isPlayer();
assertSame(player, IrisCreator.createTeleportTarget(playerSender, false, false));
assertNull(IrisCreator.createTeleportTarget(consoleSender, false, false));
assertNull(IrisCreator.createTeleportTarget(playerSender, true, false));
assertNull(IrisCreator.createTeleportTarget(playerSender, false, true));
}
@Test
public void teleportSenderToCreatedWorld_loadsAndResolvesSafeEntryBeforeTeleport() {
Player player = mock(Player.class);
World world = mock(World.class);
Chunk chunk = mock(Chunk.class);
WorldRuntimeControlService runtimeControl = mock(WorldRuntimeControlService.class);
Location anchor = new Location(world, 32.5D, 80D, -15.5D);
Location safeEntry = new Location(world, 32.5D, 94D, -15.5D);
doReturn(anchor).when(runtimeControl).resolveEntryAnchor(world);
doReturn(CompletableFuture.completedFuture(chunk))
.when(runtimeControl).requestChunkAsync(world, 2, -1, true);
doReturn(CompletableFuture.completedFuture(safeEntry))
.when(runtimeControl).resolveSafeEntry(world, anchor);
doReturn(CompletableFuture.completedFuture(true))
.when(runtimeControl).teleport(player, safeEntry);
CompletableFuture<Boolean> result = IrisCreator.teleportSenderToCreatedWorld(player, world, runtimeControl);
assertTrue(result.join());
InOrder order = inOrder(runtimeControl);
order.verify(runtimeControl).resolveEntryAnchor(world);
order.verify(runtimeControl).requestChunkAsync(world, 2, -1, true);
order.verify(runtimeControl).resolveSafeEntry(world, anchor);
order.verify(runtimeControl).teleport(player, safeEntry);
}
@Test
public void teleportSenderToCreatedWorld_preservesFalseTeleportResult() {
Player player = mock(Player.class);
World world = mock(World.class);
WorldRuntimeControlService runtimeControl = mock(WorldRuntimeControlService.class);
Location anchor = new Location(world, 0.5D, 80D, 0.5D);
doReturn(anchor).when(runtimeControl).resolveEntryAnchor(world);
doReturn(CompletableFuture.completedFuture(mock(Chunk.class)))
.when(runtimeControl).requestChunkAsync(world, 0, 0, true);
doReturn(CompletableFuture.completedFuture(anchor))
.when(runtimeControl).resolveSafeEntry(world, anchor);
doReturn(CompletableFuture.completedFuture(false))
.when(runtimeControl).teleport(player, anchor);
CompletableFuture<Boolean> result = IrisCreator.teleportSenderToCreatedWorld(player, world, runtimeControl);
assertFalse(result.join());
}
@Test
public void teleportSenderToCreatedWorld_failsWhenSafeEntryCannotResolve() {
Player player = mock(Player.class);
World world = mock(World.class);
WorldRuntimeControlService runtimeControl = mock(WorldRuntimeControlService.class);
Location anchor = new Location(world, 0.5D, 80D, 0.5D);
doReturn("irisworld").when(world).getName();
doReturn(anchor).when(runtimeControl).resolveEntryAnchor(world);
doReturn(CompletableFuture.completedFuture(mock(Chunk.class)))
.when(runtimeControl).requestChunkAsync(world, 0, 0, true);
doReturn(CompletableFuture.completedFuture(null))
.when(runtimeControl).resolveSafeEntry(world, anchor);
CompletableFuture<Boolean> result = IrisCreator.teleportSenderToCreatedWorld(player, world, runtimeControl);
assertThrows(CompletionException.class, result::join);
}
}
@@ -30,6 +30,7 @@ import art.arcane.iris.engine.object.IrisObject;
import art.arcane.iris.engine.object.IrisPosition;
import art.arcane.iris.engine.object.IrisRegion;
import art.arcane.iris.engine.object.IrisStructure;
import art.arcane.iris.engine.object.IrisStructureCarveShape;
import art.arcane.iris.engine.object.IrisStructurePlacement;
import art.arcane.iris.engine.object.NativeStructureSuppression;
import art.arcane.iris.engine.object.ObjectPlaceMode;
@@ -469,8 +470,41 @@ public class IrisStructureLocatorContractTest {
KList<PlacedStructurePiece> pieces = new KList<>();
pieces.add(piece(0, 60, 0, 1, 80, 1));
assertEquals(Integer.valueOf(-17), IrisStructureLocator.resolveUndergroundBurialShift(
assertEquals(Integer.valueOf(-14), IrisStructureLocator.resolveUndergroundBurialShift(
engine, pieces, placement, 60, -63, 319));
placement.setOverboreErosionStrength(0D);
assertEquals(Integer.valueOf(-1), IrisStructureLocator.resolveUndergroundBurialShift(
engine, pieces, placement, 60, -63, 319));
placement.setOverboreShape(IrisStructureCarveShape.ROUNDED);
assertEquals(Integer.valueOf(-1), IrisStructureLocator.resolveUndergroundBurialShift(
engine, pieces, placement, 60, -63, 319));
placement.setOverboreHeight(0);
assertEquals(Integer.valueOf(0), IrisStructureLocator.resolveUndergroundBurialShift(
engine, pieces, placement, 60, -63, 319));
}
@Test
public void zeroOverboreRadiusDoesNotExpandTheBurialEnvelope() {
Engine engine = mock(Engine.class);
when(engine.getMinHeight()).thenReturn(-64);
when(engine.getHeight(anyInt(), anyInt(), eq(true))).thenReturn(164);
IrisStructurePlacement placement = new IrisStructurePlacement()
.setUnderground(true)
.setMinHeight(-64)
.setMaxHeight(100)
.setOverbore(true)
.setOverboreShape(IrisStructureCarveShape.ROUNDED)
.setOverboreRadius(0)
.setOverboreHeight(0);
KList<PlacedStructurePiece> pieces = new KList<>();
pieces.add(piece(4, 60, 7, 4, 80, 7));
assertEquals(Integer.valueOf(0), IrisStructureLocator.resolveUndergroundBurialShift(
engine, pieces, placement, 60, -63, 319));
verify(engine, times(1)).getHeight(4, 7, true);
}
@Test
@@ -1,5 +1,6 @@
package art.arcane.iris.engine.mantle.components;
import art.arcane.iris.engine.object.IrisStructureCarveShape;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
@@ -8,47 +9,56 @@ import static org.junit.Assert.assertTrue;
public class IrisStructureComponentOverboreTest {
@Test
public void exactPieceInteriorAlwaysCarves() {
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(0.0, 0.0));
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(0.0, 1.0));
}
@Test
public void lowNoiseRemovesTheFormerFlatShell() {
double oneBlockAtRadiusSix = 1.0 / 6.0;
public void exactStructureFootprintAlwaysCarves() {
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(
oneBlockAtRadiusSix * oneBlockAtRadiusSix, 0.0));
assertFalse(IrisStructureComponent.shouldCarveOverboreCell(0.25 * 0.25, 0.0));
IrisStructureCarveShape.ERODED, 0D, 0D, 1D));
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(
IrisStructureCarveShape.ROUNDED, 0D, 0D, 1D));
}
@Test
public void maximumNoiseStopsAtConfiguredReach() {
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(1.0, 1.0));
assertFalse(IrisStructureComponent.shouldCarveOverboreCell(1.000001, 1.0));
public void boxModeKeepsStraightCandidateVolume() {
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(
IrisStructureCarveShape.BOX, 100D, 0D, 1D));
}
@Test
public void diagonalCornersUseEuclideanFalloff() {
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(1.0, 1.0));
assertFalse(IrisStructureComponent.shouldCarveOverboreCell(0.8 * 0.8 + 0.8 * 0.8, 1.0));
public void roundedModeStopsAtConfiguredReach() {
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(
IrisStructureCarveShape.ROUNDED, 1D, 0D, 1D));
assertFalse(IrisStructureComponent.shouldCarveOverboreCell(
IrisStructureCarveShape.ROUNDED, 1.000001D, 1D, 0D));
}
@Test
public void boundaryNoiseIsClampedAndDeterministic() {
assertEquals(0.2, IrisStructureComponent.overboreBoundaryLimit(-10.0), 0.0);
assertEquals(1.0, IrisStructureComponent.overboreBoundaryLimit(10.0), 0.0);
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(0.36, 0.5));
assertFalse(IrisStructureComponent.shouldCarveOverboreCell(0.360001, 0.5));
public void zeroErosionStrengthMatchesRoundedMode() {
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(
IrisStructureCarveShape.ERODED, 1D, 0D, 0D));
assertFalse(IrisStructureComponent.shouldCarveOverboreCell(
IrisStructureCarveShape.ERODED, 1.000001D, 1D, 0D));
}
@Test
public void candidateExtensionsDoNotScanImpossibleOuterShell() {
assertEquals(6, IrisStructureComponent.overboreSideExtension(6));
assertEquals(18, IrisStructureComponent.overboreUpExtension(10.0));
public void zeroConfiguredCeilingHasNoErodedExtension() {
assertEquals(0D, IrisStructureComponent.erodedUpReach(null, 0.16D, 1D, 0, 0, 0), 0D);
}
long oldVolume = (41L + 18L) * (31L + 27L) * (35L + 18L);
long newVolume = (41L + 12L) * (31L + 18L) * (35L + 12L);
assertTrue(newVolume < oldVolume);
@Test
public void zeroErosionStrengthKeepsTheRoundedCeiling() {
assertEquals(10D, IrisStructureComponent.erodedUpReach(null, 0.16D, 0D, 0, 0, 10), 0D);
}
@Test
public void erosionStrengthAndNoiseAreClampedDeterministically() {
assertEquals(0.2D, IrisStructureComponent.overboreBoundaryLimit(-10D, 0.8D), 1.0E-12D);
assertEquals(1D, IrisStructureComponent.overboreBoundaryLimit(10D, 0.8D), 0D);
assertEquals(0.5D, IrisStructureComponent.overboreBoundaryLimit(0.5D, 1D), 0D);
assertEquals(1D, IrisStructureComponent.overboreBoundaryLimit(0D, -1D), 0D);
assertEquals(0D, IrisStructureComponent.overboreBoundaryLimit(0D, 10D), 0D);
assertTrue(IrisStructureComponent.shouldCarveOverboreCell(
null, 0.25D, 0.5D, 1D));
assertFalse(IrisStructureComponent.shouldCarveOverboreCell(
null, 0.250001D, 0.5D, 1D));
}
@Test
@@ -0,0 +1,192 @@
package art.arcane.iris.engine.mantle.components;
import art.arcane.iris.engine.framework.PlacedStructurePiece;
import art.arcane.iris.engine.object.IrisObject;
import art.arcane.iris.engine.object.IrisObjectRotation;
import art.arcane.iris.spi.PlatformBlockState;
import art.arcane.iris.util.common.math.IrisBlockVector;
import art.arcane.volmlib.util.collection.KList;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class StructureCarvingFootprintTest {
@Test
public void preservesIrregularFootprintInsteadOfFillingItsBoundingBox() {
PlacedStructurePiece piece = piece(10, 40, 20, IrisObjectRotation.of(0, 0, 0),
block(0, 0, 0), block(1, 0, 0), block(2, 0, 0),
block(0, 0, 1), block(0, 0, 2));
StructureCarvingFootprint footprint = StructureCarvingFootprint.from(pieces(piece), 0);
assertNotNull(footprint);
assertEquals(10, footprint.minX());
assertEquals(12, footprint.maxX());
assertEquals(20, footprint.minZ());
assertEquals(22, footprint.maxZ());
assertEquals(3, footprint.width());
assertEquals(3, footprint.depth());
assertEquals(0L, footprint.distanceSquaredAt(12, 20));
assertEquals(4L, footprint.distanceSquaredAt(12, 22));
}
@Test
public void reportsExactDistanceAndNearestColumnHeights() {
PlacedStructurePiece piece = piece(0, 0, 0, IrisObjectRotation.of(0, 0, 0),
block(0, 10, 0), block(0, 12, 0),
block(4, 30, 0), block(4, 35, 0));
StructureCarvingFootprint footprint = StructureCarvingFootprint.from(pieces(piece), 2);
StructureCarvingFootprint.Column nearestRight = footprint.sample(3, 2);
StructureCarvingFootprint.Column tied = footprint.sample(2, 0);
assertNotNull(nearestRight);
assertEquals(5L, nearestRight.distanceSquared());
assertEquals(30, nearestRight.sourceMinY());
assertEquals(35, nearestRight.sourceMaxY());
assertNotNull(tied);
assertEquals(4L, tied.distanceSquared());
assertEquals(10, tied.sourceMinY());
assertEquals(12, tied.sourceMaxY());
}
@Test
public void rotatesAndMergesOverlappingColumns() {
PlacedStructurePiece rotated = piece(10, 20, 30, IrisObjectRotation.of(0, 90, 0),
block(2, 1, 0));
PlacedStructurePiece overlapping = piece(10, 5, 28, IrisObjectRotation.of(0, 0, 0),
block(0, 3, 0), block(0, 9, 0));
StructureCarvingFootprint footprint = StructureCarvingFootprint.from(
pieces(rotated, overlapping), 0);
assertNotNull(footprint);
assertEquals(10, footprint.minX());
assertEquals(10, footprint.maxX());
assertEquals(28, footprint.minZ());
assertEquals(28, footprint.maxZ());
assertEquals(0L, footprint.distanceSquaredAt(10, 28));
assertEquals(8, footprint.sourceMinYAt(10, 28));
assertEquals(21, footprint.sourceMaxYAt(10, 28));
}
@Test
public void matchesBruteForceSquaredDistancesAcrossSparseColumns() {
int[][] sourceBlocks = new int[][]{
block(0, 2, 0), block(3, 4, 1), block(1, 6, 4), block(5, 8, 5)
};
PlacedStructurePiece piece = piece(
-8, 30, 11, IrisObjectRotation.of(0, 0, 0), sourceBlocks);
StructureCarvingFootprint footprint = StructureCarvingFootprint.from(pieces(piece), 3);
assertNotNull(footprint);
for (int z = footprint.minZ(); z <= footprint.maxZ(); z++) {
for (int x = footprint.minX(); x <= footprint.maxX(); x++) {
long expected = Long.MAX_VALUE;
for (int[] source : sourceBlocks) {
long deltaX = (long) x - (piece.getX() + source[0]);
long deltaZ = (long) z - (piece.getZ() + source[2]);
expected = Math.min(expected, deltaX * deltaX + deltaZ * deltaZ);
}
assertEquals(expected, footprint.distanceSquaredAt(x, z));
}
}
}
@Test
public void returnsNullForEmptyInvalidOrOversizedInputs() {
assertNull(StructureCarvingFootprint.from(null, 0));
assertNull(StructureCarvingFootprint.from(new KList<>(), 0));
KList<PlacedStructurePiece> invalidPieces = new KList<>();
invalidPieces.add((PlacedStructurePiece) null);
invalidPieces.add(new PlacedStructurePiece(
null, null, 0, 0, 0, null, 0, 0, 0, 0, 0, 0));
assertNull(StructureCarvingFootprint.from(invalidPieces, 0));
PlacedStructurePiece piece = piece(0, 0, 0, IrisObjectRotation.of(0, 0, 0), block(0, 0, 0));
assertNull(StructureCarvingFootprint.from(pieces(piece), 2, 24));
PlacedStructurePiece overflowing = piece(
Integer.MAX_VALUE, 0, 0, IrisObjectRotation.of(0, 0, 0), block(1, 0, 0));
assertNull(StructureCarvingFootprint.from(pieces(overflowing), 0));
try {
StructureCarvingFootprint.from(pieces(piece), -1);
fail("Expected negative padding to fail");
} catch (IllegalArgumentException e) {
assertEquals("padding must be non-negative", e.getMessage());
}
}
@Test
public void includesTheEntirePaddingBoundary() {
PlacedStructurePiece piece = piece(7, 14, -4, IrisObjectRotation.of(0, 0, 0), block(0, 0, 0));
StructureCarvingFootprint footprint = StructureCarvingFootprint.from(pieces(piece), 2, 25);
assertNotNull(footprint);
assertEquals(5, footprint.minX());
assertEquals(9, footprint.maxX());
assertEquals(-6, footprint.minZ());
assertEquals(-2, footprint.maxZ());
assertEquals(5, footprint.width());
assertEquals(5, footprint.depth());
assertTrue(footprint.contains(5, -6));
assertTrue(footprint.contains(9, -2));
assertFalse(footprint.contains(4, -6));
assertEquals(8L, footprint.distanceSquaredAt(5, -6));
assertNull(footprint.sample(4, -6));
}
@Test
public void explicitAirDoesNotExpandTheExteriorFootprint() {
IrisObject object = new IrisObject(1, 1, 1);
PlatformBlockState solid = mock(PlatformBlockState.class);
PlatformBlockState air = mock(PlatformBlockState.class);
when(air.isAir()).thenReturn(true);
object.getBlocks().put(new IrisBlockVector(0, 0, 0), solid);
object.getBlocks().put(new IrisBlockVector(8, 4, 8), air);
PlacedStructurePiece piece = new PlacedStructurePiece(
null, object, 10, 20, 30, IrisObjectRotation.of(0, 0, 0),
10, 20, 30, 18, 24, 38);
StructureCarvingFootprint footprint = StructureCarvingFootprint.from(pieces(piece), 0);
assertNotNull(footprint);
assertEquals(10, footprint.minX());
assertEquals(10, footprint.maxX());
assertEquals(30, footprint.minZ());
assertEquals(30, footprint.maxZ());
}
private static PlacedStructurePiece piece(int x, int y, int z, IrisObjectRotation rotation,
int[]... blocks) {
IrisObject object = new IrisObject(1, 1, 1);
PlatformBlockState state = mock(PlatformBlockState.class);
for (int[] block : blocks) {
object.getBlocks().put(new IrisBlockVector(block[0], block[1], block[2]), state);
}
return new PlacedStructurePiece(
null, object, x, y, z, rotation, x, y, z, x, y, z);
}
private static KList<PlacedStructurePiece> pieces(PlacedStructurePiece... pieces) {
KList<PlacedStructurePiece> result = new KList<>();
for (PlacedStructurePiece piece : pieces) {
result.add(piece);
}
return result;
}
private static int[] block(int x, int y, int z) {
return new int[]{x, y, z};
}
}
@@ -0,0 +1,89 @@
package art.arcane.iris.engine.object;
import art.arcane.iris.engine.object.annotations.MaxNumber;
import art.arcane.iris.engine.object.annotations.MinNumber;
import org.junit.Test;
import java.lang.reflect.Field;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
public class IrisStructurePlacementCarveSettingsTest {
@Test
public void defaultsSelectErodedOverbore() {
IrisStructurePlacement placement = new IrisStructurePlacement();
assertEquals(IrisStructureCarveShape.ERODED, placement.getOverboreShape());
assertEquals(IrisStructureCarveShape.ERODED, placement.resolvedOverboreShape());
assertEquals(0.8D, placement.getOverboreErosionStrength(), 0D);
assertEquals(0.8D, placement.resolvedOverboreErosionStrength(), 0D);
assertEquals(0.07D, placement.getOverboreErosionFrequency(), 0D);
assertEquals(0.07D, placement.resolvedOverboreErosionFrequency(), 0D);
}
@Test
public void carveShapesExposeTheirMaximumCeilingScale() {
assertEquals(1D, IrisStructureCarveShape.BOX.maximumCeilingScale(), 0D);
assertEquals(1D, IrisStructureCarveShape.ROUNDED.maximumCeilingScale(), 0D);
assertEquals(1.8D, IrisStructureCarveShape.ERODED.maximumCeilingScale(), 0D);
assertEquals(10, IrisStructureCarveShape.BOX.maximumCeilingExtension(10));
assertEquals(10, IrisStructureCarveShape.ROUNDED.maximumCeilingExtension(10));
assertEquals(18, IrisStructureCarveShape.ERODED.maximumCeilingExtension(10));
assertEquals(10, IrisStructureCarveShape.ERODED.maximumCeilingExtension(10, 0D));
assertEquals(14, IrisStructureCarveShape.ERODED.maximumCeilingExtension(10, 0.5D));
assertEquals(0, IrisStructureCarveShape.ERODED.maximumCeilingExtension(0));
}
@Test
public void nullAndNonFiniteValuesResolveToDefaults() {
IrisStructurePlacement placement = new IrisStructurePlacement()
.setOverboreShape(null)
.setOverboreErosionStrength(Double.NaN)
.setOverboreErosionFrequency(Double.POSITIVE_INFINITY);
assertEquals(IrisStructureCarveShape.ERODED, placement.resolvedOverboreShape());
assertEquals(0.8D, placement.resolvedOverboreErosionStrength(), 0D);
assertEquals(0.07D, placement.resolvedOverboreErosionFrequency(), 0D);
placement.setOverboreErosionStrength(Double.NEGATIVE_INFINITY);
placement.setOverboreErosionFrequency(Double.NaN);
assertEquals(0.8D, placement.resolvedOverboreErosionStrength(), 0D);
assertEquals(0.07D, placement.resolvedOverboreErosionFrequency(), 0D);
}
@Test
public void finiteValuesClampToAuthoredBounds() {
IrisStructurePlacement placement = new IrisStructurePlacement()
.setOverboreErosionStrength(-1D)
.setOverboreErosionFrequency(0D);
assertEquals(0D, placement.resolvedOverboreErosionStrength(), 0D);
assertEquals(0.001D, placement.resolvedOverboreErosionFrequency(), 0D);
placement.setOverboreErosionStrength(2D);
placement.setOverboreErosionFrequency(2D);
assertEquals(1D, placement.resolvedOverboreErosionStrength(), 0D);
assertEquals(1D, placement.resolvedOverboreErosionFrequency(), 0D);
}
@Test
public void numericSchemaBoundsMatchRuntimeBounds() throws NoSuchFieldException {
assertBounds("overboreErosionStrength", 0D, 1D);
assertBounds("overboreErosionFrequency", 0.001D, 1D);
}
private void assertBounds(String fieldName, double expectedMinimum,
double expectedMaximum) throws NoSuchFieldException {
Field field = IrisStructurePlacement.class.getDeclaredField(fieldName);
MinNumber minimum = field.getAnnotation(MinNumber.class);
MaxNumber maximum = field.getAnnotation(MaxNumber.class);
assertNotNull(minimum);
assertNotNull(maximum);
assertEquals(expectedMinimum, minimum.value(), 0D);
assertEquals(expectedMaximum, maximum.value(), 0D);
}
}