mirror of
https://github.com/GTNewHorizons/ForgeMultipart.git
synced 2026-11-12 16:37:38 +00:00
test: characterize MultiPartRegistry
Splits the characterization across both layers, because part registration needs FML's active mod container and throws without one, so it cannot run headless at all. Plain JVM covers what is reachable: converters are dispatched per block, a converter returning null does not stop the scan, converters registered for another block are never consulted, a converter claiming several blocks is registered for each, and an unknown mod container throws rather than returning null. The Forge server suite covers registration itself, and deliberately exercises both overloads downstream depends on. Content registers through the deprecated IPartFactory array form that seven jars use, and each microblock class registers through the IPartFactory2 varargs form that ProjRed uses, so resolving mc_torch and mcr_face proves both paths. Part ids also round trip through the shared IDWriter carrier. 78 plain-JVM tests and 5 server tests, all passing against the untouched Scala.
This commit is contained in:
1 parent
9f454042b4
commit
ff380ee3bc
2 files changed
+181
No files matched your search
@@ -7,17 +7,28 @@ import static org.junit.jupiter.api.Assertions.assertNotSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.DataInputStream;
|
||||
import java.io.DataOutputStream;
|
||||
import java.lang.reflect.Proxy;
|
||||
import java.util.BitSet;
|
||||
|
||||
import net.minecraft.nbt.NBTTagCompound;
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import codechicken.lib.data.MCDataInput;
|
||||
import codechicken.lib.data.MCDataOutput;
|
||||
import codechicken.lib.data.MCDataOutputWrapper;
|
||||
import codechicken.microblock.FaceMicroClass$;
|
||||
import codechicken.microblock.HollowMicroClass$;
|
||||
import codechicken.microblock.Microblock;
|
||||
import codechicken.microblock.MicroblockGenerator;
|
||||
import codechicken.multipart.MultiPartRegistry;
|
||||
import codechicken.multipart.TFacePart;
|
||||
import codechicken.multipart.TMultiPart;
|
||||
import codechicken.multipart.TileMultipart;
|
||||
import codechicken.multipart.asm.MultipartMixinFactory;
|
||||
import codechicken.multipart.scalatraits.TSlottedTile;
|
||||
@@ -65,6 +76,63 @@ class ForgeEnvironmentSmokeTest {
|
||||
assertFalse(((TFacePart) hollow).solid(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Part registration needs FML's active mod container, so none of this is reachable from a plain JVM test. Startup
|
||||
* exercises both overloads downstream depends on: Content registers through the deprecated IPartFactory array form,
|
||||
* and each microblock class registers through the IPartFactory2 varargs form.
|
||||
*/
|
||||
@Test
|
||||
void registryResolvesPartsRegisteredThroughBothFactoryOverloads() throws Exception {
|
||||
assertTrue(MultiPartRegistry.loaded(), "Registration must be closed once the server has started");
|
||||
|
||||
// mc_torch comes from the deprecated IPartFactory array overload.
|
||||
TMultiPart torch = MultiPartRegistry.loadPart("mc_torch", null);
|
||||
assertNotNull(torch);
|
||||
assertEquals("mc_torch", torch.getType());
|
||||
|
||||
// mcr_face comes from the IPartFactory2 varargs overload. Its factory reads the tag, so it needs a real one;
|
||||
// an absent material name resolves to the missing-material placeholder rather than failing.
|
||||
TMultiPart face = MultiPartRegistry.loadPart("mcr_face", new NBTTagCompound());
|
||||
assertNotNull(face);
|
||||
assertEquals("mcr_face", face.getType());
|
||||
|
||||
assertNotNull(MultiPartRegistry.getModContainer("mc_torch"));
|
||||
assertNotNull(MultiPartRegistry.getModContainer("mcr_face"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void partIdsRoundTripThroughTheRegistryCarrier() {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
MCDataOutput output = new MCDataOutputWrapper(new DataOutputStream(bytes));
|
||||
TMultiPart torch = MultiPartRegistry.loadPart("mc_torch", null);
|
||||
MultiPartRegistry.writePartID(output, torch);
|
||||
|
||||
TMultiPart read = MultiPartRegistry.readPart(input(bytes.toByteArray()));
|
||||
|
||||
assertNotNull(read);
|
||||
assertEquals("mc_torch", read.getType());
|
||||
}
|
||||
|
||||
/** CodeChickenLib has no stream-backed MCDataInput, so the carrier reads are proxied. */
|
||||
private static MCDataInput input(byte[] bytes) {
|
||||
DataInputStream data = new DataInputStream(new ByteArrayInputStream(bytes));
|
||||
return (MCDataInput) Proxy.newProxyInstance(
|
||||
MCDataInput.class.getClassLoader(),
|
||||
new Class<?>[] { MCDataInput.class },
|
||||
(proxy, method, arguments) -> {
|
||||
switch (method.getName()) {
|
||||
case "readUByte":
|
||||
return (short) data.readUnsignedByte();
|
||||
case "readUShort":
|
||||
return data.readUnsignedShort();
|
||||
case "readInt":
|
||||
return data.readInt();
|
||||
default:
|
||||
throw new AssertionError("Unexpected read method: " + method.getName());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
void generatesAndCachesSlottedTileClass() {
|
||||
int traitId = MultipartMixinFactory.getId(TSlottedTile.class.getName().replace('.', '/'));
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
package codechicken.multipart;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.material.Material;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import codechicken.lib.vec.BlockCoord;
|
||||
import codechicken.multipart.MultiPartRegistry.IPartConverter;
|
||||
|
||||
/**
|
||||
* Part registration needs FML's active mod container, so it cannot run headless; that half of the registry is
|
||||
* characterized in the Forge server suite instead. Converter registration and dispatch touch neither FML nor the
|
||||
* logger, so they are covered here.
|
||||
*/
|
||||
class MultiPartRegistryCharacterizationTest {
|
||||
|
||||
@Test
|
||||
void noPartsAreLoadedBeforeRegistrationRuns() {
|
||||
assertFalse(MultiPartRegistry.loaded());
|
||||
}
|
||||
|
||||
@Test
|
||||
void unknownModContainerThrowsRatherThanReturningNull() {
|
||||
assertThrows(NoSuchElementException.class, () -> MultiPartRegistry.getModContainer("test:nosuchpart"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void convertersAreDispatchedPerBlockAndFirstNonNullWins() {
|
||||
Block block = new Block(Material.rock) {};
|
||||
Block other = new Block(Material.rock) {};
|
||||
|
||||
RecordingConverter declines = new RecordingConverter(block, null);
|
||||
RecordingConverter accepts = new RecordingConverter(block, new TestPart());
|
||||
RecordingConverter unrelated = new RecordingConverter(other, new TestPart());
|
||||
|
||||
MultiPartRegistry.registerConverter(declines);
|
||||
MultiPartRegistry.registerConverter(accepts);
|
||||
MultiPartRegistry.registerConverter(unrelated);
|
||||
|
||||
assertSame(accepts.result, MultiPartRegistry.convertBlock(null, null, block));
|
||||
assertEquals(1, declines.calls, "A converter returning null must not stop the scan");
|
||||
assertEquals(1, accepts.calls);
|
||||
assertEquals(0, unrelated.calls, "Converters registered for another block must not be consulted");
|
||||
}
|
||||
|
||||
@Test
|
||||
void blockWithNoConverterYieldsNull() {
|
||||
assertNull(MultiPartRegistry.convertBlock(null, null, new Block(Material.rock) {}));
|
||||
}
|
||||
|
||||
@Test
|
||||
void aConverterIsRegisteredForEveryBlockItClaims() {
|
||||
Block first = new Block(Material.rock) {};
|
||||
Block second = new Block(Material.rock) {};
|
||||
TestPart part = new TestPart();
|
||||
|
||||
RecordingConverter converter = new RecordingConverter(Arrays.asList(first, second), part);
|
||||
MultiPartRegistry.registerConverter(converter);
|
||||
|
||||
assertSame(part, MultiPartRegistry.convertBlock(null, null, first));
|
||||
assertSame(part, MultiPartRegistry.convertBlock(null, null, second));
|
||||
assertEquals(2, converter.calls);
|
||||
}
|
||||
|
||||
private static final class RecordingConverter implements IPartConverter {
|
||||
|
||||
private final List<Block> blocks = new ArrayList<>();
|
||||
private final TMultiPart result;
|
||||
private int calls;
|
||||
|
||||
private RecordingConverter(Block block, TMultiPart result) {
|
||||
this.blocks.add(block);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
private RecordingConverter(List<Block> blocks, TMultiPart result) {
|
||||
this.blocks.addAll(blocks);
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Iterable<Block> blockTypes() {
|
||||
return blocks;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TMultiPart convert(World world, BlockCoord pos) {
|
||||
calls++;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestPart extends TMultiPart {
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "test:converted";
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user