All Downloads are FREE. Search and download functionalities are using the official Maven repository.

src.com.android.server.backup.testing.TestUtils Maven / Gradle / Ivy

/*
 * Copyright (C) 2018 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License
 */

package com.android.server.backup.testing;

import static com.google.common.truth.Truth.assertThat;
import static com.google.common.truth.Truth.assertWithMessage;

import static org.robolectric.Shadows.shadowOf;

import static java.util.stream.Collectors.toSet;

import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.os.SystemClock;

import com.android.server.testing.shadows.ShadowEventLog;

import org.robolectric.shadows.ShadowLog;
import org.robolectric.shadows.ShadowLooper;

import java.util.Arrays;
import java.util.concurrent.Callable;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.function.Predicate;
import java.util.function.Supplier;
import java.util.stream.IntStream;

public class TestUtils {
    private static final long TIMEOUT_MS = 3000;
    private static final long STEP_MS = 50;

    /**
     * Counts the number of messages in the looper {@code looper} that satisfy {@code
     * messageFilter}.
     */
    public static int messagesInLooper(Looper looper, Predicate messageFilter) {
        MessageQueue queue = looper.getQueue();
        int i = 0;
        for (Message m = shadowOf(queue).getHead(); m != null; m = shadowOf(m).getNext()) {
            if (messageFilter.test(m)) {
                i += 1;
            }
        }
        return i;
    }

    public static void waitUntil(Supplier condition)
            throws InterruptedException, TimeoutException {
        waitUntil(condition, STEP_MS, TIMEOUT_MS);
    }

    public static void waitUntil(Supplier condition, long stepMs, long timeoutMs)
            throws InterruptedException, TimeoutException {
        long deadline = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(timeoutMs);
        while (true) {
            if (condition.get()) {
                return;
            }
            if (System.nanoTime() > deadline) {
                throw new TimeoutException("Test timed-out waiting for condition");
            }
            Thread.sleep(stepMs);
        }
    }

    /** Version of {@link ShadowLooper#runToEndOfTasks()} that also advances the system clock. */
    public static void runToEndOfTasks(Looper looper) {
        ShadowLooper shadowLooper = shadowOf(looper);
        shadowLooper.runToEndOfTasks();
        // Handler instances have their own clock, so advancing looper (with runToEndOfTasks())
        // above does NOT advance the handlers' clock, hence whenever a handler post messages with
        // specific time to the looper the time of those messages will be before the looper's time.
        // To fix this we advance SystemClock as well since that is from where the handlers read
        // time.
        SystemClock.setCurrentTimeMillis(shadowLooper.getScheduler().getCurrentTime());
    }

    /**
     * Reset logcat with {@link ShadowLog#reset()} before the test case if you do anything that uses
     * logcat before that.
     */
    public static void assertLogcatAtMost(String tag, int level) {
        assertWithMessage("All logs <= " + level).that(
                ShadowLog.getLogsForTag(tag).stream().allMatch(logItem -> logItem.type <= level))
                .isTrue();
    }

    /**
     * Reset logcat with {@link ShadowLog#reset()} before the test case if you do anything that uses
     * logcat before that.
     */
    public static void assertLogcatAtLeast(String tag, int level) {
        assertWithMessage("Any log >= " + level).that(
                ShadowLog.getLogsForTag(tag).stream().anyMatch(logItem -> logItem.type >= level))
                .isTrue();
    }

    /**
     * Verifies that logcat has produced log items as specified per level in {@code logs} (with
     * repetition).
     *
     * 

So, if you call {@code assertLogcat(TAG, Log.ERROR, Log.ERROR)}, you assert that there are * exactly 2 log items, each with level ERROR. * *

Reset logcat with {@link ShadowLog#reset()} before the test case if you do anything * that uses logcat before that. */ public static void assertLogcat(String tag, int... logs) { assertWithMessage("Log items (specified per level)").that( ShadowLog.getLogsForTag(tag).stream() .map(logItem -> logItem.type) .collect(toSet())) .containsExactly(IntStream.of(logs).boxed().toArray()); } public static void assertLogcatContains(String tag, Predicate predicate) { assertThat(ShadowLog.getLogsForTag(tag).stream().anyMatch(predicate)).isTrue(); } /** Declare shadow {@link ShadowEventLog} to use this. */ public static void assertEventLogged(int tag, Object... values) { assertWithMessage("Event logs").that(ShadowEventLog.getEntries()) .contains(new ShadowEventLog.Entry(tag, Arrays.asList(values))); } /** Declare shadow {@link ShadowEventLog} to use this. */ public static void assertEventNotLogged(int tag, Object... values) { assertWithMessage("Event logs").that(ShadowEventLog.getEntries()) .doesNotContain(new ShadowEventLog.Entry(tag, Arrays.asList(values))); } /** * Calls {@link Runnable#run()} and returns if no exception is thrown. Otherwise, if the * exception is unchecked, rethrow it; if it's checked wrap in a {@link RuntimeException} and * throw. * *

Warning:DON'T use this outside tests. A wrapped checked exception is just a failure * in a test. */ public static void uncheck(ThrowingRunnable runnable) { try { runnable.runOrThrow(); } catch (Exception e) { throw wrapIfChecked(e); } } /** * Calls {@link Callable#call()} and returns the value if no exception is thrown. Otherwise, if * the exception is unchecked, rethrow it; if it's checked wrap in a {@link RuntimeException} * and throw. * *

Warning:DON'T use this outside tests. A wrapped checked exception is just a failure * in a test. */ public static T uncheck(Callable callable) { try { return callable.call(); } catch (Exception e) { throw wrapIfChecked(e); } } /** * Wrap {@code e} in a {@link RuntimeException} only if it's not one already, in which case it's * returned. */ public static RuntimeException wrapIfChecked(Exception e) { if (e instanceof RuntimeException) { return (RuntimeException) e; } return new RuntimeException(e); } /** An equivalent of {@link Runnable} that allows throwing checked exceptions. */ @FunctionalInterface public interface ThrowingRunnable { void runOrThrow() throws Exception; } private TestUtils() {} }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy