mobile/android/base/tests/testANRReporter.java

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/mobile/android/base/tests/testANRReporter.java	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,229 @@
     1.4 +package org.mozilla.gecko.tests;
     1.5 +
     1.6 +import org.mozilla.gecko.AppConstants;
     1.7 +
     1.8 +import android.content.Context;
     1.9 +import android.content.Intent;
    1.10 +
    1.11 +import com.jayway.android.robotium.solo.Condition;
    1.12 +
    1.13 +import java.io.File;
    1.14 +import java.io.FileReader;
    1.15 +import java.io.FileWriter;
    1.16 +
    1.17 +import org.json.JSONObject;
    1.18 +
    1.19 +/**
    1.20 + * Tests the proper operation of the ANR reporter.
    1.21 + */
    1.22 +public class testANRReporter extends BaseTest {
    1.23 +
    1.24 +    private static final String ANR_ACTION = "android.intent.action.ANR";
    1.25 +    private static final String PING_DIR = "saved-telemetry-pings";
    1.26 +    private static final int WAIT_FOR_PING_TIMEOUT = 10000;
    1.27 +    private static final String ANR_PATH = "/data/anr/traces.txt";
    1.28 +    private static final String SAMPLE_ANR
    1.29 +        = "----- pid 1 at 2014-01-15 18:55:51 -----\n"
    1.30 +        + "Cmd line: " + AppConstants.ANDROID_PACKAGE_NAME + "\n"
    1.31 +        + "\n"
    1.32 +        + "JNI: CheckJNI is off; workarounds are off; pins=0; globals=397\n"
    1.33 +        + "\n"
    1.34 +        + "DALVIK THREADS:\n"
    1.35 +        + "(mutexes: tll=0 tsl=0 tscl=0 ghl=0)\n"
    1.36 +        + "\n"
    1.37 +        + "\"main\" prio=5 tid=1 WAIT\n"
    1.38 +        + "  | group=\"main\" sCount=1 dsCount=0 obj=0x41d6bc90 self=0x41d5a3c8\n"
    1.39 +        + "  | sysTid=3485 nice=0 sched=0/0 cgrp=apps handle=1074852180\n"
    1.40 +        + "  | state=S schedstat=( 0 0 0 ) utm=1065 stm=152 core=0\n"
    1.41 +        + "  at java.lang.Object.wait(Native Method)\n"
    1.42 +        + "  - waiting on <0x427ab340> (a org.mozilla.gecko.GeckoEditable$5)\n"
    1.43 +        + "  at java.lang.Object.wait(Object.java:364)\n"
    1.44 +        + "  at org.mozilla.gecko.GeckoEditable$5.run(GeckoEditable.java:746)\n"
    1.45 +        + "  at android.os.Handler.handleCallback(Handler.java:733)\n"
    1.46 +        + "  at android.os.Handler.dispatchMessage(Handler.java:95)\n"
    1.47 +        + "  at android.os.Looper.loop(Looper.java:137)\n"
    1.48 +        + "  at android.app.ActivityThread.main(ActivityThread.java:4998)\n"
    1.49 +        + "  at java.lang.reflect.Method.invokeNative(Native Method)\n"
    1.50 +        + "  at java.lang.reflect.Method.invoke(Method.java:515)\n"
    1.51 +        + "  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)\n"
    1.52 +        + "  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)\n"
    1.53 +        + "  at dalvik.system.NativeStart.main(Native Method)\n"
    1.54 +        + "\n"
    1.55 +        + "\"Gecko\" prio=5 tid=16 SUSPENDED\n"
    1.56 +        + "  | group=\"main\" sCount=1 dsCount=0 obj=0x426e2b28 self=0x76ae92e8\n"
    1.57 +        + "  | sysTid=3541 nice=0 sched=0/0 cgrp=apps handle=1991153472\n"
    1.58 +        + "  | state=S schedstat=( 0 0 0 ) utm=1118 stm=145 core=0\n"
    1.59 +        + "  #00  pc 00000904  /system/lib/libc.so (__futex_syscall3+4294832136)\n"
    1.60 +        + "  #01  pc 0000eec4  /system/lib/libc.so (__pthread_cond_timedwait_relative+48)\n"
    1.61 +        + "  #02  pc 0000ef24  /system/lib/libc.so (__pthread_cond_timedwait+64)\n"
    1.62 +        + "  #03  pc 000536b7  /system/lib/libdvm.so\n"
    1.63 +        + "  #04  pc 00053c79  /system/lib/libdvm.so (dvmChangeStatus(Thread*, ThreadStatus)+34)\n"
    1.64 +        + "  #05  pc 00049507  /system/lib/libdvm.so\n"
    1.65 +        + "  #06  pc 0004d84b  /system/lib/libdvm.so\n"
    1.66 +        + "  #07  pc 0003f1df  /dev/ashmem/libxul.so (deleted)\n"
    1.67 +        + "  at org.mozilla.gecko.mozglue.GeckoLoader.nativeRun(Native Method)\n"
    1.68 +        + "  at org.mozilla.gecko.GeckoAppShell.runGecko(GeckoAppShell.java:384)\n"
    1.69 +        + "  at org.mozilla.gecko.GeckoThread.run(GeckoThread.java:177)\n"
    1.70 +        + "\n"
    1.71 +        + "----- end 1 -----\n"
    1.72 +        + "\n"
    1.73 +        + "\n"
    1.74 +        + "----- pid 2 at 2013-01-25 13:27:01 -----\n"
    1.75 +        + "Cmd line: system_server\n"
    1.76 +        + "\n"
    1.77 +        + "----- end 2 -----\n";
    1.78 +
    1.79 +    private boolean mDone;
    1.80 +
    1.81 +    private JSONObject readPingFile(final File pingFile) throws Exception {
    1.82 +        final long fileSize = pingFile.length();
    1.83 +        if (fileSize == 0 || fileSize > Integer.MAX_VALUE) {
    1.84 +            throw new Exception("Invalid ping file size");
    1.85 +        }
    1.86 +        final char[] buffer = new char[(int) fileSize];
    1.87 +        final FileReader reader = new FileReader(pingFile);
    1.88 +        try {
    1.89 +            final int readSize = reader.read(buffer);
    1.90 +            if (readSize == 0 || readSize > buffer.length) {
    1.91 +                throw new Exception("Invalid number of bytes read");
    1.92 +            }
    1.93 +        } finally {
    1.94 +            reader.close();
    1.95 +        }
    1.96 +        return new JSONObject(new String(buffer));
    1.97 +    }
    1.98 +
    1.99 +    public void testANRReporter() throws Exception {
   1.100 +        blockForGeckoReady();
   1.101 +
   1.102 +        // Cannot test ANR reporter if it's disabled.
   1.103 +        if (!AppConstants.MOZ_ANDROID_ANR_REPORTER) {
   1.104 +            mAsserter.ok(true, "ANR reporter is disabled", null);
   1.105 +            return;
   1.106 +        }
   1.107 +
   1.108 +        // For the ANR reporter to work, we need to provide sample ANR traces to it.
   1.109 +        // Therefore, we need the ANR file to exist and writable. If not, we don't
   1.110 +        // have the right permissions to create the file, so we just bail.
   1.111 +        final File anrFile = new File(ANR_PATH);
   1.112 +        if (!anrFile.exists()) {
   1.113 +            mAsserter.ok(true, "ANR file does not exist", null);
   1.114 +            return;
   1.115 +        }
   1.116 +        if (!anrFile.canWrite()) {
   1.117 +            mAsserter.ok(true, "ANR file is not writable", null);
   1.118 +            return;
   1.119 +        }
   1.120 +
   1.121 +        final FileWriter anrWriter = new FileWriter(anrFile);
   1.122 +        try {
   1.123 +            anrWriter.write(SAMPLE_ANR);
   1.124 +        } finally {
   1.125 +            anrWriter.close();
   1.126 +        }
   1.127 +
   1.128 +        // Block the UI thread to simulate an ANR
   1.129 +        final Runnable uiBlocker = new Runnable() {
   1.130 +            @Override
   1.131 +            public synchronized void run() {
   1.132 +                while (!mDone) {
   1.133 +                    try {
   1.134 +                        wait();
   1.135 +                    } catch (final InterruptedException e) {
   1.136 +                    }
   1.137 +                }
   1.138 +            }
   1.139 +        };
   1.140 +        getActivity().runOnUiThread(uiBlocker);
   1.141 +
   1.142 +        // Make sure our initial ping directory is empty.
   1.143 +        final File pingDir = new File(mProfile, PING_DIR);
   1.144 +        final String[] initialFiles = pingDir.list();
   1.145 +        mAsserter.ok(initialFiles == null || initialFiles.length == 0,
   1.146 +                     "Ping directory is empty", null);
   1.147 +
   1.148 +        final Intent anrIntent = new Intent(ANR_ACTION);
   1.149 +        anrIntent.setPackage(AppConstants.ANDROID_PACKAGE_NAME);
   1.150 +        mAsserter.is(anrIntent.getPackage(), AppConstants.ANDROID_PACKAGE_NAME,
   1.151 +                     "Successfully set package name");
   1.152 +
   1.153 +        final Context testContext = getInstrumentation().getContext();
   1.154 +        mAsserter.isnot(testContext, null, "testContext should not be null");
   1.155 +
   1.156 +        // Trigger the ANR.
   1.157 +        mAsserter.info("Triggering ANR", null);
   1.158 +        testContext.sendBroadcast(anrIntent);
   1.159 +
   1.160 +        // ANR reporter is supposed to ignore duplicate ANRs.
   1.161 +        // This will be checked later when we look for ping files.
   1.162 +        mAsserter.info("Triggering second ANR", null);
   1.163 +        testContext.sendBroadcast(new Intent(anrIntent));
   1.164 +
   1.165 +        mAsserter.info("Waiting for ping", null);
   1.166 +        waitForCondition(new Condition() {
   1.167 +            @Override
   1.168 +            public boolean isSatisfied() {
   1.169 +                final File[] newFiles = pingDir.listFiles();
   1.170 +                if (newFiles == null || newFiles.length == 0) {
   1.171 +                    // Keep waiting.
   1.172 +                    return false;
   1.173 +                }
   1.174 +                // Make sure we have a complete file. We skip assertions and catch all
   1.175 +                // exceptions here because the condition may not be satisfied now but may
   1.176 +                // be satisfied later. After the wait is over, we will repeat the same
   1.177 +                // steps with assertions and exceptions.
   1.178 +                try {
   1.179 +                    return readPingFile(newFiles[0]).has("slug");
   1.180 +                } catch (final Exception e) {
   1.181 +                    return false;
   1.182 +                }
   1.183 +            }
   1.184 +        }, WAIT_FOR_PING_TIMEOUT);
   1.185 +
   1.186 +        mAsserter.ok(pingDir.exists(), "Ping directory exists", null);
   1.187 +        mAsserter.ok(pingDir.isDirectory(), "Ping directory is a directory", null);
   1.188 +
   1.189 +        final File[] newFiles = pingDir.listFiles();
   1.190 +        mAsserter.isnot(newFiles, null, "Ping directory is not empty");
   1.191 +        mAsserter.is(newFiles.length, 1, "ANR reporter wrote one ping");
   1.192 +        mAsserter.ok(newFiles[0].exists(), "Ping exists", null);
   1.193 +        mAsserter.ok(newFiles[0].isFile(), "Ping is a file", null);
   1.194 +        mAsserter.ok(newFiles[0].canRead(), "Ping is readable", null);
   1.195 +        mAsserter.info("Found ping file", newFiles[0].getPath());
   1.196 +
   1.197 +        // Check standard properties required by Telemetry server.
   1.198 +        final JSONObject pingObject = readPingFile(newFiles[0]);
   1.199 +        mAsserter.ok(pingObject.has("slug"), "Ping has slug property", null);
   1.200 +        mAsserter.ok(pingObject.has("reason"), "Ping has reason property", null);
   1.201 +        mAsserter.ok(pingObject.has("payload"), "Ping has payload property", null);
   1.202 +
   1.203 +        final JSONObject pingPayload = pingObject.getJSONObject("payload");
   1.204 +        mAsserter.ok(pingPayload.has("ver"), "Payload has ver property", null);
   1.205 +        mAsserter.ok(pingPayload.has("info"), "Payload has info property", null);
   1.206 +        mAsserter.ok(pingPayload.has("androidANR"), "Payload has androidANR property", null);
   1.207 +
   1.208 +        final JSONObject pingInfo = pingPayload.getJSONObject("info");
   1.209 +        mAsserter.ok(pingInfo.has("reason"), "Info has reason property", null);
   1.210 +        mAsserter.ok(pingInfo.has("appName"), "Info has appName property", null);
   1.211 +        mAsserter.ok(pingInfo.has("appUpdateChannel"), "Info has appUpdateChannel property", null);
   1.212 +        mAsserter.ok(pingInfo.has("appVersion"), "Info has appVersion property", null);
   1.213 +        mAsserter.ok(pingInfo.has("appBuildID"), "Info has appBuildID property", null);
   1.214 +
   1.215 +        // Do some profile clean up. This is not absolutely necessary because the profile
   1.216 +        // is blown away after test runs anyways, so we don't check return values here.
   1.217 +        for (final File ping : newFiles) {
   1.218 +            ping.delete();
   1.219 +        }
   1.220 +        pingDir.delete();
   1.221 +
   1.222 +        // Unblock UI thread
   1.223 +        synchronized (uiBlocker) {
   1.224 +            mDone = true;
   1.225 +            uiBlocker.notify();
   1.226 +        }
   1.227 +
   1.228 +        // Clear the sample ANR
   1.229 +        final FileWriter anrClearer = new FileWriter(anrFile);
   1.230 +        anrClearer.close();
   1.231 +    }
   1.232 +}

mercurial