001    /*
002     * Created on Nov 17, 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.junit.v4_3_1.runner;
016    
017    
018    import static org.junit.runner.Description.createSuiteDescription;
019    import static org.junit.runner.Description.createTestDescription;
020    
021    import java.lang.reflect.InvocationTargetException;
022    import java.lang.reflect.Method;
023    import java.util.List;
024    
025    import org.fest.swing.junit.runner.FailureScreenshotTaker;
026    import org.fest.swing.junit.runner.ImageFolderCreator;
027    import org.junit.Test;
028    import org.junit.internal.runners.*;
029    import org.junit.runner.Description;
030    import org.junit.runner.Runner;
031    import org.junit.runner.notification.RunNotifier;
032    
033    /**
034     * Understands a JUnit 4.3.1 test runner that takes a screenshot of a failed GUI test.
035     *
036     * @author Alex Ruiz
037     * @author Yvonne Wang
038     */
039    public class GUITestRunner extends Runner {
040    
041      private final List<Method> testMethods;
042      private final Class<?> testClass;
043    
044      private final FailureScreenshotTaker screenshotTaker;
045    
046      /**
047       * Creates a new <code>{@link GUITestRunner}</code>.
048       * @param testClass the class containing the tests to run.
049       * @throws InitializationError if something goes wrong when creating this runner.
050       */
051      public GUITestRunner(Class<?> testClass) throws InitializationError {
052        this.testClass = testClass;
053        testMethods = new TestIntrospector(testClass).getTestMethods(Test.class);
054        validate();
055        screenshotTaker = new FailureScreenshotTaker(new ImageFolderCreator().createImageFolder());
056      }
057    
058      private void validate() throws InitializationError {
059        MethodValidator methodValidator = new MethodValidator(testClass);
060        methodValidator.validateMethodsForDefaultRunner();
061        methodValidator.assertValid();
062      }
063    
064      /**
065       * Run the tests for this runner, taking screenshots of failing tests.
066       * @param notifier will be notified of events while tests are being run, started, finishing, and failing.
067       */
068      @Override
069      public void run(RunNotifier notifier) {
070        new InnerRunner(this, notifier).runProtected();
071      }
072    
073      // called by InnerRunner
074      void doRun(RunNotifier notifier) {
075        if (testMethods.isEmpty()) notifier.testAborted(getDescription(), new Exception("No runnable methods"));
076        for (Method method : testMethods)
077          invokeTestMethod(method, notifier);
078      }
079    
080      private void invokeTestMethod(Method method, RunNotifier notifier) {
081        Object test;
082        try {
083          test = testClass.getConstructor().newInstance();
084        } catch (InvocationTargetException e) {
085          notifier.testAborted(descriptionOf(method), e.getCause());
086          return;
087        } catch (Exception e) {
088          notifier.testAborted(descriptionOf(method), e);
089          return;
090        }
091        createMethodRunner(test, method, notifier).run();
092      }
093    
094      private MethodRunner createMethodRunner(Object test, Method method, RunNotifier notifier) {
095        return new MethodRunner(new TestInfo(test, testClass, method), notifier, screenshotTaker);
096      }
097    
098      final Class<?> testClass() {
099        return testClass;
100      }
101    
102      /**
103       * Returns a <code>{@link Description}</code> showing the tests to be run by the receiver.
104       * @return a <code>Description</code> showing the tests to be run by the receiver.
105       */
106      public Description getDescription() {
107        Description spec = createSuiteDescription(testClass.getName());
108        for (Method method : testMethods)
109          spec.addChild(descriptionOf(method));
110        return spec;
111      }
112    
113      private Description descriptionOf(Method method) {
114        return createTestDescription(testClass, method.getName());
115      }
116    }