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 libcore.junit.util.compat;
18 
19 import android.compat.Compatibility;
20 import android.compat.Compatibility.Callbacks;
21 import android.compat.Compatibility.ChangeConfig;
22 
23 import org.junit.rules.TestRule;
24 import org.junit.runner.Description;
25 import org.junit.runners.model.Statement;
26 
27 import java.lang.annotation.ElementType;
28 import java.lang.annotation.Retention;
29 import java.lang.annotation.RetentionPolicy;
30 import java.lang.annotation.Target;
31 import java.util.HashSet;
32 import java.util.Set;
33 import com.google.common.primitives.Longs;
34 
35 /**
36  * Allows tests to specify the which change to disable.
37  *
38  * <p>To use add the following to the test class. It will only change the behavior of a test method
39  * if it is annotated with {@link EnableCompatChanges} and/or {@link DisableCompatChanges}.
40  *
41  * <pre>
42  * &#64;Rule
43  * public TestRule compatChangeRule = new CoreCompatChangeRule();
44  * </pre>
45  *
46  * <p>Each test method that needs to disable a specific change needs to be annotated
47  * with {@link EnableCompatChanges} and/or {@link DisableCompatChanges} specifying the change id.
48  * e.g.:
49  *
50  * <pre>
51  *   &#64;Test
52  *   &#64;DisableCompatChanges({42})
53  *   public void testAsIfChange42Disabled() {
54  *     // check behavior
55  *   }
56  *
57  *   &#64;Test
58  *   &#64;EnableCompatChanges({42})
59  *   public void testAsIfChange42Enabled() {
60  *     // check behavior
61  *
62  * </pre>
63  */
64 public class CoreCompatChangeRule implements TestRule {
65 
66     @Override
apply(final Statement statement, Description description)67     public Statement apply(final Statement statement, Description description) {
68         Set<Long> enabled = new HashSet<>();
69         Set<Long> disabled = new HashSet<>();
70         EnableCompatChanges enableCompatChanges = description.getAnnotation(
71                 EnableCompatChanges.class);
72         DisableCompatChanges disableCompatChanges = description.getAnnotation(
73                 DisableCompatChanges.class);
74         if (enableCompatChanges != null) {
75             enabled.addAll(Longs.asList(enableCompatChanges.value()));
76         }
77         if (disableCompatChanges != null) {
78             disabled.addAll(Longs.asList(disableCompatChanges.value()));
79         }
80         ChangeConfig config = new ChangeConfig(enabled, disabled);
81         if (config.isEmpty()) {
82             return statement;
83         } else {
84             return createStatementForConfig(statement, config);
85         }
86     }
87 
createStatementForConfig(final Statement statement, ChangeConfig config)88     protected Statement createStatementForConfig(final Statement statement, ChangeConfig config) {
89             return new CompatChangeStatement(statement, config);
90     }
91 
92     private static class CompatChangeStatement extends Statement {
93         private final Statement testStatement;
94         private final ChangeConfig config;
95 
CompatChangeStatement(Statement testStatement, ChangeConfig config)96         private CompatChangeStatement(Statement testStatement, ChangeConfig config) {
97             this.testStatement = testStatement;
98             this.config = config;
99         }
100 
101         @Override
evaluate()102         public void evaluate() throws Throwable {
103             Compatibility.setOverrides(config);
104             try {
105                 testStatement.evaluate();
106             } finally {
107                 Compatibility.clearOverrides();
108             }
109         }
110     }
111 
112     @Retention(RetentionPolicy.RUNTIME)
113     @Target(ElementType.METHOD)
114     public @interface EnableCompatChanges {
value()115         long[] value();
116     }
117 
118     @Retention(RetentionPolicy.RUNTIME)
119     @Target(ElementType.METHOD)
120     public @interface DisableCompatChanges {
value()121         long[] value();
122     }
123 }
124