/* * Copyright 2013 Thomas Bocek * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package net.tomp2p.message; import static org.mockito.Matchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBufAllocator; import io.netty.buffer.Unpooled; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelPromise; import io.netty.channel.socket.DatagramChannel; import io.netty.util.Attribute; import io.netty.util.AttributeKey; import java.net.InetSocketAddress; import java.security.KeyPair; import java.security.KeyPairGenerator; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Random; import java.util.concurrent.atomic.AtomicReference; import net.tomp2p.Utils2; import net.tomp2p.connection2.DefaultSignatureFactory; import net.tomp2p.message.Message2.Content; import net.tomp2p.peers.Number160; import net.tomp2p.peers.Number480; import net.tomp2p.storage.Data; import net.tomp2p.utils.Utils; import org.junit.Assert; import org.junit.Test; import org.mockito.invocation.InvocationOnMock; import org.mockito.stubbing.Answer; /** * Tests encoding of an empty message. These tests should not be used for performance measuremenst, since mockito is * used, and I guess this would falsify the results. * * As a reference, on my laptop IBM T60 its around 190 enc-dec/ms, so we can encode and decode with a single core * 192'000 messages per second. For a message smallest size with 59bytes, this means that we can saturate an 86mbit/s * link. The larger the message, the more bandwidth it is used (QuadCore~330 enc-dec/ms). * * @author Thomas Bocek * */ public class TestMessage { /** * Test a simple message to endcode and then decode. * * @throws Exception . */ @Test public void testEncodeDecode() throws Exception { // encode Message2 m1 = Utils2.createDummyMessage(); Message2 m2 = encodeDecode(m1); compareMessage(m1, m2); } /** * Tests a different command, type and integer. * * @throws Exception . */ @Test public void testEncodeDecode2() throws Exception { // encode Random rnd = new Random(42); Message2 m1 = Utils2.createDummyMessage(); m1.setCommand((byte) 3); m1.setType(Message2.Type.DENIED); Number160 key1 = new Number160(5667); Number160 key2 = new Number160(5667); m1.setKey(key1); m1.setKey(key2); List<Number480> tmp2 = new ArrayList<Number480>(); Number480 n1 = new Number480(rnd); Number480 n2 = new Number480(rnd); tmp2.add(n1); tmp2.add(n2); m1.setKeys(new Keys(tmp2)); Message2 m2 = encodeDecode(m1); Assert.assertEquals(false, m2.getKeyList() == null); Assert.assertEquals(false, m2.getKeysList() == null); compareMessage(m1, m2); } /** * Tests Number160 and string. * * @throws Exception . */ @Test public void testEncodeDecode3() throws Exception { // encode Message2 m1 = Utils2.createDummyMessage(); m1.setType(Message2.Type.DENIED); m1.setLong(8888888); byte[] me = new byte[10000]; ByteBuf tmp = Unpooled.wrappedBuffer(me); m1.setBuffer(new Buffer(tmp)); Message2 m2 = encodeDecode(m1); Assert.assertEquals(false, m2.getBuffer(0) == null); compareMessage(m1, m2); } /** * Tests neighbors and payload. * * @throws Exception . */ @Test public void testEncodeDecode4() throws Exception { Message2 m1 = Utils2.createDummyMessage(); Random rnd = new Random(42); m1.setType(Message2.Type.DENIED); m1.setHintSign(); KeyPairGenerator gen = KeyPairGenerator.getInstance("DSA"); KeyPair pair1 = gen.generateKeyPair(); m1.setPublicKeyAndSign(pair1); Map<Number480, Data> dataMap = new HashMap<Number480, Data>(); dataMap.put(new Number480(rnd), new Data(new byte[] { 3, 4, 5 }, true, true)); dataMap.put(new Number480(rnd), new Data(new byte[] { 4, 5, 6 }, true, true)); dataMap.put(new Number480(rnd), new Data(new byte[] { 5, 6, 7 }, true, true)); m1.setDataMap(new DataMap(dataMap)); Map<Number480, Number160> keysMap = new HashMap<Number480, Number160>(); keysMap.put(new Number480(rnd), new Number160(rnd)); keysMap.put(new Number480(rnd), new Number160(rnd)); keysMap.put(new Number480(rnd), new Number160(rnd)); m1.setKeysMap(new KeysMap(keysMap)); // Message2 m2 = encodeDecode(m1); Assert.assertEquals(true, m2.getPublicKey() != null); Assert.assertEquals(false, m2.getDataMap(0) == null); Assert.assertEquals(false, m2.getKeysMap(0) == null); compareMessage(m1, m2); } @Test public void testEncodeDecode6() throws Exception { for (int i = 0; i < 4; i++) { // encode and test for is firewallend and ipv4 Message2 m1 = Utils2.createDummyMessage((i & 1) > 0, (i & 2) > 0); Message2 m2 = encodeDecode(m1); compareMessage(m1, m2); } } @Test public void testNumber160Conversion() { Number160 i1 = new Number160("0x9908836242582063284904568868592094332017"); Number160 i2 = new Number160("0x9609416068124319312270864915913436215856"); Number160 i3 = new Number160("0x7960941606812431931227086491591343621585"); byte[] me = i1.toByteArray(); Assert.assertEquals(i1, new Number160(me)); me = i2.toByteArray(); Assert.assertEquals(i2, new Number160(me)); me = i3.toByteArray(); byte[] me2 = new byte[20]; System.arraycopy(me, 0, me2, me2.length - me.length, me.length); Assert.assertEquals(i3, new Number160(me2)); } @Test public void testBigData() throws Exception { final int size = 50 * 1024 * 1024; Random rnd = new Random(42); Message2 m1 = Utils2.createDummyMessage(); Map<Number480, Data> dataMap = new HashMap<Number480, Data>(); Data data = new Data(new byte[size], true, false); dataMap.put(new Number480(rnd), data); m1.setDataMap(new DataMap(dataMap)); Message2 m2 = encodeDecode(m1); compareMessage(m1, m2); } @Test public void testEncodeDecode480Map() throws Exception { // encode Message2 m1 = Utils2.createDummyMessage(); m1.setType(Message2.Type.PARTIALLY_OK); KeyPairGenerator gen = KeyPairGenerator.getInstance("DSA"); KeyPair pair1 = gen.generateKeyPair(); m1.setPublicKeyAndSign(pair1); Map<Number480, Data> dataMap = new HashMap<Number480, Data>(1000); Random rnd = new Random(42l); for (int i = 0; i < 1000; i++) { dataMap.put(new Number480(new Number160(rnd), new Number160(rnd), new Number160(rnd)), new Data(new byte[] { (byte) rnd.nextInt(), (byte) rnd.nextInt(), (byte) rnd.nextInt(), (byte) rnd.nextInt(), (byte) rnd.nextInt() }, true, true)); } m1.setDataMap(new DataMap(dataMap)); Message2 m2 = encodeDecode(m1); Assert.assertEquals(true, m2.getPublicKey() != null); compareMessage(m1, m2); } @Test public void testEncodeDecode480Set() throws Exception { // encode Message2 m1 = Utils2.createDummyMessage(); m1.setType(Message2.Type.NOT_FOUND); KeyPairGenerator gen = KeyPairGenerator.getInstance("DSA"); KeyPair pair1 = gen.generateKeyPair(); m1.setPublicKeyAndSign(pair1); Collection<Number480> list = new ArrayList<Number480>(); Random rnd = new Random(42l); for (int i = 0; i < 1000; i++) { list.add(new Number480(new Number160(rnd), new Number160(rnd), new Number160(rnd))); } m1.setKeys(new Keys(list)); Message2 m2 = encodeDecode(m1); Assert.assertEquals(true, m2.getPublicKey() != null); compareMessage(m1, m2); } /** * Encodes and decodes a message. * * @param m1 * The message the will be encoded * @return The message that was decoded. * @throws Exception . */ private Message2 encodeDecode(final Message2 m1) throws Exception { AtomicReference<Message2> m2 = new AtomicReference<Message2>(); TomP2POutbound encoder = new TomP2POutbound(true, new DefaultSignatureFactory()); ByteBuf buf = Unpooled.buffer(); ChannelHandlerContext ctx = mockChannelHandlerContext(buf, m2); encoder.write(ctx, m1, null); TomP2PDecoder decoder = new TomP2PDecoder(new DefaultSignatureFactory()); decoder.decode(ctx, buf, m1.getRecipient().createSocketTCP(), m1.getSender().createSocketTCP()); return m2.get(); } /** * Mock Nettys ChannelHandlerContext with the minimal functions. * * @param buf * The buffer to use for decoding * @param m2 * The message reference to store the result * @return The mocked ChannelHandlerContext */ @SuppressWarnings("unchecked") private ChannelHandlerContext mockChannelHandlerContext(final ByteBuf buf, final AtomicReference<Message2> m2) { ChannelHandlerContext ctx = mock(ChannelHandlerContext.class); ByteBufAllocator alloc = mock(ByteBufAllocator.class); when(ctx.alloc()).thenReturn(alloc); when(alloc.ioBuffer()).thenReturn(buf); DatagramChannel dc = mock(DatagramChannel.class); when(ctx.channel()).thenReturn(dc); when(ctx.writeAndFlush(any(), any(ChannelPromise.class))).thenReturn(null); Attribute<InetSocketAddress> attr = mock(Attribute.class); when(ctx.attr(any(AttributeKey.class))).thenReturn(attr); when(ctx.fireChannelRead(any())).then(new Answer<Void>() { @Override public Void answer(final InvocationOnMock invocation) throws Throwable { Object[] args = invocation.getArguments(); m2.set((Message2) args[0]); return null; } }); return ctx; } /** * Checks if two messages are the same. * * @param m1 * The first message * @param m2 * The second message */ private void compareMessage(final Message2 m1, final Message2 m2) { Assert.assertEquals(m1.getMessageId(), m2.getMessageId()); Assert.assertEquals(m1.getVersion(), m2.getVersion()); Assert.assertEquals(m1.getCommand(), m2.getCommand()); compareContentTypes(m1, m2); Assert.assertEquals(m1.getRecipient(), m2.getRecipient()); Assert.assertEquals(m1.getType(), m2.getType()); Assert.assertEquals(m1.getSender(), m2.getSender()); Assert.assertEquals(true, Utils.isSameSets(m1.getBloomFilterList(), m2.getBloomFilterList())); Assert.assertEquals(true, Utils.isSameSets(m1.getBufferList(), m2.getBufferList())); Assert.assertEquals(true, Utils.isSameSets(m1.getDataMapList(), m2.getDataMapList())); Assert.assertEquals(true, Utils.isSameSets(m1.getIntegerList(), m2.getIntegerList())); Assert.assertEquals(true, Utils.isSameSets(m1.getKeyList(), m2.getKeyList())); Assert.assertEquals(true, Utils.isSameSets(m1.getKeysList(), m2.getKeysList())); Assert.assertEquals(true, Utils.isSameSets(m1.getKeysMapList(), m2.getKeysMapList())); Assert.assertEquals(true, Utils.isSameSets(m1.getLongList(), m2.getLongList())); Assert.assertEquals(true, Utils.isSameSets(m1.getNeighborsSetList(), m2.getNeighborsSetList())); Assert.assertEquals(m1.getSender().isFirewalledTCP(), m2.getSender().isFirewalledTCP()); Assert.assertEquals(m1.getSender().isFirewalledUDP(), m2.getSender().isFirewalledUDP()); } /** * Create the content types of messages. If a content type is null or Empty, this is the same. * * @param m1 * The first message * @param m2 * The second message */ private void compareContentTypes(final Message2 m1, final Message2 m2) { for (int i = 0; i < m1.getContentTypes().length; i++) { Content type1 = m1.getContentTypes()[i]; Content type2 = m2.getContentTypes()[i]; if (type1 == null) { type1 = Content.EMPTY; } if (type2 == null) { type2 = Content.EMPTY; } Assert.assertEquals(type1, type2); } } }