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

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

Merge "Moved practically all of the prerefactoring functionality to the new design."

parents 1a987b8e 5f0ccd76
Loading
Loading
Loading
Loading
+5 −5
Original line number Diff line number Diff line
@@ -25,13 +25,13 @@ limitations under the License.
            </intent-filter>
        </activity>

        <activity android:name=".LayoutTestsRunner"
                  android:label="Layout tests' runner">
        <activity android:name=".TestsListActivity"
                  android:label="Tests' list activity">
        </activity>

        <activity android:name=".LayoutTestsExecuter"
                  android:label="Layout tests' executer"
                  android:process=":executer">
        <activity android:name=".LayoutTestsExecutor"
                  android:label="Layout tests' executor"
                  android:process=":executor">
        </activity>

        <service android:name="ManagerService">
+17 −2
Original line number Diff line number Diff line
@@ -27,8 +27,21 @@ import android.webkit.WebView;
public abstract class AbstractResult {

    public enum TestType {
        TEXT,
        PIXEL
        TEXT {
            @Override
            public AbstractResult createResult(Bundle bundle) {
                return new TextResult(bundle);
            }
        },
        RENDER_TREE {
            @Override
            public AbstractResult createResult(Bundle bundle) {
                /** TODO: RenderTree tests are not yet supported */
                return null;
            }
        };

        public abstract AbstractResult createResult(Bundle bundle);
    }

    public enum ResultCode {
@@ -101,6 +114,8 @@ public abstract class AbstractResult {
     */
    public abstract TestType getType();

    public abstract String getRelativePath();

    /**
     * Returns a piece of HTML code that presents a visual diff between a result and
     * the expected result.
+0 −183
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.app.Activity;
import android.net.Uri;
import android.os.Handler;
import android.os.Message;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.webkit.WebStorage.QuotaUpdater;

import java.io.File;

/**
 * A class that represents a single layout test. It is responsible for running the test,
 * checking its result and creating an AbstractResult object.
 */
public class LayoutTest {

    private static final String LOG_TAG = "LayoutTest";

    public static final int MSG_ACTUAL_RESULT_OBTAINED = 0;

    private String mRelativePath;
    private String mTestsRootDirPath;
    private String mUrl;
    private boolean mOnTestFinishedCalled;
    private Message mTestFinishedMsg;
    private AbstractResult mResult;

    private WebView mWebView;
    private Activity mActivity;

    private final Handler mResultHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == MSG_ACTUAL_RESULT_OBTAINED) {
                mResult.setExpectedTextResult(LayoutTestsRunnerThread
                        .getExpectedTextResult(mRelativePath));
                mResult.setExpectedImageResult(LayoutTestsRunnerThread
                        .getExpectedImageResult(mRelativePath));
                mTestFinishedMsg.sendToTarget();
            }
        }
    };

    private WebViewClient mWebViewClient = new WebViewClient() {
        @Override
        public void onPageFinished(WebView view, String url) {
            /** Some tests fire up many page loads, we don't want to detect them */
            if (!url.equals(mUrl)) {
                return;
            }

            onTestFinished();
        }
    };

    private WebChromeClient mWebChromeClient = new WebChromeClient() {
        @Override
        public void onExceededDatabaseQuota(String url, String databaseIdentifier,
                long currentQuota, long estimatedSize, long totalUsedQuota,
                QuotaUpdater quotaUpdater) {
            /** TODO: This should be recorded as part of the text result */
            quotaUpdater.updateQuota(currentQuota + 5 * 1024 * 1024);
        }

        @Override
        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
            /** TODO: Alerts should be recorded as part of text result */
            result.confirm();
            return true;
        }

        @Override
        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
            /** TODO: Alerts should be recorded as part of text result */
            result.confirm();
            return true;
        }

        @Override
        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
                JsPromptResult result) {
            /** TODO: Alerts should be recorded as part of text result */
            result.confirm();
            return true;
        }

    };

    public LayoutTest(String relativePath, String testsRootDirPath, Message testFinishedMsg,
            LayoutTestsRunner activity) {
        mRelativePath = relativePath;
        mTestsRootDirPath = testsRootDirPath;
        mTestFinishedMsg = testFinishedMsg;
        mActivity = activity;
    }

    public LayoutTest(AbstractResult result, String relativePath) {
        mResult = result;
        mRelativePath = relativePath;
    }

    public void run() {
        mWebView = new WebView(mActivity);
        mActivity.setContentView(mWebView);

        setupWebView();

        /** TODO: Add timeout msg */
        mUrl = Uri.fromFile(new File(mTestsRootDirPath, mRelativePath)).toString();
        mWebView.loadUrl(mUrl);
    }

    private void onTestFinished() {
        if (mOnTestFinishedCalled) {
            return;
        }

        mOnTestFinishedCalled = true;

        /**
         * If the result has not been set by the time the test finishes we create
         * a default type of result.
         */
        if (mResult == null) {
            /** TODO: Default type should be RenderTreeResult. We don't support it now. */
            mResult = new TextResult(mRelativePath);
        }

        /** TODO: Implement waitUntilDone */

        mResult.obtainActualResults(mWebView,
                mResultHandler.obtainMessage(MSG_ACTUAL_RESULT_OBTAINED));
    }

    private void setupWebView() {
        WebSettings webViewSettings = mWebView.getSettings();
        webViewSettings.setAppCacheEnabled(true);
        webViewSettings.setAppCachePath(mActivity.getApplicationContext().getCacheDir().getPath());
        webViewSettings.setAppCacheMaxSize(Long.MAX_VALUE);
        webViewSettings.setJavaScriptEnabled(true);
        webViewSettings.setJavaScriptCanOpenWindowsAutomatically(true);
        webViewSettings.setSupportMultipleWindows(true);
        webViewSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
        webViewSettings.setDatabaseEnabled(true);
        webViewSettings.setDatabasePath(mActivity.getDir("databases", 0).getAbsolutePath());
        webViewSettings.setDomStorageEnabled(true);
        webViewSettings.setWorkersEnabled(false);
        webViewSettings.setXSSAuditorEnabled(false);

        mWebView.setWebViewClient(mWebViewClient);
        mWebView.setWebChromeClient(mWebChromeClient);
    }

    public AbstractResult getResult() {
        return mResult;
    }

    public String getRelativePath() {
        return mRelativePath;
    }
}
 No newline at end of file
+34 −5
Original line number Diff line number Diff line
@@ -30,6 +30,7 @@ import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.util.Log;
import android.view.Window;
import android.webkit.JsPromptResult;
import android.webkit.JsResult;
import android.webkit.WebChromeClient;
@@ -47,7 +48,7 @@ import java.util.List;
 * to ManagerService. The reason why is to handle crashing (test that crashes brings down
 * whole process with it).
 */
public class LayoutTestsExecuter extends Activity {
public class LayoutTestsExecutor extends Activity {

    /** TODO: make it a setting */
    static final String TESTS_ROOT_DIR_PATH =
@@ -55,14 +56,23 @@ public class LayoutTestsExecuter extends Activity {
            File.separator + "android" +
            File.separator + "LayoutTests";

    private static final String LOG_TAG = "LayoutTestExecuter";
    private static final String LOG_TAG = "LayoutTestExecutor";

    public static final String EXTRA_TESTS_LIST = "TestsList";
    public static final String EXTRA_TEST_INDEX = "TestIndex";

    private static final int MSG_ACTUAL_RESULT_OBTAINED = 0;

    private List<String> mTestsList;
    private int mCurrentTestCount = 0;

    /**
     * This is a number of currently running test. It is 0-based and doesn't reset after
     * the crash. Initial index is passed to LayoutTestsExecuter in the intent that starts
     * it.
     */
    private int mCurrentTestIndex;

    private int mTotalTestCount;

    private WebView mCurrentWebView;
    private String mCurrentTestRelativePath;
@@ -94,6 +104,8 @@ public class LayoutTestsExecuter extends Activity {
        public void handleMessage(Message msg) {
            if (msg.what == MSG_ACTUAL_RESULT_OBTAINED) {
                reportResultToService();
                mCurrentTestIndex++;
                updateProgressBar();
                runNextTest();
            }
        }
@@ -153,8 +165,12 @@ public class LayoutTestsExecuter extends Activity {
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_PROGRESS);

        Intent intent = getIntent();
        mTestsList = intent.getStringArrayListExtra(EXTRA_TESTS_LIST);
        mCurrentTestIndex = intent.getIntExtra(EXTRA_TEST_INDEX, -1);
        mTotalTestCount = mCurrentTestIndex + mTestsList.size();

        bindService(new Intent(this, ManagerService.class), mServiceConnection,
                Context.BIND_AUTO_CREATE);
@@ -191,7 +207,6 @@ public class LayoutTestsExecuter extends Activity {
            return;
        }

        mCurrentTestCount++;
        mCurrentTestRelativePath = mTestsList.remove(0);
        mCurrentTestUri =
                Uri.fromFile(new File(TESTS_ROOT_DIR_PATH, mCurrentTestRelativePath)).toString();
@@ -226,6 +241,7 @@ public class LayoutTestsExecuter extends Activity {
            Message serviceMsg =
                    Message.obtain(null, ManagerService.MSG_PROCESS_ACTUAL_RESULTS);
            Bundle bundle = mCurrentResult.getBundle();
            bundle.putInt("testIndex", mCurrentTestIndex);
            /** TODO: Add timeout info to bundle */
            serviceMsg.setData(bundle);
            mManagerServiceMessenger.send(serviceMsg);
@@ -234,7 +250,20 @@ public class LayoutTestsExecuter extends Activity {
        }
    }

    private void updateProgressBar() {
        getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
                mCurrentTestIndex * Window.PROGRESS_END / mTotalTestCount);
        setTitle(mCurrentTestIndex * 100 / mTotalTestCount + "% " +
                "(" + mCurrentTestIndex + "/" + mTotalTestCount + ")");
    }

    private void onAllTestsFinished() {
        Log.d(LOG_TAG + "::onAllTestsFisnihed", "Begin.");
        try {
            Message serviceMsg =
                    Message.obtain(null, ManagerService.MSG_ALL_TESTS_FINISHED);
            mManagerServiceMessenger.send(serviceMsg);
        } catch (RemoteException e) {
            Log.e(LOG_TAG + "::onAllTestsFinished", e.getMessage());
        }
    }
}
 No newline at end of file
+0 −304
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.content.Intent;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.util.Log;

import java.io.File;
import java.util.ArrayList;
import java.util.LinkedList;

/**
 * A Thread that is responsible for finding and loading the tests, starting them and
 * generating summaries. The actual running of the test is delegated to LayoutTestsRunner
 * activity (a UI thread) because of a WebView object that need to be created in UI thread
 * so it can be displayed on the screen. However, the logic for doing this remains in
 * this class (in handler created in constructor).
 */
public class LayoutTestsRunnerThread extends Thread {

    private static final String LOG_TAG = "LayoutTestsRunnerThread";

    /** Messages for handler on this thread */
    public static final int MSG_TEST_FINISHED = 0;

    /** Messages for our handler running on UI thread */
    public static final int MSG_RUN_TEST = 0;

    /** TODO: make it a setting */
    private static final String TESTS_ROOT_DIR_PATH =
            Environment.getExternalStorageDirectory() +
            File.separator + "android" +
            File.separator + "LayoutTests";

    /** TODO: make it a setting */
    private static final String RESULTS_ROOT_DIR_PATH =
            Environment.getExternalStorageDirectory() +
            File.separator + "android" +
            File.separator + "LayoutTests-results";

    /** TODO: Make it a setting */
    private static final String EXPECTED_RESULT_SECONDARY_LOCATION_RELATIVE_DIR_PREFIX =
            "platform" + File.separator +
            "android-v8" + File.separator;

    /** TODO: Make these settings */
    private static final String TEXT_RESULT_EXTENSION = "txt";
    private static final String IMAGE_RESULT_EXTENSION = "png";

    /** A list containing relative paths of tests to run */
    private LinkedList<String> mTestsList = new LinkedList<String>();

    private FileFilter mFileFilter;
    private Summarizer mSummarizer;

    /** Our handler running on this thread. Created in run() method. */
    private Handler mHandler;

    /** Our handler running on UI thread. Created in constructor of this thread. */
    private Handler mHandlerOnUiThread;

    /**
     * A relative path to the folder with the tests we want to run or particular test.
     * Used up to and including preloadTests().
     */
    private String mRelativePath;

    private LayoutTestsRunner mActivity;

    private LayoutTest mCurrentTest;
    private String mCurrentTestPath;
    private int mCurrentTestCount = 0;
    private int mTotalTestCount;

    /**
     * The given path must be relative to the root dir. The given handler must be
     * able to handle messages that update the display (UI thread).
     *
     * @param path
     * @param uiDisplayHandler
     */
    public LayoutTestsRunnerThread(String path, LayoutTestsRunner activity) {
        mFileFilter = new FileFilter(TESTS_ROOT_DIR_PATH);
        mRelativePath = path;
        mActivity = activity;

        /** This creates a handler that runs on the thread that _created_ this thread */
        mHandlerOnUiThread = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_RUN_TEST:
                        ((LayoutTest) msg.obj).run();
                        break;
                }
            }
        };
    }

    @Override
    public void run() {
        Looper.prepare();

        mSummarizer = new Summarizer(mFileFilter, RESULTS_ROOT_DIR_PATH);

        /** A handler obtained from UI thread to handle messages concerning updating the display */
        final Handler uiDisplayHandler = mActivity.getHandler();

        /** Creates a new handler in _this_ thread */
        mHandler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                switch (msg.what) {
                    case MSG_TEST_FINISHED:
                        onTestFinished(mCurrentTest);
                        uiDisplayHandler.obtainMessage(LayoutTestsRunner.MSG_UPDATE_PROGRESS,
                                mCurrentTestCount, mTotalTestCount).sendToTarget();
                        runNextTest();
                        break;
                }
            }
        };

        /** Check if the path is correct */
        File file = new File(TESTS_ROOT_DIR_PATH, mRelativePath);
        if (!file.exists()) {
            Log.e(LOG_TAG + "::run", "Path does not exist: " + mRelativePath);
            return;
        }

        /** Populate the tests' list accordingly */
        if (file.isDirectory()) {
            uiDisplayHandler.sendEmptyMessage(LayoutTestsRunner.MSG_SHOW_PROGRESS_DIALOG);
            preloadTests(mRelativePath);
            uiDisplayHandler.sendEmptyMessage(LayoutTestsRunner.MSG_DISMISS_PROGRESS_DIALOG);
        } else {
            mTestsList.addLast(mRelativePath);
            mTotalTestCount = 1;
        }

        /**
         * Instead of running next test here, we send a tests' list to Executer activity.
         * Rest of the code is never executed and will be gradually moved to the service.
         */
        Intent intent = new Intent();
        intent.setClass(mActivity, LayoutTestsExecuter.class);
        intent.setAction(Intent.ACTION_RUN);
        intent.putStringArrayListExtra(LayoutTestsExecuter.EXTRA_TESTS_LIST,
                new ArrayList<String>(mTestsList));
        mActivity.startActivity(intent);

        Looper.loop();
    }

    /**
     * Loads all the tests from the given folders and all the subfolders
     * into mTestsList.
     *
     * @param dirRelativePath
     */
    private void preloadTests(String dirRelativePath) {
        LinkedList<String> foldersList = new LinkedList<String>();
        foldersList.add(dirRelativePath);

        String relativePath;
        String currentDirRelativePath;
        String itemName;
        File[] items;
        while (!foldersList.isEmpty()) {
            currentDirRelativePath = foldersList.removeFirst();
            items = new File(TESTS_ROOT_DIR_PATH, currentDirRelativePath).listFiles();
            for (File item : items) {
                itemName = item.getName();
                relativePath = currentDirRelativePath + File.separator + itemName;

                if (item.isDirectory() && FileFilter.isTestDir(itemName)) {
                    foldersList.add(relativePath);
                    continue;
                }

                if (FileFilter.isTestFile(itemName)) {
                    if (!mFileFilter.isSkip(relativePath)) {
                        mTestsList.addLast(relativePath);
                    } else {
                        mSummarizer.addSkippedTest(relativePath);
                    }
                }
            }
        }

        mTotalTestCount = mTestsList.size();
    }

    private void runNextTest() {
        if (mTestsList.isEmpty()) {
            onFinishedTests();
            return;
        }

        mCurrentTestCount++;
        mCurrentTestPath = mTestsList.removeFirst();
        mCurrentTest = new LayoutTest(mCurrentTestPath, TESTS_ROOT_DIR_PATH,
                mHandler.obtainMessage(MSG_TEST_FINISHED), mActivity);

        /**
         * This will run the test on UI thread. The reason why we need to run the test
         * on UI thread is because of the WebView. If we want to display the webview on
         * the screen it needs to be in the UI thread. WebView should be created as
         * part of the LayoutTest.run() method.
         */
        mHandlerOnUiThread.obtainMessage(MSG_RUN_TEST, mCurrentTest).sendToTarget();
    }

    private void onTestFinished(LayoutTest test) {
        String testPath = test.getRelativePath();

        /** Obtain the result */
        AbstractResult result = test.getResult();
        if (result == null) {
            Log.e(LOG_TAG + "::runTests", testPath + ": result NULL!!");
            return;
        }

        dumpResultData(result, testPath);

        mSummarizer.appendTest(test);
    }

    private void dumpResultData(AbstractResult result, String testPath) {
        dumpActualTextResult(result, testPath);
        dumpActualImageResult(result, testPath);
    }

    private void dumpActualTextResult(AbstractResult result, String testPath) {
        String actualTextResult = result.getActualTextResult();
        if (actualTextResult == null) {
            return;
        }

        String resultPath = FileFilter.setPathEnding(testPath, "-actual." + TEXT_RESULT_EXTENSION);
        FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
                actualTextResult.getBytes(), false);
    }

    private void dumpActualImageResult(AbstractResult result, String testPath) {
        byte[] actualImageResult = result.getActualImageResult();
        if (actualImageResult == null) {
            return;
        }

        String resultPath = FileFilter.setPathEnding(testPath, "-actual." + IMAGE_RESULT_EXTENSION);
        FsUtils.writeDataToStorage(new File(RESULTS_ROOT_DIR_PATH, resultPath),
                actualImageResult, false);
    }

    private void onFinishedTests() {
        Log.d(LOG_TAG + "::onFinishedTests", "Begin.");
        Looper.myLooper().quit();
        mSummarizer.summarize();
        /** TODO: Present some kind of notification to the user that
         * allows to chose next action, e.g:
         * - go to html view of results
         * - zip results
         * - run more tests before zipping */
    }

    public static String getExpectedTextResult(String relativePath) {
        return new String(getExpectedResult(relativePath, TEXT_RESULT_EXTENSION));
    }

    public static byte[] getExpectedImageResult(String relativePath) {
        return getExpectedResult(relativePath, IMAGE_RESULT_EXTENSION);
    }

    private static byte[] getExpectedResult(String relativePath, String extension) {
        relativePath = FileFilter.setPathEnding(relativePath, "-expected." + extension);

        byte[] bytes = FsUtils.readDataFromStorage(new File(TESTS_ROOT_DIR_PATH, relativePath));
        if (bytes == null) {
            relativePath = EXPECTED_RESULT_SECONDARY_LOCATION_RELATIVE_DIR_PREFIX + relativePath;
            bytes = FsUtils.readDataFromStorage(new File(TESTS_ROOT_DIR_PATH, relativePath));
        }

        return bytes;
    }
}
 No newline at end of file
Loading