diff options
Diffstat (limited to 'tests/DumpRenderTree')
5 files changed, 439 insertions, 38 deletions
diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java index a03490d..2eecef8 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/LayoutTestsAutoTest.java @@ -16,6 +16,9 @@ package com.android.dumprendertree; +import com.android.dumprendertree.forwarder.AdbUtils; +import com.android.dumprendertree.forwarder.ForwardServer; + import android.app.Instrumentation; import android.content.Intent; import android.os.Bundle; @@ -42,7 +45,7 @@ class MyTestRecorder { private BufferedOutputStream mBufferedOutputFailedStream; private BufferedOutputStream mBufferedOutputNoresultStream; private BufferedOutputStream mBufferedOutputTimedoutStream; - + public void passed(String layout_file) { try { mBufferedOutputPassedStream.write(layout_file.getBytes()); @@ -52,7 +55,7 @@ class MyTestRecorder { e.printStackTrace(); } } - + public void failed(String layout_file) { try { mBufferedOutputFailedStream.write(layout_file.getBytes()); @@ -62,7 +65,7 @@ class MyTestRecorder { e.printStackTrace(); } } - + public void noresult(String layout_file) { try { mBufferedOutputNoresultStream.write(layout_file.getBytes()); @@ -72,7 +75,7 @@ class MyTestRecorder { e.printStackTrace(); } } - + public void timedout(String url) { try { mBufferedOutputTimedoutStream.write(url.getBytes()); @@ -82,14 +85,14 @@ class MyTestRecorder { e.printStackTrace(); } } - + public MyTestRecorder(boolean resume) { try { File resultsPassedFile = new File("/sdcard/layout_tests_passed.txt"); File resultsFailedFile = new File("/sdcard/layout_tests_failed.txt"); File noExpectedResultFile = new File("/sdcard/layout_tests_nontext.txt"); File resultTimedoutFile = new File("/sdcard/layout_tests_timedout.txt"); - + mBufferedOutputPassedStream = new BufferedOutputStream(new FileOutputStream(resultsPassedFile, resume)); mBufferedOutputFailedStream = @@ -102,7 +105,7 @@ class MyTestRecorder { e.printStackTrace(); } } - + public void close() { try { mBufferedOutputPassedStream.close(); @@ -120,7 +123,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh private static final String LOGTAG = "LayoutTests"; static final int DEFAULT_TIMEOUT_IN_MILLIS = 5000; - + static final String LAYOUT_TESTS_ROOT = "/sdcard/android/layout_tests/"; static final String LAYOUT_TESTS_RESULT_DIR = "/sdcard/android/layout_tests_results/"; static final String ANDROID_EXPECTED_RESULT_DIR = "/sdcard/android/expected_results/"; @@ -139,14 +142,35 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh static final String LAYOUT_RESULTS_CRASHED_RESULT_FILE = "results/layout_tests_crashed.txt"; static final String LAYOUT_TESTS_RUNNER = "run_layout_tests.py"; + static final String HTTP_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/"; + static final String HTTPS_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/ssl/"; + static final String HTTP_LOCAL_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/local/"; + static final String HTTP_MEDIA_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/media/"; + static final String HTTP_WML_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/wml/"; + + + private ForwardServer fs8000, fs8080, fs8443; + private MyTestRecorder mResultRecorder; private Vector<String> mTestList; private boolean mRebaselineResults; private String mTestPathPrefix; private boolean mFinished; - + public LayoutTestsAutoTest() { super("com.android.dumprendertree", TestShellActivity.class); + + int addr = -1; + try { + addr = AdbUtils.resolve("android-browser-test.mtv.corp.google.com"); + } catch (IOException ioe) { + Log.e(LOGTAG, "failed to resolve server address.", ioe); + } + if(addr != -1) { + fs8000 = new ForwardServer(8000, addr, 8000); + fs8080 = new ForwardServer(8080, addr, 8080); + fs8443 = new ForwardServer(8443, addr, 8443); + } } // This function writes the result of the layout test to @@ -157,7 +181,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh bundle.putBoolean(file, result); inst.sendStatus(0, bundle); } - + private void getTestList() { // Read test list. try { @@ -174,7 +198,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh Log.e(LOGTAG, "Error while reading test list : " + e.getMessage()); } } - + private void resumeTestList() { // read out the test name it stoped last time. try { @@ -189,7 +213,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE); } } - + private void clearTestStatus() { // Delete TEST_STATUS_FILE try { @@ -208,13 +232,13 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh // Write actual results to result directory. return shortName.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_RESULT_DIR) + "-result.txt"; } - + private String getExpectedResultFile(String test) { int pos = test.lastIndexOf('.'); if(pos == -1) return null; String shortName = test.substring(0, pos); - return shortName + "-expected.txt"; + return shortName + "-expected.txt"; } private String getAndroidExpectedResultFile(String expectedResultFile) { @@ -224,7 +248,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh // Wrap up private void failedCase(String file) { Log.w("Layout test: ", file + " failed"); - mResultRecorder.failed(file); + mResultRecorder.failed(file); } private void passedCase(String file) { @@ -236,7 +260,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh Log.v("Layout test:", file + " no expected result"); mResultRecorder.noresult(file); } - + private void processResult(String testFile, String actualResultFile, String expectedResultFile) { Log.v(LOGTAG, " Processing result: " + testFile); @@ -257,13 +281,13 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh break; } } - + if (passing) { passedCase(testFile); } else { failedCase(testFile); } - + fe.close(); fr.close(); } catch (FileNotFoundException ex) { @@ -278,7 +302,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh noresultCase(testFile); } } - + private void runTestAndWaitUntilDone(TestShellActivity activity, String test, int timeout) { activity.setCallback(new TestShellCallback() { public void finished() { @@ -287,7 +311,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh LayoutTestsAutoTest.this.notifyAll(); } } - + public void timedOut(String url) { } }); @@ -306,16 +330,16 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh resultFile = getAndroidExpectedResultFile(expectedResultFile); } - + mFinished = false; Intent intent = new Intent(Intent.ACTION_VIEW); intent.setClass(activity, TestShellActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); - intent.putExtra(TestShellActivity.TEST_URL, "file://" + test); + intent.putExtra(TestShellActivity.TEST_URL, getTestUrl(test)); intent.putExtra(TestShellActivity.RESULT_FILE, resultFile); intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout); activity.startActivity(intent); - + // Wait until done. synchronized (this) { while(!mFinished){ @@ -324,18 +348,18 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh } catch (InterruptedException e) { } } } - + if (!mRebaselineResults) { String expectedResultFile = getExpectedResultFile(test); File f = new File(expectedResultFile); if (!f.exists()) { expectedResultFile = getAndroidExpectedResultFile(expectedResultFile); } - + processResult(test, resultFile, expectedResultFile); } - } - + } + // Invokes running of layout tests // and waits till it has finished running. public void executeLayoutTests(boolean resume) { @@ -348,28 +372,28 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh } this.mTestList = new Vector<String>(); - + // Read settings try { this.mTestPathPrefix = (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getCanonicalPath(); - } catch (IOException e) { + } catch (IOException e) { Log.e(LOGTAG, "Cannot find test path prefix: " + e.getMessage()); return; } - + this.mRebaselineResults = runner.mRebaseline; - + int timeout = runner.mTimeoutInMillis; if (timeout <= 0) { timeout = DEFAULT_TIMEOUT_IN_MILLIS; } - + this.mResultRecorder = new MyTestRecorder(resume); - + if (!resume) clearTestStatus(); - + getTestList(); if (resume) resumeTestList(); @@ -377,6 +401,15 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh TestShellActivity activity = (TestShellActivity) getActivity(); // Run tests. + int addr = -1; + try{ + addr = AdbUtils.resolve("android-browser-test.mtv.corp.google.com"); + } catch (IOException ioe) { + Log.w(LOGTAG, "error while resolving test host name", ioe); + } + if(addr == -1) { + Log.w(LOGTAG, "failed to resolve test host. http tests will fail."); + } for (int i = 0; i < mTestList.size(); i++) { String s = mTestList.elementAt(i); FsUtils.updateTestStatus(TEST_STATUS_FILE, s); @@ -385,10 +418,48 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh } FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE"); - + if(fs8000 != null) + fs8000.stop(); + if(fs8080 != null) + fs8080.stop(); + if(fs8443 != null) + fs8443.stop(); + activity.finish(); } + private void startForwardServerIfNeeded() { + try { + if(fs8000 != null) + fs8000.start(); + if(fs8080 != null) + fs8080.start(); + if(fs8443 != null) + fs8443.start(); + } catch (IOException ioe) { + Log.w(LOGTAG, "failed to start forwarder. http tests will fail.", ioe); + } + } + + private String getTestUrl(String path) { + String url = null; + if (!path.startsWith(HTTP_TESTS_PREFIX)) { + url = "file://" + path; + } else { + startForwardServerIfNeeded(); + if (path.startsWith(HTTPS_TESTS_PREFIX)) { + // still cut the URL after "http/tests/" + url = "https://127.0.0.1:8443/" + path.substring(HTTP_TESTS_PREFIX.length()); + } else if (!path.startsWith(HTTP_LOCAL_TESTS_PREFIX) + && !path.startsWith(HTTP_MEDIA_TESTS_PREFIX) + && !path.startsWith(HTTP_WML_TESTS_PREFIX)) { + url = "http://127.0.0.1:8000/" + path.substring(HTTP_TESTS_PREFIX.length()); + } else { + url = "file://" + path; + } + } + return url; + } private String getTestPath() { LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation(); @@ -403,10 +474,10 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh Log.e("LayoutTestsAutoTest", "Cannot get cannonical path " + e.getMessage()); } Log.v("LayoutTestsAutoTest", " Test path : " + test_path); - + return test_path; } - + public void generateTestList() { try { File tests_list = new File(LAYOUT_TESTS_LIST_FILE); @@ -431,7 +502,7 @@ public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestSh } catch (Exception e) { e.printStackTrace(); } - + executeLayoutTests(false); } diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java index 48c1e5d..4483a8e 100644 --- a/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/TestShellActivity.java @@ -531,6 +531,12 @@ public class TestShellActivity extends Activity implements LayoutTestController } @Override + public boolean onJsTimeout() { + Log.v(LOGTAG, "JavaScript timeout"); + return false; + } + + @Override public void onExceededDatabaseQuota(String url_str, String databaseIdentifier, long currentQuota, WebStorage.QuotaUpdater callback) { @@ -614,6 +620,9 @@ public class TestShellActivity extends Activity implements LayoutTestController } WebSettings settings = webview.getSettings(); + settings.setAppCacheEnabled(true); + settings.setAppCachePath(getApplicationContext().getCacheDir().getPath()); + settings.setAppCacheMaxSize(Long.MAX_VALUE); settings.setJavaScriptEnabled(true); settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setSupportMultipleWindows(true); diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java new file mode 100644 index 0000000..9a3e9c2 --- /dev/null +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/AdbUtils.java @@ -0,0 +1,112 @@ +package com.android.dumprendertree.forwarder; + +import android.util.Log; + +import java.io.DataInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +public class AdbUtils { + + private static final String ADB_OK = "OKAY"; + private static final int ADB_PORT = 5037; + private static final String ADB_HOST = "127.0.0.1"; + private static final int ADB_RESPONSE_SIZE = 4; + + private static final String LOGTAG = "AdbUtils"; + + /** + * + * Convert integer format IP into xxx.xxx.xxx.xxx format + * + * @param host IP address in integer format + * @return human readable format + */ + public static String convert(int host) { + return ((host >> 24) & 0xFF) + "." + + ((host >> 16) & 0xFF) + "." + + ((host >> 8) & 0xFF) + "." + + (host & 0xFF); + } + + /** + * + * Resolve DNS name into IP address + * + * @param host DNS name + * @return IP address in integer format + * @throws IOException + */ + public static int resolve(String host) throws IOException { + Socket localSocket = new Socket(ADB_HOST, ADB_PORT); + DataInputStream dis = new DataInputStream(localSocket.getInputStream()); + OutputStream os = localSocket.getOutputStream(); + int count_read = 0; + byte[] buf = new byte[128]; + + if (localSocket == null || dis == null || os == null) + return -1; + String cmd = "dns:" + host; + + if(!sendAdbCmd(dis, os, cmd)) + return -1; + + count_read = dis.readInt(); + localSocket.close(); + return count_read; + } + + /** + * + * Send an ADB command using existing socket connection + * + * the streams provided must be from a socket connected to adbd already + * + * @param is input stream of the socket connection + * @param os output stream of the socket + * @param cmd the adb command to send + * @return if adb gave a success response + * @throws IOException + */ + public static boolean sendAdbCmd(InputStream is, OutputStream os, + String cmd) throws IOException { + byte[] buf = new byte[ADB_RESPONSE_SIZE]; + + cmd = String.format("%04X", cmd.length()) + cmd; + os.write(cmd.getBytes()); + int read = is.read(buf); + if(read != ADB_RESPONSE_SIZE || !ADB_OK.equals(new String(buf))) { + Log.w(LOGTAG, "adb cmd faild."); + return false; + } + return true; + } + + /** + * + * Get a tcp socket connection to specified IP address and port proxied by adb + * + * The proxying is transparent, e.g. if a socket is returned, then it can be written to and + * read from as if it is directly connected to the target + * + * @param remoteAddress IP address of the host to connect to + * @param remotePort port of the host to connect to + * @return a valid Socket instance if successful, null otherwise + */ + public static Socket getForwardedSocket(int remoteAddress, int remotePort) { + try { + Socket socket = new Socket(ADB_HOST, ADB_PORT); + String cmd = "tcp:" + remotePort + ":" + convert(remoteAddress); + if(!sendAdbCmd(socket.getInputStream(), socket.getOutputStream(), cmd)) { + socket.close(); + return null; + } + return socket; + } catch (IOException ioe) { + Log.w(LOGTAG, "error creating adb socket", ioe); + return null; + } + } +} diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java new file mode 100644 index 0000000..74e018e --- /dev/null +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/ForwardServer.java @@ -0,0 +1,117 @@ +package com.android.dumprendertree.forwarder; + +import android.util.Log; + +import java.io.IOException; +import java.net.ServerSocket; +import java.net.Socket; +import java.util.HashSet; +import java.util.Set; + +/** + * + * A port forwarding server. Listens at specified local port and forward the tcp communications to + * external host/port via adb networking proxy. + * + */ +public class ForwardServer { + + private static final String LOGTAG = "ForwardServer"; + + private int remotePort; + private int remoteAddress; + private int localPort; + private ServerSocket serverSocket; + private boolean started; + + private Set<Forwarder> forwarders; + + public ForwardServer(int localPort, int remoteAddress, int remotePort) { + this.localPort = localPort; + this.remoteAddress = remoteAddress; + this.remotePort = remotePort; + started = false; + forwarders = new HashSet<Forwarder>(); + } + + public synchronized void start() throws IOException { + if(!started) { + serverSocket = new ServerSocket(localPort); + Thread serverThread = new Thread(new ServerRunner(serverSocket)); + serverThread.setName(LOGTAG); + serverThread.start(); + started = true; + } + } + + public synchronized void stop() { + if(started) { + synchronized (forwarders) { + for(Forwarder forwarder : forwarders) + forwarder.stop(); + forwarders.clear(); + } + try { + serverSocket.close(); + } catch (IOException ioe) { + Log.v(LOGTAG, "exception while closing", ioe); + } finally { + started = false; + } + } + } + + public synchronized boolean isRunning() { + return started; + } + + private class ServerRunner implements Runnable { + + private ServerSocket socket; + + public ServerRunner(ServerSocket socket) { + this.socket = socket; + } + + public void run() { + try { + while (true) { + Socket localSocket = socket.accept(); + Socket remoteSocket = AdbUtils.getForwardedSocket(remoteAddress, remotePort); + if(remoteSocket == null) { + try { + localSocket.close(); + } catch (IOException ioe) { + Log.w(LOGTAG, "error while closing socket", ioe); + } finally { + Log.w(LOGTAG, "failed to start forwarding from " + localSocket); + } + } else { + Forwarder forwarder = new Forwarder(localSocket, remoteSocket, + ForwardServer.this); + forwarder.start(); + } + } + } catch (IOException ioe) { + return; + } + } + } + + public void register(Forwarder forwarder) { + synchronized (forwarders) { + if(!forwarders.contains(forwarder)) { + forwarders.add(forwarder); + } + } + } + + public void unregister(Forwarder recyclable) { + synchronized (forwarders) { + if(forwarders.contains(recyclable)) { + recyclable.stop(); + forwarders.remove(recyclable); + } + } + } +}
\ No newline at end of file diff --git a/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java new file mode 100644 index 0000000..e1e04a7 --- /dev/null +++ b/tests/DumpRenderTree/src/com/android/dumprendertree/forwarder/Forwarder.java @@ -0,0 +1,92 @@ +package com.android.dumprendertree.forwarder; + +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.Socket; + +/** + * + * Worker class for {@link ForwardServer}. A Forwarder will be created once the ForwardServer + * accepts an incoming connection, and it will then forward the incoming/outgoing streams to a + * connection already proxied by adb networking (see also {@link AdbUtils}). + * + */ +public class Forwarder { + + private ForwardServer server; + private Socket from, to; + + private static final String LOGTAG = "Forwarder"; + + public Forwarder (Socket from, Socket to, ForwardServer server) { + this.server = server; + this.from = from; + this.to = to; + server.register(this); + } + + public void start() { + Thread outgoing = new Thread(new SocketPipe(from, to)); + Thread incoming = new Thread(new SocketPipe(to, from)); + outgoing.setName(LOGTAG); + incoming.setName(LOGTAG); + outgoing.start(); + incoming.start(); + } + + public void stop() { + shutdown(from); + shutdown(to); + } + + private void shutdown(Socket socket) { + try { + socket.shutdownInput(); + } catch (IOException e) { + Log.v(LOGTAG, "Socket#shutdownInput", e); + } + try { + socket.shutdownOutput(); + } catch (IOException e) { + Log.v(LOGTAG, "Socket#shutdownOutput", e); + } + try { + socket.close(); + } catch (IOException e) { + Log.v(LOGTAG, "Socket#close", e); + } + } + + private class SocketPipe implements Runnable { + + private Socket in, out; + + public SocketPipe(Socket in, Socket out) { + this.in = in; + this.out = out; + } + + public void run() { + try { + int length; + InputStream is = in.getInputStream(); + OutputStream os = out.getOutputStream(); + byte[] buffer = new byte[4096]; + while ((length = is.read(buffer)) > 0) { + os.write(buffer, 0, length); + } + } catch (IOException ioe) { + } finally { + server.unregister(Forwarder.this); + } + } + + @Override + public String toString() { + return "SocketPipe{" + in + "=>" + out + "}"; + } + } +} |
