Skip to content

Introduce flags to allow filtering of targets by rule name #7

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,29 @@ public static Predicate<Target> tagFilter(List<String> tagFilterList) {
};
}

/**
* Returns a predicate to be used for test rule name filtering, i.e., that only accepts tests that match
* a required rule name and not an excluded rule name.
*/
public static Predicate<Target> ruleFilter(List<String> ruleFilterList) {
Pair<Collection<String>, Collection<String>> ruleLists =
TestTargetUtils.sortTagsBySense(ruleFilterList);
final Collection<String> requiredRules = ruleLists.first;
final Collection<String> excludedRules = ruleLists.second;
return input -> {
if (requiredRules.isEmpty() && excludedRules.isEmpty()) {
return true;
}

if (!(input instanceof Rule)) {
return requiredRules.isEmpty();
}

return TestTargetUtils.testMatchesRuleFilters(
((Rule) input).getRuleClass(), requiredRules, excludedRules);
};
}

/** Return {@link Location} for {@link Target} target, if it should not be null. */
@Nullable
public static Location getLocationMaybe(Target target) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,18 @@ private static boolean testMatchesFilters(
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.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,35 @@ public class LoadingOptions extends OptionsBase {
)
public List<String> testLangFilterList;

@Option(
name = "build_rule_filters",
converter = CommaSeparatedOptionListConverter.class,
defaultValue = "",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help =
"Specifies a comma-separated list of rule names. Each rule name can be optionally "
+ "preceded with '-' to specify excluded rule names. Only those targets will be built that "
+ "equal the positive rule name or do not equal the negative rule name. This option "
+ "does not affect the set of tests executed with the 'test' command; those are be "
+ "governed by the test filtering options, for example '--test_rule_filters'"
)
public List<String> buildRuleFilterList;

@Option(
name = "test_rule_filters",
converter = CommaSeparatedOptionListConverter.class,
defaultValue = "",
documentationCategory = OptionDocumentationCategory.UNCATEGORIZED,
effectTags = {OptionEffectTag.UNKNOWN},
help =
"Specifies a comma-separated list of rule names. Each rule name can be optionally "
+ "preceded with '-' to specify excluded rule names. Only those test targets will be "
+ "found that equal the positive rule name or do not equal the negative rule name."
+ "This option affects --build_tests_only behavior and the test command."
)
public List<String> testRuleFilterList;

@Option(
name = "build_manual_tests",
defaultValue = "false",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,25 +46,29 @@ public static TestFilter forOptions(LoadingOptions options) {
ImmutableSet.copyOf(options.testSizeFilterSet),
ImmutableSet.copyOf(options.testTimeoutFilterSet),
ImmutableList.copyOf(options.testTagFilterList),
ImmutableList.copyOf(options.testLangFilterList));
ImmutableList.copyOf(options.testLangFilterList),
ImmutableList.copyOf(options.testRuleFilterList));
}

private final ImmutableSet<TestSize> testSizeFilterSet;
private final ImmutableSet<TestTimeout> testTimeoutFilterSet;
private final ImmutableList<String> testTagFilterList;
private final ImmutableList<String> testLangFilterList;
private final ImmutableList<String> testRuleFilterList;
private final Predicate<Target> impl;

@VisibleForSerialization
TestFilter(
ImmutableSet<TestSize> testSizeFilterSet,
ImmutableSet<TestTimeout> testTimeoutFilterSet,
ImmutableList<String> testTagFilterList,
ImmutableList<String> testLangFilterList) {
ImmutableList<String> testLangFilterList,
ImmutableList<String> testRuleFilterList) {
this.testSizeFilterSet = testSizeFilterSet;
this.testTimeoutFilterSet = testTimeoutFilterSet;
this.testTagFilterList = testTagFilterList;
this.testLangFilterList = testLangFilterList;
this.testRuleFilterList = testRuleFilterList;
Predicate<Target> testFilter = ALWAYS_TRUE;
if (!testSizeFilterSet.isEmpty()) {
testFilter = testFilter.and(testSizeFilter(testSizeFilterSet));
Expand All @@ -78,6 +82,9 @@ public static TestFilter forOptions(LoadingOptions options) {
if (!testLangFilterList.isEmpty()) {
testFilter = testFilter.and(testLangFilter(testLangFilterList));
}
if (!testRuleFilterList.isEmpty()) {
testFilter = testFilter.and(testRuleFilter(testRuleFilterList));
}
impl = testFilter;
}

Expand All @@ -89,7 +96,7 @@ public boolean apply(@Nullable Target input) {
@Override
public int hashCode() {
return Objects.hash(testSizeFilterSet, testTimeoutFilterSet, testTagFilterList,
testLangFilterList);
testLangFilterList, testRuleFilterList);
}

@Override
Expand All @@ -103,7 +110,8 @@ public boolean equals(Object o) {
return f.testSizeFilterSet.equals(testSizeFilterSet)
&& f.testTimeoutFilterSet.equals(testTimeoutFilterSet)
&& f.testTagFilterList.equals(testTagFilterList)
&& f.testLangFilterList.equals(testLangFilterList);
&& f.testLangFilterList.equals(testLangFilterList)
&& f.testRuleFilterList.equals(testRuleFilterList);
}

@Override
Expand All @@ -113,6 +121,7 @@ public String toString() {
.add("testTimeoutFilterSet", testTimeoutFilterSet)
.add("testTagFilterList", testTagFilterList)
.add("testLangFilterList", testLangFilterList)
.add("testRuleFilterList", testRuleFilterList)
.toString();
}

Expand Down Expand Up @@ -159,4 +168,28 @@ private static Predicate<Target> testLangFilter(List<String> langFilterList) {
&& !excludedLangs.contains(ruleLang);
};
}

/**
* Returns a predicate to be used for test rule filtering, i.e., that only accepts tests of
* the specified rule names.
*/
private static Predicate<Target> testRuleFilter(List<String> ruleFilterList) {
final Set<String> requiredRules = new HashSet<>();
final Set<String> excludedRules = new HashSet<>();

for (String ruleFilter : ruleFilterList) {
if (ruleFilter.startsWith("-")) {
ruleFilter = ruleFilter.substring(1);
excludedRules.add(ruleFilter);
} else {
requiredRules.add(ruleFilter);
}
}

return rule -> {
String ruleName = rule.getRuleClass();
return (requiredRules.isEmpty() || requiredRules.contains(ruleName))
&& !excludedRules.contains(ruleName);
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3002,6 +3002,7 @@ public TargetPatternPhaseValue loadTargetPatternsWithFilters(
options.buildTestsOnly,
determineTests,
ImmutableList.copyOf(options.buildTagFilterList),
ImmutableList.copyOf(options.buildRuleFilterList),
options.buildManualTests,
options.expandTestSuites,
TestFilter.forOptions(options));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -367,8 +367,10 @@ private static ResolvedTargets<Target> mergeAll(
}
}

ResolvedTargets<Target> result =
builder.filter(TargetUtils.tagFilter(options.getBuildTargetFilter())).build();
builder.filter(TargetUtils.tagFilter(options.getBuildTargetFilter()));
builder.filter(TargetUtils.ruleFilter(options.getBuildRuleFilter()));

ResolvedTargets<Target> result = builder.build();
if (options.getCompileOneDependency()) {
EnvironmentBackedRecursivePackageProvider environmentBackedRecursivePackageProvider =
new EnvironmentBackedRecursivePackageProvider(env);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ public static TargetPatternPhaseKey key(
boolean buildTestsOnly,
boolean determineTests,
ImmutableList<String> buildTargetFilter,
ImmutableList<String> buildRuleFilter,
boolean buildManualTests,
boolean expandTestSuites,
@Nullable TestFilter testFilter) {
Expand All @@ -165,6 +166,7 @@ public static TargetPatternPhaseKey key(
buildTestsOnly,
determineTests,
buildTargetFilter,
buildRuleFilter,
buildManualTests,
expandTestSuites,
testFilter);
Expand All @@ -181,7 +183,7 @@ public static TargetPatternPhaseKey key(
public static SkyKey keyWithoutFilters(
ImmutableList<String> targetPatterns, PathFragment offset) {
return new TargetPatternPhaseKey(
targetPatterns, offset, false, false, false, ImmutableList.of(), false, false, null);
targetPatterns, offset, false, false, false, ImmutableList.of(), ImmutableList.of(), false, false, null);
}

/** The configuration needed to run the target pattern evaluation phase. */
Expand All @@ -194,6 +196,7 @@ static final class TargetPatternPhaseKey implements SkyKey {
private final boolean buildTestsOnly;
private final boolean determineTests;
private final ImmutableList<String> buildTargetFilter;
private final ImmutableList<String> buildRuleFilter;
private final boolean buildManualTests;
private final boolean expandTestSuites;
@Nullable private final TestFilter testFilter;
Expand All @@ -205,6 +208,7 @@ private TargetPatternPhaseKey(
boolean buildTestsOnly,
boolean determineTests,
ImmutableList<String> buildTargetFilter,
ImmutableList<String> buildRuleFilter,
boolean buildManualTests,
boolean expandTestSuites,
@Nullable TestFilter testFilter) {
Expand All @@ -214,6 +218,7 @@ private TargetPatternPhaseKey(
this.buildTestsOnly = buildTestsOnly;
this.determineTests = determineTests;
this.buildTargetFilter = Preconditions.checkNotNull(buildTargetFilter);
this.buildRuleFilter = Preconditions.checkNotNull(buildRuleFilter);
this.buildManualTests = buildManualTests;
this.expandTestSuites = expandTestSuites;
this.testFilter = testFilter;
Expand Down Expand Up @@ -251,6 +256,10 @@ public ImmutableList<String> getBuildTargetFilter() {
return buildTargetFilter;
}

public ImmutableList<String> getBuildRuleFilter() {
return buildRuleFilter;
}

public boolean getBuildManualTests() {
return buildManualTests;
}
Expand Down Expand Up @@ -305,6 +314,7 @@ public boolean equals(Object obj) {
&& other.buildTestsOnly == buildTestsOnly
&& other.determineTests == determineTests
&& other.buildTargetFilter.equals(buildTargetFilter)
&& other.buildRuleFilter.equals(buildRuleFilter)
&& other.buildManualTests == buildManualTests
&& other.expandTestSuites == expandTestSuites
&& Objects.equals(other.testFilter, testFilter);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,48 @@ public void testFilterByTag() throws Exception {
assertThat(tagFilter.apply(tag1b)).isFalse();
}

@Test
public void testFilterByRuleName() throws Exception {
scratch.file(
"tests/BUILD",
"""
load("//test_defs:foo_binary.bzl", "foo_binary")
load("//test_defs:foo_test.bzl", "foo_test")
foo_binary(
name = "foo_binary",
)

foo_test(
name = "foo_test",
)
""");

Target fooBinary = getTarget("//tests:foo_binary");
Target fooTest = getTarget("//tests:foo_test");

Predicate<Target> ruleFilter = TargetUtils.ruleFilter(Lists.<String>newArrayList());
assertThat(ruleFilter.apply(fooBinary)).isTrue();
assertThat(ruleFilter.apply(fooTest)).isTrue();
ruleFilter = TargetUtils.ruleFilter(Lists.newArrayList("foo_binary", "foo_test"));
assertThat(ruleFilter.apply(fooBinary)).isTrue();
assertThat(ruleFilter.apply(fooTest)).isTrue();
ruleFilter = TargetUtils.ruleFilter(Lists.newArrayList("foo_binary"));
assertThat(ruleFilter.apply(fooBinary)).isTrue();
assertThat(ruleFilter.apply(fooTest)).isFalse();
ruleFilter = TargetUtils.ruleFilter(Lists.newArrayList("-foo_test"));
assertThat(ruleFilter.apply(fooBinary)).isTrue();
assertThat(ruleFilter.apply(fooTest)).isFalse();
// Applying same tag as positive and negative filter produces an empty
// result because the negative filter is applied first and positive filter will
// not match anything.
ruleFilter = TargetUtils.ruleFilter(Lists.newArrayList("foo_test", "-foo_test"));
assertThat(ruleFilter.apply(fooBinary)).isFalse();
assertThat(ruleFilter.apply(fooTest)).isFalse();
ruleFilter = TargetUtils.ruleFilter(Lists.newArrayList("foo_test", "-foo_binary"));
assertThat(ruleFilter.apply(fooBinary)).isFalse();
assertThat(ruleFilter.apply(fooTest)).isTrue();
}

@Test
public void testExecutionInfo() throws Exception {
scratch.file(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,34 @@ public void testFilterBySize() {
public void testFilterByLang() {
LoadingOptions options = new LoadingOptions();
options.testLangFilterList = ImmutableList.of("positive", "-negative");
options.testRuleFilterList = ImmutableList.of();
options.testSizeFilterSet = ImmutableSet.of();
options.testTimeoutFilterSet = ImmutableSet.of();
options.testTagFilterList = ImmutableList.of();
TestFilter filter = TestFilter.forOptions(options);
Package pkg = mock(Package.class);
RuleClass ruleClass = mock(RuleClass.class);
when(ruleClass.getDefaultImplicitOutputsFunction())
.thenReturn(SafeImplicitOutputsFunction.NONE);
when(ruleClass.getAttributeProvider()).thenReturn(mock(AttributeProvider.class));
Rule mockRule =
new Rule(
pkg,
Label.parseCanonicalUnchecked("//pkg:a"),
ruleClass,
Location.fromFile(""),
/* interiorCallStack= */ null);
when(ruleClass.getName()).thenReturn("positive_test");
assertThat(filter.apply(mockRule)).isTrue();
when(ruleClass.getName()).thenReturn("negative_test");
assertThat(filter.apply(mockRule)).isFalse();
}

@Test
public void testFilterByRule() {
LoadingOptions options = new LoadingOptions();
options.testLangFilterList = ImmutableList.of();
options.testRuleFilterList = ImmutableList.of("positive_test", "-negative_test");
options.testSizeFilterSet = ImmutableSet.of();
options.testTimeoutFilterSet = ImmutableSet.of();
options.testTagFilterList = ImmutableList.of();
Expand Down
Loading