/* * Jajuk * Copyright (C) The Jajuk Team * http://jajuk.info * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package org.jajuk; import java.util.LinkedList; import java.util.List; import junit.framework.Assert; import org.jajuk.util.log.Log; /** * Helper class to test with many threads. * * Sample usage is as follows: * * <code> public void testMultipleThreads() throws Exception { ThreadTestHelper helper = new ThreadTestHelper(NUMBER_OF_THREADS, NUMBER_OF_TESTS); helper.executeTest(new ThreadTestHelper.TestRunnable() { @Override public void doEnd(int threadnum) throws Exception { // do stuff at the end ... } @Override public void run(int threadnum, int iter) { // do the actual threaded work ... } }); } </code> */ public class ThreadTestHelper { private final int threadCount; private final int testsPerThread; private boolean failed = false; private int executions[] = null; /** * Initialize the class with the number of tests that should be executed. * * @param threadCount The number of threads to start running in parallel. * @param testsPerThread The number of single test-executions that are done in each thread */ public ThreadTestHelper(int threadCount, int testsPerThread) { this.threadCount = threadCount; this.testsPerThread = testsPerThread; // Initialize array to allow to summarize afterwards executions = new int[threadCount]; } public void executeTest(TestRunnable run) throws Exception { Log.debug("Starting thread test"); List<Thread> threads = new LinkedList<Thread>(); // start all threads for (int i = 0; i < threadCount; i++) { Thread t = startThread(i, run); threads.add(t); } // wait for all threads for (int i = 0; i < threadCount; i++) { threads.get(i).join(); } // make sure the resulting number of executions is correct for (int i = 0; i < threadCount; i++) { // check if enough items were performed Assert.assertEquals("Thread " + i + " did not execute all iterations", testsPerThread, executions[i]); } // check that we did not fail in any thread, i.e. no exception occurred... Assert.assertFalse(failed); } /** * This method is executed to start one thread. The thread will execute the * provided runnable a number of times. * * @param threadnum * The number of this thread * @param run * The Runnable object that is used to perform the actual test * operation * * @return The thread that was started. * */ private Thread startThread(final int threadnum, final TestRunnable run) { Log.debug("Starting thread number: " + threadnum); Thread t1 = null; t1 = new Thread(new Runnable() { @Override public void run() { try { for (int iter = 0; iter < testsPerThread; iter++) { // Log.debug("Executing iteration " + iter + " in thread" + // Thread.currentThread().getName()); // call the actual testcode run.run(threadnum, iter); executions[threadnum]++; } // do end-work here, we don't do this in a finally as we log Exception // then anyway run.doEnd(threadnum); } catch (Throwable e) { Log.error(e); failed = true; } } }, "Thread " + threadnum); t1.start(); return t1; } /** * . */ public interface TestRunnable { /** * When an object implementing interface <code>Runnable</code> is used to * create a thread, starting the thread causes the object's <code>run</code> * method to be called in that separately executing thread. * <p> * The general contract of the method <code>run</code> is that it may take * any action whatsoever. * * @param threadnum The number of the thread executing this run() * @param iter The count of how many times this thread executed the method * @throws Exception the exception * @see java.lang.Thread#run() */ public abstract void run(int threadnum, int iter) throws Exception; /** * Perform any action that should be done at the end. * * This method should throw an Exception if any check fails at this point. * * @param threadnum * @throws Exception the exception */ void doEnd(int threadnum) throws Exception; } /** * Test dummy. * */ public void testDummy() { // small empty test to not fail if this class is executed as test case by // Hudson/Sonar } }