summaryrefslogtreecommitdiff
path: root/src/com/android/messaging/util/Assert.java
blob: 437965cd51b591a363ca44d70f4a90c8f06475bb (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
/*
 * 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.
     * <p>Intended for use in code paths that should be run only for tests and never on
     * a real build.
     * <p>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());
            }
        }
    }
}