1 /* 2 * Copyright (C) 2022 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 17 package android.platform.test.util; 18 19 import android.platform.uiautomator_helpers.WaitUtils; 20 21 import androidx.test.uiautomator.StaleObjectException; 22 23 import java.time.Duration; 24 import java.util.Optional; 25 import java.util.function.Supplier; 26 27 /** Helper class for writing health tests. */ 28 public class HealthTestingUtils { 29 30 private static final String TAG = "HealthTestingUtils"; 31 private static final int WAIT_TIME_MS = 10000; 32 private static final int DEFAULT_SETTLE_TIME_MS = 3000; 33 HealthTestingUtils()34 private HealthTestingUtils() {} 35 36 /** Supplier of a boolean that can throw an exception. */ 37 public interface Condition { isTrue()38 boolean isTrue() throws Throwable; 39 } 40 41 /** 42 * Waits for a diagnostics to become null within 10 sec. 43 * 44 * @param diagnostics Supplier of the error message. It should return null in case of success. 45 * The method repeatedly calls this provider while it's returning non-null. If it keeps 46 * returning non-null after 10 sec, the method throws an exception using the last string 47 * returned by the provider. Otherwise, it succeeds. 48 */ waitForNullDiag(Supplier<String> diagnostics)49 public static void waitForNullDiag(Supplier<String> diagnostics) { 50 final String[] lastDiag = new String[1]; 51 waitForCondition(() -> lastDiag[0], () -> (lastDiag[0] = diagnostics.get()) == null); 52 } 53 54 /** 55 * Waits for a value producer to produce a result that's isPresent() and fails if it doesn't 56 * happen within 10 sec. 57 * 58 * @param message Supplier of the error message. 59 * @param resultProducer Result producer. 60 * @return The present value returned by the producer. 61 */ waitForValuePresent( Supplier<String> message, Supplier<Optional<T>> resultProducer)62 public static <T> T waitForValuePresent( 63 Supplier<String> message, Supplier<Optional<T>> resultProducer) { 64 class ResultHolder { 65 public T value; 66 } 67 final ResultHolder result = new ResultHolder(); 68 69 waitForCondition( 70 message, 71 () -> { 72 final Optional<T> optionalResult = resultProducer.get(); 73 if (optionalResult.isPresent()) { 74 result.value = optionalResult.get(); 75 return true; 76 } else { 77 return false; 78 } 79 }); 80 81 return result.value; 82 } 83 84 /** 85 * Waits for a result to be produced without throwing a {@link StaleObjectException}. 86 * 87 * <p>This is useful in case of dealing with containers that change or go out of screen during 88 * the test, to reduce flakiness. 89 * 90 * @param errorMessage message thrown when resultProduces fails after the maximum number of 91 * retries. 92 * @param resultProducer produces the output. Might throw {@link StaleObjectException}. 93 */ waitForValueCatchingStaleObjectExceptions( Supplier<String> errorMessage, Supplier<T> resultProducer)94 public static <T> T waitForValueCatchingStaleObjectExceptions( 95 Supplier<String> errorMessage, Supplier<T> resultProducer) { 96 return waitForValuePresent( 97 errorMessage, 98 () -> { 99 try { 100 return Optional.ofNullable(resultProducer.get()); 101 } catch (StaleObjectException e) { 102 return Optional.empty(); 103 } 104 }); 105 } 106 107 /** 108 * Waits for a condition and fails if it doesn't become true within 10 sec. 109 * 110 * @param message Supplier of the error message. 111 * @param condition Condition. 112 */ 113 public static void waitForCondition(Supplier<String> message, Condition condition) { 114 waitForCondition(message, condition, WAIT_TIME_MS); 115 } 116 117 /** 118 * Waits for a condition and fails if it doesn't become true within specified time period. 119 * 120 * @param message Supplier of the error message. 121 * @param condition Condition. 122 * @param timeoutMs Timeout. 123 */ 124 public static void waitForCondition( 125 Supplier<String> message, Condition condition, long timeoutMs) { 126 127 WaitUtils.ensureThat( 128 "waitForCondition", 129 /* timeout= */ Duration.ofMillis(timeoutMs), 130 /* errorProvider= */ message::get, 131 /* condition= */ () -> { 132 try { 133 return condition.isTrue(); 134 } catch (Throwable t) { 135 throw new RuntimeException(t); 136 } 137 }); 138 } 139 140 /** @see HealthTestingUtils#waitForValueToSettle */ 141 public static <T> T waitForValueToSettle(Supplier<String> errorMessage, Supplier<T> supplier) { 142 return waitForValueToSettle( 143 errorMessage, 144 supplier, 145 /* minimumSettleTime= */ DEFAULT_SETTLE_TIME_MS, 146 /* timeoutMs= */ WAIT_TIME_MS); 147 } 148 149 /** 150 * Waits for the supplier to return the same value for a specified time period. If the value 151 * changes, the timer gets restarted. Fails when reaching the timeout. The minimum running time 152 * of this method is the settle time. 153 * 154 * @return the settled value. Fails if it doesn't settle. 155 */ 156 public static <T> T waitForValueToSettle( 157 Supplier<String> errorMessage, 158 Supplier<T> supplier, 159 long minimumSettleTime, 160 long timeoutMs) { 161 return WaitUtils.waitForNullableValueToSettle( 162 "waitForValueToSettle", 163 /* minimumSettleTime= */ Duration.ofMillis(minimumSettleTime), 164 /* timeout= */ Duration.ofMillis(timeoutMs), 165 /* errorProvider= */ errorMessage::get, 166 /* supplier */ supplier::get); 167 } 168 } 169