aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--ChangeLog32
-rw-r--r--Makefile.am262
-rw-r--r--configure.ac4
-rw-r--r--plugin/icedteanp/IcedTeaNPPlugin.cc55
-rw-r--r--tests/jacoco-operator/org/jacoco/operator/Main.java288
-rw-r--r--tests/jacoco-operator/org/jacoco/operator/MergeTask.java157
-rw-r--r--tests/jacoco-operator/org/jacoco/operator/ReportGenerator.java292
7 files changed, 1053 insertions, 37 deletions
diff --git a/ChangeLog b/ChangeLog
index f50e581..a304e09 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,35 @@
+2012-12-11 Jiri Vanek <[email protected]>
+
+ Added jacoco code coverage support
+ * Makefile.am: (JACOCO_*) bunch of new variables encapsulating jacoco
+ files. (PLUGIN_COVERAGE_BOOTCLASSPATH) classpath to be used in plugin
+ instead of normal one in coverage mode. (COVERABLE_PLUGIN_DIR) for
+ compiling plugin with agent on. (jacoco-operator-source-files.txt) for
+ storing files of reporting tool. All XSLTPROC command were done as non-fatal
+ (stamps/compile-jacoco-operator.stamp) for compiling report operator.
+ (stamps/run-unit-test-code-coverage-jacoco.stam) for cover unittests
+ Set of (COVERABLE_PLUGIN_*) targets to compile plugin with agent on.
+ (stamps/build-fake-plugin.stamp) top level target for fake plugin.
+ (stamps/run-reproducers-test-code-coverage-jacoco.stamp) target for
+ cover reproducers. (run-test-code-coverage-jacoco) for merged coverage
+ (clean-unit-test-code-coverage-jacoco) and
+ (clean-reproducers-test-code-coverage-jacoco) and
+ (clean-test-code-coverage-jacoco) and
+ (clean-test-code-coverage-tools-jacoco) cleaning targets.
+ (run-reproducers-test-code-coverage-jacoco) and
+ (run-unit-test-code-coverage-jacoco) as top level aliases.
+ * configure.ac: added check for jacoco library and asm library
+ * plugin/icedteanp/IcedTeaNPPlugin.cc removed duplicate code
+ (plugin_start_appletviewe) removed duplicated code and added handling of
+ java agent if defined.
+ * tests/jacoco-operator/org/jacoco/operator/Main.java : New class,
+ comamndline tool for merging results and for generating reports.
+ * tests/jacoco-operator/org/jacoco/operator/MergeTask.java: New class.
+ Utility class responsible for merging exec results to one exec file.
+ * tests/jacoco-operator/org/jacoco/operator/ReportGenerator: New class.
+ Utility method for gathering sources and builds and outputing xml and
+ html reports.
+
2012-12-11 Adam Domurad <[email protected]>
* plugin/icedteanp/java/sun/applet/PluginParameterParser.java:
diff --git a/Makefile.am b/Makefile.am
index 420fe3b..6973185 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -20,6 +20,8 @@ export NETX_UNIT_TEST_DIR=$(NETX_TEST_DIR)/unit
export JUNIT_RUNNER_DIR=$(TESTS_DIR)/junit-runner
export JUNIT_RUNNER_SRCDIR=$(TESTS_SRCDIR)/junit-runner
+export JACOCO_OPERATOR_DIR=$(TESTS_DIR)/jacoco-operator
+export JACOCO_OPERATOR_SRCDIR=$(TESTS_SRCDIR)/jacoco-operator
export TEST_EXTENSIONS_SRCDIR=$(TESTS_SRCDIR)/test-extensions
export TEST_EXTENSIONS_TESTS_SRCDIR=$(TESTS_SRCDIR)/test-extensions-tests
@@ -56,6 +58,22 @@ export CUSTOM_REPRODUCERS=custom
export ALL_NONCUSTOM_REPRODUCERS=$(SIMPLE_REPRODUCERS) $(SIGNED_REPRODUCERS)
export ALL_REPRODUCERS=$(ALL_NONCUSTOM_REPRODUCERS) $(CUSTOM_REPRODUCERS)
+export JACOCO_PATH:=$(shell dirname "$(JACOCO_JAR)")
+export JACOCO_AGENT=org.jacoco.agent.jar
+export JACOCO_ANT=org.jacoco.ant.jar
+export JACOCO_REPORT=org.jacoco.report.jar
+export JACOCO_AGENTRT=org.jacoco.agent.rt.jar
+export JACOCO_CORE=org.jacoco.core.jar
+export JACOCO_JAVAWS_RESULTS=$(TEST_EXTENSIONS_DIR)/jacoco_javaws.exec
+export JACOCO_PLUGIN_RESULTS=$(TEST_EXTENSIONS_DIR)/jacoco_plugin.exec
+export JACOCO_CLASSPATH=$(JACOCO_PATH)/$(JACOCO_CORE):$(JACOCO_PATH)/$(JACOCO_AGENT):$(JACOCO_PATH)/$(JACOCO_REPORT):$(JACOCO_PATH)/$(JACOCO_AGENTRT):$(JACOCO_PATH)/$(JACOCO_ANT):$(ASM_JAR)
+export JACOCO_AGENT_SWITCH_BODY=-javaagent:$(JACOCO_PATH)/$(JACOCO_AGENTRT)
+export JACOCO_BASE_EXCLUDE=org.junit.*:junit.*
+export JACOCO_ADVANCED_EXCLUDE=:*jacoco*:java.lang.*:java.reflect.*:java.util.*:sun.reflect.*
+export JACOCO_AGENT_SWITCH="$(JACOCO_AGENT_SWITCH_BODY)=excludes=$(JACOCO_BASE_EXCLUDE)"
+export JACOCO_AGENT_JAVAWS_SWITCH=\"$(JACOCO_AGENT_SWITCH_BODY)=excludes=$(JACOCO_BASE_EXCLUDE)$(JACOCO_ADVANCED_EXCLUDE),xboot=true,destfile=$(JACOCO_JAVAWS_RESULTS)\"
+export JACOCO_AGENT_PLUGIN_SWITCH=\"$(JACOCO_AGENT_SWITCH_BODY)=excludes=$(JACOCO_BASE_EXCLUDE)$(JACOCO_ADVANCED_EXCLUDE),xboot=true,destfile=$(JACOCO_PLUGIN_RESULTS)\"
+export JACOCO_OPERATOR_EXEC=$(BOOT_DIR)/bin/java $(EMMA_JAVA_ARGS) -cp $(JACOCO_OPERATOR_DIR):$(JACOCO_CLASSPATH):. org.jacoco.operator.Main
# linking variables
export PLUGIN_LINK_NAME=libjavaplugin.so
@@ -103,6 +121,7 @@ export IT_JAVACFLAGS=$(IT_JAVAC_SETTINGS) -source $(IT_LANGUAGE_SOURCE_VERSION)
#
export LAUNCHER_BOOTCLASSPATH="-Xbootclasspath/a:$(datadir)/$(PACKAGE_NAME)/netx.jar$(RHINO_RUNTIME)"
export PLUGIN_BOOTCLASSPATH='"-Xbootclasspath/a:$(datadir)/$(PACKAGE_NAME)/netx.jar:$(datadir)/$(PACKAGE_NAME)/plugin.jar$(RHINO_RUNTIME)"'
+export PLUGIN_COVERAGE_BOOTCLASSPATH='"-Xbootclasspath/a:$(datadir)/$(PACKAGE_NAME)/netx.jar:$(datadir)/$(PACKAGE_NAME)/plugin.jar$(RHINO_RUNTIME):$(JACOCO_CLASSPATH)"'
# Fake update version to work with the Deployment Toolkit script used by Oracle
# http://download.oracle.com/javase/tutorial/deployment/deploymentInDepth/depltoolkit_index.html
@@ -128,6 +147,8 @@ export PLUGIN_SRCDIR=$(abs_top_srcdir)/plugin/icedteanp
export LIVECONNECT_SRCS = $(PLUGIN_SRCDIR)/java
export ICEDTEAPLUGIN_TARGET = $(PLUGIN_DIR)/$(BUILT_PLUGIN_LIBRARY) stamps/liveconnect-dist.stamp
export PLUGIN_PKGS = sun.applet netscape.security netscape.javascript
+#this is for plugin testcoverage
+export COVERABLE_PLUGIN_DIR=$(TESTS_DIR)/icedteanp-build-with-jacoco
endif
if CP_SUPPORTS_REFLINK
@@ -588,6 +609,9 @@ stamps/check-pac-functions.stamp: stamps/bootstrap-directory.stamp
junit-runner-source-files.txt:
find $(JUNIT_RUNNER_SRCDIR) -name '*.java' | sort > $@
+jacoco-operator-source-files.txt:
+ find $(JACOCO_OPERATOR_SRCDIR) -name '*.java' | sort > $@
+
$(JUNIT_RUNNER_JAR): junit-runner-source-files.txt stamps/test-extensions-compile.stamp
mkdir -p $(JUNIT_RUNNER_DIR) && \
$(BOOT_DIR)/bin/javac $(IT_JAVACFLAGS) \
@@ -825,8 +849,8 @@ stamps/run-netx-dist-tests.stamp: stamps/netx-dist.stamp extra-lib/about.jar sta
$(BOOT_DIR)/bin/java $(REPRODUCERS_DPARAMETERS) \
-Xbootclasspath:$(RUNTIME) CommandLine $$class_names
if WITH_XSLTPROC
- $(XSLTPROC) --stringparam logs logs_reproducers.html $(TESTS_SRCDIR)/$(REPORT_STYLES_DIRNAME)/jreport.xsl $(TEST_EXTENSIONS_DIR)/tests-output.xml > $(TESTS_DIR)/index_reproducers.html
- $(XSLTPROC) $(TESTS_SRCDIR)/$(REPORT_STYLES_DIRNAME)/logs.xsl $(TEST_EXTENSIONS_DIR)/ServerAccess-logs.xml > $(TESTS_DIR)/logs_reproducers.html
+ -$(XSLTPROC) --stringparam logs logs_reproducers.html $(TESTS_SRCDIR)/$(REPORT_STYLES_DIRNAME)/jreport.xsl $(TEST_EXTENSIONS_DIR)/tests-output.xml > $(TESTS_DIR)/index_reproducers.html
+ -$(XSLTPROC) $(TESTS_SRCDIR)/$(REPORT_STYLES_DIRNAME)/logs.xsl $(TEST_EXTENSIONS_DIR)/ServerAccess-logs.xml > $(TESTS_DIR)/logs_reproducers.html
endif
touch $@
@@ -1002,8 +1026,8 @@ stamps/run-netx-unit-tests.stamp: stamps/netx-unit-tests-compile.stamp $(JUNIT_R
CLASSPATH=$(NETX_DIR)/lib/classes.jar:$(abs_top_builddir)/liveconnect/lib/classes.jar:$(JUNIT_JAR):$(JUNIT_RUNNER_JAR):$(TEST_EXTENSIONS_DIR):. \
$(BOOT_DIR)/bin/java -Xbootclasspath:$(RUNTIME) CommandLine $$class_names
if WITH_XSLTPROC
- $(XSLTPROC) --stringparam logs logs_unit.html $(TESTS_SRCDIR)/$(REPORT_STYLES_DIRNAME)/jreport.xsl $(NETX_UNIT_TEST_DIR)/tests-output.xml > $(TESTS_DIR)/index_unit.html
- $(XSLTPROC) $(TESTS_SRCDIR)/$(REPORT_STYLES_DIRNAME)/logs.xsl $(NETX_UNIT_TEST_DIR)/ServerAccess-logs.xml > $(TESTS_DIR)/logs_unit.html
+ -$(XSLTPROC) --stringparam logs logs_unit.html $(TESTS_SRCDIR)/$(REPORT_STYLES_DIRNAME)/jreport.xsl $(NETX_UNIT_TEST_DIR)/tests-output.xml > $(TESTS_DIR)/index_unit.html
+ -$(XSLTPROC) $(TESTS_SRCDIR)/$(REPORT_STYLES_DIRNAME)/logs.xsl $(NETX_UNIT_TEST_DIR)/ServerAccess-logs.xml > $(TESTS_DIR)/logs_unit.html
endif
mkdir -p stamps && \
touch $@
@@ -1049,6 +1073,53 @@ else
endif
touch $@
+stamps/compile-jacoco-operator.stamp: jacoco-operator-source-files.txt
+if WITH_JACOCO
+ mkdir -p $(JACOCO_OPERATOR_DIR) && \
+ $(BOOT_DIR)/bin/javac $(IT_JAVACFLAGS) \
+ -d $(JACOCO_OPERATOR_DIR) \
+ -classpath $(JACOCO_CLASSPATH) \
+ @jacoco-operator-source-files.txt ;
+else
+ echo "Sorry, jacoco coverage report generator cant be compiled without jacoco installed. Try installing jacoco or specify with-jacoco value" ;
+ exit 5
+endif
+ touch $@
+
+
+#warning, during this target tests.build/netx/unit/tests-output.xml is backup and rewriten (but not coresponding html file)
+#xml results run with jacoco agent however, can be wrong, co the new tests-output.xml is then renamed and orginal one restored
+stamps/run-unit-test-code-coverage-jacoco.stamp: stamps/netx-unit-tests-compile.stamp $(JUNIT_RUNNER_JAR) \
+ $(TESTS_DIR)/$(REPORT_STYLES_DIRNAME) $(UNIT_CLASS_NAMES) stamps/compile-jacoco-operator.stamp
+if WITH_JACOCO
+ filename=" " ; \
+ cd $(NETX_UNIT_TEST_SRCDIR) ; \
+ for file in `find . -type f \! -iname "*.java"`; do\
+ filename=`echo $$file `; \
+ cp --parents $$filename $(NETX_UNIT_TEST_DIR) ; \
+ done ; \
+ cd $(NETX_UNIT_TEST_DIR) ; \
+ for file in $(EMMA_MODIFIED_FILES) ; do \
+ mv $(NETX_UNIT_TEST_DIR)/$$file $(NETX_UNIT_TEST_DIR)/"$$file""$(EMMA_BACKUP_SUFFIX)" ; \
+ done ;\
+ class_names=`cat $(UNIT_CLASS_NAMES)` ; \
+ CLASSPATH=$(NETX_DIR)/lib/classes.jar:$(abs_top_builddir)/liveconnect/lib/classes.jar:$(JUNIT_JAR):$(JUNIT_RUNNER_JAR):$(TEST_EXTENSIONS_DIR):$(JACOCO_CLASSPATH):. \
+ $(BOOT_DIR)/bin/java $(JACOCO_AGENT_SWITCH) -Xbootclasspath:$(RUNTIME) CommandLine $$class_names ; \
+ for file in $(EMMA_MODIFIED_FILES) ; do \
+ mv $(NETX_UNIT_TEST_DIR)/$$file $(NETX_UNIT_TEST_DIR)/"$$file""$(EMMA_SUFFIX)" ; \
+ mv $(NETX_UNIT_TEST_DIR)/"$$file""$(EMMA_BACKUP_SUFFIX)" $(NETX_UNIT_TEST_DIR)/$$file ; \
+ done ; \
+ $(JACOCO_OPERATOR_EXEC) \
+ report --die-soon --html-output coverage --xml-output coverage.xml --input-file jacoco.exec \
+ --input-srcs $(NETX_SRCDIR) $(PLUGIN_SRCDIR)/java $(NETX_UNIT_TEST_SRCDIR) $(JUNIT_RUNNER_SRCDIR) $(TEST_EXTENSIONS_SRCDIR) \
+ --input-builds $(NETX_DIR)/lib/classes.jar $(abs_top_builddir)/liveconnect/lib/classes.jar $(NETX_UNIT_TEST_DIR) $(JUNIT_RUNNER_JAR) $(TEST_EXTENSIONS_DIR) \
+ --title "IcedTea-Web unit-tests codecoverage" ;
+else
+ echo "Sorry, coverage report cant be run without jacoco installed. Try installing jacoco or specify with-jacoco value" ;
+ exit 5
+endif
+ touch $@
+
#warning, during this target tests.build/netx/jnlp_testsengine/tests-output.xml is backup and rewriten (but not coresponding html file)
#xml results run from emma sandbox, however, can be wrong, co the new tests-output.xml is then renamed and orginal one restored
stamps/run-reproducers-test-code-coverage.stamp: stamps/run-netx-dist-tests.stamp $(REPRODUCERS_CLASS_NAMES)
@@ -1139,7 +1210,145 @@ else
endif
touch $@
-run-test-code-coverage: run-unit-test-code-coverage run-reproducers-test-code-coverage
+$(COVERABLE_PLUGIN_DIR):
+ mkdir -p $(COVERABLE_PLUGIN_DIR);
+
+$(COVERABLE_PLUGIN_DIR)/%.o: $(PLUGIN_SRCDIR)/%.cc
+ cd $(COVERABLE_PLUGIN_DIR) && \
+ $(CXX) $(CXXFLAGS) \
+ $(DEFS) $(VERSION_DEFS) \
+ -DJDK_UPDATE_VERSION="\"$(JDK_UPDATE_VERSION)\"" \
+ -DPLUGIN_NAME="\"IcedTea-Web Plugin with jacoco coverage agent\"" \
+ -DPLUGIN_VERSION="\"$(PLUGIN_VERSION)\"" \
+ -DPACKAGE_URL="\"$(PACKAGE_URL)\"" \
+ -DMOZILLA_VERSION_COLLAPSED="$(MOZILLA_VERSION_COLLAPSED)" \
+ -DICEDTEA_WEB_JRE="\"$(SYSTEM_JRE_DIR)\"" \
+ -DPLUGIN_BOOTCLASSPATH=$(PLUGIN_COVERAGE_BOOTCLASSPATH) \
+ -DCOVERAGE_AGENT=$(JACOCO_AGENT_PLUGIN_SWITCH) \
+ $(GLIB_CFLAGS) \
+ $(GTK_CFLAGS) \
+ $(MOZILLA_CFLAGS) \
+ -fvisibility=hidden \
+ -fPIC -o $@ -c $<
+
+$(COVERABLE_PLUGIN_DIR)/$(BUILT_PLUGIN_LIBRARY): $(addprefix $(COVERABLE_PLUGIN_DIR)/,$(PLUGIN_OBJECTS))
+ cd $(COVERABLE_PLUGIN_DIR) && \
+ $(CXX) $(CXXFLAGS) \
+ $(PLUGIN_OBJECTS) \
+ $(GLIB_LIBS) \
+ $(GTK_LIBS) \
+ $(MOZILLA_LIBS) \
+ -shared -o $@
+
+stamps/build-fake-plugin.stamp: $(COVERABLE_PLUGIN_DIR) $(addprefix $(PLUGIN_SRCDIR)/,$(PLUGIN_SRC)) $(addprefix $(COVERABLE_PLUGIN_DIR)/,$(PLUGIN_OBJECTS)) stamps/liveconnect-dist.stamp $(COVERABLE_PLUGIN_DIR)/$(BUILT_PLUGIN_LIBRARY)
+ touch $@
+
+#warning, during this target tests.build/netx/jnlp_testsengine/tests-output.xml is backup and rewriten (but not coresponding html file)
+#xml results run with jacoco agent, however, can be wrong, co the new tests-output.xml is then renamed and orginal one restored
+stamps/run-reproducers-test-code-coverage-jacoco.stamp: stamps/run-netx-dist-tests.stamp $(REPRODUCERS_CLASS_NAMES) \
+stamps/compile-jacoco-operator.stamp stamps/build-fake-plugin.stamp
+if WITH_JACOCO
+ cd $(TESTS_DIR) ; \
+ for file in $(EMMA_MODIFIED_FILES) ; do \
+ mv $(TEST_EXTENSIONS_DIR)/$$file $(TEST_EXTENSIONS_DIR)/"$$file""$(EMMA_BACKUP_SUFFIX)" ; \
+ done ;\
+ echo "backuping javaws in $(DESTDIR)$(bindir)" ; \
+ javaws_backup=$(DESTDIR)$(bindir)/javaws_backup ; \
+ mv $(DESTDIR)$(bindir)/javaws $$javaws_backup ; \
+ echo "patching $(javaws)" ; \
+ nw_bootclasspath="$(LAUNCHER_BOOTCLASSPATH):$(JACOCO_CLASSPATH)" ; \
+ cat $$javaws_backup | sed "s|COMMAND.k.=\"..JAVA.\"|COMMAND[k]=\"\\$$\\{JAVA\\}\" ; k=1 ; COMMAND[k]=$(JACOCO_AGENT_JAVAWS_SWITCH)|" | sed "s,$(LAUNCHER_BOOTCLASSPATH),$$nw_bootclasspath," > $(DESTDIR)$(bindir)/$(javaws) ; \
+ chmod 777 $(DESTDIR)$(bindir)/$(javaws) ; \
+ echo "backuping plugin in $(DESTDIR)/$(libdir)$(BUILT_PLUGIN_LIBRARY)" ; \
+ plugin_backup=$(DESTDIR)$(libdir)/$(BUILT_PLUGIN_LIBRARY)_backup ; \
+ mv $(DESTDIR)$(libdir)/$(BUILT_PLUGIN_LIBRARY) $$plugin_backup ; \
+ echo "fakeing plugin" ; \
+ cp $(COVERABLE_PLUGIN_DIR)/$(BUILT_PLUGIN_LIBRARY) $(DESTDIR)$(libdir)/$(BUILT_PLUGIN_LIBRARY) ; \
+ testcases_srcs=( ) ; \
+ k=0 ; \
+ types=($(ALL_REPRODUCERS)); \
+ for which in "$${types[@]}" ; do \
+ . $(abs_top_srcdir)/NEW_LINE_IFS ; \
+ simpleReproducers=(`cat $(abs_top_builddir)/junit-jnlp-dist-$$which.txt `); \
+ IFS="$$IFS_BACKUP" ; \
+ for dir in "$${simpleReproducers[@]}" ; do \
+ testcases_srcs[k]="$(REPRODUCERS_TESTS_SRCDIR)/$$which/$$dir/testcases/" ; \
+ k=$$((k+1)) ; \
+ done ; \
+ done ; \
+ cd $(TEST_EXTENSIONS_DIR) ; \
+ class_names=`cat $(REPRODUCERS_CLASS_NAMES)` ; \
+ CLASSPATH=$(NETX_DIR)/lib/classes.jar:$(JUNIT_JAR):$(JUNIT_RUNNER_JAR):.:$(TEST_EXTENSIONS_DIR):$(JACOCO_CLASSPATH):$(TEST_EXTENSIONS_TESTS_DIR) \
+ $(BOOT_DIR)/bin/java $(JACOCO_AGENT_SWITCH) $(REPRODUCERS_DPARAMETERS) \
+ -Xbootclasspath:$(RUNTIME) CommandLine $$class_names ; \
+ if [ -f $(JACOCO_JAVAWS_RESULTS) ] ; then \
+ jacoco_javaws_results=$(JACOCO_JAVAWS_RESULTS) ; \
+ $(JACOCO_OPERATOR_EXEC) \
+ report --die-soon --html-output coverage-javaws --xml-output coverage-javaws.xml --input-file $(JACOCO_JAVAWS_RESULTS) \
+ --input-srcs $(NETX_SRCDIR) \
+ --input-builds $(NETX_DIR)/lib/classes.jar \
+ --title "IcedTea-Web javaws reproducers codecoverage" ; \
+ fi; \
+ if [ -f $(JACOCO_PLUGIN_RESULTS) ] ; then \
+ jacoco_plugin_results=$(JACOCO_PLUGIN_RESULTS) ; \
+ $(JACOCO_OPERATOR_EXEC) \
+ report --die-soon --html-output coverage-plugin --xml-output coverage-plugin.xml --input-file $(JACOCO_PLUGIN_RESULTS) \
+ --input-srcs $(NETX_SRCDIR) $(PLUGIN_SRCDIR)/java \
+ --input-builds $(NETX_DIR)/lib/classes.jar $(abs_top_builddir)/liveconnect/lib/classes.jar \
+ --title "IcedTea-Web plugin reproducers codecoverage" ; \
+ fi; \
+ $(JACOCO_OPERATOR_EXEC) \
+ merge --die-soon --input-files jacoco.exec $$jacoco_javaws_results $$jacoco_plugin_results --output-file jacoco-merged-reproducers.exec ; \
+ $(JACOCO_OPERATOR_EXEC) \
+ report --html-output coverage --xml-output coverage.xml --input-file jacoco-merged-reproducers.exec \
+ --input-srcs $(NETX_SRCDIR) $(PLUGIN_SRCDIR)/java $(JUNIT_RUNNER_SRCDIR) $(TEST_EXTENSIONS_SRCDIR) $(TEST_EXTENSIONS_TESTS_SRCDIR) "$${testcases_srcs[@]}" \
+ --input-builds $(NETX_DIR)/lib/classes.jar $(abs_top_builddir)/liveconnect/lib/classes.jar $(JUNIT_RUNNER_JAR) $(TEST_EXTENSIONS_DIR) $(TEST_EXTENSIONS_TESTS_DIR) \
+ --title "IcedTea-Web reproducers-tests codecoverage" ; \
+ echo "restoring javaws in $(DESTDIR)$(bindir)" ; \
+ rm -f $(DESTDIR)$(bindir)/$(javaws); \
+ mv $$javaws_backup $(DESTDIR)$(bindir)/$(javaws); \
+ echo "restoring plugin in $(DESTDIR)/$(libdir)$(BUILT_PLUGIN_LIBRARY)" ; \
+ mv $$plugin_backup $(DESTDIR)$(libdir)/$(BUILT_PLUGIN_LIBRARY) ; \
+ for file in $(EMMA_MODIFIED_FILES) ; do \
+ mv $(TEST_EXTENSIONS_DIR)/$$file $(TEST_EXTENSIONS_DIR)/"$$file""$(EMMA_SUFFIX)" ; \
+ mv $(TEST_EXTENSIONS_DIR)/"$$file""$(EMMA_BACKUP_SUFFIX)" $(TEST_EXTENSIONS_DIR)/$$file ; \
+ done ;
+else
+ echo "Sorry, coverage report cant be run without jacoco installed. Try installing jacoco or specify with-jacoco value" ;
+ exit 5
+endif
+ touch $@
+
+run-test-code-coverage-jacoco: stamps/run-unit-test-code-coverage-jacoco.stamp stamps/run-reproducers-test-code-coverage-jacoco.stamp
+if WITH_JACOCO
+ cd $(TESTS_DIR) ; \
+ k=0 ; \
+ types=($(ALL_REPRODUCERS)); \
+ for which in "$${types[@]}" ; do \
+ . $(abs_top_srcdir)/NEW_LINE_IFS ; \
+ simpleReproducers=(`cat $(abs_top_builddir)/junit-jnlp-dist-$$which.txt `); \
+ IFS="$$IFS_BACKUP" ; \
+ for dir in "$${simpleReproducers[@]}" ; do \
+ testcases_srcs[k]="$(REPRODUCERS_TESTS_SRCDIR)/$$which/$$dir/testcases/" ; \
+ k=$$((k+1)) ; \
+ done ; \
+ done ; \
+ class_names=`cat $(REPRODUCERS_CLASS_NAMES)` ; \
+ $(JACOCO_OPERATOR_EXEC) \
+ merge --die-soon --input-files $(TEST_EXTENSIONS_DIR)/jacoco-merged-reproducers.exec $(NETX_UNIT_TEST_DIR)/jacoco.exec --output-file jacoco-merged.exec; \
+ $(JACOCO_OPERATOR_EXEC) \
+ report --html-output coverage --xml-output coverage.xml --input-file jacoco-merged.exec \
+ --input-srcs $(NETX_SRCDIR) $(PLUGIN_SRCDIR)/java $(JUNIT_RUNNER_SRCDIR) $(TEST_EXTENSIONS_SRCDIR) $(TEST_EXTENSIONS_TESTS_SRCDIR) "$${testcases_srcs[@]}" \
+ --input-builds $(NETX_DIR)/lib/classes.jar $(abs_top_builddir)/liveconnect/lib/classes.jar $(JUNIT_RUNNER_JAR) $(TEST_EXTENSIONS_DIR) $(TEST_EXTENSIONS_TESTS_DIR) \
+ --input-srcs $(NETX_UNIT_TEST_SRCDIR) \
+ --input-builds $(NETX_UNIT_TEST_DIR) \
+ --title "IcedTea-Web complete codecoverage" ;
+else
+ echo "Sorry, coverage report cant be run without jacoco installed. Try installing jacoco or specify with-jacoco value" ;
+ exit 5
+endif
+
+run-test-code-coverage: stamps/run-unit-test-code-coverage.stamps stamps/run-reproducers-test-code-coverage.stamps
if WITH_EMMA
cd $(TESTS_DIR) ; \
k=0 ; \
@@ -1175,7 +1384,7 @@ else
exit 5
endif
-clean-netx-tests: clean-netx-unit-tests clean-junit-runner clean-netx-dist-tests clean-test-code-coverage
+clean-netx-tests: clean-netx-unit-tests clean-junit-runner clean-netx-dist-tests clean-test-code-coverage-jacoco clean-test-code-coverage
if [ -e $(TESTS_DIR)/netx ]; then \
rmdir $(TESTS_DIR)/netx ; \
fi
@@ -1258,6 +1467,43 @@ clean-test-code-coverage: clean-unit-test-code-coverage clean-reproducers-test-c
rm -f $(TESTS_DIR)/coverage.em ; \
fi
+clean-unit-test-code-coverage-jacoco:
+ if [ -e stamps/run-unit-test-code-coverage-jacoco.stamp ]; then \
+ rm -rf $(NETX_UNIT_TEST_DIR)/coverage ; \
+ rm -f $(NETX_UNIT_TEST_DIR)/coverage.xml ; \
+ rm -f $(NETX_UNIT_TEST_DIR)/jacoco.exec ; \
+ rm -f $(NETX_UNIT_TEST_DIR)/tests-output_withEmma.xml ; \
+ rm -f stamps/run-unit-test-code-coverage-jacoco.stamp ; \
+ fi
+
+clean-reproducers-test-code-coverage-jacoco:
+ if [ -e stamps/run-reproducers-test-code-coverage-jacoco.stamp ]; then \
+ rm -rf $(TEST_EXTENSIONS_DIR)/coverage-javaws ; \
+ rm -f $(TEST_EXTENSIONS_DIR)/coverage-javaws.xml ; \
+ rm -f $(TEST_EXTENSIONS_DIR)/jacoco_javaws.exec ; \
+ rm -rf $(TEST_EXTENSIONS_DIR)/coverage-plugin ; \
+ rm -f $(TEST_EXTENSIONS_DIR)/coverage-plugin.xml ; \
+ rm -f $(TEST_EXTENSIONS_DIR)/jacoco_plugin.exec ; \
+ rm -rf $(TEST_EXTENSIONS_DIR)/coverage ; \
+ rm -f $(TEST_EXTENSIONS_DIR)/coverage.xml ; \
+ rm -f $(TEST_EXTENSIONS_DIR)/jacoco-merged-reproducers.exec ; \
+ rm -f $(TEST_EXTENSIONS_DIR)/tests-output_withEmma.xml ; \
+ rm -f stamps/run-reproducers-test-code-coverage-jacoco.stamp ; \
+ fi
+
+clean-test-code-coverage-jacoco: clean-unit-test-code-coverage-jacoco clean-reproducers-test-code-coverage-jacoco clean-test-code-coverage-tools-jacoco
+ if [ -e $(TESTS_DIR)/coverage.xml ]; then \
+ rm -rf $(TESTS_DIR)/coverage ; \
+ rm -f $(TESTS_DIR)/jacoco-merged.exec; \
+ fi
+
+clean-test-code-coverage-tools-jacoco:
+ rm -rf $(JACOCO_OPERATOR_DIR)
+ rm -rf $(COVERABLE_PLUGIN_DIR)
+ rm -f stamps/compile-jacoco-operator.stamp;
+ rm -f jacoco-operator-source-files.txt
+ rm -f stamps/build-fake-plugin.stamp
+
# plugin tests
@@ -1377,3 +1623,7 @@ run-netx-dist-tests: stamps/run-netx-dist-tests.stamp
run-unit-test-code-coverage: stamps/run-unit-test-code-coverage.stamp
run-reproducers-test-code-coverage: stamps/run-reproducers-test-code-coverage.stamp
+
+run-unit-test-code-coverage-jacoco: stamps/run-unit-test-code-coverage-jacoco.stamp
+
+run-reproducers-test-code-coverage-jacoco: stamps/run-reproducers-test-code-coverage-jacoco.stamp
diff --git a/configure.ac b/configure.ac
index b1da7bb..57315b0 100644
--- a/configure.ac
+++ b/configure.ac
@@ -106,6 +106,10 @@ IT_FIND_OPTIONAL_JAR([junit], JUNIT,
[/usr/share/java/junit4.jar /usr/share/junit-4/lib/junit.jar])
IT_FIND_OPTIONAL_JAR([emma], EMMA,
[/usr/share/java/emma.jar])
+IT_FIND_OPTIONAL_JAR([jacoco], JACOCO,
+ [/usr/share/java/jacoco/org.jacoco.core.jar])
+IT_FIND_OPTIONAL_JAR([asm], ASM,
+ [/usr/share/java/objectweb-asm4/asm-all-4.0.jar /usr/share/java/objectweb-asm/asm-all.jar])
AC_CONFIG_FILES([jrunscript], [chmod u+x jrunscript])
AC_CONFIG_FILES([build.properties])
diff --git a/plugin/icedteanp/IcedTeaNPPlugin.cc b/plugin/icedteanp/IcedTeaNPPlugin.cc
index 48678f5..763120d 100644
--- a/plugin/icedteanp/IcedTeaNPPlugin.cc
+++ b/plugin/icedteanp/IcedTeaNPPlugin.cc
@@ -1594,20 +1594,28 @@ plugin_start_appletviewer (ITNPPluginData* data)
gchar** environment = NULL;
std::vector<std::string*>* jvm_args = get_jvm_args();
- if (plugin_debug)
+ // Construct command line parameters
+
+ command_line.push_back(appletviewer_executable);
+
+ //Add JVM args to command_line
+ for (int i = 0; i < jvm_args->size(); i++)
{
- command_line.push_back(appletviewer_executable);
+ command_line.push_back(*jvm_args->at(i));
+ }
- //Add JVM args to command_line
- for (int i = 0; i < jvm_args->size(); i++)
- {
- command_line.push_back(*jvm_args->at(i));
- }
+ command_line.push_back(PLUGIN_BOOTCLASSPATH);
+ // set the classpath to avoid using the default (cwd).
+ command_line.push_back("-classpath");
+ command_line.push_back(ICEDTEA_WEB_JRE "/lib/rt.jar");
+
+ // Enable coverage agent if we are running instrumented plugin
+#ifdef COVERAGE_AGENT
+ command_line.push_back(COVERAGE_AGENT);
+#endif
- command_line.push_back(PLUGIN_BOOTCLASSPATH);
- // set the classpath to avoid using the default (cwd).
- command_line.push_back("-classpath");
- command_line.push_back(ICEDTEA_WEB_JRE "/lib/rt.jar");
+ if (plugin_debug)
+ {
command_line.push_back("-Xdebug");
command_line.push_back("-Xnoagent");
@@ -1615,28 +1623,13 @@ plugin_start_appletviewer (ITNPPluginData* data)
std::string debug_flags = "-Xrunjdwp:transport=dt_socket,address=8787,server=y,";
debug_flags += plugin_debug_suspend ? "suspend=y" : "suspend=n";
command_line.push_back(debug_flags);
+ }
- command_line.push_back("sun.applet.PluginMain");
- command_line.push_back(out_pipe_name);
- command_line.push_back(in_pipe_name);
- } else
- {
- command_line.push_back(std::string(appletviewer_executable));
-
- //Add JVM args to command_line
- for (int i = 0; i < jvm_args->size(); i++)
- {
- command_line.push_back(*jvm_args->at(i));
- }
-
- command_line.push_back(PLUGIN_BOOTCLASSPATH);
- command_line.push_back("-classpath");
- command_line.push_back(ICEDTEA_WEB_JRE "/lib/rt.jar");
- command_line.push_back("sun.applet.PluginMain");
- command_line.push_back(out_pipe_name);
- command_line.push_back(in_pipe_name);
+ command_line.push_back("sun.applet.PluginMain");
+ command_line.push_back(out_pipe_name);
+ command_line.push_back(in_pipe_name);
- }
+ // Finished command line parameters
environment = plugin_filter_environment();
std::vector<gchar*> vector_gchar = IcedTeaPluginUtilities::vectorStringToVectorGchar(&command_line);
diff --git a/tests/jacoco-operator/org/jacoco/operator/Main.java b/tests/jacoco-operator/org/jacoco/operator/Main.java
new file mode 100644
index 0000000..7537ae1
--- /dev/null
+++ b/tests/jacoco-operator/org/jacoco/operator/Main.java
@@ -0,0 +1,288 @@
+/*
+Copyright (C) 2012 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package org.jacoco.operator;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Commandline launcher
+ */
+public class Main {
+
+ //main switches
+ private static final String MERGE = "merge";
+ private static final String REPORT = "report";
+ //switches
+ private static final String die_on_failure = "--die-soon";
+ //merge
+ private static final String output_file = "--output-file";
+ private static final String input_files = "--input-files";
+ //report
+ private static final String html_output = "--html-output";
+ private static final String xml_output = "--xml-output";
+ private static final String input_srcs = "--input-srcs";
+ private static final String input_builds = "--input-builds";
+ private static final String title = "--title";
+ private static String input_file = "--input-file";
+ /**
+ * *
+ */
+ private static boolean dieOnFailure = false;
+ private static boolean warned = false;
+
+ public static void main(String[] args) throws IOException {
+ if (args.length < 2) {
+ printHelp();
+ System.exit(0);
+ }
+
+ Runnable r = null;
+ if (args[0].equalsIgnoreCase(MERGE)) {
+ r = proceedMerge(cutFirstParam(args));
+ } else if (args[0].equalsIgnoreCase(REPORT)) {
+ r = proceedReport(cutFirstParam(args));
+ } else {
+ System.err.println("Unsuported main switch `" + args[0] + "`, use " + MERGE + " or " + REPORT);
+ printHelp();
+ System.exit(1);
+ }
+ if (dieOnFailure && warned) {
+ System.err.println(die_on_failure + " is specified and warning occured. Exiting");
+ System.exit(2);
+ }
+ r.run();
+
+ }
+
+ private static void printHelp() {
+ System.out.println("Usage: java `classpath` org.jacoco.operator.Main [" + MERGE + "|" + REPORT + "] switches/files");
+ System.out.println(" order of switches does not matter");
+ System.out.println(" Merge usage: java `classpath` org.jacoco.operator.Main " + MERGE + " " + output_file + " file " + input_files + " file file file ...");
+ System.out.println(" Report usage: java `classpath` org.jacoco.operator.Main " + REPORT + " " + html_output + " file " + xml_output + " file " + input_srcs + " file file file ... " + input_builds + " file file file " + title + " titleOfReport " + input_file + " file");
+ System.out.println("Where:");
+ System.out.println(" classpath should contain this application, and complete jacoco, and sometimes asm3 (depends on jacoco bundle)");
+ System.out.println(" " + die_on_failure + " - can be set as first parameter (after main switch), each warning then will cause exit of application");
+ System.out.println(" " + MERGE);
+ System.out.println(" " + output_file + " - is file where merged inputs will be saved");
+ System.out.println(" " + input_files + " - is list of files which will be merged into output file");
+ System.out.println(" " + REPORT);
+ System.out.println(" " + html_output + " - name of directory into which report will be generated. Should be empty or not yet exist");
+ System.out.println(" " + xml_output + " - is name of file into which xml report will be written");
+ System.out.println(" " + input_srcs + " - jars, zips or directories with java sources which will be used during report generation");
+ System.out.println(" " + input_builds + " - jars, zips or directories with compiled java classes, debug information must be present");
+ System.out.println(" " + title + " - title of report");
+ System.out.println(" " + input_file + " - input file with recorded coverage-run-session. By default jacoco saves into " + MergeTask.DEFAULT_NAME);
+
+ }
+
+ private static String[] cutFirstParam(String[] args) {
+ String[] arg = new String[args.length - 1];
+ System.arraycopy(args, 1, arg, 0, arg.length);
+ return arg;
+ }
+
+ private static Runnable proceedMerge(String[] a) throws IOException {
+ String doing = null;
+ String outputFile = null;
+ List<String> inputFiles = new ArrayList<String>(2);
+ for (String s : a) {
+ if (s.startsWith("--")) {
+ if (s.equalsIgnoreCase(die_on_failure)) {
+ doing = null;
+ dieOnFailure = true;
+ } else if (s.equalsIgnoreCase(output_file)) {
+ doing = output_file;
+ } else if (s.equalsIgnoreCase(input_files)) {
+ doing = input_files;
+ } else {
+ warnOrDie("Unknown Switch for merge " + s);
+ doing = null;
+ }
+ } else {
+ if (doing == null) {
+ warnOrDie("Missing switch during processing of " + s);
+ } else {
+ if (doing.equalsIgnoreCase(output_file)) {
+ outputFile = s;
+ } else if (doing.equalsIgnoreCase(input_files)) {
+ inputFiles.add(s);
+ } else {
+ warnOrDie("Unknown processing of switch of" + doing);
+ }
+
+ }
+ }
+ }
+ throwIfNullOrEmpty(outputFile, "empty output file");
+ File ff = new File(outputFile);
+ if (ff.exists()) {
+ warnOrDie("Warning, output file " + ff.getAbsolutePath() + " exists");
+ }
+ MergeTask m = new MergeTask(ff);
+ for (String string : inputFiles) {
+ if (checkIfNotNullOrEmpty(string)) {
+ File f = new File(string);
+ if (!f.exists()) {
+ warnOrDie("Warning, input coverage " + f.getAbsolutePath() + " does not exists!");
+ }
+ m.addInputFile(f);
+ }
+ }
+ return m;
+
+ }
+
+ private static Runnable proceedReport(String[] a) throws IOException {
+ String doing = null;
+ String htmlDir = null;
+ String xmlFile = null;
+ List<String> inputSrcs = new ArrayList<String>(1);
+ List<String> inputBuilds = new ArrayList<String>(1);
+ String titleValue = null;
+ String inputFile = null;
+ for (String s : a) {
+ if (s.startsWith("--")) {
+ if (s.equalsIgnoreCase(die_on_failure)) {
+ doing = null;
+ dieOnFailure = true;
+ } else if (s.equalsIgnoreCase(html_output)) {
+ doing = html_output;
+ } else if (s.equalsIgnoreCase(xml_output)) {
+ doing = xml_output;
+ } else if (s.equalsIgnoreCase(input_srcs)) {
+ doing = input_srcs;
+ } else if (s.equalsIgnoreCase(input_builds)) {
+ doing = input_builds;
+ } else if (s.equalsIgnoreCase(title)) {
+ doing = title;
+ } else if (s.equalsIgnoreCase(input_file)) {
+ doing = input_file;
+ } else {
+ warnOrDie("Unknown Switch for report " + s);
+ doing = null;
+ }
+ } else {
+ if (doing == null) {
+ warnOrDie("Missing switch during processing of " + s);
+ } else {
+ if (doing.equalsIgnoreCase(html_output)) {
+ htmlDir = s;
+ } else if (doing.equalsIgnoreCase(xml_output)) {
+ xmlFile = s;
+ } else if (doing.equalsIgnoreCase(input_srcs)) {
+ inputSrcs.add(s);
+ } else if (doing.equalsIgnoreCase(input_builds)) {
+ inputBuilds.add(s);
+ } else if (doing.equalsIgnoreCase(title)) {
+ titleValue = s;
+ } else if (doing.equalsIgnoreCase(input_file)) {
+ inputFile = s;
+ } else {
+ warnOrDie("Unknown processing of switch of " + doing);
+ }
+
+ }
+ }
+ }
+ File finalHtmlFile = null;
+ if (checkIfNotNullOrEmpty(htmlDir)) {
+ finalHtmlFile = new File(htmlDir);
+ if (finalHtmlFile.exists()) {
+ warnOrDie("Warning, direcotry for html report exists! " + finalHtmlFile.getAbsolutePath());
+ }
+ }
+ File finalXmlFile = null;
+ if (checkIfNotNullOrEmpty(xmlFile)) {
+ finalXmlFile = new File(xmlFile);
+ if (finalXmlFile.exists()) {
+ warnOrDie("Warning, file for xml report exists! " + finalHtmlFile.getAbsolutePath());
+ }
+ }
+ if (chckIfNUllOrEmpty(titleValue)) {
+ titleValue = "Coverage report";
+ }
+ throwIfNullOrEmpty(inputFile, "No coverage data file specified!");
+ File finalInputFile = new File(inputFile);
+
+ ReportGenerator rg = new ReportGenerator(titleValue, finalInputFile, finalHtmlFile, finalXmlFile);
+
+ for (String string : inputSrcs) {
+ if (checkIfNotNullOrEmpty(string)) {
+ File f = new File(string);
+ if (!f.exists()) {
+ warnOrDie("Warning, input source " + f.getAbsolutePath() + " does not exists!");
+ }
+ rg.addSource(f);
+ }
+ }
+ for (String string : inputBuilds) {
+ if (checkIfNotNullOrEmpty(string)) {
+ File f = new File(string);
+ if (!f.exists()) {
+ warnOrDie("Warning, input build " + f.getAbsolutePath() + " does not exists!");
+ }
+ rg.addClasses(f);
+ }
+ }
+ return rg;
+ }
+
+ private static String throwIfNullOrEmpty(String outputFile, String message) throws RuntimeException {
+ if (chckIfNUllOrEmpty(outputFile)) {
+ throw new RuntimeException(message);
+ }
+ return outputFile;
+ }
+
+ private static boolean checkIfNotNullOrEmpty(String string) {
+ return string != null && string.trim().length() != 0;
+ }
+
+ private static boolean chckIfNUllOrEmpty(String outputFile) {
+ return outputFile == null || outputFile.trim().length() == 0;
+ }
+
+ private static void warnOrDie(String string) {
+ System.err.println(string);
+ warned = true;
+
+ }
+}
diff --git a/tests/jacoco-operator/org/jacoco/operator/MergeTask.java b/tests/jacoco-operator/org/jacoco/operator/MergeTask.java
new file mode 100644
index 0000000..02c1b80
--- /dev/null
+++ b/tests/jacoco-operator/org/jacoco/operator/MergeTask.java
@@ -0,0 +1,157 @@
+/*
+Copyright (C) 2012 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+ */
+package org.jacoco.operator;
+
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import org.jacoco.core.data.ExecutionDataReader;
+import org.jacoco.core.data.ExecutionDataStore;
+import org.jacoco.core.data.ExecutionDataWriter;
+import org.jacoco.core.data.SessionInfoStore;
+
+/**
+ * Task for merging a set of execution data store files into a single file
+ *
+ * Inspired by:
+ * https://raw.github.com/jacoco/jacoco/master/org.jacoco.ant/src/org/jacoco/ant/MergeTask.java
+ */
+public class MergeTask implements Runnable {
+
+ public static final String DEFAULT_NAME = "jacoco.exec";
+ private File destfile;
+ private final List<File> files = new ArrayList<File>(1);
+
+ public MergeTask(File destfile) {
+ this.destfile = destfile;
+ }
+
+ public MergeTask(File destfile, List<File> inputs) {
+ this.destfile = destfile;
+ files.addAll(inputs);
+ }
+
+ /**
+ * Sets the location of the merged data store
+ *
+ * @param destfile Destination data store location
+ */
+ public void setDestfile(final File destfile) {
+ this.destfile = destfile;
+ }
+
+ public void addInputFile(final File input) {
+ if (input != null) {
+ files.add(input);
+ }
+ }
+
+ public void addInputFiles(final List<File> input) {
+ files.addAll(input);
+ }
+
+ public void execute() throws IOException {
+ if (destfile == null) {
+ throw new RuntimeException("Destination file must be supplied");
+ }
+
+ final SessionInfoStore infoStore = new SessionInfoStore();
+ final ExecutionDataStore dataStore = new ExecutionDataStore();
+
+ loadSourceFiles(infoStore, dataStore);
+
+ OutputStream outputStream = null;
+ try {
+
+ outputStream = new BufferedOutputStream(new FileOutputStream(
+ destfile));
+ final ExecutionDataWriter dataWriter = new ExecutionDataWriter(
+ outputStream);
+ infoStore.accept(dataWriter);
+ dataStore.accept(dataWriter);
+ } finally {
+ if (outputStream != null) {
+ outputStream.close();
+ }
+ }
+
+ }
+
+ private void loadSourceFiles(final SessionInfoStore infoStore, final ExecutionDataStore dataStore) throws IOException {
+ if (files == null || files.isEmpty()) {
+ throw new RuntimeException("No input files");
+ }
+ final Iterator<?> resourceIterator = files.iterator();
+ while (resourceIterator.hasNext()) {
+ final File resource = (File) resourceIterator.next();
+
+ if (resource.isDirectory()) {
+ continue;
+ }
+ InputStream resourceStream = null;
+ try {
+ resourceStream = new FileInputStream(resource);
+ final ExecutionDataReader reader = new ExecutionDataReader(
+ resourceStream);
+ reader.setSessionInfoVisitor(infoStore);
+ reader.setExecutionDataVisitor(dataStore);
+ reader.read();
+ } finally {
+ if (resourceStream != null) {
+ resourceStream.close();
+ }
+ }
+ }
+ }
+
+ @Override
+ public void run() {
+ try {
+ execute();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+}
diff --git a/tests/jacoco-operator/org/jacoco/operator/ReportGenerator.java b/tests/jacoco-operator/org/jacoco/operator/ReportGenerator.java
new file mode 100644
index 0000000..84629f9
--- /dev/null
+++ b/tests/jacoco-operator/org/jacoco/operator/ReportGenerator.java
@@ -0,0 +1,292 @@
+/*
+Copyright (C) 2012 Red Hat, Inc.
+
+This file is part of IcedTea.
+
+IcedTea is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License as published by
+the Free Software Foundation, version 2.
+
+IcedTea is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with IcedTea; see the file COPYING. If not, write to
+the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA.
+
+Linking this library statically or dynamically with other modules is
+making a combined work based on this library. Thus, the terms and
+conditions of the GNU General Public License cover the whole
+combination.
+
+As a special exception, the copyright holders of this library give you
+permission to link this library with independent modules to produce an
+executable, regardless of the license terms of these independent
+modules, and to copy and distribute the resulting executable under
+terms of your choice, provided that you also meet, for each linked
+independent module, the terms and conditions of the license of that
+module. An independent module is a module which is not derived from
+or based on this library. If you modify this library, you may extend
+this exception to your version of the library, but you are not
+obligated to do so. If you do not wish to do so, delete this
+exception statement from your version.
+ */
+
+package org.jacoco.operator;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+import java.util.List;
+import org.jacoco.core.analysis.Analyzer;
+import org.jacoco.core.analysis.CoverageBuilder;
+import org.jacoco.core.analysis.IBundleCoverage;
+import org.jacoco.core.data.ExecutionDataReader;
+import org.jacoco.core.data.ExecutionDataStore;
+import org.jacoco.core.data.SessionInfoStore;
+import org.jacoco.report.DirectorySourceFileLocator;
+import org.jacoco.report.FileMultiReportOutput;
+import org.jacoco.report.IReportVisitor;
+import org.jacoco.report.MultiSourceFileLocator;
+import org.jacoco.report.html.HTMLFormatter;
+import org.jacoco.report.xml.XMLFormatter;
+
+/**
+ * This example creates a HTML report for eclipse like projects based on a
+ * single execution data store called jacoco.exec. The report contains no
+ * grouping information.
+ *
+ * The class files under test must be compiled with debug information, otherwise
+ * source highlighting will not work.
+ *
+ * Originally based on:
+ * http://www.eclemma.org/jacoco/trunk/doc/examples/java/ReportGenerator.java
+ */
+public class ReportGenerator implements Runnable {
+
+ private final String title;
+ private final File executionDataFile;
+ private final List<File> classesDirectories = new ArrayList<File>(1);
+ private final List<File> sourceDirectories = new ArrayList<File>(1);
+ private File reportDirectory;
+ private File xmlOutput;
+ private ExecutionDataStore executionDataStore;
+ private SessionInfoStore sessionInfoStore;
+ private String XML_DEF_NAME = "coverage-summary.xml";
+
+ /**
+ * Create a new generator based for the given project.
+ *
+ * @param projectDirectory
+ */
+ public ReportGenerator(final File projectDirectory) {
+ this.title = projectDirectory.getName();
+ this.executionDataFile = new File(projectDirectory, MergeTask.DEFAULT_NAME);
+ this.classesDirectories.add(new File(projectDirectory, "bin"));
+ this.sourceDirectories.add(new File(projectDirectory, "src"));
+ this.reportDirectory = new File(projectDirectory, "coveragereport");
+ this.xmlOutput = new File(projectDirectory, XML_DEF_NAME);
+ }
+
+ public ReportGenerator(String title, File exec, File classes, File sources, File htmlReport, File xmlReport) {
+ this.title = title;
+ this.executionDataFile = exec;
+ if (classes != null) {
+ this.classesDirectories.add(classes);
+ }
+ if (sources != null) {
+ this.sourceDirectories.add(sources);
+ }
+ this.reportDirectory = htmlReport;
+
+ this.xmlOutput = xmlReport;
+ }
+
+ public ReportGenerator(String title, File exec, List<File> classes, List<File> sources, File htmlReport, File xmlReport) {
+ this.title = title;
+ this.executionDataFile = exec;
+ if (classes != null) {
+ this.classesDirectories.addAll(classes);
+ }
+ if (sources != null) {
+ this.sourceDirectories.addAll(sources);
+ }
+ this.reportDirectory = htmlReport;
+ this.xmlOutput = xmlReport;
+ }
+
+ public ReportGenerator(String title, File exec, List<File> classes, List<File> sources, File report) {
+ this.title = title;
+ this.executionDataFile = exec;
+ if (classes != null) {
+ this.classesDirectories.addAll(classes);
+ }
+ if (sources != null) {
+ this.sourceDirectories.addAll(sources);
+ }
+ this.reportDirectory = report;
+ this.xmlOutput = new File(report, XML_DEF_NAME);
+ }
+
+ public ReportGenerator(String title, File exec, File htmlReport, File xmlReport) {
+ this.title = title;
+ this.executionDataFile = exec;
+ this.reportDirectory = htmlReport;
+ this.xmlOutput = xmlReport;
+ }
+
+ public ReportGenerator(String title, File exec, File report) {
+ this.title = title;
+ this.executionDataFile = exec;
+ this.reportDirectory = report;
+ this.xmlOutput = new File(report, XML_DEF_NAME);
+ }
+
+ public void addSource(File f) {
+ sourceDirectories.add(f);
+
+ }
+
+ public void addClasses(File f) {
+ classesDirectories.add(f);
+
+ }
+
+ /**
+ * Create the report.
+ *
+ * @throws IOException
+ */
+ public void execute() throws IOException {
+
+ // Read the jacoco.exec file. Multiple data stores could be merged
+ // at this point
+ loadExecutionData();
+
+ // Run the structure analyzer on a single class folder to build up
+ // the coverage model. The process would be similar if your classes
+ // were in a jar file. Typically you would create a bundle for each
+ // class folder and each jar you want in your report. If you have
+ // more than one bundle you will need to add a grouping node to your
+ // report
+ final IBundleCoverage bundleCoverage = analyzeStructure();
+
+ if (reportDirectory != null) {
+ createHtmlReport(bundleCoverage);
+ }
+ if (xmlOutput != null) {
+ createXmlReport(bundleCoverage);
+ }
+
+ }
+
+ private void createHtmlReport(final IBundleCoverage bundleCoverage)
+ throws IOException {
+
+ // Create a concrete report visitor based on some supplied
+ // configuration. In this case we use the defaults
+ final HTMLFormatter htmlFormatter = new HTMLFormatter();
+ final IReportVisitor visitor = htmlFormatter.createVisitor(new FileMultiReportOutput(reportDirectory));
+
+ // Initialize the report with all of the execution and session
+ // information. At this point the report doesn't know about the
+ // structure of the report being created
+ visitor.visitInfo(sessionInfoStore.getInfos(),
+ executionDataStore.getContents());
+
+ // Populate the report structure with the bundle coverage information.
+ // Call visitGroup if you need groups in your report.
+ MultiSourceFileLocator msf = new MultiSourceFileLocator(4);
+ for (File file : sourceDirectories) {
+ msf.add(new DirectorySourceFileLocator(
+ file, "utf-8", 4));
+ }
+
+ visitor.visitBundle(bundleCoverage, msf);
+
+ // Signal end of structure information to allow report to write all
+ // information out
+ visitor.visitEnd();
+
+ }
+
+ private void createXmlReport(final IBundleCoverage bundleCoverage)
+ throws IOException {
+
+ OutputStream fos = new FileOutputStream(xmlOutput);
+ try {
+ // Create a concrete report visitor based on some supplied
+ // configuration. In this case we use the defaults
+ final XMLFormatter htmlFormatter = new XMLFormatter();
+ final IReportVisitor visitor = htmlFormatter.createVisitor(fos);
+
+ // Initialize the report with all of the execution and session
+ // information. At this point the report doesn't know about the
+ // structure of the report being created
+ visitor.visitInfo(sessionInfoStore.getInfos(),
+ executionDataStore.getContents());
+
+ // Populate the report structure with the bundle coverage information.
+ // Call visitGroup if you need groups in your report.
+ visitor.visitBundle(bundleCoverage, null);
+
+
+ // Signal end of structure information to allow report to write all
+ // information out
+ visitor.visitEnd();
+ } finally {
+ if (fos != null) {
+ fos.close();
+ }
+ }
+
+ }
+
+ private void loadExecutionData() throws IOException {
+ final FileInputStream fis = new FileInputStream(executionDataFile);
+ try {
+ final ExecutionDataReader executionDataReader = new ExecutionDataReader(
+ fis);
+ executionDataStore = new ExecutionDataStore();
+ sessionInfoStore = new SessionInfoStore();
+
+ executionDataReader.setExecutionDataVisitor(executionDataStore);
+ executionDataReader.setSessionInfoVisitor(sessionInfoStore);
+
+ while (executionDataReader.read()) {
+ }
+ } finally {
+ if (fis != null) {
+ fis.close();
+ }
+ }
+ }
+
+ private IBundleCoverage analyzeStructure() throws IOException {
+ final CoverageBuilder coverageBuilder = new CoverageBuilder();
+ final Analyzer analyzer = new Analyzer(executionDataStore,
+ coverageBuilder);
+ for (File file : classesDirectories) {
+ analyzer.analyzeAll(file);
+
+ }
+
+ return coverageBuilder.getBundle(title);
+ }
+
+ @Override
+ public void run() {
+ try {
+ execute();
+ } catch (IOException ex) {
+ throw new RuntimeException(ex);
+ }
+ }
+
+}