1/*
2 * Copyright (C) 2012 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 */
16package com.android.test.runner.listener;
17
18import android.app.Instrumentation;
19import android.os.Bundle;
20import android.util.Log;
21
22import java.io.File;
23import java.io.PrintStream;
24import java.lang.reflect.InvocationTargetException;
25import java.lang.reflect.Method;
26
27/**
28 * A test {@link RunListener} that generates EMMA code coverage.
29 */
30public class CoverageListener extends InstrumentationRunListener {
31
32    private String mCoverageFilePath;
33
34    /**
35     * If included in the status or final bundle sent to an IInstrumentationWatcher, this key
36     * identifies the path to the generated code coverage file.
37     */
38    private static final String REPORT_KEY_COVERAGE_PATH = "coverageFilePath";
39    // Default file name for code coverage
40    private static final String DEFAULT_COVERAGE_FILE_NAME = "coverage.ec";
41
42    private static final String LOG_TAG = null;
43
44    /**
45     * Creates a {@link CoverageListener).
46     *
47     * @param instr the {@link Instrumentation} that the test is running under
48     * @param customCoverageFilePath an optional user specified path for the coverage file
49     *         If null, file will be generated in test app's file directory.
50     */
51    public CoverageListener(Instrumentation instr, String customCoverageFilePath) {
52        super(instr);
53        mCoverageFilePath = customCoverageFilePath;
54        if (mCoverageFilePath == null) {
55            mCoverageFilePath = instr.getTargetContext().getFilesDir().getAbsolutePath() +
56                    File.separator + DEFAULT_COVERAGE_FILE_NAME;
57        }
58    }
59
60    @Override
61    public void instrumentationRunFinished(PrintStream writer, Bundle results) {
62        generateCoverageReport(writer, results);
63    }
64
65    private void generateCoverageReport(PrintStream writer, Bundle results) {
66        // use reflection to call emma dump coverage method, to avoid
67        // always statically compiling against emma jar
68        java.io.File coverageFile = new java.io.File(mCoverageFilePath);
69        try {
70            Class<?> emmaRTClass = Class.forName("com.vladium.emma.rt.RT");
71            Method dumpCoverageMethod = emmaRTClass.getMethod("dumpCoverageData",
72                    coverageFile.getClass(), boolean.class, boolean.class);
73
74            dumpCoverageMethod.invoke(null, coverageFile, false, false);
75
76            // output path to generated coverage file so it can be parsed by a test harness if
77            // needed
78            results.putString(REPORT_KEY_COVERAGE_PATH, mCoverageFilePath);
79            // also output a more user friendly msg
80            writer.format("\nGenerated code coverage data to %s",mCoverageFilePath);
81        } catch (ClassNotFoundException e) {
82            reportEmmaError(writer, "Is emma jar on classpath?", e);
83        } catch (SecurityException e) {
84            reportEmmaError(writer, e);
85        } catch (NoSuchMethodException e) {
86            reportEmmaError(writer, e);
87        } catch (IllegalArgumentException e) {
88            reportEmmaError(writer, e);
89        } catch (IllegalAccessException e) {
90            reportEmmaError(writer, e);
91        } catch (InvocationTargetException e) {
92            reportEmmaError(writer, e);
93        }
94    }
95
96    private void reportEmmaError(PrintStream writer, Exception e) {
97        reportEmmaError(writer, "", e);
98    }
99
100    private void reportEmmaError(PrintStream writer, String hint, Exception e) {
101        String msg = "Failed to generate emma coverage. " + hint;
102        Log.e(LOG_TAG, msg, e);
103        writer.format("\nError: %s", msg);
104    }
105}
106