mobile/android/base/tests/testANRReporter.java

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 package org.mozilla.gecko.tests;
     3 import org.mozilla.gecko.AppConstants;
     5 import android.content.Context;
     6 import android.content.Intent;
     8 import com.jayway.android.robotium.solo.Condition;
    10 import java.io.File;
    11 import java.io.FileReader;
    12 import java.io.FileWriter;
    14 import org.json.JSONObject;
    16 /**
    17  * Tests the proper operation of the ANR reporter.
    18  */
    19 public class testANRReporter extends BaseTest {
    21     private static final String ANR_ACTION = "android.intent.action.ANR";
    22     private static final String PING_DIR = "saved-telemetry-pings";
    23     private static final int WAIT_FOR_PING_TIMEOUT = 10000;
    24     private static final String ANR_PATH = "/data/anr/traces.txt";
    25     private static final String SAMPLE_ANR
    26         = "----- pid 1 at 2014-01-15 18:55:51 -----\n"
    27         + "Cmd line: " + AppConstants.ANDROID_PACKAGE_NAME + "\n"
    28         + "\n"
    29         + "JNI: CheckJNI is off; workarounds are off; pins=0; globals=397\n"
    30         + "\n"
    31         + "DALVIK THREADS:\n"
    32         + "(mutexes: tll=0 tsl=0 tscl=0 ghl=0)\n"
    33         + "\n"
    34         + "\"main\" prio=5 tid=1 WAIT\n"
    35         + "  | group=\"main\" sCount=1 dsCount=0 obj=0x41d6bc90 self=0x41d5a3c8\n"
    36         + "  | sysTid=3485 nice=0 sched=0/0 cgrp=apps handle=1074852180\n"
    37         + "  | state=S schedstat=( 0 0 0 ) utm=1065 stm=152 core=0\n"
    38         + "  at java.lang.Object.wait(Native Method)\n"
    39         + "  - waiting on <0x427ab340> (a org.mozilla.gecko.GeckoEditable$5)\n"
    40         + "  at java.lang.Object.wait(Object.java:364)\n"
    41         + "  at org.mozilla.gecko.GeckoEditable$5.run(GeckoEditable.java:746)\n"
    42         + "  at android.os.Handler.handleCallback(Handler.java:733)\n"
    43         + "  at android.os.Handler.dispatchMessage(Handler.java:95)\n"
    44         + "  at android.os.Looper.loop(Looper.java:137)\n"
    45         + "  at android.app.ActivityThread.main(ActivityThread.java:4998)\n"
    46         + "  at java.lang.reflect.Method.invokeNative(Native Method)\n"
    47         + "  at java.lang.reflect.Method.invoke(Method.java:515)\n"
    48         + "  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)\n"
    49         + "  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)\n"
    50         + "  at dalvik.system.NativeStart.main(Native Method)\n"
    51         + "\n"
    52         + "\"Gecko\" prio=5 tid=16 SUSPENDED\n"
    53         + "  | group=\"main\" sCount=1 dsCount=0 obj=0x426e2b28 self=0x76ae92e8\n"
    54         + "  | sysTid=3541 nice=0 sched=0/0 cgrp=apps handle=1991153472\n"
    55         + "  | state=S schedstat=( 0 0 0 ) utm=1118 stm=145 core=0\n"
    56         + "  #00  pc 00000904  /system/lib/libc.so (__futex_syscall3+4294832136)\n"
    57         + "  #01  pc 0000eec4  /system/lib/libc.so (__pthread_cond_timedwait_relative+48)\n"
    58         + "  #02  pc 0000ef24  /system/lib/libc.so (__pthread_cond_timedwait+64)\n"
    59         + "  #03  pc 000536b7  /system/lib/libdvm.so\n"
    60         + "  #04  pc 00053c79  /system/lib/libdvm.so (dvmChangeStatus(Thread*, ThreadStatus)+34)\n"
    61         + "  #05  pc 00049507  /system/lib/libdvm.so\n"
    62         + "  #06  pc 0004d84b  /system/lib/libdvm.so\n"
    63         + "  #07  pc 0003f1df  /dev/ashmem/libxul.so (deleted)\n"
    64         + "  at org.mozilla.gecko.mozglue.GeckoLoader.nativeRun(Native Method)\n"
    65         + "  at org.mozilla.gecko.GeckoAppShell.runGecko(GeckoAppShell.java:384)\n"
    66         + "  at org.mozilla.gecko.GeckoThread.run(GeckoThread.java:177)\n"
    67         + "\n"
    68         + "----- end 1 -----\n"
    69         + "\n"
    70         + "\n"
    71         + "----- pid 2 at 2013-01-25 13:27:01 -----\n"
    72         + "Cmd line: system_server\n"
    73         + "\n"
    74         + "----- end 2 -----\n";
    76     private boolean mDone;
    78     private JSONObject readPingFile(final File pingFile) throws Exception {
    79         final long fileSize = pingFile.length();
    80         if (fileSize == 0 || fileSize > Integer.MAX_VALUE) {
    81             throw new Exception("Invalid ping file size");
    82         }
    83         final char[] buffer = new char[(int) fileSize];
    84         final FileReader reader = new FileReader(pingFile);
    85         try {
    86             final int readSize = reader.read(buffer);
    87             if (readSize == 0 || readSize > buffer.length) {
    88                 throw new Exception("Invalid number of bytes read");
    89             }
    90         } finally {
    91             reader.close();
    92         }
    93         return new JSONObject(new String(buffer));
    94     }
    96     public void testANRReporter() throws Exception {
    97         blockForGeckoReady();
    99         // Cannot test ANR reporter if it's disabled.
   100         if (!AppConstants.MOZ_ANDROID_ANR_REPORTER) {
   101             mAsserter.ok(true, "ANR reporter is disabled", null);
   102             return;
   103         }
   105         // For the ANR reporter to work, we need to provide sample ANR traces to it.
   106         // Therefore, we need the ANR file to exist and writable. If not, we don't
   107         // have the right permissions to create the file, so we just bail.
   108         final File anrFile = new File(ANR_PATH);
   109         if (!anrFile.exists()) {
   110             mAsserter.ok(true, "ANR file does not exist", null);
   111             return;
   112         }
   113         if (!anrFile.canWrite()) {
   114             mAsserter.ok(true, "ANR file is not writable", null);
   115             return;
   116         }
   118         final FileWriter anrWriter = new FileWriter(anrFile);
   119         try {
   120             anrWriter.write(SAMPLE_ANR);
   121         } finally {
   122             anrWriter.close();
   123         }
   125         // Block the UI thread to simulate an ANR
   126         final Runnable uiBlocker = new Runnable() {
   127             @Override
   128             public synchronized void run() {
   129                 while (!mDone) {
   130                     try {
   131                         wait();
   132                     } catch (final InterruptedException e) {
   133                     }
   134                 }
   135             }
   136         };
   137         getActivity().runOnUiThread(uiBlocker);
   139         // Make sure our initial ping directory is empty.
   140         final File pingDir = new File(mProfile, PING_DIR);
   141         final String[] initialFiles = pingDir.list();
   142         mAsserter.ok(initialFiles == null || initialFiles.length == 0,
   143                      "Ping directory is empty", null);
   145         final Intent anrIntent = new Intent(ANR_ACTION);
   146         anrIntent.setPackage(AppConstants.ANDROID_PACKAGE_NAME);
   147         mAsserter.is(anrIntent.getPackage(), AppConstants.ANDROID_PACKAGE_NAME,
   148                      "Successfully set package name");
   150         final Context testContext = getInstrumentation().getContext();
   151         mAsserter.isnot(testContext, null, "testContext should not be null");
   153         // Trigger the ANR.
   154         mAsserter.info("Triggering ANR", null);
   155         testContext.sendBroadcast(anrIntent);
   157         // ANR reporter is supposed to ignore duplicate ANRs.
   158         // This will be checked later when we look for ping files.
   159         mAsserter.info("Triggering second ANR", null);
   160         testContext.sendBroadcast(new Intent(anrIntent));
   162         mAsserter.info("Waiting for ping", null);
   163         waitForCondition(new Condition() {
   164             @Override
   165             public boolean isSatisfied() {
   166                 final File[] newFiles = pingDir.listFiles();
   167                 if (newFiles == null || newFiles.length == 0) {
   168                     // Keep waiting.
   169                     return false;
   170                 }
   171                 // Make sure we have a complete file. We skip assertions and catch all
   172                 // exceptions here because the condition may not be satisfied now but may
   173                 // be satisfied later. After the wait is over, we will repeat the same
   174                 // steps with assertions and exceptions.
   175                 try {
   176                     return readPingFile(newFiles[0]).has("slug");
   177                 } catch (final Exception e) {
   178                     return false;
   179                 }
   180             }
   181         }, WAIT_FOR_PING_TIMEOUT);
   183         mAsserter.ok(pingDir.exists(), "Ping directory exists", null);
   184         mAsserter.ok(pingDir.isDirectory(), "Ping directory is a directory", null);
   186         final File[] newFiles = pingDir.listFiles();
   187         mAsserter.isnot(newFiles, null, "Ping directory is not empty");
   188         mAsserter.is(newFiles.length, 1, "ANR reporter wrote one ping");
   189         mAsserter.ok(newFiles[0].exists(), "Ping exists", null);
   190         mAsserter.ok(newFiles[0].isFile(), "Ping is a file", null);
   191         mAsserter.ok(newFiles[0].canRead(), "Ping is readable", null);
   192         mAsserter.info("Found ping file", newFiles[0].getPath());
   194         // Check standard properties required by Telemetry server.
   195         final JSONObject pingObject = readPingFile(newFiles[0]);
   196         mAsserter.ok(pingObject.has("slug"), "Ping has slug property", null);
   197         mAsserter.ok(pingObject.has("reason"), "Ping has reason property", null);
   198         mAsserter.ok(pingObject.has("payload"), "Ping has payload property", null);
   200         final JSONObject pingPayload = pingObject.getJSONObject("payload");
   201         mAsserter.ok(pingPayload.has("ver"), "Payload has ver property", null);
   202         mAsserter.ok(pingPayload.has("info"), "Payload has info property", null);
   203         mAsserter.ok(pingPayload.has("androidANR"), "Payload has androidANR property", null);
   205         final JSONObject pingInfo = pingPayload.getJSONObject("info");
   206         mAsserter.ok(pingInfo.has("reason"), "Info has reason property", null);
   207         mAsserter.ok(pingInfo.has("appName"), "Info has appName property", null);
   208         mAsserter.ok(pingInfo.has("appUpdateChannel"), "Info has appUpdateChannel property", null);
   209         mAsserter.ok(pingInfo.has("appVersion"), "Info has appVersion property", null);
   210         mAsserter.ok(pingInfo.has("appBuildID"), "Info has appBuildID property", null);
   212         // Do some profile clean up. This is not absolutely necessary because the profile
   213         // is blown away after test runs anyways, so we don't check return values here.
   214         for (final File ping : newFiles) {
   215             ping.delete();
   216         }
   217         pingDir.delete();
   219         // Unblock UI thread
   220         synchronized (uiBlocker) {
   221             mDone = true;
   222             uiBlocker.notify();
   223         }
   225         // Clear the sample ANR
   226         final FileWriter anrClearer = new FileWriter(anrFile);
   227         anrClearer.close();
   228     }
   229 }

mercurial