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