aurora-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From wfar...@apache.org
Subject git commit: Add a factory to produce OneWayUpdate instances based on job state and update settings.
Date Fri, 05 Sep 2014 20:27:51 GMT
Repository: incubator-aurora
Updated Branches:
  refs/heads/master 9a602a779 -> eafb80ac6


Add a factory to produce OneWayUpdate instances based on job state and update settings.

Bugs closed: AURORA-613

Reviewed at https://reviews.apache.org/r/25356/


Project: http://git-wip-us.apache.org/repos/asf/incubator-aurora/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-aurora/commit/eafb80ac
Tree: http://git-wip-us.apache.org/repos/asf/incubator-aurora/tree/eafb80ac
Diff: http://git-wip-us.apache.org/repos/asf/incubator-aurora/diff/eafb80ac

Branch: refs/heads/master
Commit: eafb80ac69fe59535168cc814b4a4c83fba7c436
Parents: 9a602a7
Author: Bill Farner <wfarner@apache.org>
Authored: Fri Sep 5 13:19:17 2014 -0700
Committer: Bill Farner <wfarner@apache.org>
Committed: Fri Sep 5 13:19:17 2014 -0700

----------------------------------------------------------------------
 .../scheduler/updater/InstanceUpdater.java      |   6 +-
 .../scheduler/updater/OneWayJobUpdater.java     |  15 +-
 .../updater/OneWayJobUpdaterFactory.java        | 176 ++++++++++++++
 .../scheduler/updater/StateEvaluator.java       |   2 +-
 .../scheduler/updater/InstanceUpdaterTest.java  |  30 +--
 .../updater/OneWayJobUpdateControllerTest.java  | 232 +++++++++++++++++++
 .../OneWayJobUpdaterFactoryImplTest.java        | 110 +++++++++
 .../scheduler/updater/OneWayJobUpdaterTest.java | 232 -------------------
 8 files changed, 548 insertions(+), 255 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/eafb80ac/src/main/java/org/apache/aurora/scheduler/updater/InstanceUpdater.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/updater/InstanceUpdater.java b/src/main/java/org/apache/aurora/scheduler/updater/InstanceUpdater.java
index 85196af..d725bc3 100644
--- a/src/main/java/org/apache/aurora/scheduler/updater/InstanceUpdater.java
+++ b/src/main/java/org/apache/aurora/scheduler/updater/InstanceUpdater.java
@@ -33,7 +33,7 @@ import org.apache.aurora.scheduler.storage.entities.ITaskEvent;
 import static java.util.Objects.requireNonNull;
 
 import static org.apache.aurora.gen.ScheduleStatus.RUNNING;
-import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.EVALUATE_AFTER_RUNNING_LIMIT;
+import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.EVALUATE_AFTER_MIN_RUNNING_MS;
 import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.EVALUATE_ON_STATE_CHANGE;
 import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.FAILED;
 import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE;
@@ -146,7 +146,7 @@ class InstanceUpdater implements StateEvaluator<Optional<IScheduledTask>>
{
           return SUCCEEDED;
         } else {
           // Not running long enough to consider stable, check again later.
-          return EVALUATE_AFTER_RUNNING_LIMIT;
+          return EVALUATE_AFTER_MIN_RUNNING_MS;
         }
       } else if (Tasks.isTerminated(status)) {
         // The desired task has terminated, this is a failure.
@@ -160,7 +160,7 @@ class InstanceUpdater implements StateEvaluator<Optional<IScheduledTask>>
{
             : KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE;
       } else {
         // The task is in a transient state on the way into or out of running, check back
later.
-        return EVALUATE_AFTER_RUNNING_LIMIT;
+        return EVALUATE_AFTER_MIN_RUNNING_MS;
       }
     } else {
       // This is not the configuration that we would like to run.

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/eafb80ac/src/main/java/org/apache/aurora/scheduler/updater/OneWayJobUpdater.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/updater/OneWayJobUpdater.java b/src/main/java/org/apache/aurora/scheduler/updater/OneWayJobUpdater.java
index aa8b5f2..d6a1e4b 100644
--- a/src/main/java/org/apache/aurora/scheduler/updater/OneWayJobUpdater.java
+++ b/src/main/java/org/apache/aurora/scheduler/updater/OneWayJobUpdater.java
@@ -17,11 +17,13 @@ import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 
+import com.google.common.annotations.VisibleForTesting;
 import com.google.common.base.Function;
 import com.google.common.base.Optional;
 import com.google.common.base.Preconditions;
 import com.google.common.base.Predicates;
 import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
 import com.google.common.collect.Maps;
 import com.twitter.common.util.StateMachine;
 
@@ -106,8 +108,8 @@ class OneWayJobUpdater<K, T> {
         return Optional.of(InstanceAction.REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE);
       case KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE:
         return Optional.of(InstanceAction.KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE);
-      case EVALUATE_AFTER_RUNNING_LIMIT:
-        return Optional.of(InstanceAction.EVALUATE_AFTER_RUNNING_LIMIT);
+      case EVALUATE_AFTER_MIN_RUNNING_MS:
+        return Optional.of(InstanceAction.EVALUATE_AFTER_MIN_RUNNING_MS);
       default:
         break;
     }
@@ -115,10 +117,15 @@ class OneWayJobUpdater<K, T> {
     return Optional.absent();
   }
 
+  @VisibleForTesting
+  Set<K> getInstances() {
+    return ImmutableSet.copyOf(instances.keySet());
+  }
+
   /**
    * Performs an evaluation of the job.  An evaluation would normally be triggered to initiate
the
    * update, as a result of a state change relevant to the update, or due to a
-   * {@link InstanceAction#EVALUATE_AFTER_RUNNING_LIMIT requested} instance re-evaluation.
+   * {@link InstanceAction#EVALUATE_AFTER_MIN_RUNNING_MS requested} instance re-evaluation.
    *
    * @param instancesNeedingUpdate Instances triggering the event, if any.
    * @param stateProvider Provider to fetch state of instances, and pass to
@@ -270,7 +277,7 @@ class OneWayJobUpdater<K, T> {
     EVALUATE_ON_STATE_CHANGE,
     REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE,
     KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE,
-    EVALUATE_AFTER_RUNNING_LIMIT
+    EVALUATE_AFTER_MIN_RUNNING_MS
   }
 
   /**

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/eafb80ac/src/main/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterFactory.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterFactory.java
b/src/main/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterFactory.java
new file mode 100644
index 0000000..80baa7f
--- /dev/null
+++ b/src/main/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterFactory.java
@@ -0,0 +1,176 @@
+/**
+ * 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 org.apache.aurora.scheduler.updater;
+
+import java.util.Set;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.DiscreteDomain;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableRangeSet;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Range;
+import com.google.common.collect.Sets;
+import com.google.inject.Inject;
+import com.twitter.common.quantity.Amount;
+import com.twitter.common.quantity.Time;
+import com.twitter.common.util.Clock;
+
+import org.apache.aurora.scheduler.storage.entities.IInstanceTaskConfig;
+import org.apache.aurora.scheduler.storage.entities.IJobUpdateConfiguration;
+import org.apache.aurora.scheduler.storage.entities.IJobUpdateSettings;
+import org.apache.aurora.scheduler.storage.entities.IRange;
+import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
+import org.apache.aurora.scheduler.storage.entities.ITaskConfig;
+import org.apache.aurora.scheduler.updater.strategy.QueueStrategy;
+import org.apache.aurora.scheduler.updater.strategy.UpdateStrategy;
+
+import static java.util.Objects.requireNonNull;
+
+/**
+ * A factory that produces one-way job updaters based on a job update configuration.
+ */
+interface OneWayJobUpdaterFactory {
+
+  /**
+   * Creates a one-way job updater that will execute the job update configuration in the
direction
+   * specified by {@code rollingForward}.
+   *
+   * @param configuration Configuration to act on.
+   * @param rollingForward {@code true} if this is a job update, {@code false} if it is a
rollback.
+   * @return A one-way updater that will execute the job update as specified in the
+   *         {@code configuration}.
+   * @throws UpdateConfigurationException If the provided configuration cannot be used.
+   */
+  OneWayJobUpdater<Integer, Optional<IScheduledTask>> newUpdate(
+      IJobUpdateConfiguration configuration,
+      boolean rollingForward) throws UpdateConfigurationException;
+
+  /**
+   * Thrown when an invalid job update configuration is encountered.
+   */
+  class UpdateConfigurationException extends Exception {
+    UpdateConfigurationException(String msg) {
+      super(msg);
+    }
+  }
+
+  class OneWayJobUpdaterFactoryImpl implements OneWayJobUpdaterFactory {
+    private final Clock clock;
+
+    @Inject
+    OneWayJobUpdaterFactoryImpl(Clock clock) {
+      this.clock = requireNonNull(clock);
+    }
+
+    @Override
+    public OneWayJobUpdater<Integer, Optional<IScheduledTask>> newUpdate(
+        IJobUpdateConfiguration configuration,
+        boolean rollingForward) throws UpdateConfigurationException {
+
+      requireNonNull(configuration);
+
+      Set<Integer> instances;
+      IJobUpdateSettings settings = configuration.getSettings();
+      Range<Integer> updateConfigurationInstances =
+          Range.closedOpen(0, configuration.getInstanceCount());
+      if (settings.getUpdateOnlyTheseInstances().isEmpty()) {
+        Set<Integer> newInstanceIds =
+            ImmutableRangeSet.of(updateConfigurationInstances).asSet(DiscreteDomain.integers());
+
+        // In a full job update, the working set is the union of instance IDs before and
after.
+        instances =  ImmutableSet.copyOf(
+            Sets.union(expandInstanceIds(configuration.getOldTaskConfigs()), newInstanceIds));
+      } else {
+        instances = rangesToInstanceIds(settings.getUpdateOnlyTheseInstances());
+
+        if (!updateConfigurationInstances.containsAll(instances)) {
+          throw new UpdateConfigurationException(
+              "When updating specific instances, "
+                  + "all specified instances must be in the update configuration.");
+        }
+      }
+
+      ImmutableMap.Builder<Integer, StateEvaluator<Optional<IScheduledTask>>>
evaluators =
+          ImmutableMap.builder();
+      for (int instanceId : instances) {
+        Optional<ITaskConfig> desiredState;
+        if (rollingForward) {
+          desiredState = updateConfigurationInstances.contains(instanceId)
+              ? Optional.of(configuration.getNewTaskConfig())
+              : Optional.<ITaskConfig>absent();
+        } else {
+          desiredState = getConfig(instanceId, configuration.getOldTaskConfigs());
+        }
+
+        evaluators.put(
+            instanceId,
+            new InstanceUpdater(
+                desiredState,
+                settings.getMaxPerInstanceFailures(),
+                Amount.of((long) settings.getMinWaitInInstanceRunningMs(), Time.MILLISECONDS),
+                Amount.of((long) settings.getMaxWaitToInstanceRunningMs(), Time.MILLISECONDS),
+                clock));
+      }
+
+      // TODO(wfarner): Add the batch_completion flag to JobUpdateSettings and pick correct
+      // strategy.
+      UpdateStrategy<Integer> strategy = new QueueStrategy<>(settings.getUpdateGroupSize());
+
+      return new OneWayJobUpdater<>(
+          strategy,
+          settings.getMaxFailedInstances(),
+          evaluators.build());
+    }
+
+    private static Range<Integer> toRange(IRange range) {
+      return Range.closed(range.getFirst(), range.getLast());
+    }
+
+    private static Set<Integer> rangesToInstanceIds(Set<IRange> ranges) {
+      ImmutableRangeSet.Builder<Integer> instanceIds = ImmutableRangeSet.builder();
+      for (IRange range : ranges) {
+        instanceIds.add(toRange(range));
+      }
+
+      return instanceIds.build().asSet(DiscreteDomain.integers());
+    }
+
+    private static Set<Integer> expandInstanceIds(Set<IInstanceTaskConfig> instanceGroups)
{
+      ImmutableRangeSet.Builder<Integer> instanceIds = ImmutableRangeSet.builder();
+      for (IInstanceTaskConfig group : instanceGroups) {
+        for (IRange range : group.getInstances()) {
+          instanceIds.add(toRange(range));
+        }
+      }
+
+      return instanceIds.build().asSet(DiscreteDomain.integers());
+    }
+
+    private static Optional<ITaskConfig> getConfig(
+        int id,
+        Set<IInstanceTaskConfig> instanceGroups) {
+
+      for (IInstanceTaskConfig group : instanceGroups) {
+        for (IRange range : group.getInstances()) {
+          if (toRange(range).contains(id)) {
+            return Optional.of(group.getTask());
+          }
+        }
+      }
+
+      return Optional.absent();
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/eafb80ac/src/main/java/org/apache/aurora/scheduler/updater/StateEvaluator.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/apache/aurora/scheduler/updater/StateEvaluator.java b/src/main/java/org/apache/aurora/scheduler/updater/StateEvaluator.java
index dca55ef..453daeb 100644
--- a/src/main/java/org/apache/aurora/scheduler/updater/StateEvaluator.java
+++ b/src/main/java/org/apache/aurora/scheduler/updater/StateEvaluator.java
@@ -47,7 +47,7 @@ interface StateEvaluator<T> {
     EVALUATE_ON_STATE_CHANGE,
     REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE,
     KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE,
-    EVALUATE_AFTER_RUNNING_LIMIT,
+    EVALUATE_AFTER_MIN_RUNNING_MS,
     SUCCEEDED,
     FAILED
   }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/eafb80ac/src/test/java/org/apache/aurora/scheduler/updater/InstanceUpdaterTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/updater/InstanceUpdaterTest.java b/src/test/java/org/apache/aurora/scheduler/updater/InstanceUpdaterTest.java
index d7baaa6..f38e6a3 100644
--- a/src/test/java/org/apache/aurora/scheduler/updater/InstanceUpdaterTest.java
+++ b/src/test/java/org/apache/aurora/scheduler/updater/InstanceUpdaterTest.java
@@ -35,7 +35,7 @@ import static org.apache.aurora.gen.ScheduleStatus.PENDING;
 import static org.apache.aurora.gen.ScheduleStatus.RUNNING;
 import static org.apache.aurora.gen.ScheduleStatus.STARTING;
 import static org.apache.aurora.scheduler.updater.StateEvaluator.Result;
-import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.EVALUATE_AFTER_RUNNING_LIMIT;
+import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.EVALUATE_AFTER_MIN_RUNNING_MS;
 import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.EVALUATE_ON_STATE_CHANGE;
 import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE;
 import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE;
@@ -120,7 +120,7 @@ public class InstanceUpdaterTest {
     f.evaluate(EVALUATE_ON_STATE_CHANGE, KILLING);
     f.evaluate(REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE, FINISHED);
     f.setActualState(NEW);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, PENDING, ASSIGNED, STARTING, RUNNING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, PENDING, ASSIGNED, STARTING, RUNNING);
     f.advanceTime(MIN_RUNNING_TIME);
     f.evaluateCurrentState(SUCCEEDED);
   }
@@ -133,10 +133,10 @@ public class InstanceUpdaterTest {
     f.evaluate(EVALUATE_ON_STATE_CHANGE, KILLING);
     f.evaluate(REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE, FINISHED);
     f.setActualState(NEW);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, PENDING, ASSIGNED, STARTING, RUNNING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, PENDING, ASSIGNED, STARTING, RUNNING);
     f.evaluate(EVALUATE_ON_STATE_CHANGE, FAILED);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, PENDING, ASSIGNED, STARTING);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, RUNNING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, PENDING, ASSIGNED, STARTING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, RUNNING);
     f.advanceTime(MIN_RUNNING_TIME);
     f.evaluateCurrentState(SUCCEEDED);
   }
@@ -149,7 +149,7 @@ public class InstanceUpdaterTest {
     f.evaluate(EVALUATE_ON_STATE_CHANGE, KILLING);
     f.evaluate(REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE, FINISHED);
     f.setActualState(NEW);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, PENDING, ASSIGNED, STARTING, RUNNING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, PENDING, ASSIGNED, STARTING, RUNNING);
     f.evaluate(Result.FAILED, FAILED);
   }
 
@@ -157,7 +157,7 @@ public class InstanceUpdaterTest {
   public void testNoopUpdate() {
     TestFixture f = new TestFixture(NEW, 1);
     f.setActualState(NEW);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, RUNNING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, RUNNING);
     f.advanceTime(MIN_RUNNING_TIME);
     f.evaluate(SUCCEEDED, RUNNING);
   }
@@ -175,7 +175,7 @@ public class InstanceUpdaterTest {
     f.setActualStateAbsent();
     f.evaluateCurrentState(REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE);
     f.setActualState(NEW);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, PENDING, ASSIGNED, STARTING, RUNNING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, PENDING, ASSIGNED, STARTING, RUNNING);
     f.advanceTime(MIN_RUNNING_TIME);
     f.evaluateCurrentState(SUCCEEDED);
   }
@@ -198,13 +198,13 @@ public class InstanceUpdaterTest {
     f.evaluate(EVALUATE_ON_STATE_CHANGE, KILLING);
     f.evaluate(REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE, FINISHED);
     f.setActualState(NEW);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, PENDING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, PENDING);
     f.advanceTime(MAX_NON_RUNNING_TIME);
     f.evaluateCurrentState(KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE);
     f.setActualStateAbsent();
     f.evaluateCurrentState(REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE);
     f.setActualState(NEW);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, PENDING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, PENDING);
     f.advanceTime(MAX_NON_RUNNING_TIME);
     f.evaluateCurrentState(Result.FAILED);
   }
@@ -217,15 +217,15 @@ public class InstanceUpdaterTest {
     f.evaluate(EVALUATE_ON_STATE_CHANGE, KILLING);
     f.evaluate(REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE, FINISHED);
     f.setActualState(NEW);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, PENDING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, PENDING);
     f.advanceTime(MAX_NON_RUNNING_TIME);
     f.evaluateCurrentState(KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE);
     f.setActualStateAbsent();
     f.evaluateCurrentState(REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE);
     f.setActualState(NEW);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, PENDING);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, ASSIGNED, STARTING, RUNNING);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, KILLING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, PENDING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, ASSIGNED, STARTING, RUNNING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, KILLING);
     f.advanceTime(MAX_NON_RUNNING_TIME);
     f.evaluateCurrentState(Result.FAILED);
   }
@@ -264,7 +264,7 @@ public class InstanceUpdaterTest {
     f.setActualStateAbsent();
     f.evaluateCurrentState(REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE);
     f.setActualState(NEW);
-    f.evaluate(EVALUATE_AFTER_RUNNING_LIMIT, PENDING, ASSIGNED, STARTING, RUNNING);
+    f.evaluate(EVALUATE_AFTER_MIN_RUNNING_MS, PENDING, ASSIGNED, STARTING, RUNNING);
     f.advanceTime(MIN_RUNNING_TIME);
     f.evaluateCurrentState(SUCCEEDED);
   }

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/eafb80ac/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdateControllerTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdateControllerTest.java
b/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdateControllerTest.java
new file mode 100644
index 0000000..f0b6835
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdateControllerTest.java
@@ -0,0 +1,232 @@
+/**
+ * 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 org.apache.aurora.scheduler.updater;
+
+import java.util.Map;
+import java.util.Set;
+
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.twitter.common.testing.easymock.EasyMockTest;
+
+import org.apache.aurora.scheduler.updater.strategy.UpdateStrategy;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.apache.aurora.scheduler.updater.OneWayJobUpdater.EvaluationResult;
+import static org.apache.aurora.scheduler.updater.OneWayJobUpdater.InstanceAction;
+import static org.apache.aurora.scheduler.updater.OneWayJobUpdater.OneWayStatus;
+import static org.apache.aurora.scheduler.updater.StateEvaluator.Result;
+import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.EVALUATE_AFTER_MIN_RUNNING_MS;
+import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.EVALUATE_ON_STATE_CHANGE;
+import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.FAILED;
+import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE;
+import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE;
+import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.SUCCEEDED;
+import static org.easymock.EasyMock.expect;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.fail;
+
+public class OneWayJobUpdateControllerTest extends EasyMockTest {
+  private static final Set<Integer> EMPTY = ImmutableSet.of();
+  private static final Map<Integer, InstanceAction> NO_ACTIONS = ImmutableMap.of();
+
+  private UpdateStrategy<Integer> strategy;
+  private StateEvaluator<String> instance0;
+  private StateEvaluator<String> instance1;
+  private StateEvaluator<String> instance2;
+  private StateEvaluator<String> instance3;
+  private Map<Integer, StateEvaluator<String>> allInstances;
+  private InstanceStateProvider<Integer, String> stateProvider;
+
+  private OneWayJobUpdater<Integer, String> jobUpdater;
+
+  @Before
+  public void setUp() {
+    strategy = createMock(new Clazz<UpdateStrategy<Integer>>() { });
+    instance0 = createMock(new Clazz<StateEvaluator<String>>() { });
+    instance1 = createMock(new Clazz<StateEvaluator<String>>() { });
+    instance2 = createMock(new Clazz<StateEvaluator<String>>() { });
+    instance3 = createMock(new Clazz<StateEvaluator<String>>() { });
+    allInstances = ImmutableMap.of(
+        0, instance0,
+        1, instance1,
+        2, instance2,
+        3, instance3);
+    stateProvider = createMock(new Clazz<InstanceStateProvider<Integer, String>>()
{ });
+  }
+
+  private void evaluate(OneWayStatus expectedStatus, Map<Integer, InstanceAction> expectedActions)
{
+    assertEquals(
+        new EvaluationResult<>(expectedStatus, expectedActions),
+        jobUpdater.evaluate(ImmutableSet.<Integer>of(), stateProvider));
+  }
+
+  private void evaluate(
+      int instanceId,
+      OneWayStatus expectedStatus,
+      Map<Integer, InstanceAction> expectedActions) {
+
+    assertEquals(
+        new EvaluationResult<>(expectedStatus, expectedActions),
+        jobUpdater.evaluate(ImmutableSet.of(instanceId), stateProvider));
+  }
+
+  private void expectEvaluate(
+      int instanceId,
+      StateEvaluator<String> instanceMock,
+      String state,
+      Result result) {
+
+    expect(stateProvider.getState(instanceId)).andReturn(state);
+    expect(instanceMock.evaluate(state)).andReturn(result);
+  }
+
+  @Test
+  public void testSuccessfulUpdate() {
+    expect(strategy.getNextGroup(ImmutableSet.of(0, 1, 2, 3), EMPTY))
+        .andReturn(ImmutableSet.of(0, 2));
+    String s0 = "0";
+    String s1 = "1";
+    String s2 = "2";
+    String s3 = "3";
+    expectEvaluate(
+        0,
+        instance0,
+        s0,
+        KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE);
+    expectEvaluate(
+        2,
+        instance2,
+        s2,
+        REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE);
+
+    expectEvaluate(0, instance0, s0, EVALUATE_ON_STATE_CHANGE);
+    expect(strategy.getNextGroup(ImmutableSet.of(1, 3), ImmutableSet.of(0, 2))).andReturn(EMPTY);
+    expectEvaluate(0, instance0, s0, SUCCEEDED);
+    expect(strategy.getNextGroup(ImmutableSet.of(1, 3), ImmutableSet.of(2))).andReturn(EMPTY);
+    expectEvaluate(2, instance2, s2, SUCCEEDED);
+    expect(strategy.getNextGroup(ImmutableSet.of(1, 3), EMPTY))
+        .andReturn(ImmutableSet.of(1, 3));
+    expectEvaluate(
+        1,
+        instance1,
+        s1,
+        SUCCEEDED);
+    expectEvaluate(
+        3,
+        instance3,
+        s3,
+        EVALUATE_AFTER_MIN_RUNNING_MS);
+    expectEvaluate(3, instance3, s3, SUCCEEDED);
+
+    control.replay();
+
+    jobUpdater = new OneWayJobUpdater<>(strategy, 0, allInstances);
+
+    evaluate(
+        OneWayStatus.WORKING,
+        ImmutableMap.of(
+            0, InstanceAction.KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE,
+            2, InstanceAction.REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE));
+    evaluate(
+        0,
+        OneWayStatus.WORKING,
+        ImmutableMap.of(0, InstanceAction.EVALUATE_ON_STATE_CHANGE));
+    evaluate(
+        0,
+        OneWayStatus.WORKING,
+        NO_ACTIONS);
+    evaluate(
+        2,
+        OneWayStatus.WORKING,
+        ImmutableMap.of(
+            3, InstanceAction.EVALUATE_AFTER_MIN_RUNNING_MS));
+    evaluate(
+        3,
+        OneWayStatus.SUCCEEDED,
+        NO_ACTIONS);
+  }
+
+  @Test
+  public void testFailedUpdate() {
+    expect(strategy.getNextGroup(ImmutableSet.of(0, 1, 2, 3), EMPTY))
+        .andReturn(ImmutableSet.of(0, 1));
+    String s0 = "0";
+    String s1 = "1";
+    expectEvaluate(
+        0,
+        instance0,
+        s0,
+        FAILED);
+    expectEvaluate(
+        1,
+        instance1,
+        s1,
+        KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE);
+
+    control.replay();
+
+    jobUpdater = new OneWayJobUpdater<>(strategy, 0, allInstances);
+
+    evaluate(
+        OneWayStatus.FAILED,
+        ImmutableMap.of(
+            1, InstanceAction.KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE));
+
+    // The updater should now reject further attempts to evaluate.
+    try {
+      jobUpdater.evaluate(ImmutableSet.<Integer>of(), stateProvider);
+      fail();
+    } catch (IllegalStateException e) {
+      // Expected.
+    }
+  }
+
+  @Test(expected = IllegalArgumentException.class)
+  public void testBadInput() {
+    control.replay();
+
+    new OneWayJobUpdater<>(strategy, 0, ImmutableMap.<Integer, StateEvaluator<String>>of());
+  }
+
+  @Test
+  public void testEvaluateCompletedInstance() {
+    expect(strategy.getNextGroup(ImmutableSet.of(0, 1, 2, 3), EMPTY))
+        .andReturn(ImmutableSet.of(0));
+    expect(strategy.getNextGroup(ImmutableSet.of(1, 2, 3), EMPTY))
+        .andReturn(ImmutableSet.<Integer>of());
+    String s0 = "0";
+    expectEvaluate(
+        0,
+        instance0,
+        s0,
+        SUCCEEDED);
+
+    control.replay();
+
+    jobUpdater = new OneWayJobUpdater<>(strategy, 0, allInstances);
+
+    evaluate(
+        OneWayStatus.WORKING,
+        NO_ACTIONS);
+
+    // Instance 0 is already considered finished, so any further notifications of its state
will
+    // no-op.
+    evaluate(
+        0,
+        OneWayStatus.WORKING,
+        NO_ACTIONS);
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/eafb80ac/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterFactoryImplTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterFactoryImplTest.java
b/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterFactoryImplTest.java
new file mode 100644
index 0000000..ae65462
--- /dev/null
+++ b/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterFactoryImplTest.java
@@ -0,0 +1,110 @@
+/**
+ * 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 org.apache.aurora.scheduler.updater;
+
+import com.google.common.base.Optional;
+import com.google.common.collect.ImmutableSet;
+import com.twitter.common.util.testing.FakeClock;
+
+import org.apache.aurora.gen.InstanceTaskConfig;
+import org.apache.aurora.gen.JobUpdateConfiguration;
+import org.apache.aurora.gen.JobUpdateSettings;
+import org.apache.aurora.gen.Range;
+import org.apache.aurora.gen.TaskConfig;
+import org.apache.aurora.scheduler.storage.entities.IJobUpdateConfiguration;
+import org.apache.aurora.scheduler.storage.entities.IScheduledTask;
+import org.junit.Before;
+import org.junit.Test;
+
+import static org.apache.aurora.scheduler.updater.OneWayJobUpdaterFactory.UpdateConfigurationException;
+import static org.junit.Assert.assertEquals;
+
+/**
+ * This test can't exercise much functionality of the output from the factory without duplicating
+ * test behavior in the job updater integration test.  So instead, we test only some basic
behavior.
+ */
+public class OneWayJobUpdaterFactoryImplTest {
+
+  private static final IJobUpdateConfiguration CONFIG = IJobUpdateConfiguration.build(
+      new JobUpdateConfiguration()
+          .setNewTaskConfig(new TaskConfig())
+          .setInstanceCount(3)
+          .setOldTaskConfigs(ImmutableSet.of(new InstanceTaskConfig()
+              .setInstances(ImmutableSet.of(new Range(1, 2)))
+              .setTask(new TaskConfig())))
+          .setSettings(new JobUpdateSettings()
+              .setMaxFailedInstances(1)
+              .setMaxPerInstanceFailures(1)
+              .setMaxWaitToInstanceRunningMs(100)
+              .setMinWaitInInstanceRunningMs(100)
+              .setUpdateGroupSize(2)
+              .setUpdateOnlyTheseInstances(ImmutableSet.<Range>of())));
+
+  private OneWayJobUpdaterFactory factory;
+
+  @Before
+  public void setUp() {
+    factory = new OneWayJobUpdaterFactory.OneWayJobUpdaterFactoryImpl(new FakeClock());
+  }
+
+  @Test
+  public void testRollingForward() throws Exception  {
+    OneWayJobUpdater<Integer, Optional<IScheduledTask>> update = factory.newUpdate(CONFIG,
true);
+    assertEquals(ImmutableSet.of(0, 1, 2), update.getInstances());
+  }
+
+  @Test
+  public void testRollingBack() throws Exception {
+    OneWayJobUpdater<Integer, Optional<IScheduledTask>> update = factory.newUpdate(CONFIG,
false);
+    assertEquals(ImmutableSet.of(0, 1, 2), update.getInstances());
+  }
+
+  @Test
+  public void testRollForwardSpecificInstances() throws Exception {
+    JobUpdateConfiguration config = CONFIG.newBuilder();
+    config.getSettings().setUpdateOnlyTheseInstances(ImmutableSet.of(new Range(1, 2)));
+
+    OneWayJobUpdater<Integer, Optional<IScheduledTask>> update =
+        factory.newUpdate(IJobUpdateConfiguration.build(config), true);
+    assertEquals(ImmutableSet.of(1, 2), update.getInstances());
+  }
+
+  @Test
+  public void testRollBackSpecificInstances() throws Exception {
+    JobUpdateConfiguration config = CONFIG.newBuilder();
+    config.getSettings().setUpdateOnlyTheseInstances(ImmutableSet.of(new Range(1, 2)));
+
+    OneWayJobUpdater<Integer, Optional<IScheduledTask>> update =
+        factory.newUpdate(IJobUpdateConfiguration.build(config), false);
+    assertEquals(ImmutableSet.of(1, 2), update.getInstances());
+  }
+
+  @Test(expected = UpdateConfigurationException.class)
+  public void testInvalidConfiguration() throws Exception {
+    JobUpdateConfiguration config = CONFIG.newBuilder();
+    config.getSettings().setUpdateOnlyTheseInstances(ImmutableSet.of(new Range(10, 10)));
+
+    factory.newUpdate(IJobUpdateConfiguration.build(config), true);
+  }
+
+  @Test
+  public void testUpdateRemovesInstance() throws Exception {
+    JobUpdateConfiguration config = CONFIG.newBuilder();
+    config.setInstanceCount(2);
+
+    OneWayJobUpdater<Integer, Optional<IScheduledTask>> update =
+        factory.newUpdate(IJobUpdateConfiguration.build(config), true);
+    assertEquals(ImmutableSet.of(0, 1, 2), update.getInstances());
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-aurora/blob/eafb80ac/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterTest.java
----------------------------------------------------------------------
diff --git a/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterTest.java b/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterTest.java
deleted file mode 100644
index e3e50d7..0000000
--- a/src/test/java/org/apache/aurora/scheduler/updater/OneWayJobUpdaterTest.java
+++ /dev/null
@@ -1,232 +0,0 @@
-/**
- * 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 org.apache.aurora.scheduler.updater;
-
-import java.util.Map;
-import java.util.Set;
-
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
-import com.twitter.common.testing.easymock.EasyMockTest;
-
-import org.apache.aurora.scheduler.updater.strategy.UpdateStrategy;
-import org.junit.Before;
-import org.junit.Test;
-
-import static org.apache.aurora.scheduler.updater.OneWayJobUpdater.EvaluationResult;
-import static org.apache.aurora.scheduler.updater.OneWayJobUpdater.InstanceAction;
-import static org.apache.aurora.scheduler.updater.OneWayJobUpdater.OneWayStatus;
-import static org.apache.aurora.scheduler.updater.StateEvaluator.Result;
-import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.EVALUATE_AFTER_RUNNING_LIMIT;
-import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.EVALUATE_ON_STATE_CHANGE;
-import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.FAILED;
-import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE;
-import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE;
-import static org.apache.aurora.scheduler.updater.StateEvaluator.Result.SUCCEEDED;
-import static org.easymock.EasyMock.expect;
-import static org.junit.Assert.assertEquals;
-import static org.junit.Assert.fail;
-
-public class OneWayJobUpdaterTest extends EasyMockTest {
-  private static final Set<Integer> EMPTY = ImmutableSet.of();
-  private static final Map<Integer, InstanceAction> NO_ACTIONS = ImmutableMap.of();
-
-  private UpdateStrategy<Integer> strategy;
-  private StateEvaluator<String> instance0;
-  private StateEvaluator<String> instance1;
-  private StateEvaluator<String> instance2;
-  private StateEvaluator<String> instance3;
-  private Map<Integer, StateEvaluator<String>> allInstances;
-  private InstanceStateProvider<Integer, String> stateProvider;
-
-  private OneWayJobUpdater<Integer, String> jobUpdater;
-
-  @Before
-  public void setUp() {
-    strategy = createMock(new Clazz<UpdateStrategy<Integer>>() { });
-    instance0 = createMock(new Clazz<StateEvaluator<String>>() { });
-    instance1 = createMock(new Clazz<StateEvaluator<String>>() { });
-    instance2 = createMock(new Clazz<StateEvaluator<String>>() { });
-    instance3 = createMock(new Clazz<StateEvaluator<String>>() { });
-    allInstances = ImmutableMap.of(
-        0, instance0,
-        1, instance1,
-        2, instance2,
-        3, instance3);
-    stateProvider = createMock(new Clazz<InstanceStateProvider<Integer, String>>()
{ });
-  }
-
-  private void evaluate(OneWayStatus expectedStatus, Map<Integer, InstanceAction> expectedActions)
{
-    assertEquals(
-        new EvaluationResult<>(expectedStatus, expectedActions),
-        jobUpdater.evaluate(ImmutableSet.<Integer>of(), stateProvider));
-  }
-
-  private void evaluate(
-      int instanceId,
-      OneWayStatus expectedStatus,
-      Map<Integer, InstanceAction> expectedActions) {
-
-    assertEquals(
-        new EvaluationResult<>(expectedStatus, expectedActions),
-        jobUpdater.evaluate(ImmutableSet.of(instanceId), stateProvider));
-  }
-
-  private void expectEvaluate(
-      int instanceId,
-      StateEvaluator<String> instanceMock,
-      String state,
-      Result result) {
-
-    expect(stateProvider.getState(instanceId)).andReturn(state);
-    expect(instanceMock.evaluate(state)).andReturn(result);
-  }
-
-  @Test
-  public void testSuccessfulUpdate() {
-    expect(strategy.getNextGroup(ImmutableSet.of(0, 1, 2, 3), EMPTY))
-        .andReturn(ImmutableSet.of(0, 2));
-    String s0 = "0";
-    String s1 = "1";
-    String s2 = "2";
-    String s3 = "3";
-    expectEvaluate(
-        0,
-        instance0,
-        s0,
-        KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE);
-    expectEvaluate(
-        2,
-        instance2,
-        s2,
-        REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE);
-
-    expectEvaluate(0, instance0, s0, EVALUATE_ON_STATE_CHANGE);
-    expect(strategy.getNextGroup(ImmutableSet.of(1, 3), ImmutableSet.of(0, 2))).andReturn(EMPTY);
-    expectEvaluate(0, instance0, s0, SUCCEEDED);
-    expect(strategy.getNextGroup(ImmutableSet.of(1, 3), ImmutableSet.of(2))).andReturn(EMPTY);
-    expectEvaluate(2, instance2, s2, SUCCEEDED);
-    expect(strategy.getNextGroup(ImmutableSet.of(1, 3), EMPTY))
-        .andReturn(ImmutableSet.of(1, 3));
-    expectEvaluate(
-        1,
-        instance1,
-        s1,
-        SUCCEEDED);
-    expectEvaluate(
-        3,
-        instance3,
-        s3,
-        EVALUATE_AFTER_RUNNING_LIMIT);
-    expectEvaluate(3, instance3, s3, SUCCEEDED);
-
-    control.replay();
-
-    jobUpdater = new OneWayJobUpdater<>(strategy, 0, allInstances);
-
-    evaluate(
-        OneWayStatus.WORKING,
-        ImmutableMap.of(
-            0, InstanceAction.KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE,
-            2, InstanceAction.REPLACE_TASK_AND_EVALUATE_ON_STATE_CHANGE));
-    evaluate(
-        0,
-        OneWayStatus.WORKING,
-        ImmutableMap.of(0, InstanceAction.EVALUATE_ON_STATE_CHANGE));
-    evaluate(
-        0,
-        OneWayStatus.WORKING,
-        NO_ACTIONS);
-    evaluate(
-        2,
-        OneWayStatus.WORKING,
-        ImmutableMap.of(
-            3, InstanceAction.EVALUATE_AFTER_RUNNING_LIMIT));
-    evaluate(
-        3,
-        OneWayStatus.SUCCEEDED,
-        NO_ACTIONS);
-  }
-
-  @Test
-  public void testFailedUpdate() {
-    expect(strategy.getNextGroup(ImmutableSet.of(0, 1, 2, 3), EMPTY))
-        .andReturn(ImmutableSet.of(0, 1));
-    String s0 = "0";
-    String s1 = "1";
-    expectEvaluate(
-        0,
-        instance0,
-        s0,
-        FAILED);
-    expectEvaluate(
-        1,
-        instance1,
-        s1,
-        KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE);
-
-    control.replay();
-
-    jobUpdater = new OneWayJobUpdater<>(strategy, 0, allInstances);
-
-    evaluate(
-        OneWayStatus.FAILED,
-        ImmutableMap.of(
-            1, InstanceAction.KILL_TASK_AND_EVALUATE_ON_STATE_CHANGE));
-
-    // The updater should now reject further attempts to evaluate.
-    try {
-      jobUpdater.evaluate(ImmutableSet.<Integer>of(), stateProvider);
-      fail();
-    } catch (IllegalStateException e) {
-      // Expected.
-    }
-  }
-
-  @Test(expected = IllegalArgumentException.class)
-  public void testBadInput() {
-    control.replay();
-
-    new OneWayJobUpdater<>(strategy, 0, ImmutableMap.<Integer, StateEvaluator<String>>of());
-  }
-
-  @Test
-  public void testEvaluateCompletedInstance() {
-    expect(strategy.getNextGroup(ImmutableSet.of(0, 1, 2, 3), EMPTY))
-        .andReturn(ImmutableSet.of(0));
-    expect(strategy.getNextGroup(ImmutableSet.of(1, 2, 3), EMPTY))
-        .andReturn(ImmutableSet.<Integer>of());
-    String s0 = "0";
-    expectEvaluate(
-        0,
-        instance0,
-        s0,
-        SUCCEEDED);
-
-    control.replay();
-
-    jobUpdater = new OneWayJobUpdater<>(strategy, 0, allInstances);
-
-    evaluate(
-        OneWayStatus.WORKING,
-        NO_ACTIONS);
-
-    // Instance 0 is already considered finished, so any further notifications of its state
will
-    // no-op.
-    evaluate(
-        0,
-        OneWayStatus.WORKING,
-        NO_ACTIONS);
-  }
-}


Mime
View raw message