| // Copyright 2018 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.mocknetworkclient; |
| |
| import static com.google.common.truth.Truth.assertThat; |
| import static org.mockito.Matchers.any; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.when; |
| import static org.mockito.MockitoAnnotations.initMocks; |
| |
| import android.app.Activity; |
| import android.content.Context; |
| import android.net.Uri; |
| import com.google.android.libraries.feed.api.actionmanager.ActionReader; |
| import com.google.android.libraries.feed.api.protocoladapter.ProtocolAdapter; |
| 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.testing.FakeMainThreadRunner; |
| import com.google.android.libraries.feed.common.concurrent.testing.FakeTaskQueue; |
| 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.common.protoextensions.FeedExtensionRegistry; |
| import com.google.android.libraries.feed.common.time.TimingUtils; |
| import com.google.android.libraries.feed.common.time.testing.FakeClock; |
| import com.google.android.libraries.feed.feedrequestmanager.FeedRequestManager; |
| import com.google.android.libraries.feed.host.config.ApplicationInfo; |
| import com.google.android.libraries.feed.host.config.Configuration; |
| import com.google.android.libraries.feed.host.network.HttpRequest; |
| import com.google.android.libraries.feed.host.network.HttpRequest.HttpMethod; |
| import com.google.android.libraries.feed.host.network.HttpResponse; |
| import com.google.android.libraries.feed.host.scheduler.SchedulerApi; |
| import com.google.android.libraries.feed.host.stream.TooltipSupportedApi; |
| import com.google.android.libraries.feed.testing.conformance.network.NetworkClientConformanceTest; |
| import com.google.android.libraries.feed.testing.host.logging.FakeBasicLoggingApi; |
| import com.google.common.truth.extensions.proto.LiteProtoTruth; |
| import com.google.common.util.concurrent.MoreExecutors; |
| import com.google.protobuf.ByteString; |
| import com.google.protobuf.CodedInputStream; |
| 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 com.google.search.now.wire.feed.ResponseProto.Response.ResponseVersion; |
| import com.google.search.now.wire.feed.mockserver.MockServerProto.ConditionalResponse; |
| import com.google.search.now.wire.feed.mockserver.MockServerProto.MockServer; |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Captor; |
| import org.mockito.Mock; |
| import org.robolectric.Robolectric; |
| import org.robolectric.RobolectricTestRunner; |
| |
| /** Tests of the {@link MockServerNetworkClient} class. */ |
| @RunWith(RobolectricTestRunner.class) |
| public class MockServerNetworkClientTest extends NetworkClientConformanceTest { |
| private final Configuration configuration = new Configuration.Builder().build(); |
| private final FakeClock fakeClock = new FakeClock(); |
| private final FakeThreadUtils fakeThreadUtils = new FakeThreadUtils(); |
| private final FeedExtensionRegistry extensionRegistry = new FeedExtensionRegistry(ArrayList::new); |
| private final TimingUtils timingUtils = new TimingUtils(); |
| |
| @Mock private ActionReader actionReader; |
| @Mock private ProtocolAdapter protocolAdapter; |
| @Mock private SchedulerApi scheduler; |
| @Mock private TooltipSupportedApi tooltipSupportedApi; |
| @Captor private ArgumentCaptor<Response> responseCaptor; |
| private ApplicationInfo applicationInfo; |
| private Context context; |
| private FakeBasicLoggingApi basicLoggingApi; |
| private MainThreadRunner mainThreadRunner; |
| |
| @Override |
| protected Uri getValidUri(@HttpMethod String method) { |
| // The URI does not matter - mockNetworkClient will default to an empty response |
| return new Uri.Builder().path("foo").appendPath(method).build(); |
| } |
| |
| @Before |
| public void setUp() throws Exception { |
| initMocks(this); |
| context = Robolectric.buildActivity(Activity.class).get(); |
| applicationInfo = new ApplicationInfo.Builder(context).setVersionString("0").build(); |
| mainThreadRunner = FakeMainThreadRunner.runTasksImmediately(); |
| |
| MockServer mockServer = MockServer.getDefaultInstance(); |
| networkClient = new MockServerNetworkClient(context, mockServer, /* responseDelayMillis= */ 0L); |
| |
| when(actionReader.getDismissActionsWithSemanticProperties()) |
| .thenReturn(Result.success(Collections.emptyList())); |
| |
| basicLoggingApi = new FakeBasicLoggingApi(); |
| } |
| |
| @Test |
| public void testSend() { |
| MockServer.Builder mockServerBuilder = MockServer.newBuilder(); |
| Response initialResponse = |
| Response.newBuilder().setResponseVersion(ResponseVersion.FEED_RESPONSE).build(); |
| mockServerBuilder.setInitialResponse(initialResponse); |
| MockServerNetworkClient networkClient = |
| new MockServerNetworkClient( |
| context, mockServerBuilder.build(), /* responseDelayMillis= */ 0L); |
| Consumer<HttpResponse> responseConsumer = |
| input -> { |
| try { |
| CodedInputStream inputStream = CodedInputStream.newInstance(input.getResponseBody()); |
| int length = inputStream.readRawVarint32(); |
| assertThat(inputStream.readRawBytes(length)).isEqualTo(initialResponse.toByteArray()); |
| assertThat(input.getResponseCode()).isEqualTo(200); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| }; |
| networkClient.send( |
| new HttpRequest(Uri.EMPTY, HttpMethod.POST, Collections.emptyList(), new byte[] {}), |
| responseConsumer); |
| } |
| |
| @Test |
| public void testSend_oneTimeResponse() { |
| MockServer.Builder mockServerBuilder = MockServer.newBuilder(); |
| Response initialResponse = |
| Response.newBuilder().setResponseVersion(ResponseVersion.FEED_RESPONSE).build(); |
| mockServerBuilder.setInitialResponse(initialResponse); |
| MockServerNetworkClient networkClient = |
| new MockServerNetworkClient( |
| context, mockServerBuilder.build(), /* responseDelayMillis= */ 0L); |
| Consumer<HttpResponse> responseConsumer = |
| input -> { |
| try { |
| CodedInputStream inputStream = CodedInputStream.newInstance(input.getResponseBody()); |
| int length = inputStream.readRawVarint32(); |
| assertThat(inputStream.readRawBytes(length)).isEqualTo(initialResponse.toByteArray()); |
| assertThat(input.getResponseCode()).isEqualTo(200); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| }; |
| networkClient.send( |
| new HttpRequest(Uri.EMPTY, HttpMethod.POST, Collections.emptyList(), new byte[] {}), |
| responseConsumer); |
| } |
| |
| @Test |
| public void testPaging() { |
| MockServer.Builder mockServerBuilder = MockServer.newBuilder(); |
| Response response = |
| Response.newBuilder().setResponseVersion(ResponseVersion.FEED_RESPONSE).build(); |
| ByteString token = ByteString.copyFromUtf8("fooToken"); |
| StreamToken streamToken = StreamToken.newBuilder().setNextPageToken(token).build(); |
| ConditionalResponse.Builder conditionalResponseBuilder = ConditionalResponse.newBuilder(); |
| conditionalResponseBuilder.setContinuationToken(token).setResponse(response); |
| mockServerBuilder.addConditionalResponses(conditionalResponseBuilder.build()); |
| MockServerNetworkClient networkClient = |
| new MockServerNetworkClient( |
| context, mockServerBuilder.build(), /* responseDelayMillis= */ 0L); |
| FeedRequestManager feedRequestManager = |
| new FeedRequestManager( |
| configuration, |
| networkClient, |
| protocolAdapter, |
| extensionRegistry, |
| scheduler, |
| getTaskQueue(), |
| timingUtils, |
| fakeThreadUtils, |
| actionReader, |
| context, |
| applicationInfo, |
| mainThreadRunner, |
| basicLoggingApi, |
| tooltipSupportedApi); |
| when(protocolAdapter.createModel(any(Response.class))) |
| .thenReturn(Result.success(new ArrayList<>())); |
| |
| fakeThreadUtils.enforceMainThread(false); |
| feedRequestManager.loadMore( |
| streamToken, |
| ConsistencyToken.getDefaultInstance(), |
| result -> { |
| assertThat(result.isSuccessful()).isTrue(); |
| assertThat(result.getValue()).hasSize(0); |
| }); |
| |
| verify(protocolAdapter).createModel(responseCaptor.capture()); |
| LiteProtoTruth.assertThat(responseCaptor.getValue()).isEqualTo(response); |
| } |
| |
| @Test |
| public void testPaging_noMatch() { |
| MockServer.Builder mockServerBuilder = MockServer.newBuilder(); |
| Response response = |
| Response.newBuilder().setResponseVersion(ResponseVersion.FEED_RESPONSE).build(); |
| // Create a MockServerConfig without a matching token. |
| ConditionalResponse.Builder conditionalResponseBuilder = ConditionalResponse.newBuilder(); |
| conditionalResponseBuilder.setResponse(response); |
| mockServerBuilder.addConditionalResponses(conditionalResponseBuilder.build()); |
| MockServerNetworkClient networkClient = |
| new MockServerNetworkClient( |
| context, mockServerBuilder.build(), /* responseDelayMillis= */ 0L); |
| FeedRequestManager feedRequestManager = |
| new FeedRequestManager( |
| configuration, |
| networkClient, |
| protocolAdapter, |
| extensionRegistry, |
| scheduler, |
| getTaskQueue(), |
| timingUtils, |
| fakeThreadUtils, |
| actionReader, |
| context, |
| applicationInfo, |
| mainThreadRunner, |
| basicLoggingApi, |
| tooltipSupportedApi); |
| when(protocolAdapter.createModel(any(Response.class))) |
| .thenReturn(Result.success(new ArrayList<>())); |
| |
| fakeThreadUtils.enforceMainThread(false); |
| ByteString token = ByteString.copyFromUtf8("fooToken"); |
| StreamToken streamToken = StreamToken.newBuilder().setNextPageToken(token).build(); |
| feedRequestManager.loadMore( |
| streamToken, |
| ConsistencyToken.getDefaultInstance(), |
| result -> { |
| assertThat(result.isSuccessful()).isTrue(); |
| assertThat(result.getValue()).hasSize(0); |
| }); |
| |
| verify(protocolAdapter).createModel(responseCaptor.capture()); |
| LiteProtoTruth.assertThat(responseCaptor.getValue()).isEqualToDefaultInstance(); |
| } |
| |
| private FakeTaskQueue getTaskQueue() { |
| FakeTaskQueue taskQueue = |
| new FakeTaskQueue(MoreExecutors.directExecutor(), fakeClock, fakeThreadUtils); |
| taskQueue.initialize(() -> {}); |
| return taskQueue; |
| } |
| } |