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