Merge "Added crash-detection mechanism."
This commit is contained in:
committed by
Android (Google) Code Review
commit
d689c784af
@@ -0,0 +1,85 @@
|
||||
/*
|
||||
* Copyright (C) 2010 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.dumprendertree2;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Message;
|
||||
import android.webkit.WebView;
|
||||
|
||||
/**
|
||||
* A dummy class representing test that crashed.
|
||||
*/
|
||||
public class CrashedDummyResult extends AbstractResult {
|
||||
String mRelativePath;
|
||||
|
||||
public CrashedDummyResult(String relativePath) {
|
||||
mRelativePath = relativePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] getActualImageResult() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getActualTextResult() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Bundle getBundle() {
|
||||
/** TODO: */
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDiffAsHtml() {
|
||||
/** TODO: Probably show at least expected results */
|
||||
return "Ooops, I crashed...";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getRelativePath() {
|
||||
return mRelativePath;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResultCode getResultCode() {
|
||||
return ResultCode.FAIL_CRASHED;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TestType getType() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void obtainActualResults(WebView webview, Message resultObtainedMsg) {
|
||||
/** This method is not applicable for this type of result */
|
||||
assert false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpectedImageResult(byte[] expectedResult) {
|
||||
/** TODO */
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setExpectedTextResult(String expectedResult) {
|
||||
/** TODO */
|
||||
}
|
||||
}
|
||||
@@ -82,6 +82,7 @@ public class LayoutTestsExecutor extends Activity {
|
||||
|
||||
private static final int DEFAULT_TIME_OUT_MS = 15 * 1000;
|
||||
|
||||
/** A list of tests that remain to run since last crash */
|
||||
private List<String> mTestsList;
|
||||
|
||||
/**
|
||||
@@ -91,6 +92,7 @@ public class LayoutTestsExecutor extends Activity {
|
||||
*/
|
||||
private int mCurrentTestIndex;
|
||||
|
||||
/** The total number of tests to run, doesn't reset after crash */
|
||||
private int mTotalTestCount;
|
||||
|
||||
private WebView mCurrentWebView;
|
||||
@@ -119,7 +121,7 @@ public class LayoutTestsExecutor extends Activity {
|
||||
@Override
|
||||
public void onServiceConnected(ComponentName name, IBinder service) {
|
||||
mManagerServiceMessenger = new Messenger(service);
|
||||
runNextTest();
|
||||
startTests();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -303,6 +305,26 @@ public class LayoutTestsExecutor extends Activity {
|
||||
webViewSettings.setXSSAuditorEnabled(false);
|
||||
}
|
||||
|
||||
private void startTests() {
|
||||
try {
|
||||
Message serviceMsg =
|
||||
Message.obtain(null, ManagerService.MSG_FIRST_TEST);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
if (!mTestsList.isEmpty()) {
|
||||
bundle.putString("firstTest", mTestsList.get(0));
|
||||
bundle.putInt("index", mCurrentTestIndex);
|
||||
}
|
||||
|
||||
serviceMsg.setData(bundle);
|
||||
mManagerServiceMessenger.send(serviceMsg);
|
||||
} catch (RemoteException e) {
|
||||
Log.e(LOG_TAG + "::startTests", e.getMessage());
|
||||
}
|
||||
|
||||
runNextTest();
|
||||
}
|
||||
|
||||
private void runNextTest() {
|
||||
assert mCurrentState == CurrentState.IDLE : "mCurrentState = " + mCurrentState.name();
|
||||
|
||||
@@ -312,6 +334,8 @@ public class LayoutTestsExecutor extends Activity {
|
||||
}
|
||||
|
||||
mCurrentTestRelativePath = mTestsList.remove(0);
|
||||
Log.d(LOG_TAG + "::runNextTest", "Start: " + mCurrentTestRelativePath +
|
||||
"(" + mCurrentTestIndex + ")");
|
||||
mCurrentTestUri =
|
||||
Uri.fromFile(new File(TESTS_ROOT_DIR_PATH, mCurrentTestRelativePath)).toString();
|
||||
|
||||
@@ -386,6 +410,9 @@ public class LayoutTestsExecutor extends Activity {
|
||||
if (mCurrentTestTimedOut) {
|
||||
bundle.putString("resultCode", AbstractResult.ResultCode.FAIL_TIMED_OUT.name());
|
||||
}
|
||||
if (!mTestsList.isEmpty()) {
|
||||
bundle.putString("nextTest", mTestsList.get(0));
|
||||
}
|
||||
|
||||
serviceMsg.setData(bundle);
|
||||
mManagerServiceMessenger.send(serviceMsg);
|
||||
|
||||
@@ -38,6 +38,10 @@ public class ManagerService extends Service {
|
||||
|
||||
private static final String LOG_TAG = "ManagerService";
|
||||
|
||||
private static final int MSG_TEST_CRASHED = 0;
|
||||
|
||||
private static final int CRASH_TIMEOUT_MS = 20 * 1000;
|
||||
|
||||
/** TODO: make it a setting */
|
||||
static final String TESTS_ROOT_DIR_PATH =
|
||||
Environment.getExternalStorageDirectory() +
|
||||
@@ -67,11 +71,21 @@ public class ManagerService extends Service {
|
||||
|
||||
static final int MSG_PROCESS_ACTUAL_RESULTS = 0;
|
||||
static final int MSG_ALL_TESTS_FINISHED = 1;
|
||||
static final int MSG_FIRST_TEST = 2;
|
||||
|
||||
/**
|
||||
* This handler is purely for IPC. It is used to create mMessenger
|
||||
* that generates a binder returned in onBind method.
|
||||
*/
|
||||
private Handler mIncomingHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_FIRST_TEST:
|
||||
Bundle bundle = msg.getData();
|
||||
ensureNextTestSetup(bundle.getString("firstTest"), bundle.getInt("index"));
|
||||
break;
|
||||
|
||||
case MSG_PROCESS_ACTUAL_RESULTS:
|
||||
Log.d(LOG_TAG + ".mIncomingHandler", msg.getData().getString("relativePath"));
|
||||
onActualResultsObtained(msg.getData());
|
||||
@@ -86,9 +100,21 @@ public class ManagerService extends Service {
|
||||
|
||||
private Messenger mMessenger = new Messenger(mIncomingHandler);
|
||||
|
||||
private Handler mCrashMessagesHandler = new Handler() {
|
||||
@Override
|
||||
public void handleMessage(Message msg) {
|
||||
if (msg.what == MSG_TEST_CRASHED) {
|
||||
onTestCrashed();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private FileFilter mFileFilter;
|
||||
private Summarizer mSummarizer;
|
||||
|
||||
private String mCurrentlyRunningTest;
|
||||
private int mCurrentlyRunningTestIndex;
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
@@ -97,14 +123,55 @@ public class ManagerService extends Service {
|
||||
mSummarizer = new Summarizer(mFileFilter, RESULTS_ROOT_DIR_PATH);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int onStartCommand(Intent intent, int flags, int startId) {
|
||||
return START_STICKY;
|
||||
}
|
||||
|
||||
@Override
|
||||
public IBinder onBind(Intent intent) {
|
||||
return mMessenger.getBinder();
|
||||
}
|
||||
|
||||
private void onActualResultsObtained(Bundle bundle) {
|
||||
mCrashMessagesHandler.removeMessages(MSG_TEST_CRASHED);
|
||||
ensureNextTestSetup(bundle.getString("nextTest"), bundle.getInt("testIndex") + 1);
|
||||
|
||||
AbstractResult results =
|
||||
AbstractResult.TestType.valueOf(bundle.getString("type")).createResult(bundle);
|
||||
|
||||
handleResults(results);
|
||||
}
|
||||
|
||||
private void ensureNextTestSetup(String nextTest, int index) {
|
||||
if (nextTest == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
mCurrentlyRunningTest = nextTest;
|
||||
mCurrentlyRunningTestIndex = index;
|
||||
mCrashMessagesHandler.sendEmptyMessageDelayed(MSG_TEST_CRASHED, CRASH_TIMEOUT_MS);
|
||||
}
|
||||
|
||||
/**
|
||||
* This sends an intent to TestsListActivity to restart LayoutTestsExecutor.
|
||||
* The more detailed description of the flow is in the comment of onNewIntent
|
||||
* method in TestsListActivity.
|
||||
*/
|
||||
private void onTestCrashed() {
|
||||
handleResults(new CrashedDummyResult(mCurrentlyRunningTest));
|
||||
|
||||
Log.w(LOG_TAG + "::onTestCrashed", mCurrentlyRunningTest +
|
||||
"(" + mCurrentlyRunningTestIndex + ")");
|
||||
|
||||
Intent intent = new Intent(this, TestsListActivity.class);
|
||||
intent.setAction(Intent.ACTION_REBOOT);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
|
||||
intent.putExtra("crashedTestIndex", mCurrentlyRunningTestIndex);
|
||||
startActivity(intent);
|
||||
}
|
||||
|
||||
private void handleResults(AbstractResult results) {
|
||||
String relativePath = results.getRelativePath();
|
||||
results.setExpectedTextResult(getExpectedTextResult(relativePath));
|
||||
results.setExpectedImageResult(getExpectedImageResult(relativePath));
|
||||
@@ -134,7 +201,8 @@ public class ManagerService extends Service {
|
||||
return;
|
||||
}
|
||||
|
||||
String resultPath = FileFilter.setPathEnding(testPath, "-actual." + IMAGE_RESULT_EXTENSION);
|
||||
String resultPath = FileFilter.setPathEnding(testPath,
|
||||
"-actual." + IMAGE_RESULT_EXTENSION);
|
||||
FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
|
||||
actualImageResult, false);
|
||||
}
|
||||
|
||||
@@ -29,7 +29,7 @@ import java.util.ArrayList;
|
||||
/**
|
||||
* An Activity that generates a list of tests and sends the intent to
|
||||
* LayoutTestsExecuter to run them. It also restarts the LayoutTestsExecuter
|
||||
* after it crashes (TODO).
|
||||
* after it crashes.
|
||||
*/
|
||||
public class TestsListActivity extends Activity {
|
||||
|
||||
@@ -79,9 +79,49 @@ public class TestsListActivity extends Activity {
|
||||
sProgressDialog.show();
|
||||
Message doneMsg = Message.obtain(mHandler, MSG_TEST_LIST_PRELOADER_DONE);
|
||||
|
||||
Intent serviceIntent = new Intent(this, ManagerService.class);
|
||||
startService(serviceIntent);
|
||||
|
||||
new TestsListPreloaderThread(path, doneMsg).start();
|
||||
}
|
||||
|
||||
/**
|
||||
* This method handles an intent that comes from ManageService when crash is detected.
|
||||
* The intent contains an index in mTestsList of the test that crashed. TestsListActivity
|
||||
* restarts the LayoutTestsExecutor from the following test in mTestsList, by sending
|
||||
* an intent to it. This new intent contains a list of remaining tests to run,
|
||||
* total count of all tests, and the index of the first test to run after restarting.
|
||||
* LayoutTestExecutor runs then as usual, sending reports to ManagerService. If it
|
||||
* detects the crash it sends a new intent and the flow repeats.
|
||||
*/
|
||||
@Override
|
||||
protected void onNewIntent(Intent intent) {
|
||||
if (!intent.getAction().equals(Intent.ACTION_REBOOT)) {
|
||||
return;
|
||||
}
|
||||
|
||||
int nextTestToRun = intent.getIntExtra("crashedTestIndex", -1) + 1;
|
||||
if (nextTestToRun > 0 && nextTestToRun <= mTotalTestCount) {
|
||||
restartExecutor(nextTestToRun);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
outState.putStringArrayList("testsList", mTestsList);
|
||||
outState.putInt("totalCount", mTotalTestCount);
|
||||
|
||||
super.onSaveInstanceState(outState);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
|
||||
mTestsList = savedInstanceState.getStringArrayList("testsList");
|
||||
mTotalTestCount = savedInstanceState.getInt("totalCount");
|
||||
}
|
||||
|
||||
/**
|
||||
* (Re)starts the executer activity from the given test number (inclusive, 0-based).
|
||||
* This number is an index in mTestsList, not the sublist passed in the intent.
|
||||
@@ -93,9 +133,16 @@ public class TestsListActivity extends Activity {
|
||||
Intent intent = new Intent();
|
||||
intent.setClass(this, LayoutTestsExecutor.class);
|
||||
intent.setAction(Intent.ACTION_RUN);
|
||||
intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST,
|
||||
new ArrayList<String>(mTestsList.subList(startFrom, mTotalTestCount)));
|
||||
intent.putExtra(LayoutTestsExecutor.EXTRA_TEST_INDEX, startFrom);
|
||||
|
||||
if (startFrom < mTotalTestCount) {
|
||||
intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST,
|
||||
new ArrayList<String>(mTestsList.subList(startFrom, mTotalTestCount)));
|
||||
intent.putExtra(LayoutTestsExecutor.EXTRA_TEST_INDEX, startFrom);
|
||||
} else {
|
||||
intent.putStringArrayListExtra(LayoutTestsExecutor.EXTRA_TESTS_LIST,
|
||||
new ArrayList<String>());
|
||||
}
|
||||
|
||||
startActivity(intent);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user