Introduce ConditionalStep.
Summary: This is needed for the upcoming predex work.
Test Plan: Sandcastle builds.
diff --git a/src/com/facebook/buck/step/ConditionalStep.java b/src/com/facebook/buck/step/ConditionalStep.java
new file mode 100644
index 0000000..ce35b4c
--- /dev/null
+++ b/src/com/facebook/buck/step/ConditionalStep.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2013-present Facebook, Inc.
+ *
+ * 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.facebook.buck.step;
+
+import com.google.common.base.Preconditions;
+import com.google.common.base.Supplier;
+
+/** {@link Step} that is run conditionally based on {@code Supplier<Boolean> shouldRunStep}. */
+public class ConditionalStep implements Step {
+
+ private final Supplier<Boolean> shouldRunStep;
+ private final Step step;
+
+ public ConditionalStep(Supplier<Boolean> shouldRunStep, Step step) {
+ this.shouldRunStep = Preconditions.checkNotNull(shouldRunStep);
+ this.step = Preconditions.checkNotNull(step);
+ }
+
+ @Override
+ public int execute(ExecutionContext context) {
+ if (shouldRunStep.get()) {
+ return step.execute(context);
+ } else {
+ return 0;
+ }
+ }
+
+ @Override
+ public String getShortName() {
+ // Use the short name of the underlying Step because StepEvent.getCategory() uses the short
+ // name to group similar step types together so they can be audited for time spent.
+ return step.getShortName();
+ }
+
+ @Override
+ public String getDescription(ExecutionContext context) {
+ return "conditionally: " + step.getDescription(context);
+ }
+}
diff --git a/test/com/facebook/buck/step/ConditionalStepTest.java b/test/com/facebook/buck/step/ConditionalStepTest.java
new file mode 100644
index 0000000..5b2407b
--- /dev/null
+++ b/test/com/facebook/buck/step/ConditionalStepTest.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright 2013-present Facebook, Inc.
+ *
+ * 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.facebook.buck.step;
+
+import static org.junit.Assert.assertEquals;
+
+import com.facebook.buck.util.TriState;
+import com.google.common.base.Supplier;
+
+import org.easymock.EasyMockSupport;
+import org.junit.Test;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicReference;
+
+public class ConditionalStepTest extends EasyMockSupport {
+
+ @Test
+ public void testExecuteConditionalStepWhenTrue() {
+ // Create a Supplier<Boolean> that returns a value once an external condition has been
+ // satisfied.
+ final AtomicReference<TriState> condition = new AtomicReference<>(
+ TriState.UNSPECIFIED);
+ Supplier<Boolean> conditional = new Supplier<Boolean>() {
+ @Override
+ public Boolean get() {
+ if (!condition.get().isSet()) {
+ throw new IllegalStateException("condition must be satisfied before this is queried.");
+ } else {
+ return condition.get().asBoolean();
+ }
+ }
+ };
+
+ // Create a step to run when the Supplier<Boolean> is true. Exit code is always 37.
+ final AtomicInteger numCalls = new AtomicInteger(0);
+ Step stepToRunWhenSupplierIsTrue = new AbstractExecutionStep("inc") {
+ @Override
+ public int execute(ExecutionContext context) {
+ numCalls.incrementAndGet();
+ return 37;
+ }
+ };
+
+ // Create and execute the ConditionalStep.
+ ConditionalStep conditionalStep = new ConditionalStep(conditional, stepToRunWhenSupplierIsTrue);
+ condition.set(TriState.TRUE);
+ ExecutionContext context = TestExecutionContext.newInstance();
+ int exitCode = conditionalStep.execute(context);
+ assertEquals("stepToRunWhenSupplierIsTrue should have been run once.", 1, numCalls.get());
+ assertEquals("The exit code of stepToRunWhenSupplierIsTrue should be passed through.",
+ 37,
+ exitCode);
+ assertEquals("inc", conditionalStep.getShortName());
+ assertEquals("conditionally: inc", conditionalStep.getDescription(context));
+ }
+
+ @Test
+ public void testExecuteConditionalStepWhenFalse() {
+ // Create a Supplier<Boolean> that returns a value once an external condition has been
+ // satisfied.
+ final AtomicReference<TriState> condition = new AtomicReference<>(
+ TriState.UNSPECIFIED);
+ Supplier<Boolean> conditional = new Supplier<Boolean>() {
+ @Override
+ public Boolean get() {
+ if (!condition.get().isSet()) {
+ throw new IllegalStateException("condition must be satisfied before this is queried.");
+ } else {
+ return condition.get().asBoolean();
+ }
+ }
+ };
+
+ // Create a step to run when the Supplier<Boolean> is true. Because the Supplier<Boolean> will
+ // be false, this step should not be run.
+ final AtomicInteger numCalls = new AtomicInteger(0);
+ Step stepToRunWhenSupplierIsTrue = new AbstractExecutionStep("inc") {
+ @Override
+ public int execute(ExecutionContext context) {
+ numCalls.incrementAndGet();
+ throw new IllegalStateException("This step should not be executed.");
+ }
+ };
+
+ // Create and execute the ConditionalStep.
+ ConditionalStep conditionalStep = new ConditionalStep(conditional, stepToRunWhenSupplierIsTrue);
+ condition.set(TriState.FALSE);
+ ExecutionContext context = TestExecutionContext.newInstance();
+ int exitCode = conditionalStep.execute(context);
+ assertEquals("stepToRunWhenSupplierIsTrue should not have been run.", 0, numCalls.get());
+ assertEquals("The exit code should be zero when the conditional is false.", 0, exitCode);
+ assertEquals("inc", conditionalStep.getShortName());
+ assertEquals("conditionally: inc", conditionalStep.getDescription(context));
+ }
+}