1. public class PublicStaticFields { 2. public static String className = PublicStaticFields.class.getCanonicalName(); public static String s; static { 5. s = "SsS"; 6. } }
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import org.junit.Test; public class TestPublicStaticFields { @Test public void test1() { assertEquals(PublicStaticFields.class.getCanonicalName(), PublicStaticFields.className); assertEquals("SsS", PublicStaticFields.s); } @Test public void test2() { // empty } @Test public void test3() { assertEquals(PublicStaticFields.class.getCanonicalName(), PublicStaticFields.className); assertEquals("SsS", PublicStaticFields.s); } @Test public void test4() { assertNotNull(new PublicStaticFields()); } }
Hi,IMO term "coverage of static fields" is quite vague. And so not clear what and why you want to measure?
From here and given what is explained below any tricks with ClassLoaders sound crazy.
JaCoCo does not record access to fields. It records execution of code that initializes fields. Code to initialize static fields is executed when class is initialized. So behavior that you observe is correct one, because class is initialized only once.
JaCoCo doesn't introduce any new ClassLoaders into application and doesn't do any tricks with existing ones.
If two ClassLoaders load class (not return already loaded class), then at runtime these two classes are different, with all the consequences. Including two initializations of two classes, memory allocation in JVM to represent each, two instrumentations, etc. Not counting that this might be not expected by your application and tests and hence they might work incorrectly - e.g. usage of instance of one class as an argument to the equals method of another class will give you false even if getClass().getName() is the same, classes were loaded from the same class file on disk and have the same code:class Foo {public boolean equals(Object o) {if (o == null || getClass() != o.getClass()) {return false;}...}}
As far as JaCoCo concerned: if at runtime you have two different classes that are actually the same code loaded by different class loaders, JaCoCo knows that their code is the same thanks to http://www.jacoco.org/jacoco/trunk/doc/classids.html So recorded coverage will be stored in a single place, even if there will be two instrumentations.
No code in class is executed by test3 and so 0 is a correct value, not 3.
And since no code is executed, then how it can fail? Something is missing in your example/explanation.
assertEquals("SsS", PublicStaticFields.s);assertEquals("fooBar", PublicStaticFields.s);What is the target of test3? What it should test / catch?
If target is a process of initialization and state after initialization, then you need to isolate this process in this test to guarantee that it happens here (note that if something else used class prior to test1, then even test1 won't observe initialization),
so indeed I don't see any other ways than separate JVM
or properly coded ClassLoader.
If initialization and its test do not have "ClassLoader leak", then JVM and GC will free memory that was used during this process. And as was said before - this won't impact memory consumed by JaCoCo. And I suppose time spent on instrumentation is negligibly small compared to the rest. You can do own benchmarks and compre cases with and without instrumentation/coverage - this is an interesting exercise and we'll be interested to know results. However as a rough approximation - time to instrument rt.jar (20693 classes) of JDK 8u131 on my not so fast machine is about 15 seconds (including reading from disk, unpacking from JAR, instrumentation, packing to JAR and writing back to disk).
On Wednesday, August 30, 2017 at 12:31:22 AM UTC+1, Evgeny Mandrikov wrote:No code in class is executed by test3 and so 0 is a correct value, not 3.Coverage of test3 is only 0 because test cases test1 and test3 are executed on thesame JVM. However, when test cases are executed in isolation (i.e., one JVM pertest) both test cases cover 3 lines.And since no code is executed, then how it can fail? Something is missing in your example/explanation.Lets change for a moment the following assertionassertEquals("SsS", PublicStaticFields.s);of test3 to something likeassertEquals("fooBar", PublicStaticFields.s);Now test3 fails, right? How would we explain that fails and does not cover anything?
What is the target of test3? What it should test / catch?Exactly the same as test1, the process of initialisation.
If target is a process of initialization and state after initialization, then you need to isolate this process in this test to guarantee that it happens here (note that if something else used class prior to test1, then even test1 won't observe initialization),Yes, I totally agree with you. Thing is, there are really bad test suites / cases out therethat are not coded properly. And I'm just trying to find a common solution that could beaccurate even for those shit test suites / cases.so indeed I don't see any other ways than separate JVMI know maven (by default) forks a new JVM per test class, but I do not know any optionto fork a new JVM per test case. Are you aware of any?
or properly coded ClassLoader.How could that be done? Can you please provide me some pointers?