1a307039346a769b1fc62782ef847e71c9691838aBrett Chabot/*
2a307039346a769b1fc62782ef847e71c9691838aBrett Chabot * Copyright (C) 2012 The Android Open Source Project
3a307039346a769b1fc62782ef847e71c9691838aBrett Chabot *
4a307039346a769b1fc62782ef847e71c9691838aBrett Chabot * Licensed under the Apache License, Version 2.0 (the "License");
5a307039346a769b1fc62782ef847e71c9691838aBrett Chabot * you may not use this file except in compliance with the License.
6a307039346a769b1fc62782ef847e71c9691838aBrett Chabot * You may obtain a copy of the License at
7a307039346a769b1fc62782ef847e71c9691838aBrett Chabot *
8a307039346a769b1fc62782ef847e71c9691838aBrett Chabot *      http://www.apache.org/licenses/LICENSE-2.0
9a307039346a769b1fc62782ef847e71c9691838aBrett Chabot *
10a307039346a769b1fc62782ef847e71c9691838aBrett Chabot * Unless required by applicable law or agreed to in writing, software
11a307039346a769b1fc62782ef847e71c9691838aBrett Chabot * distributed under the License is distributed on an "AS IS" BASIS,
12a307039346a769b1fc62782ef847e71c9691838aBrett Chabot * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a307039346a769b1fc62782ef847e71c9691838aBrett Chabot * See the License for the specific language governing permissions and
14a307039346a769b1fc62782ef847e71c9691838aBrett Chabot * limitations under the License.
15a307039346a769b1fc62782ef847e71c9691838aBrett Chabot */
16a307039346a769b1fc62782ef847e71c9691838aBrett Chabotpackage com.android.test.runner.listener;
17a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
18a307039346a769b1fc62782ef847e71c9691838aBrett Chabotimport android.app.Instrumentation;
19a307039346a769b1fc62782ef847e71c9691838aBrett Chabotimport android.os.Bundle;
20a307039346a769b1fc62782ef847e71c9691838aBrett Chabotimport android.util.Log;
21a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
22a307039346a769b1fc62782ef847e71c9691838aBrett Chabotimport java.io.File;
23a307039346a769b1fc62782ef847e71c9691838aBrett Chabotimport java.io.PrintStream;
24a307039346a769b1fc62782ef847e71c9691838aBrett Chabotimport java.lang.reflect.InvocationTargetException;
25a307039346a769b1fc62782ef847e71c9691838aBrett Chabotimport java.lang.reflect.Method;
26a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
27a307039346a769b1fc62782ef847e71c9691838aBrett Chabot/**
28a307039346a769b1fc62782ef847e71c9691838aBrett Chabot * A test {@link RunListener} that generates EMMA code coverage.
29a307039346a769b1fc62782ef847e71c9691838aBrett Chabot */
30a307039346a769b1fc62782ef847e71c9691838aBrett Chabotpublic class CoverageListener extends InstrumentationRunListener {
31a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
32a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    private String mCoverageFilePath;
33a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
34a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    /**
35a307039346a769b1fc62782ef847e71c9691838aBrett Chabot     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
36a307039346a769b1fc62782ef847e71c9691838aBrett Chabot     * identifies the path to the generated code coverage file.
37a307039346a769b1fc62782ef847e71c9691838aBrett Chabot     */
38a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    private static final String REPORT_KEY_COVERAGE_PATH = "coverageFilePath";
39a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    // Default file name for code coverage
40a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    private static final String DEFAULT_COVERAGE_FILE_NAME = "coverage.ec";
41a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
42a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    private static final String LOG_TAG = null;
43a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
44a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    /**
45a307039346a769b1fc62782ef847e71c9691838aBrett Chabot     * Creates a {@link CoverageListener).
46a307039346a769b1fc62782ef847e71c9691838aBrett Chabot     *
47a307039346a769b1fc62782ef847e71c9691838aBrett Chabot     * @param instr the {@link Instrumentation} that the test is running under
48a307039346a769b1fc62782ef847e71c9691838aBrett Chabot     * @param customCoverageFilePath an optional user specified path for the coverage file
49a307039346a769b1fc62782ef847e71c9691838aBrett Chabot     *         If null, file will be generated in test app's file directory.
50a307039346a769b1fc62782ef847e71c9691838aBrett Chabot     */
51a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    public CoverageListener(Instrumentation instr, String customCoverageFilePath) {
52a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        super(instr);
53a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        mCoverageFilePath = customCoverageFilePath;
54a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        if (mCoverageFilePath == null) {
55a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            mCoverageFilePath = instr.getTargetContext().getFilesDir().getAbsolutePath() +
56a307039346a769b1fc62782ef847e71c9691838aBrett Chabot                    File.separator + DEFAULT_COVERAGE_FILE_NAME;
57a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        }
58a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    }
59a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
60a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    @Override
61a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    public void instrumentationRunFinished(PrintStream writer, Bundle results) {
62a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        generateCoverageReport(writer, results);
63a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    }
64a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
65a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    private void generateCoverageReport(PrintStream writer, Bundle results) {
66a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        // use reflection to call emma dump coverage method, to avoid
67a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        // always statically compiling against emma jar
68a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        java.io.File coverageFile = new java.io.File(mCoverageFilePath);
69a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        try {
70a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
71a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData",
72a307039346a769b1fc62782ef847e71c9691838aBrett Chabot                    coverageFile.getClass(), boolean.class, boolean.class);
73a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
74a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            dumpCoverageMethod.invoke(null, coverageFile, false, false);
75a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
76a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            // output path to generated coverage file so it can be parsed by a test harness if
77a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            // needed
78a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            results.putString(REPORT_KEY_COVERAGE_PATH, mCoverageFilePath);
79a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            // also output a more user friendly msg
80a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            writer.format("\nGenerated code coverage data to %s",mCoverageFilePath);
81a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        } catch (ClassNotFoundException e) {
82a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            reportEmmaError(writer, "Is emma jar on classpath?", e);
83a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        } catch (SecurityException e) {
84a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            reportEmmaError(writer, e);
85a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        } catch (NoSuchMethodException e) {
86a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            reportEmmaError(writer, e);
87a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        } catch (IllegalArgumentException e) {
88a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            reportEmmaError(writer, e);
89a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        } catch (IllegalAccessException e) {
90a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            reportEmmaError(writer, e);
91a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        } catch (InvocationTargetException e) {
92a307039346a769b1fc62782ef847e71c9691838aBrett Chabot            reportEmmaError(writer, e);
93a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        }
94a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    }
95a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
96a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    private void reportEmmaError(PrintStream writer, Exception e) {
97a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        reportEmmaError(writer, "", e);
98a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    }
99a307039346a769b1fc62782ef847e71c9691838aBrett Chabot
100a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    private void reportEmmaError(PrintStream writer, String hint, Exception e) {
101a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        String msg = "Failed to generate emma coverage. " + hint;
102a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        Log.e(LOG_TAG, msg, e);
103a307039346a769b1fc62782ef847e71c9691838aBrett Chabot        writer.format("\nError: %s", msg);
104a307039346a769b1fc62782ef847e71c9691838aBrett Chabot    }
105a307039346a769b1fc62782ef847e71c9691838aBrett Chabot}
106