LayoutTestsAutoTest.java revision b937ba138fd75116cb773405e34c1312353b6b57
1/* 2 * Copyright (C) 2008 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 com.android.dumprendertree.TestShellActivity.DumpDataType; 20import com.android.dumprendertree.forwarder.AdbUtils; 21import com.android.dumprendertree.forwarder.ForwardServer; 22 23import android.app.Instrumentation; 24import android.content.Intent; 25import android.os.Bundle; 26import android.test.ActivityInstrumentationTestCase2; 27import android.util.Log; 28 29import java.io.BufferedOutputStream; 30import java.io.BufferedReader; 31import java.io.File; 32import java.io.FileNotFoundException; 33import java.io.FileOutputStream; 34import java.io.FileReader; 35import java.io.IOException; 36import java.io.InputStream; 37import java.io.OutputStream; 38import java.util.Vector; 39 40//TestRecorder creates two files, one for passing tests 41//and another for failing tests and writes the paths to 42//layout tests one line at a time. TestRecorder does not 43//have ability to clear the results. 44class MyTestRecorder { 45 private BufferedOutputStream mBufferedOutputPassedStream; 46 private BufferedOutputStream mBufferedOutputFailedStream; 47 private BufferedOutputStream mBufferedOutputNoresultStream; 48 private BufferedOutputStream mBufferedOutputTimedoutStream; 49 50 public void passed(String layout_file) { 51 try { 52 mBufferedOutputPassedStream.write(layout_file.getBytes()); 53 mBufferedOutputPassedStream.write('\n'); 54 mBufferedOutputPassedStream.flush(); 55 } catch(Exception e) { 56 e.printStackTrace(); 57 } 58 } 59 60 public void failed(String layout_file) { 61 try { 62 mBufferedOutputFailedStream.write(layout_file.getBytes()); 63 mBufferedOutputFailedStream.write('\n'); 64 mBufferedOutputFailedStream.flush(); 65 } catch(Exception e) { 66 e.printStackTrace(); 67 } 68 } 69 70 public void noresult(String layout_file) { 71 try { 72 mBufferedOutputNoresultStream.write(layout_file.getBytes()); 73 mBufferedOutputNoresultStream.write('\n'); 74 mBufferedOutputNoresultStream.flush(); 75 } catch(Exception e) { 76 e.printStackTrace(); 77 } 78 } 79 80 public void timedout(String url) { 81 try { 82 mBufferedOutputTimedoutStream.write(url.getBytes()); 83 mBufferedOutputTimedoutStream.write('\n'); 84 mBufferedOutputTimedoutStream.flush(); 85 } catch (Exception e) { 86 e.printStackTrace(); 87 } 88 } 89 90 public MyTestRecorder(boolean resume) { 91 try { 92 File resultsPassedFile = new File("/sdcard/layout_tests_passed.txt"); 93 File resultsFailedFile = new File("/sdcard/layout_tests_failed.txt"); 94 File noExpectedResultFile = new File("/sdcard/layout_tests_nontext.txt"); 95 File resultTimedoutFile = new File("/sdcard/layout_tests_timedout.txt"); 96 97 mBufferedOutputPassedStream = 98 new BufferedOutputStream(new FileOutputStream(resultsPassedFile, resume)); 99 mBufferedOutputFailedStream = 100 new BufferedOutputStream(new FileOutputStream(resultsFailedFile, resume)); 101 mBufferedOutputNoresultStream = 102 new BufferedOutputStream(new FileOutputStream(noExpectedResultFile, resume)); 103 mBufferedOutputTimedoutStream = 104 new BufferedOutputStream(new FileOutputStream(resultTimedoutFile, resume)); 105 } catch (Exception e) { 106 e.printStackTrace(); 107 } 108 } 109 110 public void close() { 111 try { 112 mBufferedOutputPassedStream.close(); 113 mBufferedOutputFailedStream.close(); 114 mBufferedOutputNoresultStream.close(); 115 mBufferedOutputTimedoutStream.close(); 116 } catch (Exception e) { 117 e.printStackTrace(); 118 } 119 } 120} 121 122 123public class LayoutTestsAutoTest extends ActivityInstrumentationTestCase2<TestShellActivity> { 124 125 private static final String LOGTAG = "LayoutTests"; 126 static final int DEFAULT_TIMEOUT_IN_MILLIS = 5000; 127 128 static final String LAYOUT_TESTS_ROOT = "/sdcard/android/layout_tests/"; 129 static final String LAYOUT_TESTS_RESULT_DIR = "/sdcard/android/layout_tests_results/"; 130 static final String ANDROID_EXPECTED_RESULT_DIR = "/sdcard/android/expected_results/"; 131 static final String LAYOUT_TESTS_LIST_FILE = "/sdcard/android/layout_tests_list.txt"; 132 static final String TEST_STATUS_FILE = "/sdcard/android/running_test.txt"; 133 static final String LAYOUT_TESTS_RESULTS_REFERENCE_FILES[] = { 134 "results/layout_tests_passed.txt", 135 "results/layout_tests_failed.txt", 136 "results/layout_tests_nontext.txt", 137 "results/layout_tests_crashed.txt", 138 "run_layout_tests.py" 139 }; 140 141 static final String LAYOUT_RESULTS_FAILED_RESULT_FILE = "results/layout_tests_failed.txt"; 142 static final String LAYOUT_RESULTS_NONTEXT_RESULT_FILE = "results/layout_tests_nontext.txt"; 143 static final String LAYOUT_RESULTS_CRASHED_RESULT_FILE = "results/layout_tests_crashed.txt"; 144 static final String LAYOUT_TESTS_RUNNER = "run_layout_tests.py"; 145 146 static final String HTTP_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/"; 147 static final String HTTPS_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/ssl/"; 148 static final String HTTP_LOCAL_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/local/"; 149 static final String HTTP_MEDIA_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/media/"; 150 static final String HTTP_WML_TESTS_PREFIX = "/sdcard/android/layout_tests/http/tests/wml/"; 151 152 153 static final String DEFAULT_TEST_HOST = "android-browser-test.mtv.corp.google.com"; 154 static final String FORWARD_HOST_CONF = "/sdcard/drt_forward_host.txt"; 155 private ForwardServer fs8000, fs8080, fs8443; 156 157 private MyTestRecorder mResultRecorder; 158 private Vector<String> mTestList; 159 private boolean mRebaselineResults; 160 private String mTestPathPrefix; 161 private boolean mFinished; 162 163 public LayoutTestsAutoTest() { 164 super("com.android.dumprendertree", TestShellActivity.class); 165 166 int addr = getForwardHostAddr(); 167 if(addr != -1) { 168 fs8000 = new ForwardServer(8000, addr, 8000); 169 fs8080 = new ForwardServer(8080, addr, 8080); 170 fs8443 = new ForwardServer(8443, addr, 8443); 171 } 172 } 173 174 private int getForwardHostAddr() { 175 int addr = -1; 176 String host = null; 177 File forwardHostConf = new File(FORWARD_HOST_CONF); 178 if (forwardHostConf.isFile()) { 179 BufferedReader hostReader = null; 180 try { 181 hostReader = new BufferedReader(new FileReader(forwardHostConf)); 182 host = hostReader.readLine(); 183 Log.v(LOGTAG, "read forward host from file: " + host); 184 } catch (IOException ioe) { 185 Log.v(LOGTAG, "cannot read forward host from file", ioe); 186 } finally { 187 if (hostReader != null) { 188 try { 189 hostReader.close(); 190 } catch (IOException ioe) { 191 // burn!!! 192 } 193 } 194 } 195 } 196 if (host == null || host.length() == 0) 197 host = DEFAULT_TEST_HOST; 198 try { 199 addr = AdbUtils.resolve(host); 200 } catch (IOException ioe) { 201 Log.e(LOGTAG, "failed to resolve server address", ioe); 202 } 203 return addr; 204 } 205 206 // This function writes the result of the layout test to 207 // Am status so that it can be picked up from a script. 208 private void passOrFailCallback(String file, boolean result) { 209 Instrumentation inst = getInstrumentation(); 210 Bundle bundle = new Bundle(); 211 bundle.putBoolean(file, result); 212 inst.sendStatus(0, bundle); 213 } 214 215 private void getTestList() { 216 // Read test list. 217 try { 218 BufferedReader inReader = new BufferedReader(new FileReader(LAYOUT_TESTS_LIST_FILE)); 219 String line = inReader.readLine(); 220 while (line != null) { 221 if (line.startsWith(mTestPathPrefix)) 222 mTestList.add(line); 223 line = inReader.readLine(); 224 } 225 inReader.close(); 226 Log.v(LOGTAG, "Test list has " + mTestList.size() + " test(s)."); 227 } catch (Exception e) { 228 Log.e(LOGTAG, "Error while reading test list : " + e.getMessage()); 229 } 230 } 231 232 private void resumeTestList() { 233 // read out the test name it stoped last time. 234 try { 235 String line = FsUtils.readTestStatus(TEST_STATUS_FILE); 236 for (int i = 0; i < mTestList.size(); i++) { 237 if (mTestList.elementAt(i).equals(line)) { 238 mTestList = new Vector<String>(mTestList.subList(i+1, mTestList.size())); 239 break; 240 } 241 } 242 } catch (Exception e) { 243 Log.e(LOGTAG, "Error reading " + TEST_STATUS_FILE); 244 } 245 } 246 247 private void clearTestStatus() { 248 // Delete TEST_STATUS_FILE 249 try { 250 File f = new File(TEST_STATUS_FILE); 251 if (f.delete()) 252 Log.v(LOGTAG, "Deleted " + TEST_STATUS_FILE); 253 else 254 Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE); 255 } catch (Exception e) { 256 Log.e(LOGTAG, "Fail to delete " + TEST_STATUS_FILE + " : " + e.getMessage()); 257 } 258 } 259 260 private String getResultFile(String test) { 261 String shortName = test.substring(0, test.lastIndexOf('.')); 262 // Write actual results to result directory. 263 return shortName.replaceFirst(LAYOUT_TESTS_ROOT, LAYOUT_TESTS_RESULT_DIR) + "-result.txt"; 264 } 265 266 private String getExpectedResultFile(String test) { 267 int pos = test.lastIndexOf('.'); 268 if(pos == -1) 269 return null; 270 String shortName = test.substring(0, pos); 271 return shortName + "-expected.txt"; 272 } 273 274 private String getAndroidExpectedResultFile(String expectedResultFile) { 275 return expectedResultFile.replaceFirst(LAYOUT_TESTS_ROOT, ANDROID_EXPECTED_RESULT_DIR); 276 } 277 278 // Wrap up 279 private void failedCase(String file) { 280 Log.w("Layout test: ", file + " failed"); 281 mResultRecorder.failed(file); 282 } 283 284 private void passedCase(String file) { 285 Log.v("Layout test:", file + " passed"); 286 mResultRecorder.passed(file); 287 } 288 289 private void noresultCase(String file) { 290 Log.v("Layout test:", file + " no expected result"); 291 mResultRecorder.noresult(file); 292 } 293 294 private void processResult(String testFile, String actualResultFile, String expectedResultFile) { 295 Log.v(LOGTAG, " Processing result: " + testFile); 296 297 File actual = new File(actualResultFile); 298 File expected = new File(expectedResultFile); 299 if (actual.exists() && expected.exists()) { 300 try { 301 boolean passing = true; 302 BufferedReader fr = new BufferedReader(new FileReader(actual)); 303 BufferedReader fe = new BufferedReader(new FileReader(expected)); 304 while (true) { 305 String s1 = fr.readLine(); 306 String s2 = fe.readLine(); 307 if (s1 == null && s2 == null) 308 break; // both files are the same 309 if (s1 == null || s2 == null || !s1.equals(s2)) { 310 passing = false; 311 break; 312 } 313 } 314 315 if (passing) { 316 passedCase(testFile); 317 } else { 318 failedCase(testFile); 319 } 320 321 fe.close(); 322 fr.close(); 323 } catch (FileNotFoundException ex) { 324 Log.e(LOGTAG, "File not found : " + ex.getMessage()); 325 } catch (IOException ex) { 326 Log.e(LOGTAG, "IO Error : " + ex.getMessage()); 327 } 328 return; 329 } 330 331 if (!expected.exists()) { 332 noresultCase(testFile); 333 } 334 } 335 336 private void runTestAndWaitUntilDone(TestShellActivity activity, String test, int timeout) { 337 activity.setCallback(new TestShellCallback() { 338 public void finished() { 339 synchronized (LayoutTestsAutoTest.this) { 340 mFinished = true; 341 LayoutTestsAutoTest.this.notifyAll(); 342 } 343 } 344 345 public void timedOut(String url) { 346 Log.v(LOGTAG, "layout timeout: " + url); 347 } 348 }); 349 350 String resultFile = getResultFile(test); 351 if(resultFile == null) { 352 //simply ignore this test 353 return; 354 } 355 if (mRebaselineResults) { 356 String expectedResultFile = getExpectedResultFile(test); 357 File f = new File(expectedResultFile); 358 if (f.exists()) { 359 return; // don't run test and don't overwrite default tests. 360 } 361 362 resultFile = getAndroidExpectedResultFile(expectedResultFile); 363 } 364 365 mFinished = false; 366 Intent intent = new Intent(Intent.ACTION_VIEW); 367 intent.setClass(activity, TestShellActivity.class); 368 intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); 369 intent.putExtra(TestShellActivity.TEST_URL, getTestUrl(test)); 370 intent.putExtra(TestShellActivity.RESULT_FILE, resultFile); 371 intent.putExtra(TestShellActivity.TIMEOUT_IN_MILLIS, timeout); 372 activity.startActivity(intent); 373 374 // Wait until done. 375 synchronized (this) { 376 while(!mFinished){ 377 try { 378 this.wait(); 379 } catch (InterruptedException e) { } 380 } 381 } 382 383 if (!mRebaselineResults) { 384 String expectedResultFile = getExpectedResultFile(test); 385 File f = new File(expectedResultFile); 386 if (!f.exists()) { 387 expectedResultFile = getAndroidExpectedResultFile(expectedResultFile); 388 } 389 390 processResult(test, resultFile, expectedResultFile); 391 } 392 } 393 394 // Invokes running of layout tests 395 // and waits till it has finished running. 396 public void executeLayoutTests(boolean resume) { 397 LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation(); 398 // A convenient method to be called by another activity. 399 400 if (runner.mTestPath == null) { 401 Log.e(LOGTAG, "No test specified"); 402 return; 403 } 404 405 this.mTestList = new Vector<String>(); 406 407 // Read settings 408 try { 409 this.mTestPathPrefix = 410 (new File(LAYOUT_TESTS_ROOT + runner.mTestPath)).getCanonicalPath(); 411 } catch (IOException e) { 412 Log.e(LOGTAG, "Cannot find test path prefix: " + e.getMessage()); 413 return; 414 } 415 416 this.mRebaselineResults = runner.mRebaseline; 417 418 int timeout = runner.mTimeoutInMillis; 419 if (timeout <= 0) { 420 timeout = DEFAULT_TIMEOUT_IN_MILLIS; 421 } 422 423 this.mResultRecorder = new MyTestRecorder(resume); 424 425 if (!resume) 426 clearTestStatus(); 427 428 getTestList(); 429 if (resume) 430 resumeTestList(); 431 432 TestShellActivity activity = getActivity(); 433 activity.setDefaultDumpDataType(DumpDataType.DUMP_AS_TEXT); 434 435 // Run tests. 436 int addr = -1; 437 try{ 438 addr = AdbUtils.resolve("android-browser-test.mtv.corp.google.com"); 439 } catch (IOException ioe) { 440 Log.w(LOGTAG, "error while resolving test host name", ioe); 441 } 442 if(addr == -1) { 443 Log.w(LOGTAG, "failed to resolve test host. http tests will fail."); 444 } 445 for (int i = 0; i < mTestList.size(); i++) { 446 String s = mTestList.elementAt(i); 447 FsUtils.updateTestStatus(TEST_STATUS_FILE, s); 448 // Run tests 449 runTestAndWaitUntilDone(activity, s, runner.mTimeoutInMillis); 450 } 451 452 FsUtils.updateTestStatus(TEST_STATUS_FILE, "#DONE"); 453 if(fs8000 != null) 454 fs8000.stop(); 455 if(fs8080 != null) 456 fs8080.stop(); 457 if(fs8443 != null) 458 fs8443.stop(); 459 460 activity.finish(); 461 } 462 463 private void startForwardServerIfNeeded() { 464 try { 465 if(fs8000 != null) 466 fs8000.start(); 467 if(fs8080 != null) 468 fs8080.start(); 469 if(fs8443 != null) 470 fs8443.start(); 471 } catch (IOException ioe) { 472 Log.w(LOGTAG, "failed to start forwarder. http tests will fail.", ioe); 473 } 474 } 475 476 private String getTestUrl(String path) { 477 String url = null; 478 if (!path.startsWith(HTTP_TESTS_PREFIX)) { 479 url = "file://" + path; 480 } else { 481 startForwardServerIfNeeded(); 482 if (path.startsWith(HTTPS_TESTS_PREFIX)) { 483 // still cut the URL after "http/tests/" 484 url = "https://127.0.0.1:8443/" + path.substring(HTTP_TESTS_PREFIX.length()); 485 } else if (!path.startsWith(HTTP_LOCAL_TESTS_PREFIX) 486 && !path.startsWith(HTTP_MEDIA_TESTS_PREFIX) 487 && !path.startsWith(HTTP_WML_TESTS_PREFIX)) { 488 url = "http://127.0.0.1:8000/" + path.substring(HTTP_TESTS_PREFIX.length()); 489 } else { 490 url = "file://" + path; 491 } 492 } 493 return url; 494 } 495 496 private String getTestPath() { 497 LayoutTestsAutoRunner runner = (LayoutTestsAutoRunner) getInstrumentation(); 498 499 String test_path = LAYOUT_TESTS_ROOT; 500 if (runner.mTestPath != null) { 501 test_path += runner.mTestPath; 502 } 503 try { 504 test_path = new File(test_path).getCanonicalPath(); 505 } catch (IOException e) { 506 Log.e("LayoutTestsAutoTest", "Cannot get cannonical path " + e.getMessage()); 507 } 508 Log.v("LayoutTestsAutoTest", " Test path : " + test_path); 509 510 return test_path; 511 } 512 513 public void generateTestList() { 514 try { 515 File tests_list = new File(LAYOUT_TESTS_LIST_FILE); 516 BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(tests_list, false)); 517 FsUtils.findLayoutTestsRecursively(bos, getTestPath()); 518 bos.flush(); 519 bos.close(); 520 } catch (Exception e) { 521 Log.e(LOGTAG, "Error when creating test list: " + e.getMessage()); 522 } 523 } 524 525 // Running all the layout tests at once sometimes 526 // causes the dumprendertree to run out of memory. 527 // So, additional tests are added to run the tests 528 // in chunks. 529 public void startLayoutTests() { 530 try { 531 File tests_list = new File(LAYOUT_TESTS_LIST_FILE); 532 if (!tests_list.exists()) 533 generateTestList(); 534 } catch (Exception e) { 535 e.printStackTrace(); 536 } 537 538 executeLayoutTests(false); 539 } 540 541 public void resumeLayoutTests() { 542 executeLayoutTests(true); 543 } 544 545 public void copyResultsAndRunnerAssetsToCache() { 546 try { 547 String out_dir = getActivity().getApplicationContext().getCacheDir().getPath() + "/"; 548 549 for( int i=0; i< LAYOUT_TESTS_RESULTS_REFERENCE_FILES.length; i++) { 550 InputStream in = getActivity().getAssets().open(LAYOUT_TESTS_RESULTS_REFERENCE_FILES[i]); 551 OutputStream out = new FileOutputStream(out_dir + LAYOUT_TESTS_RESULTS_REFERENCE_FILES[i]); 552 553 byte[] buf = new byte[2048]; 554 int len; 555 556 while ((len = in.read(buf)) >= 0 ) { 557 out.write(buf, 0, len); 558 } 559 out.close(); 560 in.close(); 561 } 562 }catch (IOException e) { 563 e.printStackTrace(); 564 } 565 566 } 567 568} 569