forked from bazelbuild/bazel
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathTestTargetUtils.java
151 lines (141 loc) · 5.7 KB
/
TestTargetUtils.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
// Copyright 2014 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.packages;
import com.google.devtools.build.lib.util.Pair;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
/**
* Utility functions over test Targets that don't really belong in the base {@link Target}
* interface.
*/
public final class TestTargetUtils {
/**
* Returns whether a test with the specified tags matches a filter (as specified by the set
* of its positive and its negative filters).
*/
public static boolean testMatchesFilters(
Collection<String> testTags,
Collection<String> requiredTags,
Collection<String> excludedTags,
boolean mustMatchAllPositive) {
for (String tag : excludedTags) {
if (testTags.contains(tag)) {
return false;
}
}
// Check required tags, if there are any.
if (requiredTags.isEmpty()) {
return true;
} else if (mustMatchAllPositive) {
// Require all tags to be present.
for (String tag : requiredTags) {
if (!testTags.contains(tag)) {
return false;
}
}
return true;
} else {
// Require at least one positive tag. If the two collections are not disjoint, then they have
// at least one element in common.
return !Collections.disjoint(requiredTags, testTags);
}
}
/**
* Decides whether to include a test in a test_suite or not.
* @param testTags Collection of all tags exhibited by a given test.
* @param requiredTags Tags declared by the suite. A Test must match ALL of these.
* @param excludedTags Tags declared by the suite. A Test must match NONE of these.
* @return false is the test is to be removed.
*/
public static boolean testMatchesFilters(
Collection<String> testTags,
Collection<String> requiredTags,
Collection<String> excludedTags) {
return testMatchesFilters(
testTags, requiredTags, excludedTags, /* mustMatchAllPositive= */ true);
}
/**
* Decides whether to include a test in a test_suite or not.
* @param testTarget A given test target.
* @param requiredTags Tags declared by the suite. A Test must match ALL of these.
* @param excludedTags Tags declared by the suite. A Test must match NONE of these.
* @return false is the test is to be removed.
*/
private static boolean testMatchesFilters(
Rule testTarget,
Collection<String> requiredTags,
Collection<String> excludedTags) {
AttributeMap nonConfigurableAttrs = NonconfigurableAttributeMapper.of(testTarget);
Set<String> testTags = new HashSet<>(nonConfigurableAttrs.get("tags", Types.STRING_LIST));
testTags.add(nonConfigurableAttrs.get("size", Type.STRING));
return testMatchesFilters(testTags, requiredTags, excludedTags);
}
/**
* Returns whether a test with a rule name matches a filter (as specified by the set
* of its positive and its negative filters).
*/
public static boolean testMatchesRuleFilters(
String ruleName,
Collection<String> requiredRules,
Collection<String> excludedRules) {
return (requiredRules.isEmpty() || requiredRules.contains(ruleName)) &&
!excludedRules.contains(ruleName);
}
/**
* Filters 'tests' (by mutation) according to the 'tags' attribute, specifically those that
* match ALL of the tags in tagsAttribute.
*
* @precondition {@code env.getAccessor().isTestSuite(testSuite)}
* @precondition {@code env.getAccessor().isTestRule(test)} for all test in tests
*/
public static void filterTests(Rule testSuite, Set<Target> tests) {
List<String> tagsAttribute =
NonconfigurableAttributeMapper.of(testSuite).get("tags", Types.STRING_LIST);
// Split the tags list into positive and negative tags
Pair<Collection<String>, Collection<String>> tagLists = sortTagsBySense(tagsAttribute);
Collection<String> positiveTags = tagLists.first;
Collection<String> negativeTags = tagLists.second;
tests.removeIf((Target t) -> !testMatchesFilters((Rule) t, positiveTags, negativeTags));
}
/**
* Separates a list of text "tags" into a Pair of Collections, where
* the first element are the required or positive tags and the second element
* are the excluded or negative tags.
* This should work on tag list provided from the command line
* --test_tags_filters flag or on tag filters explicitly declared in the
* suite.
*
* @param tagList A collection of text targets to separate.
*/
public static Pair<Collection<String>, Collection<String>> sortTagsBySense(
Iterable<String> tagList) {
Collection<String> requiredTags = new HashSet<>();
Collection<String> excludedTags = new HashSet<>();
for (String tag : tagList) {
if (tag.startsWith("-")) {
excludedTags.add(tag.substring(1));
} else if (tag.startsWith("+")) {
requiredTags.add(tag.substring(1));
} else if (!tag.equals("manual")) {
// Ignore manual attribute because it is an exception: it is not a filter but a property of
// test_suite.
requiredTags.add(tag);
}
}
return Pair.of(requiredTags, excludedTags);
}
}