/*
* $Id$
*
* Copyright (c) 2010 by Joel Uckelman
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License (LGPL) as published by the Free Software Foundation.
*
* This library 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
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, copies are available
* at http://www.opensource.org.
*/
package VASSAL.tools.ipc;
import java.io.InputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import VASSAL.tools.ThrowableUtils;
import VASSAL.tools.concurrent.SimpleFuture;
import VASSAL.tools.concurrent.listener.EventListener;
import VASSAL.tools.concurrent.listener.MultiEventListenerSupport;
import org.junit.Test;
import static org.junit.Assert.*;
public class IPCMessengerTest {
@Test
public void testSendNoReply() throws IOException, ClassNotFoundException {
final PipedOutputStream out1 = new PipedOutputStream();
final PipedInputStream in2 = new PipedInputStream(out1);
final PipedOutputStream out2 = new PipedOutputStream();
final PipedInputStream in1 = new PipedInputStream(out2);
new Thread(new Runnable() {
public void run() {
try {
final IPCMessenger ipc1 = new IPCMessenger(in1, out1);
ipc1.start();
for (int i = 0; i < 100; ++i) {
ipc1.send(new SimpleIPCMessage());
}
ipc1.stop();
}
catch (Throwable e) {
fail(ThrowableUtils.getStackTrace(e));
}
}
}).start();
final ObjectOutputStream out = new ObjectOutputStream(out2);
final ObjectInputStream in = new ObjectInputStream(in2);
Object o;
// test message sending
for (int i = 0; i < 100; ++i) {
o = in.readObject();
assertTrue(o instanceof SimpleIPCMessage);
assertEquals(i, ((SimpleIPCMessage) o).getId());
}
// test shutdown
o = in.readObject();
assertTrue(o instanceof Halt);
final Halt halt = new Halt();
halt.setId(0);
out.writeObject(halt);
final Fin fin = new Fin((Halt) o);
fin.setId(1);
out.writeObject(fin);
out.flush();
o = in.readObject();
assertTrue(o instanceof Fin);
in.close();
}
@Test
public void testSendWithReply() throws IOException, ClassNotFoundException {
final PipedOutputStream out1 = new PipedOutputStream();
final PipedInputStream in2 = new PipedInputStream(out1);
final PipedOutputStream out2 = new PipedOutputStream();
final PipedInputStream in1 = new PipedInputStream(out2);
new Thread(new Runnable() {
public void run() {
try {
final IPCMessenger ipc = new IPCMessenger(in1, out1);
ipc.addEventListener(Ack.class, new EventListener<Ack>() {
protected int irt = 0;
public void receive(Object src, Ack ack) {
assertEquals(irt++, ack.getInReplyTo());
}
});
ipc.start();
for (int i = 0; i < 100; ++i) {
ipc.send(new SimpleIPCMessage());
}
ipc.stop();
}
catch (Throwable e) {
fail(ThrowableUtils.getStackTrace(e));
}
}
}).start();
final ObjectOutputStream out = new ObjectOutputStream(out2);
final ObjectInputStream in = new ObjectInputStream(in2);
Object o;
// test message sending
for (int i = 0; i < 100; ++i) {
o = in.readObject();
assertTrue(o instanceof SimpleIPCMessage);
final SimpleIPCMessage m = (SimpleIPCMessage) o;
assertEquals(i, m.getId());
out.writeObject(new Ack(m));
}
// test shutdown
o = in.readObject();
assertTrue(o instanceof Halt);
final Halt halt = new Halt();
halt.setId(0);
out.writeObject(halt);
final Fin fin = new Fin((Halt) o);
fin.setId(1);
out.writeObject(fin);
out.flush();
o = in.readObject();
assertTrue(o instanceof Fin);
in.close();
}
protected static class Msg extends SimpleIPCMessage {
private static final long serialVersionUID = 1L;
@Override
public boolean expectsReply() { return true; }
}
protected Future<?> runIPCMessenger(String threadName,
final InputStream in,
final OutputStream out,
final Future<IPCMessage>[] f) {
final SimpleFuture<?> d = new SimpleFuture<Void>();
new Thread(new Runnable() {
public void run() {
try {
final IPCMessenger ipc = new IPCMessenger(in, out);
ipc.addEventListener(Msg.class, new EventListener<Msg>() {
public void receive(Object src, Msg msg) {
try {
ipc.send(new Ack(msg));
}
catch (IOException e) {
fail(ThrowableUtils.getStackTrace(e));
}
}
});
ipc.start();
for (int i = 0; i < f.length; ++i) {
f[i] = ipc.send(new Msg());
}
ipc.stop();
d.set(null);
}
catch (Throwable e) {
fail(ThrowableUtils.getStackTrace(e));
}
}
}, threadName).start();
return d;
}
@Test
@SuppressWarnings("unchecked")
public void testBidirectional() throws IOException,
ClassNotFoundException,
ExecutionException,
InterruptedException {
final Future<IPCMessage> f1[] = new Future[1000];
final Future<IPCMessage> f2[] = new Future[1000];
final PipedOutputStream out1 = new PipedOutputStream();
final PipedInputStream in2 = new PipedInputStream(out1);
final PipedOutputStream out2 = new PipedOutputStream();
final PipedInputStream in1 = new PipedInputStream(out2);
final Future<?> d1 = runIPCMessenger("ipc1", in1, out1, f1);
final Future<?> d2 = runIPCMessenger("ipc2", in2, out2, f2);
d1.get();
d2.get();
for (int i = 0; i < f1.length; ++i) {
assertTrue(f1[i].get() instanceof Ack);
}
for (int i = 0; i < f2.length; ++i) {
assertTrue(f2[i].get() instanceof Ack);
}
}
}