001: /*
002: * @(#)SubTestTestCase.java
003: *
004: * Copyright (C) 2002-2003 Matt Albrecht
005: * groboclown@users.sourceforge.net
006: * http://groboutils.sourceforge.net
007: *
008: * Permission is hereby granted, free of charge, to any person obtaining a
009: * copy of this software and associated documentation files (the "Software"),
010: * to deal in the Software without restriction, including without limitation
011: * the rights to use, copy, modify, merge, publish, distribute, sublicense,
012: * and/or sell copies of the Software, and to permit persons to whom the
013: * Software is furnished to do so, subject to the following conditions:
014: *
015: * The above copyright notice and this permission notice shall be included in
016: * all copies or substantial portions of the Software.
017: *
018: * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
019: * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
020: * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
021: * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
022: * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
023: * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
024: * DEALINGS IN THE SOFTWARE.
025: */
026:
027: package net.sourceforge.groboutils.junit.v1;
028:
029: import org.apache.log4j.Logger;
030: import junit.framework.Test;
031: import junit.framework.TestCase;
032: import junit.framework.TestResult;
033:
034: import java.util.Hashtable;
035: import java.util.Vector;
036: import java.util.Enumeration;
037:
038: /**
039: * A TestCase which enables tests to run a subset of tests from an active
040: * test. Examples would include running unit tests associated with an
041: * object returned from a creation method called on the class under test.
042: * <P>
043: * Note that added sub-tests should be new Test instances, not the same test.
044: * This is because these sub-tests will run after the registered instance
045: * has <tt>tearDown()</tt> called on it.
046: *
047: * @author Matt Albrecht <a href="mailto:groboclown@users.sourceforge.net">groboclown@users.sourceforge.net</a>
048: * @version $Date: 2003/02/10 22:52:19 $
049: * @since July 26, 2002
050: */
051: public class SubTestTestCase extends TestCase {
052: private static final Logger LOG = Logger
053: .getLogger(SubTestTestCase.class);
054:
055: private Hashtable perThreadTestList = new Hashtable();
056:
057: /**
058: * Creates a new test case with the given name. This allows for JUnit
059: * 3.7 compatibility.
060: */
061: public SubTestTestCase(String name) {
062: super (name);
063: }
064:
065: /**
066: * Default constructor that uses the JUnit 3.8 version of <tt>setName()</tt>
067: * for setting the test's name.
068: *
069: * @since December 28, 2002
070: */
071: public SubTestTestCase() {
072: // do nothing
073: }
074:
075: /**
076: * Allows for execution of the given test instance from inside another
077: * test. This will use the current test's TestResult, or if there
078: * is no current <tt>TestResult</tt>, it will create a new one for the
079: * test. Note that the list of tests will be run at the end of the current
080: * test, after the <tt>tearDown()</tt> method has been called. This is for
081: * legacy TestListener support.
082: *
083: * @param test the test object to add as a sub-test.
084: */
085: public void addSubTest(Test test) {
086: // Since the vector will be pulled from a hashtable that stores the
087: // vectors on a per thread basis, there is no chance that the same
088: // vector will be requested from two different threads at the same time.
089: // Hence, no need for synchronization.
090: if (test != null) {
091: Thread t = Thread.currentThread();
092: Vector v = (Vector) this .perThreadTestList.get(t);
093: if (v != null) {
094: LOG.debug("Adding test [" + test + "] to test ["
095: + getName() + "]");
096: v.addElement(test);
097: } else {
098: LOG.warn("Attemted to add test [" + test
099: + "] to test [" + getName()
100: + "] without calling the run() method.");
101: }
102: } else {
103: LOG.warn("Attempted to add null test to test [" + getName()
104: + "]");
105: }
106: }
107:
108: /**
109: * Runs the test case and collects the results in TestResult.
110: */
111: public void run(TestResult result)
112: {
113: // Note: it is 'bad form' for the test to store the result. It is
114: // also very bad to store the result for the purpose of running the
115: // requested sub-test when asked, as this will cause a recursive-
116: // style event in the result listeners, which some listeners may
117: // not support. Therefore, the tests are loaded into a list, and
118: // executed at the end of the test run. Note that this causes the
119: // added tests to run after the tearDown method.
120:
121: Thread t = Thread.currentThread();
122: Vector list = (Vector)this .perThreadTestList.get( t );
123: // shouldn't be re-entrant!
124: // but we'll allow it, however the tests added in this run will
125: // only be executed at the end of the recursive run calls.
126: if (list == null)
127: {
128: this .perThreadTestList.put( t, new Vector() );
129: }
130:
131: super .run( result );
132:
133: // if this method is not a reentrant method...
134: if (list == null)
135: {
136: // run all the added tests
137: list = (Vector)this .perThreadTestList.get( t );
138: if (list != null)
139: {
140: LOG.debug( "run method now executing all added tests (count="+
141: list.size()+"); current ran test count = "+
142: result.runCount() );
143: Enumeration enum = list.elements();
144: while (enum.hasMoreElements())
145: {
146: Test test = (Test)enum.nextElement();
147: LOG.debug( "running test ["+test+"] from test ["+
148: getName()+"]" );
149: test.run( result );
150: LOG.debug( "run over for test ["+test+
151: "] from test ["+getName()+
152: "]; current ran test count = "+result.runCount() );
153: }
154:
155: // now remove the list from the hashtable, for future
156: // reentrancy
157: this.perThreadTestList.remove( t );
158: }
159: }
160: else
161: {
162: LOG.debug(
163: "run method was re-entered. Ignoring added tests for now." );
164: }
165: }
166: }
|