diff options
Diffstat (limited to 'tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java')
-rw-r--r-- | tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java | 607 |
1 files changed, 607 insertions, 0 deletions
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java new file mode 100644 index 0000000..9521f80 --- /dev/null +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/HTMLHostActivity.java @@ -0,0 +1,607 @@ +/* + * Copyright (C) 2007 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.dumprendertree; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.BufferedOutputStream; +import java.io.FileInputStream; +import java.io.IOException; +import java.util.Vector; +import java.util.Stack; + +import android.app.Activity; +import android.content.Intent; +import android.os.Bundle; +import android.util.Log; +import android.view.KeyEvent; +import android.view.ViewGroup; +import android.webkit.JsPromptResult; +import android.webkit.JsResult; +import android.webkit.WebChromeClient; +import android.webkit.WebSettings; +import android.webkit.WebView; +import android.widget.LinearLayout; +import android.os.*; +import android.test.TestRecorder; + +// SQLite3 in android has a bunch of bugs which +// is causing TestRecorder to not record the results +// properly. This class is a wrapper around it and records +// results in a file as well. +class TestRecorderV2 extends TestRecorder { + @Override + public void passed(String layout_file) { + try { + mBufferedOutputPassedStream.write(layout_file.getBytes()); + mBufferedOutputPassedStream.write('\n'); + mBufferedOutputPassedStream.flush(); + } catch(Exception e) { + e.printStackTrace(); + } + super.passed(layout_file); + } + + @Override + public void failed(String layout_file, String reason) { + try { + mBufferedOutputFailedStream.write(layout_file.getBytes()); + mBufferedOutputFailedStream.write('\n'); + mBufferedOutputFailedStream.flush(); + } catch(Exception e) { + e.printStackTrace(); + } + super.failed(layout_file, reason); + } + + public TestRecorderV2() { + super(); + try { + File resultsPassedFile = new File("/sdcard/layout_test_presults.txt"); + File resultsFailedFile = new File("/sdcard/layout_test_fresults.txt"); + + mBufferedOutputPassedStream = + new BufferedOutputStream(new FileOutputStream(resultsPassedFile, true)); + mBufferedOutputFailedStream = + new BufferedOutputStream(new FileOutputStream(resultsFailedFile, true)); + + } catch (Exception e) { + e.printStackTrace(); + } + } + + protected void finalize() throws Throwable { + mBufferedOutputPassedStream.flush(); + mBufferedOutputFailedStream.flush(); + mBufferedOutputPassedStream.close(); + mBufferedOutputFailedStream.close(); + } + + private static BufferedOutputStream mBufferedOutputPassedStream; + private static BufferedOutputStream mBufferedOutputFailedStream; +} + +public class HTMLHostActivity extends Activity + implements LayoutTestController { + + private TestRecorderV2 mResultRecorder = new TestRecorderV2(); + private HTMLHostCallbackInterface mCallback = null; + private CallbackProxy mCallbackProxy; + + public class FileEntry { + public FileEntry(String path, int index) { + mPath = path; mIndex=index; + } + String mPath; + int mIndex; + } + + public class AsyncHandler extends Handler { + @Override + public void handleMessage(Message msg) { + if (msg.what == MSG_DUMP) { + this.removeMessages(MSG_TIMEOUT); + mTimedOut = false; + requestWebKitData(); + return; + } else if (msg.what == MSG_TIMEOUT) { + mTimedOut = true; + requestWebKitData(); + return; + } else if (msg.what == MSG_WEBKIT_DATA) { + HTMLHostActivity.this.dump(mTimedOut, (String)msg.obj); + return; + } + + super.handleMessage(msg); + } + + void requestWebKitData() { + Message callback = obtainMessage(MSG_WEBKIT_DATA); + if (dumpAsText) { + mWebView.documentAsText(callback); + } else { + mWebView.externalRepresentation(callback); + } + } + + } + + // Activity methods + public void onCreate(Bundle icicle) { + super.onCreate(icicle); + + LinearLayout contentView = new LinearLayout(this); + contentView.setOrientation(LinearLayout.VERTICAL); + setContentView(contentView); + + mWebView = new WebView(this); + mWebView.getSettings().setJavaScriptEnabled(true); + mWebView.setWebChromeClient(mChromeClient); + eventSender = new WebViewEventSender(mWebView); + mCallbackProxy = new CallbackProxy(eventSender, this); + + mWebView.addJavascriptInterface(mCallbackProxy, "layoutTestController"); + mWebView.addJavascriptInterface(mCallbackProxy, "eventSender"); + contentView.addView(mWebView, new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 0.0f)); + + mHandler = new AsyncHandler(); + } + + @Override + protected void onRestoreInstanceState(Bundle savedInstanceState) { + super.onRestoreInstanceState(savedInstanceState); + } + + protected void onResume() { + super.onResume(); + if (mProcessStack == null || mProcessStack.isEmpty() ) { + mOutstandingLoads = 0; + dumpAsText = false; + pageComplete = false; + + mWebView.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); + + mFinishedStack = new Stack(); + + Intent intent = getIntent(); + if (intent.getData() != null) { + File f = new File(intent.getData().toString()); + + if (f.isDirectory()) { + mProcessStack = new Vector(); + mProcessStack.add(new FileEntry(intent.getData().toString(), 0)); + Log.v(LOGTAG, "Initial dir: "+intent.getData().toString()); + loadNextPage(); + } else { + mCurrentFile = intent.getData().toString(); + mWebView.loadUrl("file://"+intent.getData().toString()); + } + + } + else + mWebView.loadUrl("about:"); + } + } + + protected void onStop() { + super.onStop(); + mWebView.stopLoading(); + } + + protected void onDestroy() { + super.onDestroy(); + mWebView.destroy(); + mWebView = null; + } + + public boolean dispatchKeyEvent(KeyEvent event) { + // Log key strokes as they don't seem to be matched + //Log.e(LOGTAG, "Event: "+event); + return super.dispatchKeyEvent(event); + } + + // Functions + + protected void loadNextPage() { + dumpAsText = false; + pageComplete = false; + dumpTitleChanges = false; + eventSender.resetMouse(); + while (!mProcessStack.isEmpty()) { + FileEntry fe = (FileEntry)mProcessStack.remove(0); + if (fe.mIndex == 0) { + System.out.println(); + System.out.print(fe.mPath); + } + Log.v(LOGTAG, "Processing dir: "+fe.mPath+" size: "+mProcessStack.size()); + File f = new File(fe.mPath); + String [] files = f.list(); + for (int i = fe.mIndex; i < files.length; i++) { + if (FileFilter.ignoreTest(files[i])) { + continue; + } + File c = new File(f.getPath(), files[i]); + if (c.isDirectory()) { + Log.v(LOGTAG, "Adding dir: "+fe.mPath+"/"+files[i]); + mProcessStack.add(new FileEntry(fe.mPath+"/"+files[i], 0)); + } else if (files[i].toLowerCase().endsWith("ml")) { + mProcessStack.add(0, new FileEntry(fe.mPath, i+1)); + mCurrentFile = fe.mPath+"/"+files[i]; + Log.e(LOGTAG, "Processing: "+mCurrentFile); + mWebView.loadUrl("file://"+mCurrentFile); + + // Create a timeout timer + Message m = mHandler.obtainMessage(MSG_TIMEOUT); + // Some tests can take up to 5secs to run. + mHandler.sendMessageDelayed(m, 6000); + return; + } + } + Log.v(LOGTAG, "Finished dir: "+fe.mPath+" size: "+mProcessStack.size()); + } + // If we got to here, then we must have finished completely + finished(); + } + + public void scheduleDump() { + // Only schedule if we really are ready + if (waitToDump || mOutstandingLoads > 0 || mDumpRequested) { + return; + } + mDumpRequested = true; + mHandler.obtainMessage(MSG_DUMP).sendToTarget(); + } + + // Dump the page + public void dump(boolean timeout, String webkitData) { + mDumpRequested = false; + System.out.print('.'); + + // remove the extension + String resultFile = mCurrentFile.substring(0, mCurrentFile.lastIndexOf('.')); + + // store the finished file on the stack so that we can do a diff at the end. + mFinishedStack.push(resultFile); + + // dumpAsText version can be directly compared to expected results + if (dumpAsText) { + resultFile += "-results.txt"; + } else { + resultFile += "-android-results.txt"; + } + try { + FileOutputStream os = new FileOutputStream(resultFile); + if (timeout) { + Log.i("Layout test: Timeout", resultFile); + os.write("**Test timeout\n".getBytes()); + } + if (dumpTitleChanges) + os.write(mTitleChanges.toString().getBytes()); + if (mDialogStrings != null) + os.write(mDialogStrings.toString().getBytes()); + mDialogStrings = null; + os.write(webkitData.getBytes()); + os.flush(); + os.close(); + } catch (FileNotFoundException ex) { + ex.printStackTrace(); + } catch (IOException ex) { + ex.printStackTrace(); + } + + if (mProcessStack != null) + loadNextPage(); + else + finished(); + } + + // Wrap up + public void failedCase(String file, String reason) { + Log.i("Layout test:", file + " failed" + reason); + mResultRecorder.failed(file, reason); + + file = file + ".html"; + String bugNumber = FileFilter.isKnownBug(file); + if (bugNumber != null) { + System.out.println("FAIL known:"+bugNumber+ " "+file+reason); + return; + } + if (FileFilter.ignoreResults(file)) { + return; + } + System.out.println("FAIL: "+file+reason); + } + + public void passedCase(String file) { + // Add the result to the sqlite database + Log.i("Layout test:", file + " passed"); + mResultRecorder.passed(file); + + file = file + ".html"; + String bugNumber = FileFilter.isKnownBug(file); + if (bugNumber != null) { + System.out.println("Bug Fixed: "+bugNumber+ " "+file); + return; + } + + if (FileFilter.ignoreResults(file)) { + System.out.println("Ignored test passed: "+file); + return; + } + } + + public void setCallback(HTMLHostCallbackInterface callback) { + mCallback = callback; + } + + public void finished() { + int passed = 0; + while (!mFinishedStack.empty()) { + Log.v(LOGTAG, "Comparing dump and reference"); + String file = (String)mFinishedStack.pop(); + + // Only check results that we can check, ie dumpAsText results + String dumpFile = file + "-results.txt"; + File f = new File(dumpFile); + if (f.exists()) { + try { + FileInputStream fr = new FileInputStream(file+"-results.txt"); + FileInputStream fe = new FileInputStream(file+"-expected.txt"); + + mResultRecorder.started(file); + + // If the length is different then they are different + int diff = fe.available() - fr.available(); + if (diff > 1 || diff < 0) { + failedCase(file, " different length"); + fr.close(); + fe.close(); + + mResultRecorder.finished(file); + continue; + } + byte[] br = new byte[fr.available()]; + byte[] be = new byte[fe.available()]; + fr.read(br); + fe.read(be); + boolean fail = false; + for (int i = 0; i < br.length; i++) { + if (br[i] != be[i]) { + failedCase(file, " @offset: "+i); + fail = true; + break; + } + } + if (br.length != be.length && be[be.length-1] == '\n') { + Log.d(LOGTAG, "Extra new line being ignore:" + file); + } + fr.close(); + fe.close(); + if (!fail) { + passed++; + passedCase(file); + } + } catch (FileNotFoundException ex) { + // TODO do something here + } catch (IOException ex) { + // Failed on available() or read() + } + mResultRecorder.finished(file); + } + } + + if (mCallback != null) { + mCallback.waitForFinish(); + } + + finish(); + } + + // LayoutTestController Functions + public void dumpAsText() { + dumpAsText = true; + String url = mWebView.getUrl(); + Log.v(LOGTAG, "dumpAsText called:"+url); + if (url.length() > 60) + url = url.substring(60); + } + + public void waitUntilDone() { + waitToDump = true; + } + public void notifyDone() { + waitToDump = false; + mChromeClient.onProgressChanged(mWebView, 100); + } + public void display() { + mWebView.invalidate(); + } + + public void clearBackForwardList() { + mWebView.clearHistory(); + + } + + public void dumpBackForwardList() { + //printf("\n============== Back Forward List ==============\n"); + // mWebHistory + //printf("===============================================\n"); + + } + + public void dumpChildFrameScrollPositions() { + // TODO Auto-generated method stub + + } + + public void dumpEditingCallbacks() { + // TODO Auto-generated method stub + + } + + public void dumpSelectionRect() { + // TODO Auto-generated method stub + + } + + public void dumpTitleChanges() { + if (!dumpTitleChanges) { + mTitleChanges = new StringBuffer(); + } + dumpTitleChanges = true; + } + + public void keepWebHistory() { + if (!keepWebHistory) { + mWebHistory = new Vector(); + } + keepWebHistory = true; + + } + + public void queueBackNavigation(int howfar) { + // TODO Auto-generated method stub + + } + + public void queueForwardNavigation(int howfar) { + // TODO Auto-generated method stub + + } + + public void queueLoad(String Url, String frameTarget) { + // TODO Auto-generated method stub + + } + + public void queueReload() { + mWebView.reload(); + } + + public void queueScript(String scriptToRunInCurrentContext) { + mWebView.loadUrl("javascript:"+scriptToRunInCurrentContext); + } + + public void repaintSweepHorizontally() { + // TODO Auto-generated method stub + + } + + public void setAcceptsEditing(boolean b) { + // TODO Auto-generated method stub + + } + + public void setMainFrameIsFirstResponder(boolean b) { + // TODO Auto-generated method stub + + } + + public void setWindowIsKey(boolean b) { + // This is meant to show/hide the window. The best I can find + // is setEnabled() + mWebView.setEnabled(b); + } + + public void testRepaint() { + mWebView.invalidate(); + } + + // Instrumentation calls this to find + // if the activity has finished running the layout tests + public boolean hasFinishedRunning() { + if( mProcessStack == null || mFinishedStack == null) + return false; + + if (mProcessStack.isEmpty() && mFinishedStack.empty()) { + return true; + } + + return false; + } + + private final WebChromeClient mChromeClient = new WebChromeClient() { + @Override + public void onProgressChanged(WebView view, int newProgress) { + if (newProgress == 100) { + pageComplete = true; + String url = mWebView.getUrl(); + if (url != null) { + Log.v(LOGTAG, "Finished: "+ url); + if (url.length() > 60) + url = url.substring(60); + scheduleDump(); + } + } + } + + @Override + public void onReceivedTitle(WebView view, String title) { + if (title.length() > 30) + title = "..."+title.substring(title.length()-30); + setTitle(title); + if (dumpTitleChanges) { + mTitleChanges.append("TITLE CHANGED: "); + mTitleChanges.append(title); + mTitleChanges.append("\n"); + } + } + + @Override + public boolean onJsAlert(WebView view, String url, String message, + JsResult result) { + if (mDialogStrings == null) { + mDialogStrings = new StringBuffer(); + } + mDialogStrings.append("ALERT: "); + mDialogStrings.append(message); + mDialogStrings.append('\n'); + return false; + } + }; + + private WebView mWebView; + private WebViewEventSender eventSender; + private Vector mProcessStack; + private Stack mFinishedStack; + static final String LOGTAG="DumpRenderTree"; + private String mCurrentFile; + private int mOutstandingLoads; + private AsyncHandler mHandler; + private boolean mDumpRequested; + + private boolean dumpAsText; + private boolean waitToDump; + private boolean pageComplete; + + private boolean dumpTitleChanges; + private StringBuffer mTitleChanges; + + private StringBuffer mDialogStrings; + + private boolean keepWebHistory; + private Vector mWebHistory; + + private boolean mTimedOut; + + static final int MSG_DUMP = 0; + static final int MSG_TIMEOUT = 1; + static final int MSG_WEBKIT_DATA = 2; + +} |