blob: a4e56e36a73aab7e022b1eba638aa40cdf7cd193 [file] [log] [blame]
/* $Id$
*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.etch.util;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
/** Test of CircularQueue */
public class TestCircularQueue
{
// change this to a much smaller number (e.g., 0.1) to actually test the
// circular queue timeout accuracy.
private final static double REL_ERROR = 100000;
/** @throws Exception */
@Test
public void testgettimes() throws Exception
{
@SuppressWarnings("unused")
double[] times;
times = gettimes();
// System.out.printf( "times: hp %f / tod %f\n", times[0], times[1] );
times = gettimes();
// System.out.printf( "times: hp %f / tod %f\n", times[0], times[1] );
times = gettimes();
// System.out.printf( "times: hp %f / tod %f\n", times[0], times[1] );
}
private double[] gettimes() throws InterruptedException
{
long t0 = Timer.getNanos();
long s0 = System.currentTimeMillis();
Thread.sleep( 1000 );
double t = Timer.getSecondsSince( t0 );
double s = (System.currentTimeMillis() - s0) / 1000.0;
return new double[] { t, s };
}
/** @throws Exception */
@Test( expected = IllegalArgumentException.class )
public void construct1() throws Exception
{
new CircularQueue<Integer>( 0 );
}
/** @throws Exception */
@Test( expected = IllegalArgumentException.class )
public void construct2() throws Exception
{
new CircularQueue<Integer>( -1 );
}
/** @throws Exception */
@Test
public void construct3() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>( 1 );
assertEquals( 1, queue.size() );
}
/** @throws Exception */
@Test
public void construct4() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>( 2 );
assertEquals( 2, queue.size() );
}
/** @throws Exception */
@Test
public void construct5() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>();
assertEquals( 10, queue.size() );
}
/** @throws Exception */
@Test
public void putget1() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>();
assertEquals( 0, queue.count() );
assertTrue( queue.isEmpty() );
assertFalse( queue.isFull() );
for (int i = 0; i < 10000; i++)
{
queue.put( i );
assertEquals( 1, queue.count() );
assertFalse( queue.isEmpty() );
assertEquals( i, queue.get() );
assertEquals( 0, queue.count() );
assertTrue( queue.isEmpty() );
}
}
/** @throws Exception */
@Test
public void putget2() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>();
assertNull( queue.get( 1 ) );
assertNull( queue.get( -1 ) );
assertNull( queue.get( 10 ) );
queue.put( 9 );
assertEquals( 9, queue.get( 1 ) );
queue.put( 9 );
assertEquals( 9, queue.get( -1 ) );
queue.put( 9 );
assertEquals( 9, queue.get( 10 ) );
assertNull( queue.get( 1 ) );
assertNull( queue.get( -1 ) );
assertNull( queue.get( 10 ) );
long t0;
double t;
t0 = System.nanoTime();
assertNull( queue.get( 10 ) );
t = (System.nanoTime() - t0) / 1000000.0;
// TODO is there a better way for choosing relative error?
TestHelp.assertRelError( 10, t, REL_ERROR );
t0 = System.nanoTime();
assertNull( queue.get( 20 ) );
t = (System.nanoTime() - t0) / 1000000.0;
// TODO is there a better way for choosing relative error?
TestHelp.assertRelError( 20, t, REL_ERROR );
t0 = System.nanoTime();
assertNull( queue.get( 30 ) );
t = (System.nanoTime() - t0) / 1000000.0;
// TODO is there a better way for choosing relative error?
TestHelp.assertRelError( 30, t, REL_ERROR );
}
/** @throws Exception */
@Test
public void putget3() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>();
assertTrue( queue.put( 1 ) );
assertTrue( queue.put( 2 ) );
assertTrue( queue.put( 3 ) );
assertEquals( 1, queue.get() );
assertEquals( 2, queue.get() );
assertEquals( 3, queue.get() );
assertTrue( queue.put( 1, 0 ) );
assertTrue( queue.put( 2, 0 ) );
assertTrue( queue.put( 3, 0 ) );
assertEquals( 1, queue.get( 0 ) );
assertEquals( 2, queue.get( 0 ) );
assertEquals( 3, queue.get( 0 ) );
assertTrue( queue.put( 1, -1 ) );
assertTrue( queue.put( 2, -1 ) );
assertTrue( queue.put( 3, -1 ) );
assertEquals( 1, queue.get( -1 ) );
assertEquals( 2, queue.get( -1 ) );
assertEquals( 3, queue.get( -1 ) );
assertTrue( queue.put( 1, 1 ) );
assertTrue( queue.put( 2, 1 ) );
assertTrue( queue.put( 3, 1 ) );
assertEquals( 1, queue.get( 1 ) );
assertEquals( 2, queue.get( 1 ) );
assertEquals( 3, queue.get( 1 ) );
}
/** @throws Exception */
@Test
public void get() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>( 1 );
System.nanoTime();
assertNull( queue.get( 10 ) );
TestHelp.assertRelError( 1, 1, 1 );
for (int i: new int[] { 10, 20, 30, 50, 80, 130, 210, 340, 550, 890, 1440 } )
{
// System.out.printf( "get delay = %d\n", i );
long t0 = System.nanoTime();
assertNull( queue.get( i ) );
double t = (System.nanoTime() - t0) / 1000000.0;
// TODO is there a better way for choosing relative error?
TestHelp.assertRelError( i, t, REL_ERROR );
}
}
/** @throws Exception */
@Test( expected = NullPointerException.class )
public void put() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>();
queue.put( null );
}
/** @throws Exception */
@Test
public void full() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>( 1 );
assertFalse( queue.isFull() );
assertTrue( queue.put( 0 ) );
assertTrue( queue.isFull() );
assertEquals( 0, queue.get() );
assertFalse( queue.isFull() );
assertTrue( queue.put( 0 ) );
assertFalse( queue.put( 0, -1 ) );
assertFalse( queue.put( 0, 1 ) );
assertFalse( queue.put( 0, 10 ) );
}
/** @throws Exception */
@Test
public void close1() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>();
assertFalse( queue.isClosed() );
queue.close();
assertTrue( queue.isClosed() );
assertNull( queue.get() );
assertNull( queue.get( -1 ) );
assertNull( queue.get( 0 ) );
assertNull( queue.get( 1 ) );
queue.close();
assertTrue( queue.isClosed() );
}
/** @throws Exception */
@Test
public void close2() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>();
assertFalse( queue.isClosed() );
queue.close();
assertTrue( queue.isClosed() );
assertFalse( queue.put( 0 ) );
assertFalse( queue.put( 0, -1 ) );
assertFalse( queue.put( 0, 0 ) );
assertFalse( queue.put( 0, 1 ) );
queue.close();
assertTrue( queue.isClosed() );
}
/** @throws Exception */
@Test
public void close3() throws Exception
{
CircularQueue<Integer> queue = new CircularQueue<Integer>();
queue.put( 1 );
queue.put( 2 );
queue.put( 3 );
queue.close();
assertEquals( 1, queue.get() );
assertEquals( 2, queue.get() );
assertEquals( 3, queue.get() );
assertNull( queue.get() );
}
/** @throws Exception */
@Test
public void delay1() throws Exception
{
final CircularQueue<Integer> queue = new CircularQueue<Integer>();
TestHelp.delayAndStart( 10, new TestHelp.DelayRunnable() { public void run() throws Exception { queue.put( 99 ); } } );
assertEquals( 99, queue.get() );
TestHelp.delayAndStart( 10, new TestHelp.DelayRunnable() { public void run() throws Exception { queue.close(); } } );
assertNull( queue.get() );
}
/** @throws Exception */
@Test
public void delay2() throws Exception
{
final CircularQueue<Integer> queue = new CircularQueue<Integer>( 1 );
assertTrue( queue.put( 1 ) );
TestHelp.delayAndStart( 10, new TestHelp.DelayRunnable() { public void run() throws Exception { assertEquals( 1, queue.get() ); } } );
assertTrue( queue.put( 2 ) );
assertEquals( 2, queue.get() );
assertTrue( queue.put( 1 ) );
TestHelp.delayAndStart( 10, new TestHelp.DelayRunnable() { public void run() throws Exception { queue.close(); } } );
assertFalse( queue.put( 2 ) );
assertEquals( 1, queue.get() );
assertNull( queue.get() );
}
/** @throws Exception */
@Test
public void stress1() throws Exception
{
final CircularQueue<Integer> queue = new CircularQueue<Integer>( 1 );
final int n = 10000;
new Thread( new Runnable()
{
public void run()
{
for (int i = 0; i < n; i++)
{
try
{
queue.put( i );
}
catch ( InterruptedException e )
{
///CLOVER:OFF
e.printStackTrace();
///CLOVER:ON
}
}
}
} ).start();
for (int i = 0; i < n; i++)
assertEquals( i, queue.get() );
}
/** @throws Exception */
@Test
public void stress2() throws Exception
{
final CircularQueue<Integer> queue = new CircularQueue<Integer>( 1 );
final int n = 1000;
new Thread( new Runnable()
{
public void run()
{
for (int i = 0; i < n; i++)
{
try
{
Thread.sleep( 5 );
queue.put( i );
}
catch ( InterruptedException e )
{
///CLOVER:OFF
e.printStackTrace();
///CLOVER:ON
}
}
}
} ).start();
for (int i = 0; i < n; i++)
assertEquals( i, queue.get() );
}
/** @throws Exception */
@Test
public void stress3() throws Exception
{
final CircularQueue<Integer> queue = new CircularQueue<Integer>( 1 );
final int n = 1000;
new Thread( new Runnable()
{
public void run()
{
for (int i = 0; i < n; i++)
{
try
{
queue.put( i );
}
catch ( InterruptedException e )
{
///CLOVER:OFF
e.printStackTrace();
///CLOVER:ON
}
}
}
} ).start();
for (int i = 0; i < n; i++)
{
Thread.sleep( 5 );
assertEquals( i, queue.get() );
}
}
/** @throws Exception */
@Test
public void stress4() throws Exception
{
final CircularQueue<Integer> queue = new CircularQueue<Integer>();
// we will setup two threads waiting to get and
// then in a single synchronized step put two
// items in the queue. the first thread will be
// woken to get, and once done the second thread
// should be woken by the first.
Thread t1 = new Thread( new Runnable()
{
public void run()
{
try
{
assertNotNull( queue.get() );
}
catch ( InterruptedException e )
{
// e.printStackTrace();
}
}
}, "t1" );
Thread t2 = new Thread( new Runnable()
{
public void run()
{
try
{
assertNotNull( queue.get() );
}
catch ( InterruptedException e )
{
// e.printStackTrace();
}
}
}, "t2" );
t1.start();
t2.start();
// wait until both threads are waiting on queue...
Thread.sleep( 100 );
synchronized (queue)
{
queue.put( 1 );
queue.put( 2 );
}
harvest( t1 );
harvest( t2 );
}
/** @throws Exception */
@Test
public void stress5() throws Exception
{
final CircularQueue<Integer> queue = new CircularQueue<Integer>( 3 );
// we will setup two threads waiting to put to the queue,
// then in a single synchronized step, read two items from
// the queue. the first thread will be woken to put, and
// once done the second thread should be woken by the first.
queue.put( 0 );
queue.put( 1 );
queue.put( 2 );
assertTrue( queue.isFull() );
Thread t1 = new Thread( new Runnable()
{
public void run()
{
try
{
assertTrue( queue.put( 3 ) );
}
catch ( InterruptedException e )
{
// e.printStackTrace();
}
}
}, "t1" );
Thread t2 = new Thread( new Runnable()
{
public void run()
{
try
{
assertTrue( queue.put( 4 ) );
}
catch ( InterruptedException e )
{
// e.printStackTrace();
}
}
}, "t2" );
t1.start();
t2.start();
// wait until both threads are waiting on queue...
Thread.sleep( 100 );
synchronized (queue)
{
assertNotNull( queue.get() );
assertNotNull( queue.get() );
}
harvest( t1 );
harvest( t2 );
}
/** @throws Exception */
@Test
public void stress6() throws Exception
{
final CircularQueue<Integer> queue = new CircularQueue<Integer>( 5 );
// start two getters and two putters and let 'em duke it out...
Thread t1 = new Thread( new Runnable()
{
public void run()
{
try
{
for (int i = 0; i < 100; i++)
assertTrue( queue.put( i ) );
}
catch ( InterruptedException e )
{
// e.printStackTrace();
}
}
}, "t1" );
Thread t2 = new Thread( new Runnable()
{
public void run()
{
try
{
for (int i = 0; i < 100; i++)
assertTrue( queue.put( i ) );
}
catch ( InterruptedException e )
{
// e.printStackTrace();
}
}
}, "t2" );
Thread t3 = new Thread( new Runnable()
{
public void run()
{
try
{
for (int i = 0; i < 100; i++)
assertNotNull( queue.get() );
}
catch ( InterruptedException e )
{
// e.printStackTrace();
}
}
}, "t3" );
Thread t4 = new Thread( new Runnable()
{
public void run()
{
try
{
for (int i = 0; i < 100; i++)
assertNotNull( queue.get() );
}
catch ( InterruptedException e )
{
// e.printStackTrace();
}
}
}, "t4" );
t1.start();
t2.start();
t3.start();
t4.start();
harvest( t1 );
harvest( t2 );
harvest( t3 );
harvest( t4 );
}
/** @throws Exception */
@Test
public void harvest1() throws Exception
{
Thread t = new Thread( new Runnable()
{
public void run()
{
// nothing to do.
}
}, "t" );
t.start();
harvest( t );
}
/** @throws Exception */
@Test( expected = TimeoutException.class )
public void harvest2() throws Exception
{
Thread t = new Thread( new Runnable()
{
public void run()
{
try
{
Thread.sleep( 10000 );
}
catch ( InterruptedException e )
{
// ignore.
}
}
}, "t" );
t.start();
harvest( t );
}
private void harvest( Thread t ) throws Exception
{
t.join( 1000 );
if (t.isAlive())
{
t.interrupt();
throw new TimeoutException( t.getName()+" is stuck" );
}
}
}