/* This code is part of Freenet. It is distributed under the GNU General
 * Public License, version 2 (or at your option any later version). See
 * http://www.gnu.org/ for further details of the GPL. */
package freenet.node;

import static org.junit.Assert.*;

import java.util.Arrays;
import java.util.Random;

import org.junit.Test;

public class NPFPacketTest {
    
    static final int MAX_PACKET_SIZE = 1400;

	public NullBasePeerNode pn = new NullBasePeerNode();
	
	@Test
	public void testEmptyPacket() {
		byte[] packet = new byte[] {
		                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, //Sequence number 0
		                (byte)0x00}; // 0 acks
		NPFPacket r = NPFPacket.create(packet, pn);

		assertEquals(0, r.getSequenceNumber());
		assertEquals(0, r.getAcks().size());
		assertEquals(0, r.getFragments().size());
		assertFalse(r.getError());
	}

	@Test
	public void testPacketWithAck() {
		byte[] packet = new byte[] {
		                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, //Sequence number 0
		                (byte)0x01, //1 ack
		                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01}; //Ack for packet range [0..0] of length 1
		NPFPacket r = NPFPacket.create(packet, pn);

		assertEquals(0, r.getSequenceNumber());
		assertEquals(1, r.getAcks().size());
		assertTrue(r.getAcks().contains(Integer.valueOf(0)));
		assertEquals(0, r.getFragments().size());
		assertFalse(r.getError());
	}

	@Test
	public void testPacketWithAcks() {
		byte[] packet = new byte[] {
		                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, //Sequence number 0
		                (byte)0x03, //3 ack ranges
		                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x05, (byte)0x01, //Ack for packet 5
		                (byte)0x05, (byte)0x02, //Ack range for packets [10..11] of size 2
		                (byte)0x00 /*Far-range marker*/, (byte)0x00, (byte)0x0F, (byte)0x57, (byte)0xF3 /*Ack id (1005555)*/, (byte) 0x05 /*Range size*/}; 
		NPFPacket r = NPFPacket.create(packet, pn);

		assertEquals(0, r.getSequenceNumber());
		assertEquals(8, r.getAcks().size());
		assertTrue(r.getAcks().contains(Integer.valueOf(5)));
		assertTrue(r.getAcks().contains(Integer.valueOf(10)));
		assertTrue(r.getAcks().contains(Integer.valueOf(11)));
		assertTrue(r.getAcks().contains(Integer.valueOf(1005555)));
		assertTrue(r.getAcks().contains(Integer.valueOf(1005556)));
		assertTrue(r.getAcks().contains(Integer.valueOf(1005557)));
		assertTrue(r.getAcks().contains(Integer.valueOf(1005558)));
		assertTrue(r.getAcks().contains(Integer.valueOf(1005559)));
		assertEquals(0, r.getFragments().size());
		assertFalse(r.getError());
	}

	@Test
	public void testPacketWithFragment() {
		byte[] packet = new byte[] {
		                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, //Sequence number 0
		                (byte)0x00, // 0 acks
		                (byte)0xB0, (byte)0x00, (byte)0x00, (byte)0x00,//Flags (short, first fragment and full id) and messageID 0
		                (byte)0x08, //Fragment length
		                (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF}; //Data
		NPFPacket r = NPFPacket.create(packet, pn);

		assertEquals(0, r.getSequenceNumber());
		assertEquals(0, r.getAcks().size());
		assertEquals(1, r.getFragments().size());

		MessageFragment frag = r.getFragments().get(0);
		assertTrue(frag.shortMessage);
		assertFalse(frag.isFragmented);
		assertTrue(frag.firstFragment);
		assertEquals(0, frag.messageID);
		assertEquals(8, frag.fragmentLength);
		assertEquals(0, frag.fragmentOffset);
		assertEquals(8, frag.messageLength);
		assertTrue(Arrays.equals(frag.fragmentData, new byte[] { (byte)0x01, (byte)0x23, (byte)0x45,
		                (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF }));

		assertFalse(r.getError());
	}

	@Test
	public void testPacketWithFragments() {
		byte[] packet = new byte[] { (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, // Sequence number 0
		                (byte)0x00, // 0 acks
		                (byte)0xB0, (byte)0x00, (byte)0x00, (byte)0x00,// Flags (short and first fragment) and messageID 0
		                (byte)0x08, // Fragment length
		                (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF, // Data
		                (byte)0xA0, (byte)0x00, // Flags (short and first fragment) and messageID 0
		                (byte)0x08, // Fragment length
		                (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF }; // Data
		NPFPacket r = NPFPacket.create(packet, pn);

		assertEquals(0, r.getSequenceNumber());
		assertEquals(0, r.getAcks().size());
		assertEquals(2, r.getFragments().size());

		// Check first fragment
		MessageFragment frag = r.getFragments().get(0);
		assertTrue(frag.shortMessage);
		assertFalse(frag.isFragmented);
		assertTrue(frag.firstFragment);
		assertEquals(0, frag.messageID);
		assertEquals(8, frag.fragmentLength);
		assertEquals(0, frag.fragmentOffset);
		assertEquals(8, frag.messageLength);
		assertTrue(Arrays.equals(frag.fragmentData, new byte[] { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89,
		                (byte)0xAB, (byte)0xCD, (byte)0xEF }));

		// Check second fragment
		frag = r.getFragments().get(1);
		assertTrue(frag.shortMessage);
		assertFalse(frag.isFragmented);
		assertTrue(frag.firstFragment);
		assertEquals(0, frag.messageID);
		assertEquals(8, frag.fragmentLength);
		assertEquals(0, frag.fragmentOffset);
		assertEquals(8, frag.messageLength);
		assertTrue(Arrays.equals(frag.fragmentData, new byte[] { (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89,
		                (byte)0xAB, (byte)0xCD, (byte)0xEF }));

		assertFalse(r.getError());
	}

	@Test
	public void testReceivedLargeFragment() {
		byte[] packet = new byte[] { (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Sequence number 0
		                (byte) 0x00, // 0 acks
		                (byte) 0xB0, (byte) 0x00, (byte) 0x00, (byte) 0x00, // Flags (short and first fragment) and messageID 0
		                (byte) 0x80, // Fragment length
		                (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB,
		                (byte) 0xCD, (byte) 0xEF, (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67,
		                (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF, (byte) 0x01, (byte) 0x23,
		                (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF,
		                (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB,
		                (byte) 0xCD, (byte) 0xEF, (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67,
		                (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF, (byte) 0x01, (byte) 0x23,
		                (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF,
		                (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB,
		                (byte) 0xCD, (byte) 0xEF, (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67,
		                (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF, (byte) 0x01, (byte) 0x23,
		                (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF,
		                (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB,
		                (byte) 0xCD, (byte) 0xEF, (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67,
		                (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF, (byte) 0x01, (byte) 0x23,
		                (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF,
		                (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB,
		                (byte) 0xCD, (byte) 0xEF, (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67,
		                (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF, (byte) 0x01, (byte) 0x23,
		                (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB, (byte) 0xCD, (byte) 0xEF,
		                (byte) 0x01, (byte) 0x23, (byte) 0x45, (byte) 0x67, (byte) 0x89, (byte) 0xAB,
		                (byte) 0xCD, (byte) 0xEF };
		NPFPacket r = NPFPacket.create(packet, pn);
		assertEquals(0, r.getAcks().size());
		assertEquals(1, r.getFragments().size());
		MessageFragment f = r.getFragments().get(0);
		assertTrue(f.firstFragment);
		assertTrue(f.shortMessage);
		assertEquals(0, f.messageID);
		assertEquals(128, f.fragmentLength);
		assertFalse(r.getError());
	}

	@Test
	public void testReceiveSequenceNumber() {
		byte[] packet = new byte[] {
		                (byte)0x01, (byte)0x02, (byte)0x04, (byte)0x08, //Sequence number
		                (byte)0x00}; // 0 acks
		NPFPacket r = NPFPacket.create(packet, pn);

		assertEquals(16909320, r.getSequenceNumber());
		assertEquals(0, r.getAcks().size());
		assertEquals(0, r.getFragments().size());
		assertFalse(r.getError());
	}

	@Test
	public void testReceiveLongFragmentedMessage() {
		byte[] packetNoData = new byte[] {
		                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, //Sequence number (0)
		                (byte)0x00, //0 acks
		                (byte)0x50, (byte)0x00, (byte) 0x00, (byte) 0x00, // Flags (long, fragmented, not first) and messageID 0
		                (byte)0x01, (byte)0x01, //Fragment length
		                (byte)0x01, (byte)0x01}; //Fragment offset
		byte[] packet = new byte[packetNoData.length + 257];
		System.arraycopy(packetNoData, 0, packet, 0, packetNoData.length);

		NPFPacket r = NPFPacket.create(packet, pn);
		assertEquals(0, r.getSequenceNumber());
		assertEquals(0, r.getAcks().size());
		assertEquals(1, r.getFragments().size());

		MessageFragment f = r.getFragments().get(0);
		assertFalse(f.shortMessage);
		assertFalse(f.firstFragment);
		assertTrue(f.isFragmented);
		assertEquals(257, f.fragmentLength);
		assertEquals(257, f.fragmentOffset);
		assertEquals(0, f.messageID);

		assertFalse(r.getError());
	}

	@Test
	public void testReceiveBadFragment() {
		byte[] packet = new byte[] {
		                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
		                (byte)0x00,
		                (byte)0xC0, (byte)0x00,
		                (byte)0x01,
		                (byte)0x00};

		NPFPacket r = NPFPacket.create(packet, pn);
		assertEquals(0, r.getFragments().size());
		assertTrue(r.getError());
	}

	@Test
	public void testReceiveZeroLengthFragment() {
		byte[] packet = new byte[] {
		                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
		                (byte)0x00,
		                (byte)0xB0, (byte)0x00, (byte) 0x00, (byte) 0x00,
		                (byte)0x00};

		NPFPacket r = NPFPacket.create(packet, pn);
		assertFalse(r.getError());
		assertEquals(1, r.getFragments().size());

		MessageFragment f = r.getFragments().get(0);
		assertEquals(0, f.fragmentLength);
		assertEquals(0, f.fragmentData.length);
		assertEquals(0, f.messageID);
	}

	@Test
	public void testSendEmptyPacket() {
		NPFPacket p = new NPFPacket();
		p.setSequenceNumber(0);

		byte[] correctData = new byte[] {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, //Sequence number (0)
		                (byte)0x00}; //Number of acks (0)

		checkPacket(p, correctData);
	}

    @Test
    public void testSendPacketWithAck() {
        NPFPacket p = new NPFPacket();
        p.setSequenceNumber(0);
        p.addAck(0, MAX_PACKET_SIZE);

        byte[] correctData = new byte[] {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
                        (byte)0x01,
                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01};

        checkPacket(p, correctData);
    }

    @Test
    public void testSendPacketWithAckRange() {
        NPFPacket p = new NPFPacket();
        p.setSequenceNumber(0);
        p.addAck(0, MAX_PACKET_SIZE);
        p.addAck(1, MAX_PACKET_SIZE);
        p.addAck(2, MAX_PACKET_SIZE);

        byte[] correctData = new byte[] {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
                        (byte)0x01,
                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x03};

        checkPacket(p, correctData);
    }

    @Test
    public void testSendPacketWithTwoAcks() {
        NPFPacket p = new NPFPacket();
        p.setSequenceNumber(0);
        p.addAck(0, MAX_PACKET_SIZE);
        p.addAck(5, MAX_PACKET_SIZE);

        byte[] correctData = new byte[] {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
                        (byte)0x02,
                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
                (byte)0x05, (byte) 0x01};

        checkPacket(p, correctData);
    }

    @Test
    public void testSendPacketWithTwoAcksLong() {
        NPFPacket p = new NPFPacket();
        p.setSequenceNumber(0);
        p.addAck(0, MAX_PACKET_SIZE);
        p.addAck(1000000, MAX_PACKET_SIZE);

        byte[] correctData = new byte[] {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
                        (byte)0x02,
                (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
                (byte)0x00 /* marker */, (byte)0x00, (byte)0x0F, (byte)0x42, (byte)0x40, (byte)0x01};

        checkPacket(p, correctData);
    }

	@Test
	public void testSendPacketWithAcks() {
		NPFPacket p = new NPFPacket();
		p.setSequenceNumber(0);
		p.addAck(0, MAX_PACKET_SIZE);
		p.addAck(5, MAX_PACKET_SIZE);
		p.addAck(10, MAX_PACKET_SIZE);

		byte[] correctData = new byte[] {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00,
		                (byte)0x03,
				(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x00, (byte)0x01,
				(byte)0x05, (byte) 0x01,
				(byte)0x05, (byte) 0x01};

		checkPacket(p, correctData);
	}

	@Test
	public void testSendPacketWithFragment() {
		NPFPacket p = new NPFPacket();
		p.setSequenceNumber(100);
		p.addMessageFragment(new MessageFragment(true, false, true, 0, 8, 8, 0,
		                new byte[] {(byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF}, null));

		byte[] correctData = new byte[] {(byte)0x00, (byte)0x00, (byte)0x00, (byte)0x64, //Sequence number (100)
		                (byte)0x00,
				(byte)0xB0, (byte)0x00, (byte)0x00, (byte)0x00, //Flags + messageID
				(byte)0x08, //Fragment length
				(byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF};

		checkPacket(p, correctData);
	}

	@Test
	public void testSendCompletePacket() {
		NPFPacket p = new NPFPacket();
		p.setSequenceNumber(2130706432);
		// Range 1 [1000000..1000000]
		p.addAck(1000000, MAX_PACKET_SIZE);
		
		//Range 2 [1000010..1000010]
		p.addAck(1000010, MAX_PACKET_SIZE);
		
		//Range 3 [1000255..1000257]
		p.addAck(1000255, MAX_PACKET_SIZE);
		p.addAck(1000256, MAX_PACKET_SIZE);
		p.addAck(1000257, MAX_PACKET_SIZE);
		
		//Range 4 [1005555..1005559]
		p.addAck(1005555, MAX_PACKET_SIZE);
		p.addAck(1005556, MAX_PACKET_SIZE);
		p.addAck(1005557, MAX_PACKET_SIZE);
		p.addAck(1005558, MAX_PACKET_SIZE);
		p.addAck(1005559, MAX_PACKET_SIZE);
		
		p.addMessageFragment(new MessageFragment(true, false, true, 0, 8, 8, 0,
		                new byte[] {(byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF}, null));
		p.addMessageFragment(new MessageFragment(false, true, false, 4095, 14, 1024, 256, new byte[] {
		                (byte)0xfd, (byte)0x47, (byte)0xc2, (byte)0x30,
		                (byte)0x41, (byte)0x53, (byte)0x57, (byte)0x56,
		                (byte)0x0e, (byte)0x56, (byte)0x69, (byte)0xf5,
		                (byte)0x00, (byte)0x0d}, null));

		byte[] correctData = new byte[] {(byte)0x7F, (byte)0x00, (byte)0x00, (byte)0x00, //Sequence number
		                (byte)0x04, //Number of ack ranges
		                (byte)0x00, (byte)0x0F, (byte)0x42, (byte)0x40, (byte) 0x01, // First ack + range length
		                (byte)0x0A, (byte)0x01, // 2nd Range + range length
		                (byte)0xF5, (byte)0x03, // 3rd range + range length
		                (byte)0x00 /*Far-range marker*/, (byte)0x00, (byte)0x0F, (byte)0x57, (byte)0xF3 /*Ack id*/, (byte) 0x05 /*Range size*/, 
		                //First fragment
		                (byte)0xB0, (byte)0x00, (byte)0x00, (byte)0x00, //Message id + flags
		                (byte)0x08, //Fragment length
		                (byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF,
		                //Second fragment
		                (byte)0x4F, (byte)0xFF,
		                (byte)0x00, (byte)0x0e, //Fragment length
		                (byte)0x01, (byte)0x00, //Fragment offset
		                (byte)0xfd, (byte)0x47, (byte)0xc2, (byte)0x30,
		                (byte)0x41, (byte)0x53, (byte)0x57, (byte)0x56,
		                (byte)0x0e, (byte)0x56, (byte)0x69, (byte)0xf5,
		                (byte)0x00, (byte)0x0d};

		checkPacket(p, correctData);
	}

	@Test
	public void testLength() {
		NPFPacket p = new NPFPacket();

		p.addMessageFragment(new MessageFragment(true, false, true, 0, 10, 10, 0, new byte[10], null));
		assertEquals(20, p.getLength()); //Seqnum (4), numAcks (1), msgID (4), length (1), data (10)

		p.addMessageFragment(new MessageFragment(true, false, true, 5000, 10, 10, 0, new byte[10], null));
		assertEquals(35, p.getLength()); // + msgID (4), length (1), data (10)

		//This fragment adds 13, but the next won't need a full message id anymore, so this should only add 11
		//bytes
		p.addMessageFragment(new MessageFragment(true, false, true, 2500, 10, 10, 0, new byte[10], null));
		assertEquals(46, p.getLength());
	}
	
	@Test
	public void testEncodeDecodeLossyPerPacketMessages() {
		NPFPacket p = new NPFPacket();
		byte[] fragData = new byte[] {(byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF};
		p.addMessageFragment(new MessageFragment(true, false, true, 0, 8, 8, 0,
                fragData, null));
		byte[] lossyFragment = new byte[] { (byte)0xFF, (byte)0xEE, (byte)0xDD, (byte)0xCC, (byte)0xBB, (byte)0xAA};
		p.addLossyMessage(lossyFragment);
		byte[] encoded = new byte[p.getLength()];
		p.toBytes(encoded, 0, null);
		NPFPacket received = NPFPacket.create(encoded, pn);
		assertEquals(1, received.getFragments().size());
		assertEquals(0, received.countAcks());
		assertEquals(1, received.getLossyMessages().size());
		assertEquals(encoded.length, received.getLength());
		byte[] decodedFragData = received.getFragments().get(0).fragmentData;
		checkEquals(fragData, decodedFragData);
		byte[] decodedLossyMessage = received.getLossyMessages().get(0);
		checkEquals(lossyFragment, decodedLossyMessage);
	}

	@Test
	public void testEncodeDecodeLossyPerPacketMessages2() {
		NPFPacket p = new NPFPacket();
		byte[] fragData = new byte[] {(byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF};
		p.addMessageFragment(new MessageFragment(true, false, true, 0, 8, 8, 0,
                fragData, null));
		byte[] lossyFragment = new byte[] { (byte)0xFF, (byte)0xEE, (byte)0xDD, (byte)0xCC, (byte)0xBB, (byte)0xAA};
		byte[] lossyFragment2 = new byte[] { (byte)0xAA, (byte)0x99, (byte)0x88, (byte)0x77, (byte)0x66, (byte)0x55};
		p.addLossyMessage(lossyFragment);
		p.addLossyMessage(lossyFragment2);
		byte[] encoded = new byte[p.getLength()];
		p.toBytes(encoded, 0, null);
		NPFPacket received = NPFPacket.create(encoded, pn);
		assertEquals(1, received.getFragments().size());
		assertEquals(0, received.countAcks());
		assertEquals(2, received.getLossyMessages().size());
		assertEquals(encoded.length, received.getLength());
		byte[] decodedFragData = received.getFragments().get(0).fragmentData;
		checkEquals(fragData, decodedFragData);
		byte[] decodedLossyMessage = received.getLossyMessages().get(0);
		checkEquals(lossyFragment, decodedLossyMessage);
		decodedLossyMessage = received.getLossyMessages().get(1);
		checkEquals(lossyFragment2, decodedLossyMessage);
	}

	@Test
	public void testEncodeDecodeLossyPerPacketMessages2Padded() {
		NPFPacket p = new NPFPacket();
		byte[] fragData = new byte[] {(byte)0x01, (byte)0x23, (byte)0x45, (byte)0x67, (byte)0x89, (byte)0xAB, (byte)0xCD, (byte)0xEF};
		p.addMessageFragment(new MessageFragment(true, false, true, 0, 8, 8, 0,
                fragData, null));
		byte[] lossyFragment = new byte[] { (byte)0xFF, (byte)0xEE, (byte)0xDD, (byte)0xCC, (byte)0xBB, (byte)0xAA};
		byte[] lossyFragment2 = new byte[] { (byte)0xAA, (byte)0x99, (byte)0x88, (byte)0x77, (byte)0x66, (byte)0x55};
		p.addLossyMessage(lossyFragment);
		p.addLossyMessage(lossyFragment2);
		byte[] encoded = new byte[p.getLength() + 20];
		int randomSeed = new Random().nextInt();
		p.toBytes(encoded, 0, new Random(randomSeed));
		NPFPacket received = NPFPacket.create(encoded, pn);
		assertEquals(1, received.getFragments().size());
		assertEquals(0, received.countAcks());
		assertEquals("Seed was "+randomSeed, 2, received.getLossyMessages().size());
		assertEquals(p.getLength(), received.getLength());
		assertEquals(encoded.length - 20, received.getLength());
		byte[] decodedFragData = received.getFragments().get(0).fragmentData;
		checkEquals(fragData, decodedFragData);
		byte[] decodedLossyMessage = received.getLossyMessages().get(0);
		checkEquals(lossyFragment, decodedLossyMessage);
		decodedLossyMessage = received.getLossyMessages().get(1);
		checkEquals(lossyFragment2, decodedLossyMessage);
	}

	private void checkPacket(NPFPacket packet, byte[] correctData) {
		byte[] data = new byte[packet.getLength()];
		packet.toBytes(data, 0, null);
		
		checkEquals(correctData, data);
	}
	
	public static void checkEquals(byte[] correctData, byte[] data) {
		assertEquals("Packet lengths differ:", correctData.length, data.length);
		for(int i = 0; i < data.length; i++) {
			if(data[i] != correctData[i]) {
				fail("Different values at index " + i + ": Expected 0x"
				                + Integer.toHexString(correctData[i] & 0xFF) + ", but was 0x"
						+ Integer.toHexString(data[i] & 0xFF));
			}
		}
	}
}
