diff options
author | Ajay Nadathur <ajayns@google.com> | 2017-02-21 17:24:54 -0800 |
---|---|---|
committer | Ajay Nadathur <ajayns@google.com> | 2017-03-02 14:25:46 -0800 |
commit | ab3f5ca46e3f37ca9d16dad6894167969617237c (patch) | |
tree | eb100c40553464af01d6eea19b36c53cb8d8d2b8 | |
parent | ec93e8cd828f701dea5f92c2de11788945d5f816 (diff) | |
download | misc-ab3f5ca46e3f37ca9d16dad6894167969617237c.tar.gz |
Add code coverage support for robolectric tests
- Added targets to generate coverage reports after running robolectric
tests.
- Use suffix: ${target}-jacoco to run. For example, to run cobertura for
setupwizard tests, use:
make RunSuwRoboTests-jacoco
- Generated reports are placed under
$OUT/obj/FAKE/${project}_intermediates/coverage/
bug:32617403
Test: Ran manually, verified that reports are generated.
Change-Id: Ia1da0a642901de02bffbbc1d7a0e0d33e9ea3eab
-rw-r--r-- | common/jacoco/Android.mk | 30 | ||||
-rw-r--r-- | common/jacoco/build.gradle | 11 | ||||
-rw-r--r-- | common/jacoco/lib/NOTICE.txt | 236 | ||||
-rw-r--r-- | common/jacoco/lib/README | 6 | ||||
-rw-r--r-- | common/jacoco/lib/asm-debug-all-5.0.1.jar | bin | 0 -> 380292 bytes | |||
-rw-r--r-- | common/jacoco/lib/jacocoagent.jar | bin | 0 -> 286862 bytes | |||
-rw-r--r-- | common/jacoco/lib/org.jacoco.core-0.7.2.201409121644.jar | bin | 0 -> 132915 bytes | |||
-rw-r--r-- | common/jacoco/lib/org.jacoco.report-0.7.2.201409121644.jar | bin | 0 -> 140205 bytes | |||
-rw-r--r-- | common/jacoco/src/main/java/com/google/android/jacoco/reporter/ReportGenerator.java | 201 | ||||
-rw-r--r-- | common/robolectric/report-internal.mk | 58 | ||||
-rw-r--r-- | common/robolectric/run_robotests.mk | 108 |
11 files changed, 625 insertions, 25 deletions
diff --git a/common/jacoco/Android.mk b/common/jacoco/Android.mk new file mode 100644 index 00000000..0cc6bf6a --- /dev/null +++ b/common/jacoco/Android.mk @@ -0,0 +1,30 @@ +LOCAL_PATH:= $(call my-dir) + +include $(CLEAR_VARS) + +my_jacoco_version := 0.7.2.201409121644 + +LOCAL_SRC_FILES := \ + $(call all-java-files-under, src/main/java) \ + +LOCAL_STATIC_JAVA_LIBRARIES := \ + jvm-jacoco-core \ + jvm-jacoco-report \ + jvm-jacoco-asm \ + commons-cli-1.2 + +LOCAL_MODULE := jvm-jacoco-reporter + +include $(BUILD_HOST_JAVA_LIBRARY) + +include $(CLEAR_VARS) + +LOCAL_IS_HOST_MODULE := true +LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \ + jvm-jacoco-core:lib/org.jacoco.core-$(my_jacoco_version).jar \ + jvm-jacoco-report:lib/org.jacoco.report-$(my_jacoco_version).jar \ + jvm-jacoco-asm:lib/asm-debug-all-5.0.1.jar + +include $(BUILD_MULTI_PREBUILT) + +my_jacoco_version := diff --git a/common/jacoco/build.gradle b/common/jacoco/build.gradle new file mode 100644 index 00000000..ae2fb4dd --- /dev/null +++ b/common/jacoco/build.gradle @@ -0,0 +1,11 @@ +// NOTE: This is not used during the actual build. Added to conveniently import into intellij +apply plugin: 'java' + +sourceCompatibility = 1.8 + +def jacocoVersion = "0.7.2.201409121644" +dependencies { + compile files("lib/asm-debug-all-5.0.1.jar") + compile files("lib/org.jacoco.core-${jacocoVersion}.jar", "lib/org.jacoco.report-${jacocoVersion}.jar") + compile files("../commons-cli/commons-cli-1.2.jar") +} diff --git a/common/jacoco/lib/NOTICE.txt b/common/jacoco/lib/NOTICE.txt new file mode 100644 index 00000000..336f1f70 --- /dev/null +++ b/common/jacoco/lib/NOTICE.txt @@ -0,0 +1,236 @@ +License + +Copyright © 2009, 2016 Mountainminds GmbH & Co. KG and Contributors + +The JaCoCo Java Code Coverage Library and all included documentation is made +available by Mountainminds GmbH & Co. KG, Munich. Except indicated below, the +Content is provided to you under the terms and conditions of the Eclipse Public +License Version 1.0 ("EPL"). A copy of the EPL is provided with this Content and +is also available at http://www.eclipse.org/legal/epl-v10.html. + +Trademarks + +Java and all Java-based trademarks are trademarks of Oracle Corporation in the +United States, other countries, or both. Eclipse and all Eclipse related +trademarks and logos are trademarks of the Eclipse Foundation, Inc. OSGi is a +trademark, registered trademark, or service mark of The OSGi Alliance in the US +and other countries. Apache Ant and Apache Maven are trademarks of the Apache +Software Foundation. Android and Dalvik are trademarks of Google Inc. All other +trademarks are the property of their respective owners. + +Third Party Content + +The Content includes items that have been sourced from third parties as set out +below. + +ASM + +ASM is subject to the terms and conditions of the following license: + +Copyright (c) 2012 France Télécom +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. Neither the name of the copyright holders nor the names of its + contributors may be used to endorse or promote products derived from + this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF +THE POSSIBILITY OF SUCH DAMAGE. +Google Code Prettify + +Google Code Prettify is subject to the terms and conditions of the following license: + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. diff --git a/common/jacoco/lib/README b/common/jacoco/lib/README new file mode 100644 index 00000000..38b583ab --- /dev/null +++ b/common/jacoco/lib/README @@ -0,0 +1,6 @@ +This directory contains JAR files from the distribution of JaCoCo +(http://eclemma.org/jacoco/), version 0.7.2.201409121644. + +URL: http://search.maven.org/remotecontent?filepath=org/jacoco/jacoco/0.7.2.201409121644/jacoco-0.7.2.201409121644.zip + +Only the files that are needed have been included here. diff --git a/common/jacoco/lib/asm-debug-all-5.0.1.jar b/common/jacoco/lib/asm-debug-all-5.0.1.jar Binary files differnew file mode 100644 index 00000000..76d4b6a7 --- /dev/null +++ b/common/jacoco/lib/asm-debug-all-5.0.1.jar diff --git a/common/jacoco/lib/jacocoagent.jar b/common/jacoco/lib/jacocoagent.jar Binary files differnew file mode 100644 index 00000000..29f58bd8 --- /dev/null +++ b/common/jacoco/lib/jacocoagent.jar diff --git a/common/jacoco/lib/org.jacoco.core-0.7.2.201409121644.jar b/common/jacoco/lib/org.jacoco.core-0.7.2.201409121644.jar Binary files differnew file mode 100644 index 00000000..addca25e --- /dev/null +++ b/common/jacoco/lib/org.jacoco.core-0.7.2.201409121644.jar diff --git a/common/jacoco/lib/org.jacoco.report-0.7.2.201409121644.jar b/common/jacoco/lib/org.jacoco.report-0.7.2.201409121644.jar Binary files differnew file mode 100644 index 00000000..6152df44 --- /dev/null +++ b/common/jacoco/lib/org.jacoco.report-0.7.2.201409121644.jar diff --git a/common/jacoco/src/main/java/com/google/android/jacoco/reporter/ReportGenerator.java b/common/jacoco/src/main/java/com/google/android/jacoco/reporter/ReportGenerator.java new file mode 100644 index 00000000..c428f146 --- /dev/null +++ b/common/jacoco/src/main/java/com/google/android/jacoco/reporter/ReportGenerator.java @@ -0,0 +1,201 @@ +/* + * Copyright (C) 2017 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the + * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the specific language governing + * permissions and limitations under the License. + */ +package com.google.android.jacoco.reporter; + +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.HelpFormatter; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; +import org.apache.commons.cli.ParseException; +import org.apache.commons.cli.PosixParser; +import org.jacoco.core.analysis.Analyzer; +import org.jacoco.core.analysis.CoverageBuilder; +import org.jacoco.core.analysis.IBundleCoverage; +import org.jacoco.core.data.ExecutionDataStore; +import org.jacoco.core.tools.ExecFileLoader; +import org.jacoco.report.DirectorySourceFileLocator; +import org.jacoco.report.FileMultiReportOutput; +import org.jacoco.report.IMultiReportOutput; +import org.jacoco.report.IReportVisitor; +import org.jacoco.report.MultiReportVisitor; +import org.jacoco.report.MultiSourceFileLocator; +import org.jacoco.report.html.HTMLFormatter; +import org.jacoco.report.xml.XMLFormatter; +import org.objectweb.asm.ClassReader; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class ReportGenerator { + private static final String OPT_CLASSPATH = "classpath"; + private static final String OPT_REPORT_NAME = "name"; + private static final String OPT_EXEC_FILE = "exec-file"; + private static final String OPT_SOURCES = "srcs"; + private static final String OPT_REPORT_DIR = "report-dir"; + private static final int TAB_WIDTH = 4; + + private final Config mConfig; + + private ReportGenerator(Config config) { + mConfig = config; + } + + private void execute() { + ExecFileLoader execFileLoader = new ExecFileLoader(); + try { + execFileLoader.load(mConfig.mExecFileDir); + IReportVisitor reportVisitor = new MultiReportVisitor(getVisitors()); + reportVisitor.visitInfo(execFileLoader.getSessionInfoStore().getInfos(), + execFileLoader.getExecutionDataStore().getContents()); + MultiSourceFileLocator sourceFileLocator = new MultiSourceFileLocator(TAB_WIDTH); + mConfig.mSourceDirs.stream().filter(File::isDirectory) + .map(sourceDir -> new DirectorySourceFileLocator(sourceDir, null, TAB_WIDTH)) + .forEach(sourceFileLocator::add); + reportVisitor.visitBundle(createBundle(execFileLoader.getExecutionDataStore()), + sourceFileLocator); + reportVisitor.visitEnd(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + private IBundleCoverage createBundle(ExecutionDataStore dataStore) throws IOException { + CoverageBuilder coverageBuilder = new CoverageBuilder(); + Analyzer analyzer = new Analyzer(dataStore, coverageBuilder) { + @Override + public void analyzeClass(ClassReader reader) { + if (weHaveSourceFor(reader.getClassName())) { + super.analyzeClass(reader); + } + } + + private boolean weHaveSourceFor(String asmClassName) { + String fileName = asmClassName.replaceFirst("\\$.*", "") + ".java"; + return mConfig.mSourceDirs.stream().map(parent -> new File(parent, fileName)) + .anyMatch(File::exists); + } + }; + + for (File file : mConfig.mClasspath) { + analyzer.analyzeAll(file); + } + return coverageBuilder.getBundle(mConfig.mReportName); + } + + private List<IReportVisitor> getVisitors() throws Exception { + List<IReportVisitor> visitors = new ArrayList<>(); + visitors.add(new XMLFormatter().createVisitor(mConfig.getXmlOutputStream())); + visitors.add(new HTMLFormatter().createVisitor(mConfig.getHtmlReportOutput())); + return visitors; + } + + private static class Config { + final String mReportName; + final List<File> mClasspath; + final List<File> mSourceDirs; + final File mReportDir; + final File mExecFileDir; + + Config(String reportName, List<File> classpath, List<File> sourceDirs, + File reportDir, File execFileDir) { + mReportName = reportName; + mClasspath = classpath; + mSourceDirs = sourceDirs; + mReportDir = reportDir; + mExecFileDir = execFileDir; + } + + FileOutputStream getXmlOutputStream() throws FileNotFoundException { + File xmlFile = new File(mReportDir, "coverage.xml"); + return new FileOutputStream(xmlFile); + } + + static Config from(CommandLine commandLine) { + List<File> classpaths = parse(commandLine.getOptionValue(OPT_CLASSPATH), + "WARN: Classpath entry [%s] does not exist or is not a directory"); + List<File> sources = parse(commandLine.getOptionValue(OPT_SOURCES), + "WARN: Source entry [%s] does not exist or is not a directory"); + File execFileDir = new File(commandLine.getOptionValue(OPT_EXEC_FILE)); + ensure(execFileDir.exists() && execFileDir.canRead() && execFileDir.isFile(), + "execFile: [%s] does not exist or could not be read.", execFileDir); + + File reportDir = new File(commandLine.getOptionValue(OPT_REPORT_DIR)); + ensure(reportDir.exists() || reportDir.mkdirs(), + "Unable to create report dir [%s]", reportDir); + + return new Config(commandLine.getOptionValue(OPT_REPORT_NAME), classpaths, sources, + reportDir, execFileDir); + } + + IMultiReportOutput getHtmlReportOutput() { + return new FileMultiReportOutput(mReportDir); + } + } + + private static void ensure(boolean condition, String errorMessage, Object... args) { + if (!condition) { + System.err.printf(errorMessage, args); + System.exit(0); + } + } + + private static List<File> parse(String value, String warningMessage) { + List<File> files = new ArrayList<>(0); + for (String classpath : value.split(System.getProperty("path.separator"))) { + File file = new File(classpath); + if (file.exists()) { + files.add(file); + } else { + System.out.println(String.format(warningMessage, classpath)); + } + } + return files; + } + + private static void printHelp(ParseException e, Options options) { + System.out.println(e.getMessage()); + HelpFormatter helpFormatter = new HelpFormatter(); + helpFormatter.printHelp("java -cp ${report.jar} " + ReportGenerator.class.getName(), + "Generates jacoco reports in XML and HTML format.", options, "", true); + } + + private static void addOption(Options options, String longName, String description) { + Option option = new Option(null, longName, true, description); + option.setArgs(1); + option.setRequired(true); + options.addOption(option); + } + + public static void main(String[] args) { + Options options = new Options(); + try { + // TODO: Support multiple exec-files + addOption(options, OPT_CLASSPATH, "Classpath used during testing"); + addOption(options, OPT_EXEC_FILE, "File generated by jacoco during testing"); + addOption(options, OPT_REPORT_NAME, "Name of the project tested"); + addOption(options, OPT_REPORT_DIR, "Directory into which reports will be generated"); + addOption(options, OPT_SOURCES, "List of source directories"); + CommandLine commandLine = new PosixParser().parse(options, args); + new ReportGenerator(Config.from(commandLine)) + .execute(); + } catch (ParseException e) { + printHelp(e, options); + System.exit(1); + } + } +} diff --git a/common/robolectric/report-internal.mk b/common/robolectric/report-internal.mk new file mode 100644 index 00000000..e74fb81f --- /dev/null +++ b/common/robolectric/report-internal.mk @@ -0,0 +1,58 @@ +# Defines a target named $(my_target) for generating a coverage report. + +my_report_dir := $(my_coverage_dir)/reports +my_coverage_output := $(my_report_dir)/html + +# Private variables. +$(my_coverage_output): \ + PRIVATE_MODULE := $(LOCAL_MODULE) +$(my_coverage_output): \ + PRIVATE_COVERAGE_FILE := $(my_coverage_file) +$(my_coverage_output): \ + PRIVATE_COVERAGE_SRCS_JARS := $(my_coverage_srcs_jars) +$(my_coverage_output): \ + PRIVATE_INSTRUMENT_SOURCE_DIRS := $(my_instrument_source_dirs) +$(my_coverage_output): \ + PRIVATE_COVERAGE_REPORT_CLASS := $(my_coverage_report_class) +$(my_coverage_output): \ + PRIVATE_COVERAGE_REPORT_JAR := $(my_coverage_report_jar) +$(my_coverage_output): \ + PRIVATE_REPORT_DIR := $(my_report_dir) + +# Generate the coverage report. +$(my_coverage_output): $(my_collect_target) $(my_coverage_report_jar) + $(hide) rm -rf $(PRIVATE_REPORT_DIR) + $(hide) mkdir -p $(PRIVATE_REPORT_DIR) + $(hide) java \ + -cp $(PRIVATE_COVERAGE_REPORT_JAR) \ + $(PRIVATE_COVERAGE_REPORT_CLASS) \ + --classpath $(strip $(call normalize-path-list, $(PRIVATE_COVERAGE_SRCS_JARS))) \ + --exec-file $(PRIVATE_COVERAGE_FILE) \ + --name $(PRIVATE_MODULE) \ + --report-dir $(PRIVATE_REPORT_DIR)/ \ + --srcs $(strip $(call normalize-path-list, $(PRIVATE_INSTRUMENT_SOURCE_DIRS))) \ + >$(PRIVATE_REPORT_DIR)/reporter.txt 2>&1 + $(hide) abs="$$(realpath "$(PRIVATE_REPORT_DIR)")"; \ + echo "Coverage report:" \ + "file://$$abs/index.html" \ + +# Generate a ZIP file of the coverage report. +my_coverage_output_zip := \ + $(my_coverage_dir)/report-$(my_coverage_report_format).zip + +$(my_coverage_output_zip): \ + PRIVATE_REPORT_DIR := $(my_report_dir) +$(my_coverage_output_zip): $(my_coverage_output) + $(hide) cd $(PRIVATE_REPORT_DIR) && zip -r $(PWD)/$@ . + +# Add coverage report zip to dist files. +$(call dist-for-goals, $(my_target), \ + $(my_coverage_output_zip):robotests/$(my_coverage_report_dist_file)) + +# Running the coverage will always generate the report. +$(my_target): $(my_coverage_output) + +# Reset local variables. +my_coverage_output := +my_coverage_output_zip := +my_report_dir := diff --git a/common/robolectric/run_robotests.mk b/common/robolectric/run_robotests.mk index 3ea95d67..1f11ae94 100644 --- a/common/robolectric/run_robotests.mk +++ b/common/robolectric/run_robotests.mk @@ -109,6 +109,8 @@ my_robolectric_jars := \ $(my_robolectric_path)/xpp3_min-1.1.4c.jar \ $(my_robolectric_path)/xstream-1.4.8.jar +my_collect_target := $(LOCAL_MODULE)-coverage +my_report_target := $(LOCAL_MODULE)-jacoco # Whether or not to ignore the result of running the robotests. # LOCAL_ROBOTEST_FAILURE_FATAL will take precedence over ROBOTEST_FAILURE_FATAL, # if present. @@ -140,25 +142,13 @@ else my_test_filter_command := grep -E "$(ROBOTEST_FILTER)" endif -# Setting the DEBUG_ROBOLECTRIC environment variable will print additional logging from -# Robolectric and also make it wait for a debugger to be connected. -# For Android Studio / IntelliJ the debugger can be connected via the "remote" configuration: -# https://www.jetbrains.com/help/idea/2016.2/run-debug-configuration-remote.html -# From command line the debugger can be connected via -# jdb -attach localhost:5005 -ifdef DEBUG_ROBOLECTRIC - # The arguments to the JVM needed to debug the tests. - # - server: wait for connection rather than connecting to a debugger - # - transport: how to accept debugger connections (sockets) - # - address: the port on which to accept debugger connections - # - timeout: how long (in ms) to wait for a debugger to connect - # - suspend: do not start running any code until the debugger connects - my_java_args := \ - -Drobolectric.logging.enabled=true \ - -Xdebug -agentlib:jdwp=server=y,transport=dt_socket,address=5005,suspend=y - - # Remove the timeout so Robolectric doesn't get killed while debugging - my_timeout := 0 +# The directory containing the sources. +ifeq ($(strip $(LOCAL_INSTRUMENT_SOURCE_DIRS)),) + # If not specified, defaults to the src and java directories in the parent + # directory. + my_instrument_source_dirs := $(dir $(LOCAL_PATH))/src $(dir $(LOCAL_PATH))/java +else + my_instrument_source_dirs := $(LOCAL_INSTRUMENT_SOURCE_DIRS) endif ########################## @@ -207,23 +197,91 @@ my_jars := $(my_robolectric_jars) \ prebuilts/sdk/$(LOCAL_SDK_VERSION)/android.jar \ $(my_srcs_jars) + + # Run tests. my_target := $(LOCAL_BUILT_MODULE) + +# Setting the DEBUG_ROBOLECTRIC environment variable will print additional logging from +# Robolectric and also make it wait for a debugger to be connected. +# For Android Studio / IntelliJ the debugger can be connected via the "remote" configuration: +# https://www.jetbrains.com/help/idea/2016.2/run-debug-configuration-remote.html +# From command line the debugger can be connected via +# jdb -attach localhost:5005 +ifdef DEBUG_ROBOLECTRIC + # The arguments to the JVM needed to debug the tests. + # - server: wait for connection rather than connecting to a debugger + # - transport: how to accept debugger connections (sockets) + # - address: the port on which to accept debugger connections + # - timeout: how long (in ms) to wait for a debugger to connect + # - suspend: do not start running any code until the debugger connects + my_java_args := \ + -Drobolectric.logging.enabled=true \ + -Xdebug -agentlib:jdwp=server=y,transport=dt_socket,address=5005,suspend=y + + # Remove the timeout so Robolectric doesn't get killed while debugging + my_timeout := 0 +endif + include $(my_robolectric_script_path)/robotest-internal.mk +# clean local variables +my_java_args := +my_target := -# Clear temporary variables. +# Target for running robolectric tests using jacoco +my_target := $(my_collect_target) +my_jacoco_dir := \ + prebuilts/misc/common/jacoco + +my_coverage_dir := $(dir $(LOCAL_BUILT_MODULE))/coverage/ +my_coverage_file := $(my_coverage_dir)/jacoco.exec + +# List of packages to exclude jacoco from running +my_jacoco_excludes := \ + org.robolectric.*:org.mockito.*:org.junit.*:org.objectweb.*:com.thoughtworks.xstream.* +# The Jacoco agent JAR. +my_jacoco_agent_jar := \ + $(my_jacoco_dir)/lib/jacocoagent.jar +my_coverage_java_args := \ + -javaagent:$(my_jacoco_agent_jar)=destfile=$(my_coverage_file),excludes=$(my_jacoco_excludes) +my_java_args := $(my_coverage_java_args) +include $(my_robolectric_script_path)/robotest-internal.mk +# Clear temporary variables +my_coverage_java_args := my_failure_fatal := -my_jars := +my_jacoco_agent_jar := +my_jacoco_dir := +my_jacoco_excludes := my_java_args := my_robolectric_jars := my_robolectric_path := -my_robolectric_script_path := -my_srcs_jars := my_target := -my_target_message := my_test_filter_command := my_tests := -my_timeout := + +# Target for generating code coverage reports using jacoco.exec +my_target := $(my_report_target) +# The JAR file containing the report generation tool. +my_coverage_report_class := \ + com.google.android.jacoco.reporter.ReportGenerator +my_coverage_report_jar := \ + $(call intermediates-dir-for, \ + JAVA_LIBRARIES, jvm-jacoco-reporter,host)/javalib.jar +my_coverage_srcs_jars := $(my_srcs_jars) +my_coverage_report_dist_file := $(my_report_target)-html.zip + +## jacoco code coverage reports +include $(my_robolectric_script_path)/report-internal.mk +# Clear temporary variables +my_coverage_dir := +my_coverage_file := +my_coverage_report_class := +my_coverage_report_dist_file := +my_coverage_report_jar := +my_coverage_srcs_jars := +my_robolectric_script_path := +my_srcs_jars := +my_target := # Clear local variables specific to this build. LOCAL_ROBOTEST_FAILURE_FATAL := |