Home | History | Annotate | Download | only in junit
      1 /*
      2  * Copyright (C) 2016 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package vogar.target.junit;
     18 
     19 import java.lang.annotation.Annotation;
     20 import java.lang.reflect.Method;
     21 import java.util.ArrayList;
     22 import java.util.HashMap;
     23 import java.util.List;
     24 import java.util.Map;
     25 import java.util.Set;
     26 import junit.framework.AssertionFailedError;
     27 import org.junit.runners.BlockJUnit4ClassRunner;
     28 import org.junit.runners.model.FrameworkMethod;
     29 import org.junit.runners.model.InitializationError;
     30 import org.junit.runners.model.RunnerBuilder;
     31 
     32 /**
     33  * A specialization of {@link BlockJUnit4ClassRunner} to implement behavior required by Vogar.
     34  *
     35  * <ol>
     36  * <li>Defers validation of test methods,
     37  * see {@link ValidateTestMethodWhenRunBlockJUnit4ClassRunner}.</li>
     38  * <li>Applies global rules, see {@link ApplyGlobalRulesBlockJUnit4ClassRunner}</li>
     39  * <li>Selects either explicitly requested methods, or all methods.</li>
     40  * </ol>
     41  */
     42 public class VogarBlockJUnit4ClassRunner
     43         extends ValidateTestMethodWhenRunBlockJUnit4ClassRunner {
     44 
     45     private final RunnerParams runnerParams;
     46 
     47     /**
     48      * Used by annotation runner.
     49      */
     50     @SuppressWarnings("unused")
     51     public VogarBlockJUnit4ClassRunner(Class<?> klass, RunnerBuilder suiteBuilder)
     52             throws InitializationError {
     53         this(klass, ((VogarRunnerBuilder) suiteBuilder).getRunnerParams());
     54     }
     55 
     56     public VogarBlockJUnit4ClassRunner(Class<?> klass, RunnerParams runnerParams)
     57             throws InitializationError {
     58         super(klass, runnerParams.getTestRule());
     59         this.runnerParams = runnerParams;
     60     }
     61 
     62     @Override
     63     protected List<FrameworkMethod> getChildren() {
     64         // Overridden to handle requested methods.
     65         Set<String> requestedMethodNames = JUnitUtils.mergeQualificationAndArgs(
     66                 runnerParams.getQualification(), runnerParams.getArgs());
     67         List<FrameworkMethod> methods = super.getChildren();
     68 
     69         // If specific methods have been requested then select them from all the methods that were
     70         // found. If they cannot be found then add a fake one that will report the method as
     71         // missing.
     72         if (!requestedMethodNames.isEmpty()) {
     73             // Store all the methods in a map by name. That should be safe as test methods do not
     74             // have parameters so there can only be one method in a class with each name.
     75             Map<String, FrameworkMethod> map = new HashMap<>();
     76             for (FrameworkMethod method : methods) {
     77                 map.put(method.getName(), method);
     78             }
     79 
     80             methods = new ArrayList<>();
     81             for (final String name : requestedMethodNames) {
     82                 FrameworkMethod method = map.get(name);
     83                 if (method == null) {
     84                     // The method could not be found so add one that when invoked will report the
     85                     // method as missing.
     86                     methods.add(new MissingFrameworkMethod(name));
     87                 } else {
     88                     methods.add(method);
     89                 }
     90             }
     91         }
     92         return methods;
     93     }
     94 
     95     /**
     96      * A {@link FrameworkMethod} that is used when a specific method has been requested but no
     97      * suitable {@link Method} exists.
     98      *
     99      * <p>It overrides a number of methods that are called during normal processing in order to
    100      * avoid throwing a NPE. It also overrides {@link #validatePublicVoidNoArg(boolean, List)} to
    101      * report the method as being missing. It relies on a {@link ValidateMethodStatement} to call
    102      * that method immediately prior to invoking the method.
    103      */
    104     private static class MissingFrameworkMethod extends FrameworkMethod {
    105         private static final Annotation[] NO_ANNOTATIONS = new Annotation[0];
    106         private static final Method DUMMY_METHOD;
    107         static {
    108             DUMMY_METHOD = Object.class.getMethods()[0];
    109         }
    110         private final String name;
    111 
    112         public MissingFrameworkMethod(String name) {
    113             super(DUMMY_METHOD);
    114             this.name = name;
    115         }
    116 
    117         @Override
    118         public String getName() {
    119             // Overridden to avoid NPE.
    120             return name;
    121         }
    122 
    123         @Override
    124         public void validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors) {
    125             // Overridden to report the method as missing.
    126             errors.add(new AssertionFailedError("Method \"" + name + "\" not found"));
    127         }
    128 
    129         @Override
    130         public Annotation[] getAnnotations() {
    131             // Overridden to avoid NPE.
    132             return NO_ANNOTATIONS;
    133         }
    134 
    135         @Override
    136         public <T extends Annotation> T getAnnotation(Class<T> annotationType) {
    137             // Overridden to avoid NPE.
    138             return null;
    139         }
    140     }
    141 }
    142