1 /*
2  * Copyright (C) 2018 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 com.android.compatibility.common.util;
17 
18 import static junit.framework.Assert.fail;
19 
20 import android.os.SystemClock;
21 import android.util.Log;
22 
23 import java.util.function.BooleanSupplier;
24 
25 public class TestUtils {
26     private static final String TAG = "CtsTestUtils";
27 
TestUtils()28     private TestUtils() {
29     }
30 
31     public static final int DEFAULT_TIMEOUT_SECONDS = 30;
32 
33     /** Print an error log and fail. */
failWithLog(String message)34     public static void failWithLog(String message) {
35         Log.e(TAG, message);
36         fail(message);
37     }
38 
39     @FunctionalInterface
40     public interface BooleanSupplierWithThrow {
getAsBoolean()41         boolean getAsBoolean() throws Exception;
42     }
43 
44     @FunctionalInterface
45     public interface RunnableWithThrow {
run()46         void run() throws Exception;
47     }
48 
49     /**
50      * Wait until {@code predicate} is satisfied, or fail, with {@link #DEFAULT_TIMEOUT_SECONDS}.
51      */
waitUntil(String message, BooleanSupplierWithThrow predicate)52     public static void waitUntil(String message, BooleanSupplierWithThrow predicate)
53             throws Exception {
54         waitUntil(message, 0, predicate);
55     }
56 
57     /**
58      * Wait until {@code predicate} is satisfied, or fail, with a given timeout.
59      */
waitUntil( String message, int timeoutSecond, BooleanSupplierWithThrow predicate)60     public static void waitUntil(
61             String message, int timeoutSecond, BooleanSupplierWithThrow predicate)
62             throws Exception {
63         if (timeoutSecond <= 0) {
64             timeoutSecond = DEFAULT_TIMEOUT_SECONDS;
65         }
66         int sleep = 125;
67         final long timeout = SystemClock.uptimeMillis() + timeoutSecond * 1000;
68         while (SystemClock.uptimeMillis() < timeout) {
69             if (predicate.getAsBoolean()) {
70                 return; // okay
71             }
72             Thread.sleep(sleep);
73             sleep *= 5;
74             sleep = Math.min(2000, sleep);
75         }
76         failWithLog("Timeout: " + message);
77     }
78 
79     /**
80      * Run a Runnable {@code r}, and if it throws, also run {@code onFailure}.
81      */
runWithFailureHook(RunnableWithThrow r, RunnableWithThrow onFailure)82     public static void runWithFailureHook(RunnableWithThrow r, RunnableWithThrow onFailure)
83             throws Exception {
84         if (r == null) {
85             throw new NullPointerException("r");
86         }
87         if (onFailure == null) {
88             throw new NullPointerException("onFailure");
89         }
90         try {
91             r.run();
92         } catch (Throwable th) {
93             Log.e(TAG, "Caught exception: " + th, th);
94             onFailure.run();
95             throw th;
96         }
97     }
98 
99     /**
100      * Synchronized wait for a specified condition.
101      *
102      * @param notifyLock Lock that will be notified when the condition might have changed
103      * @param condition The condition to check
104      * @param timeoutMs The timeout in millis
105      * @param conditionName The name to include in the assertion. If null, will be given a default.
106      */
waitOn(Object notifyLock, BooleanSupplier condition, long timeoutMs, String conditionName)107     public static void waitOn(Object notifyLock, BooleanSupplier condition,
108             long timeoutMs, String conditionName) {
109         if (conditionName == null) conditionName = "condition";
110         if (condition.getAsBoolean()) return;
111 
112         synchronized (notifyLock) {
113             try {
114                 long timeSlept = 0;
115                 while (!condition.getAsBoolean() && timeSlept < timeoutMs) {
116                     long sleepStart = SystemClock.uptimeMillis();
117                     notifyLock.wait(timeoutMs - timeSlept);
118                     timeSlept += SystemClock.uptimeMillis() - sleepStart;
119                 }
120                 if (!condition.getAsBoolean()) {
121                     throw new AssertionError("Timed out after " + timeSlept
122                             + "ms waiting for: " + conditionName);
123                 }
124             } catch (InterruptedException e) {
125                 throw new RuntimeException(e);
126             }
127         }
128     }
129 }
130 
131