/* * Copyright (C) 2010 The Android Open Source Project * * 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. */ package com.android.tradefed.util; import com.android.tradefed.util.ConditionPriorityBlockingQueue.IMatcher; import junit.framework.TestCase; import java.util.Comparator; import java.util.concurrent.TimeUnit; /** * Unit tests for {@link ConditionPriorityBlockingQueue}. */ public class ConditionPriorityBlockingQueueTest extends TestCase { private ConditionPriorityBlockingQueue<Integer> mQueue; /** * {@inheritDoc} */ @Override protected void setUp() throws Exception { super.setUp(); mQueue = new ConditionPriorityBlockingQueue<Integer>(new IntCompare()); } /** * Test {@link ConditionPriorityBlockingQueue#poll()} when queue is empty. */ public void testPoll_empty() { assertNull(mQueue.poll()); } /** * Test {@link ConditionPriorityBlockingQueue#take()} when a single object is in queue. */ public void testTake() throws InterruptedException { Integer one = new Integer(1); mQueue.add(one); assertEquals(one, mQueue.take()); assertNull(mQueue.poll()); } /** * Test {@link ConditionPriorityBlockingQueue#take()} when multiple objects are in queue, and * verify objects are returned in expected order. */ public void testTake_priority() throws InterruptedException { Integer one = new Integer(1); Integer two = new Integer(2); mQueue.add(two); mQueue.add(one); assertEquals(one, mQueue.take()); assertEquals(two, mQueue.take()); assertNull(mQueue.poll()); } /** * Test {@link ConditionPriorityBlockingQueue#poll()} when using FIFO ordering. */ public void testTake_fifo() throws InterruptedException { ConditionPriorityBlockingQueue<Integer> fifoQueue = new ConditionPriorityBlockingQueue<Integer>(); Integer one = new Integer(1); Integer two = new Integer(2); fifoQueue.add(two); fifoQueue.add(one); assertEquals(two, fifoQueue.take()); assertEquals(one, fifoQueue.take()); assertNull(fifoQueue.poll()); } /** * Same as {@link ConditionPriorityBlockingQueueTest#testTake_priority()}, but add the test * objects in inverse order. */ public void testTake_priorityReverse() throws InterruptedException { Integer one = new Integer(1); Integer two = new Integer(2); mQueue.add(one); mQueue.add(two); assertEquals(one, mQueue.take()); assertEquals(two, mQueue.take()); assertNull(mQueue.poll()); } /** * Test {@link ConditionPriorityBlockingQueue#take()} when object is not initially present. */ public void testTake_delayedAdd() throws InterruptedException { final Integer one = new Integer(1); Thread delayedAdd = new Thread() { @Override public void run() { try { sleep(200); } catch (InterruptedException e) { } mQueue.add(one); } }; delayedAdd.start(); assertEquals(one, mQueue.take()); assertNull(mQueue.poll()); } /** * Test {@link ConditionPriorityBlockingQueue#take(IMatcher)} when object that matches is not * initially present. */ public void testTake_matcher_delayedAdd() throws InterruptedException { final Integer one = new Integer(1); final Integer two = new Integer(2); mQueue.add(two); Thread delayedAdd = new Thread() { @Override public void run() { try { sleep(200); } catch (InterruptedException e) { } mQueue.add(one); } }; delayedAdd.start(); assertEquals(one, mQueue.take(new OneMatcher())); assertNull(mQueue.poll(new OneMatcher())); assertEquals(two, mQueue.poll()); } /** * Test {@link ConditionPriorityBlockingQueue#take(IMatcher)} when multiple threads are waiting */ public void testTake_multiple_matchers() throws InterruptedException { final Integer one = new Integer(1); final Integer second_one = new Integer(1); Thread waiter = new Thread() { @Override public void run() { try { mQueue.take(new OneMatcher()); } catch (InterruptedException e) { } } }; waiter.start(); Thread waiter2 = new Thread() { @Override public void run() { try { mQueue.take(new OneMatcher()); } catch (InterruptedException e) { } } }; waiter2.start(); Thread delayedAdd = new Thread() { @Override public void run() { try { sleep(200); } catch (InterruptedException e) { } mQueue.add(one); } }; delayedAdd.start(); Thread delayedAdd2 = new Thread() { @Override public void run() { try { sleep(300); } catch (InterruptedException e) { } mQueue.add(second_one); } }; delayedAdd2.start(); // wait for blocked threads to die. This test will deadlock if failed waiter.join(); waiter2.join(); assertNull(mQueue.poll()); } /** * Test {@link ConditionPriorityBlockingQueue#poll(IMatcher)} when queue is empty. */ public void testPoll_condition_empty() { assertNull(mQueue.poll(new OneMatcher())); } /** * Test {@link ConditionPriorityBlockingQueue#poll(long, TimeUnit, IMatcher)} when queue is * empty. */ public void testPoll_time_empty() throws InterruptedException { assertNull(mQueue.poll(100, TimeUnit.MILLISECONDS, new OneMatcher())); } /** * Test {@link ConditionPriorityBlockingQueue#poll(IMatcher)} when object matches, and one * doesn't. */ public void testPoll_condition() { Integer one = new Integer(1); Integer two = new Integer(2); mQueue.add(one); mQueue.add(two); assertEquals(one, mQueue.poll(new OneMatcher())); assertNull(mQueue.poll(new OneMatcher())); } /** * Test {@link ConditionPriorityBlockingQueue#poll(long, TimeUnit, IMatcher)} when object * matches, and one doesn't. */ public void testPoll_time_condition() throws InterruptedException { Integer one = new Integer(1); Integer two = new Integer(2); mQueue.add(one); mQueue.add(two); assertEquals(one, mQueue.poll(100, TimeUnit.MILLISECONDS, new OneMatcher())); assertNull(mQueue.poll(100, TimeUnit.MILLISECONDS, new OneMatcher())); } /** * Test {@link ConditionPriorityBlockingQueue#poll(IMatcher)} when object matches, and one * doesn't, using FIFO ordering. */ public void testPoll_fifo_condition() { ConditionPriorityBlockingQueue<Integer> fifoQueue = new ConditionPriorityBlockingQueue<Integer>(); Integer one = new Integer(1); Integer two = new Integer(2); fifoQueue.add(two); fifoQueue.add(one); assertEquals(one, fifoQueue.poll(new OneMatcher())); assertNull(fifoQueue.poll(new OneMatcher())); } /** * Same as {@link ConditionPriorityBlockingQueueTest#testPoll_condition()}, but objects are * added in inverse order. */ public void testPoll_condition_reverse() { Integer one = new Integer(1); Integer two = new Integer(2); mQueue.add(two); mQueue.add(one); assertEquals(one, mQueue.poll(new OneMatcher())); assertNull(mQueue.poll(new OneMatcher())); } /** * Test behavior when queue is modified during iteration * @throws Throwable */ public void testModificationOnIterating() throws Throwable { final ConditionPriorityBlockingQueue<Integer> queue = new ConditionPriorityBlockingQueue<Integer>(new IntCompare()); for (int i = 0; i < 10; i++) { queue.add(i); } final Throwable[] throwables = new Throwable[1]; Thread iterator = new Thread() { @Override public void run() { try { for (@SuppressWarnings("unused") Integer i : queue) { Thread.sleep(10); } } catch (Throwable t) { throwables[0] = t; } } }; iterator.start(); for (int i = 0; i < 10 && throwables[0] == null; i++) { queue.add(i); Thread.sleep(10); } if (throwables[0] != null) { throw throwables[0]; } } /** * A {@link Comparator} for {@link Integer} */ private static class IntCompare implements Comparator<Integer> { @Override public int compare(Integer o1, Integer o2) { if (o1 == o2) { return 0; } else if (o1 < o2) { return -1; } else { return 1; } } } private static class OneMatcher implements IMatcher<Integer> { @Override public boolean matches(Integer element) { return element == 1; } } }