Java tutorial
/** Copyright [2013] [Mushroom] 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. */ /** Notice : this source is extracted from Hadoop metric2 package and some source code may changed by zavakid */ package com.zavakid.mushroom.impl; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import java.util.ConcurrentModificationException; import java.util.concurrent.Semaphore; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.junit.Test; /** * Test the half-blocking metrics sink queue * * @author Hadoop metric2 package's authors * @since 0.1 */ public class TestSinkQueue { private final Log LOG = LogFactory.getLog(TestSinkQueue.class); /** * Test common use case * * @throws Exception */ @Test public void testCommon() throws Exception { final SinkQueue<Integer> q = new SinkQueue<Integer>(2); q.enqueue(1); assertEquals("queue front", 1, (int) q.front()); assertEquals("queue back", 1, (int) q.back()); assertEquals("element", 1, (int) q.dequeue()); assertTrue("should enqueue", q.enqueue(2)); q.consume(new Consumer<Integer>() { public void consume(Integer e) { assertEquals("element", 2, (int) e); } }); assertTrue("should enqueue", q.enqueue(3)); assertEquals("element", 3, (int) q.dequeue()); assertEquals("queue size", 0, q.size()); assertEquals("queue front", null, q.front()); assertEquals("queue back", null, q.back()); } /** * Test blocking when queue is empty * * @throws Exception */ @Test public void testEmptyBlocking() throws Exception { final SinkQueue<Integer> q = new SinkQueue<Integer>(2); final Runnable trigger = mock(Runnable.class); // try consuming emtpy equeue and blocking Thread t = new Thread() { @Override public void run() { try { assertEquals("element", 1, (int) q.dequeue()); q.consume(new Consumer<Integer>() { public void consume(Integer e) { assertEquals("element", 2, (int) e); trigger.run(); } }); } catch (InterruptedException e) { LOG.warn("Interrupted", e); } } }; t.start(); Thread.yield(); // Let the other block q.enqueue(1); q.enqueue(2); t.join(); verify(trigger).run(); } /** * Test nonblocking enqueue when queue is full * * @throws Exception */ @Test public void testFull() throws Exception { final SinkQueue<Integer> q = new SinkQueue<Integer>(1); q.enqueue(1); assertTrue("should drop", !q.enqueue(2)); assertEquals("element", 1, (int) q.dequeue()); q.enqueue(3); q.consume(new Consumer<Integer>() { public void consume(Integer e) { assertEquals("element", 3, (int) e); } }); assertEquals("queue size", 0, q.size()); } /** * Test the consumeAll method * * @throws Exception */ @Test public void testConsumeAll() throws Exception { final int capacity = 64; // arbitrary final SinkQueue<Integer> q = new SinkQueue<Integer>(capacity); for (int i = 0; i < capacity; ++i) { assertTrue("should enqueue", q.enqueue(i)); } assertTrue("should not enqueue", !q.enqueue(capacity)); final Runnable trigger = mock(Runnable.class); q.consumeAll(new Consumer<Integer>() { private int expected = 0; public void consume(Integer e) { assertEquals("element", expected++, (int) e); trigger.run(); } }); verify(trigger, times(capacity)).run(); } /** * Test the consumer throwing exceptions * * @throws Exception */ @Test public void testConsumerException() throws Exception { final SinkQueue<Integer> q = new SinkQueue<Integer>(1); final RuntimeException ex = new RuntimeException("expected"); q.enqueue(1); try { q.consume(new Consumer<Integer>() { public void consume(Integer e) { throw ex; } }); } catch (Exception expected) { assertSame("consumer exception", ex, expected); } // The queue should be in consistent state after exception assertEquals("queue size", 1, q.size()); assertEquals("element", 1, (int) q.dequeue()); } /** * Test the clear method */ @Test public void testClear() { final SinkQueue<Integer> q = new SinkQueue<Integer>(128); for (int i = 0; i < q.capacity() + 97; ++i) { q.enqueue(i); } assertEquals("queue size", q.capacity(), q.size()); q.clear(); assertEquals("queue size", 0, q.size()); } /** * Test consumers that take their time. * * @throws Exception */ @Test public void testHangingConsumer() throws Exception { SinkQueue<Integer> q = newSleepingConsumerQueue(2, 1, 2); assertEquals("queue back", 2, (int) q.back()); assertTrue("should drop", !q.enqueue(3)); // should not block assertEquals("queue size", 2, q.size()); assertEquals("queue head", 1, (int) q.front()); assertEquals("queue back", 2, (int) q.back()); } /** * Test concurrent consumer access, which is illegal * * @throws Exception */ @Test public void testConcurrentConsumers() throws Exception { final SinkQueue<Integer> q = newSleepingConsumerQueue(2, 1); assertTrue("should enqueue", q.enqueue(2)); assertEquals("queue back", 2, (int) q.back()); assertTrue("should drop", !q.enqueue(3)); // should not block shouldThrowCME(new Fun() { public void run() { q.clear(); } }); shouldThrowCME(new Fun() { public void run() throws Exception { q.consume(null); } }); shouldThrowCME(new Fun() { public void run() throws Exception { q.consumeAll(null); } }); shouldThrowCME(new Fun() { public void run() throws Exception { q.dequeue(); } }); // The queue should still be in consistent state after all the exceptions assertEquals("queue size", 2, q.size()); assertEquals("queue front", 1, (int) q.front()); assertEquals("queue back", 2, (int) q.back()); } private void shouldThrowCME(Fun callback) throws Exception { try { callback.run(); } catch (ConcurrentModificationException e) { LOG.info(e); return; } fail("should've thrown"); } private SinkQueue<Integer> newSleepingConsumerQueue(int capacity, int... values) { final SinkQueue<Integer> q = new SinkQueue<Integer>(capacity); final Semaphore semaphore = new Semaphore(0); for (int i : values) { q.enqueue(i); } Thread t = new Thread() { @Override public void run() { try { q.consume(new Consumer<Integer>() { public void consume(Integer e) throws InterruptedException { semaphore.release(1); LOG.info("sleeping"); Thread.sleep(1000 * 86400); // a long time } }); } catch (InterruptedException ex) { LOG.warn("Interrupted", ex); } } }; t.setName("Sleeping consumer"); t.setDaemon(true); // so jvm can exit t.start(); try { semaphore.acquire(); } catch (InterruptedException e) { e.printStackTrace(); } LOG.debug("Returning new sleeping consumer queue"); return q; } static interface Fun { void run() throws Exception; } }