diff --git a/ProtocolLib/pom.xml b/ProtocolLib/pom.xml
index 1c8bec49..4fa87e38 100644
--- a/ProtocolLib/pom.xml
+++ b/ProtocolLib/pom.xml
@@ -8,6 +8,7 @@
cp1252
+ 1.5
@@ -211,5 +212,22 @@
4.10
test
+
+ org.mockito
+ mockito-all
+ 1.8.4
+
+
+ org.powermock
+ powermock-module-junit4
+ ${powermock.version}
+ test
+
+
+ org.powermock
+ powermock-api-mockito
+ ${powermock.version}
+ test
+
\ No newline at end of file
diff --git a/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java
new file mode 100644
index 00000000..ce907afe
--- /dev/null
+++ b/ProtocolLib/src/test/java/com/comphenix/protocol/events/PacketContainerTest.java
@@ -0,0 +1,340 @@
+package com.comphenix.protocol.events;
+
+import static org.junit.Assert.*;
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+import java.util.List;
+
+// Will have to be updated for every version though
+import org.bukkit.craftbukkit.v1_4_6.inventory.CraftItemFactory;
+
+import org.bukkit.Bukkit;
+import org.bukkit.Material;
+import org.bukkit.Server;
+import org.bukkit.WorldType;
+import org.bukkit.inventory.ItemFactory;
+import org.bukkit.inventory.ItemStack;
+import org.bukkit.inventory.meta.ItemMeta;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.powermock.core.classloader.annotations.PrepareForTest;
+
+import com.comphenix.protocol.Packets;
+import com.comphenix.protocol.reflect.FieldUtils;
+import com.comphenix.protocol.reflect.StructureModifier;
+import com.comphenix.protocol.utility.MinecraftReflection;
+import com.comphenix.protocol.wrappers.ChunkPosition;
+import com.comphenix.protocol.wrappers.WrappedDataWatcher;
+import com.comphenix.protocol.wrappers.WrappedWatchableObject;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+
+// Ensure that the CraftItemFactory is mockable
+@RunWith(org.powermock.modules.junit4.PowerMockRunner.class)
+@PrepareForTest(CraftItemFactory.class)
+public class PacketContainerTest {
+
+ @BeforeClass
+ public static void initializeBukkit() throws IllegalAccessException {
+ // Initialize reflection
+ MinecraftReflection.setMinecraftPackage("net.minecraft.server.v1_4_6", "org.bukkit.craftbukkit.v1_4_6");
+
+ // Mock the server object
+ Server mockedServer = mock(Server.class);
+ ItemFactory mockedFactory = mock(CraftItemFactory.class);
+ ItemMeta mockedMeta = mock(ItemMeta.class);
+
+ when(mockedServer.getItemFactory()).thenReturn(mockedFactory);
+ when(mockedFactory.getItemMeta(any(Material.class))).thenReturn(mockedMeta);
+
+ // Inject this fake server
+ FieldUtils.writeStaticField(Bukkit.class, "server", mockedServer, true);
+
+ // And the fake item factory
+ FieldUtils.writeStaticField(CraftItemFactory.class, "instance", mockedFactory, true);
+ }
+
+ private void testPrimitive(StructureModifier modifier, int index, T initialValue, T testValue) {
+ // Check initial value
+ assertEquals(initialValue, modifier.read(index));
+
+ // Test assignment
+ modifier.write(index, testValue);
+ assertEquals(testValue, modifier.read(0));
+ }
+
+ private void testObjectArray(StructureModifier modifier, int index, T[] initialValue, T[] testValue) {
+ // Check initial value
+ assertNull(modifier.read(index));
+ modifier.writeDefaults();
+
+ // Test initial
+ assertArrayEquals(initialValue, modifier.read(index));
+
+ // Test assignment
+ modifier.write(index, testValue);
+ assertArrayEquals(testValue, modifier.read(0));
+ }
+
+ @Test
+ public void testGetByteArrays() {
+ // Contains a byte array we will test
+ PacketContainer customPayload = new PacketContainer(Packets.Server.CUSTOM_PAYLOAD);
+ StructureModifier bytes = customPayload.getByteArrays();
+ byte[] testArray = new byte[] { 1, 2, 3 };
+
+ // It's NULL at first
+ assertArrayEquals(null, (byte[]) bytes.read(0));
+ customPayload.getModifier().writeDefaults();
+
+ // Then it should create an empty array
+ assertArrayEquals(new byte[0], (byte[]) bytes.read(0));
+
+ // Check and see if we can write to it
+ bytes.write(0, testArray);
+ assertArrayEquals(testArray, (byte[]) bytes.read(0));
+ }
+
+ @Test
+ public void testGetBytes() {
+ PacketContainer spawnMob = new PacketContainer(Packets.Server.MOB_SPAWN);
+ testPrimitive(spawnMob.getBytes(), 0, (byte)0, (byte)1);
+ }
+
+ @Test
+ public void testGetShorts() {
+ PacketContainer itemData = new PacketContainer(Packets.Server.ITEM_DATA);
+ testPrimitive(itemData.getShorts(), 0, (short)0, (short)1);
+ }
+
+ @Test
+ public void testGetIntegers() {
+ PacketContainer updateSign = new PacketContainer(Packets.Server.UPDATE_SIGN);
+ testPrimitive(updateSign.getIntegers(), 0, (int)0, (int)1);
+ }
+
+ @Test
+ public void testGetLongs() {
+ PacketContainer updateTime = new PacketContainer(Packets.Server.UPDATE_TIME);
+ testPrimitive(updateTime.getLongs(), 0, (long)0, (long)1);
+ }
+
+ @Test
+ public void testGetFloat() {
+ PacketContainer explosion = new PacketContainer(Packets.Server.EXPLOSION);
+ testPrimitive(explosion.getFloat(), 0, (float)0, (float)0.8);
+ }
+
+ @Test
+ public void testGetDoubles() {
+ PacketContainer explosion = new PacketContainer(Packets.Server.EXPLOSION);
+ testPrimitive(explosion.getDoubles(), 0, (double)0, (double)0.8);
+ }
+
+ @Test
+ public void testGetStrings() {
+ PacketContainer explosion = new PacketContainer(Packets.Server.CHAT);
+ testPrimitive(explosion.getStrings(), 0, null, "hello");
+ }
+
+ @Test
+ public void testGetStringArrays() {
+ PacketContainer explosion = new PacketContainer(Packets.Server.UPDATE_SIGN);
+ testObjectArray(explosion.getStringArrays(), 0, new String[0], new String[] { "hello", "world" });
+ }
+
+ @Test
+ public void testGetIntegerArrays() {
+ // Contains a byte array we will test
+ PacketContainer mapChunkBulk = new PacketContainer(Packets.Server.MAP_CHUNK_BULK);
+ StructureModifier integers = mapChunkBulk.getIntegerArrays();
+ int[] testArray = new int[] { 1, 2, 3 };
+
+ // Pre and post conditions
+ assertArrayEquals(null, (int[]) integers.read(0));
+ mapChunkBulk.getModifier().writeDefaults();
+ assertArrayEquals(new int[0], (int[]) integers.read(0));
+
+ integers.write(0, testArray);
+ assertArrayEquals(testArray, (int[]) integers.read(0));
+ }
+
+ @Test
+ public void testGetItemModifier() {
+ PacketContainer windowClick = new PacketContainer(Packets.Client.WINDOW_CLICK);
+
+ StructureModifier items = windowClick.getItemModifier();
+ ItemStack goldAxe = new ItemStack(Material.GOLD_AXE);
+
+ assertNull(items.read(0));
+
+ // Insert the goldaxe and check if it's there
+ items.write(0, goldAxe);
+ assertTrue(equivalentItem(goldAxe, items.read(0)));
+ }
+
+ @Test
+ public void testGetItemArrayModifier() {
+ PacketContainer windowItems = new PacketContainer(Packets.Server.WINDOW_ITEMS);
+ StructureModifier itemAccess = windowItems.getItemArrayModifier();
+
+ ItemStack[] itemArray = new ItemStack[] {
+ new ItemStack(Material.GOLD_AXE),
+ new ItemStack(Material.DIAMOND_AXE)
+ };
+
+ assertNull(itemAccess.read(0));
+
+ // Insert and check that it was succesful
+ itemAccess.write(0, itemArray);
+
+ // Read back array
+ ItemStack[] comparision = itemAccess.read(0);
+ assertEquals(itemArray.length, comparision.length);
+
+ // Check that it is equivalent
+ for (int i = 0; i < itemArray.length; i++) {
+ assertTrue(String.format("Array element %s is not the same: %s != %s",
+ i, itemArray[i], comparision[i]), equivalentItem(itemArray[i], comparision[i]));
+ }
+ }
+
+ private boolean equivalentItem(ItemStack first, ItemStack second) {
+ if (first == null)
+ return second == null;
+ else if (second == null)
+ return false;
+ else
+ return first.getType().equals(second.getType());
+ }
+
+ private boolean equivalentPacket(PacketContainer first, PacketContainer second) {
+ if (first == null)
+ return second == null;
+ else if (second == null)
+ return false;
+ else
+ return first.getModifier().getValues().equals(second.getModifier().getValues());
+ }
+
+ @Test
+ public void testGetWorldTypeModifier() {
+ PacketContainer loginPacket = new PacketContainer(Packets.Server.LOGIN);
+ StructureModifier worldAccess = loginPacket.getWorldTypeModifier();
+
+ WorldType testValue = WorldType.LARGE_BIOMES;
+
+ assertNull(worldAccess.read(0));
+
+ // Insert and read back
+ worldAccess.write(0, testValue);
+ assertEquals(testValue, worldAccess.read(0));
+ }
+
+ @Test
+ public void testGetDataWatcherModifier() {
+ PacketContainer mobSpawnPacket = new PacketContainer(Packets.Server.MOB_SPAWN);
+ StructureModifier watcherAccessor = mobSpawnPacket.getDataWatcherModifier();
+
+ WrappedDataWatcher dataWatcher = new WrappedDataWatcher();
+ dataWatcher.setObject(1, 100);
+ dataWatcher.setObject(2, 125);
+
+ assertNull(watcherAccessor.read(0));
+
+ // Insert and read back
+ watcherAccessor.write(0, dataWatcher);
+ assertEquals(dataWatcher, watcherAccessor.read(0));
+ }
+
+ // Unfortunately, it might be too difficult to mock this one
+ //
+ // @Test
+ // public void testGetEntityModifier() { }
+
+ // No packet expose this type directly.
+ //
+ // @Test
+ // public void testGetPositionModifier() { }
+
+ @Test
+ public void testGetPositionCollectionModifier() {
+ PacketContainer explosionPacket = new PacketContainer(Packets.Server.EXPLOSION);
+ StructureModifier> positionAccessor = explosionPacket.getPositionCollectionModifier();
+
+ assertNull(positionAccessor.read(0));
+
+ List positions = Lists.newArrayList();
+ positions.add(new ChunkPosition(1, 2, 3));
+ positions.add(new ChunkPosition(3, 4, 5));
+
+ // Insert and read back
+ positionAccessor.write(0, positions);
+ assertEquals(positions, positionAccessor.read(0));
+ }
+
+ @Test
+ public void testGetWatchableCollectionModifier() {
+ PacketContainer entityMetadata = new PacketContainer(Packets.Server.ENTITY_METADATA);
+ StructureModifier> watchableAccessor =
+ entityMetadata.getWatchableCollectionModifier();
+
+ assertNull(watchableAccessor.read(0));
+
+ WrappedDataWatcher watcher = new WrappedDataWatcher();
+ watcher.setObject(1, 10);
+ watcher.setObject(8, 10);
+
+ List list = watcher.getWatchableObjects();
+
+ // Insert and read back
+ watchableAccessor.write(0, list);
+ assertEquals(list, watchableAccessor.read(0));
+ }
+
+ @Test
+ public void testDeepClone() {
+ // Try constructing all the packets
+ for (Integer id : Iterables.concat(
+ Packets.getClientRegistry().values(),
+ Packets.getServerRegistry().values() )) {
+
+ // Whether or not this packet has been registered
+ boolean registered = Packets.Server.isSupported(id) ||
+ Packets.Client.isSupported(id);
+
+ try {
+ PacketContainer constructed = new PacketContainer(id);
+
+ if (!registered) {
+ fail("Expected IllegalArgumentException(Packet " + id + " not registered");
+ }
+
+ // Make sure these packets contains fields as well
+ assertTrue("Constructed packet with no known fields (" + id + ")",
+ constructed.getModifier().size() > 0);
+
+ // Initialize default values
+ constructed.getModifier().writeDefaults();
+
+ // Clone the packet
+ PacketContainer cloned = constructed.deepClone();
+
+ // Make sure they're equivalent
+ assertTrue("Packet " + id + " could not be cloned.", equivalentPacket(constructed, cloned));
+
+ } catch (IllegalArgumentException e) {
+ if (!registered) {
+ // Check the same
+ assertEquals(e.getMessage(), "The packet ID " + id + " is not registered.");
+ } else {
+ // Something is very wrong
+ throw e;
+ }
+ }
+ }
+ }
+}