Class skipped by JaCoCo

174 views
Skip to first unread message

andries...@gmail.com

unread,
Mar 23, 2018, 5:01:56 AM3/23/18
to JaCoCo and EclEmma Users
Hi,

I'm struggling with the following and running out of options I can think of...

The original issue was that JaCoCo had a different coverage than IntelliJ (java code running via maven).

Diving into the JaCoCo coverage report I found out that a complete package was simply not covered. In the sessions of JaCoCo, the classes of that package were also not listed. (Other packages had no issues and had coverage and where also listed in the sessions)

I dumped the classes by specifying a classDumpDir and there only the test classes of that package were dumped. The classes under test were nowhere to be found.

I already found out that the only reason for a class not to be dumped, is that it's filtered prior to instrumentation (see https://github.com/jacoco/jacoco/blob/master/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java#L85 ) and that these are the only reasons to be filtered: ( see https://github.com/jacoco/jacoco/blob/master/org.jacoco.agent.rt/src/org/jacoco/agent/rt/internal/CoverageTransformer.java#L113-L132 )

- they are explicitly excluded / not included according to your configuration
- their class loader excluded according to your configuration ( see agent option "exclclassloader", which defaults to "sun.reflect.DelegatingClassLoader" )
- their class loader does not preserve source location ( see agent option "inclnolocationclasses", which defaults to "false" )
- they are loaded by a bootstrap class loader

Next to that I also learned that there's a known limitation in JaCoCo, namely that it has no coverage for exceptions.
The latter isn't what's happening here as there are also tests without exceptions in them and even when I remove these tests altogether, the issue stays the same.

So based upon the info from above I tried the following section in the pom:

<plugin>
<groupId>org.jacoco</groupId>
<artifactId>jacoco-maven-plugin</artifactId>
<version>${jacoco.version}</version>
<executions>
<execution>
<id>agent</id>
<goals>
<goal>prepare-agent</goal>
</goals>
</execution>
<execution>
<id>report</id>
<phase>site</phase>
<goals>
<goal>report</goal>
</goals>
</execution>
</executions>
<configuration>
<propertyName>jacoco.agent.argLine</propertyName>
<!-- default: argLine -->
<includes>
<include>com/sometool/**</include>
</includes>
<exclClassLoaders>false</exclClassLoaders>
<inclBootstrapClasses>true</inclBootstrapClasses>
<inclNoLocationClasses>true</inclNoLocationClasses>
<destFile>${project.build.directory}/jacoco-integration.exec</destFile>
<!-- agent -->
<dataFile>${project.build.directory}/jacoco-integration.exec</dataFile>
<!-- report -->
</configuration>
</plugin>

But still I have no classes dumped by JaCoCo from that package let alone some coverage....

Any help would be greatly appreciated. Probably it's something simple but I'm simply not seeing it.

Marco

Evgeny Mandrikov

unread,
Mar 23, 2018, 5:30:43 AM3/23/18
to JaCoCo and EclEmma Users
Please provide complete reproducer of your problem.

andries...@gmail.com

unread,
Mar 23, 2018, 6:06:23 AM3/23/18
to JaCoCo and EclEmma Users
Hi Evgeny,

Ok I'll add the code hereunder as it's going to be open source code anyway.

This is the class under test:

/*
This file is part of the iText (R) project.
Copyright (c) 1998-2017 iText Group NV
Authors: iText Software.

This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License version 3
as published by the Free Software Foundation with the addition of the
following permission added to Section 15 as permitted in Section 7(a):
FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
OF THIRD PARTY RIGHTS

This program 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 Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program; if not, see http://www.gnu.org/licenses or write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA, 02110-1301 USA, or download the license from the following URL:
http://itextpdf.com/terms-of-use/

The interactive user interfaces in modified source and object code versions
of this program must display Appropriate Legal Notices, as required under
Section 5 of the GNU Affero General Public License.

In accordance with Section 7(b) of the GNU Affero General Public License,
a covered work must retain the producer line in every PDF that is created
or manipulated using iText.

You can be released from the requirements of the license by purchasing
a commercial license. Buying such a license is mandatory as soon as you
develop commercial activities involving the iText software without
disclosing the source code of your own applications.
These activities include: offering paid services to customers as an ASP,
serving PDFs on the fly in a web application, shipping iText with a closed
source product.

For more information, please contact iText Software Corp. at this
address: sa...@itextpdf.com
*/
package com.itextpdf.svg.utils;

import com.itextpdf.kernel.geom.AffineTransform;
import com.itextpdf.svg.exceptions.SvgLogMessageConstant;
import com.itextpdf.svg.exceptions.SvgProcessingException;

import java.util.ArrayList;
import java.util.List;
import java.util.StringTokenizer;

/**
* Utility class responsible for converting Strings containing transformation declarations
* into AffineTransform objects.
*
* This class only supports the transformations as described in the SVG specification:
* - matrix
* - rotate
* - scale
* - skewX
* - skewY
* - translate
*/
public final class TransformUtils {

/**
* Keyword for matrix transformations. Accepts 6 values.
*
* matrix(0 1 2 3 4 5)
*/
private static final String MATRIX = "MATRIX";

/**
* Keyword for rotation transformation. Accepts either 1 or 3 values.
* In the case of 1 value, x and y are assumed to be the origin of the user space.
*
* rotate(angle x y)
* rotate(angle)
*/
private static final String ROTATE = "ROTATE";

/**
* Keyword for scale transformation. Accepts either 1 or 2 values.
* In the case of 1 value, the second value is assumed to be the same as the first one.
*
* scale(x y)
* scale(x)
*/
private static final String SCALE = "SCALE";

/**
* Keyword for skewX transformation. Accepts 1 value.
*
* skewX(angle)
*/
private static final String SKEWX = "SKEWX";

/**
* Keyword for skewY transformation. Accepts 1 value.
*
* skewY(angle)
*/
private static final String SKEWY = "SKEWY";

/**
* Keyword for translate transformation. Accepts either 1 or 2 values.
* In the case of 1 value, the y value is assumed to be 0.
*
* translate(x y)
* translate(x)
*/
private static final String TRANSLATE = "TRANSLATE";

private TransformUtils() {}

/**
* Converts a string containing a transform declaration into an AffineTransform object.
* This class only supports the transformations as described in the SVG specification:
* - matrix
* - translate
* - skewx
* - skewy
* - rotate
* - scale
*
* @param transform value to be parsed
* @return the AffineTransform object
*/
public static AffineTransform parseTransform(String transform) {
if (transform == null ) {
throw new SvgProcessingException(SvgLogMessageConstant.TRANSFORM_NULL);
}

if (transform.isEmpty()) {
throw new SvgProcessingException(SvgLogMessageConstant.TRANSFORM_EMPTY);
}

AffineTransform matrix = new AffineTransform();

List<String> listWithTransformations = splitString(transform);

for (String transformation : listWithTransformations) {
AffineTransform newMatrix = transformationStringToMatrix(transformation);

if (newMatrix != null) {
matrix.concatenate(newMatrix);
}
}

return matrix;
}

/**
* A transformation attribute can encompass multiple transformation operation (e.g. "translate(10,20) scale(30,40)".
* This method splits the original transformation string into multiple strings so that they can be handled separately.
*
* @param transform the transformation value
* @return a list containing strings describing a single transformation operation
*/
private static List<String> splitString(String transform) {
ArrayList<String> list = new ArrayList<>();
StringTokenizer tokenizer = new StringTokenizer(transform, ")", false);

while (tokenizer.hasMoreTokens()) {
list.add(tokenizer.nextToken().trim() + ")");
}

return list;
}

/**
* This method decides which transformation operation the given transformation strings maps onto.
*
* @param transformation string containing a transformation operation
* @return the mapped AffineTransform object
*/
private static AffineTransform transformationStringToMatrix(String transformation) {
String name = getNameFromString(transformation).toUpperCase();

if ( name.isEmpty() ) {
throw new SvgProcessingException(SvgLogMessageConstant.INVALID_TRANSFORM_DECLARATION);
}

switch (name) {
case MATRIX:
return createMatrixTransformation(getValuesFromTransformationString(transformation));
case TRANSLATE:
return createTranslateTransformation(getValuesFromTransformationString(transformation));
case SCALE:
return createScaleTransformation(getValuesFromTransformationString(transformation));
case ROTATE:
return createRotationTransformation(getValuesFromTransformationString(transformation));
case SKEWX:
return createSkewXTransformation(getValuesFromTransformationString(transformation));
case SKEWY:
return createSkewYTransformation(getValuesFromTransformationString(transformation));
default:
throw new SvgProcessingException(SvgLogMessageConstant.UNKNOWN_TRANSFORMATION_TYPE);
}
}

/**
* Creates a skewY transformation.
*
* @param values values of the transformation
* @return AffineTransform for the skew operation
*/
private static AffineTransform createSkewYTransformation(List<String> values) {
if (values.size() != 1) {
throw new SvgProcessingException(SvgLogMessageConstant.TRANSFORM_INCORRECT_NUMBER_OF_VALUES);
}

double tan = Math.tan(Math.toRadians(SvgCssUtils.parseFloat(values.get(0))));

return new AffineTransform(1, 0, tan, 1, 0, 0);
}

/**
* Creates a skewX transformation.
*
* @param values values of the transformation
* @return AffineTransform for the skew operation
*/
private static AffineTransform createSkewXTransformation(List<String> values) {
if (values.size() != 1) {
throw new SvgProcessingException(SvgLogMessageConstant.TRANSFORM_INCORRECT_NUMBER_OF_VALUES);
}

double tan = Math.tan(Math.toRadians(SvgCssUtils.parseFloat(values.get(0))));

return new AffineTransform(1, tan, 0, 1, 0, 0);
}

/**
* Creates a rotate transformation.
*
* @param values values of the transformation
* @return AffineTransform for the rotate operation
*/
private static AffineTransform createRotationTransformation(List<String> values) {
if (values.size() != 1 && values.size() != 3) {
throw new SvgProcessingException(SvgLogMessageConstant.TRANSFORM_INCORRECT_NUMBER_OF_VALUES);
}

double angle = Math.toRadians(SvgCssUtils.parseFloat(values.get(0)));

if (values.size() == 3) {
float centerX = SvgCssUtils.parseFloat(values.get(1));
float centerY = SvgCssUtils.parseFloat(values.get(2));
return AffineTransform.getRotateInstance(angle, centerX, centerY);
}

return AffineTransform.getRotateInstance(angle);
}

/**
* Creates a scale transformation.
*
* @param values values of the transformation
* @return AffineTransform for the scale operation
*/
private static AffineTransform createScaleTransformation(List<String> values) {
if (values.size() == 0 || values.size() > 2) {
throw new SvgProcessingException(SvgLogMessageConstant.TRANSFORM_INCORRECT_NUMBER_OF_VALUES);
}

float scaleX = SvgCssUtils.parseFloat(values.get(0));
float scaleY = values.size() == 2 ? SvgCssUtils.parseFloat(values.get(1)) : scaleX;

return AffineTransform.getScaleInstance(scaleX, scaleY);
}

/**
* Creates a translate transformation.
*
* @param values values of the transformation
* @return AffineTransform for the translate operation
*/
private static AffineTransform createTranslateTransformation(List<String> values) {
if (values.size() == 0 || values.size() > 2) {
throw new SvgProcessingException(SvgLogMessageConstant.TRANSFORM_INCORRECT_NUMBER_OF_VALUES);
}

float translateX = SvgCssUtils.parseFloat(values.get(0));
float translateY = values.size() == 2 ? SvgCssUtils.parseFloat(values.get(1)) : 0;

return AffineTransform.getTranslateInstance(translateX, translateY);
}

/**
* Creates a matrix transformation.
*
* @param values values of the transformation
* @return AffineTransform for the matrix keyword
*/
private static AffineTransform createMatrixTransformation(List<String> values) {
if (values.size() != 6) {
throw new SvgProcessingException(SvgLogMessageConstant.TRANSFORM_INCORRECT_NUMBER_OF_VALUES);
}

float a = SvgCssUtils.parseFloat(values.get(0));
float b = SvgCssUtils.parseFloat(values.get(1));
float c = SvgCssUtils.parseFloat(values.get(2));
float d = SvgCssUtils.parseFloat(values.get(3));
float e = SvgCssUtils.parseFloat(values.get(4));
float f = SvgCssUtils.parseFloat(values.get(5));

return new AffineTransform(a, b, c, d, e, f);
}

/**
* This method extracts the transformation name given a transformation.
*
* @param transformation the transformation
* @return the name of the transformation
*/
private static String getNameFromString(String transformation) {
int indexOfParenthesis = transformation.indexOf("(");

if ( indexOfParenthesis == -1 ) {
throw new SvgProcessingException(SvgLogMessageConstant.INVALID_TRANSFORM_DECLARATION);
}

return transformation.substring(0, transformation.indexOf("("));
}

/**
* This method extracts the values from a transformation.
*
* @param transformation the transformation
* @return values of the transformation
*/
private static List<String> getValuesFromTransformationString(String transformation) {
String numbers = transformation.substring(transformation.indexOf('(') + 1, transformation.indexOf(')'));

return SvgCssUtils.splitValueList(numbers);
}
}

This is the test class:

package com.itextpdf.svg.utils;

import com.itextpdf.kernel.geom.AffineTransform;
import com.itextpdf.svg.exceptions.SvgLogMessageConstant;
import com.itextpdf.svg.exceptions.SvgProcessingException;
import com.itextpdf.test.annotations.type.UnitTest;

import org.junit.Assert;
import org.junit.Rule;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.rules.ExpectedException;

@Category(UnitTest.class)
public class TransformUtilsTest {

@Rule
public ExpectedException junitExpectedException = ExpectedException.none();

@Test
public void nullStringTest() {
junitExpectedException.expect(SvgProcessingException.class);
junitExpectedException.expectMessage(SvgLogMessageConstant.TRANSFORM_NULL);

TransformUtils.parseTransform(null);
}

@Test
public void emptyTest() {
junitExpectedException.expect(SvgProcessingException.class);
junitExpectedException.expectMessage(SvgLogMessageConstant.TRANSFORM_EMPTY);

TransformUtils.parseTransform("");
}

@Test
public void noTransformationTest() {
junitExpectedException.expect(SvgProcessingException.class);
junitExpectedException.expectMessage(SvgLogMessageConstant.INVALID_TRANSFORM_DECLARATION);

TransformUtils.parseTransform("Lorem ipsum");
}

@Test
public void wrongTypeOfValuesTest() {
junitExpectedException.expect(SvgProcessingException.class);
junitExpectedException.expectMessage(SvgLogMessageConstant.FLOAT_PARSING_NAN);

TransformUtils.parseTransform("matrix(a b c d e f)");
}

@Test
public void tooManyParenthesesTest() {
junitExpectedException.expect(SvgProcessingException.class);
junitExpectedException.expectMessage(SvgLogMessageConstant.INVALID_TRANSFORM_DECLARATION);

TransformUtils.parseTransform("(((())))");
}

@Test
public void noClosingParenthesisTest() {
AffineTransform expected = new AffineTransform(0d, 0d, 0d, 0d, 0d, 0d);
AffineTransform actual = TransformUtils.parseTransform("matrix(0 0 0 0 0 0");

Assert.assertEquals(expected, actual);
}

@Test
public void mixedCaseTest() {
AffineTransform expected = new AffineTransform(0d, 0d, 0d, 0d, 0d, 0d);
AffineTransform actual = TransformUtils.parseTransform("maTRix(0 0 0 0 0 0)");

Assert.assertEquals(expected, actual);
}

@Test
public void upperCaseTest() {
AffineTransform expected = new AffineTransform(0d, 0d, 0d, 0d, 0d, 0d);
AffineTransform actual = TransformUtils.parseTransform("MATRIX(0 0 0 0 0 0)");

Assert.assertEquals(expected, actual);
}

@Test
public void whitespaceTest() {
AffineTransform expected = new AffineTransform(0d, 0d, 0d, 0d, 0d, 0d);
AffineTransform actual = TransformUtils.parseTransform("matrix(0 0 0 0 0 0)");

Assert.assertEquals(expected, actual);
}

@Test
public void commasWithWhitespaceTest() {
AffineTransform expected = new AffineTransform(10d,20d,30d,40d,50d, 60d);
AffineTransform actual = TransformUtils.parseTransform("matrix(10, 20, 30, 40, 50, 60)");

Assert.assertEquals(expected, actual);
}

@Test
public void commasTest() {
AffineTransform expected = new AffineTransform(10d,20d,30d,40d,50d, 60d);
AffineTransform actual = TransformUtils.parseTransform("matrix(10,20,30,40,50,60)");

Assert.assertEquals(expected, actual);
}

@Test
public void combinedTransformTest() {
AffineTransform actual = TransformUtils.parseTransform("translate(40,20) scale(3)");
AffineTransform expected = new AffineTransform(3,0,0,3,40,20);

Assert.assertEquals(actual, expected);
}

@Test
public void combinedReverseTransformTest() {
AffineTransform actual = TransformUtils.parseTransform("scale(3) translate(40,20)");
AffineTransform expected = new AffineTransform(3,0,0,3,120,60);

Assert.assertEquals(actual, expected);
}

@Test
public void doubleTransformationTest() {
double expectedScaleValue = Math.pow(43d, 2);
AffineTransform expected = new AffineTransform(expectedScaleValue, 0d, 0d, expectedScaleValue, 0d, 0d);
AffineTransform actual = TransformUtils.parseTransform("scale(43) scale(43)");

Assert.assertEquals(expected, actual);
}

@Test
public void oppositeTransformationSequenceTest() {
AffineTransform expected = new AffineTransform(1,0,0,1,0,0);
AffineTransform actual = TransformUtils.parseTransform("translate(10 10) translate(-10 -10)");

Assert.assertEquals(expected, actual);
}

@Test
public void unknownTransformationTest() {
junitExpectedException.expect(SvgProcessingException.class);
junitExpectedException.expectMessage(SvgLogMessageConstant.UNKNOWN_TRANSFORMATION_TYPE);

TransformUtils.parseTransform("unknown(1 2 3)");
}
}

Hope this helps....

Evgeny Mandrikov

unread,
Mar 23, 2018, 6:19:21 AM3/23/18
to JaCoCo and EclEmma Users
Complete - means that others should be able to run it, what includes all required configuration for compilation and etc. What you provided can't be compiled in absence of dependent classes. And you know about archivers and attachments, right?

andries...@gmail.com

unread,
Mar 23, 2018, 6:23:39 AM3/23/18
to JaCoCo and EclEmma Users
What do you mean with archivers and attachments?
Anyhow I won't be able to put everything out there as it's not open source currently....

Thanks for the effort anyway

Evgeny Mandrikov

unread,
Mar 23, 2018, 7:31:15 AM3/23/18
to JaCoCo and EclEmma Users
Hope you understand that hard to debug/speculate about runtime behavior without ability to try/run.

And I'm not asking you to send us all your big application - do a bit of homework to extract / reduce your problem from your big application into a small yet self-sufficient set of files that you can archive and send as attachment.

Thank you for your understanding.

Marco Andries

unread,
Mar 23, 2018, 7:41:12 AM3/23/18
to jac...@googlegroups.com
No problem. Surely I understand but currently I don't have time to do some homework....

For now we will leave it as is. When it's posted on github and out there for everyone tot beste used, perhaps I'll contact you again.

Marco



Op vr 23 mrt. 2018 12:31 schreef Evgeny Mandrikov <mand...@gmail.com>:
--
You received this message because you are subscribed to a topic in the Google Groups "JaCoCo and EclEmma Users" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/jacoco/VQ_M9faKHpg/unsubscribe.
To unsubscribe from this group and all its topics, send an email to jacoco+un...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/jacoco/1d57751c-5f1d-4dda-aeff-8385603e3a3e%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Reply all
Reply to author
Forward
This conversation is locked
You cannot reply and perform actions on locked conversations.
0 new messages