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") VogarBlockJUnit4ClassRunner(Class<?> klass, RunnerBuilder suiteBuilder)51 public VogarBlockJUnit4ClassRunner(Class<?> klass, RunnerBuilder suiteBuilder) 52 throws InitializationError { 53 this(klass, ((VogarRunnerBuilder) suiteBuilder).getRunnerParams()); 54 } 55 VogarBlockJUnit4ClassRunner(Class<?> klass, RunnerParams runnerParams)56 public VogarBlockJUnit4ClassRunner(Class<?> klass, RunnerParams runnerParams) 57 throws InitializationError { 58 super(klass, runnerParams.getTestRule()); 59 this.runnerParams = runnerParams; 60 } 61 62 @Override getChildren()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 MissingFrameworkMethod(String name)112 public MissingFrameworkMethod(String name) { 113 super(DUMMY_METHOD); 114 this.name = name; 115 } 116 117 @Override getName()118 public String getName() { 119 // Overridden to avoid NPE. 120 return name; 121 } 122 123 @Override validatePublicVoidNoArg(boolean isStatic, List<Throwable> errors)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 getAnnotations()130 public Annotation[] getAnnotations() { 131 // Overridden to avoid NPE. 132 return NO_ANNOTATIONS; 133 } 134 135 @Override getAnnotation(Class<T> annotationType)136 public <T extends Annotation> T getAnnotation(Class<T> annotationType) { 137 // Overridden to avoid NPE. 138 return null; 139 } 140 } 141 } 142