/**
* Copyright (C) 2009 - present by OpenGamma Inc. and the OpenGamma group of companies
*
* Please see distribution for license.
*/
package com.opengamma.transport.socket;
import static org.testng.AssertJUnit.assertEquals;
import static org.testng.AssertJUnit.assertFalse;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertNull;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.fail;
import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import org.fudgemsg.FudgeContext;
import org.fudgemsg.wire.FudgeSize;
import org.testng.annotations.BeforeMethod;
import org.testng.annotations.Test;
import com.opengamma.OpenGammaRuntimeException;
import com.opengamma.util.test.TestGroup;
/**
* Test.
*/
@Test(groups = TestGroup.INTEGRATION)
public class MessageBatchingWriterTest {
private static final class DelayingOutputStream extends OutputStream {
private final Map<Thread, AtomicInteger> _writes = new HashMap<Thread, AtomicInteger>();
private final AtomicBoolean _writing = new AtomicBoolean();
@Override
public void write(int b) throws IOException {
assertFalse(_writing.getAndSet(true));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
throw new OpenGammaRuntimeException("Interrupted", e);
}
AtomicInteger count = _writes.get(Thread.currentThread());
if (count == null) {
count = new AtomicInteger(1);
_writes.put(Thread.currentThread(), count);
} else {
count.incrementAndGet();
}
assertTrue(_writing.getAndSet(false));
}
}
private DelayingOutputStream _out;
private MessageBatchingWriter _writer;
@BeforeMethod
public void init() throws IOException {
_out = new DelayingOutputStream();
_writer = new MessageBatchingWriter(FudgeContext.GLOBAL_DEFAULT, _out);
}
public void sequentialWritesNoBatching() {
final int count = 10;
for (int i = 0; i < count; i++) {
_writer.write(FudgeContext.EMPTY_MESSAGE);
}
// Everything written from the calling thread
assertEquals(1, _out._writes.size());
assertNotNull(_out._writes.get(Thread.currentThread()));
assertEquals(count * FudgeSize.calculateMessageEnvelopeSize(FudgeContext.EMPTY_MESSAGE), _out._writes.get(Thread.currentThread()).intValue());
}
@Test(invocationCount = 5, successPercentage = 19)
public void concurrentWritesWithBatching() throws InterruptedException {
final Thread[] threads = new Thread[6];
for (int i = 0; i < threads.length; i++) {
final int id = i;
threads[i] = new Thread() {
@Override
public void run() {
try {
Thread.sleep(10);
} catch (InterruptedException e) {
fail();
}
switch (id) {
case 0:
case 1:
case 3:
case 4:
threads[id + 1].start();
break;
}
_writer.write(FudgeContext.EMPTY_MESSAGE);
switch (id) {
case 0:
threads[3].start();
break;
}
}
};
}
threads[0].start();
for (int i = 0; i < threads.length; i++) {
threads[i].join();
}
// Thread 0 should have written 1 message.
// Thread 1 should have batched up 2 messages.
// Thread 2 should have offloaded its message to 1
// Thread 3 should have been blocked while thread 1 was writing and then written 3 batched messages
// Thread 4 should have offloaded its message to 3
// Thread 5 should have offloaded its message to 3
// System.out.println(_out._writes);
assertEquals(3, _out._writes.size());
assertNotNull(_out._writes.get(threads[0]));
assertEquals(FudgeSize.calculateMessageEnvelopeSize(FudgeContext.EMPTY_MESSAGE), _out._writes.get(threads[0]).intValue());
assertNotNull(_out._writes.get(threads[1]));
assertEquals(2 * FudgeSize.calculateMessageEnvelopeSize(FudgeContext.EMPTY_MESSAGE), _out._writes.get(threads[1]).intValue());
assertNull(_out._writes.get(threads[2]));
assertNotNull(_out._writes.get(threads[3]));
assertEquals(3 * FudgeSize.calculateMessageEnvelopeSize(FudgeContext.EMPTY_MESSAGE), _out._writes.get(threads[3]).intValue());
assertNull(_out._writes.get(threads[4]));
assertNull(_out._writes.get(threads[5]));
}
}