001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s): Alexandre Iline.
025: *
026: * The Original Software is the Jemmy library.
027: * The Initial Developer of the Original Software is Alexandre Iline.
028: * All Rights Reserved.
029: *
030: * If you wish your version of this file to be governed by only the CDDL
031: * or only the GPL Version 2, indicate your decision by adding
032: * "[Contributor] elects to include this software in this distribution
033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
034: * single choice of license, a recipient has the option to distribute
035: * your version of this file under either the CDDL, the GPL Version 2 or
036: * to extend the choice of license to its licensees as provided above.
037: * However, if you add GPL Version 2 code and therefore, elected the GPL
038: * Version 2 license, then the option applies only if the new code is
039: * made subject to such option by the copyright holder.
040: *
041: *
042: *
043: * $Id$ $Revision$ $Date$
044: *
045: */
046:
047: package org.netbeans.jemmy;
048:
049: import java.io.PrintStream;
050: import java.io.PrintWriter;
051:
052: import java.lang.reflect.InvocationTargetException;
053:
054: /**
055: *
056: * Jemmy itself provides a way to create tests.
057: * Test should implement org.netbeans.jemmy.Scenario interface.
058: *
059: * Test can be executed from command line:<BR>
060: * <code>java [application options] [jemmy options] org.netbeans.jemmy.Test [full name of test class] [test args]</code><BR>
061: * Test elso can be executed by one of the run(...) methods or by <BR>
062: * <code>new Test([test class name]).startTest([test args]);</code><BR>
063: *
064: * <BR><BR>Timeouts used: <BR>
065: * Test.WholeTestTimeout - time for the whole test<BR>
066: *
067: * @author Alexandre Iline (alexandre.iline@sun.com)
068: */
069:
070: public class Test extends ActionProducer implements Timeoutable,
071: Outputable, Scenario {
072:
073: private final static long WHOLE_TEST_TIMEOUT = 3600000;
074:
075: /**
076: * Status returned by test if wrong parameter was passed.
077: */
078: public static int WRONG_PARAMETERS_STATUS = 101;
079:
080: /**
081: * Status returned by test if exception appeared inside scenario.
082: */
083: public static int SCENARIO_EXCEPTION_STATUS = 102;
084:
085: /**
086: * Positive test status.
087: */
088: public static int TEST_PASSED_STATUS = 0;
089:
090: /**
091: Test timeouts.
092: */
093: protected Timeouts timeouts;
094:
095: /**
096: Test output.
097: */
098: protected TestOut output;
099:
100: private Scenario scenario;
101: private static int TEST_FAILED_STATUS = 1;
102:
103: /**
104: * Constructor for tests requiring only a class instance.
105: * Creates a subclass of <code>ActionProducer</code> and <code>java.lang.Thread</code>
106: * that runs in a separate thread of execution and waits for execution to finish.
107: * The current output stream assignments and timeouts are used.
108: * @param testClassName Full test class name
109: */
110: public Test(String testClassName) {
111: super (true);
112: setOutput(JemmyProperties.getCurrentOutput());
113: setTimeouts(JemmyProperties.getCurrentTimeouts());
114: scenario = testForName(testClassName);
115: }
116:
117: /**
118: * Constructor for scenarios that require an instance and might require an argument.
119: * Creates a subclass of <code>ActionProducer</code> and <code>java.lang.Thread</code>
120: * that runs in a separate thread of execution and waits for execution to finish.
121: * The current output stream assignments and timeouts are used.
122: * @param scenario a test scenario
123: * @see org.netbeans.jemmy.Scenario
124: */
125: public Test(Scenario scenario) {
126: super (true);
127: setOutput(JemmyProperties.getCurrentOutput());
128: setTimeouts(JemmyProperties.getCurrentTimeouts());
129: this .scenario = scenario;
130: }
131:
132: /**
133: * No argument constructor.
134: * Used by subclasses of this <code>Test</code> class.
135: * Creates a subclass of <code>ActionProducer</code> and <code>java.lang.Thread</code>
136: * that runs in a separate thread of execution and waits for execution to finish.
137: * The current output stream assignments and timeouts are used.
138: */
139: protected Test() {
140: super (true);
141: setOutput(JemmyProperties.getCurrentOutput());
142: setTimeouts(JemmyProperties.getCurrentTimeouts());
143: }
144:
145: /**
146: * Throws TestCompletedException exception.
147: * The exception thrown contains a pass/fail status and a short
148: * status <code>java.lang.String</code>.
149: * Can by invoked from test to abort test execution.
150: * @param status If 0 - test passed, otherwise failed.
151: * @throws TextCompletedException all of the time.
152: */
153: public static void closeDown(int status) {
154: if (status == 0) {
155: throw (new TestCompletedException(status, "Test passed"));
156: } else {
157: throw (new TestCompletedException(status,
158: "Test failed with status "
159: + Integer.toString(status)));
160: }
161: }
162:
163: /**
164: * Executes a test.
165: * @param argv First element should be a test class name,
166: * all others - test args.
167: * @return test status.
168: */
169: public static int run(String[] argv) {
170: String[] args = argv;
171: JemmyProperties.getProperties().init();
172: if (argv.length < 1) {
173: JemmyProperties
174: .getCurrentOutput()
175: .printErrLine(
176: "First element of String array should be test classname");
177: return (WRONG_PARAMETERS_STATUS);
178: }
179: JemmyProperties.getCurrentOutput().printLine(
180: "Executed test " + argv[0]);
181: Test test = new Test(argv[0]);
182: if (argv.length >= 1) {
183: args = shiftArray(args);
184: }
185: if (argv.length >= 2) {
186: JemmyProperties.getCurrentOutput().printLine(
187: "Work directory: " + argv[1]);
188: System.setProperty("user.dir", argv[1]);
189: args = shiftArray(args);
190: }
191: int status = TEST_FAILED_STATUS;
192: if (test != null) {
193: status = test.startTest(args);
194: }
195: JemmyProperties.getCurrentOutput().flush();
196: return (status);
197: }
198:
199: /**
200: * Executes a test.
201: * @param argv First element should be a test class name,
202: * all others - test args.
203: * @param output Stream to put test output and errput into.
204: * @return test status.
205: */
206: public static int run(String[] argv, PrintStream output) {
207: JemmyProperties.setCurrentOutput(new TestOut(System.in, output,
208: output));
209: return (run(argv));
210: }
211:
212: /**
213: * Executes a test.
214: * @param argv First element should be a test class name,
215: * all others - test args.
216: * @param output Stream to put test output into.
217: * @param errput Stream to put test errput into.
218: * @return test status.
219: */
220: public static int run(String[] argv, PrintStream output,
221: PrintStream errput) {
222: JemmyProperties.setCurrentOutput(new TestOut(System.in, output,
223: errput));
224: return (run(argv));
225: }
226:
227: /**
228: * Executes a test.
229: * @param argv First element should be a test class name,
230: * all others - test args.
231: * @param output Writer to put test output and errput into.
232: * @return test status.
233: */
234: public static int run(String[] argv, PrintWriter output) {
235: JemmyProperties.setCurrentOutput(new TestOut(System.in, output,
236: output));
237: return (run(argv));
238: }
239:
240: /**
241: * Executes a test.
242: * @param argv First element should be a test class name,
243: * all others - test args.
244: * @param output Writer to put test output into.
245: * @param errput Writer to put test errput into.
246: * @return test status.
247: */
248: public static int run(String[] argv, PrintWriter output,
249: PrintWriter errput) {
250: JemmyProperties.setCurrentOutput(new TestOut(System.in, output,
251: errput));
252: return (run(argv));
253: }
254:
255: /**
256: * Invoke this <code>Test</code>.
257: * The call might be directly from the command line.
258: * @param argv First element should be a test class name,
259: * all others - test args.
260: */
261: public static void main(String[] argv) {
262: System.exit(run(argv, System.out));
263: }
264:
265: static {
266: Timeouts.initDefault("Test.WholeTestTimeout",
267: WHOLE_TEST_TIMEOUT);
268: }
269:
270: /**
271: * Creates an instance of a class named by the parameter.
272: * @param testName Full test class name
273: * @return an instance of the test <code>Scenario</code> to launch.
274: * @see org.netbeans.jemmy.Scenario
275: */
276: public Scenario testForName(String testName) {
277: try {
278: return ((Scenario) (Class.forName(testName).getConstructor(
279: new Class[0]).newInstance(new Object[0])));
280: } catch (ClassNotFoundException e) {
281: output.printErrLine("Class " + testName
282: + " does not exist!");
283: output.printStackTrace(e);
284: } catch (NoSuchMethodException e) {
285: output.printErrLine("Class " + testName
286: + " has not constructor!");
287: output.printStackTrace(e);
288: } catch (InvocationTargetException e) {
289: output.printErrLine("Exception inside " + testName
290: + " constructor:");
291: output.printStackTrace(e.getTargetException());
292: } catch (IllegalAccessException e) {
293: output.printErrLine("Cannot access to " + testName
294: + " constructor!");
295: output.printStackTrace(e);
296: } catch (InstantiationException e) {
297: output.printErrLine("Cannot instantiate " + testName
298: + " class!");
299: output.printStackTrace(e);
300: }
301: return (null);
302: }
303:
304: /**
305: * Set the timeouts used by this <code>Test</code>.
306: * @param timeouts A collection of timeout assignments.
307: * @see org.netbeans.jemmy.Timeoutable
308: * @see org.netbeans.jemmy.Timeouts
309: * @see #getTimeouts
310: */
311: public void setTimeouts(Timeouts timeouts) {
312: this .timeouts = timeouts;
313: Timeouts times = timeouts.cloneThis();
314: times.setTimeout("ActionProducer.MaxActionTime", timeouts
315: .getTimeout("Test.WholeTestTimeout"));
316: super .setTimeouts(times);
317: }
318:
319: /**
320: * Get the timeouts used by this <code>Test</code>.
321: * @see org.netbeans.jemmy.Timeoutable
322: * @see org.netbeans.jemmy.Timeouts
323: * @see #setTimeouts
324: */
325: public Timeouts getTimeouts() {
326: return (timeouts);
327: }
328:
329: /**
330: * Set the streams or writers used for print output.
331: * @param out An object used to identify both output and error
332: * print streams.
333: * @see org.netbeans.jemmy.Outputable
334: * @see org.netbeans.jemmy.TestOut
335: * @see #getOutput
336: */
337: public void setOutput(TestOut out) {
338: output = out;
339: super .setOutput(out);
340: }
341:
342: /**
343: * Get the streams or writers used for print output.
344: * @return an object containing references to both output and error print
345: * streams.
346: * @see org.netbeans.jemmy.Outputable
347: * @see org.netbeans.jemmy.TestOut
348: * @see #setOutput
349: */
350: public TestOut getOutput() {
351: return (output);
352: }
353:
354: /**
355: * Executes test.
356: * @param param Object to be passed into this test's launch(Object) method.
357: * @return test status.
358: */
359: public int startTest(Object param) {
360: if (scenario != null) {
361: output.printLine("Test " + scenario.getClass().getName()
362: + " has been started");
363: } else {
364: output.printLine("Test " + getClass().getName()
365: + " has been started");
366: }
367: try {
368: return (((Integer) produceAction(param)).intValue());
369: } catch (InterruptedException e) {
370: output.printErrLine("Test was interrupted.");
371: output.printStackTrace(e);
372: } catch (TimeoutExpiredException e) {
373: output.printErrLine("Test was not finished in "
374: + Long.toString(timeouts
375: .getTimeout("Test.WholeTestTimeout"))
376: + " milliseconds");
377: output.printStackTrace(e);
378: } catch (Exception e) {
379: output.printStackTrace(e);
380: }
381: return (1);
382: }
383:
384: /**
385: * Launch an action.
386: * Pass arguments to and execute a test <code>Scenario</code>.
387: * @param obj An argument object that controls test execution.
388: * This might be a <code>java.lang.String[]</code> containing
389: * command line arguments.
390: * @see org.netbeans.jemmy.Action
391: * @return an Integer containing test status.
392: */
393: public final Object launch(Object obj) {
394: setTimeouts(timeouts);
395: int status;
396: try {
397: if (scenario != null) {
398: closeDown(scenario.runIt(obj));
399: } else {
400: closeDown(runIt(obj));
401: }
402: } catch (TestCompletedException e) {
403: output.printStackTrace(e);
404: return (new Integer(e.getStatus()));
405: } catch (Throwable e) {
406: output.printStackTrace(e);
407: return (new Integer(SCENARIO_EXCEPTION_STATUS));
408: }
409: return (new Integer(TEST_PASSED_STATUS));
410: }
411:
412: /**
413: * Supposed to be overridden to print a synopsys into test output.
414: */
415: public void printSynopsis() {
416: output.printLine("Here should be a test synopsis.");
417: }
418:
419: /**
420: * @see org.netbeans.jemmy.Action
421: */
422: public final String getDescription() {
423: return ("Test " + scenario.getClass().getName() + " finished");
424: }
425:
426: /**
427: * Defines a way to execute this <code>Test</code>.
428: * @param param An object passed to configure the test scenario
429: * execution. For example, this parameter might be a
430: * <code>java.lang.String[]<code> object that lists the
431: * command line arguments to the Java application corresponding
432: * to a test.
433: * @return an int that tells something about the execution.
434: * For, example, a status code.
435: * @see org.netbeans.jemmy.Scenario
436: */
437: public int runIt(Object param) {
438: return (0);
439: }
440:
441: /**
442: * Sleeps.
443: * @param time The sleep time in milliseconds.
444: */
445: protected void doSleep(long time) {
446: try {
447: Thread.currentThread().sleep(time);
448: } catch (InterruptedException e) {
449: }
450: }
451:
452: private static String[] shiftArray(String[] orig) {
453: String[] result = new String[orig.length - 1];
454: for (int i = 0; i < result.length; i++) {
455: result[i] = orig[i + 1];
456: }
457: return (result);
458: }
459:
460: }
|