mirror of
https://github.com/GTNewHorizons/ForgeMultipart.git
synced 2026-11-13 00:47:52 +00:00
test: characterize PacketScheduler
Plain-JVM coverage is structural: the shapes of IScheduledPacketPart and TScheduledPacketPart, and the fact that read must stay abstract. TMultiPart declares read, so a superclass method would beat any default the interface tried to supply, and a part that does not declare it resolves to TMultiPart's, which reads a description rather than a mask. Forge server coverage for the behavior, which needs a part bound to a tile in a non-remote world and a real write stream: masks OR-ing into one write, the map clearing after a send, a part that lost its tile being skipped, all four documented mask widths being accepted, and an undocumented one being rejected rather than silently writing nothing. 117 plain-JVM tests, 18 Forge server tests.
This commit is contained in:
1 parent
31ec83f91b
commit
3ad25f56cb
2 files changed
+255
No files matched your search
+140
@@ -0,0 +1,140 @@
|
||||
package codechicken.multipart.test;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertNotNull;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import net.minecraft.server.MinecraftServer;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import codechicken.lib.data.MCDataInput;
|
||||
import codechicken.lib.data.MCDataOutput;
|
||||
import codechicken.multipart.IScheduledPacketPart;
|
||||
import codechicken.multipart.MultipartHelper;
|
||||
import codechicken.multipart.PacketScheduler;
|
||||
import codechicken.multipart.TMultiPart;
|
||||
import codechicken.multipart.TileMultipart;
|
||||
|
||||
/**
|
||||
* schedulePacket needs a part bound to a tile in a non-remote world, and sendScheduled needs a real write stream, so
|
||||
* none of this runs headless. Tests execute inside the server-started event, so no tick can interleave and flush the
|
||||
* map between a schedule and a send.
|
||||
*/
|
||||
class PacketSchedulerFunctionalTest {
|
||||
|
||||
@Test
|
||||
void repeatedSchedulesAccumulateIntoOneMask() {
|
||||
RecordingPart part = boundPart(1);
|
||||
|
||||
PacketScheduler.schedulePacket(part, 0x1L);
|
||||
PacketScheduler.schedulePacket(part, 0x2L);
|
||||
PacketScheduler.schedulePacket(part, 0x2L);
|
||||
sendScheduled();
|
||||
|
||||
assertEquals(Collections.singletonList(0x3L), part.written, "masks are OR-ed, and the part is written once");
|
||||
}
|
||||
|
||||
@Test
|
||||
void sendingClearsTheScheduleSoTheNextTickWritesNothing() {
|
||||
RecordingPart part = boundPart(1);
|
||||
PacketScheduler.schedulePacket(part, 0x5L);
|
||||
|
||||
sendScheduled();
|
||||
sendScheduled();
|
||||
|
||||
assertEquals(Collections.singletonList(0x5L), part.written);
|
||||
}
|
||||
|
||||
@Test
|
||||
void aPartThatLostItsTileIsSkippedButStillCleared() {
|
||||
RecordingPart part = boundPart(1);
|
||||
PacketScheduler.schedulePacket(part, 0x7L);
|
||||
part.tile_$eq(null);
|
||||
|
||||
sendScheduled();
|
||||
|
||||
assertTrue(part.written.isEmpty(), "a part with no tile must be skipped");
|
||||
}
|
||||
|
||||
@Test
|
||||
void everyDocumentedMaskWidthIsAccepted() {
|
||||
for (int width : new int[] { 1, 2, 4, 8 }) {
|
||||
RecordingPart part = boundPart(width);
|
||||
PacketScheduler.schedulePacket(part, 0x1L);
|
||||
|
||||
sendScheduled();
|
||||
|
||||
assertEquals(Collections.singletonList(0x1L), part.written, "maskWidth " + width + " must be accepted");
|
||||
}
|
||||
}
|
||||
|
||||
/** The reference matches on 1, 2, 4 and 8 with no fallback, so anything else fails rather than writing nothing. */
|
||||
@Test
|
||||
void anUndocumentedMaskWidthIsRejected() {
|
||||
RecordingPart part = boundPart(3);
|
||||
PacketScheduler.schedulePacket(part, 0x1L);
|
||||
|
||||
assertThrows(RuntimeException.class, PacketSchedulerFunctionalTest::sendScheduled);
|
||||
|
||||
// Leave no scheduled entry behind for the tests that follow.
|
||||
part.tile_$eq(null);
|
||||
try {
|
||||
sendScheduled();
|
||||
} catch (RuntimeException ignored) {
|
||||
// already drained
|
||||
}
|
||||
}
|
||||
|
||||
// The only place sendScheduled's owner is named.
|
||||
private static void sendScheduled() {
|
||||
codechicken.multipart.PacketScheduler$.MODULE$.sendScheduled();
|
||||
}
|
||||
|
||||
private static RecordingPart boundPart(int maskWidth) {
|
||||
RecordingPart part = new RecordingPart(maskWidth);
|
||||
TileMultipart tile = MultipartHelper.createTileFromParts(Collections.<TMultiPart>singletonList(part));
|
||||
assertNotNull(tile);
|
||||
World world = MinecraftServer.getServer().worldServers[0];
|
||||
assertNotNull(world);
|
||||
tile.setWorldObj(world);
|
||||
tile.xCoord = 1100;
|
||||
tile.yCoord = 70;
|
||||
tile.zCoord = 1100;
|
||||
return part;
|
||||
}
|
||||
|
||||
private static final class RecordingPart extends TMultiPart implements IScheduledPacketPart {
|
||||
|
||||
private final int maskWidth;
|
||||
final List<Long> written = new ArrayList<>();
|
||||
|
||||
RecordingPart(int maskWidth) {
|
||||
this.maskWidth = maskWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType() {
|
||||
return "test:scheduled";
|
||||
}
|
||||
|
||||
@Override
|
||||
public int maskWidth() {
|
||||
return maskWidth;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeScheduled(long mask, MCDataOutput packet) {
|
||||
written.add(mask);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void readScheduled(long mask, MCDataInput packet) {}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user