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