001 /*
002 * Created on May 6, 2007
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
005 * the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
010 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
011 * specific language governing permissions and limitations under the License.
012 *
013 * Copyright @2007-2009 the original author or authors.
014 */
015 package org.fest.swing.testng.listener;
016
017 import java.lang.reflect.Method;
018 import java.util.logging.Logger;
019
020 import org.testng.ITestContext;
021 import org.testng.ITestResult;
022 import org.testng.Reporter;
023
024 import org.fest.swing.annotation.GUITestFinder;
025 import org.fest.swing.image.ImageException;
026 import org.fest.swing.image.ScreenshotTaker;
027 import org.fest.util.VisibleForTesting;
028
029 import static java.io.File.separator;
030 import static java.util.logging.Level.SEVERE;
031
032 import static org.fest.swing.testng.listener.ScreenshotFileNameGenerator.screenshotFileNameFrom;
033 import static org.fest.util.Strings.*;
034
035 /**
036 * Understands a <a href="http://testng.org" target="_blank">TestNG</a> listener that takes a screenshot when a GUI test
037 * fails.
038 * <p>
039 * <strong>Note:</strong> A test is consider a GUI test if it is marked with the annotation
040 * <code>{@link org.fest.swing.annotation.GUITest}</code>.
041 * </p>
042 * <p>
043 * To use this listener, we just need to make TestNG aware of it. The following is an example using Ant:
044 * <pre>
045 * <testng <strong><span style="text-decoration: underline">listeners="org.fest.swing.testng.listener.ScreenshotOnFailureListener"</span></strong> outputDir="${target.test.results.dir}" haltOnFailure="true" verbose="2">
046 * <classfileset dir="${target.test.classes.dir}" includes="**/*Test.class" />
047 * <classpath location="${target.test.classes.dir}" />
048 * <classpath location="${target.classes.dir}" />
049 * <classpath refid="test.classpath" />
050 * </testng>
051 * </pre>
052 * </p>
053 * <p>
054 * You can find more information
055 * <a href="http://www.jroller.com/page/alexRuiz?entry=screenshots_of_failures_in_test" target="_blank">here</a>.
056 * </p>
057 *
058 * @author Alex Ruiz
059 */
060 public class ScreenshotOnFailureListener extends AbstractTestListener {
061
062 private static Logger logger = Logger.getAnonymousLogger();
063
064 private ScreenshotTaker screenshotTaker;
065 private OutputDirectory output;
066 private boolean ready;
067
068 /**
069 * Creates a new <code>{@link ScreenshotOnFailureListener}</code>.
070 */
071 public ScreenshotOnFailureListener() {
072 try {
073 screenshotTaker = new ScreenshotTaker();
074 } catch (ImageException e) {
075 logger.log(SEVERE, "Unable to create ScreenshotTaker", e);
076 }
077 }
078
079 @VisibleForTesting
080 String output() { return output.path(); }
081
082 /**
083 * Gets the output directory from the given context after the test class is instantiated and before any configuration
084 * method is called.
085 * @param context the given method context.
086 */
087 @Override public void onStart(ITestContext context) {
088 output = new OutputDirectory(context);
089 logger.info(concat("TestNG output directory: ", quote(output.path())));
090 ready = output.hasPath() && screenshotTaker != null;
091 }
092
093 /**
094 * When a test fails, this method takes a screenshot of the desktop and adds an hyperlink to the screenshot it in the
095 * HTML test report.
096 * @param result contains information about the failing test.
097 */
098 @Override public void onTestFailure(ITestResult result) {
099 if (!ready || !isGUITest(result)) return;
100 String screenshotFileName = takeScreenshotAndReturnFileName(result);
101 if (isEmpty(screenshotFileName)) return;
102 logger.info(concat("Screenshot of desktop saved as: ", quote(screenshotFileName)));
103 Reporter.setCurrentTestResult(result);
104 Reporter.log(concat("<a href=\"", screenshotFileName, "\">Screenshot</a>"));
105 }
106
107 private static boolean isGUITest(ITestResult testResult) {
108 Class<?> realClass = testResult.getTestClass().getRealClass();
109 Method testMethod = testResult.getMethod().getMethod();
110 return GUITestFinder.isGUITest(realClass, testMethod);
111 }
112
113 private String takeScreenshotAndReturnFileName(ITestResult result) {
114 String imageName = screenshotFileNameFrom(result);
115 String imagePath = concat(output(), separator, imageName);
116 try {
117 output.createIfNecessary();
118 screenshotTaker.saveDesktopAsPng(imagePath);
119 } catch (Exception e) {
120 logger.log(SEVERE, e.getMessage(), e);
121 return null;
122 }
123 return imageName;
124 }
125 }