/* * Copyright (C) 2015 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.messaging.util; import android.os.Looper; import java.util.Arrays; public final class Assert { public static @interface RunsOnMainThread {} public static @interface DoesNotRunOnMainThread {} public static @interface RunsOnAnyThread {} private static final String TEST_THREAD_SUBSTRING = "test"; private static boolean sIsEngBuild; private static boolean sShouldCrash; // Private constructor so no one creates this class. private Assert() { } // The proguard rules will strip this method out on user/userdebug builds. // If you change the method signature you MUST edit proguard-release.flags. private static void setIfEngBuild() { sShouldCrash = sIsEngBuild = true; } private static void refreshGservices(final BugleGservices gservices) { sShouldCrash = sIsEngBuild; if (!sShouldCrash) { sShouldCrash = gservices.getBoolean( BugleGservicesKeys.ASSERTS_FATAL, BugleGservicesKeys.ASSERTS_FATAL_DEFAULT); } } // Static initializer block to find out if we're running an eng or // release build. static { setIfEngBuild(); } // This is called from FactoryImpl once the Gservices class is initialized. public static void initializeGservices (final BugleGservices gservices) { gservices.registerForChanges(new Runnable() { @Override public void run() { refreshGservices(gservices); } }); refreshGservices(gservices); } /** * Halt execution if this is not an eng build. *

Intended for use in code paths that should be run only for tests and never on * a real build. *

Note that this will crash on a user build even though asserts don't normally * crash on a user build. */ public static void isEngBuild() { isTrueReleaseCheck(sIsEngBuild); } /** * Halt execution if this isn't the case. */ public static void isTrue(final boolean condition) { if (!condition) { fail("Expected condition to be true", false); } } /** * Halt execution if this isn't the case. */ public static void isFalse(final boolean condition) { if (condition) { fail("Expected condition to be false", false); } } /** * Halt execution even in release builds if this isn't the case. */ public static void isTrueReleaseCheck(final boolean condition) { if (!condition) { fail("Expected condition to be true", true); } } public static void equals(final int expected, final int actual) { if (expected != actual) { fail("Expected " + expected + " but got " + actual, false); } } public static void equals(final long expected, final long actual) { if (expected != actual) { fail("Expected " + expected + " but got " + actual, false); } } public static void equals(final Object expected, final Object actual) { if (expected != actual && (expected == null || actual == null || !expected.equals(actual))) { fail("Expected " + expected + " but got " + actual, false); } } public static void oneOf(final int actual, final int ...expected) { for (int value : expected) { if (actual == value) { return; } } fail("Expected value to be one of " + Arrays.toString(expected) + " but was " + actual); } public static void inRange( final int val, final int rangeMinInclusive, final int rangeMaxInclusive) { if (val < rangeMinInclusive || val > rangeMaxInclusive) { fail("Expected value in range [" + rangeMinInclusive + ", " + rangeMaxInclusive + "], but was " + val, false); } } public static void inRange( final long val, final long rangeMinInclusive, final long rangeMaxInclusive) { if (val < rangeMinInclusive || val > rangeMaxInclusive) { fail("Expected value in range [" + rangeMinInclusive + ", " + rangeMaxInclusive + "], but was " + val, false); } } public static void isMainThread() { if (Looper.myLooper() != Looper.getMainLooper() && !Thread.currentThread().getName().contains(TEST_THREAD_SUBSTRING)) { fail("Expected to run on main thread", false); } } public static void isNotMainThread() { if (Looper.myLooper() == Looper.getMainLooper() && !Thread.currentThread().getName().contains(TEST_THREAD_SUBSTRING)) { fail("Not expected to run on main thread", false); } } /** * Halt execution if the value passed in is not null * @param obj The object to check */ public static void isNull(final Object obj) { if (obj != null) { fail("Expected object to be null", false); } } /** * Halt execution if the value passed in is not null * @param obj The object to check * @param failureMessage message to print when halting execution */ public static void isNull(final Object obj, final String failureMessage) { if (obj != null) { fail(failureMessage, false); } } /** * Halt execution if the value passed in is null * @param obj The object to check */ public static void notNull(final Object obj) { if (obj == null) { fail("Expected value to be non-null", false); } } public static void fail(final String message) { fail("Assert.fail() called: " + message, false); } private static void fail(final String message, final boolean crashRelease) { LogUtil.e(LogUtil.BUGLE_TAG, message); if (crashRelease || sShouldCrash) { throw new AssertionError(message); } else { // Find the method whose assertion failed. We're using a depth of 2, because all public // Assert methods delegate to this one (see javadoc on getCaller() for details). StackTraceElement caller = DebugUtils.getCaller(2); if (caller != null) { // This log message can be de-obfuscated by the Proguard retrace tool, just like a // full stack trace from a crash. LogUtil.e(LogUtil.BUGLE_TAG, "\tat " + caller.toString()); } } } }