1515 */
1616package com .google .cloud .bigtable .data .v2 .stub ;
1717
18- import static org .mockito .Mockito .never ;
19- import static org .mockito .Mockito .times ;
20- import static org .mockito .Mockito .verify ;
18+ import static com .google .common .truth .Truth .assertThat ;
2119import static org .mockito .Mockito .when ;
2220
2321import com .google .api .core .ApiFuture ;
2422import com .google .api .gax .core .NoCredentialsProvider ;
25- import com .google .api .gax .tracing .ApiTracer ;
2623import com .google .api .gax .tracing .ApiTracerFactory ;
2724import com .google .auto .value .AutoValue ;
2825import com .google .bigtable .v2 .BigtableGrpc ;
4340import com .google .cloud .bigtable .data .v2 .models .TableId ;
4441import com .google .cloud .bigtable .data .v2 .models .TargetId ;
4542import com .google .cloud .bigtable .data .v2 .stub .metrics .BigtableTracer ;
43+ import com .google .cloud .bigtable .data .v2 .stub .metrics .NoopMetricsProvider ;
4644import com .google .common .base .Preconditions ;
4745import com .google .common .base .Supplier ;
4846import com .google .common .collect .ImmutableList ;
5654import io .grpc .ServerServiceDefinition ;
5755import io .grpc .stub .ServerCalls ;
5856import io .grpc .stub .StreamObserver ;
57+ import java .util .Optional ;
58+ import java .util .concurrent .ConcurrentHashMap ;
5959import java .util .concurrent .ExecutionException ;
6060import java .util .concurrent .LinkedBlockingDeque ;
6161import java .util .concurrent .TimeUnit ;
6262import java .util .concurrent .TimeoutException ;
63+ import java .util .concurrent .atomic .AtomicInteger ;
6364import org .junit .After ;
6465import org .junit .Assert ;
6566import org .junit .Before ;
6970import org .junit .runners .JUnit4 ;
7071import org .mockito .Mock ;
7172import org .mockito .Mockito ;
72- import org .mockito .exceptions .verification .WantedButNotInvoked ;
7373import org .mockito .junit .MockitoJUnit ;
7474import org .mockito .junit .MockitoRule ;
7575
@@ -85,7 +85,7 @@ public class SkipTrailersTest {
8585 private Server server ;
8686
8787 @ Mock private ApiTracerFactory tracerFactory ;
88- @ Mock private BigtableTracer tracer ;
88+ private FakeTracer tracer = new FakeTracer () ;
8989
9090 private BigtableDataClient client ;
9191
@@ -95,12 +95,12 @@ public void setUp() throws Exception {
9595 server = FakeServiceBuilder .create (hackedService ).start ();
9696
9797 when (tracerFactory .newTracer (Mockito .any (), Mockito .any (), Mockito .any ())).thenReturn (tracer );
98- when (tracer .inScope ()).thenReturn (Mockito .mock (ApiTracer .Scope .class ));
9998
10099 BigtableDataSettings .Builder clientBuilder =
101100 BigtableDataSettings .newBuilderForEmulator (server .getPort ())
102101 .setProjectId (PROJECT_ID )
103102 .setInstanceId (INSTANCE_ID )
103+ .setMetricsProvider (NoopMetricsProvider .INSTANCE )
104104 .setCredentialsProvider (NoCredentialsProvider .create ());
105105 clientBuilder .stubSettings ().setEnableSkipTrailers (true ).setTracerFactory (tracerFactory );
106106
@@ -159,7 +159,7 @@ private <T> void test(Supplier<ApiFuture<?>> invoker, T fakeResponse)
159159
160160 // Wait for the call to start on the server
161161 @ SuppressWarnings ("unchecked" )
162- ServerRpc <?, T > rpc = (ServerRpc <?, T >) hackedService .rpcs .poll (10 , TimeUnit .SECONDS );
162+ ServerRpc <?, T > rpc = (ServerRpc <?, T >) hackedService .rpcs .poll (30 , TimeUnit .SECONDS );
163163 Preconditions .checkNotNull (
164164 rpc , "Timed out waiting for the call to be received by the mock server" );
165165
@@ -173,8 +173,21 @@ private <T> void test(Supplier<ApiFuture<?>> invoker, T fakeResponse)
173173 Assert .fail ("timed out waiting for the trailer optimization future to resolve" );
174174 }
175175
176- verify (tracer , times (1 )).operationFinishEarly ();
177- verify (tracer , never ()).operationSucceeded ();
176+ // The tracer will be notified in parallel to the future being resolved
177+ // This normal and expected, but requires the test to wait a bit
178+ for (int i = 10 ; i > 0 ; i --) {
179+ try {
180+ assertThat (tracer .getCallCount ("operationFinishEarly" )).isEqualTo (1 );
181+ break ;
182+ } catch (AssertionError e ) {
183+ if (i > 1 ) {
184+ Thread .sleep (100 );
185+ } else {
186+ throw e ;
187+ }
188+ }
189+ }
190+ assertThat (tracer .getCallCount ("operationSucceeded" )).isEqualTo (0 );
178191
179192 // clean up
180193 rpc .getResponseStream ().onCompleted ();
@@ -183,9 +196,9 @@ private <T> void test(Supplier<ApiFuture<?>> invoker, T fakeResponse)
183196 // Since we dont have a way to know exactly when this happens, we poll
184197 for (int i = 10 ; i > 0 ; i --) {
185198 try {
186- verify (tracer , times ( 1 )).operationSucceeded ( );
199+ assertThat (tracer . getCallCount ( "operationSucceeded" )).isEqualTo ( 1 );
187200 break ;
188- } catch (WantedButNotInvoked e ) {
201+ } catch (AssertionError e ) {
189202 if (i > 1 ) {
190203 Thread .sleep (100 );
191204 } else {
@@ -195,6 +208,27 @@ private <T> void test(Supplier<ApiFuture<?>> invoker, T fakeResponse)
195208 }
196209 }
197210
211+ static class FakeTracer extends BigtableTracer {
212+ ConcurrentHashMap <String , AtomicInteger > callCounts = new ConcurrentHashMap <>();
213+
214+ @ Override
215+ public void operationFinishEarly () {
216+ record ("operationFinishEarly" );
217+ }
218+
219+ @ Override
220+ public void operationSucceeded () {
221+ record ("operationSucceeded" );
222+ }
223+
224+ private void record (String op ) {
225+ callCounts .computeIfAbsent (op , (ignored ) -> new AtomicInteger ()).getAndIncrement ();
226+ }
227+
228+ private int getCallCount (String op ) {
229+ return Optional .ofNullable (callCounts .get (op )).map (AtomicInteger ::get ).orElse (0 );
230+ }
231+ }
198232 /**
199233 * Hack the srvice definition to allow grpc server to simulate delayed trailers. This will augment
200234 * the bigtable service definition to promote unary rpcs to server streaming
0 commit comments