1 /*
2  * Copyright (C) 2023 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 package android.platform.test.rules;
17 
18 import org.junit.Assume;
19 import org.junit.rules.MethodRule;
20 import org.junit.runners.model.FrameworkMethod;
21 import org.junit.runners.model.Statement;
22 
23 import java.lang.reflect.Modifier;
24 import java.util.function.Supplier;
25 
26 /** A JUnit Rule for ignoring a test based on a condition. */
27 public class ConditionalIgnoreRule implements MethodRule {
28     @Override
apply(Statement base, FrameworkMethod method, Object target)29     public Statement apply(Statement base, FrameworkMethod method, Object target) {
30         if (method.getAnnotation(ConditionalIgnore.class) != null) {
31             ConditionalIgnore annotation = method.getAnnotation(ConditionalIgnore.class);
32             Supplier<Boolean> condition;
33             Class<? extends Supplier<Boolean>> conditionType = annotation.condition();
34             boolean conditionTypeStandalone =
35                     !conditionType.isMemberClass()
36                             || Modifier.isStatic(conditionType.getModifiers());
37             if (!conditionTypeStandalone
38                     && !target.getClass().isAssignableFrom(conditionType.getDeclaringClass())) {
39                 String msg =
40                         "Conditional class '%s' is a member class but was not declared inside the"
41                                 + " test case using it.\n"
42                                 + "Either make this class a static class, standalone class (by"
43                                 + " declaring it in its own file) or move it inside the test case"
44                                 + " using it";
45                 throw new IllegalArgumentException(String.format(msg, conditionType.getName()));
46             }
47             try {
48                 condition =
49                         conditionTypeStandalone
50                                 ? conditionType.getDeclaredConstructor().newInstance()
51                                 : conditionType
52                                         .getDeclaredConstructor(target.getClass())
53                                         .newInstance(target);
54             } catch (RuntimeException re) {
55                 throw re;
56             } catch (Exception e) {
57                 throw new RuntimeException(e);
58             }
59             if (condition.get()) {
60                 return new IgnoreStatement(condition);
61             }
62         }
63         return base;
64     }
65 
66     private static class IgnoreStatement extends Statement {
67         private final Supplier<Boolean> mCondition;
68 
IgnoreStatement(Supplier<Boolean> condition)69         IgnoreStatement(Supplier<Boolean> condition) {
70             this.mCondition = condition;
71         }
72 
73         @Override
evaluate()74         public void evaluate() {
75             Assume.assumeTrue("Ignored by " + mCondition.getClass().getSimpleName(), false);
76         }
77     }
78 }
79