Add infra integration tests for session creation and restore.

PiperOrigin-RevId: 243251493
Change-Id: I3bf07c61816c2f948e913932e570b765313a8bb4
diff --git a/src/main/java/com/google/android/libraries/feed/common/testing/BUILD b/src/main/java/com/google/android/libraries/feed/common/testing/BUILD
index 1f14e11..6e3dc3e 100644
--- a/src/main/java/com/google/android/libraries/feed/common/testing/BUILD
+++ b/src/main/java/com/google/android/libraries/feed/common/testing/BUILD
@@ -24,6 +24,7 @@
         "//src/main/java/com/google/android/libraries/feed/feedsessionmanager",
         "//src/main/java/com/google/android/libraries/feed/feedstore",
         "//src/main/java/com/google/android/libraries/feed/host/config",
+        "//src/main/java/com/google/android/libraries/feed/host/logging",
         "//src/main/java/com/google/android/libraries/feed/host/proto",
         "//src/main/java/com/google/android/libraries/feed/host/scheduler",
         "//src/main/java/com/google/android/libraries/feed/host/storage",
@@ -32,6 +33,7 @@
         "//src/main/java/com/google/android/libraries/feed/internalapi/protocoladapter",
         "//src/main/java/com/google/android/libraries/feed/testing/host/logging",
         "//src/main/java/com/google/android/libraries/feed/testing/host/scheduler",
+        "//src/main/java/com/google/android/libraries/feed/testing/modelprovider",
         "//src/main/java/com/google/android/libraries/feed/testing/requestmanager",
         "//src/main/proto/com/google/android/libraries/feed/internalapi/proto:client_feed_java_proto_lite",
         "//src/main/proto/search/now/ui/piet:piet_java_proto_lite",
diff --git a/src/main/java/com/google/android/libraries/feed/common/testing/InfraIntegrationScope.java b/src/main/java/com/google/android/libraries/feed/common/testing/InfraIntegrationScope.java
index 9034ee3..30a36fd 100644
--- a/src/main/java/com/google/android/libraries/feed/common/testing/InfraIntegrationScope.java
+++ b/src/main/java/com/google/android/libraries/feed/common/testing/InfraIntegrationScope.java
@@ -57,7 +57,6 @@
  */
 public class InfraIntegrationScope {
   private static final boolean USE_TIMEOUT_SCHEDULER = true;
-  private static final long TIMEOUT_TIMEOUT_MS = 2;
 
   /**
    * For the TimeoutSession tests, this is how long we allow it to run before declaring a timeout
@@ -172,6 +171,10 @@
     return fakeClock;
   }
 
+  public FakeMainThreadRunner getFakeMainThreadRunner() {
+    return fakeMainThreadRunner;
+  }
+
   public FeedStore getStore() {
     return store;
   }
@@ -208,14 +211,6 @@
     }
   }
 
-  public static Configuration getTimeoutSchedulerConfig() {
-    return new Configuration.Builder()
-        .put(ConfigKey.USE_TIMEOUT_SCHEDULER, USE_TIMEOUT_SCHEDULER)
-        .put(ConfigKey.TIMEOUT_TIMEOUT_MS, TIMEOUT_TIMEOUT_MS)
-        .put(ConfigKey.USE_DIRECT_STORAGE, Boolean.FALSE)
-        .build();
-  }
-
   /** Builder for creating the {@link InfraIntegrationScope} */
   public static class Builder {
     private final FakeClock fakeClock = new FakeClock();
@@ -238,6 +233,15 @@
       return this;
     }
 
+    public Builder withTimeoutSessionConfiguration(long timeoutMs) {
+      this.configuration =
+          new Configuration.Builder()
+              .put(ConfigKey.USE_TIMEOUT_SCHEDULER, USE_TIMEOUT_SCHEDULER)
+              .put(ConfigKey.TIMEOUT_TIMEOUT_MS, timeoutMs)
+              .build();
+      return this;
+    }
+
     public InfraIntegrationScope build() {
       return new InfraIntegrationScope(
           threadUtils,
diff --git a/src/main/java/com/google/android/libraries/feed/common/testing/SessionTestUtils.java b/src/main/java/com/google/android/libraries/feed/common/testing/SessionTestUtils.java
new file mode 100644
index 0000000..b58a8f8
--- /dev/null
+++ b/src/main/java/com/google/android/libraries/feed/common/testing/SessionTestUtils.java
@@ -0,0 +1,157 @@
+// Copyright 2019 The Feed Authors.
+//
+// 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.android.libraries.feed.common.testing;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.libraries.feed.api.common.MutationContext;
+import com.google.android.libraries.feed.common.concurrent.testing.FakeThreadUtils;
+import com.google.android.libraries.feed.host.logging.RequestReason;
+import com.google.android.libraries.feed.host.scheduler.SchedulerApi.RequestBehavior;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelChild;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider.ViewDepthProvider;
+import com.google.android.libraries.feed.testing.host.scheduler.FakeSchedulerApi;
+import com.google.android.libraries.feed.testing.modelprovider.FakeViewDepthProvider;
+import com.google.search.now.wire.feed.ContentIdProto.ContentId;
+import java.util.List;
+
+/** Utilities for tests that are creating new sessions. */
+public final class SessionTestUtils {
+  public static final long TIMEOUT_MS = 50L;
+
+  private static final ContentId[] REQUEST_1 =
+      new ContentId[] {
+        ResponseBuilder.createFeatureContentId(1), ResponseBuilder.createFeatureContentId(2)
+      };
+  private static final ContentId[] REQUEST_2 =
+      new ContentId[] {
+        ResponseBuilder.createFeatureContentId(3), ResponseBuilder.createFeatureContentId(4)
+      };
+  private static final long DELAY_MS = 100L;
+  private static final long SHORT_DELAY_MS = 25L;
+
+  private final InfraIntegrationScope scope;
+  private long currentDelay;
+
+  public SessionTestUtils(@RequestBehavior int requestBehavior) {
+    scope =
+        new InfraIntegrationScope.Builder()
+            .setSchedulerApi(
+                new FakeSchedulerApi(FakeThreadUtils.withoutThreadChecks())
+                    .setRequestBehavior(requestBehavior))
+            .withTimeoutSessionConfiguration(TIMEOUT_MS)
+            .build();
+    currentDelay = DELAY_MS;
+  }
+
+  /** Shortens the delay on requests so that they complete before timeout. */
+  public SessionTestUtils requestBeforeTimeout() {
+    currentDelay = SHORT_DELAY_MS;
+    return this;
+  }
+
+  /** Returns the {@link InfraIntegrationScope}. */
+  public InfraIntegrationScope getScope() {
+    return scope;
+  }
+
+  private ViewDepthProvider createViewDepthProvider() {
+    return new FakeViewDepthProvider()
+        .setChildViewDepth(scope.getProtocolAdapter().getStreamContentId(REQUEST_1[1]));
+  }
+
+  /** Creates a new session and returns the {@link ModelProvider}. */
+  public ModelProvider createNewSession() {
+    return scope.getModelProviderFactory().createNew(createViewDepthProvider());
+  }
+
+  /** Populates HEAD with data. */
+  public void populateHead() {
+    scope
+        .getRequestManager()
+        .queueResponse(ResponseBuilder.forClearAllWithCards(REQUEST_1).build())
+        .triggerRefresh(
+            RequestReason.OPEN_WITHOUT_CONTENT,
+            scope.getSessionManager().getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
+  }
+
+  /** Enqueues an error response and returns the delay in milliseconds. */
+  public long queueError() {
+    scope.getRequestManager().queueError(currentDelay);
+    return currentDelay;
+  }
+
+  /** Enqueues a response and returns the delay in milliseconds. */
+  public long queueRequest() {
+    scope
+        .getRequestManager()
+        .queueResponse(ResponseBuilder.forClearAllWithCards(REQUEST_2).build(), currentDelay);
+    return currentDelay;
+  }
+
+  /** Enqueues an error response, triggers the refresh, and returns the delay in milliseconds. */
+  public long startOutstandingRequestWithError() {
+    long delayMs = queueError();
+    scope
+        .getRequestManager()
+        .triggerRefresh(
+            RequestReason.OPEN_WITHOUT_CONTENT,
+            scope.getSessionManager().getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
+    return delayMs;
+  }
+
+  /** Enqueues a response, triggers the refresh, and returns the delay in milliseconds. */
+  public long startOutstandingRequest() {
+    long delayMs = queueRequest();
+    scope
+        .getRequestManager()
+        .triggerRefresh(
+            RequestReason.OPEN_WITHOUT_CONTENT,
+            scope.getSessionManager().getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
+    return delayMs;
+  }
+
+  /** Asserts that the children are from HEAD. */
+  public void assertHeadContent(List<ModelChild> rootChildren) {
+    assertThat(rootChildren).hasSize(REQUEST_1.length);
+    for (int i = 0; i < rootChildren.size(); i++) {
+      assertThat(rootChildren.get(i).getContentId())
+          .isEqualTo(scope.getProtocolAdapter().getStreamContentId(REQUEST_1[i]));
+    }
+  }
+
+  /** Asserts that the children are from the outstanding request. */
+  public void assertNewContent(List<ModelChild> rootChildren) {
+    assertThat(rootChildren).hasSize(REQUEST_2.length);
+    for (int i = 0; i < rootChildren.size(); i++) {
+      assertThat(rootChildren.get(i).getContentId())
+          .isEqualTo(scope.getProtocolAdapter().getStreamContentId(REQUEST_2[i]));
+    }
+  }
+
+  /** Asserts that the children are the union of HEAD and the outstanding request. */
+  public void assertAppendedContent(List<ModelChild> rootChildren) {
+    assertThat(rootChildren).hasSize(REQUEST_1.length + REQUEST_2.length);
+    assertHeadContent(rootChildren.subList(0, REQUEST_1.length));
+    assertNewContent(rootChildren.subList(REQUEST_1.length, REQUEST_1.length + REQUEST_2.length));
+  }
+
+  /** Asserts that no runnables are pending in the {@link TaskQueue} or {@link MainThreadRunner}. */
+  public void assertWorkComplete() {
+    assertThat(scope.getTaskQueue().hasBacklog()).isFalse();
+    assertThat(scope.getFakeMainThreadRunner().hasPendingTasks()).isFalse();
+  }
+}
diff --git a/src/main/java/com/google/android/libraries/feed/common/time/testing/BUILD b/src/main/java/com/google/android/libraries/feed/common/time/testing/BUILD
index b41c3c0..287a59c 100644
--- a/src/main/java/com/google/android/libraries/feed/common/time/testing/BUILD
+++ b/src/main/java/com/google/android/libraries/feed/common/time/testing/BUILD
@@ -7,6 +7,8 @@
     testonly = 1,
     srcs = glob(["*.java"]),
     deps = [
+        "//src/main/java/com/google/android/libraries/feed/common/logging",
         "//src/main/java/com/google/android/libraries/feed/common/time",
+        "@maven//:com_google_truth_truth",
     ],
 )
diff --git a/src/main/java/com/google/android/libraries/feed/common/time/testing/FakeClock.java b/src/main/java/com/google/android/libraries/feed/common/time/testing/FakeClock.java
index 7421450..fd440af 100644
--- a/src/main/java/com/google/android/libraries/feed/common/time/testing/FakeClock.java
+++ b/src/main/java/com/google/android/libraries/feed/common/time/testing/FakeClock.java
@@ -14,15 +14,20 @@
 
 package com.google.android.libraries.feed.common.time.testing;
 
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.libraries.feed.common.logging.Logger;
 import com.google.android.libraries.feed.common.time.Clock;
 import java.util.HashSet;
 import java.util.Set;
 
 /** Fake implementation of {@link Clock} for testing. */
 public class FakeClock implements Clock {
+  private static final String TAG = "FakeClock";
+
+  private final Set<ClockUpdateListener> listeners;
   private long currentTime;
   private long elapsedRealtimeMs;
-  private final Set<ClockUpdateListener> listeners;
 
   public FakeClock() {
     this(0, 0);
@@ -57,11 +62,22 @@
   }
 
   public void advance(long millis) {
+    assertThat(millis).isAtLeast(0L);
     currentTime += millis;
     elapsedRealtimeMs += millis;
+
+    Logger.i(TAG, "Advancing clock to %d", currentTime);
     onClockUpdated(currentTime, elapsedRealtimeMs);
   }
 
+  /**
+   * Advance the clock forward to the specified time. This will fail if {@link currentTimeMillis} is
+   * not the current time or in the future.
+   */
+  public void advanceTo(long currentTimeMillis) {
+    advance(currentTimeMillis - currentTimeMillis());
+  }
+
   @Override
   public long currentTimeMillis() {
     return currentTime;
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/BUILD b/src/test/java/com/google/android/libraries/feed/infraintegration/BUILD
index 2848fc9..8f5875d 100644
--- a/src/test/java/com/google/android/libraries/feed/infraintegration/BUILD
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/BUILD
@@ -134,16 +134,13 @@
     manifest_values = DEFAULT_ANDROID_LOCAL_TEST_MANIFEST,
     deps = [
         "//src/main/java/com/google/android/libraries/feed/api/common",
-        "//src/main/java/com/google/android/libraries/feed/api/sessionmanager",
         "//src/main/java/com/google/android/libraries/feed/common/testing",
         "//src/main/java/com/google/android/libraries/feed/host/logging",
         "//src/main/java/com/google/android/libraries/feed/internalapi/modelprovider",
-        "//src/main/java/com/google/android/libraries/feed/testing/requestmanager",
         "//src/main/proto/search/now/wire/feed:feed_java_proto_lite",
         "//third_party:robolectric",
         "@com_google_protobuf_javalite//:protobuf_java_lite",
         "@maven//:com_google_truth_truth",
-        "@maven//:org_mockito_mockito_core",
         "@robolectric//bazel:android-all",
     ],
 )
@@ -242,6 +239,114 @@
 )
 
 android_local_test(
+    name = "NoRequestWithContentTest",
+    size = "small",
+    timeout = "moderate",
+    srcs = ["NoRequestWithContentTest.java"],
+    aapt_version = "aapt2",
+    manifest_values = DEFAULT_ANDROID_LOCAL_TEST_MANIFEST,
+    deps = [
+        "//src/main/java/com/google/android/libraries/feed/common/testing",
+        "//src/main/java/com/google/android/libraries/feed/host/scheduler",
+        "//src/main/java/com/google/android/libraries/feed/internalapi/modelprovider",
+        "//third_party:robolectric",
+        "@com_google_protobuf_javalite//:protobuf_java_lite",
+        "@maven//:com_google_truth_truth",
+        "@robolectric//bazel:android-all",
+    ],
+)
+
+android_local_test(
+    name = "NoRequestWithTimeoutTest",
+    size = "small",
+    timeout = "moderate",
+    srcs = ["NoRequestWithTimeoutTest.java"],
+    aapt_version = "aapt2",
+    manifest_values = DEFAULT_ANDROID_LOCAL_TEST_MANIFEST,
+    deps = [
+        "//src/main/java/com/google/android/libraries/feed/common/testing",
+        "//src/main/java/com/google/android/libraries/feed/host/scheduler",
+        "//src/main/java/com/google/android/libraries/feed/internalapi/modelprovider",
+        "//third_party:robolectric",
+        "@com_google_protobuf_javalite//:protobuf_java_lite",
+        "@maven//:com_google_truth_truth",
+        "@robolectric//bazel:android-all",
+    ],
+)
+
+android_local_test(
+    name = "NoRequestWithWaitTest",
+    size = "small",
+    timeout = "moderate",
+    srcs = ["NoRequestWithWaitTest.java"],
+    aapt_version = "aapt2",
+    manifest_values = DEFAULT_ANDROID_LOCAL_TEST_MANIFEST,
+    deps = [
+        "//src/main/java/com/google/android/libraries/feed/common/testing",
+        "//src/main/java/com/google/android/libraries/feed/host/scheduler",
+        "//src/main/java/com/google/android/libraries/feed/internalapi/modelprovider",
+        "//third_party:robolectric",
+        "@com_google_protobuf_javalite//:protobuf_java_lite",
+        "@maven//:com_google_truth_truth",
+        "@robolectric//bazel:android-all",
+    ],
+)
+
+android_local_test(
+    name = "RequestWithContentTest",
+    size = "small",
+    timeout = "moderate",
+    srcs = ["RequestWithContentTest.java"],
+    aapt_version = "aapt2",
+    manifest_values = DEFAULT_ANDROID_LOCAL_TEST_MANIFEST,
+    deps = [
+        "//src/main/java/com/google/android/libraries/feed/common/testing",
+        "//src/main/java/com/google/android/libraries/feed/host/scheduler",
+        "//src/main/java/com/google/android/libraries/feed/internalapi/modelprovider",
+        "//third_party:robolectric",
+        "@com_google_protobuf_javalite//:protobuf_java_lite",
+        "@maven//:com_google_truth_truth",
+        "@robolectric//bazel:android-all",
+    ],
+)
+
+android_local_test(
+    name = "RequestWithTimeoutTest",
+    size = "small",
+    timeout = "moderate",
+    srcs = ["RequestWithTimeoutTest.java"],
+    aapt_version = "aapt2",
+    manifest_values = DEFAULT_ANDROID_LOCAL_TEST_MANIFEST,
+    deps = [
+        "//src/main/java/com/google/android/libraries/feed/common/testing",
+        "//src/main/java/com/google/android/libraries/feed/host/scheduler",
+        "//src/main/java/com/google/android/libraries/feed/internalapi/modelprovider",
+        "//third_party:robolectric",
+        "@com_google_protobuf_javalite//:protobuf_java_lite",
+        "@maven//:com_google_truth_truth",
+        "@robolectric//bazel:android-all",
+    ],
+)
+
+android_local_test(
+    name = "RequestWithWaitTest",
+    size = "small",
+    timeout = "moderate",
+    srcs = ["RequestWithWaitTest.java"],
+    aapt_version = "aapt2",
+    manifest_values = DEFAULT_ANDROID_LOCAL_TEST_MANIFEST,
+    deps = [
+        "//src/main/java/com/google/android/libraries/feed/common/testing",
+        "//src/main/java/com/google/android/libraries/feed/host/scheduler",
+        "//src/main/java/com/google/android/libraries/feed/internalapi/modelprovider",
+        "//third_party:robolectric",
+        "@com_google_protobuf_javalite//:protobuf_java_lite",
+        "@maven//:com_google_truth_truth",
+        "@robolectric//bazel:android-all",
+    ],
+)
+
+android_local_test(
     name = "RemoveTrackingBehaviorTest",
     size = "small",
     timeout = "moderate",
@@ -459,7 +564,6 @@
         "//src/main/java/com/google/android/libraries/feed/common/testing",
         "//src/main/java/com/google/android/libraries/feed/common/time/testing",
         "//src/main/java/com/google/android/libraries/feed/feedsessionmanager",
-        "//src/main/java/com/google/android/libraries/feed/host/config",
         "//src/main/java/com/google/android/libraries/feed/host/scheduler",
         "//src/main/java/com/google/android/libraries/feed/internalapi/modelprovider",
         "//src/main/java/com/google/android/libraries/feed/testing/host/scheduler",
@@ -485,7 +589,6 @@
         "//src/main/java/com/google/android/libraries/feed/common/concurrent/testing",
         "//src/main/java/com/google/android/libraries/feed/common/testing",
         "//src/main/java/com/google/android/libraries/feed/common/time/testing",
-        "//src/main/java/com/google/android/libraries/feed/host/config",
         "//src/main/java/com/google/android/libraries/feed/host/scheduler",
         "//src/main/java/com/google/android/libraries/feed/internalapi/modelprovider",
         "//src/main/java/com/google/android/libraries/feed/internalapi/protocoladapter",
@@ -512,7 +615,6 @@
         "//src/main/java/com/google/android/libraries/feed/api/common",
         "//src/main/java/com/google/android/libraries/feed/api/sessionmanager",
         "//src/main/java/com/google/android/libraries/feed/common/testing",
-        "//src/main/java/com/google/android/libraries/feed/host/config",
         "//src/main/java/com/google/android/libraries/feed/host/logging",
         "//src/main/java/com/google/android/libraries/feed/host/scheduler",
         "//src/main/java/com/google/android/libraries/feed/internalapi/modelprovider",
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/ExistingSessionTest.java b/src/test/java/com/google/android/libraries/feed/infraintegration/ExistingSessionTest.java
index fea3b8a..8766a6e 100644
--- a/src/test/java/com/google/android/libraries/feed/infraintegration/ExistingSessionTest.java
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/ExistingSessionTest.java
@@ -15,10 +15,8 @@
 package com.google.android.libraries.feed.infraintegration;
 
 import static com.google.common.truth.Truth.assertThat;
-import static org.mockito.MockitoAnnotations.initMocks;
 
 import com.google.android.libraries.feed.api.common.MutationContext;
-import com.google.android.libraries.feed.api.sessionmanager.SessionManager;
 import com.google.android.libraries.feed.common.testing.InfraIntegrationScope;
 import com.google.android.libraries.feed.common.testing.ModelProviderValidator;
 import com.google.android.libraries.feed.common.testing.ResponseBuilder;
@@ -28,8 +26,8 @@
 import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider;
 import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider.State;
 import com.google.android.libraries.feed.internalapi.modelprovider.ModelProviderFactory;
-import com.google.android.libraries.feed.testing.requestmanager.FakeRequestManager;
 import com.google.search.now.wire.feed.ContentIdProto.ContentId;
+import org.junit.After;
 import org.junit.Before;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -38,34 +36,37 @@
 /** Tests which verify creating a new Model Provider from an existing session. */
 @RunWith(RobolectricTestRunner.class)
 public class ExistingSessionTest {
-  private FakeRequestManager requestManager;
-  private SessionManager sessionManager;
-  private ModelProviderFactory modelProviderFactory;
-  private ModelProviderValidator modelValidator;
+  private static final ContentId[] CARDS =
+      new ContentId[] {
+        ResponseBuilder.createFeatureContentId(1),
+        ResponseBuilder.createFeatureContentId(2),
+        ResponseBuilder.createFeatureContentId(3)
+      };
+
+  private final InfraIntegrationScope scope = new InfraIntegrationScope.Builder().build();
+  private final ModelProviderFactory modelProviderFactory = scope.getModelProviderFactory();
+  private final ModelProviderValidator modelValidator =
+      new ModelProviderValidator(scope.getProtocolAdapter());
 
   @Before
   public void setUp() {
-    initMocks(this);
-    InfraIntegrationScope scope = new InfraIntegrationScope.Builder().build();
-    requestManager = scope.getRequestManager();
-    sessionManager = scope.getSessionManager();
-    modelProviderFactory = scope.getModelProviderFactory();
-    modelValidator = new ModelProviderValidator(scope.getProtocolAdapter());
+    // Create a simple stream with a root and three features
+    scope
+        .getRequestManager()
+        .queueResponse(ResponseBuilder.forClearAllWithCards(CARDS).build())
+        .triggerRefresh(
+            RequestReason.OPEN_WITHOUT_CONTENT,
+            scope.getSessionManager().getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
+  }
+
+  @After
+  public void tearDown() {
+    assertThat(scope.getTaskQueue().hasBacklog()).isFalse();
+    assertThat(scope.getFakeMainThreadRunner().hasPendingTasks()).isFalse();
   }
 
   @Test
   public void createModelProvider() {
-    // Create a simple stream with a root and three features
-    ContentId[] cards =
-        new ContentId[] {
-          ResponseBuilder.createFeatureContentId(1),
-          ResponseBuilder.createFeatureContentId(2),
-          ResponseBuilder.createFeatureContentId(3)
-        };
-    requestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-    requestManager.triggerRefresh(
-        RequestReason.OPEN_WITHOUT_CONTENT,
-        sessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
     ModelProvider modelProvider = modelProviderFactory.createNew(null);
 
     ModelFeature initRoot = modelProvider.getRootFeature();
@@ -83,7 +84,7 @@
     ModelCursor cursor2 = root2.getCursor();
     assertThat(cursor2).isNotNull();
     assertThat(modelProvider2.getSessionId()).isEqualTo(sessionId);
-    modelValidator.assertCursorContents(cursor2, cards);
+    modelValidator.assertCursorContents(cursor2, CARDS);
 
     // Creating the new session will invalidate the previous ModelProvider and Cursor
     assertThat(modelProvider.getCurrentState()).isEqualTo(State.INVALIDATED);
@@ -91,18 +92,41 @@
   }
 
   @Test
+  public void createModelProvider_restoreSession() {
+    ModelProvider modelProvider = modelProviderFactory.createNew(null);
+    String sessionId = modelProvider.getSessionId();
+    modelProvider.detachModelProvider();
+
+    // Restore the session.
+    ModelProvider restoredModelProvider = modelProviderFactory.create(sessionId);
+    assertThat(restoredModelProvider).isNotNull();
+    assertThat(restoredModelProvider.getAllRootChildren()).hasSize(CARDS.length);
+  }
+
+  @Test
+  public void createModelProvider_restoreSessionWithOutstandingRequest() {
+    ModelProvider modelProvider = modelProviderFactory.createNew(null);
+    String sessionId = modelProvider.getSessionId();
+    modelProvider.detachModelProvider();
+
+    // Start a request.
+    scope
+        .getRequestManager()
+        .queueResponse(ResponseBuilder.forClearAllWithCards(CARDS).build(), /* delayMs= */ 100)
+        .triggerRefresh(
+            RequestReason.OPEN_WITHOUT_CONTENT,
+            scope.getSessionManager().getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
+
+    // Restore the session.
+    ModelProvider restoredModelProvider = modelProviderFactory.create(sessionId);
+    assertThat(restoredModelProvider.getAllRootChildren()).hasSize(CARDS.length);
+
+    // Advance the clock to process the outstanding request.
+    scope.getFakeClock().advance(100);
+  }
+
+  @Test
   public void createModelProvider_unknownSession() {
-    // Create a simple stream with a root and three features
-    ContentId[] cards =
-        new ContentId[] {
-          ResponseBuilder.createFeatureContentId(1),
-          ResponseBuilder.createFeatureContentId(2),
-          ResponseBuilder.createFeatureContentId(3)
-        };
-    requestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-    requestManager.triggerRefresh(
-        RequestReason.OPEN_WITHOUT_CONTENT,
-        sessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
     ModelProvider modelProvider = modelProviderFactory.createNew(null);
 
     ModelFeature initRoot = modelProvider.getRootFeature();
@@ -125,7 +149,7 @@
     assertThat(root2).isNotNull();
     ModelCursor cursor2 = root2.getCursor();
     assertThat(cursor2).isNotNull();
-    modelValidator.assertCursorContents(cursor2, cards);
+    modelValidator.assertCursorContents(cursor2, CARDS);
 
     // two sessions against the same $HEAD instance
     assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
@@ -134,18 +158,6 @@
 
   @Test
   public void createModelProvider_invalidatedSession() {
-    // Create a simple stream with a root and three features
-    ContentId[] cards =
-        new ContentId[] {
-          ResponseBuilder.createFeatureContentId(1),
-          ResponseBuilder.createFeatureContentId(2),
-          ResponseBuilder.createFeatureContentId(3)
-        };
-    requestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-    requestManager.triggerRefresh(
-        RequestReason.OPEN_WITHOUT_CONTENT,
-        sessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-
     ModelProvider modelProvider = modelProviderFactory.createNew(null);
     assertThat(modelProvider).isNotNull();
     assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
@@ -164,18 +176,6 @@
 
   @Test
   public void createModelProvider_detachSession() {
-    // Create a simple stream with a root and three features
-    ContentId[] cards =
-        new ContentId[] {
-          ResponseBuilder.createFeatureContentId(1),
-          ResponseBuilder.createFeatureContentId(2),
-          ResponseBuilder.createFeatureContentId(3)
-        };
-    requestManager.queueResponse(ResponseBuilder.forClearAllWithCards(cards).build());
-    requestManager.triggerRefresh(
-        RequestReason.OPEN_WITHOUT_CONTENT,
-        sessionManager.getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
-
     ModelProvider modelProvider = modelProviderFactory.createNew(null);
     assertThat(modelProvider).isNotNull();
     assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/NoRequestWithContentTest.java b/src/test/java/com/google/android/libraries/feed/infraintegration/NoRequestWithContentTest.java
new file mode 100644
index 0000000..67e35cd
--- /dev/null
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/NoRequestWithContentTest.java
@@ -0,0 +1,89 @@
+// Copyright 2019 The Feed Authors.
+//
+// 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.android.libraries.feed.infraintegration;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.libraries.feed.common.testing.InfraIntegrationScope;
+import com.google.android.libraries.feed.common.testing.SessionTestUtils;
+import com.google.android.libraries.feed.host.scheduler.SchedulerApi.RequestBehavior;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider.State;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests the NO_REQUEST_WITH_CONTENT behavior for creating a new session. */
+@RunWith(RobolectricTestRunner.class)
+public final class NoRequestWithContentTest {
+  private final SessionTestUtils utils =
+      new SessionTestUtils(RequestBehavior.NO_REQUEST_WITH_CONTENT);
+  private final InfraIntegrationScope scope = utils.getScope();
+
+  @Before
+  public void setUp() {
+    utils.populateHead();
+  }
+
+  @After
+  public void tearDown() {
+    utils.assertWorkComplete();
+  }
+
+  @Test
+  public void test_hasContentWithRequest_shouldShowContentImmediatelyAndAppend() {
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+
+    // REQUEST_2 items should be appended.
+    scope.getFakeClock().advance(delayMs);
+    utils.assertAppendedContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentWithRequest_shouldShowZeroState() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+
+    // REQUEST_2 items should not be appended.
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentNoRequest_shouldShowZeroState() {
+    scope.getAppLifecycleListener().onClearAll();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_hasContentNoRequest_shouldShowContentImmediately() {
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+}
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/NoRequestWithTimeoutTest.java b/src/test/java/com/google/android/libraries/feed/infraintegration/NoRequestWithTimeoutTest.java
new file mode 100644
index 0000000..ca0bc6f
--- /dev/null
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/NoRequestWithTimeoutTest.java
@@ -0,0 +1,174 @@
+// Copyright 2019 The Feed Authors.
+//
+// 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.android.libraries.feed.infraintegration;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.libraries.feed.common.testing.InfraIntegrationScope;
+import com.google.android.libraries.feed.common.testing.SessionTestUtils;
+import com.google.android.libraries.feed.host.scheduler.SchedulerApi.RequestBehavior;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider.State;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests the NO_REQUEST_WITH_TIMEOUT behavior for creating a new session. */
+@RunWith(RobolectricTestRunner.class)
+public final class NoRequestWithTimeoutTest {
+  private final SessionTestUtils utils =
+      new SessionTestUtils(RequestBehavior.NO_REQUEST_WITH_TIMEOUT);
+  private final InfraIntegrationScope scope = utils.getScope();
+
+  @Before
+  public void setUp() {
+    utils.populateHead();
+  }
+
+  @After
+  public void tearDown() {
+    utils.assertWorkComplete();
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContent() {
+    long delayMs = utils.requestBeforeTimeout().startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContentWithError() {
+    long delayMs = utils.requestBeforeTimeout().startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContentOnTimeoutAndAppend() {
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+
+    scope.getFakeClock().advanceTo(delayMs);
+    utils.assertAppendedContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContentOnTimeoutWithError() {
+    long delayMs = utils.startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+
+    scope.getFakeClock().advanceTo(delayMs);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenShowContent() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.requestBeforeTimeout().startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenZeroStateWithError() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.requestBeforeTimeout().startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenZeroStateOnTimeout() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+
+    scope.getFakeClock().advanceTo(delayMs);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenZeroStateOnTimeoutWithError() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+
+    scope.getFakeClock().advanceTo(delayMs);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentNoRequest_shouldShowZeroState() {
+    scope.getAppLifecycleListener().onClearAll();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_hasContentNoRequest_shouldShowContentImmediately() {
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+}
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/NoRequestWithWaitTest.java b/src/test/java/com/google/android/libraries/feed/infraintegration/NoRequestWithWaitTest.java
new file mode 100644
index 0000000..bf600bf
--- /dev/null
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/NoRequestWithWaitTest.java
@@ -0,0 +1,111 @@
+// Copyright 2019 The Feed Authors.
+//
+// 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.android.libraries.feed.infraintegration;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.libraries.feed.common.testing.InfraIntegrationScope;
+import com.google.android.libraries.feed.common.testing.SessionTestUtils;
+import com.google.android.libraries.feed.host.scheduler.SchedulerApi.RequestBehavior;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider.State;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests the NO_REQUEST_WITH_WAIT behavior for creating a new session. */
+@RunWith(RobolectricTestRunner.class)
+public final class NoRequestWithWaitTest {
+  private final SessionTestUtils utils = new SessionTestUtils(RequestBehavior.NO_REQUEST_WITH_WAIT);
+  private final InfraIntegrationScope scope = utils.getScope();
+
+  @Before
+  public void setUp() {
+    utils.populateHead();
+  }
+
+  @After
+  public void tearDown() {
+    utils.assertWorkComplete();
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContent() {
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContentOnFailure() {
+    long delayMs = utils.startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenShowContent() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenZeroStateOnFailure() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentNoRequest_shouldShowZeroState() {
+    scope.getAppLifecycleListener().onClearAll();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_hasContentNoRequest_shouldShowContentImmediately() {
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+}
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/RequestWithContentTest.java b/src/test/java/com/google/android/libraries/feed/infraintegration/RequestWithContentTest.java
new file mode 100644
index 0000000..715beb2
--- /dev/null
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/RequestWithContentTest.java
@@ -0,0 +1,99 @@
+// Copyright 2019 The Feed Authors.
+//
+// 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.android.libraries.feed.infraintegration;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.libraries.feed.common.testing.InfraIntegrationScope;
+import com.google.android.libraries.feed.common.testing.SessionTestUtils;
+import com.google.android.libraries.feed.host.scheduler.SchedulerApi.RequestBehavior;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider.State;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests the REQUEST_WITH_CONTENT behavior for creating a new session. */
+@RunWith(RobolectricTestRunner.class)
+public final class RequestWithContentTest {
+  private final SessionTestUtils utils = new SessionTestUtils(RequestBehavior.REQUEST_WITH_CONTENT);
+  private final InfraIntegrationScope scope = utils.getScope();
+
+  @Before
+  public void setUp() {
+    utils.populateHead();
+  }
+
+  @After
+  public void tearDown() {
+    utils.assertWorkComplete();
+  }
+
+  @Test
+  public void test_hasContentWithRequest_shouldShowContentImmediatelyAndAppend() {
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+
+    // REQUEST_2 items should be appended.
+    scope.getFakeClock().advance(delayMs);
+    utils.assertAppendedContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentWithRequest_shouldShowZeroState() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+
+    // REQUEST_2 items should not be appended.
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentNoRequest_shouldShowZeroState() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.queueRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+
+    // REQUEST_2 items should not be appended.
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_hasContentNoRequest_shouldShowContentImmediatelyAndAppend() {
+    long delayMs = utils.queueRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+
+    // REQUEST_2 items should be appended.
+    scope.getFakeClock().advance(delayMs);
+    utils.assertAppendedContent(modelProvider.getAllRootChildren());
+  }
+}
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/RequestWithTimeoutTest.java b/src/test/java/com/google/android/libraries/feed/infraintegration/RequestWithTimeoutTest.java
new file mode 100644
index 0000000..465bfbe
--- /dev/null
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/RequestWithTimeoutTest.java
@@ -0,0 +1,269 @@
+// Copyright 2019 The Feed Authors.
+//
+// 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.android.libraries.feed.infraintegration;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.libraries.feed.common.testing.InfraIntegrationScope;
+import com.google.android.libraries.feed.common.testing.SessionTestUtils;
+import com.google.android.libraries.feed.host.scheduler.SchedulerApi.RequestBehavior;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider.State;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests the REQUEST_WITH_TIMEOUT behavior for creating a new session. */
+@RunWith(RobolectricTestRunner.class)
+public final class RequestWithTimeoutTest {
+  private final SessionTestUtils utils = new SessionTestUtils(RequestBehavior.REQUEST_WITH_TIMEOUT);
+  private final InfraIntegrationScope scope = utils.getScope();
+
+  @Before
+  public void setUp() {
+    utils.populateHead();
+  }
+
+  @After
+  public void tearDown() {
+    utils.assertWorkComplete();
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContent() {
+    long delayMs = utils.requestBeforeTimeout().startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContentWithError() {
+    long delayMs = utils.requestBeforeTimeout().startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContentOnTimeoutAndAppend() {
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+
+    scope.getFakeClock().advanceTo(delayMs);
+    utils.assertAppendedContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContentOnTimeoutWithError() {
+    long delayMs = utils.startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+
+    scope.getFakeClock().advanceTo(delayMs);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenShowContent() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.requestBeforeTimeout().startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenZeroStateWithError() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.requestBeforeTimeout().startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenZeroStateOnTimeout() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+
+    scope.getFakeClock().advanceTo(delayMs);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenZeroStateOnTimeoutWithError() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+
+    scope.getFakeClock().advanceTo(delayMs);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentNoRequest_spinnerThenShowContent() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.requestBeforeTimeout().queueRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentNoRequest_spinnerThenZeroStateWithError() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.requestBeforeTimeout().queueError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentNoRequest_spinnerThenZeroStateOnTimeout() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.queueRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+
+    scope.getFakeClock().advanceTo(delayMs);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentNoRequest_spinnerThenZeroStateOnTimeoutWithError() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.queueError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+
+    scope.getFakeClock().advanceTo(delayMs);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_hasContentNoRequest_spinnerThenShowContent() {
+    long delayMs = utils.requestBeforeTimeout().queueRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentNoRequest_spinnerThenShowContentWithError() {
+    long delayMs = utils.requestBeforeTimeout().queueError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentNoRequest_spinnerThenShowContentOnTimeoutAndAppend() {
+    long delayMs = utils.queueRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+
+    scope.getFakeClock().advanceTo(delayMs);
+    utils.assertAppendedContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentNoRequest_spinnerThenShowContentOnTimeoutWithError() {
+    long delayMs = utils.queueError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(SessionTestUtils.TIMEOUT_MS);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+
+    scope.getFakeClock().advanceTo(delayMs);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+}
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/RequestWithWaitTest.java b/src/test/java/com/google/android/libraries/feed/infraintegration/RequestWithWaitTest.java
new file mode 100644
index 0000000..181255d
--- /dev/null
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/RequestWithWaitTest.java
@@ -0,0 +1,144 @@
+// Copyright 2019 The Feed Authors.
+//
+// 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.android.libraries.feed.infraintegration;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.android.libraries.feed.common.testing.InfraIntegrationScope;
+import com.google.android.libraries.feed.common.testing.SessionTestUtils;
+import com.google.android.libraries.feed.host.scheduler.SchedulerApi.RequestBehavior;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider;
+import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider.State;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+
+/** Tests the REQUEST_WITH_WAIT behavior for creating a new session. */
+@RunWith(RobolectricTestRunner.class)
+public final class RequestWithWaitTest {
+  private final SessionTestUtils utils = new SessionTestUtils(RequestBehavior.REQUEST_WITH_WAIT);
+  private final InfraIntegrationScope scope = utils.getScope();
+
+  @Before
+  public void setUp() {
+    utils.populateHead();
+  }
+
+  @After
+  public void tearDown() {
+    utils.assertWorkComplete();
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContent() {
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentWithRequest_spinnerThenShowContentOnFailure() {
+    long delayMs = utils.startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenShowContent() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.startOutstandingRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentWithRequest_spinnerThenZeroStateOnFailure() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.startOutstandingRequestWithError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_noContentNoRequest_spinnerThenShowContent() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.queueRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_noContentNoRequest_spinnerThenZeroStateOnFailure() {
+    scope.getAppLifecycleListener().onClearAll();
+    long delayMs = utils.queueError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getAllRootChildren()).isEmpty();
+  }
+
+  @Test
+  public void test_hasContentNoRequest_spinnerThenShowContent() {
+    long delayMs = utils.queueRequest();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertNewContent(modelProvider.getAllRootChildren());
+  }
+
+  @Test
+  public void test_hasContentNoRequest_spinnerThenShowContentOnFailure() {
+    long delayMs = utils.queueError();
+
+    ModelProvider modelProvider = utils.createNewSession();
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.INITIALIZING);
+
+    scope.getFakeClock().advance(delayMs);
+    assertThat(modelProvider.getCurrentState()).isEqualTo(State.READY);
+    utils.assertHeadContent(modelProvider.getAllRootChildren());
+  }
+}
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/TimeoutSessionBaseTest.java b/src/test/java/com/google/android/libraries/feed/infraintegration/TimeoutSessionBaseTest.java
index 9d58b6b..a1bd27a 100644
--- a/src/test/java/com/google/android/libraries/feed/infraintegration/TimeoutSessionBaseTest.java
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/TimeoutSessionBaseTest.java
@@ -24,7 +24,6 @@
 import com.google.android.libraries.feed.common.testing.ResponseBuilder;
 import com.google.android.libraries.feed.common.time.testing.FakeClock;
 import com.google.android.libraries.feed.feedsessionmanager.FeedSessionManager;
-import com.google.android.libraries.feed.host.config.Configuration;
 import com.google.android.libraries.feed.host.scheduler.SchedulerApi.RequestBehavior;
 import com.google.android.libraries.feed.internalapi.modelprovider.ModelError;
 import com.google.android.libraries.feed.internalapi.modelprovider.ModelProvider;
@@ -66,11 +65,10 @@
   @Before
   public void setUp() {
     initMocks(this);
-    Configuration configuration = InfraIntegrationScope.getTimeoutSchedulerConfig();
     InfraIntegrationScope scope =
         new InfraIntegrationScope.Builder()
-            .setConfiguration(configuration)
             .setSchedulerApi(fakeSchedulerApi)
+            .withTimeoutSessionConfiguration(2L)
             .build();
     fakeClock = scope.getFakeClock();
     requestManager = scope.getRequestManager();
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/TimeoutSessionWithContentTest.java b/src/test/java/com/google/android/libraries/feed/infraintegration/TimeoutSessionWithContentTest.java
index 3124ff2..f1bf553 100644
--- a/src/test/java/com/google/android/libraries/feed/infraintegration/TimeoutSessionWithContentTest.java
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/TimeoutSessionWithContentTest.java
@@ -23,7 +23,6 @@
 import com.google.android.libraries.feed.common.testing.ModelProviderValidator;
 import com.google.android.libraries.feed.common.testing.ResponseBuilder;
 import com.google.android.libraries.feed.common.time.testing.FakeClock;
-import com.google.android.libraries.feed.host.config.Configuration;
 import com.google.android.libraries.feed.host.scheduler.SchedulerApi.RequestBehavior;
 import com.google.android.libraries.feed.internalapi.modelprovider.FeatureChange.ChildChanges;
 import com.google.android.libraries.feed.internalapi.modelprovider.ModelError;
@@ -80,11 +79,10 @@
   @Before
   public void setUp() {
     initMocks(this);
-    Configuration configuration = InfraIntegrationScope.getTimeoutSchedulerConfig();
     InfraIntegrationScope scope =
         new InfraIntegrationScope.Builder()
-            .setConfiguration(configuration)
             .setSchedulerApi(fakeSchedulerApi)
+            .withTimeoutSessionConfiguration(2L)
             .build();
     fakeClock = scope.getFakeClock();
     requestManager = scope.getRequestManager();
diff --git a/src/test/java/com/google/android/libraries/feed/infraintegration/ViewDepthProviderTests.java b/src/test/java/com/google/android/libraries/feed/infraintegration/ViewDepthProviderTests.java
index 4dee7e0..ca68da5 100644
--- a/src/test/java/com/google/android/libraries/feed/infraintegration/ViewDepthProviderTests.java
+++ b/src/test/java/com/google/android/libraries/feed/infraintegration/ViewDepthProviderTests.java
@@ -23,7 +23,6 @@
 import com.google.android.libraries.feed.common.testing.InfraIntegrationScope;
 import com.google.android.libraries.feed.common.testing.ModelProviderValidator;
 import com.google.android.libraries.feed.common.testing.ResponseBuilder;
-import com.google.android.libraries.feed.host.config.Configuration;
 import com.google.android.libraries.feed.host.logging.RequestReason;
 import com.google.android.libraries.feed.host.scheduler.SchedulerApi;
 import com.google.android.libraries.feed.host.scheduler.SchedulerApi.RequestBehavior;
@@ -88,11 +87,10 @@
   @Before
   public void setUp() {
     initMocks(this);
-    Configuration configuration = InfraIntegrationScope.getTimeoutSchedulerConfig();
     InfraIntegrationScope scope =
         new InfraIntegrationScope.Builder()
-            .setConfiguration(configuration)
             .setSchedulerApi(schedulerApi)
+            .withTimeoutSessionConfiguration(2L)
             .build();
     requestManager = scope.getRequestManager();
     modelProviderFactory = scope.getModelProviderFactory();