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