001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.jorphan.test;
020:
021: import java.io.File;
022: import java.io.FileInputStream;
023: import java.io.FileNotFoundException;
024: import java.io.IOException;
025: import java.lang.reflect.Method;
026: import java.util.Iterator;
027: import java.util.List;
028: import java.util.Properties;
029:
030: import junit.framework.Test;
031: import junit.framework.TestCase;
032: import junit.framework.TestSuite;
033: import junit.textui.TestRunner;
034:
035: import org.apache.jmeter.util.JMeterUtils;
036: import org.apache.jorphan.logging.LoggingManager;
037: import org.apache.jorphan.reflect.ClassFinder;
038: import org.apache.jorphan.util.JOrphanUtils;
039: import org.apache.log.Logger;
040:
041: /**
042: * Provides a quick and easy way to run all <a
043: * href="http://junit.sourceforge.net">junit</a> unit tests in your java
044: * project. It will find all unit test classes and run all their test methods.
045: * There is no need to configure it in any way to find these classes except to
046: * give it a path to search.
047: * <p>
048: * Here is an example Ant target (See Ant at <a
049: * href="http://jakarta.apache.org/ant">Apache</a>) that runs all your unit
050: * tests:
051: *
052: * <pre>
053: *
054: * <target name="test" depends="compile">
055: * <java classname="org.apache.jorphan.test.AllTests" fork="yes">
056: * <classpath>
057: * <path refid="YOUR_CLASSPATH"/>
058: * <pathelement location="ROOT_DIR_OF_YOUR_COMPILED_CLASSES"/>
059: * </classpath>
060: * <arg value="SEARCH_PATH/"/>
061: * <arg value="PROPERTY_FILE"/>
062: * <arg value="NAME_OF_UNITTESTMANAGER_CLASS"/>
063: * </java>
064: * </target>
065: *
066: * </pre>
067: *
068: * <dl>
069: * <dt>YOUR_CLASSPATH</dt>
070: * <dd>Refers to the classpath that includes all jars and libraries need to run
071: * your unit tests</dd>
072: *
073: * <dt>ROOT_DIR_OF_YOUR_COMPILED_CLASSES</dt>
074: * <dd>The classpath should include the directory where all your project's
075: * classes are compiled to, if it doesn't already.</dd>
076: *
077: * <dt>SEARCH_PATH</dt>
078: * <dd>The first argument tells AllTests where to look for unit test classes to
079: * execute. In most cases, it is identical to ROOT_DIR_OF_YOUR_COMPILED_CLASSES.
080: * You can specify multiple directories or jars to search by providing a
081: * comma-delimited list.</dd>
082: *
083: * <dt>PROPERTY_FILE</dt>
084: * <dd>A simple property file that sets logging parameters. It is optional and
085: * is only relevant if you use the same logging packages that JOrphan uses.</dd>
086: *
087: * <dt>NAME_OF_UNITTESTMANAGER_CLASS</dt>
088: * <dd>If your system requires some configuration to run correctly, you can
089: * implement the {@link UnitTestManager} interface and be given an opportunity
090: * to initialize your system from a configuration file.</dd>
091: * </dl>
092: *
093: * @see UnitTestManager
094: * @author Michael Stover (mstover1 at apache.org)
095: * @version $Revision: 584560 $
096: */
097: public final class AllTests {
098: transient private static Logger log = LoggingManager
099: .getLoggerForClass();
100:
101: /**
102: * Private constructor to prevent instantiation.
103: */
104: private AllTests() {
105: }
106:
107: private static void logprop(String prop, boolean show) {
108: String value = System.getProperty(prop);
109: log.info(prop + "=" + value);
110: if (show)
111: System.out.println(prop + "=" + value);
112: }
113:
114: private static void logprop(String prop) {
115: logprop(prop, false);
116: }
117:
118: /**
119: * Starts a run through all unit tests found in the specified classpaths.
120: * The first argument should be a list of paths to search. The second
121: * argument is optional and specifies a properties file used to initialize
122: * logging. The third argument is also optional, and specifies a class that
123: * implements the UnitTestManager interface. This provides a means of
124: * initializing your application with a configuration file prior to the
125: * start of any unit tests.
126: *
127: * @param args
128: * the command line arguments
129: */
130: public static void main(String[] args) {
131: if (args.length < 1) {
132: System.out
133: .println("You must specify a comma-delimited list of paths to search "
134: + "for unit tests");
135: System.exit(1);
136: }
137: String home = new File(System.getProperty("user.dir"))
138: .getParent();
139: System.out.println("Setting JMeterHome: " + home);
140: JMeterUtils.setJMeterHome(home);
141: initializeLogging(args);
142: initializeManager(args);
143:
144: log.info("JMeterVersion=" + JMeterUtils.getJMeterVersion());
145: logprop("java.version", true);
146: logprop("java.vendor");
147: logprop("java.home", true);
148: logprop("user.home");
149: logprop("user.dir", true);
150: logprop("os.name");
151: logprop("os.version");
152: logprop("os.arch");
153: logprop("java.class.version");
154: // logprop("java.class.path");
155: String cp = System.getProperty("java.class.path");
156: String cpe[] = JOrphanUtils.split(cp,
157: java.io.File.pathSeparator);
158: StringBuffer sb = new StringBuffer(3000);
159: sb.append("java.class.path=");
160: for (int i = 0; i < cpe.length; i++) {
161: sb.append("\n");
162: sb.append(cpe[i]);
163: if (new java.io.File(cpe[i]).exists()) {
164: sb.append(" - OK");
165: } else {
166: sb.append(" - ??");
167: }
168: }
169: log.info(sb.toString());
170:
171: // ++
172: // GUI tests throw the error
173: // testArgumentCreation(org.apache.jmeter.config.gui.ArgumentsPanel$Test)java.lang.NoClassDefFoundError
174: // at java.lang.Class.forName0(Native Method)
175: // at java.lang.Class.forName(Class.java:141)
176: // at
177: // java.awt.GraphicsEnvironment.getLocalGraphicsEnvironment(GraphicsEnvironment.java:62)
178: //
179: // Try to find out why this is ...
180:
181: System.out.println("+++++++++++");
182: logprop("java.awt.headless", true);
183: logprop("java.awt.graphicsenv", true);
184: //
185: // try {//
186: // Class c = Class.forName(n);
187: // System.out.println("Found class: "+n);
188: // // c.newInstance();
189: // // System.out.println("Instantiated: "+n);
190: // } catch (Exception e1) {
191: // System.out.println("Error finding class "+n+" "+e1);
192: // } catch (java.lang.InternalError e1){
193: // System.out.println("Error finding class "+n+" "+e1);
194: // }
195: //
196: System.out.println("------------");
197: // don't call isHeadless() here, as it has a side effect.
198: // --
199: System.out.println("Creating test suite");
200: TestSuite suite = suite(args[0]);
201: int countTestCases = suite.countTestCases();
202: System.out.println("Starting test run, test count = "
203: + countTestCases);
204: // for (int i=0;i<suite.testCount();i++){
205: // Test testAt = suite.testAt(i);
206: // int testCases = testAt.countTestCases();
207: // if (testAt instanceof junit.framework.TestCase){
208: // System.out.print(((junit.framework.TestCase) testAt).getName());
209: // }
210: // if (testAt instanceof TestSuite){
211: // TestSuite testSuite = ((TestSuite) testAt);
212: // String name = testSuite.getName();
213: // System.out.print(name);
214: // System.out.println(" "+testCases);
215: // }
216: // }
217:
218: // Jeremy Arnold: This method used to attempt to write results to
219: // a file, but it had a bug and instead just wrote to System.out.
220: // Since nobody has complained about this behavior, I'm changing
221: // the code to not attempt to write to a file, so it will continue
222: // behaving as it did before. It would be simple to make it write
223: // to a file instead if that is the desired behavior.
224: TestRunner.run(suite);
225: // ++
226: // Recheck settings:
227: //System.out.println("+++++++++++");
228: // System.out.println(e+"="+System.getProperty(e));
229: // System.out.println(g+"="+System.getProperty(g));
230: // System.out.println("Headless?
231: // "+java.awt.GraphicsEnvironment.isHeadless());//JDK 1.4
232: // try {
233: // Class c = Class.forName(n);
234: // System.out.println("Found class: "+n);
235: // c.newInstance();
236: // System.out.println("Instantiated: "+n);
237: // } catch (Exception e1) {
238: // System.out.println("Error with class "+n+" "+e1);
239: // } catch (java.lang.InternalError e1){
240: // System.out.println("Error with class "+n+" "+e1);
241: // }
242: //System.out.println("------------");
243: // --
244: System.exit(0); // this is needed because the test may start the AWT EventQueue thread which is not a daemon.
245: }
246:
247: /**
248: * An overridable method that initializes the logging for the unit test run,
249: * using the properties file passed in as the second argument.
250: *
251: * @param args
252: */
253: protected static void initializeLogging(String[] args) {
254: if (args.length >= 2) {
255: Properties props = new Properties();
256: try {
257: System.out
258: .println("Setting up logging props using file: "
259: + args[1]);
260: props.load(new FileInputStream(args[1]));
261: LoggingManager.initializeLogging(props);
262: } catch (FileNotFoundException e) {
263: System.out.println(e.getLocalizedMessage());
264: } catch (IOException e) {
265: System.out.println(e.getLocalizedMessage());
266: }
267: }
268: }
269:
270: /**
271: * An overridable method that that instantiates a UnitTestManager (if one
272: * was specified in the command-line arguments), and hands it the name of
273: * the properties file to use to configure the system.
274: *
275: * @param args
276: */
277: protected static void initializeManager(String[] args) {
278: if (args.length >= 3) {
279: try {
280: System.out.println("Using initializeProperties() from "
281: + args[2]);
282: UnitTestManager um = (UnitTestManager) Class.forName(
283: args[2]).newInstance();
284: System.out
285: .println("Setting up initial properties using: "
286: + args[1]);
287: um.initializeProperties(args[1]);
288: } catch (ClassNotFoundException e) {
289: System.out.println("Couldn't create: " + args[2]);
290: e.printStackTrace();
291: } catch (InstantiationException e) {
292: System.out.println("Couldn't create: " + args[2]);
293: e.printStackTrace();
294: } catch (IllegalAccessException e) {
295: System.out.println("Couldn't create: " + args[2]);
296: e.printStackTrace();
297: }
298: }
299: }
300:
301: /*
302: * Externally callable suite() method for use by JUnit Allows tests to be
303: * run directly under JUnit, rather than using the startup code in the rest
304: * of the module. No parameters can be passed in, so it is less flexible.
305: */
306: public static TestSuite suite() {
307: String args[] = { "../lib/ext", "./jmetertest.properties",
308: "org.apache.jmeter.util.JMeterUtils" };
309:
310: initializeManager(args);
311: return suite(args[0]);
312: }
313:
314: /**
315: * A unit test suite for JUnit.
316: *
317: * @return The test suite
318: */
319: private static TestSuite suite(String searchPaths) {
320: TestSuite suite = new TestSuite("All Tests");
321: System.out.println("Scanning " + searchPaths
322: + " for test cases");
323: int tests = 0;
324: int suites = 0;
325: try {
326: log.info("ClassFinder(TestCase)");
327: List classList = ClassFinder.findClassesThatExtend(
328: JOrphanUtils.split(searchPaths, ","),
329: new Class[] { TestCase.class }, true);
330: int sz = classList.size();
331: log.info("ClassFinder(TestCase) found: " + sz
332: + " TestCase classes");
333: System.out.println("ClassFinder found: " + sz
334: + " TestCase classes");
335: Iterator classes = classList.iterator();
336: while (classes.hasNext()) {
337: String name = (String) classes.next();
338: try {
339: /*
340: * TestSuite only finds testXXX() methods, and does not look
341: * for suite() methods.
342: *
343: * To provide more compatibilty with stand-alone tests,
344: * where JUnit does look for a suite() method, check for it
345: * first here.
346: *
347: */
348:
349: Class clazz = Class.forName(name);
350: Test t = null;
351: try {
352: Method m = clazz.getMethod("suite",
353: new Class[0]);
354: t = (Test) m.invoke(clazz, null);
355: suites++;
356: } catch (NoSuchMethodException e) {
357: } // this is not an error, the others are
358: // catch (SecurityException e) {}
359: // catch (IllegalAccessException e) {}
360: // catch (IllegalArgumentException e) {}
361: // catch (InvocationTargetException e) {}
362:
363: if (t == null) {
364: t = new TestSuite(clazz);
365: }
366:
367: tests++;
368: suite.addTest(t);
369: } catch (Exception ex) {
370: System.out.println("Error adding test for class "
371: + name + " " + ex.toString());
372: log.error("error adding test :", ex);
373: }
374: }
375: } catch (IOException e) {
376: log.error("", e);
377: }
378: System.out.println("Created: " + tests + " tests including "
379: + suites + " suites");
380: return suite;
381: }
382: }
|