diff options
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/actions/util/TestAction.java')
-rw-r--r-- | src/test/java/com/google/devtools/build/lib/actions/util/TestAction.java | 176 |
1 files changed, 176 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/actions/util/TestAction.java b/src/test/java/com/google/devtools/build/lib/actions/util/TestAction.java new file mode 100644 index 0000000000..086138440e --- /dev/null +++ b/src/test/java/com/google/devtools/build/lib/actions/util/TestAction.java @@ -0,0 +1,176 @@ +// Copyright 2015 Google Inc. 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.actions.util; + +import static com.google.devtools.build.lib.actions.util.ActionsTestUtil.NULL_ACTION_OWNER; + +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import com.google.devtools.build.lib.actions.AbstractAction; +import com.google.devtools.build.lib.actions.ActionExecutionContext; +import com.google.devtools.build.lib.actions.ActionExecutionException; +import com.google.devtools.build.lib.actions.Artifact; +import com.google.devtools.build.lib.actions.Executor; +import com.google.devtools.build.lib.actions.ResourceSet; +import com.google.devtools.build.lib.util.StringUtilities; +import com.google.devtools.build.lib.vfs.FileSystemUtils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.concurrent.Callable; +import java.util.concurrent.Executors; + +/** + * A dummy action for testing. Its execution runs the specified + * Runnable or Callable, which is defined by the test case, + * and touches all the output files. + */ +public class TestAction extends AbstractAction { + + public static final Runnable NO_EFFECT = new Runnable() { @Override public void run() {} }; + + private static final ResourceSet RESOURCES = + new ResourceSet(/*memoryMb=*/1.0, /*cpu=*/0.1, /*io=*/0.0); + + private final Callable<Void> effect; + + /** Use this constructor if the effect can't throw exceptions. */ + public TestAction(Runnable effect, + Collection<Artifact> inputs, + Collection<Artifact> outputs) { + super(NULL_ACTION_OWNER, inputs, outputs); + this.effect = Executors.callable(effect, null); + } + + /** + * Use this constructor if the effect can throw exceptions. + * Any checked exception thrown will be repackaged as an + * ActionExecutionException. + */ + public TestAction(Callable<Void> effect, + Collection<Artifact> inputs, + Collection<Artifact> outputs) { + super(NULL_ACTION_OWNER, inputs, outputs); + this.effect = effect; + } + + @Override + public Collection<Artifact> getMandatoryInputs() { + List<Artifact> mandatoryInputs = new ArrayList<>(); + for (Artifact input : getInputs()) { + if (!input.getExecPath().getBaseName().endsWith(".optional")) { + mandatoryInputs.add(input); + } + } + return mandatoryInputs; + } + + @Override + public boolean discoversInputs() { + for (Artifact input : getInputs()) { + if (!input.getExecPath().getBaseName().endsWith(".optional")) { + return true; + } + } + return false; + } + + @Override + public void discoverInputs(ActionExecutionContext actionExecutionContext) { + Preconditions.checkState(discoversInputs(), this); + } + + @Override + public void execute(ActionExecutionContext actionExecutionContext) + throws ActionExecutionException { + for (Artifact artifact : getInputs()) { + // Do not check *.optional artifacts - artifacts with such extension are + // used by tests to specify artifacts that may or may not be missing. + // This is used, e.g., to test Blaze behavior when action has missing + // input artifacts but still is successfully executed. + if (!artifact.getPath().exists() && + !artifact.getExecPath().getBaseName().endsWith(".optional")) { + throw new IllegalStateException("action's input file does not exist: " + + artifact.getPath()); + } + } + + try { + effect.call(); + } catch (RuntimeException | Error e) { + throw e; + } catch (Exception e) { + throw new ActionExecutionException("TestAction failed due to exception", + e, this, false); + } + + try { + for (Artifact artifact: getOutputs()) { + FileSystemUtils.touchFile(artifact.getPath()); + } + } catch (IOException e) { + throw new AssertionError(e); + } + } + + @Override + public String describeStrategy(Executor executor) { + return ""; + } + + @Override + protected String computeKey() { + List<String> outputsList = new ArrayList<>(); + for (Artifact output : getOutputs()) { + outputsList.add(output.getPath().getPathString()); + } + // This could use a functional iterable and avoid creating a list + return "test " + StringUtilities.combineKeys(outputsList); + } + + @Override + public String getMnemonic() { return "Test"; } + + @Override + public ResourceSet estimateResourceConsumption(Executor executor) { + return RESOURCES; + } + + + /** No-op action that has exactly one output, and can be a middleman action. */ + public static class DummyAction extends TestAction { + private static final Runnable NOOP = new Runnable() { + @Override + public void run() {} + }; + + private final MiddlemanType type; + + public DummyAction(Collection<Artifact> inputs, Artifact output, MiddlemanType type) { + super(NOOP, inputs, ImmutableList.of(output)); + this.type = type; + } + + public DummyAction(Collection<Artifact> inputs, Artifact output) { + this(inputs, output, MiddlemanType.NORMAL); + } + + @Override + public MiddlemanType getActionType() { + return type; + } + } +} |