// Copyright 2015 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.actions; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Sets; import com.google.devtools.build.lib.actions.MutableActionGraph.ActionConflictException; import com.google.devtools.build.lib.actions.util.ActionsTestUtil.UncheckedActionConflictException; import com.google.devtools.build.lib.actions.util.TestAction; import com.google.devtools.build.lib.concurrent.AbstractQueueVisitor; import com.google.devtools.build.lib.concurrent.ErrorClassifier; import com.google.devtools.build.lib.util.BlazeClock; import com.google.devtools.build.lib.vfs.FileSystem; import com.google.devtools.build.lib.vfs.Path; import com.google.devtools.build.lib.vfs.inmemoryfs.InMemoryFileSystem; import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for {@link MapBasedActionGraph}. */ @RunWith(JUnit4.class) public class MapBasedActionGraphTest { @Test public void testSmoke() throws Exception { MutableActionGraph actionGraph = new MapBasedActionGraph(); FileSystem fileSystem = new InMemoryFileSystem(BlazeClock.instance()); Path path = fileSystem.getPath("/root/foo"); Artifact output = new Artifact(path, Root.asDerivedRoot(path)); Action action = new TestAction(TestAction.NO_EFFECT, ImmutableSet.<Artifact>of(), ImmutableSet.of(output)); actionGraph.registerAction(action); actionGraph.unregisterAction(action); path = fileSystem.getPath("/root/bar"); output = new Artifact(path, Root.asDerivedRoot(path)); Action action2 = new TestAction(TestAction.NO_EFFECT, ImmutableSet.<Artifact>of(), ImmutableSet.of(output)); actionGraph.registerAction(action); actionGraph.registerAction(action2); actionGraph.unregisterAction(action); } @Test public void testNoActionConflictWhenUnregisteringSharedAction() throws Exception { MutableActionGraph actionGraph = new MapBasedActionGraph(); FileSystem fileSystem = new InMemoryFileSystem(BlazeClock.instance()); Path path = fileSystem.getPath("/root/foo"); Artifact output = new Artifact(path, Root.asDerivedRoot(path)); Action action = new TestAction(TestAction.NO_EFFECT, ImmutableSet.<Artifact>of(), ImmutableSet.of(output)); actionGraph.registerAction(action); Action otherAction = new TestAction(TestAction.NO_EFFECT, ImmutableSet.<Artifact>of(), ImmutableSet.of(output)); actionGraph.registerAction(otherAction); actionGraph.unregisterAction(action); } private static class ActionRegisterer extends AbstractQueueVisitor { private final MutableActionGraph graph = new MapBasedActionGraph(); private final Artifact output; // Just to occasionally add actions that were already present. private final Set<Action> allActions = Sets.newConcurrentHashSet(); private final AtomicInteger actionCount = new AtomicInteger(0); private ActionRegisterer() { super( 200, 1, TimeUnit.SECONDS, /*failFastOnException=*/ true, "action-graph-test", AbstractQueueVisitor.EXECUTOR_FACTORY, ErrorClassifier.DEFAULT); FileSystem fileSystem = new InMemoryFileSystem(BlazeClock.instance()); Path path = fileSystem.getPath("/root/foo"); output = new Artifact(path, Root.asDerivedRoot(path)); allActions.add(new TestAction(TestAction.NO_EFFECT, ImmutableSet.<Artifact>of(), ImmutableSet.of(output))); } private void registerAction(final Action action) { execute( new Runnable() { @Override public void run() { try { graph.registerAction(action); } catch (ActionConflictException e) { throw new UncheckedActionConflictException(e); } doRandom(); } }); } private void unregisterAction(final Action action) { execute( new Runnable() { @Override public void run() { graph.unregisterAction(action); doRandom(); } }); } private void doRandom() { if (actionCount.incrementAndGet() > 10000) { return; } Action action = null; if (Math.random() < 0.5) { action = Iterables.getFirst(allActions, null); } else { action = new TestAction(TestAction.NO_EFFECT, ImmutableSet.<Artifact>of(), ImmutableSet.of(output)); allActions.add(action); } if (Math.random() < 0.5) { registerAction(action); } else { unregisterAction(action); } } private void work() throws InterruptedException { awaitQuiescence(/*interruptWorkers=*/ true); } } @Test public void testSharedActionStressTest() throws Exception { ActionRegisterer actionRegisterer = new ActionRegisterer(); actionRegisterer.doRandom(); actionRegisterer.work(); } }