1/*
2 * Copyright (C) 2015 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 */
16package com.android.messaging.util;
17
18import android.os.Looper;
19
20import java.util.Arrays;
21
22public final class Assert {
23    public static @interface RunsOnMainThread {}
24    public static @interface DoesNotRunOnMainThread {}
25    public static @interface RunsOnAnyThread {}
26
27    private static final String TEST_THREAD_SUBSTRING = "test";
28
29    private static boolean sIsEngBuild;
30    private static boolean sShouldCrash;
31
32    // Private constructor so no one creates this class.
33    private Assert() {
34    }
35
36    // The proguard rules will strip this method out on user/userdebug builds.
37    // If you change the method signature you MUST edit proguard-release.flags.
38    private static void setIfEngBuild() {
39        sShouldCrash = sIsEngBuild = true;
40    }
41
42    private static void refreshGservices(final BugleGservices gservices) {
43        sShouldCrash = sIsEngBuild;
44        if (!sShouldCrash) {
45            sShouldCrash = gservices.getBoolean(
46                    BugleGservicesKeys.ASSERTS_FATAL,
47                    BugleGservicesKeys.ASSERTS_FATAL_DEFAULT);
48        }
49    }
50
51    // Static initializer block to find out if we're running an eng or
52    // release build.
53    static {
54        setIfEngBuild();
55    }
56
57    // This is called from FactoryImpl once the Gservices class is initialized.
58    public static void initializeGservices (final BugleGservices gservices) {
59        gservices.registerForChanges(new Runnable() {
60            @Override
61            public void run() {
62                refreshGservices(gservices);
63            }
64        });
65        refreshGservices(gservices);
66    }
67
68    /**
69     * Halt execution if this is not an eng build.
70     * <p>Intended for use in code paths that should be run only for tests and never on
71     * a real build.
72     * <p>Note that this will crash on a user build even though asserts don't normally
73     * crash on a user build.
74     */
75    public static void isEngBuild() {
76        isTrueReleaseCheck(sIsEngBuild);
77    }
78
79    /**
80     * Halt execution if this isn't the case.
81     */
82    public static void isTrue(final boolean condition) {
83        if (!condition) {
84            fail("Expected condition to be true", false);
85        }
86    }
87
88    /**
89     * Halt execution if this isn't the case.
90     */
91    public static void isFalse(final boolean condition) {
92        if (condition) {
93            fail("Expected condition to be false", false);
94        }
95    }
96
97    /**
98     * Halt execution even in release builds if this isn't the case.
99     */
100    public static void isTrueReleaseCheck(final boolean condition) {
101        if (!condition) {
102            fail("Expected condition to be true", true);
103        }
104    }
105
106    public static void equals(final int expected, final int actual) {
107        if (expected != actual) {
108            fail("Expected " + expected + " but got " + actual, false);
109        }
110    }
111
112    public static void equals(final long expected, final long actual) {
113        if (expected != actual) {
114            fail("Expected " + expected + " but got " + actual, false);
115        }
116    }
117
118    public static void equals(final Object expected, final Object actual) {
119        if (expected != actual
120                && (expected == null || actual == null || !expected.equals(actual))) {
121            fail("Expected " + expected + " but got " + actual, false);
122        }
123    }
124
125    public static void oneOf(final int actual, final int ...expected) {
126        for (int value : expected) {
127            if (actual == value) {
128                return;
129            }
130        }
131        fail("Expected value to be one of " + Arrays.toString(expected) + " but was " + actual);
132    }
133
134    public static void inRange(
135            final int val, final int rangeMinInclusive, final int rangeMaxInclusive) {
136        if (val < rangeMinInclusive || val > rangeMaxInclusive) {
137            fail("Expected value in range [" + rangeMinInclusive + ", " +
138                    rangeMaxInclusive + "], but was " + val, false);
139        }
140    }
141
142    public static void inRange(
143            final long val, final long rangeMinInclusive, final long rangeMaxInclusive) {
144        if (val < rangeMinInclusive || val > rangeMaxInclusive) {
145            fail("Expected value in range [" + rangeMinInclusive + ", " +
146                    rangeMaxInclusive + "], but was " + val, false);
147        }
148    }
149
150    public static void isMainThread() {
151        if (Looper.myLooper() != Looper.getMainLooper()
152                && !Thread.currentThread().getName().contains(TEST_THREAD_SUBSTRING)) {
153            fail("Expected to run on main thread", false);
154        }
155    }
156
157    public static void isNotMainThread() {
158        if (Looper.myLooper() == Looper.getMainLooper()
159                && !Thread.currentThread().getName().contains(TEST_THREAD_SUBSTRING)) {
160            fail("Not expected to run on main thread", false);
161        }
162    }
163
164    /**
165     * Halt execution if the value passed in is not null
166     * @param obj The object to check
167     */
168    public static void isNull(final Object obj) {
169        if (obj != null) {
170            fail("Expected object to be null", false);
171        }
172    }
173
174    /**
175     * Halt execution if the value passed in is not null
176     * @param obj The object to check
177     * @param failureMessage message to print when halting execution
178     */
179    public static void isNull(final Object obj, final String failureMessage) {
180        if (obj != null) {
181            fail(failureMessage, false);
182        }
183    }
184
185    /**
186     * Halt execution if the value passed in is null
187     * @param obj The object to check
188     */
189    public static void notNull(final Object obj) {
190        if (obj == null) {
191            fail("Expected value to be non-null", false);
192        }
193    }
194
195    public static void fail(final String message) {
196        fail("Assert.fail() called: " + message, false);
197    }
198
199    private static void fail(final String message, final boolean crashRelease) {
200        LogUtil.e(LogUtil.BUGLE_TAG, message);
201        if (crashRelease || sShouldCrash) {
202            throw new AssertionError(message);
203        } else {
204            // Find the method whose assertion failed. We're using a depth of 2, because all public
205            // Assert methods delegate to this one (see javadoc on getCaller() for details).
206            StackTraceElement caller = DebugUtils.getCaller(2);
207            if (caller != null) {
208                // This log message can be de-obfuscated by the Proguard retrace tool, just like a
209                // full stack trace from a crash.
210                LogUtil.e(LogUtil.BUGLE_TAG, "\tat " + caller.toString());
211            }
212        }
213    }
214}
215