• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2016 The gRPC Authors
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package io.grpc.cronet;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertTrue;
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.Mockito.mock;
23 import static org.mockito.Mockito.times;
24 import static org.mockito.Mockito.verify;
25 import static org.mockito.Mockito.when;
26 
27 import android.os.Build;
28 import io.grpc.Attributes;
29 import io.grpc.CallOptions;
30 import io.grpc.ClientStreamTracer;
31 import io.grpc.Metadata;
32 import io.grpc.MethodDescriptor;
33 import io.grpc.SecurityLevel;
34 import io.grpc.Status;
35 import io.grpc.cronet.CronetChannelBuilder.StreamBuilderFactory;
36 import io.grpc.internal.ClientStreamListener;
37 import io.grpc.internal.GrpcAttributes;
38 import io.grpc.internal.ManagedClientTransport;
39 import io.grpc.internal.TransportTracer;
40 import io.grpc.testing.TestMethodDescriptors;
41 import java.net.InetSocketAddress;
42 import java.util.concurrent.Executor;
43 import org.chromium.net.BidirectionalStream;
44 import org.junit.Before;
45 import org.junit.Rule;
46 import org.junit.Test;
47 import org.junit.runner.RunWith;
48 import org.mockito.ArgumentCaptor;
49 import org.mockito.Mock;
50 import org.mockito.junit.MockitoJUnit;
51 import org.mockito.junit.MockitoRule;
52 import org.robolectric.RobolectricTestRunner;
53 import org.robolectric.annotation.Config;
54 
55 @RunWith(RobolectricTestRunner.class)
56 @Config(sdk = Build.VERSION_CODES.P)
57 public final class CronetClientTransportTest {
58   @Rule public final MockitoRule mocks = MockitoJUnit.rule();
59 
60   private static final String AUTHORITY = "test.example.com";
61   private static final Attributes.Key<String> EAG_ATTR_KEY =
62       Attributes.Key.create("eag-attr");
63 
64   private static final Attributes EAG_ATTRS =
65       Attributes.newBuilder().set(EAG_ATTR_KEY, "value").build();
66 
67   private final ClientStreamTracer[] tracers =
68       new ClientStreamTracer[]{ new ClientStreamTracer() {} };
69   private CronetClientTransport transport;
70   @Mock private StreamBuilderFactory streamFactory;
71   private MethodDescriptor<Void, Void> descriptor = TestMethodDescriptors.voidMethod();
72   @Mock private ManagedClientTransport.Listener clientTransportListener;
73   @Mock private BidirectionalStream.Builder builder;
74   private final Executor executor = new Executor() {
75       @Override
76       public void execute(Runnable r) {
77         r.run();
78       }
79     };
80 
81   @Before
setUp()82   public void setUp() {
83     transport =
84         new CronetClientTransport(
85             streamFactory,
86             new InetSocketAddress("localhost", 443),
87             AUTHORITY,
88             null,
89             EAG_ATTRS,
90             executor,
91             5000,
92             false,
93             TransportTracer.getDefaultFactory().create(),
94             false,
95             false);
96     Runnable callback = transport.start(clientTransportListener);
97     assertTrue(callback != null);
98     callback.run();
99     verify(clientTransportListener).transportReady();
100   }
101 
102   @Test
transportAttributes()103   public void transportAttributes() {
104     Attributes attrs = transport.getAttributes();
105     assertEquals(
106         SecurityLevel.PRIVACY_AND_INTEGRITY, attrs.get(GrpcAttributes.ATTR_SECURITY_LEVEL));
107     assertEquals(EAG_ATTRS, attrs.get(GrpcAttributes.ATTR_CLIENT_EAG_ATTRS));
108   }
109 
110   @Test
shutdownTransport()111   public void shutdownTransport() throws Exception {
112     CronetClientStream stream1 =
113         transport.newStream(descriptor, new Metadata(), CallOptions.DEFAULT, tracers);
114     CronetClientStream stream2 =
115         transport.newStream(descriptor, new Metadata(), CallOptions.DEFAULT, tracers);
116 
117     // Create a transport and start two streams on it.
118     ArgumentCaptor<BidirectionalStream.Callback> callbackCaptor =
119         ArgumentCaptor.forClass(BidirectionalStream.Callback.class);
120     when(streamFactory.newBidirectionalStreamBuilder(
121             any(String.class), callbackCaptor.capture(), any(Executor.class)))
122         .thenReturn(builder);
123     BidirectionalStream cronetStream1 = mock(BidirectionalStream.class);
124     when(builder.build()).thenReturn(cronetStream1);
125     stream1.start(mock(ClientStreamListener.class));
126     BidirectionalStream.Callback callback1 = callbackCaptor.getValue();
127 
128     BidirectionalStream cronetStream2 = mock(BidirectionalStream.class);
129     when(builder.build()).thenReturn(cronetStream2);
130     stream2.start(mock(ClientStreamListener.class));
131     BidirectionalStream.Callback callback2 = callbackCaptor.getValue();
132     // Shut down the transport. transportShutdown should be called immediately.
133     transport.shutdown();
134     verify(clientTransportListener).transportShutdown(any(Status.class));
135     // Have two live streams. Transport has not been terminated.
136     verify(clientTransportListener, times(0)).transportTerminated();
137 
138     callback1.onCanceled(cronetStream1, null);
139     // Still has one live stream
140     verify(clientTransportListener, times(0)).transportTerminated();
141     callback2.onCanceled(cronetStream1, null);
142     // All streams are gone now.
143     verify(clientTransportListener, times(1)).transportTerminated();
144   }
145 
146   @Test
startStreamAfterShutdown()147   public void startStreamAfterShutdown() throws Exception {
148     CronetClientStream stream =
149         transport.newStream(descriptor, new Metadata(), CallOptions.DEFAULT, tracers);
150     transport.shutdown();
151     BaseClientStreamListener listener = new BaseClientStreamListener();
152     stream.start(listener);
153 
154     assertEquals(Status.UNAVAILABLE.getCode(), listener.status.getCode());
155   }
156 
157   private static class BaseClientStreamListener implements ClientStreamListener {
158     private Status status;
159 
160     @Override
messagesAvailable(MessageProducer producer)161     public void messagesAvailable(MessageProducer producer) {}
162 
163     @Override
onReady()164     public void onReady() {}
165 
166     @Override
headersRead(Metadata headers)167     public void headersRead(Metadata headers) {}
168 
169     @Override
closed(Status status, RpcProgress rpcProgress, Metadata trailers)170     public void closed(Status status, RpcProgress rpcProgress, Metadata trailers) {
171       this.status = status;
172     }
173   }
174 }
175