Home | History | Annotate | Download | only in collect
      1 /*
      2  * Copyright (C) 2011 The Guava Authors
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  * http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.common.collect;
     18 
     19 import com.google.common.util.concurrent.Uninterruptibles;
     20 
     21 import junit.framework.TestCase;
     22 
     23 import java.util.Collection;
     24 import java.util.List;
     25 import java.util.concurrent.ArrayBlockingQueue;
     26 import java.util.concurrent.BlockingQueue;
     27 import java.util.concurrent.ExecutorService;
     28 import java.util.concurrent.Executors;
     29 import java.util.concurrent.Future;
     30 import java.util.concurrent.LinkedBlockingQueue;
     31 import java.util.concurrent.PriorityBlockingQueue;
     32 import java.util.concurrent.SynchronousQueue;
     33 import java.util.concurrent.TimeUnit;
     34 
     35 /**
     36  * Tests for {@link Queues}.
     37  *
     38  * @author Dimitris Andreou
     39  */
     40 
     41 public class QueuesTest extends TestCase {
     42   /*
     43    * All the following tests relate to BlockingQueue methods in Queues.
     44    */
     45 
     46   public static List<BlockingQueue<Object>> blockingQueues() {
     47     return ImmutableList.<BlockingQueue<Object>>of(
     48         new LinkedBlockingQueue<Object>(),
     49         new LinkedBlockingQueue<Object>(10),
     50         new SynchronousQueue<Object>(),
     51         new ArrayBlockingQueue<Object>(10),
     52         new PriorityBlockingQueue<Object>(10, Ordering.arbitrary()));
     53   }
     54 
     55   private ExecutorService threadPool;
     56 
     57   @Override
     58   public void setUp() {
     59     threadPool = Executors.newCachedThreadPool();
     60   }
     61 
     62   @Override
     63   public void tearDown() throws InterruptedException {
     64     // notice that if a Producer is interrupted (a bug), the Producer will go into an infinite
     65     // loop, which will be noticed here
     66     threadPool.shutdown();
     67     assertTrue("Some worker didn't finish in time",
     68         threadPool.awaitTermination(1, TimeUnit.SECONDS));
     69   }
     70 
     71   private static <T> int drain(BlockingQueue<T> q, Collection<? super T> buffer, int maxElements,
     72       long timeout, TimeUnit unit, boolean interruptibly) throws InterruptedException {
     73     return interruptibly
     74         ? Queues.drain(q, buffer, maxElements, timeout, unit)
     75         : Queues.drainUninterruptibly(q, buffer, maxElements, timeout, unit);
     76   }
     77 
     78   public void testMultipleProducers() throws Exception {
     79     for (BlockingQueue<Object> q : blockingQueues()) {
     80       testMultipleProducers(q);
     81     }
     82   }
     83 
     84   private void testMultipleProducers(BlockingQueue<Object> q)
     85       throws InterruptedException {
     86     for (boolean interruptibly : new boolean[] { true, false }) {
     87       threadPool.submit(new Producer(q, 20));
     88       threadPool.submit(new Producer(q, 20));
     89       threadPool.submit(new Producer(q, 20));
     90       threadPool.submit(new Producer(q, 20));
     91       threadPool.submit(new Producer(q, 20));
     92 
     93       List<Object> buf = Lists.newArrayList();
     94       int elements = drain(q, buf, 100, Long.MAX_VALUE, TimeUnit.NANOSECONDS, interruptibly);
     95       assertEquals(100, elements);
     96       assertEquals(100, buf.size());
     97       assertDrained(q);
     98     }
     99   }
    100 
    101   public void testDrainTimesOut() throws Exception {
    102     for (BlockingQueue<Object> q : blockingQueues()) {
    103       testDrainTimesOut(q);
    104     }
    105   }
    106 
    107   private void testDrainTimesOut(BlockingQueue<Object> q) throws Exception {
    108     for (boolean interruptibly : new boolean[] { true, false }) {
    109       assertEquals(0, Queues.drain(q, ImmutableList.of(), 1, 10, TimeUnit.MILLISECONDS));
    110 
    111       // producing one, will ask for two
    112       Future<?> submitter = threadPool.submit(new Producer(q, 1));
    113 
    114       // make sure we time out
    115       long startTime = System.nanoTime();
    116 
    117       int drained = drain(q, Lists.newArrayList(), 2, 10, TimeUnit.MILLISECONDS, interruptibly);
    118       assertTrue(drained <= 1);
    119 
    120       assertTrue((System.nanoTime() - startTime) >= TimeUnit.MILLISECONDS.toNanos(10));
    121 
    122       // If even the first one wasn't there, clean up so that the next test doesn't see an element.
    123       submitter.get();
    124       if (drained == 0) {
    125         assertNotNull(q.poll());
    126       }
    127     }
    128   }
    129 
    130   public void testZeroElements() throws Exception {
    131     for (BlockingQueue<Object> q : blockingQueues()) {
    132       testZeroElements(q);
    133     }
    134   }
    135 
    136   private void testZeroElements(BlockingQueue<Object> q) throws InterruptedException {
    137     for (boolean interruptibly : new boolean[] { true, false }) {
    138       // asking to drain zero elements
    139       assertEquals(0, drain(q, ImmutableList.of(), 0, 10, TimeUnit.MILLISECONDS, interruptibly));
    140     }
    141   }
    142 
    143   public void testEmpty() throws Exception {
    144     for (BlockingQueue<Object> q : blockingQueues()) {
    145       testEmpty(q);
    146     }
    147   }
    148 
    149   private void testEmpty(BlockingQueue<Object> q) {
    150     assertDrained(q);
    151   }
    152 
    153   public void testNegativeMaxElements() throws Exception {
    154     for (BlockingQueue<Object> q : blockingQueues()) {
    155       testNegativeMaxElements(q);
    156     }
    157   }
    158 
    159   private void testNegativeMaxElements(BlockingQueue<Object> q) throws InterruptedException {
    160     threadPool.submit(new Producer(q, 1));
    161 
    162     List<Object> buf = Lists.newArrayList();
    163     int elements = Queues.drain(q, buf, -1, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
    164     assertEquals(elements, 0);
    165     assertTrue(buf.isEmpty());
    166 
    167     // Clean up produced element to free the producer thread, otherwise it will complain
    168     // when we shutdown the threadpool.
    169     Queues.drain(q, buf, 1, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
    170   }
    171 
    172   public void testDrain_throws() throws Exception {
    173     for (BlockingQueue<Object> q : blockingQueues()) {
    174       testDrain_throws(q);
    175     }
    176   }
    177 
    178   private void testDrain_throws(BlockingQueue<Object> q) {
    179     threadPool.submit(new Interrupter(Thread.currentThread()));
    180     try {
    181       Queues.drain(q, ImmutableList.of(), 100, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
    182       fail();
    183     } catch (InterruptedException expected) {
    184     }
    185   }
    186 
    187   public void testDrainUninterruptibly_doesNotThrow() throws Exception {
    188     for (BlockingQueue<Object> q : blockingQueues()) {
    189       testDrainUninterruptibly_doesNotThrow(q);
    190     }
    191   }
    192 
    193   private void testDrainUninterruptibly_doesNotThrow(final BlockingQueue<Object> q) {
    194     final Thread mainThread = Thread.currentThread();
    195     threadPool.submit(new Runnable() {
    196       public void run() {
    197         new Producer(q, 50).run();
    198         new Interrupter(mainThread).run();
    199         new Producer(q, 50).run();
    200       }
    201     });
    202     List<Object> buf = Lists.newArrayList();
    203     int elements =
    204         Queues.drainUninterruptibly(q, buf, 100, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
    205     // so when this drains all elements, we know the thread has also been interrupted in between
    206     assertTrue(Thread.interrupted());
    207     assertEquals(100, elements);
    208     assertEquals(100, buf.size());
    209   }
    210 
    211   public void testNewLinkedBlockingDequeCapacity() {
    212     try {
    213       Queues.newLinkedBlockingDeque(0);
    214       fail("Should have thrown IllegalArgumentException");
    215     } catch (IllegalArgumentException expected) {
    216       // any capacity less than 1 should throw IllegalArgumentException
    217     }
    218     assertEquals(1, Queues.newLinkedBlockingDeque(1).remainingCapacity());
    219     assertEquals(11, Queues.newLinkedBlockingDeque(11).remainingCapacity());
    220   }
    221 
    222   public void testNewLinkedBlockingQueueCapacity() {
    223     try {
    224       Queues.newLinkedBlockingQueue(0);
    225       fail("Should have thrown IllegalArgumentException");
    226     } catch (IllegalArgumentException expected) {
    227       // any capacity less than 1 should throw IllegalArgumentException
    228     }
    229     assertEquals(1, Queues.newLinkedBlockingQueue(1).remainingCapacity());
    230     assertEquals(11, Queues.newLinkedBlockingQueue(11).remainingCapacity());
    231   }
    232 
    233   /**
    234    * Checks that #drain() invocations behave correctly for a drained (empty) queue.
    235    */
    236   private void assertDrained(BlockingQueue<Object> q) {
    237     assertNull(q.peek());
    238     assertInterruptibleDrained(q);
    239     assertUninterruptibleDrained(q);
    240   }
    241 
    242   private void assertInterruptibleDrained(BlockingQueue<Object> q) {
    243     // nothing to drain, thus this should wait doing nothing
    244     try {
    245       assertEquals(0, Queues.drain(q, ImmutableList.of(), 0, 10, TimeUnit.MILLISECONDS));
    246     } catch (InterruptedException e) {
    247       throw new AssertionError();
    248     }
    249 
    250     // but does the wait actually occurs?
    251     threadPool.submit(new Interrupter(Thread.currentThread()));
    252     try {
    253       // if waiting works, this should get stuck
    254       Queues.drain(q, Lists.newArrayList(), 1, Long.MAX_VALUE, TimeUnit.NANOSECONDS);
    255       fail();
    256     } catch (InterruptedException expected) {
    257       // we indeed waited; a slow thread had enough time to interrupt us
    258     }
    259   }
    260 
    261   // same as above; uninterruptible version
    262   private void assertUninterruptibleDrained(BlockingQueue<Object> q) {
    263     assertEquals(0,
    264         Queues.drainUninterruptibly(q, ImmutableList.of(), 0, 10, TimeUnit.MILLISECONDS));
    265 
    266     // but does the wait actually occurs?
    267     threadPool.submit(new Interrupter(Thread.currentThread()));
    268 
    269     long startTime = System.nanoTime();
    270     Queues.drainUninterruptibly(
    271         q, Lists.newArrayList(), 1, 10, TimeUnit.MILLISECONDS);
    272     assertTrue((System.nanoTime() - startTime) >= TimeUnit.MILLISECONDS.toNanos(10));
    273     // wait for interrupted status and clear it
    274     while (!Thread.interrupted()) { Thread.yield(); }
    275   }
    276 
    277   private static class Producer implements Runnable {
    278     final BlockingQueue<Object> q;
    279     final int elements;
    280 
    281     Producer(BlockingQueue<Object> q, int elements) {
    282       this.q = q;
    283       this.elements = elements;
    284     }
    285 
    286     @Override public void run() {
    287       try {
    288         for (int i = 0; i < elements; i++) {
    289           q.put(new Object());
    290         }
    291       } catch (InterruptedException e) {
    292         // TODO(user): replace this when there is a better way to spawn threads in tests and
    293         // have threads propagate their errors back to the test thread.
    294         e.printStackTrace();
    295         // never returns, so that #tearDown() notices that one worker isn't done
    296         Uninterruptibles.sleepUninterruptibly(Long.MAX_VALUE, TimeUnit.MILLISECONDS);
    297       }
    298     }
    299   }
    300 
    301   private static class Interrupter implements Runnable {
    302     final Thread threadToInterrupt;
    303 
    304     Interrupter(Thread threadToInterrupt) {
    305       this.threadToInterrupt = threadToInterrupt;
    306     }
    307 
    308     @Override public void run() {
    309       try {
    310         Thread.sleep(100);
    311       } catch (InterruptedException e) {
    312         throw new AssertionError();
    313       } finally {
    314         threadToInterrupt.interrupt();
    315       }
    316     }
    317   }
    318 }
    319