• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2019 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 com.android.server.pm.parsing.library;
18 
19 import org.junit.Assume;
20 import org.junit.runner.Description;
21 import org.junit.runner.Runner;
22 import org.junit.runner.notification.RunNotifier;
23 import org.junit.runners.JUnit4;
24 import org.junit.runners.ParentRunner;
25 import org.junit.runners.model.InitializationError;
26 import org.junit.runners.model.Statement;
27 
28 import java.lang.annotation.ElementType;
29 import java.lang.annotation.Retention;
30 import java.lang.annotation.RetentionPolicy;
31 import java.lang.annotation.Target;
32 import java.util.Collections;
33 import java.util.List;
34 
35 /**
36  * Will run a test class, iff a class, specified by name in the {@link OptionalClass} annotation,
37  * exists on the class path.
38  *
39  * <p>It is an {@link InitializationError} if no {@link OptionalClass} annotation is specified on
40  * the class that has {@code @RunWith(OptionalClassRunner.class)}.
41  *
42  * <p>If the named class cannot be found then the test class is reported as having been ignored.
43  */
44 public class OptionalClassRunner extends Runner {
45 
46     private final Runner mDelegate;
47 
OptionalClassRunner(Class<?> testClass)48     public OptionalClassRunner(Class<?> testClass) throws InitializationError {
49         OptionalClass annotation = testClass.getAnnotation(OptionalClass.class);
50         if (annotation == null) {
51             throw new InitializationError(
52                     "No " + OptionalClass.class.getName() + " annotation found on " + testClass);
53         }
54 
55         String className = annotation.value();
56         Runner delegate;
57         try {
58             Class.forName(className);
59             // The class could be found so create a JUnit4 delegate for the class to run.
60             delegate = new JUnit4(testClass);
61         } catch (ClassNotFoundException e) {
62             // The class could not be found so create a Runner delegate that will treat the
63             // test as having failed a test assumption.
64             delegate = new ClassNotFoundRunner(testClass, className);
65         }
66 
67         this.mDelegate = delegate;
68     }
69 
70     @Override
getDescription()71     public Description getDescription() {
72         return mDelegate.getDescription();
73     }
74 
75     @Override
run(RunNotifier notifier)76     public void run(RunNotifier notifier) {
77         mDelegate.run(notifier);
78     }
79 
80     @Retention(RetentionPolicy.RUNTIME)
81     @Target(ElementType.TYPE)
82     public @interface OptionalClass {
value()83         String value();
84     }
85 
86     /**
87      * Emulates a class containing a single test that fails due to an invalid assumption caused by
88      * the missing class.
89      */
90     private static class ClassNotFoundRunner extends ParentRunner<Runner> {
91 
92         private List<Runner> mChildren;
93 
ClassNotFoundRunner(Class<?> testClass, String className)94         ClassNotFoundRunner(Class<?> testClass, String className)
95                 throws InitializationError {
96             super(testClass);
97             this.mChildren = Collections.singletonList(new ChildRunner(testClass, className));
98         }
99 
100         @Override
getChildren()101         protected List<Runner> getChildren() {
102             return mChildren;
103         }
104 
105         @Override
describeChild(Runner child)106         protected Description describeChild(Runner child) {
107             return child.getDescription();
108         }
109 
110         @Override
runChild(Runner child, RunNotifier notifier)111         protected void runChild(Runner child, RunNotifier notifier) {
112             child.run(notifier);
113         }
114 
115         private class ChildRunner extends Runner {
116 
117             private final Class<?> mTestClass;
118 
119             private final String mClassName;
120 
ChildRunner(Class<?> testClass, String className)121             ChildRunner(Class<?> testClass, String className) {
122                 this.mTestClass = testClass;
123                 this.mClassName = className;
124             }
125 
126             @Override
getDescription()127             public Description getDescription() {
128                 return Description.createTestDescription(mTestClass, "classNotFound");
129             }
130 
131             @Override
run(RunNotifier notifier)132             public void run(RunNotifier notifier) {
133                 runLeaf(new Statement() {
134                     @Override
135                     public void evaluate() throws Throwable {
136                         Assume.assumeTrue("Could not find class: " + mClassName, false);
137                     }
138                 }, getDescription(), notifier);
139             }
140         }
141     }
142 }
143