aboutsummaryrefslogtreecommitdiffhomepage
path: root/src/test/java/com/google/devtools/build/lib/analysis/util
diff options
context:
space:
mode:
Diffstat (limited to 'src/test/java/com/google/devtools/build/lib/analysis/util')
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/util/ActionTester.java76
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java59
-rw-r--r--src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestUtil.java430
3 files changed, 565 insertions, 0 deletions
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/ActionTester.java b/src/test/java/com/google/devtools/build/lib/analysis/util/ActionTester.java
new file mode 100644
index 0000000000..2dfa0eae3d
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/ActionTester.java
@@ -0,0 +1,76 @@
+// 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.analysis.util;
+
+import static com.google.common.truth.Truth.assertThat;
+import static com.google.common.truth.Truth.assertWithMessage;
+
+import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.Actions;
+
+/**
+ * Test helper for testing {@link Action} implementations.
+ */
+public class ActionTester {
+
+ /**
+ * A generator for action instances.
+ */
+ public interface ActionCombinationFactory {
+
+ /**
+ * Returns a new action instance. The parameter {@code i} is used to vary the parameters used to
+ * create the action. Implementations should do something like this:
+ * <code><pre>
+ * return new MyAction(owner, inputs, outputs, configuration,
+ * (i & 1) == 0 ? a1 : a2,
+ * (i & 2) == 0 ? b1 : b2,
+ * (i & 4) == 0 ? c1 : c2);
+ * (i & 16) == 0 ? d1 : d2);
+ * </pre></code>
+ *
+ * <p>The wrap-around (in this case at 32) is intentional and is checked for by the testing
+ * method.
+ *
+ * <p>To reduce the combinatorial complexity of testing an action class, all elements that are
+ * only used to change the executed command line should go into a single parameter, and the key
+ * computation should take the generated command line into account.
+ *
+ * <p>Furthermore, when called with identical parameters, this method should return different
+ * instances (i.e. according to {@code ==}), but they should have the same key.
+ */
+ Action generate(int i);
+ }
+
+ /**
+ * Tests that different actions have different keys. The count should specify how many different
+ * permutations the {@link ActionCombinationFactory} can generate.
+ */
+ public static void runTest(int count, ActionCombinationFactory factory) throws Exception {
+ Action[] actions = new Action[count];
+ for (int i = 0; i < actions.length; i++) {
+ actions[i] = factory.generate(i);
+ }
+ // Sanity check that the count is correct.
+ assertThat(Actions.canBeShared(actions[0], factory.generate(count))).isTrue();
+
+ for (int i = 0; i < actions.length; i++) {
+ assertThat(Actions.canBeShared(actions[i], factory.generate(i))).isTrue();
+ for (int j = i + 1; j < actions.length; j++) {
+ assertWithMessage(i + " and " + j).that(Actions.canBeShared(actions[i], actions[j]))
+ .isFalse();
+ }
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java
new file mode 100644
index 0000000000..c411695605
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisMock.java
@@ -0,0 +1,59 @@
+// 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.analysis.util;
+
+import com.google.devtools.build.lib.analysis.config.ConfigurationFactory;
+import com.google.devtools.build.lib.packages.util.MockToolsConfig;
+
+import java.io.IOException;
+import java.util.Collection;
+
+/**
+ * Create a mock client for the analysis phase, as well as a configuration factory.
+ */
+public abstract class AnalysisMock {
+
+ /**
+ * This is called from test setup to create the mock directory layout needed to create the
+ * configuration.
+ */
+ public abstract void setupMockClient(MockToolsConfig mockToolsConfig) throws IOException;
+
+ public abstract ConfigurationFactory createConfigurationFactory();
+
+ public abstract Collection<String> getOptionOverrides();
+
+ public static class Delegate extends AnalysisMock {
+ private final AnalysisMock delegate;
+
+ public Delegate(AnalysisMock delegate) {
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void setupMockClient(MockToolsConfig mockToolsConfig) throws IOException {
+ delegate.setupMockClient(mockToolsConfig);
+ }
+
+ @Override
+ public ConfigurationFactory createConfigurationFactory() {
+ return delegate.createConfigurationFactory();
+ }
+
+ @Override
+ public Collection<String> getOptionOverrides() {
+ return delegate.getOptionOverrides();
+ }
+ }
+}
diff --git a/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestUtil.java b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestUtil.java
new file mode 100644
index 0000000000..3d75939dd9
--- /dev/null
+++ b/src/test/java/com/google/devtools/build/lib/analysis/util/AnalysisTestUtil.java
@@ -0,0 +1,430 @@
+// 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.analysis.util;
+
+import com.google.common.base.Supplier;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.devtools.build.lib.actions.Action;
+import com.google.devtools.build.lib.actions.ActionContextProvider;
+import com.google.devtools.build.lib.actions.ActionExecutionContext;
+import com.google.devtools.build.lib.actions.ActionExecutionException;
+import com.google.devtools.build.lib.actions.ActionGraph;
+import com.google.devtools.build.lib.actions.ActionInputFileCache;
+import com.google.devtools.build.lib.actions.Artifact;
+import com.google.devtools.build.lib.actions.ArtifactFactory;
+import com.google.devtools.build.lib.actions.ArtifactOwner;
+import com.google.devtools.build.lib.actions.ExecutionStrategy;
+import com.google.devtools.build.lib.actions.Executor;
+import com.google.devtools.build.lib.actions.Executor.ActionContext;
+import com.google.devtools.build.lib.actions.ExecutorInitException;
+import com.google.devtools.build.lib.actions.MiddlemanFactory;
+import com.google.devtools.build.lib.actions.MutableActionGraph;
+import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException;
+import com.google.devtools.build.lib.actions.ResourceSet;
+import com.google.devtools.build.lib.actions.Root;
+import com.google.devtools.build.lib.actions.util.ActionsTestUtil;
+import com.google.devtools.build.lib.analysis.AnalysisEnvironment;
+import com.google.devtools.build.lib.analysis.BlazeDirectories;
+import com.google.devtools.build.lib.analysis.BuildInfoHelper;
+import com.google.devtools.build.lib.analysis.RuleContext;
+import com.google.devtools.build.lib.analysis.WorkspaceStatusAction;
+import com.google.devtools.build.lib.analysis.WorkspaceStatusAction.Key;
+import com.google.devtools.build.lib.analysis.buildinfo.BuildInfoFactory.BuildInfoKey;
+import com.google.devtools.build.lib.analysis.config.BuildConfiguration;
+import com.google.devtools.build.lib.analysis.config.BuildConfigurationCollection;
+import com.google.devtools.build.lib.events.EventHandler;
+import com.google.devtools.build.lib.packages.Attribute.ConfigurationTransition;
+import com.google.devtools.build.lib.vfs.FileSystemUtils;
+import com.google.devtools.build.lib.vfs.PathFragment;
+import com.google.devtools.build.skyframe.SkyFunction;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.UUID;
+
+public final class AnalysisTestUtil {
+
+ /**
+ * An {@link AnalysisEnvironment} implementation that collects the actions registered.
+ */
+ public static class CollectingAnalysisEnvironment implements AnalysisEnvironment {
+ private final List<Action> actions = new ArrayList<>();
+ private final AnalysisEnvironment original;
+
+ public CollectingAnalysisEnvironment(AnalysisEnvironment original) {
+ this.original = original;
+ }
+
+ public void clear() {
+ actions.clear();
+ }
+
+ @Override
+ public void registerAction(Action... actions) {
+ for (Action action : actions) {
+ this.actions.add(action);
+ }
+ original.registerAction(actions);
+ }
+
+ /** Calls {@link MutableActionGraph#registerAction} for all collected actions. */
+ public void registerWith(MutableActionGraph actionGraph) {
+ for (Action action : actions) {
+ try {
+ actionGraph.registerAction(action);
+ } catch (ActionConflictException e) {
+ throw new ActionsTestUtil.UncheckedActionConflictException(e);
+ }
+ }
+ }
+
+ @Override
+ public EventHandler getEventHandler() {
+ return original.getEventHandler();
+ }
+
+ @Override
+ public boolean hasErrors() {
+ return original.hasErrors();
+ }
+
+ @Override
+ public Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root) {
+ return original.getDerivedArtifact(rootRelativePath, root);
+ }
+
+ @Override
+ public Artifact getConstantMetadataArtifact(PathFragment rootRelativePath, Root root) {
+ return original.getConstantMetadataArtifact(rootRelativePath, root);
+ }
+
+ @Override
+ public Artifact getFilesetArtifact(PathFragment rootRelativePath, Root root) {
+ return original.getFilesetArtifact(rootRelativePath, root);
+ }
+
+ @Override
+ public Artifact getEmbeddedToolArtifact(String embeddedPath) {
+ return original.getEmbeddedToolArtifact(embeddedPath);
+ }
+
+ @Override
+ public MiddlemanFactory getMiddlemanFactory() {
+ return original.getMiddlemanFactory();
+ }
+
+ @Override
+ public Action getLocalGeneratingAction(Artifact artifact) {
+ return original.getLocalGeneratingAction(artifact);
+ }
+
+ @Override
+ public Iterable<Action> getRegisteredActions() {
+ return original.getRegisteredActions();
+ }
+
+ @Override
+ public SkyFunction.Environment getSkyframeEnv() {
+ return null;
+ }
+
+ @Override
+ public Artifact getStableWorkspaceStatusArtifact() {
+ return original.getStableWorkspaceStatusArtifact();
+ }
+
+ @Override
+ public Artifact getVolatileWorkspaceStatusArtifact() {
+ return original.getVolatileWorkspaceStatusArtifact();
+ }
+
+ @Override
+ public ImmutableList<Artifact> getBuildInfo(RuleContext ruleContext, BuildInfoKey key) {
+ return original.getBuildInfo(ruleContext, key);
+ }
+
+ @Override
+ public ArtifactOwner getOwner() {
+ return original.getOwner();
+ }
+
+ @Override
+ public ImmutableSet<Artifact> getOrphanArtifacts() {
+ return original.getOrphanArtifacts();
+ }
+ }
+
+ public static class DummyWorkspaceStatusAction extends WorkspaceStatusAction {
+ private final String key;
+ private final Artifact stableStatus;
+ private final Artifact volatileStatus;
+
+ public DummyWorkspaceStatusAction(String key,
+ Artifact stableStatus, Artifact volatileStatus) {
+ super(
+ BuildInfoHelper.BUILD_INFO_ACTION_OWNER,
+ ImmutableList.<Artifact>of(),
+ ImmutableList.of(stableStatus, volatileStatus));
+ this.key = key;
+ this.stableStatus = stableStatus;
+ this.volatileStatus = volatileStatus;
+ }
+
+ @Override
+ public String describeStrategy(Executor executor) {
+ return "";
+ }
+
+ @Override
+ public void execute(ActionExecutionContext actionExecutionContext)
+ throws ActionExecutionException {
+ try {
+ FileSystemUtils.writeContent(stableStatus.getPath(), new byte[] {});
+ FileSystemUtils.writeContent(volatileStatus.getPath(), new byte[] {});
+ } catch (IOException e) {
+ throw new ActionExecutionException(e, this, true);
+ }
+ }
+
+ @Override
+ public String getMnemonic() {
+ return "DummyBuildInfoAction" + key;
+ }
+
+ @Override
+ public ResourceSet estimateResourceConsumption(Executor executor) {
+ return ResourceSet.ZERO;
+ }
+
+ @Override
+ public String computeKey() {
+ return "";
+ }
+
+ @Override
+ public Artifact getVolatileStatus() {
+ return volatileStatus;
+ }
+
+ @Override
+ public Artifact getStableStatus() {
+ return stableStatus;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (!(o instanceof DummyWorkspaceStatusAction)) {
+ return false;
+ }
+
+ DummyWorkspaceStatusAction that = (DummyWorkspaceStatusAction) o;
+ return that.key.equals(this.key);
+ }
+ }
+
+ @ExecutionStrategy(contextType = WorkspaceStatusAction.Context.class)
+ public static class DummyWorkspaceStatusActionContext implements WorkspaceStatusAction.Context {
+ @Override
+ public ImmutableMap<String, Key> getStableKeys() {
+ return ImmutableMap.of();
+ }
+
+ @Override
+ public ImmutableMap<String, Key> getVolatileKeys() {
+ return ImmutableMap.of();
+ }
+ }
+
+ public static class DummyWorkspaceActionContextProvider implements ActionContextProvider {
+ @Override
+ public Iterable<ActionContext> getActionContexts() {
+ return ImmutableList.<ActionContext>of(new DummyWorkspaceStatusActionContext());
+ }
+
+ @Override
+ public void executorCreated(Iterable<ActionContext> usedContexts) throws ExecutorInitException {
+ }
+
+ @Override
+ public void executionPhaseStarting(ActionInputFileCache actionInputFileCache,
+ ActionGraph actionGraph,
+ Iterable<Artifact> topLevelArtifacts) throws ExecutorInitException, InterruptedException {
+ }
+
+ @Override
+ public void executionPhaseEnding() {
+ }
+ }
+
+ /**
+ * A workspace status action factory that does not do any interaction with the environment.
+ */
+ public static class DummyWorkspaceStatusActionFactory implements WorkspaceStatusAction.Factory {
+ private final BlazeDirectories directories;
+ private String key;
+
+ public DummyWorkspaceStatusActionFactory(BlazeDirectories directories) {
+ this.directories = directories;
+ this.key = "";
+ }
+
+ public void setKey(String key) {
+ this.key = key;
+ }
+
+ @Override
+ public WorkspaceStatusAction createWorkspaceStatusAction(
+ ArtifactFactory artifactFactory, ArtifactOwner artifactOwner, Supplier<UUID> buildId) {
+ Artifact stableStatus = artifactFactory.getDerivedArtifact(
+ new PathFragment("build-info.txt"),
+ directories.getBuildDataDirectory(), artifactOwner);
+ Artifact volatileStatus = artifactFactory.getConstantMetadataArtifact(
+ new PathFragment("build-changelist.txt"),
+ directories.getBuildDataDirectory(), artifactOwner);
+ return new DummyWorkspaceStatusAction(key, stableStatus, volatileStatus);
+ }
+
+ @Override
+ public Map<String, String> createDummyWorkspaceStatus() {
+ return ImmutableMap.of();
+ }
+ }
+
+ public static final AnalysisEnvironment STUB_ANALYSIS_ENVIRONMENT = new AnalysisEnvironment() {
+ @Override
+ public void registerAction(Action... action) {
+ }
+
+ @Override
+ public boolean hasErrors() {
+ return false;
+ }
+
+ @Override
+ public Artifact getEmbeddedToolArtifact(String embeddedPath) {
+ return null;
+ }
+
+ @Override
+ public Artifact getConstantMetadataArtifact(PathFragment rootRelativePath, Root root) {
+ return null;
+ }
+
+ @Override
+ public EventHandler getEventHandler() {
+ return null;
+ }
+
+ @Override
+ public MiddlemanFactory getMiddlemanFactory() {
+ return null;
+ }
+
+ @Override
+ public Action getLocalGeneratingAction(Artifact artifact) {
+ return null;
+ }
+
+ @Override
+ public Iterable<Action> getRegisteredActions() {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public SkyFunction.Environment getSkyframeEnv() {
+ return null;
+ }
+
+ @Override
+ public Artifact getFilesetArtifact(PathFragment rootRelativePath, Root root) {
+ return null;
+ }
+
+ @Override
+ public Artifact getDerivedArtifact(PathFragment rootRelativePath, Root root) {
+ return null;
+ }
+
+ @Override
+ public Artifact getStableWorkspaceStatusArtifact() {
+ return null;
+ }
+
+ @Override
+ public Artifact getVolatileWorkspaceStatusArtifact() {
+ return null;
+ }
+
+ @Override
+ public ImmutableList<Artifact> getBuildInfo(RuleContext ruleContext, BuildInfoKey key) {
+ return ImmutableList.of();
+ }
+
+ @Override
+ public ArtifactOwner getOwner() {
+ return ArtifactOwner.NULL_OWNER;
+ }
+
+ @Override
+ public ImmutableSet<Artifact> getOrphanArtifacts() {
+ return ImmutableSet.<Artifact>of();
+ }
+ };
+
+ /**
+ * Given a collection of Artifacts, returns a corresponding set of strings of
+ * the form "{root} {relpath}", such as "bin x/libx.a". Such strings make
+ * assertions easier to write.
+ *
+ * <p>The returned set preserves the order of the input.
+ */
+ public static Set<String> artifactsToStrings(BuildConfigurationCollection configurations,
+ Iterable<Artifact> artifacts) {
+ Map<Root, String> rootMap = new HashMap<>();
+ BuildConfiguration targetConfiguration =
+ Iterables.getOnlyElement(configurations.getTargetConfigurations());
+ BuildConfiguration hostConfiguration =
+ targetConfiguration.getConfiguration(ConfigurationTransition.HOST);
+ rootMap.put(targetConfiguration.getBinDirectory(), "bin");
+ rootMap.put(targetConfiguration.getGenfilesDirectory(), "genfiles");
+ rootMap.put(targetConfiguration.getMiddlemanDirectory(), "internal");
+ rootMap.put(hostConfiguration.getBinDirectory(), "bin(host)");
+ rootMap.put(hostConfiguration.getGenfilesDirectory(), "genfiles(host)");
+ rootMap.put(hostConfiguration.getMiddlemanDirectory(), "internal(host)");
+
+ Set<String> files = new LinkedHashSet<>();
+ for (Artifact artifact : artifacts) {
+ Root root = artifact.getRoot();
+ if (root.isSourceRoot()) {
+ files.add("src " + artifact.getRootRelativePath());
+ } else {
+ String name = rootMap.get(root);
+ if (name == null) {
+ name = "/";
+ }
+ files.add(name + " " + artifact.getRootRelativePath());
+ }
+ }
+ return files;
+ }
+
+}