TestShellActivity.java revision f92bd42a702af7047ac4bd7c95b4a82973b7a79d
1/*
2 * Copyright (C) 2007 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.dumprendertree;
18
19import android.app.Activity;
20import android.app.AlertDialog;
21import android.content.DialogInterface;
22import android.content.Intent;
23import android.content.DialogInterface.OnClickListener;
24import android.graphics.Bitmap;
25import android.net.http.SslError;
26import android.os.Bundle;
27import android.os.Handler;
28import android.os.Message;
29import android.util.Log;
30import android.view.ViewGroup;
31import android.webkit.HttpAuthHandler;
32import android.webkit.JsPromptResult;
33import android.webkit.JsResult;
34import android.webkit.SslErrorHandler;
35import android.webkit.WebChromeClient;
36import android.webkit.WebSettings;
37import android.webkit.WebView;
38import android.webkit.WebViewClient;
39import android.widget.LinearLayout;
40
41import java.io.BufferedReader;
42import java.io.File;
43import java.io.FileOutputStream;
44import java.io.FileReader;
45import java.io.IOException;
46import java.util.Vector;
47
48public class TestShellActivity extends Activity implements LayoutTestController {
49
50    static enum DumpDataType {DUMP_AS_TEXT, EXT_REPR, NO_OP}
51
52    public class AsyncHandler extends Handler {
53        @Override
54        public void handleMessage(Message msg) {
55            if (msg.what == MSG_TIMEOUT) {
56                mTimedOut = true;
57                if(mCallback != null)
58                    mCallback.timedOut(mWebView.getUrl());
59                requestWebKitData();
60                return;
61            } else if (msg.what == MSG_WEBKIT_DATA) {
62                TestShellActivity.this.dump(mTimedOut, (String)msg.obj);
63                return;
64            }
65
66            super.handleMessage(msg);
67        }
68    }
69
70    public void requestWebKitData() {
71        Message callback = mHandler.obtainMessage(MSG_WEBKIT_DATA);
72
73        if (mRequestedWebKitData)
74            throw new AssertionError("Requested webkit data twice: " + mWebView.getUrl());
75
76        mRequestedWebKitData = true;
77        switch (mDumpDataType) {
78            case DUMP_AS_TEXT:
79                mWebView.documentAsText(callback);
80                break;
81            case EXT_REPR:
82                mWebView.externalRepresentation(callback);
83                break;
84            default:
85                finished();
86                break;
87        }
88    }
89
90    @Override
91    protected void onCreate(Bundle icicle) {
92        super.onCreate(icicle);
93
94        LinearLayout contentView = new LinearLayout(this);
95        contentView.setOrientation(LinearLayout.VERTICAL);
96        setContentView(contentView);
97
98        mWebView = new WebView(this);
99        mWebView.getSettings().setJavaScriptEnabled(true);
100        mWebView.setWebChromeClient(mChromeClient);
101        mWebView.setWebViewClient(new WebViewClient(){
102
103            @Override
104            public void onPageFinished(WebView view, String url) {
105                Log.v(LOGTAG, "onPageFinished, url=" + url);
106                super.onPageFinished(view, url);
107            }
108
109            @Override
110            public void onPageStarted(WebView view, String url, Bitmap favicon) {
111                Log.v(LOGTAG, "onPageStarted, url=" + url);
112                super.onPageStarted(view, url, favicon);
113            }
114
115            @Override
116            public void onReceivedError(WebView view, int errorCode, String description,
117                    String failingUrl) {
118                Log.v(LOGTAG, "onReceivedError, errorCode=" + errorCode
119                        + ", desc=" + description + ", url=" + failingUrl);
120                super.onReceivedError(view, errorCode, description, failingUrl);
121            }
122
123            @Override
124            public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler,
125                    String host, String realm) {
126                handler.cancel();
127            }
128
129            @Override
130            public void onReceivedSslError(WebView view, SslErrorHandler handler,
131                    SslError error) {
132                handler.proceed();
133            }
134
135        });
136        mEventSender = new WebViewEventSender(mWebView);
137        mCallbackProxy = new CallbackProxy(mEventSender, this);
138
139        mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController");
140        mWebView.addJavascriptInterface(mCallbackProxy, "eventSender");
141        contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f));
142
143        mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
144
145        mHandler = new AsyncHandler();
146
147        Intent intent = getIntent();
148        if (intent != null) {
149            executeIntent(intent);
150        }
151    }
152
153    @Override
154    protected void onNewIntent(Intent intent) {
155        super.onNewIntent(intent);
156        executeIntent(intent);
157    }
158
159    private void executeIntent(Intent intent) {
160        resetTestStatus();
161        if (!Intent.ACTION_VIEW.equals(intent.getAction())) {
162            return;
163        }
164
165        mTestUrl = intent.getStringExtra(TEST_URL);
166        if (mTestUrl == null) {
167            mUiAutoTestPath = intent.getStringExtra(UI_AUTO_TEST);
168            if(mUiAutoTestPath != null) {
169                beginUiAutoTest();
170            }
171            return;
172        }
173
174        mResultFile = intent.getStringExtra(RESULT_FILE);
175        mTimeoutInMillis = intent.getIntExtra(TIMEOUT_IN_MILLIS, 0);
176
177        Log.v(LOGTAG, "  Loading " + mTestUrl);
178        mWebView.loadUrl(mTestUrl);
179
180        if (mTimeoutInMillis > 0) {
181            // Create a timeout timer
182            Message m = mHandler.obtainMessage(MSG_TIMEOUT);
183            mHandler.sendMessageDelayed(m, mTimeoutInMillis);
184        }
185    }
186
187    private void beginUiAutoTest() {
188        try {
189            mTestListReader = new BufferedReader(
190                    new FileReader(mUiAutoTestPath));
191        } catch (IOException ioe) {
192            Log.e(LOGTAG, "Failed to open test list for read.", ioe);
193            finishUiAutoTest();
194            return;
195        }
196        moveToNextTest();
197    }
198
199    private void finishUiAutoTest() {
200        try {
201            if(mTestListReader != null)
202                mTestListReader.close();
203        } catch (IOException ioe) {
204            Log.w(LOGTAG, "Failed to close test list file.", ioe);
205        }
206        finished();
207    }
208
209    private void moveToNextTest() {
210        String url = null;
211        try {
212            url = mTestListReader.readLine();
213        } catch (IOException ioe) {
214            Log.e(LOGTAG, "Failed to read next test.", ioe);
215            finishUiAutoTest();
216            return;
217        }
218        if (url == null) {
219            mUiAutoTestPath = null;
220            finishUiAutoTest();
221            AlertDialog.Builder builder = new AlertDialog.Builder(this);
222            builder.setMessage("All tests finished. Exit?")
223                   .setCancelable(false)
224                   .setPositiveButton("Yes", new OnClickListener(){
225                       public void onClick(DialogInterface dialog, int which) {
226                           TestShellActivity.this.finish();
227                       }
228                   })
229                   .setNegativeButton("No", new OnClickListener(){
230                       public void onClick(DialogInterface dialog, int which) {
231                           dialog.cancel();
232                       }
233                   });
234            builder.create().show();
235            return;
236        }
237        url = "file://" + url;
238        Intent intent = new Intent(Intent.ACTION_VIEW);
239        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
240        intent.putExtra(TestShellActivity.TEST_URL, url);
241        intent.putExtra(TIMEOUT_IN_MILLIS, 10000);
242        executeIntent(intent);
243    }
244
245    @Override
246    protected void onStop() {
247        super.onStop();
248        mWebView.stopLoading();
249    }
250
251    @Override
252    protected void onDestroy() {
253        super.onDestroy();
254        mWebView.destroy();
255        mWebView = null;
256    }
257
258    @Override
259    public void onLowMemory() {
260        super.onLowMemory();
261        Log.e(LOGTAG, "Low memory, kill self");
262        System.exit(1);
263    }
264
265    // Dump the page
266    public void dump(boolean timeout, String webkitData) {
267        if (mResultFile == null || mResultFile.length() == 0) {
268            finished();
269            return;
270        }
271
272        try {
273            File parentDir = new File(mResultFile).getParentFile();
274            if (!parentDir.exists()) {
275                parentDir.mkdirs();
276            }
277
278            FileOutputStream os = new FileOutputStream(mResultFile);
279            if (timeout) {
280                Log.w("Layout test: Timeout", mResultFile);
281                os.write(TIMEOUT_STR.getBytes());
282                os.write('\n');
283            }
284            if (mDumpTitleChanges)
285                os.write(mTitleChanges.toString().getBytes());
286            if (mDialogStrings != null)
287                os.write(mDialogStrings.toString().getBytes());
288            mDialogStrings = null;
289            if (webkitData != null)
290                os.write(webkitData.getBytes());
291            os.flush();
292            os.close();
293        } catch (IOException ex) {
294            Log.e(LOGTAG, "Cannot write to " + mResultFile + ", " + ex.getMessage());
295        }
296
297        finished();
298    }
299
300    public void setCallback(TestShellCallback callback) {
301        mCallback = callback;
302    }
303
304    public void finished() {
305        if (mUiAutoTestPath != null) {
306            //don't really finish here
307            moveToNextTest();
308        } else {
309            if (mCallback != null) {
310                mCallback.finished();
311            }
312        }
313    }
314
315    public void setDefaultDumpDataType(DumpDataType defaultDumpDataType) {
316        mDefaultDumpDataType = defaultDumpDataType;
317    }
318
319    // .......................................
320    // LayoutTestController Functions
321    public void dumpAsText() {
322        mDumpDataType = DumpDataType.DUMP_AS_TEXT;
323        if (mWebView != null) {
324            String url = mWebView.getUrl();
325            Log.v(LOGTAG, "dumpAsText called: "+url);
326        }
327    }
328
329    public void waitUntilDone() {
330        mWaitUntilDone = true;
331        String url = mWebView.getUrl();
332        Log.v(LOGTAG, "waitUntilDone called: " + url);
333    }
334
335    public void notifyDone() {
336        String url = mWebView.getUrl();
337        Log.v(LOGTAG, "notifyDone called: " + url);
338        if (mWaitUntilDone) {
339            mWaitUntilDone = false;
340            mChromeClient.onProgressChanged(mWebView, 100);
341        }
342    }
343
344    public void display() {
345        mWebView.invalidate();
346    }
347
348    public void clearBackForwardList() {
349        mWebView.clearHistory();
350
351    }
352
353    public void dumpBackForwardList() {
354        //printf("\n============== Back Forward List ==============\n");
355        // mWebHistory
356        //printf("===============================================\n");
357
358    }
359
360    public void dumpChildFrameScrollPositions() {
361        // TODO Auto-generated method stub
362
363    }
364
365    public void dumpEditingCallbacks() {
366        // TODO Auto-generated method stub
367
368    }
369
370    public void dumpSelectionRect() {
371        // TODO Auto-generated method stub
372
373    }
374
375    public void dumpTitleChanges() {
376        if (!mDumpTitleChanges) {
377            mTitleChanges = new StringBuffer();
378        }
379        mDumpTitleChanges = true;
380    }
381
382    public void keepWebHistory() {
383        if (!mKeepWebHistory) {
384            mWebHistory = new Vector();
385        }
386        mKeepWebHistory = true;
387    }
388
389    public void queueBackNavigation(int howfar) {
390        // TODO Auto-generated method stub
391
392    }
393
394    public void queueForwardNavigation(int howfar) {
395        // TODO Auto-generated method stub
396
397    }
398
399    public void queueLoad(String Url, String frameTarget) {
400        // TODO Auto-generated method stub
401
402    }
403
404    public void queueReload() {
405        mWebView.reload();
406    }
407
408    public void queueScript(String scriptToRunInCurrentContext) {
409        mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext);
410    }
411
412    public void repaintSweepHorizontally() {
413        // TODO Auto-generated method stub
414
415    }
416
417    public void setAcceptsEditing(boolean b) {
418        // TODO Auto-generated method stub
419
420    }
421
422    public void setMainFrameIsFirstResponder(boolean b) {
423        // TODO Auto-generated method stub
424
425    }
426
427    public void setWindowIsKey(boolean b) {
428        // This is meant to show/hide the window. The best I can find
429        // is setEnabled()
430        mWebView.setEnabled(b);
431    }
432
433    public void testRepaint() {
434        mWebView.invalidate();
435    }
436
437    private final WebChromeClient mChromeClient = new WebChromeClient() {
438        @Override
439        public void onProgressChanged(WebView view, int newProgress) {
440            if (newProgress == 100) {
441                if (!mTimedOut && !mWaitUntilDone && !mRequestedWebKitData) {
442                    String url = mWebView.getUrl();
443                    Log.v(LOGTAG, "Finished: "+ url);
444                    mHandler.removeMessages(MSG_TIMEOUT);
445                    requestWebKitData();
446                } else {
447                    String url = mWebView.getUrl();
448                    if (mTimedOut) {
449                        Log.v(LOGTAG, "Timed out before finishing: " + url);
450                    } else if (mWaitUntilDone) {
451                        Log.v(LOGTAG, "Waiting for notifyDone: " + url);
452                    } else if (mRequestedWebKitData) {
453                        Log.v(LOGTAG, "Requested webkit data ready: " + url);
454                    }
455                }
456            }
457        }
458
459        @Override
460        public void onReceivedTitle(WebView view, String title) {
461            if (title.length() > 30)
462                title = "..."+title.substring(title.length()-30);
463            setTitle(title);
464            if (mDumpTitleChanges) {
465                mTitleChanges.append("TITLE CHANGED: ");
466                mTitleChanges.append(title);
467                mTitleChanges.append("\n");
468            }
469        }
470
471        @Override
472        public boolean onJsAlert(WebView view, String url, String message,
473                JsResult result) {
474            if (mDialogStrings == null) {
475                mDialogStrings = new StringBuffer();
476            }
477            mDialogStrings.append("ALERT: ");
478            mDialogStrings.append(message);
479            mDialogStrings.append('\n');
480            result.confirm();
481            return true;
482        }
483
484        @Override
485        public boolean onJsConfirm(WebView view, String url, String message,
486                JsResult result) {
487            if (mDialogStrings == null) {
488                mDialogStrings = new StringBuffer();
489            }
490            mDialogStrings.append("CONFIRM: ");
491            mDialogStrings.append(message);
492            mDialogStrings.append('\n');
493            result.confirm();
494            return true;
495        }
496
497        @Override
498        public boolean onJsPrompt(WebView view, String url, String message,
499                String defaultValue, JsPromptResult result) {
500            if (mDialogStrings == null) {
501                mDialogStrings = new StringBuffer();
502            }
503            mDialogStrings.append("PROMPT: ");
504            mDialogStrings.append(message);
505            mDialogStrings.append(", default text: ");
506            mDialogStrings.append(defaultValue);
507            mDialogStrings.append('\n');
508            result.confirm();
509            return true;
510        }
511    };
512
513    private void resetTestStatus() {
514        mWaitUntilDone = false;
515        mDumpDataType = mDefaultDumpDataType;
516        mTimedOut = false;
517        mDumpTitleChanges = false;
518        mRequestedWebKitData = false;
519        mEventSender.resetMouse();
520    }
521
522    private WebView mWebView;
523    private WebViewEventSender mEventSender;
524    private AsyncHandler mHandler;
525    private TestShellCallback mCallback;
526
527    private CallbackProxy mCallbackProxy;
528
529    private String mTestUrl;
530    private String mResultFile;
531    private int mTimeoutInMillis;
532    private String mUiAutoTestPath;
533    private BufferedReader mTestListReader;
534
535    // States
536    private boolean mTimedOut;
537    private boolean mRequestedWebKitData;
538    private boolean mFinishedRunning;
539
540    // Layout test controller variables.
541    private DumpDataType mDumpDataType;
542    private DumpDataType mDefaultDumpDataType = DumpDataType.EXT_REPR;
543    private boolean mWaitUntilDone;
544    private boolean mDumpTitleChanges;
545    private StringBuffer mTitleChanges;
546    private StringBuffer mDialogStrings;
547    private boolean mKeepWebHistory;
548    private Vector mWebHistory;
549
550    static final String TIMEOUT_STR = "**Test timeout";
551
552    static final int MSG_TIMEOUT = 0;
553    static final int MSG_WEBKIT_DATA = 1;
554
555    static final String LOGTAG="TestShell";
556
557    static final String TEST_URL = "TestUrl";
558    static final String RESULT_FILE = "ResultFile";
559    static final String TIMEOUT_IN_MILLIS = "TimeoutInMillis";
560    static final String UI_AUTO_TEST = "UiAutoTest";
561}
562