Source Code Cross Referenced for MultiThreadedTestRunner.java in  » Test-Coverage » GroboUtils » net » sourceforge » groboutils » junit » v1 » Java Source Code / Java DocumentationJava Source Code and Java Documentation

Java Source Code / Java Documentation
1. 6.0 JDK Core
2. 6.0 JDK Modules
3. 6.0 JDK Modules com.sun
4. 6.0 JDK Modules com.sun.java
5. 6.0 JDK Modules sun
6. 6.0 JDK Platform
7. Ajax
8. Apache Harmony Java SE
9. Aspect oriented
10. Authentication Authorization
11. Blogger System
12. Build
13. Byte Code
14. Cache
15. Chart
16. Chat
17. Code Analyzer
18. Collaboration
19. Content Management System
20. Database Client
21. Database DBMS
22. Database JDBC Connection Pool
23. Database ORM
24. Development
25. EJB Server geronimo
26. EJB Server GlassFish
27. EJB Server JBoss 4.2.1
28. EJB Server resin 3.1.5
29. ERP CRM Financial
30. ESB
31. Forum
32. GIS
33. Graphic Library
34. Groupware
35. HTML Parser
36. IDE
37. IDE Eclipse
38. IDE Netbeans
39. Installer
40. Internationalization Localization
41. Inversion of Control
42. Issue Tracking
43. J2EE
44. JBoss
45. JMS
46. JMX
47. Library
48. Mail Clients
49. Net
50. Parser
51. PDF
52. Portal
53. Profiler
54. Project Management
55. Report
56. RSS RDF
57. Rule Engine
58. Science
59. Scripting
60. Search Engine
61. Security
62. Sevlet Container
63. Source Control
64. Swing Library
65. Template Engine
66. Test Coverage
67. Testing
68. UML
69. Web Crawler
70. Web Framework
71. Web Mail
72. Web Server
73. Web Services
74. Web Services apache cxf 2.0.1
75. Web Services AXIS2
76. Wiki Engine
77. Workflow Engines
78. XML
79. XML UI
Java
Java Tutorial
Java Open Source
Jar File Download
Java Articles
Java Products
Java by API
Photoshop Tutorials
Maya Tutorials
Flash Tutorials
3ds-Max Tutorials
Illustrator Tutorials
GIMP Tutorials
C# / C Sharp
C# / CSharp Tutorial
C# / CSharp Open Source
ASP.Net
ASP.NET Tutorial
JavaScript DHTML
JavaScript Tutorial
JavaScript Reference
HTML / CSS
HTML CSS Reference
C / ANSI-C
C Tutorial
C++
C++ Tutorial
Ruby
PHP
Python
Python Tutorial
Python Open Source
SQL Server / T-SQL
SQL Server / T-SQL Tutorial
Oracle PL / SQL
Oracle PL/SQL Tutorial
PostgreSQL
SQL / MySQL
MySQL Tutorial
VB.Net
VB.Net Tutorial
Flash / Flex / ActionScript
VBA / Excel / Access / Word
XML
XML Tutorial
Microsoft Office PowerPoint 2007 Tutorial
Microsoft Office Excel 2007 Tutorial
Microsoft Office Word 2007 Tutorial
Java Source Code / Java Documentation » Test Coverage » GroboUtils » net.sourceforge.groboutils.junit.v1 
Source Cross Referenced  Class Diagram Java Document (Java Doc) 


001:        /*
002:         * @(#)MultiThreadedTestRunner.java
003:         *
004:         * The basics are taken from an article by Andy Schneider
005:         * andrew.schneider@javaworld.com
006:         * The article is "JUnit Best Practices"
007:         * http://www.javaworld.com/javaworld/jw-12-2000/jw-1221-junit_p.html
008:         *
009:         * Part of the GroboUtils package at:
010:         * http://groboutils.sourceforge.net
011:         *
012:         *  Permission is hereby granted, free of charge, to any person obtaining a
013:         *  copy of this software and associated documentation files (the "Software"),
014:         *  to deal in the Software without restriction, including without limitation
015:         *  the rights to use, copy, modify, merge, publish, distribute, sublicense,
016:         *  and/or sell copies of the Software, and to permit persons to whom the
017:         *  Software is furnished to do so, subject to the following conditions:
018:         *
019:         *  The above copyright notice and this permission notice shall be included in
020:         *  all copies or substantial portions of the Software.
021:         *
022:         *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
023:         *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
024:         *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
025:         *  THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
026:         *  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
027:         *  FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
028:         *  DEALINGS IN THE SOFTWARE.
029:         */
030:
031:        package net.sourceforge.groboutils.junit.v1;
032:
033:        import org.apache.log4j.Logger;
034:        import junit.framework.TestCase;
035:        import junit.framework.TestResult;
036:        import junit.framework.AssertionFailedError;
037:        import junit.framework.Assert;
038:
039:        import java.lang.reflect.Method;
040:
041:        /**
042:         * A framework which allows for an array of tests to be
043:         * run asynchronously.  TestCases should reference this class in a test
044:         * method.
045:         * <P>
046:         * <B>Update for July 9, 2003:</B> now, you can also register
047:         * <tt>TestRunner</tt> instances as monitors (request 771008); these run
048:         * parallel with the standard <tt>TestRunner</tt> instances, but they only quit
049:         * when all of the standard <tt>TestRunner</tt> instances end.
050:         * <P>
051:         * Fixed bugs 771000 and 771001: spawned threads are now Daemon threads,
052:         * and all "wild threads" (threads that just won't stop) are
053:         * <tt>Thread.stop()</tt>ed.
054:         * <P>
055:         * All these changes have made this class rather fragile, as there are
056:         * many threaded timing issues to deal with.  Expect future refactoring
057:         * with backwards compatibility.
058:         *
059:         * @author    Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
060:         * @since     Jan 14, 2002
061:         * @version   $Date: 2003/10/03 14:26:45 $
062:         */
063:        public class MultiThreadedTestRunner {
064:            private static final Class THIS_CLASS = MultiThreadedTestRunner.class;
065:            private static final String THIS_CLASS_NAME = THIS_CLASS.getName();
066:            private static final Logger LOG = Logger.getLogger(THIS_CLASS);
067:
068:            private static final long DEFAULT_MAX_FINAL_JOIN_TIME = 30l * 1000l;
069:            private static final long DEFAULT_MAX_WAIT_TIME = 24l * 60l * 60l * 1000l;
070:            private static final long MIN_WAIT_TIME = 10l;
071:
072:            private Object synch = new Object();
073:            private boolean threadsFinished = false;
074:            private ThreadGroup threadGroup;
075:            private Thread coreThread;
076:            private Throwable exception;
077:            private TestRunnable runners[];
078:            private TestRunnable monitors[];
079:            private long maxFinalJoinTime = DEFAULT_MAX_FINAL_JOIN_TIME;
080:            private long maxWaitTime = DEFAULT_MAX_WAIT_TIME;
081:            private boolean performKills = true;
082:
083:            /**
084:             * Create a new utility instance with the given set of parallel runners.
085:             * All runners passed into this method must end on their own, else it's
086:             * an error.
087:             */
088:            public MultiThreadedTestRunner(TestRunnable tr[]) {
089:                this (tr, null);
090:            }
091:
092:            /**
093:             * Create a new utility instance with the given set of parallel runners
094:             * and a set of monitors.  The runners must end on their own, but the
095:             * monitors can run until told to stop.
096:             *
097:             * @param runners a non-null, non-empty collection of test runners.
098:             * @param monitors a list of monitor runners, which may be <tt>null</tt> or
099:             *      empty.
100:             */
101:            public MultiThreadedTestRunner(TestRunnable runners[],
102:                    TestRunnable monitors[]) {
103:                if (runners == null) {
104:                    throw new IllegalArgumentException("no null runners");
105:                }
106:                int len = runners.length;
107:                if (len <= 0) {
108:                    throw new IllegalArgumentException(
109:                            "must have at least one runnable");
110:                }
111:                this .runners = new TestRunnable[len];
112:                System.arraycopy(runners, 0, this .runners, 0, len);
113:
114:                if (monitors != null) {
115:                    len = monitors.length;
116:                    this .monitors = new TestRunnable[len];
117:                    System.arraycopy(monitors, 0, this .monitors, 0, len);
118:                } else {
119:                    this .monitors = new TestRunnable[0];
120:                }
121:            }
122:
123:            /**
124:             * Run each test given in a separate thread. Wait for each thread
125:             * to finish running, then return.
126:             * <P>
127:             * As of July 9, 2003, this method will not wait forever, but rather
128:             * will wait for the internal maximum run time, which is by default
129:             * 24 hours; for most unit testing scenarios, this is more than
130:             * sufficient.
131:             *
132:             * @exception Throwable thrown on a test run if a threaded task
133:             *      throws an exception.
134:             */
135:            public void runTestRunnables() throws Throwable {
136:                runTestRunnables(-1);
137:            }
138:
139:            /**
140:             * Runs each test given in a separate thread. Waits for each thread
141:             * to finish running (possibly killing them), then returns.
142:             *
143:             * @param runnables the list of TestCaseRunnable objects to run
144:             *      asynchronously
145:             * @param maxTime the maximum amount of milliseconds to wait for
146:             *      the tests to run. If the time is &lt;= 0, then the tests
147:             *      will run until they are complete. Otherwise, any threads that
148:             *      don't complete by the given number of milliseconds will be killed,
149:             *      and a failure will be thrown.
150:             * @exception Throwable thrown from the underlying tests if they happen
151:             *      to cause an error.
152:             */
153:            public void runTestRunnables(long maxTime) throws Throwable {
154:                // Ensure we aren't interrupted.
155:                // This can happen from one test execution to the next, if an
156:                // interrupt was poorly timed on the core thread.  Calling
157:                // Thread.interrupted() will clear the interrupted status flag.
158:                Thread.interrupted();
159:
160:                // initialize the data.
161:                this .exception = null;
162:                this .coreThread = Thread.currentThread();
163:                this .threadGroup = new ThreadGroup(THIS_CLASS_NAME);
164:                this .threadsFinished = false;
165:
166:                // start the monitors before the runners
167:                Thread monitorThreads[] = setupThreads(this .threadGroup,
168:                        this .monitors);
169:                Thread runnerThreads[] = setupThreads(this .threadGroup,
170:                        this .runners);
171:
172:                // catch the IE exception outside the loop so that an exception
173:                // thrown in a thread will kill all the other threads.
174:                boolean threadsStillRunning;
175:                try {
176:                    threadsStillRunning = joinThreads(runnerThreads, maxTime);
177:                } catch (InterruptedException ie) {
178:                    // Thread join interrupted: some runner or monitor caused an
179:                    // exception.  Note that this is NOT a timeout!
180:                    threadsStillRunning = true;
181:                } finally {
182:                    synchronized (this .synch) {
183:                        if (!this .threadsFinished) {
184:                            interruptThreads();
185:                        } else {
186:                            LOG.debug("All threads finished within timeframe.");
187:                        }
188:                    }
189:                }
190:
191:                if (threadsStillRunning) {
192:                    LOG.debug("Halting the test threads.");
193:
194:                    // threads are still running.  If no exception was generated,
195:                    // then set a timeout error to indicate some threads didn't
196:                    // end in time.
197:                    setTimeoutError(maxTime);
198:
199:                    // kill any remaining threads
200:                    try {
201:                        // but give them one last chance!
202:                        joinThreads(runnerThreads, maxFinalJoinTime);
203:                    } catch (InterruptedException ie) {
204:                        // someone caused a real exception.  This is NOT a timeout!
205:                    }
206:                    int killCount = killThreads(runnerThreads);
207:                    if (killCount > 0) {
208:                        LOG.fatal(killCount
209:                                + " thread(s) did not stop themselves.");
210:                        setTimeoutError(maxFinalJoinTime);
211:                    }
212:                }
213:
214:                // Stop the monitor threads - they have a time limit!
215:                LOG.debug("Halting the monitor threads.");
216:                try {
217:                    joinThreads(monitorThreads, maxFinalJoinTime);
218:                } catch (InterruptedException ex) {
219:                    // don't cause a timeout error with monitor threads.
220:                }
221:                killThreads(monitorThreads);
222:
223:                if (this .exception != null) {
224:                    // an exception/error occurred during the test, so throw
225:                    // the exception so it is reported by the owning test
226:                    // correctly.
227:                    LOG.debug("Exception occurred during testing.",
228:                            this .exception);
229:                    throw this .exception;
230:                }
231:                LOG.debug("No exceptions caused during execution.");
232:            }
233:
234:            /**
235:             * Handles an exception by sending them to the test results.  Called by
236:             * runner or monitor threads.
237:             */
238:            void handleException(Throwable t) {
239:                LOG.warn("A test thread caused an exception.", t);
240:                synchronized (this .synch) {
241:                    if (this .exception == null) {
242:                        LOG.debug("Setting the exception to:", t);
243:                        this .exception = t;
244:                    }
245:
246:                    if (!this .threadsFinished) {
247:                        interruptThreads();
248:                    }
249:                }
250:
251:                if (t instanceof  ThreadDeath) {
252:                    // rethrow ThreadDeath after they have been registered
253:                    // and the threads have been signaled to halt.
254:                    throw (ThreadDeath) t;
255:                }
256:            }
257:
258:            /**
259:             * Stops all running test threads.  Called by runner or monitor threads.
260:             */
261:            void interruptThreads() {
262:                LOG.debug("Forcing all test threads to stop.");
263:                synchronized (this .synch) {
264:                    // interrupt the core thread (that might be doing a join)
265:                    // first, so that it doesn't accidentally do a join on
266:                    // other threads that were interrupted.
267:                    if (Thread.currentThread() != this .coreThread) {
268:                        this .coreThread.interrupt();
269:                    }
270:
271:                    this .threadsFinished = true;
272:
273:                    int count = this .threadGroup.activeCount();
274:                    Thread t[] = new Thread[count];
275:                    this .threadGroup.enumerate(t);
276:                    for (int i = t.length; --i >= 0;) {
277:                        if (t[i] != null && t[i].isAlive()) {
278:                            t[i].interrupt();
279:                        }
280:                    }
281:                }
282:            }
283:
284:            /**
285:             * Used by the TestRunnable instances to tell if the parallel execution
286:             * has stopped or is stopping.
287:             */
288:            boolean areThreadsFinished() {
289:                return this .threadsFinished;
290:            }
291:
292:            /**
293:             * Sets up the threads for the given runnables and starts them.
294:             */
295:            private Thread[] setupThreads(ThreadGroup tg, TestRunnable tr[]) {
296:                int len = tr.length;
297:                Thread threads[] = new Thread[len];
298:                for (int i = 0; i < len; ++i) {
299:                    tr[i].setTestRunner(this );
300:                    threads[i] = new Thread(tg, tr[i]);
301:                    threads[i].setDaemon(true);
302:                }
303:                for (int i = 0; i < len; ++i) {
304:                    threads[i].start();
305:
306:                    // wait for the threads to actually start.  If we wait 10
307:                    // times and still no dice, I expect the test already started
308:                    // and finished.
309:                    int count = 0;
310:                    while (!threads[i].isAlive() && count < 10) {
311:                        LOG.debug("Waiting for thread at index " + i
312:                                + " to start.");
313:                        Thread.yield();
314:                        ++count;
315:                    }
316:                    if (count >= 10) {
317:                        LOG.debug("Assuming thread at index " + i
318:                                + " already finished.");
319:                    }
320:                }
321:                return threads;
322:            }
323:
324:            /**
325:             * This joins all the threads together.  If the max time is exceeded,
326:             * then <tt>true</tt> is returned.  This method is only called by the core
327:             * thread.  The thread array will be altered at return time to only contain
328:             * threads which are still active (all other slots will be <tt>null</tt>).
329:             * <P>
330:             * This routine allows us to attempt to collect all the halted threads
331:             * together, while not waiting forever on threads that poorly don't
332:             * respond to outside stimuli (and thus require a stop() on the
333:             * thread).
334:             */
335:            private boolean joinThreads(Thread t[], long waitTime)
336:                    throws InterruptedException {
337:                // check the arguments
338:                if (t == null) {
339:                    return false;
340:                }
341:                int len = t.length;
342:                if (len <= 0) {
343:                    return false;
344:                }
345:                if (waitTime < 0 || waitTime > maxWaitTime) {
346:                    waitTime = DEFAULT_MAX_WAIT_TIME;
347:                }
348:
349:                // slowly halt the threads.
350:                boolean threadsRunning = true;
351:                InterruptedException iex = null;
352:                long finalTime = System.currentTimeMillis() + waitTime;
353:                while (threadsRunning && System.currentTimeMillis() < finalTime
354:                        && iex == null) {
355:                    LOG.debug("Time = " + System.currentTimeMillis()
356:                            + "; final = " + finalTime);
357:                    threadsRunning = false;
358:
359:                    // There might be circumstances where
360:                    // the time between entering the while loop and entering the
361:                    // for loop exceeds the final time, which can cause an incorrect
362:                    // threadsRunning value.  That's why this boolean exists.  Note
363:                    // that since we put in the (len <= 0) test above, we don't
364:                    // have to worry about another edge case where the length prevents
365:                    // the loop from being entered.
366:                    boolean enteredLoop = false;
367:
368:                    for (int i = 0; i < len
369:                            && System.currentTimeMillis() < finalTime; ++i) {
370:                        enteredLoop = true;
371:                        if (t[i] != null) {
372:                            try {
373:                                // this will yield our time, so we don't
374:                                // need any explicit yield statement.
375:                                t[i].join(MIN_WAIT_TIME);
376:                            } catch (InterruptedException ex) {
377:                                LOG.debug("Join for thread at index " + i
378:                                        + " was interrupted.");
379:                                iex = ex;
380:                            }
381:                            if (!t[i].isAlive()) {
382:                                LOG.debug("Joined thread at index " + i);
383:                                t[i] = null;
384:                            } else {
385:                                LOG.debug("Thread at index " + i
386:                                        + " still running.");
387:                                threadsRunning = true;
388:                            }
389:                        }
390:                    }
391:
392:                    // If the threadsRunning is true, it remains true.  If
393:                    // the enteredLoop is false, this will be true.
394:                    threadsRunning = threadsRunning || !enteredLoop;
395:                }
396:                if (iex != null) {
397:                    throw iex;
398:                }
399:                return threadsRunning;
400:            }
401:
402:            /**
403:             * This will execute a stop() on all non-null, alive threads in the list.
404:             *
405:             * @return the number of threads killed
406:             */
407:            private int killThreads(Thread[] t) {
408:                int killCount = 0;
409:                for (int i = 0; i < t.length; ++i) {
410:                    if (t[i] != null && t[i].isAlive()) {
411:                        LOG.debug("Stopping thread at index " + i);
412:                        ++killCount;
413:                        if (this .performKills) {
414:                            // Yes, this is deprecated API, but we give the threads
415:                            // "sufficient" warning to stop themselves.
416:                            int count = 0;
417:                            boolean isAlive = t[i].isAlive();
418:                            while (isAlive && count < 10) {
419:                                // send an InterruptedException, as this is handled
420:                                // specially in the TestRunnable.
421:                                t[i].stop(new TestDeathException("Thread " + i
422:                                        + " did not die on its own"));
423:                                LOG.debug("Waiting for thread at index " + i
424:                                        + " to stop.");
425:                                Thread.yield();
426:                                isAlive = t[i].isAlive();
427:
428:                                if (isAlive) {
429:                                    // it may have been in a sleep state, so
430:                                    // make it shake a leg!
431:                                    t[i].interrupt();
432:                                }
433:                                ++count;
434:                            }
435:                            if (count >= 10) {
436:                                LOG.fatal("Thread at index " + i
437:                                        + " did not stop!");
438:                            }
439:                            t[i] = null;
440:                        } else {
441:                            LOG.fatal("Did not stop thread " + t[i]);
442:                        }
443:                    }
444:                }
445:                return killCount;
446:            }
447:
448:            private void setTimeoutError(long maxTime) {
449:                Throwable t = createTimeoutError(maxTime);
450:                synchronized (this .synch) {
451:                    if (this .exception == null) {
452:                        LOG
453:                                .debug("Setting the exception to a timeout exception.");
454:                        this .exception = t;
455:                    }
456:                }
457:            }
458:
459:            private Throwable createTimeoutError(long maxTime) {
460:                Throwable ret = null;
461:                // need to set the exception to a timeout
462:                try {
463:                    Assert.fail("Threads did not finish within " + maxTime
464:                            + " milliseconds.");
465:                } catch (ThreadDeath td) {
466:                    // never trap these
467:                    throw td;
468:                } catch (Throwable t) {
469:                    t.fillInStackTrace();
470:                    ret = t;
471:                }
472:                return ret;
473:            }
474:
475:            /**
476:             * An exception that declares that the test has been stop()ed.
477:             */
478:            public static final class TestDeathException extends
479:                    RuntimeException {
480:                private TestDeathException(String msg) {
481:                    super(msg);
482:                }
483:            }
484:        }
www.java2java.com | Contact Us
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.