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