• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 package com.android.cts.core.runner;
17 
18 import android.os.Bundle;
19 import android.util.Log;
20 import com.google.common.base.Splitter;
21 import java.io.IOException;
22 import java.util.LinkedHashSet;
23 import java.util.List;
24 import java.util.Set;
25 import javax.annotation.Nullable;
26 import org.junit.runner.Description;
27 import org.junit.runner.manipulation.Filter;
28 import org.junit.runners.ParentRunner;
29 import org.junit.runners.Suite;
30 import vogar.Expectation;
31 import vogar.ExpectationStore;
32 import vogar.ModeId;
33 import vogar.Result;
34 
35 /**
36  * Filter out tests/classes that are not requested or which are expected to fail.
37  *
38  * <p>This filter has to handle both a hierarchy of {@code Description descriptions} that looks
39  * something like this:
40  * <pre>
41  * Suite
42  *     Suite
43  *         Suite
44  *             ParentRunner
45  *                 Test
46  *                 ...
47  *             ...
48  *         ParentRunner
49  *             Test
50  *             ...
51  *         ...
52  *     Suite
53  *         ParentRunner
54  *             Test
55  *             ...
56  *         ...
57  *     ...
58  * </pre>
59  *
60  * <p>It cannot filter out the non-leaf nodes in the hierarchy, i.e. {@link Suite} and
61  * {@link ParentRunner}, as that would prevent it from traversing the hierarchy and finding
62  * the leaf nodes.
63  */
64 class ExpectationBasedFilter extends Filter {
65 
66     static final String TAG = "ExpectationBasedFilter";
67 
68     private static final String ARGUMENT_EXPECTATIONS = "core-expectations";
69 
70     private static final Splitter CLASS_LIST_SPLITTER = Splitter.on(',').trimResults();
71 
72     private final ExpectationStore expectationStore;
73 
getExpectationResourcePaths(Bundle args)74     private static List<String> getExpectationResourcePaths(Bundle args) {
75         return CLASS_LIST_SPLITTER.splitToList(args.getString(ARGUMENT_EXPECTATIONS));
76     }
77 
ExpectationBasedFilter(Bundle args)78     public ExpectationBasedFilter(Bundle args) {
79         ExpectationStore expectationStore = null;
80         try {
81             // Get the set of resource names containing the expectations.
82             Set<String> expectationResources = new LinkedHashSet<>(
83                 getExpectationResourcePaths(args));
84             Log.i(TAG, "Loading expectations from: " + expectationResources);
85             expectationStore = ExpectationStore.parseResources(
86                 getClass(), expectationResources, ModeId.DEVICE);
87         } catch (IOException e) {
88             Log.e(TAG, "Could not initialize ExpectationStore: ", e);
89         }
90 
91         this.expectationStore = expectationStore;
92     }
93 
94     @Override
shouldRun(Description description)95     public boolean shouldRun(Description description) {
96         // Only filter leaf nodes. The description is for a test if and only if it is a leaf node.
97         // Non-leaf nodes must not be filtered out as that would prevent leaf nodes from being
98         // visited in the case when we are traversing the hierarchy of classes.
99         Description testDescription = getTestDescription(description);
100         if (testDescription != null) {
101             String className = testDescription.getClassName();
102             String methodName = testDescription.getMethodName();
103             String testName = className + "#" + methodName;
104 
105             if (expectationStore != null) {
106                 Expectation expectation = expectationStore.get(testName);
107                 if (expectation.getResult() != Result.SUCCESS) {
108                     Log.d(TAG, "Excluding test " + testDescription
109                             + " as it matches expectation: " + expectation);
110                     return false;
111                 }
112             }
113         }
114 
115         return true;
116     }
117 
getTestDescription(Description description)118     private Description getTestDescription(Description description) {
119         List<Description> children = description.getChildren();
120         // An empty description is by definition a test.
121         if (children.isEmpty()) {
122             return description;
123         }
124 
125         // Handle initialization errors that were wrapped in an ErrorReportingRunner as a special
126         // case. This is needed because ErrorReportingRunner is treated as a suite of Throwables,
127         // (where each Throwable corresponds to a test called initializationError) and so its
128         // description contains children, one for each Throwable, and so is not treated as a test
129         // to filter. Unfortunately, it does not support Filterable so this filter is never applied
130         // to its children.
131         // See https://github.com/junit-team/junit/issues/1253
132         Description child = children.get(0);
133         String methodName = child.getMethodName();
134         if ("initializationError".equals(methodName)) {
135             return child;
136         }
137 
138         return null;
139     }
140 
141     @Override
describe()142     public String describe() {
143         return "TestFilter";
144     }
145 }
146