/* * Minha.pt: middleware testing platform. * Copyright (c) 2011-2014, Universidade do Minho. * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ package pt.minha.test.model.net; import static java.util.concurrent.TimeUnit.SECONDS; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNotNull; import static org.testng.Assert.assertTrue; import java.io.IOException; import java.net.DatagramPacket; import java.net.DatagramSocket; import java.net.InetSocketAddress; import java.net.SocketTimeoutException; import mockit.Expectations; import mockit.FullVerifications; import mockit.Mocked; import org.testng.annotations.Test; import pt.minha.api.Entry; import pt.minha.api.Exit; import pt.minha.api.Host; import pt.minha.api.Process; import pt.minha.api.World; import pt.minha.api.sim.Global; import pt.minha.api.sim.Simulation; public class DatagramTest { @Global public static interface Callback { public void receive(DatagramPacket packet, InetSocketAddress address); public void exception(Exception e); } @Global public static interface Target { public void open(InetSocketAddress addr, Callback cb) throws IOException; public void send(DatagramPacket packet) throws IOException; public void close(); } public static class Impl implements Target { private DatagramSocket socket; private Callback callback; public void open(InetSocketAddress addr, Callback cb) throws IOException { this.callback = cb; this.socket = new DatagramSocket(addr.getPort()); new Thread() { public void run() { try { while(true) { DatagramPacket p = new DatagramPacket(new byte[1024], 1024); socket.receive(p); callback.receive(p, (InetSocketAddress)socket.getLocalSocketAddress()); } } catch(Exception e) { callback.exception(e); return; } } }.start(); } public void send(DatagramPacket packet) throws IOException { socket.send(packet); } public void close() { socket.close(); } } @Test public void loopback(@Mocked final Callback cb) throws Exception { Simulation world = new Simulation(); final Host host = world.createHost(); Process proc = host.createProcess(); final byte[] data = "Message!".getBytes(); Entry<Target> en = proc.createEntry(Target.class, Impl.class.getName()); Exit<Callback> ex = proc.createExit(Callback.class, cb); final InetSocketAddress addr = new InetSocketAddress(host.getAddress(), 12345); final DatagramPacket packet = new DatagramPacket(data, data.length, addr); en.queue().open(addr, ex.report()); en.at(1, SECONDS).queue().send(packet); world.run(); world.close(); new FullVerifications() { { DatagramPacket p; InetSocketAddress a; cb.receive(p = withCapture(), a = withCapture()); // Receiver address assertNotNull(a); assertEquals(a.getAddress(), host.getAddress()); assertEquals(a.getPort(), addr.getPort()); // Sender address assertNotNull(p); assertEquals(p.getAddress(), host.getAddress()); assertEquals(p.getPort(), addr.getPort()); // Data assertNotNull(p.getData()); assertTrue(checkData(p, data)); } }; } @Test public void p2p(@Mocked final Callback cb) throws Exception { Simulation world = new Simulation(); final Entry<Target>[] en = world.createEntries(2, Target.class, Impl.class.getName()); final Exit<Callback>[] ex = world.createExits(en, Callback.class, cb); final byte[] data = "Message!".getBytes(); final InetSocketAddress addr = new InetSocketAddress(en[1].getProcess().getHost().getAddress(), 12345); final DatagramPacket packet = new DatagramPacket(data, data.length, addr); for(int i = 0; i<en.length; i++) en[i].queue().open(addr, ex[i].report()); en[0].at(1, SECONDS).queue().send(packet); world.run(); world.close(); new FullVerifications() { { DatagramPacket p; InetSocketAddress a; cb.receive(p = withCapture(), a = withCapture()); // Receiver address assertNotNull(a); assertEquals(a.getAddress(), en[1].getProcess().getHost().getAddress()); assertEquals(a.getPort(), addr.getPort()); // Sender address assertNotNull(p); assertEquals(p.getAddress(), en[0].getProcess().getHost().getAddress()); assertEquals(p.getPort(), addr.getPort()); // Data assertNotNull(p.getData()); assertTrue(checkData(p, data)); } }; } public static boolean checkData(DatagramPacket packet, byte[] data) { if (packet.getLength() != data.length) return false; for(int i = 0; i<data.length; i++) if (data[i]!=packet.getData()[i]) return false; return true; } @Test public void wakeOnClose(@Mocked final Callback cb) throws Exception { new Expectations() { { //cb.exception(new SocketException("receive on closed socket")); } }; Simulation world = new Simulation(); final Entry<Target>[] en = world.createEntries(1, Target.class, Impl.class.getName()); final Exit<Callback>[] ex = world.createExits(en, Callback.class, cb); final InetSocketAddress addr = new InetSocketAddress(en[0].getProcess().getHost().getAddress(), 12345); en[0].queue().open(addr, ex[0].report()); en[0].at(5, SECONDS).queue().close(); world.run(10, SECONDS); world.close(); } @Global public interface Timeout { public void run() throws IOException; } public static class TimeoutImpl implements Timeout { @Override public void run() throws IOException { DatagramSocket ds = new DatagramSocket(); ds.setSoTimeout(1000); DatagramPacket p = new DatagramPacket(new byte[100], 100); ds.receive(p); } } @Test(expectedExceptions={SocketTimeoutException.class}) public void timeout() throws Exception { World world = new Simulation(); Entry<Timeout> en = world.createHost().createProcess().createEntry(Timeout.class, TimeoutImpl.class.getName()); en.call().run(); } }