blob: fa71045398fd8832bdd99ec2c4f14d15136e80be [file] [log] [blame]
// 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.api.host.logging.RequestReason;
import com.google.android.libraries.feed.api.host.scheduler.SchedulerApi.RequestBehavior;
import com.google.android.libraries.feed.api.internal.modelprovider.ModelChild;
import com.google.android.libraries.feed.api.internal.modelprovider.ModelProvider;
import com.google.android.libraries.feed.api.internal.modelprovider.ModelProvider.ViewDepthProvider;
import com.google.android.libraries.feed.common.concurrent.testing.FakeThreadUtils;
import com.google.android.libraries.feed.testing.host.scheduler.FakeSchedulerApi;
import com.google.android.libraries.feed.testing.modelprovider.FakeViewDepthProvider;
import com.google.search.now.feed.client.StreamDataProto.UiContext;
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(), UiContext.getDefaultInstance());
}
/** Populates HEAD with data. */
public void populateHead() {
scope
.getFakeFeedRequestManager()
.queueResponse(ResponseBuilder.forClearAllWithCards(REQUEST_1).build())
.triggerRefresh(
RequestReason.OPEN_WITHOUT_CONTENT,
scope.getFeedSessionManager().getUpdateConsumer(MutationContext.EMPTY_CONTEXT));
}
/** Enqueues an error response and returns the delay in milliseconds. */
public long queueError() {
scope.getFakeFeedRequestManager().queueError(currentDelay);
return currentDelay;
}
/** Enqueues a response and returns the delay in milliseconds. */
public long queueRequest() {
scope
.getFakeFeedRequestManager()
.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
.getFakeFeedRequestManager()
.triggerRefresh(
RequestReason.OPEN_WITHOUT_CONTENT,
scope.getFeedSessionManager().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
.getFakeFeedRequestManager()
.triggerRefresh(
RequestReason.OPEN_WITHOUT_CONTENT,
scope.getFeedSessionManager().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();
}
}