diff options
author | Guang Zhu <guangzhu@google.com> | 2009-07-23 14:19:35 -0700 |
---|---|---|
committer | Guang Zhu <guangzhu@google.com> | 2009-07-27 12:59:02 -0700 |
commit | f4bf552b5a5046e7648f405115ee48917b15b9aa (patch) | |
tree | f8a9041a6af6f1a59c198d48cb0e6cbcb0115525 /tests | |
parent | 2c3fc83ac5b267d6a551deb36b30064a2e617944 (diff) | |
download | frameworks_base-f4bf552b5a5046e7648f405115ee48917b15b9aa.zip frameworks_base-f4bf552b5a5046e7648f405115ee48917b15b9aa.tar.gz frameworks_base-f4bf552b5a5046e7648f405115ee48917b15b9aa.tar.bz2 |
checkin for port forwarding through adb, gets access to external network via USB, this also adds in related plumbing for running the http hosted tests, but will not enable those tests yet.
Diffstat (limited to 'tests')
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 + "}"; + } + } +} |