/******************************************************************************* * Copyright (c) 2010-present Sonatype, Inc. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Stuart McCulloch (Sonatype, Inc.) - initial API and implementation *******************************************************************************/ package org.eclipse.sisu.inject; import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; import java.util.NoSuchElementException; import java.util.Random; import java.util.concurrent.atomic.AtomicBoolean; import junit.framework.TestCase; public class RankedSequenceTest extends TestCase { static final AtomicBoolean active = new AtomicBoolean( true ); static final List<Throwable> errors = Collections.synchronizedList( new ArrayList<Throwable>() ); static final Random random = new Random( System.currentTimeMillis() ); static final int CONCURRENCY = 8; static final RankedSequence<Integer> rankedList = new RankedSequence<Integer>(); public void testOrdering() { final RankedSequence<String> list = new RankedSequence<String>(); list.insert( "A1", Integer.MAX_VALUE ); list.insert( "F", Integer.MIN_VALUE + 1 ); list.insert( "E", -1 ); list.insert( "G1", Integer.MIN_VALUE ); list.insert( "D", 0 ); list.insert( "B", Integer.MAX_VALUE - 1 ); list.insert( "C", 1 ); list.insert( "G2", Integer.MIN_VALUE ); list.insert( "D", 0 ); list.insert( "A2", Integer.MAX_VALUE ); assertEquals( 10, list.size() ); Iterator<String> itr = list.iterator(); assertTrue( itr.hasNext() ); assertTrue( itr.hasNext() ); assertTrue( itr.hasNext() ); assertEquals( "A1", itr.next() ); assertTrue( itr.hasNext() ); assertEquals( "A2", itr.next() ); assertTrue( itr.hasNext() ); assertEquals( "B", itr.next() ); assertTrue( itr.hasNext() ); assertEquals( "C", itr.next() ); assertTrue( itr.hasNext() ); assertEquals( "D", itr.next() ); assertTrue( itr.hasNext() ); assertEquals( "D", itr.next() ); assertTrue( itr.hasNext() ); assertEquals( "E", itr.next() ); assertTrue( itr.hasNext() ); assertEquals( "F", itr.next() ); assertTrue( itr.hasNext() ); assertEquals( "G1", itr.next() ); assertTrue( itr.hasNext() ); assertEquals( "G2", itr.next() ); assertFalse( itr.hasNext() ); assertFalse( itr.hasNext() ); assertFalse( itr.hasNext() ); itr = list.iterator(); assertEquals( "A1", itr.next() ); assertEquals( "A2", itr.next() ); assertEquals( "B", itr.next() ); assertEquals( "C", itr.next() ); assertEquals( "D", itr.next() ); assertEquals( "D", itr.next() ); assertEquals( "E", itr.next() ); assertEquals( "F", itr.next() ); assertEquals( "G1", itr.next() ); assertEquals( "G2", itr.next() ); itr = list.iterator(); list.remove( "D" ); list.insert( "D-", 0 ); list.remove( "D" ); assertEquals( "A1", itr.next() ); assertEquals( "A2", itr.next() ); assertTrue( itr.hasNext() ); list.remove( "B" ); list.insert( "512", 512 ); assertEquals( "B", itr.next() ); assertTrue( itr.hasNext() ); list.insert( "1024", 1024 ); assertEquals( "512", itr.next() ); assertEquals( "C", itr.next() ); assertEquals( "D-", itr.next() ); assertEquals( "E", itr.next() ); assertEquals( "F", itr.next() ); assertEquals( "G1", itr.next() ); assertEquals( "G2", itr.next() ); } public void testEmptyList() { final Iterator<Object> itr = new RankedSequence<Object>().iterator(); assertFalse( itr.hasNext() ); try { itr.next(); fail( "Expected NoSuchElementException" ); } catch ( final NoSuchElementException e ) { // expected } try { itr.remove(); fail( "Expected UnsupportedOperationException" ); } catch ( final UnsupportedOperationException e ) { // expected } } public void testConcurrentIteration() { final Thread[] threads = new Thread[3 * CONCURRENCY]; int i = 0; while ( i < threads.length ) { threads[i++] = new Thread( new Push() ); threads[i++] = new Thread( new Pull() ); threads[i++] = new Thread( new Read() ); } for ( final Thread t : threads ) { t.start(); } try { Thread.sleep( 10 * 1000 ); } catch ( final InterruptedException e ) { // ignore } active.set( false ); for ( final Thread t : threads ) { try { t.join(); } catch ( final InterruptedException e ) { // ignore } } for ( final Throwable e : errors ) { e.printStackTrace(); } if ( errors.size() > 0 ) { fail( "Unexpected errors!" ); } } static class Push implements Runnable { public void run() { while ( active.get() ) { Thread.yield(); if ( rankedList.size() < 64 * 1024 ) { final int rank = random.nextInt(); rankedList.insert( Integer.valueOf( rank ), rank ); } Thread.yield(); } } } static class Pull implements Runnable { public void run() { try { while ( active.get() || !rankedList.isEmpty() ) { Thread.yield(); Integer element = Integer.valueOf( 0 ); final Iterator<Integer> itr = rankedList.iterator(); for ( int i = 0; i < random.nextInt( rankedList.size() + 1 ) && itr.hasNext(); i++ ) { element = itr.next(); } rankedList.remove( element ); Thread.yield(); } } catch ( final Throwable e ) { errors.add( e ); } } } static class Read implements Runnable { public void run() { try { while ( active.get() ) { Thread.yield(); int lastRank = Integer.MAX_VALUE; final Iterator<Integer> itr = rankedList.iterator(); while ( itr.hasNext() ) { Thread.yield(); final int rank = itr.next().intValue(); Thread.yield(); assertTrue( "Rank should descend during iteration", lastRank >= rank ); lastRank = rank; } Thread.yield(); } } catch ( final Throwable e ) { errors.add( e ); } } } }