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