package org.marketcetera.core.publisher; import org.junit.Before; import org.junit.Test; import org.junit.BeforeClass; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertTrue; import org.marketcetera.core.LoggerConfiguration; import java.util.Random; import java.util.List; import java.util.LinkedList; import java.util.concurrent.Semaphore; /** * Tests {@link PublisherEngine}. * * @author <a href="mailto:colin@marketcetera.com">Colin DuPlantis</a> * @version $Id: PublisherEngineTest.java 16154 2012-07-14 16:34:05Z colin $ * @since 0.5.0 */ public class PublisherEngineTest { private MockPublisher mPublisher; @BeforeClass public static void logSetup() { LoggerConfiguration.logSetup(); } @Before public void setUp() throws Exception { mPublisher = new MockPublisher(); } @Test public void testInitializeThreadPool() throws Exception { mPublisher.publish(this); mPublisher.publish(this); } @Test public void testConstructor() throws Exception { PublisherEngine e = new PublisherEngine(); e.publish(this); } @Test public void testSubscribe() throws Exception { mPublisher.subscribe(null); mPublisher.publishAndWait(this); MockSubscriber s = new MockSubscriber(); mPublisher.subscribe(s); assertEquals(0, s.getPublishCount()); assertNull(s.getData()); mPublisher.publishAndWait(this); assertEquals(this, s.getData()); assertEquals(1, s.getPublishCount()); // subscribe again, make sure we get only only publication s.setData(null); mPublisher.subscribe(s); assertEquals(1, s.getPublishCount()); assertNull(s.getData()); mPublisher.publishAndWait(this); assertEquals(this, s.getData()); assertEquals(2, // not 3! s.getPublishCount()); } @Test public void testUnsubscribe() throws Exception { mPublisher.unsubscribe(null); mPublisher.publish(this); MockSubscriber s = new MockSubscriber(); assertEquals(0, s.getPublishCount()); assertEquals(null, s.getData()); mPublisher.unsubscribe(s); mPublisher.publish(this); assertEquals(0, s.getPublishCount()); assertEquals(null, s.getData()); mPublisher.subscribe(s); mPublisher.publish(this); while(s.getPublishCount() == 0) { Thread.sleep(100); } assertEquals(this, s.getData()); assertEquals(1, s.getPublishCount()); s.setData(null); mPublisher.unsubscribe(s); mPublisher.publish(this); Thread.sleep(5000); assertEquals(1, s.getPublishCount()); assertEquals(null, s.getData()); } @Test public void testParallel() throws Exception { MockPublisher[] publishers = new MockPublisher[50]; for(int i=0;i<50;i++) { publishers[i] = new MockPublisher(); } MockSubscriber[] subscribers = new MockSubscriber[500]; for(int i=0;i<subscribers.length;i++) { subscribers[i] = new MockSubscriber(); } Thread[] threads = new Thread[20]; for(int i=0;i<20;i++) { threads[i] = new Thread(new Tester(publishers, subscribers)); threads[i].start(); } for(Thread t : threads) { t.join(); } } @Test public void testSubscribers() throws Exception { List<MockSubscriber> subscribers = new LinkedList<MockSubscriber>(); //subscribers with all permutations. boolean [] values = {true,false}; for(boolean interesting: values) { for(boolean interestingThrows: values) { for(boolean publishThrows: values) { subscribers.add(new MockSubscriber(interesting, interestingThrows, publishThrows)); } } } for (boolean synchronous: values) { for(int i = 0; i < subscribers.size(); i++) { doRunTest(synchronous, subscribers.subList(0, i), null); doRunTest(synchronous, subscribers.subList(0, i), this); } } } @Test(timeout = 10000) public void testSyncAsyncPublishAndWait() throws Exception { //Test default constructor PublisherEngine engine = new PublisherEngine(); checkPublishAndWait(engine, false); //Test the other constructor engine = new PublisherEngine(false); checkPublishAndWait(engine, false); //Test the other constructor engine = new PublisherEngine(true); checkPublishAndWait(engine, true); } @Test public void testSyncAsyncPublish() throws Exception { //Test default constructor PublisherEngine engine = new PublisherEngine(); checkPublish(engine, false); //Test the other constructor engine = new PublisherEngine(false); checkPublish(engine, false); //Test the other constructor engine = new PublisherEngine(true); checkPublish(engine, true); } @Test(timeout = 10000) public void testSlowSubscriberAsync() throws Exception { MockSubscriber subscriber = new MockSubscriber(); Semaphore acquireSemaphore = new Semaphore(0); Semaphore releaseSemaphore = new Semaphore(0); subscriber.setAcquireSemaphore(acquireSemaphore); subscriber.setReleaseSemaphore(releaseSemaphore); PublisherEngine engine = new PublisherEngine(); engine.subscribe(subscriber); Object data = new Object(); engine.publish(data); //Wait for subscriber to be ready while(!acquireSemaphore.hasQueuedThreads()) { Thread.sleep(500); } //verify subscriber hasn't received anything assertNull(subscriber.getData()); //now let the subscriber through acquireSemaphore.release(); //And wait for it to get done releaseSemaphore.acquire(); //verify that subscriber received data assertEquals(data, subscriber.getData()); } private static void checkPublishAndWait(PublisherEngine inEngine, boolean inSyncNotification) throws Exception { final MockSubscriber subscriber = new MockSubscriber(); assertEquals(inSyncNotification, inEngine.isSynchronousNotification()); inEngine.subscribe(subscriber); Object data = new Object(); inEngine.publishAndWait(data); assertSame(data, subscriber.getData()); if (inSyncNotification) { assertSame(Thread.currentThread(), subscriber.getPublishThread()); } else { assertNotSame(Thread.currentThread(), subscriber.getPublishThread()); } } private static void checkPublish(PublisherEngine inEngine, boolean inSyncNotification) throws Exception { final MockSubscriber subscriber = new MockSubscriber(); final Semaphore s = new Semaphore(0); subscriber.setReleaseSemaphore(s); assertEquals(inSyncNotification, inEngine.isSynchronousNotification()); inEngine.subscribe(subscriber); Object data = new Object(); inEngine.publish(data); //wait for subscriber to receive it. s.acquire(); assertSame(data, subscriber.getData()); if (inSyncNotification) { assertSame(Thread.currentThread(), subscriber.getPublishThread()); } else { assertNotSame(Thread.currentThread(), subscriber.getPublishThread()); } } private void doRunTest(boolean inSynchronousPublication, List<MockSubscriber> inSubscribers, Object inData) throws Exception { for(MockSubscriber s : inSubscribers) { s.reset(); } PublisherEngine engine = new PublisherEngine(inSynchronousPublication); for(MockSubscriber subscriber: inSubscribers) { engine.subscribe(subscriber); } engine.publishAndWait(inData); int lastCounter = 0; for(MockSubscriber s : inSubscribers) { if(s.getInteresting() && !(s.getInterestingThrows() || s.getPublishThrows())) { assertEquals(inData, s.getData()); assertTrue(s.getCounter() > lastCounter); lastCounter = s.getCounter(); } else { assertNull(s.getData()); } } } public static class Tester implements Runnable { private Random r = new Random(System.nanoTime()); private MockPublisher[] mPublishers; private MockSubscriber[] mSubscribers; public Tester(MockPublisher[] inPublishers, MockSubscriber[] inSubscribers) { mPublishers = inPublishers; mSubscribers = inSubscribers; } public void run() { for(int i=0;i<10;i++) { for(MockSubscriber s : mSubscribers) { int publisher = r.nextInt(50); int flag = r.nextInt(3); switch(flag) { case 0: mPublishers[publisher].subscribe(s); break; case 1: mPublishers[publisher].unsubscribe(s); break; case 2: mPublishers[publisher].publish(this); break; } } } } } }