mirror of
https://github.com/GTNewHorizons/ForgeMultipart.git
synced 2026-11-13 17:04:25 +00:00
test: characterize common microblock traits
This commit is contained in:
1 parent
f5d449d041
commit
d6eb56b56f
5 files changed
+424
No files matched your search
@@ -0,0 +1,287 @@
|
||||
package codechicken.microblock;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
import java.io.InputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Base64;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Scanner;
|
||||
|
||||
import net.minecraft.util.IIcon;
|
||||
import net.minecraft.util.MovingObjectPosition;
|
||||
import net.minecraft.util.Vec3;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import codechicken.lib.vec.Cuboid6;
|
||||
import codechicken.lib.vec.Vector3;
|
||||
import codechicken.microblock.MicroMaterialRegistry.IMicroMaterial;
|
||||
import codechicken.multipart.TileMultipart;
|
||||
|
||||
/** Exercises frozen Scala forwarders compiled before the implementation extraction. */
|
||||
class MicroblockTraitsCharacterizationTest {
|
||||
|
||||
@Test
|
||||
void frozenConstructorAndSlotDispatchKeepStateAndJvmShiftSemantics() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
assertEquals(23, fixture.part.material());
|
||||
assertEquals(0, fixture.part.shape());
|
||||
assertNull(fixture.client.renderBounds());
|
||||
assertEquals(0, fixture.client.renderMask());
|
||||
for (int shape = -128; shape < 128; shape++) {
|
||||
fixture.part.shape_$eq((byte) shape);
|
||||
assertEquals(shape & 15, fixture.client.getSlot());
|
||||
assertEquals(1 << (shape & 15), fixture.client.getSlotMask());
|
||||
}
|
||||
fixture.set("overrideSlot", boolean.class, true);
|
||||
for (int slot : new int[] { -1, 0, 31, 32, 35 }) {
|
||||
fixture.set("selectedSlot", int.class, slot);
|
||||
assertEquals(1 << slot, fixture.client.getSlotMask());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void partialBoxesAreFreshImmutableListsContainingTheOriginalBounds() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
Cuboid6 bounds = fixture.bounds();
|
||||
List<Cuboid6> first = fixture.client.getPartialOcclusionBoxes();
|
||||
List<Cuboid6> second = fixture.client.getPartialOcclusionBoxes();
|
||||
assertNotSame(first, second);
|
||||
assertEquals(1, first.size());
|
||||
assertSame(bounds, first.get(0));
|
||||
assertSame(bounds, fixture.client.getRenderBounds());
|
||||
bounds.min.x = 0.125;
|
||||
assertEquals(0.125, first.get(0).min.x);
|
||||
assertThrows(UnsupportedOperationException.class, () -> first.set(0, Cuboid6.full));
|
||||
assertThrows(UnsupportedOperationException.class, () -> first.add(Cuboid6.full));
|
||||
assertThrows(UnsupportedOperationException.class, () -> first.remove(0));
|
||||
fixture.set("bounds", Cuboid6.class, null);
|
||||
assertNull(fixture.client.getPartialOcclusionBoxes().get(0));
|
||||
assertNull(fixture.client.getRenderBounds());
|
||||
assertSame(bounds, first.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
void itemClassIdUsesTheCurrentVirtualClass() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
assertThrows(NullPointerException.class, fixture.part::itemClassID);
|
||||
fixture.set("selectedClass", CommonMicroClass.class, TestClass.create(19));
|
||||
assertEquals(19, fixture.part.itemClassID());
|
||||
fixture.set("selectedClass", CommonMicroClass.class, TestClass.create(-3));
|
||||
assertEquals(-3, fixture.part.itemClassID());
|
||||
}
|
||||
|
||||
@Test
|
||||
void brokenIconsReadTheMaterialOnceAndForwardTheRequestedSide() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
IIcon icon = (IIcon) Proxy.newProxyInstance(
|
||||
getClass().getClassLoader(),
|
||||
new Class<?>[] { IIcon.class },
|
||||
(proxy, method, args) -> null);
|
||||
fixture.material((method, args) -> {
|
||||
assertEquals("getBreakingIcon", method);
|
||||
fixture.events.add("icon:" + args[0]);
|
||||
return icon;
|
||||
});
|
||||
for (int side : new int[] { -1, 0, 5, 9 }) {
|
||||
fixture.events.clear();
|
||||
assertSame(icon, fixture.client.getBrokenIcon(side));
|
||||
assertEquals(Arrays.asList("material", "icon:" + side), fixture.events);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void staticRenderingChecksThePassBeforeVirtualRender() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
fixture.material((method, args) -> {
|
||||
assertEquals("canRenderInPass", method);
|
||||
fixture.events.add("pass:" + args[0]);
|
||||
return ((Integer) args[0]) == 2;
|
||||
});
|
||||
Vector3 position = new Vector3(4, 5, 6);
|
||||
assertTrue(fixture.part.renderStatic(position, 2));
|
||||
assertSame(position, fixture.get("renderedPosition"));
|
||||
assertEquals(Arrays.asList("material", "pass:2", "render:2"), fixture.events);
|
||||
fixture.events.clear();
|
||||
assertFalse(fixture.part.renderStatic(position, 3));
|
||||
assertEquals(Arrays.asList("material", "pass:3"), fixture.events);
|
||||
fixture.set("selectedMaterial", IMicroMaterial.class, null);
|
||||
fixture.events.clear();
|
||||
assertThrows(NullPointerException.class, () -> fixture.part.renderStatic(position, 2));
|
||||
assertEquals(Arrays.asList("material"), fixture.events);
|
||||
}
|
||||
|
||||
@Test
|
||||
void commonRenderingSelectsPhysicalOrShrunkBoundsAndFaceMask() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
fixture.set("commonRender", boolean.class, true);
|
||||
List<Object[]> faces = new ArrayList<>();
|
||||
fixture.material((method, args) -> {
|
||||
assertEquals("renderMicroFace", method);
|
||||
faces.add(args.clone());
|
||||
return null;
|
||||
});
|
||||
Vector3 position = new Vector3(1, 2, 3);
|
||||
Cuboid6 shrunk = new Cuboid6(0.1, 0.2, 0.3, 0.7, 0.8, 0.9);
|
||||
fixture.client.renderBounds_$eq(shrunk);
|
||||
fixture.client.renderMask_$eq(0b101010);
|
||||
fixture.client.render(position, -4);
|
||||
assertFaces(faces, 6, position, -4, fixture.bounds());
|
||||
assertEquals(Arrays.asList("render:-4", "material", "bounds"), fixture.events);
|
||||
faces.clear();
|
||||
fixture.events.clear();
|
||||
fixture.client.render(position, 0);
|
||||
assertFaces(faces, 3, position, 0, shrunk);
|
||||
assertEquals(Arrays.asList("render:0", "material"), fixture.events);
|
||||
faces.clear();
|
||||
fixture.client.renderMask_$eq(63);
|
||||
fixture.client.render(position, 1);
|
||||
assertTrue(faces.isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
void particleForwardersReachIconEffectsAndPreserveEvaluationOrder() throws Exception {
|
||||
Fixture fixture = new Fixture();
|
||||
fixture.material((method, args) -> {
|
||||
assertEquals("getBreakingIcon", method);
|
||||
fixture.events.add("icon:" + args[0]);
|
||||
return null;
|
||||
});
|
||||
assertThrows(NullPointerException.class, () -> fixture.part.addDestroyEffects(null));
|
||||
assertEquals(
|
||||
Arrays.asList(
|
||||
"material",
|
||||
"icon:0",
|
||||
"material",
|
||||
"icon:1",
|
||||
"material",
|
||||
"icon:2",
|
||||
"material",
|
||||
"icon:3",
|
||||
"material",
|
||||
"icon:4",
|
||||
"material",
|
||||
"icon:5",
|
||||
"bounds"),
|
||||
fixture.events);
|
||||
fixture.part.bind(new TileMultipart());
|
||||
fixture.events.clear();
|
||||
MovingObjectPosition hit = new MovingObjectPosition(0, 0, 0, 3, Vec3.createVectorHelper(0, 0, 0));
|
||||
assertThrows(NullPointerException.class, () -> fixture.part.addHitEffects(hit, null));
|
||||
assertEquals(Arrays.asList("bounds", "material", "icon:3"), fixture.events);
|
||||
}
|
||||
|
||||
private static void assertFaces(List<Object[]> faces, int count, Vector3 position, int pass, Cuboid6 bounds) {
|
||||
assertEquals(count, faces.size());
|
||||
for (Object[] face : faces) {
|
||||
assertSame(position, face[0]);
|
||||
assertEquals(pass, face[1]);
|
||||
assertSame(bounds, face[2]);
|
||||
}
|
||||
}
|
||||
|
||||
private interface MaterialCall {
|
||||
|
||||
Object apply(String method, Object[] arguments);
|
||||
}
|
||||
|
||||
private static final class Fixture extends ClassLoader {
|
||||
|
||||
final Microblock part;
|
||||
final CommonMicroblockClient client;
|
||||
final List<String> events;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Fixture() throws Exception {
|
||||
super(Microblock.class.getClassLoader());
|
||||
InputStream input = Objects
|
||||
.requireNonNull(getClass().getResourceAsStream("/compat/ReferenceScalaMicroblockTraits.class.b64"));
|
||||
byte[] bytes;
|
||||
try (Scanner scanner = new Scanner(input, StandardCharsets.US_ASCII.name()).useDelimiter("\\A")) {
|
||||
bytes = Base64.getMimeDecoder().decode(scanner.next());
|
||||
}
|
||||
part = (Microblock) defineClass(null, bytes, 0, bytes.length).getConstructor().newInstance();
|
||||
client = (CommonMicroblockClient) part;
|
||||
events = (List<String>) get("events");
|
||||
}
|
||||
|
||||
Object get(String name) throws Exception {
|
||||
return part.getClass().getMethod(name).invoke(part);
|
||||
}
|
||||
|
||||
Cuboid6 bounds() throws Exception {
|
||||
return (Cuboid6) get("bounds");
|
||||
}
|
||||
|
||||
void set(String name, Class<?> type, Object value) throws Exception {
|
||||
part.getClass().getMethod(name + "_$eq", type).invoke(part, value);
|
||||
}
|
||||
|
||||
void material(MaterialCall call) throws Exception {
|
||||
set(
|
||||
"selectedMaterial",
|
||||
IMicroMaterial.class,
|
||||
Proxy.newProxyInstance(
|
||||
getParent(),
|
||||
new Class<?>[] { IMicroMaterial.class },
|
||||
(proxy, method, args) -> call.apply(method.getName(), args)));
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestClass extends CommonMicroClass {
|
||||
|
||||
private int id;
|
||||
|
||||
static TestClass create(int id) throws Exception {
|
||||
// MicroblockClass's constructor registers a trait and requires Forge initialization.
|
||||
Class<?> unsafe = Class.forName("sun.misc.Unsafe");
|
||||
Field field = unsafe.getDeclaredField("theUnsafe");
|
||||
field.setAccessible(true);
|
||||
TestClass value = (TestClass) unsafe.getMethod("allocateInstance", Class.class)
|
||||
.invoke(field.get(null), TestClass.class);
|
||||
value.id = id;
|
||||
return value;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getClassId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int itemSlot() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PlacementProperties placementProperties() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return "test:trait-class";
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends Microblock> baseTrait() {
|
||||
return Microblock.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Class<? extends MicroblockClient> clientTrait() {
|
||||
return MicroblockClient.class;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float getResistanceFactor() {
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user