Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d689c784 authored by Maksymilian Osowski's avatar Maksymilian Osowski Committed by Android (Google) Code Review
Browse files

Merge "Added crash-detection mechanism."

parents 23fdaf6f 23a0ee47
Loading
Loading
Loading
Loading
+85 −0
Original line number Diff line number Diff line
/*
 * 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 */
    }
}
 No newline at end of file
+28 −1
Original line number Diff line number Diff line
@@ -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);
+69 −1
Original line number Diff line number Diff line
@@ -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);
    }
+51 −4
Original line number Diff line number Diff line
@@ -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);

        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);
    }
}
 No newline at end of file