Bug 999071 - Add test for ANR reporter; r=blassey
☠☠ backed out by d83cad7c5853 ☠ ☠
authorJim Chen <nchen@mozilla.com>
Thu, 24 Apr 2014 17:49:02 -0400
changeset 198581 ddf970be0abd398b2c6bc0e5a2c7152e387fbbd6
parent 198580 8d2738b2227522649c54859249c23561051e07b0
child 198582 99b775c6d2bcd9713596677dfd152b66926fcd4b
push id3624
push userasasaki@mozilla.com
push dateMon, 09 Jun 2014 21:49:01 +0000
treeherdermozilla-beta@b1a5da15899a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersblassey
bugs999071
milestone31.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 999071 - Add test for ANR reporter; r=blassey
mobile/android/base/tests/robocop.ini
mobile/android/base/tests/testANRReporter.java
--- a/mobile/android/base/tests/robocop.ini
+++ b/mobile/android/base/tests/robocop.ini
@@ -5,16 +5,17 @@ skip-if = android_version == "10"
 [testAddonManager]
 # disabled on x86 only; bug 936216
 skip-if = processor == "x86"
 [testAddSearchEngine]
 # disabled on Android 2.3; bug 979552
 skip-if = android_version == "10"
 [testAdobeFlash]
 skip-if = processor == "x86"
+[testANRReporter]
 [testAwesomebar]
 [testAxisLocking]
 # disabled on x86 only; bug 927476
 skip-if = processor == "x86"
 # [testBookmark] # see bug 915350
 [testBookmarksPanel]
 # disabled on 2.3; bug 979615
 skip-if = android_version == "10"
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/tests/testANRReporter.java
@@ -0,0 +1,207 @@
+package org.mozilla.gecko.tests;
+
+import org.mozilla.gecko.AppConstants;
+
+import android.content.Context;
+import android.content.Intent;
+
+import com.jayway.android.robotium.solo.Condition;
+
+import java.io.File;
+import java.io.FileReader;
+import java.io.FileWriter;
+
+import org.json.JSONObject;
+
+/**
+ * Tests the proper operation of the ANR reporter.
+ */
+public class testANRReporter extends BaseTest {
+
+    private static final String ANR_ACTION = "android.intent.action.ANR";
+    private static final String PING_DIR = "saved-telemetry-pings";
+    private static final int WAIT_FOR_PING_TIMEOUT = 10000;
+    private static final String ANR_PATH = "/data/anr/traces.txt";
+    private static final String SAMPLE_ANR
+        = "----- pid 1 at 2014-01-15 18:55:51 -----\n"
+        + "Cmd line: " + AppConstants.ANDROID_PACKAGE_NAME + "\n"
+        + "\n"
+        + "JNI: CheckJNI is off; workarounds are off; pins=0; globals=397\n"
+        + "\n"
+        + "DALVIK THREADS:\n"
+        + "(mutexes: tll=0 tsl=0 tscl=0 ghl=0)\n"
+        + "\n"
+        + "\"main\" prio=5 tid=1 WAIT\n"
+        + "  | group=\"main\" sCount=1 dsCount=0 obj=0x41d6bc90 self=0x41d5a3c8\n"
+        + "  | sysTid=3485 nice=0 sched=0/0 cgrp=apps handle=1074852180\n"
+        + "  | state=S schedstat=( 0 0 0 ) utm=1065 stm=152 core=0\n"
+        + "  at java.lang.Object.wait(Native Method)\n"
+        + "  - waiting on <0x427ab340> (a org.mozilla.gecko.GeckoEditable$5)\n"
+        + "  at java.lang.Object.wait(Object.java:364)\n"
+        + "  at org.mozilla.gecko.GeckoEditable$5.run(GeckoEditable.java:746)\n"
+        + "  at android.os.Handler.handleCallback(Handler.java:733)\n"
+        + "  at android.os.Handler.dispatchMessage(Handler.java:95)\n"
+        + "  at android.os.Looper.loop(Looper.java:137)\n"
+        + "  at android.app.ActivityThread.main(ActivityThread.java:4998)\n"
+        + "  at java.lang.reflect.Method.invokeNative(Native Method)\n"
+        + "  at java.lang.reflect.Method.invoke(Method.java:515)\n"
+        + "  at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:777)\n"
+        + "  at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:593)\n"
+        + "  at dalvik.system.NativeStart.main(Native Method)\n"
+        + "\n"
+        + "\"Gecko\" prio=5 tid=16 SUSPENDED\n"
+        + "  | group=\"main\" sCount=1 dsCount=0 obj=0x426e2b28 self=0x76ae92e8\n"
+        + "  | sysTid=3541 nice=0 sched=0/0 cgrp=apps handle=1991153472\n"
+        + "  | state=S schedstat=( 0 0 0 ) utm=1118 stm=145 core=0\n"
+        + "  #00  pc 00000904  /system/lib/libc.so (__futex_syscall3+4294832136)\n"
+        + "  #01  pc 0000eec4  /system/lib/libc.so (__pthread_cond_timedwait_relative+48)\n"
+        + "  #02  pc 0000ef24  /system/lib/libc.so (__pthread_cond_timedwait+64)\n"
+        + "  #03  pc 000536b7  /system/lib/libdvm.so\n"
+        + "  #04  pc 00053c79  /system/lib/libdvm.so (dvmChangeStatus(Thread*, ThreadStatus)+34)\n"
+        + "  #05  pc 00049507  /system/lib/libdvm.so\n"
+        + "  #06  pc 0004d84b  /system/lib/libdvm.so\n"
+        + "  #07  pc 0003f1df  /dev/ashmem/libxul.so (deleted)\n"
+        + "  at org.mozilla.gecko.mozglue.GeckoLoader.nativeRun(Native Method)\n"
+        + "  at org.mozilla.gecko.GeckoAppShell.runGecko(GeckoAppShell.java:384)\n"
+        + "  at org.mozilla.gecko.GeckoThread.run(GeckoThread.java:177)\n"
+        + "\n"
+        + "----- end 1 -----\n"
+        + "\n"
+        + "\n"
+        + "----- pid 2 at 2013-01-25 13:27:01 -----\n"
+        + "Cmd line: system_server\n"
+        + "\n"
+        + "----- end 2 -----\n";
+
+    private boolean mDone;
+
+    public void testANRReporter() throws Exception {
+        blockForGeckoReady();
+
+        // Cannot test ANR reporter if it's disabled.
+        if (!AppConstants.MOZ_ANDROID_ANR_REPORTER) {
+            mAsserter.ok(true, "ANR reporter is disabled", null);
+            return;
+        }
+
+        // For the ANR reporter to work, we need to provide sample ANR traces to it.
+        // Therefore, we need the ANR file to exist and writable. If not, we don't
+        // have the right permissions to create the file, so we just bail.
+        final File anrFile = new File(ANR_PATH);
+        if (!anrFile.exists()) {
+            mAsserter.ok(true, "ANR file does not exist", null);
+            return;
+        }
+        if (!anrFile.canWrite()) {
+            mAsserter.ok(true, "ANR file is not writable", null);
+            return;
+        }
+
+        final FileWriter anrWriter = new FileWriter(anrFile);
+        try {
+            anrWriter.write(SAMPLE_ANR);
+        } finally {
+            anrWriter.close();
+        }
+
+        // Block the UI thread to simulate an ANR
+        final Runnable uiBlocker = new Runnable() {
+            @Override
+            public synchronized void run() {
+                while (!mDone) {
+                    try {
+                        wait();
+                    } catch (final InterruptedException e) {
+                    }
+                }
+            }
+        };
+        getActivity().runOnUiThread(uiBlocker);
+
+        // Make sure our initial ping directory is empty.
+        final File pingDir = new File(mProfile, PING_DIR);
+        final String[] initialFiles = pingDir.list();
+        mAsserter.ok(initialFiles == null || initialFiles.length == 0,
+                     "Ping directory is empty", null);
+
+        final Intent anrIntent = new Intent(ANR_ACTION);
+        anrIntent.setPackage(AppConstants.ANDROID_PACKAGE_NAME);
+        mAsserter.is(anrIntent.getPackage(), AppConstants.ANDROID_PACKAGE_NAME,
+                     "Successfully set package name");
+
+        final Context testContext = getInstrumentation().getContext();
+        mAsserter.isnot(testContext, null, "testContext should not be null");
+
+        // Trigger the ANR.
+        mAsserter.info("Triggering ANR", null);
+        testContext.sendBroadcast(anrIntent);
+
+        // ANR reporter is supposed to ignore duplicate ANRs.
+        // This will be checked later when we look for ping files.
+        mAsserter.info("Triggering second ANR", null);
+        testContext.sendBroadcast(new Intent(anrIntent));
+
+        mAsserter.info("Waiting for ping", null);
+        waitForCondition(new Condition() {
+            @Override
+            public boolean isSatisfied() {
+                final String[] newFiles = pingDir.list();
+                return newFiles != null && newFiles.length > 0;
+            }
+        }, WAIT_FOR_PING_TIMEOUT);
+
+        mAsserter.ok(pingDir.exists(), "Ping directory exists", null);
+        mAsserter.ok(pingDir.isDirectory(), "Ping directory is a directory", null);
+
+        final File[] newFiles = pingDir.listFiles();
+        mAsserter.isnot(newFiles, null, "Ping directory is not empty");
+        mAsserter.is(newFiles.length, 1, "ANR reporter wrote one ping");
+        mAsserter.ok(newFiles[0].exists(), "Ping exists", null);
+        mAsserter.ok(newFiles[0].isFile(), "Ping is a file", null);
+        mAsserter.ok(newFiles[0].canRead(), "Ping is readable", null);
+        mAsserter.info("Found ping file", newFiles[0].getPath());
+
+        final char[] buffer = new char[(int) newFiles[0].length()];
+        final FileReader reader = new FileReader(newFiles[0]);
+        try {
+            mAsserter.ok(reader.read(buffer) > 0, "Read from ping file", null);
+        } finally {
+            reader.close();
+        }
+
+        // Check standard properties required by Telemetry server.
+        final JSONObject pingObject = new JSONObject(new String(buffer));
+        mAsserter.ok(pingObject.has("slug"), "Ping has slug property", null);
+        mAsserter.ok(pingObject.has("reason"), "Ping has reason property", null);
+        mAsserter.ok(pingObject.has("payload"), "Ping has payload property", null);
+
+        final JSONObject pingPayload = pingObject.getJSONObject("payload");
+        mAsserter.ok(pingPayload.has("ver"), "Payload has ver property", null);
+        mAsserter.ok(pingPayload.has("info"), "Payload has info property", null);
+        mAsserter.ok(pingPayload.has("androidANR"), "Payload has androidANR property", null);
+
+        final JSONObject pingInfo = pingPayload.getJSONObject("info");
+        mAsserter.ok(pingInfo.has("reason"), "Info has reason property", null);
+        mAsserter.ok(pingInfo.has("appName"), "Info has appName property", null);
+        mAsserter.ok(pingInfo.has("appUpdateChannel"), "Info has appUpdateChannel property", null);
+        mAsserter.ok(pingInfo.has("appVersion"), "Info has appVersion property", null);
+        mAsserter.ok(pingInfo.has("appBuildID"), "Info has appBuildID property", null);
+
+        // Do some profile clean up. This is not absolutely necessary because the profile
+        // is blown away after test runs anyways, so we don't check return values here.
+        for (final File ping : newFiles) {
+            ping.delete();
+        }
+        pingDir.delete();
+
+        // Unblock UI thread
+        synchronized (uiBlocker) {
+            mDone = true;
+            uiBlocker.notify();
+        }
+
+        // Clear the sample ANR
+        final FileWriter anrClearer = new FileWriter(anrFile);
+        anrClearer.close();
+    }
+}