CVS: plexus-container/src/test/org/apache/plexus/util AbstractTestThread.java,NONE,1.1 SweeperPoolTest.java,NONE,1.1 TestThreadManager.java,NONE,1.1 ThreadSafeMapTest.java,NONE,1.1

Jason van Zyl <[email protected]> Tue, 5 Aug 2003 19:46:06 -0500
Newsgroups gmane.comp.java.plexus.devel
Message-ID <[email protected]>
Update of /cvsroot/plexus/plexus-container/src/test/org/apache/plexus/util
In directory hogshead.codehaus.org:/tmp/cvs-serv4255/src/test/org/apache/plexus/util

Added Files:
	AbstractTestThread.java SweeperPoolTest.java 
	TestThreadManager.java ThreadSafeMapTest.java 
Log Message:


--- NEW FILE: AbstractTestThread.java ---
package org.apache.plexus.util;

/**
 * A thread which is registered with a ThreadRegistry and notifies it when it has completed
 * running. Collects any errors and makes it available for analysis.  
 * 
 * <p>Created on 1/07/2003</p>
 *
 * @author <a href="mailto:[email protected]">Bert van Brakel</a>
 * @version $Revision: 1.1 $
 */
public abstract class AbstractTestThread implements Runnable
{
    //~ Instance fields ----------------------------------------------------------------------------
    private String name;

    public static final boolean DEBUG = true;

    private boolean isRunning = false;

    /** Error msg provided by implementing class (of why the test failed) */
    private String errorMsg = null;

    /** The registry to notify on completion */
    private TestThreadManager registry;

    /** The error thrown when running the test. Not neccesarily a test failuer as some tests
     * may test for an exception */
    private Throwable error;

    /** If the thread has been run */
    private boolean hasRun = false;

    /** Flag indicating if the test has passed. Some test might require an 
     * exception so using the error to determine if the test has passed is
     * not sufficient.
     */
    private boolean passed = false;

    //~ Constructors -------------------------------------------------------------------------------

    /**
     * Constructor
     * 
     * <p>Remember to call <code>setThreadRegistry(ThreadRegistry)</code>
     */
    public AbstractTestThread()
    {
        super();
    }

    public AbstractTestThread(TestThreadManager registry)
    {
        super();
        setThreadRegistry(registry);
    }

    //~ Methods ------------------------------------------------------------------------------------

    /** 
     * @return
     */
    public Throwable getError()
    {
        return error;
    }

	/** 
	 * Resets the test back to it's state before starting. If the test
	 * is currently running this method will block until the test has
	 * finished running. Subclasses should call this method if
	 * overriding it.
	 * 
	 * */
    public void reset()
    {
    	//shouldn't reset until the test has finished running
        synchronized (this)
        {
            while (isRunning)
             {
                 try
                 {
                     wait();
                 }
                 catch (InterruptedException e)
                 {
            
                 }            
             }
            errorMsg = null;
            error = null;
            hasRun = false;
            passed = false;
        }
    }

	/**
	 * Start this TestThread running. If the test is currently running then
	 * this method does nothing.
	 *
	 */
    public final void start()
    {
    	//shouldn't have multiple threads running this test at the same time
        synchronized (this)
        {
            if (isRunning == false)
            {
                isRunning = true;
                Thread t = new Thread(this);
                t.start();
            }
        }
    }

    /** 
     * @return
     */
    public String getErrorMsg()
    {
        return errorMsg;
    }

    /** 
     * @return
     */
    public boolean hasFailed()
    {
        return !passed;
    }

    /** 
     * DOCUMENT ME!
     *
     * @return DOCUMENT ME!
     */
    public boolean hasPassed()
    {
        return passed;
    }

    /** 
     * Don't override this. Calls <code>doRun()</code>
     *
     * @see java.lang.Runnable#run()
     */
    public final void run()
    {
        if (registry == null)
        {
            throw new IllegalArgumentException("The ThreadRegistry is null. Ensure this is set before running this thread");
        }
        passed = false;
        try
        {
            doRun();
        }
        catch (Throwable t)
        {
            error = t;
        }

        registry.completed(this);
        hasRun = true;
        isRunning = false;
        //notify objects with blocked methods which are waiting
        //on this test to complete running
        synchronized( this)
        {
        	notifyAll();
        }
    }

    /** 
     * Override this to run your custom test
     *
     * @throws Throwable
     */
    public abstract void doRun() throws Throwable;

    /** 
     * Set the registry this thread should notify when it has completed running
     *
     * @param registry
     */
    public void setThreadRegistry(TestThreadManager registry)
    
    {
        this.registry = registry;
    }

    /** 
     * Test if the test has run
     *
     * @return
     */
    public boolean hasRun()
    {
        return hasRun;
    }
    /**
     * @param throwable
     */
    public void setError(Throwable throwable)
    {
        error = throwable;
    }

    /**
     * @param string
     */
    public void setErrorMsg(String string)
    {
        errorMsg = string;
    }

    /**
     * @param b
     */
    public void setPassed(boolean b)
    {
        passed = b;
    }
    /**
     * @return
     */
    public String getName()
    {
        return name;
    }

    /**
     * @param string
     */
    public void setName(String string)
    {
        name = string;
    }

    private final void debug(String msg)
    {
        if (DEBUG)
        {
            System.out.println(this +":" + msg);
        }
    }
}

--- NEW FILE: SweeperPoolTest.java ---
package org.apache.plexus.util;

import java.util.Vector;

import junit.framework.TestCase;

/**
 * Created on 21/06/2003
 * 
 * @author <a href="mailto:[email protected]">Bert van Brakel</a>
 * @version $Revision: 1.1 $
 */
public class SweeperPoolTest extends TestCase {
	/** The pool under test */
	TestObjectPool pool;
	/** A bunch of object to pool */
	Object o1;
	Object o2;
	Object o3;
	Object o4;
	Object o5;
	Object o6;
	/**
	 * Constructor
	 * 
	 * 
	 */
	public SweeperPoolTest() {
		super();
	}

	/**
	 * Constructor
	 * 
	 * @param arg0
	 */
	public SweeperPoolTest(String arg0) {
		super(arg0);
	}

	/**
	 * Test the pool limits it's size, and disposes unneeded objects correctly
	 *
	 */
	public void testMaxSize() {
		int sweepInterval = 0;
		int initialCapacity = 5;
		int maxSize = 2;
		int minSize = 1;
		int triggerSize = 2;

		pool =
			new TestObjectPool(
				maxSize,
				minSize,
				initialCapacity,
				sweepInterval,
				triggerSize);

		Object tmp = pool.get();
		assertNull("Expected object from pool to be null", tmp);
		pool.put(o1);
		assertEquals("Expected pool to contain 1 object", 1, pool.getSize());
		tmp = pool.get();
		assertSame(
			"Expected returned pool object to be the same as the one put in",
			tmp,
			o1);
		pool.put(o1);
		pool.put(o2);
		assertEquals("Expected pool to contain 2 objects", 2, pool.getSize());
		pool.put(o3);
		assertEquals(
			"Expected pool to contain only a maximuim of 2 objects.",
			2,
			pool.getSize());
		assertEquals(
			"Expected 1 disposed pool object",
			1,
			pool.testGetDisposedObjects().size());
		tmp = pool.testGetDisposedObjects().iterator().next();

		tmp = pool.get();
		assertEquals(
			"Expected pool size to be 1 after removing one object",
			1,
			pool.getSize());
		Object tmp2 = pool.get();
		assertEquals(
			"Expected pool size to be 0 after removing 2 objects",
			0,
			pool.getSize());
		assertNotSame("Expected returned objects to be differnet", tmp, tmp2);

	}

	public void testSweepAndTrim1() {
		//test trigger 
		int sweepInterval = 1;
		int initialCapacity = 5;
		int maxSize = 5;
		int minSize = 1;
		int triggerSize = 2;

		pool =
			new TestObjectPool(
				maxSize,
				minSize,
				initialCapacity,
				sweepInterval,
				triggerSize);
		pool.put(o1);
		pool.put(o2);
		pool.put(o3);
		pool.put(o4);
		//give the seeper some time to run
		synchronized (this) {
			try {
				wait(2 * 1000);
			} catch (InterruptedException e) {
				fail(
					"Unexpected exception thrown. e="
						+ Tracer.traceToString(e));
			}
		}
		assertEquals(
			"Expected pool to only contain 1 object",
			1,
			pool.getSize());
		assertEquals(
			"Expected 3 diposed objects",
			3,
			pool.testGetDisposedObjects().size());

	}

	/**
	 * @see junit.framework.TestCase#setUp()
	 */
	protected void setUp() throws Exception {

		o1 = new Object();
		o2 = new Object();
		o3 = new Object();
		o4 = new Object();
		o5 = new Object();
		o6 = new Object();
		super.setUp();
	}

	/**
	 * @see junit.framework.TestCase#tearDown()
	 */
	protected void tearDown() throws Exception {
		pool.dispose();
		pool = null;
		super.tearDown();

	}

	class TestObjectPool extends SweeperPool {
		private Vector disposedObjects = new Vector();

		public TestObjectPool(
			int maxSize,
			int minSize,
			int intialCapacity,
			int sweepInterval,
			int triggerSize) {
			super(maxSize, minSize, intialCapacity, sweepInterval, triggerSize);
		}

		public void reset() {
			disposedObjects.clear();
		}

		/**
		 * @see nz.co.bonzo.beans.castor.pool.ObjectPool#objectDisposed(java.lang.Object)
		 */
		public void objectDisposed(Object obj) {
			disposedObjects.add(obj);
		}

		public Vector testGetDisposedObjects() {
			return disposedObjects;
		}

	}

}

--- NEW FILE: TestThreadManager.java ---
package org.apache.plexus.util;

import java.util.Collection;
import java.util.Iterator;
import java.util.Vector;
import java.util.logging.Logger;
/**
 * Manages  a number of test threads, which notify this instance when they have 
 * completed. Allows TestCases to easily start and manage multiple test threads.  
 * 
 * <p>Created on 9/06/2003</p>
 *
 * @author <a href="mailto:[email protected]">Bert van Brakel</a>
 * @version $Revision: 1.1 $
 * 
 */
public class TestThreadManager
{
	//~ Instance fields ----------------------------------------------------------------------------

	/** Test threads which have completed running */
	private Collection runThreads = new Vector();
	/** Test threads still needing to be run, or are currently running*/
	private Collection toRunThreads = new Vector();
	private Logger logger = null;
	/** Any test threads which failed */
	private Vector failedThreads = new Vector();

	/**The object to notify when all the test threads have complleted. Clients use this
	 * to lock on (wait) while waiting for the tests to complete*/
	private Object notify = null;

	//~ Constructors -------------------------------------------------------------------------------

	public TestThreadManager(Object notify)
	{
		super();
		this.notify = notify;
	}

	//~ Methods ------------------------------------------------------------------------------------

	/** 
	 * @return
	 */
	public Collection getRunThreads()
	{
		return runThreads;
	}

	public void runTestThreads()
	{
		failedThreads.clear();
		//use an array as the tests may run very quickly 
		//and modify the toRunThreads vector and hence
		//cause a Concurrent ModificationException on an
		//iterator
		Object[] threads = toRunThreads.toArray();
		for (int i = 0; i < threads.length; i++)
		{
			//System.out.println("Starting thread " + i +" ..." );
			((AbstractTestThread) threads[i]).start();
		}
	}

	public Collection getFailedTests()
	{       
		return failedThreads;
	}


	public boolean hasFailedThreads()
	{
		if( failedThreads.size() == 0)
		{
			return false;
		}
		else
			return true;
	}
	/** 
	 * Determine if any threads are still running!
	 *
	 * @return DOCUMENT ME!
	 */
	public boolean isStillRunningThreads()
	{
		return !toRunThreads.isEmpty();
	}

	/** 
	 * @return
	 */
	public Collection getToRunThreads()
	{
		return toRunThreads;
	}

	/** 
	 * DOCUMENT ME!
	 */
	public void clear()
	{
		toRunThreads.clear();
		runThreads.clear();
		failedThreads.clear();
	}

	/* (non-Javadoc)
	* @see java.util.Collection#remove(java.lang.Object)
	*/
	public void completed(AbstractTestThread thread)
	{
		toRunThreads.remove(thread);
		runThreads.add(thread);
		if (thread.hasFailed())
		{
			failedThreads.add(thread);
		}
		//wakeup thread which is waiting for the threads to complete
		//execution
		if (toRunThreads.isEmpty())
		{
			synchronized (notify)
			{
				notify.notify();
			}
		}
	}

	/** 
	 * Overide this to add your own stuff. Called after 
	 * <code>registerThread(Object)</code>
	 *
	 * @param thread DOCUMENT ME!
	 */
	public void doRegisterThread(AbstractTestThread thread)
	{
	}

	public final void registerThread(AbstractTestThread thread)
	{
		thread.setThreadRegistry( this );
		if( toRunThreads.contains( thread ) == false )
		{    	
			toRunThreads.add(thread);
			doRegisterThread(thread);
		}
        
	}

	/** 
	 * Put all the runThreads back in the que to be run again and
	 * clear the failedTest collection
	 */
	public void reset()
	{
		toRunThreads.clear();
		Iterator iter = runThreads.iterator();		
		while (iter.hasNext())
		{
			AbstractTestThread test = (AbstractTestThread) iter.next();
			test.reset();
			registerThread( test );
		}

		runThreads.clear();
		failedThreads.clear();
	}
}

--- NEW FILE: ThreadSafeMapTest.java ---
package org.apache.plexus.util;

import java.util.Iterator;

import junit.framework.TestCase;

/**
 * Created on 21/06/2003
 * 
 * @author <a href="mailto:[email protected]">Bert van Brakel</a>
 * @version $Revision: 1.1 $
 */
public class ThreadSafeMapTest extends TestCase
{
    private ThreadSafeMap map;

    /**
     * Constructor
     * 
     * 
     */
    public ThreadSafeMapTest()
    {
        super();
    }

    /**
     * Constructor
     * 
     * @param arg0
     */
    public ThreadSafeMapTest(String name)
    {
        super(name);
    }

    /**
     * @see junit.framework.TestCase#setUp()
     */
    protected void setUp() throws Exception
    {
        // TODO Auto-generated method stub
        super.setUp();
    }

    /**
     * @see junit.framework.TestCase#tearDown()
     */
    protected void tearDown() throws Exception
    {
        // TODO Auto-generated method stub
        super.tearDown();
    }

    public void test()
    {
        map = new ThreadSafeMap();

        TestThreadManager registry = new TestThreadManager(this);
        //make the readers and writers.
        //mix them up incase VM gives startup times
        //dependent on thread creation order
        for (int i = 0; i < 20; i++)
        {
            //use the same key, but different values, so there will be contention
            Object key = Integer.toString(i);
            //a writer
            TestMapThread wTest = new TestMapThread(map, Integer.toString(i), new Object(), false);
            registry.registerThread(wTest);
            //a reader
            TestMapThread rTest = new TestMapThread(map, key, new Object(), true);
            registry.registerThread(rTest);
        }
        //now run the threads
        registry.runTestThreads();

        //now wait for the threads to finish..
        synchronized (this)
        {
            try
            {
                wait();
            }
            catch (InterruptedException e)
            {
                //all threads have finished
            }
        }
        //now test for failures...
        if (registry.hasFailedThreads())
        {
            StringBuffer out = new StringBuffer();
            Iterator iter = registry.getFailedTests().iterator();
            String nl = System.getProperty("line.separator");
            while (iter.hasNext())
            {
                TestMapThread test = (TestMapThread) iter.next();
                out.append(nl);
                out.append(test.getErrorMsg());
                out.append(" Exception=" + Tracer.traceToString(test.getError()));
            }
            fail("Failed test threads: " + out);
        }
    }
}

class TestMapThread extends AbstractTestThread
{
    private ThreadSafeMap map;
    /** Indicates whether to read or write to the map */
    private boolean reader = true;

    private Object key;
    private Object value;
    /**
     * Constructor
     * 
     * 
     */
    public TestMapThread(ThreadSafeMap map, Object key, Object value, boolean reader)
    {
        super();
        this.map = map;
        this.key = key;
        this.value = value;
        this.reader = reader;
    }

    /**
    * @see java.lang.Runnable#run()
    */
    public void doRun()
    {
        try
        {

            if (reader)
            {
                value = map.get(key);
            }
            else
            {
                map.put(key, value);
            }
            setPassed(true);
        }
        catch (Throwable t)
        {
            if (reader)
            {
                setErrorMsg("Reader failed ");
            }
            else
            {
                setErrorMsg("Writer failed");
            }
            setError(t);
        }
    }

    /**
     * @return
     */
    public Object getKey()
    {
        return key;
    }

    /**
     * @return
     */
    public boolean isReader()
    {
        return reader;
    }

    /**
     * @return
     */
    public Object getValue()
    {
        return value;
    }

}