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