blob: fa2ad822b0ee55ba0a385e71fa87a3a8cbeb1d53 [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.testing.requestmanager;
import com.google.android.libraries.feed.common.Result;
import com.google.android.libraries.feed.common.concurrent.MainThreadRunner;
import com.google.android.libraries.feed.common.concurrent.TaskQueue;
import com.google.android.libraries.feed.common.concurrent.TaskQueue.TaskType;
import com.google.android.libraries.feed.common.concurrent.testing.FakeThreadUtils;
import com.google.android.libraries.feed.common.functional.Consumer;
import com.google.android.libraries.feed.host.logging.RequestReason;
import com.google.android.libraries.feed.host.logging.Task;
import com.google.android.libraries.feed.internalapi.protocoladapter.ProtocolAdapter;
import com.google.android.libraries.feed.internalapi.requestmanager.FeedRequestManager;
import com.google.search.now.feed.client.StreamDataProto.StreamDataOperation;
import com.google.search.now.feed.client.StreamDataProto.StreamToken;
import com.google.search.now.wire.feed.ConsistencyTokenProto.ConsistencyToken;
import com.google.search.now.wire.feed.ResponseProto.Response;
import java.util.ArrayDeque;
import java.util.List;
import java.util.Queue;
/**
* Fake implementation of a {@link FeedRequestManager}. This acts a Queue of responses which will be
* sent through the {@link ProtocolAdapter} to create the {@code List<StreamDataOperation>} which is
* then returned as a {@link Result} to the {@link Consumer}.
*/
public class FakeFeedRequestManager implements FeedRequestManager {
private final FakeThreadUtils fakeThreadUtils;
private final MainThreadRunner mainThreadRunner;
private final ProtocolAdapter protocolAdapter;
private final Queue<ResponseWithDelay> responses = new ArrayDeque<>();
private final TaskQueue taskQueue;
/*@Nullable*/ private StreamToken latestStreamToken = null;
@RequestReason private int latestRequestReason = RequestReason.UNKNOWN;
public FakeFeedRequestManager(
FakeThreadUtils fakeThreadUtils,
MainThreadRunner mainThreadRunner,
ProtocolAdapter protocolAdapter,
TaskQueue taskQueue) {
this.fakeThreadUtils = fakeThreadUtils;
this.mainThreadRunner = mainThreadRunner;
this.protocolAdapter = protocolAdapter;
this.taskQueue = taskQueue;
}
// TODO: queue responses for action uploads
/** Adds a Response to the queue. */
public FakeFeedRequestManager queueResponse(Response response) {
return queueResponse(response, /* delayMs= */ 0);
}
/** Adds a Response to the queue with a delay. */
public FakeFeedRequestManager queueResponse(Response response, long delayMs) {
responses.add(new ResponseWithDelay(response, delayMs));
return this;
}
/** Adds an error to the queue. */
public FakeFeedRequestManager queueError() {
return queueError(/* delayMs= */ 0);
}
/** Adds an error to the queue with a delay. */
public FakeFeedRequestManager queueError(long delayMs) {
responses.add(new ResponseWithDelay(delayMs));
return this;
}
@Override
public void loadMore(
StreamToken streamToken,
ConsistencyToken token,
Consumer<Result<List<StreamDataOperation>>> consumer) {
fakeThreadUtils.checkNotMainThread();
latestStreamToken = streamToken;
handleResponseWithDelay(responses.remove(), consumer);
}
@Override
public void triggerRefresh(
@RequestReason int reason, Consumer<Result<List<StreamDataOperation>>> consumer) {
triggerRefresh(reason, ConsistencyToken.getDefaultInstance(), consumer);
}
@Override
public void triggerRefresh(
@RequestReason int reason,
ConsistencyToken token,
Consumer<Result<List<StreamDataOperation>>> consumer) {
latestRequestReason = reason;
ResponseWithDelay responseWithDelay = responses.remove();
taskQueue.execute(
Task.UNKNOWN,
TaskType.HEAD_INVALIDATE,
() -> {
handleResponseWithDelay(responseWithDelay, consumer);
});
}
private void handleResponseWithDelay(
ResponseWithDelay responseWithDelay, Consumer<Result<List<StreamDataOperation>>> consumer) {
if (responseWithDelay.delayMs > 0) {
mainThreadRunner.executeWithDelay(
"FakeFeedRequestManager#consumer",
() -> {
invokeConsumer(responseWithDelay, consumer);
},
responseWithDelay.delayMs);
} else {
invokeConsumer(responseWithDelay, consumer);
}
}
private void invokeConsumer(
ResponseWithDelay responseWithDelay, Consumer<Result<List<StreamDataOperation>>> consumer) {
boolean policy = fakeThreadUtils.enforceMainThread(true);
if (responseWithDelay.isError) {
consumer.accept(Result.failure());
} else {
consumer.accept(protocolAdapter.createModel(responseWithDelay.response));
}
fakeThreadUtils.enforceMainThread(policy);
}
/** Returns the latest {@link StreamToken} passed in to the {@link FeedRequestManager}. */
/*@Nullable*/
public StreamToken getLatestStreamToken() {
return latestStreamToken;
}
/**
* Returns the latest {@link RequestReason} passed in to the {@link FeedRequestManager}. Returns
* {@literal RequestReason.UNKNOWN} if the request manager has not been invoked.
*/
@RequestReason
public int getLatestRequestReason() {
return latestRequestReason;
}
private static final class ResponseWithDelay {
private final Response response;
private final boolean isError;
private final long delayMs;
private ResponseWithDelay(Response response, long delayMs) {
this.response = response;
this.delayMs = delayMs;
isError = false;
}
private ResponseWithDelay(long delayMs) {
this.response = Response.getDefaultInstance();
this.delayMs = delayMs;
isError = true;
}
}
}