1 /*
2  * Copyright (C) 2020 The Android Open Source Project
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 package android.car.test.mocks;
17 
18 import android.annotation.NonNull;
19 import android.annotation.Nullable;
20 import android.util.Log;
21 
22 import java.util.Objects;
23 import java.util.concurrent.CountDownLatch;
24 import java.util.concurrent.ExecutionException;
25 import java.util.concurrent.Future;
26 import java.util.concurrent.Semaphore;
27 import java.util.concurrent.TimeUnit;
28 import java.util.concurrent.TimeoutException;
29 
30 /**
31  * Provides common Mockito calls for core Java classes.
32  */
33 public final class JavaMockitoHelper {
34 
35     static final long ASYNC_TIMEOUT_MS = 500;
36 
37     private static final String TAG = JavaMockitoHelper.class.getSimpleName();
38 
39     /**
40      * Waits for a latch to be counted down.
41      *
42      * @param timeoutMs how long to wait for
43      *
44      * @throws IllegalStateException if it times out.
45      */
await(@onNull CountDownLatch latch, long timeoutMs)46     public static void await(@NonNull CountDownLatch latch, long timeoutMs)
47             throws InterruptedException {
48         Log.v(TAG, "waiting " + timeoutMs + "ms for latch " + latch);
49         if (!latch.await(timeoutMs, TimeUnit.MILLISECONDS)) {
50             Log.e(TAG, "latch timed out");
51             throw new IllegalStateException(latch + " not called in " + timeoutMs + " ms");
52         }
53     }
54 
55     /**
56      * Waits for a semaphore.
57      *
58      * @param timeoutMs how long to wait for
59      *
60      * @throws IllegalStateException if it times out.
61      */
await(@onNull Semaphore semaphore, long timeoutMs)62     public static void await(@NonNull Semaphore semaphore, long timeoutMs)
63             throws InterruptedException {
64         Log.v(TAG, "waiting " + timeoutMs + "ms for semaphore " + semaphore);
65         if (!semaphore.tryAcquire(timeoutMs, TimeUnit.MILLISECONDS)) {
66             Log.e(TAG, "semaphore timed out");
67             throw new IllegalStateException(semaphore + " not released in " + timeoutMs + " ms");
68         }
69     }
70 
71     /**
72      * Silently waits for a latch to be counted down, without throwing any exception if it isn't.
73      *
74      * @param timeoutMs how long to wait for
75      *
76      * @return whether the latch was counted down.
77      */
silentAwait(@onNull CountDownLatch latch, long timeoutMs)78     public static boolean silentAwait(@NonNull CountDownLatch latch, long timeoutMs) {
79         boolean called;
80         try {
81             Log.v(TAG, "waiting " + timeoutMs + "ms for semaphore " + latch);
82             called = latch.await(timeoutMs, TimeUnit.MILLISECONDS);
83             if (!called) {
84                 Log.w(TAG, latch + " not called in " + timeoutMs + " ms");
85             }
86         } catch (InterruptedException e) {
87             Thread.currentThread().interrupt();
88             Log.w(TAG, latch + " interrupted", e);
89             return false;
90         }
91         return called;
92     }
93 
94     /**
95      * Gets the result of a future, or throw a {@link IllegalStateException} if it times out after
96      * {@value #ASYNC_TIMEOUT_MS} ms.
97      */
98     @NonNull
getResult(@onNull Future<T> future, @NonNull String messageFormat, @Nullable Object...messageArgs)99     public static <T> T getResult(@NonNull Future<T> future,
100             @NonNull String messageFormat, @Nullable Object...messageArgs) {
101         return getResult(future, ASYNC_TIMEOUT_MS, messageFormat, messageArgs);
102     }
103 
104     /**
105      * Gets the result of a future, or throw a {@link IllegalStateException} if it times out.
106      */
107     @NonNull
getResult(@onNull Future<T> future, long timeoutMs, @NonNull String messageFormat, @Nullable Object...messageArgs)108     public static <T> T getResult(@NonNull Future<T> future, long timeoutMs,
109             @NonNull String messageFormat, @Nullable Object...messageArgs) {
110         String msg = String.format(Objects.requireNonNull(messageFormat, "messageFormat"),
111                 messageArgs);
112         try {
113             return future.get(timeoutMs, TimeUnit.MILLISECONDS);
114         } catch (InterruptedException e) {
115             Thread.currentThread().interrupt();
116             throw new IllegalStateException("future for '" + msg + "' interrupted", e);
117         } catch (TimeoutException e) {
118             throw new IllegalStateException("future for '" + msg + "' not called in "
119                     + timeoutMs + "ms", e);
120         } catch (ExecutionException e) {
121             throw new IllegalStateException("failed to get future for '" + msg + "'", e);
122         }
123     }
124 
JavaMockitoHelper()125     private JavaMockitoHelper() {
126         throw new UnsupportedOperationException("contains only static methods");
127     }
128 }
129