blob: 86672c9310ac01848b2a16e424efc00e21a87c4d [file] [log] [blame]
// Copyright 2018 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.exec;
import static com.google.common.truth.Truth.assertThat;
import static com.google.devtools.build.lib.testutil.MoreAsserts.assertThrows;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.devtools.build.lib.actions.ActionContext;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.DynamicStrategyRegistry.DynamicMode;
import com.google.devtools.build.lib.actions.ExecutorInitException;
import com.google.devtools.build.lib.actions.ResourceSet;
import com.google.devtools.build.lib.actions.SandboxedSpawnStrategy;
import com.google.devtools.build.lib.actions.SimpleSpawn;
import com.google.devtools.build.lib.actions.Spawn;
import com.google.devtools.build.lib.actions.SpawnResult;
import com.google.devtools.build.lib.actions.SpawnStrategy;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.events.Event;
import com.google.devtools.build.lib.exec.util.FakeOwner;
import com.google.devtools.build.lib.util.RegexFilter;
import javax.annotation.Nullable;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/** Unit tests for SpawnStrategyRegistry. */
@RunWith(JUnit4.class)
public class SpawnStrategyRegistryTest {
private static final RegexFilter ELLO_MATCHER =
new RegexFilter(ImmutableList.of("ello"), ImmutableList.of());
private static void noopEventHandler(Event event) {}
@Test
public void testRegistration() throws Exception {
NoopStrategy strategy = new NoopStrategy("");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy, "foo")
.setDefaultStrategies(ImmutableList.of("foo"))
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("", ""),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy);
}
@Test
public void testMnemonicFilter() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.addMnemonicFilter("mnem", ImmutableList.of("bar", "foo"))
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("mnem", ""),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy2, strategy1);
}
@Test
public void testLaterStrategyOverridesEarlier() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "foo")
.addMnemonicFilter("mnem", ImmutableList.of("foo"))
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("mnem", ""),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy2);
}
@Test
public void testDescriptionFilter() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.addDescriptionFilter(ELLO_MATCHER, ImmutableList.of("bar", "foo"))
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("", "hello"),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy2, strategy1);
}
@Test
public void testFilterNoMatch() throws Exception {
NoopStrategy strategy = new NoopStrategy("");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy, "foo")
.addMnemonicFilter("mnem", ImmutableList.of("foo"))
.setDefaultStrategies(ImmutableList.of())
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("other", ""),
SpawnStrategyRegistryTest::noopEventHandler))
.isEmpty();
}
@Test
public void testDescriptionHasPrecedenceOverMnemonic() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.addMnemonicFilter("mnem", ImmutableList.of("foo"))
.addDescriptionFilter(ELLO_MATCHER, ImmutableList.of("bar"))
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("mnem", "hello"),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy2);
}
@Test
public void testMultipleMnemonicFilter() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.addMnemonicFilter("mnem", ImmutableList.of("foo"))
.addMnemonicFilter("mnem", ImmutableList.of("bar"))
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("mnem", ""),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy2);
}
@Test
public void testMultipleDescriptionFilter() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.addDescriptionFilter(ELLO_MATCHER, ImmutableList.of("foo"))
.addDescriptionFilter(
new RegexFilter(ImmutableList.of("ll"), ImmutableList.of()),
ImmutableList.of("bar"))
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("", "hello"),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy2);
}
@Test
public void testMultipleDescriptionFilterLegacy() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.addDescriptionFilter(ELLO_MATCHER, ImmutableList.of("foo"))
.addDescriptionFilter(
new RegexFilter(ImmutableList.of("ll"), ImmutableList.of()),
ImmutableList.of("bar"))
.useLegacyDescriptionFilterPrecedence()
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("", "hello"),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy1);
}
@Test
public void testMultipleDefaultStrategies() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
NoopStrategy strategy3 = new NoopStrategy("3");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.registerStrategy(strategy3, "baz")
.setDefaultStrategies(ImmutableList.of("foo", "baz"))
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("", ""),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy1, strategy3);
}
@Test
public void testDefaultStrategiesIndependentOfFilters() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
NoopStrategy strategy3 = new NoopStrategy("3");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.registerStrategy(strategy3, "baz")
.addMnemonicFilter("mnem", ImmutableList.of("bar"))
.setDefaultStrategies(ImmutableList.of("foo", "baz"))
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("", ""),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy1, strategy3);
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("mnem", ""),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy2);
}
@Test
public void testImplicitDefault() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("", ""),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy1, strategy2);
}
@Test
public void testImplicitDefaultWithDuplicateIdentifiers() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
NoopStrategy strategy3 = new NoopStrategy("3");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.registerStrategy(strategy3, "foo")
.build();
assertThat(
strategyRegistry.getStrategies(
createSpawnWithMnemonicAndDescription("", ""),
SpawnStrategyRegistryTest::noopEventHandler))
.containsExactly(strategy1, strategy2, strategy3);
}
@Test
public void testMnemonicStrategyNotPresent() {
NoopStrategy strategy1 = new NoopStrategy("1");
ExecutorInitException exception =
assertThrows(
ExecutorInitException.class,
() ->
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.addMnemonicFilter("mnem", ImmutableList.of("bar", "foo"))
.build());
assertThat(exception).hasMessageThat().containsMatch("bar.*Valid.*foo");
}
@Test
public void testDescriptionStrategyNotPresent() {
NoopStrategy strategy1 = new NoopStrategy("1");
ExecutorInitException exception =
assertThrows(
ExecutorInitException.class,
() ->
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.addDescriptionFilter(ELLO_MATCHER, ImmutableList.of("bar", "foo"))
.build());
assertThat(exception).hasMessageThat().containsMatch("bar.*Valid.*foo");
}
@Test
public void testDefaultStrategyNotPresent() {
NoopStrategy strategy1 = new NoopStrategy("1");
ExecutorInitException exception =
assertThrows(
ExecutorInitException.class,
() ->
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.setDefaultStrategies(ImmutableList.of("bar"))
.build());
assertThat(exception).hasMessageThat().containsMatch("bar.*Valid.*foo");
}
@Test
public void testDynamicStrategies() throws Exception {
NoopStrategy strategy1 = new NoopSandboxedStrategy("1");
NoopStrategy strategy2 = new NoopSandboxedStrategy("2");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.addDynamicLocalStrategiesByMnemonic("mnem", ImmutableList.of("bar"))
.addDynamicRemoteStrategiesByMnemonic("mnem", ImmutableList.of("foo"))
.build();
assertThat(
strategyRegistry.getDynamicSpawnActionContexts(
createSpawnWithMnemonicAndDescription("mnem", ""), DynamicMode.REMOTE))
.containsExactly(strategy1);
assertThat(
strategyRegistry.getDynamicSpawnActionContexts(
createSpawnWithMnemonicAndDescription("mnem", ""), DynamicMode.LOCAL))
.containsExactly(strategy2);
}
@Test
public void testDynamicStrategyNotPresent() {
NoopStrategy strategy1 = new NoopSandboxedStrategy("1");
ExecutorInitException exception =
assertThrows(
ExecutorInitException.class,
() ->
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.addDynamicLocalStrategiesByMnemonic("mnem", ImmutableList.of("bar"))
.build());
assertThat(exception).hasMessageThat().containsMatch("bar.*Valid.*foo");
}
@Test
public void testDynamicStrategyNotSandboxed() {
NoopStrategy strategy1 = new NoopStrategy("1");
ExecutorInitException exception =
assertThrows(
ExecutorInitException.class,
() ->
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.addDynamicLocalStrategiesByMnemonic("mnem", ImmutableList.of("foo"))
.build());
assertThat(exception).hasMessageThat().containsMatch("sandboxed strategy");
}
@Test
public void testRemoteLocalFallback() throws Exception {
NoopAbstractStrategy strategy1 = new NoopAbstractStrategy("1");
NoopAbstractStrategy strategy2 = new NoopAbstractStrategy("2");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.registerStrategy(strategy2, "bar")
.setRemoteLocalFallbackStrategyIdentifier("bar")
.build();
assertThat(strategyRegistry.getRemoteLocalFallbackStrategy()).isEqualTo(strategy2);
}
@Test
public void testRemoteLocalFallbackNotPresent() {
NoopStrategy strategy1 = new NoopStrategy("1");
ExecutorInitException exception =
assertThrows(
ExecutorInitException.class,
() ->
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "foo")
.setRemoteLocalFallbackStrategyIdentifier("bar")
.build());
assertThat(exception).hasMessageThat().containsMatch("bar.*Valid.*foo");
}
@Test
public void testRemoteLocalFallbackNotRegistered() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder().registerStrategy(strategy1, "foo").build();
assertThat(strategyRegistry.getRemoteLocalFallbackStrategy()).isNull();
}
@Test
public void testNotifyUsed() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
NoopStrategy strategy3 = new NoopStrategy("3");
NoopAbstractStrategy strategy4 = new NoopAbstractStrategy("4");
NoopStrategy strategy5 = new NoopSandboxedStrategy("5");
NoopStrategy strategy6 = new NoopSandboxedStrategy("6");
NoopStrategy strategy7 = new NoopStrategy("7");
NoopStrategy strategy8 = new NoopStrategy("8");
NoopStrategy strategy9 = new NoopStrategy("9");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "1")
.registerStrategy(strategy2, "2")
.registerStrategy(strategy3, "3")
.registerStrategy(strategy7, "4") // no notification: identifier is overridden
.registerStrategy(strategy4, "4")
.registerStrategy(strategy5, "5") // no notification: dynamic strategies are separate
.registerStrategy(strategy6, "6") // no notification: dynamic strategies are separate
.registerStrategy(strategy8, "8") // no notification: never referenced
.registerStrategy(strategy9, "9") // no notification: reference overridden
.addMnemonicFilter("mnem", ImmutableList.of("1"))
.addDescriptionFilter(ELLO_MATCHER, ImmutableList.of("2"))
.setDefaultStrategies(ImmutableList.of("9"))
.setDefaultStrategies(ImmutableList.of("3"))
.setRemoteLocalFallbackStrategyIdentifier("4")
.addDynamicLocalStrategiesByMnemonic("oy", ImmutableList.of("5"))
.addDynamicRemoteStrategiesByMnemonic("oy", ImmutableList.of("6"))
.build();
strategyRegistry.notifyUsed(null);
assertThat(strategy1.usedCalled).isEqualTo(1);
assertThat(strategy2.usedCalled).isEqualTo(1);
assertThat(strategy3.usedCalled).isEqualTo(1);
assertThat(strategy4.usedCalled).isEqualTo(1);
assertThat(strategy5.usedCalled).isEqualTo(0);
assertThat(strategy6.usedCalled).isEqualTo(0);
assertThat(strategy7.usedCalled).isEqualTo(0);
assertThat(strategy8.usedCalled).isEqualTo(0);
assertThat(strategy9.usedCalled).isEqualTo(0);
}
@Test
public void testNotifyUsedDynamic() throws Exception {
NoopStrategy strategy1 = new NoopStrategy("1");
NoopStrategy strategy2 = new NoopStrategy("2");
NoopStrategy strategy3 = new NoopStrategy("3");
NoopAbstractStrategy strategy4 = new NoopAbstractStrategy("4");
NoopStrategy strategy5 = new NoopSandboxedStrategy("5");
NoopStrategy strategy6 = new NoopSandboxedStrategy("6");
NoopStrategy strategy7 = new NoopStrategy("7");
SpawnStrategyRegistry strategyRegistry =
SpawnStrategyRegistry.builder()
.registerStrategy(strategy1, "1") // no notification: regular strategies are separate
.registerStrategy(strategy2, "2") // no notification: regular strategies are separate
.registerStrategy(strategy3, "3") // no notification: regular strategies are separate
.registerStrategy(strategy4, "4") // no notification: regular strategies are separate
.registerStrategy(strategy5, "5")
.registerStrategy(strategy6, "6")
.registerStrategy(strategy7, "7") // no notification: reference overridden
.addMnemonicFilter("mnem", ImmutableList.of("1"))
.addDescriptionFilter(ELLO_MATCHER, ImmutableList.of("2"))
.setDefaultStrategies(ImmutableList.of("3"))
.setRemoteLocalFallbackStrategyIdentifier("4")
.addDynamicLocalStrategiesByMnemonic("oy", ImmutableList.of("7"))
.addDynamicLocalStrategiesByMnemonic("oy", ImmutableList.of("5"))
.addDynamicRemoteStrategiesByMnemonic("oy", ImmutableList.of("6"))
.build();
strategyRegistry.notifyUsedDynamic(null);
assertThat(strategy1.usedCalled).isEqualTo(0);
assertThat(strategy2.usedCalled).isEqualTo(0);
assertThat(strategy3.usedCalled).isEqualTo(0);
assertThat(strategy4.usedCalled).isEqualTo(0);
assertThat(strategy5.usedCalled).isEqualTo(1);
assertThat(strategy6.usedCalled).isEqualTo(1);
assertThat(strategy7.usedCalled).isEqualTo(0);
}
private Spawn createSpawnWithMnemonicAndDescription(String mnemonic, String description) {
return new SimpleSpawn(
new FakeOwner(mnemonic, description),
ImmutableList.of(),
ImmutableMap.of(),
ImmutableMap.of(),
NestedSetBuilder.emptySet(Order.STABLE_ORDER),
ImmutableSet.of(),
ResourceSet.ZERO);
}
private static class NoopStrategy implements SpawnStrategy {
private final String name;
private int usedCalled = 0;
private NoopStrategy(String name) {
this.name = name;
}
@Override
public ImmutableList<SpawnResult> exec(
Spawn spawn, ActionExecutionContext actionExecutionContext) {
throw new UnsupportedOperationException();
}
@Override
public boolean canExec(Spawn spawn, ActionContext.ActionContextRegistry actionContextRegistry) {
return false;
}
@Override
public void usedContext(ActionContext.ActionContextRegistry actionContextRegistry) {
usedCalled++;
}
@Override
public String toString() {
return "strategy" + name;
}
}
private static class NoopSandboxedStrategy extends NoopStrategy
implements SandboxedSpawnStrategy {
private NoopSandboxedStrategy(String name) {
super(name);
}
@Override
public ImmutableList<SpawnResult> exec(
Spawn spawn,
ActionExecutionContext actionExecutionContext,
@Nullable SandboxedSpawnStrategy.StopConcurrentSpawns stopConcurrentSpawns) {
throw new UnsupportedOperationException();
}
}
private static class NoopAbstractStrategy extends AbstractSpawnStrategy {
private final String name;
private int usedCalled = 0;
public NoopAbstractStrategy(String name) {
super(null, null);
this.name = name;
}
@Override
public void usedContext(ActionContext.ActionContextRegistry actionContextRegistry) {
usedCalled++;
}
@Override
public String toString() {
return "strategy" + name;
}
}
}