blob: be7436daa232e248b86fc8bade6567895f88be3f [file] [log] [blame]
// Copyright 2021 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.starlark;
import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth8.assertThat;
import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.NULL_ACTION_OWNER;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Sets;
import com.google.devtools.build.lib.actions.Action;
import com.google.devtools.build.lib.actions.ActionExecutionContext;
import com.google.devtools.build.lib.actions.ActionExecutionContext.LostInputsCheck;
import com.google.devtools.build.lib.actions.ActionInputPrefetcher;
import com.google.devtools.build.lib.actions.Artifact;
import com.google.devtools.build.lib.actions.DiscoveredModulesPruner;
import com.google.devtools.build.lib.actions.Executor;
import com.google.devtools.build.lib.actions.ThreadStateReceiver;
import com.google.devtools.build.lib.analysis.actions.StarlarkAction;
import com.google.devtools.build.lib.analysis.util.AnalysisTestUtil;
import com.google.devtools.build.lib.analysis.util.BuildViewTestCase;
import com.google.devtools.build.lib.collect.nestedset.NestedSet;
import com.google.devtools.build.lib.collect.nestedset.NestedSetBuilder;
import com.google.devtools.build.lib.collect.nestedset.Order;
import com.google.devtools.build.lib.exec.BinTools;
import com.google.devtools.build.lib.exec.util.TestExecutorBuilder;
import com.google.devtools.build.lib.vfs.PathFragment;
import com.google.devtools.build.lib.vfs.SyscallCache;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Optional;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
import org.mockito.ArgumentMatchers;
/** Tests for {@link StarlarkAction} using the shadowed action parameter. */
@RunWith(JUnit4.class)
public final class StarlarkActionWithShadowedActionTest extends BuildViewTestCase {
private ActionExecutionContext executionContext;
private AnalysisTestUtil.CollectingAnalysisEnvironment collectingAnalysisEnvironment;
private NestedSet<Artifact> starlarkActionInputs;
private NestedSet<Artifact> shadowedActionInputs;
private NestedSet<Artifact> discoveredInputs;
private Map<String, String> starlarkActionEnvironment;
private Map<String, String> shadowedActionEnvironment;
private Artifact output;
private PathFragment executable;
@Before
public final void createArtifacts() throws Exception {
collectingAnalysisEnvironment =
new AnalysisTestUtil.CollectingAnalysisEnvironment(getTestAnalysisEnvironment());
starlarkActionInputs =
NestedSetBuilder.create(
Order.STABLE_ORDER,
getSourceArtifact("pkg/shadowed_action_inp1"),
getSourceArtifact("pkg/discovered_inp2"),
getSourceArtifact("pkg/starlark_action_inp3"));
shadowedActionInputs =
NestedSetBuilder.create(
Order.STABLE_ORDER,
getSourceArtifact("pkg/shadowed_action_inp1"),
getSourceArtifact("pkg/shadowed_action_inp2"),
getSourceArtifact("pkg/shadowed_action_inp3"));
discoveredInputs =
NestedSetBuilder.create(
Order.STABLE_ORDER,
getSourceArtifact("pkg/shadowed_action_inp1"),
getSourceArtifact("pkg/discovered_inp2"),
getSourceArtifact("pkg/discovered_inp3"));
output = getBinArtifactWithNoOwner("output");
executable = scratch.file("/bin/xxx").asFragment();
starlarkActionEnvironment =
ImmutableMap.of(
"repeated_var", "starlark_val",
"a_var", "a_val",
"b_var", "b_val");
shadowedActionEnvironment =
ImmutableMap.of(
"repeated_var", "shadowed_val",
"c_var", "c_val",
"d_var", "d_val");
}
@Before
public final void createExecutorAndContext() throws Exception {
BinTools binTools = BinTools.forUnitTesting(directories, analysisMock.getEmbeddedTools());
Executor executor = new TestExecutorBuilder(fileSystem, directories, binTools).build();
executionContext =
new ActionExecutionContext(
executor,
/*actionInputFileCache=*/ null,
ActionInputPrefetcher.NONE,
actionKeyContext,
/*metadataHandler=*/ null,
/*rewindingEnabled=*/ false,
LostInputsCheck.NONE,
/*fileOutErr=*/ null,
/*eventHandler=*/ null,
/*clientEnv=*/ ImmutableMap.of(),
/*topLevelFilesets=*/ ImmutableMap.of(),
/*artifactExpander=*/ null,
/*actionFileSystem=*/ null,
/*skyframeDepsResult=*/ null,
DiscoveredModulesPruner.DEFAULT,
SyscallCache.NO_CACHE,
ThreadStateReceiver.NULL_INSTANCE);
}
@Test
public void testUsingOnlyShadowedActionInputs() throws Exception {
// If both starlark action and the shadowed action do not have inputs, then getInputs of both of
// them should return empty set
Action shadowedAction =
createShadowedAction(
NestedSetBuilder.emptySet(Order.STABLE_ORDER), /*discoversInputs=*/ false, null);
StarlarkAction starlarkAction =
(StarlarkAction)
new StarlarkAction.Builder()
.setShadowedAction(Optional.of(shadowedAction))
.setExecutable(executable)
.addOutput(output)
.build(NULL_ACTION_OWNER, targetConfig);
collectingAnalysisEnvironment.registerAction(starlarkAction);
assertThat(starlarkAction.getInputs().toList()).isEmpty();
assertThat(starlarkAction.discoversInputs()).isFalse();
assertThat(starlarkAction.getUnusedInputsList()).isEmpty();
assertThat(starlarkAction.getAllowedDerivedInputs().toList()).isEmpty();
// If the starlark action does not have any inputs, then it will use the shadowed action inputs
shadowedAction = createShadowedAction(shadowedActionInputs, false, null);
starlarkAction =
(StarlarkAction)
new StarlarkAction.Builder()
.setShadowedAction(Optional.of(shadowedAction))
.setExecutable(executable)
.addOutput(output)
.build(NULL_ACTION_OWNER, targetConfig);
collectingAnalysisEnvironment.registerAction(starlarkAction);
assertThat(starlarkAction.getInputs().toList())
.containsExactlyElementsIn(shadowedActionInputs.toList());
assertThat(starlarkAction.discoversInputs()).isFalse();
assertThat(starlarkAction.getUnusedInputsList()).isEmpty();
assertThat(starlarkAction.getAllowedDerivedInputs().toList())
.containsExactlyElementsIn(shadowedActionInputs.toList());
}
@Test
public void testUsingOnlyShadowedActionWithDiscoveredInputs() throws Exception {
// Test that the shadowed action's discovered inputs are passed to the starlark action
Action shadowedAction =
createShadowedAction(
NestedSetBuilder.emptySet(Order.STABLE_ORDER),
/*discoversInputs=*/ true,
discoveredInputs);
StarlarkAction starlarkAction =
(StarlarkAction)
new StarlarkAction.Builder()
.setShadowedAction(Optional.of(shadowedAction))
.setExecutable(executable)
.addOutput(output)
.build(NULL_ACTION_OWNER, targetConfig);
collectingAnalysisEnvironment.registerAction(starlarkAction);
assertThat(starlarkAction.getInputs().toList()).isEmpty();
assertThat(starlarkAction.getUnusedInputsList()).isEmpty();
assertThat(starlarkAction.getAllowedDerivedInputs().toList()).isEmpty();
assertThat(starlarkAction.discoversInputs()).isTrue();
assertThat(starlarkAction.discoverInputs(executionContext).toList())
.containsExactlyElementsIn(discoveredInputs.toList());
// after discovering inputs, the starlark action inputs should be updated
assertThat(starlarkAction.inputsDiscovered()).isTrue();
assertThat(starlarkAction.getInputs().toList())
.containsExactlyElementsIn(discoveredInputs.toList());
// Test that both inputs and discovered inputs of the shadowed action are passed to the starlark
// action
shadowedAction = createShadowedAction(shadowedActionInputs, true, discoveredInputs);
starlarkAction =
(StarlarkAction)
new StarlarkAction.Builder()
.setShadowedAction(Optional.of(shadowedAction))
.setExecutable(executable)
.addOutput(output)
.build(NULL_ACTION_OWNER, targetConfig);
collectingAnalysisEnvironment.registerAction(starlarkAction);
assertThat(starlarkAction.getInputs().toList())
.containsExactlyElementsIn(shadowedActionInputs.toList());
assertThat(starlarkAction.getUnusedInputsList()).isEmpty();
assertThat(starlarkAction.getAllowedDerivedInputs().toList())
.containsExactlyElementsIn(shadowedActionInputs.toList());
assertThat(starlarkAction.discoversInputs()).isTrue();
assertThat(starlarkAction.discoverInputs(executionContext).toList())
.containsExactlyElementsIn(
Sets.<Artifact>difference(discoveredInputs.toSet(), shadowedActionInputs.toSet())
.toArray());
// after discovering inputs, the starlark action inputs should be updated
assertThat(starlarkAction.inputsDiscovered()).isTrue();
assertThat(starlarkAction.getInputs().toList())
.containsExactlyElementsIn(
Sets.<Artifact>union(shadowedActionInputs.toSet(), discoveredInputs.toSet()).toArray());
}
@Test
public void testUsingShadowedActionWithStarlarkActionInputs() throws Exception {
// Test using Starlark action's inputs without using a shadowed action
StarlarkAction starlarkAction =
(StarlarkAction)
new StarlarkAction.Builder()
.setExecutable(executable)
.addInput(starlarkActionInputs.toList().get(0))
.addInput(starlarkActionInputs.toList().get(1))
.addInput(starlarkActionInputs.toList().get(2))
.addOutput(output)
.build(NULL_ACTION_OWNER, targetConfig);
collectingAnalysisEnvironment.registerAction(starlarkAction);
assertThat(starlarkAction.getInputs().toList())
.containsExactlyElementsIn(starlarkActionInputs.toList());
assertThat(starlarkAction.getUnusedInputsList()).isEmpty();
assertThat(starlarkAction.getAllowedDerivedInputs().toList())
.containsExactlyElementsIn(starlarkActionInputs.toList());
assertThat(starlarkAction.discoversInputs()).isFalse();
// Test using Starlark actions's inputs with shadowed action's inputs
Action shadowedAction =
createShadowedAction(
shadowedActionInputs, /*discoversInputs=*/ false, /*discoveredInputs=*/ null);
starlarkAction =
(StarlarkAction)
new StarlarkAction.Builder()
.setShadowedAction(Optional.of(shadowedAction))
.setExecutable(executable)
.addInput(starlarkActionInputs.toList().get(0))
.addInput(starlarkActionInputs.toList().get(1))
.addInput(starlarkActionInputs.toList().get(2))
.addOutput(output)
.build(NULL_ACTION_OWNER, targetConfig);
collectingAnalysisEnvironment.registerAction(starlarkAction);
assertThat(starlarkAction.getInputs().toList())
.containsExactlyElementsIn(
Sets.<Artifact>union(shadowedActionInputs.toSet(), starlarkActionInputs.toSet())
.toArray());
assertThat(starlarkAction.getUnusedInputsList()).isEmpty();
assertThat(starlarkAction.getAllowedDerivedInputs().toList())
.containsExactlyElementsIn(
Sets.<Artifact>union(shadowedActionInputs.toSet(), starlarkActionInputs.toSet())
.toArray());
assertThat(starlarkAction.discoversInputs()).isFalse();
// Test using Starlark actions's inputs with shadowed action's inputs and discovered inputs
shadowedAction = createShadowedAction(shadowedActionInputs, true, discoveredInputs);
starlarkAction =
(StarlarkAction)
new StarlarkAction.Builder()
.setShadowedAction(Optional.of(shadowedAction))
.setExecutable(executable)
.addInput(starlarkActionInputs.toList().get(0))
.addInput(starlarkActionInputs.toList().get(1))
.addInput(starlarkActionInputs.toList().get(2))
.addOutput(output)
.build(NULL_ACTION_OWNER, targetConfig);
collectingAnalysisEnvironment.registerAction(starlarkAction);
assertThat(starlarkAction.getInputs().toList())
.containsExactlyElementsIn(
Sets.<Artifact>union(shadowedActionInputs.toSet(), starlarkActionInputs.toSet())
.toArray());
assertThat(starlarkAction.getUnusedInputsList()).isEmpty();
assertThat(starlarkAction.getAllowedDerivedInputs().toList())
.containsExactlyElementsIn(
Sets.<Artifact>union(shadowedActionInputs.toSet(), starlarkActionInputs.toSet())
.toArray());
assertThat(starlarkAction.discoversInputs()).isTrue();
assertThat(starlarkAction.discoverInputs(executionContext).toList())
.containsExactly(discoveredInputs.toList().get(2));
// after discovering inputs, the starlark action inputs should be updated
assertThat(starlarkAction.inputsDiscovered()).isTrue();
assertThat(starlarkAction.getInputs().toList())
.containsExactlyElementsIn(
Sets.<Artifact>union(
NestedSetBuilder.wrap(
Order.STABLE_ORDER,
Sets.<Artifact>union(
shadowedActionInputs.toSet(), starlarkActionInputs.toSet()))
.toSet(),
discoveredInputs.toSet())
.toArray());
}
@Test
public void testPassingShadowedActionEnvironment() throws Exception {
// Test using Starlark action's environment without using a shadowed action
StarlarkAction starlarkAction =
(StarlarkAction)
new StarlarkAction.Builder()
.setExecutable(executable)
.addInput(starlarkActionInputs.toList().get(0))
.addOutput(output)
.setEnvironment(starlarkActionEnvironment)
.build(NULL_ACTION_OWNER, targetConfig);
collectingAnalysisEnvironment.registerAction(starlarkAction);
assertThat(starlarkAction.getEffectiveEnvironment(ImmutableMap.of()))
.containsExactlyEntriesIn(starlarkActionEnvironment);
// Test using shadowed action's environment without Starlark actions's environment
Action shadowedAction =
createShadowedAction(
shadowedActionInputs, /*discoversInputs=*/ false, /*discoveredInputs=*/ null);
starlarkAction =
(StarlarkAction)
new StarlarkAction.Builder()
.setShadowedAction(Optional.of(shadowedAction))
.setExecutable(executable)
.addInput(starlarkActionInputs.toList().get(0))
.addOutput(output)
.build(NULL_ACTION_OWNER, targetConfig);
collectingAnalysisEnvironment.registerAction(starlarkAction);
assertThat(starlarkAction.getEffectiveEnvironment(ImmutableMap.of()))
.containsExactlyEntriesIn(shadowedActionEnvironment);
// Test using Starlark actions's environment with shadowed action's environment
starlarkAction =
(StarlarkAction)
new StarlarkAction.Builder()
.setShadowedAction(Optional.of(shadowedAction))
.setExecutable(executable)
.addInput(starlarkActionInputs.toList().get(0))
.addOutput(output)
.setEnvironment(starlarkActionEnvironment)
.build(NULL_ACTION_OWNER, targetConfig);
collectingAnalysisEnvironment.registerAction(starlarkAction);
LinkedHashMap<String, String> expectedEnvironment = new LinkedHashMap<>();
expectedEnvironment.putAll(shadowedActionEnvironment);
expectedEnvironment.putAll(starlarkActionEnvironment);
ImmutableMap<String, String> actualEnvironment =
starlarkAction.getEffectiveEnvironment(ImmutableMap.of());
assertThat(actualEnvironment).hasSize(5);
// Starlark action's env overwrites any repeated variable from the shadowed action env
assertThat(actualEnvironment).containsEntry("repeated_var", "starlark_val");
assertThat(actualEnvironment).containsExactlyEntriesIn(expectedEnvironment);
}
private Action createShadowedAction(
NestedSet<Artifact> inputs, boolean discoversInputs, NestedSet<Artifact> discoveredInputs)
throws Exception {
Action shadowedAction = mock(Action.class);
when(shadowedAction.discoversInputs()).thenReturn(discoversInputs);
when(shadowedAction.getInputs()).thenReturn(inputs);
when(shadowedAction.getAllowedDerivedInputs()).thenReturn(inputs);
when(shadowedAction.getInputFilesForExtraAction(
ArgumentMatchers.any(ActionExecutionContext.class)))
.thenReturn(discoveredInputs);
when(shadowedAction.inputsDiscovered()).thenReturn(true);
when(shadowedAction.getOwner()).thenReturn(NULL_ACTION_OWNER);
when(shadowedAction.getEffectiveEnvironment(ArgumentMatchers.anyMap()))
.thenReturn(ImmutableMap.copyOf(shadowedActionEnvironment));
return shadowedAction;
}
}