LayoutTestsExecutor.java revision 5f0ccd76a88586ce85c17cb4db058934e693a4fc
1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.dumprendertree2;
18
19import android.app.Activity;
20import android.content.ComponentName;
21import android.content.Context;
22import android.content.Intent;
23import android.content.ServiceConnection;
24import android.net.Uri;
25import android.os.Bundle;
26import android.os.Environment;
27import android.os.Handler;
28import android.os.IBinder;
29import android.os.Message;
30import android.os.Messenger;
31import android.os.RemoteException;
32import android.util.Log;
33import android.view.Window;
34import android.webkit.JsPromptResult;
35import android.webkit.JsResult;
36import android.webkit.WebChromeClient;
37import android.webkit.WebSettings;
38import android.webkit.WebView;
39import android.webkit.WebViewClient;
40import android.webkit.WebStorage.QuotaUpdater;
41
42import java.io.File;
43import java.util.List;
44
45/**
46 * This activity executes the test. It contains WebView and logic of LayoutTestController
47 * functions. It runs in a separate process and sends the results of running the test
48 * to ManagerService. The reason why is to handle crashing (test that crashes brings down
49 * whole process with it).
50 */
51public class LayoutTestsExecutor extends Activity {
52
53    /** TODO: make it a setting */
54    static final String TESTS_ROOT_DIR_PATH =
55            Environment.getExternalStorageDirectory() +
56            File.separator + "android" +
57            File.separator + "LayoutTests";
58
59    private static final String LOG_TAG = "LayoutTestExecutor";
60
61    public static final String EXTRA_TESTS_LIST = "TestsList";
62    public static final String EXTRA_TEST_INDEX = "TestIndex";
63
64    private static final int MSG_ACTUAL_RESULT_OBTAINED = 0;
65
66    private List<String> mTestsList;
67
68    /**
69     * This is a number of currently running test. It is 0-based and doesn't reset after
70     * the crash. Initial index is passed to LayoutTestsExecuter in the intent that starts
71     * it.
72     */
73    private int mCurrentTestIndex;
74
75    private int mTotalTestCount;
76
77    private WebView mCurrentWebView;
78    private String mCurrentTestRelativePath;
79    private String mCurrentTestUri;
80
81    private boolean mOnTestFinishedCalled;
82    private AbstractResult mCurrentResult;
83
84    /** COMMUNICATION WITH ManagerService */
85
86    private Messenger mManagerServiceMessenger;
87
88    private ServiceConnection mServiceConnection = new ServiceConnection() {
89
90        @Override
91        public void onServiceConnected(ComponentName name, IBinder service) {
92            mManagerServiceMessenger = new Messenger(service);
93            runNextTest();
94        }
95
96        @Override
97        public void onServiceDisconnected(ComponentName name) {
98            /** TODO */
99        }
100    };
101
102    private final Handler mResultHandler = new Handler() {
103        @Override
104        public void handleMessage(Message msg) {
105            if (msg.what == MSG_ACTUAL_RESULT_OBTAINED) {
106                reportResultToService();
107                mCurrentTestIndex++;
108                updateProgressBar();
109                runNextTest();
110            }
111        }
112    };
113
114    /** WEBVIEW CONFIGURATION */
115
116    private WebViewClient mWebViewClient = new WebViewClient() {
117        @Override
118        public void onPageFinished(WebView view, String url) {
119            /** Some tests fire up many page loads, we don't want to detect them */
120            if (!url.equals(mCurrentTestUri)) {
121                return;
122            }
123
124            /** TODO: Implement waitUntilDone */
125            onTestFinished();
126        }
127    };
128
129    private WebChromeClient mWebChromeClient = new WebChromeClient() {
130        @Override
131        public void onExceededDatabaseQuota(String url, String databaseIdentifier,
132                long currentQuota, long estimatedSize, long totalUsedQuota,
133                QuotaUpdater quotaUpdater) {
134            /** TODO: This should be recorded as part of the text result */
135            quotaUpdater.updateQuota(currentQuota + 5 * 1024 * 1024);
136        }
137
138        @Override
139        public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
140            /** TODO: Alerts should be recorded as part of text result */
141            result.confirm();
142            return true;
143        }
144
145        @Override
146        public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
147            /** TODO: Alerts should be recorded as part of text result */
148            result.confirm();
149            return true;
150        }
151
152        @Override
153        public boolean onJsPrompt(WebView view, String url, String message, String defaultValue,
154                JsPromptResult result) {
155            /** TODO: Alerts should be recorded as part of text result */
156            result.confirm();
157            return true;
158        }
159
160    };
161
162    /** IMPLEMENTATION */
163
164    @Override
165    protected void onCreate(Bundle savedInstanceState) {
166        super.onCreate(savedInstanceState);
167
168        requestWindowFeature(Window.FEATURE_PROGRESS);
169
170        Intent intent = getIntent();
171        mTestsList = intent.getStringArrayListExtra(EXTRA_TESTS_LIST);
172        mCurrentTestIndex = intent.getIntExtra(EXTRA_TEST_INDEX, -1);
173        mTotalTestCount = mCurrentTestIndex + mTestsList.size();
174
175        bindService(new Intent(this, ManagerService.class), mServiceConnection,
176                Context.BIND_AUTO_CREATE);
177    }
178
179    private void reset() {
180        mOnTestFinishedCalled = false;
181        mCurrentResult = null;
182
183        mCurrentWebView = new WebView(this);
184        mCurrentWebView.setWebViewClient(mWebViewClient);
185        mCurrentWebView.setWebChromeClient(mWebChromeClient);
186
187        WebSettings webViewSettings = mCurrentWebView.getSettings();
188        webViewSettings.setAppCacheEnabled(true);
189        webViewSettings.setAppCachePath(getApplicationContext().getCacheDir().getPath());
190        webViewSettings.setAppCacheMaxSize(Long.MAX_VALUE);
191        webViewSettings.setJavaScriptEnabled(true);
192        webViewSettings.setJavaScriptCanOpenWindowsAutomatically(true);
193        webViewSettings.setSupportMultipleWindows(true);
194        webViewSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
195        webViewSettings.setDatabaseEnabled(true);
196        webViewSettings.setDatabasePath(getDir("databases", 0).getAbsolutePath());
197        webViewSettings.setDomStorageEnabled(true);
198        webViewSettings.setWorkersEnabled(false);
199        webViewSettings.setXSSAuditorEnabled(false);
200
201        setContentView(mCurrentWebView);
202    }
203
204    private void runNextTest() {
205        if (mTestsList.isEmpty()) {
206            onAllTestsFinished();
207            return;
208        }
209
210        mCurrentTestRelativePath = mTestsList.remove(0);
211        mCurrentTestUri =
212                Uri.fromFile(new File(TESTS_ROOT_DIR_PATH, mCurrentTestRelativePath)).toString();
213
214        reset();
215        /** TODO: Implement timeout */
216        mCurrentWebView.loadUrl(mCurrentTestUri);
217    }
218
219    private void onTestFinished() {
220        if (mOnTestFinishedCalled) {
221            return;
222        }
223
224        mOnTestFinishedCalled = true;
225
226        /**
227         * If the result has not been set by the time the test finishes we create
228         * a default type of result.
229         */
230        if (mCurrentResult == null) {
231            /** TODO: Default type should be RenderTreeResult. We don't support it now. */
232            mCurrentResult = new TextResult(mCurrentTestRelativePath);
233        }
234
235        mCurrentResult.obtainActualResults(mCurrentWebView,
236                mResultHandler.obtainMessage(MSG_ACTUAL_RESULT_OBTAINED));
237    }
238
239    private void reportResultToService() {
240        try {
241            Message serviceMsg =
242                    Message.obtain(null, ManagerService.MSG_PROCESS_ACTUAL_RESULTS);
243            Bundle bundle = mCurrentResult.getBundle();
244            bundle.putInt("testIndex", mCurrentTestIndex);
245            /** TODO: Add timeout info to bundle */
246            serviceMsg.setData(bundle);
247            mManagerServiceMessenger.send(serviceMsg);
248        } catch (RemoteException e) {
249            Log.e(LOG_TAG + "::reportResultToService", e.getMessage());
250        }
251    }
252
253    private void updateProgressBar() {
254        getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
255                mCurrentTestIndex * Window.PROGRESS_END / mTotalTestCount);
256        setTitle(mCurrentTestIndex * 100 / mTotalTestCount + "% " +
257                "(" + mCurrentTestIndex + "/" + mTotalTestCount + ")");
258    }
259
260    private void onAllTestsFinished() {
261        try {
262            Message serviceMsg =
263                    Message.obtain(null, ManagerService.MSG_ALL_TESTS_FINISHED);
264            mManagerServiceMessenger.send(serviceMsg);
265        } catch (RemoteException e) {
266            Log.e(LOG_TAG + "::onAllTestsFinished", e.getMessage());
267        }
268    }
269}