OceanLife OceanLife - 1 month ago 11
Android Question

Sporadic Robolectric test failures when running inside Docker container

I am trying to move our Android team onto a Cloud-based CI solution. Other departments within the company have completed this task and we have an Enterprise TeamCity License leveraging Linux EC2s with Docker installed. The entire company uses TC so although CircleCI etc are great options, I am exploring using our own TC solution.

To date, it has gone well and using Amazon ECR we have hosted our Docker image that has the required Android SDK, Gradle and Java components installed. Our builds much of the time are a-OK but still fail more than our physical local agents owing to an error when running some of the Robolectric test cases. Has anyone seen this before?

java.lang.NoClassDefFoundError: java.lang.NoClassDefFoundError: Landroid/content/res/Resources;
java.lang.NoClassDefFoundError: Landroid/content/res/Resources;
at java.lang.Class.getDeclaredFields0(Native Method)
at java.lang.Class.privateGetDeclaredFields(Class.java:2583)
at java.lang.Class.getDeclaredFields(Class.java:1916)
at org.junit.runners.model.TestClass.getSortedDeclaredFields(TestClass.java:77)
at org.junit.runners.model.TestClass.scanAnnotatedMembers(TestClass.java:70)
at org.junit.runners.model.TestClass.<init>(TestClass.java:57)
at org.junit.runners.ParentRunner.createTestClass(ParentRunner.java:88)
at org.junit.runners.ParentRunner.<init>(ParentRunner.java:83)
at org.junit.runners.BlockJUnit4ClassRunner.<init>(BlockJUnit4ClassRunner.java:65)
at org.robolectric.internal.SandboxTestRunner$HelperTestRunner.<init>(SandboxTestRunner.java:242)
at org.robolectric.RobolectricTestRunner$HelperTestRunner.<init>(RobolectricTestRunner.java:467)
at org.robolectric.RobolectricTestRunner.getHelperTestRunner(RobolectricTestRunner.java:319)
at org.robolectric.internal.SandboxTestRunner$2.evaluate(SandboxTestRunner.java:188)
at org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:109)
at org.robolectric.internal.SandboxTestRunner.runChild(SandboxTestRunner.java:36)
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
at org.robolectric.internal.SandboxTestRunner$1.evaluate(SandboxTestRunner.java:63)
at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:114)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:57)
at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:66)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:51)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:109)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:377)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.ClassNotFoundException: couldn't load android.content.res.Resources
at org.robolectric.internal.bytecode.SandboxClassLoader.getByteCode(SandboxClassLoader.java:165)
at org.robolectric.internal.bytecode.SandboxClassLoader.maybeInstrumentClass(SandboxClassLoader.java:108)
at org.robolectric.internal.bytecode.SandboxClassLoader.findClass(SandboxClassLoader.java:101)
at java.lang.ClassLoader.loadClass(ClassLoader.java:424)
at java.lang.ClassLoader.loadClass(ClassLoader.java:357)
... 48 more
Caused by: java.util.zip.ZipException: invalid LOC header (bad signature)
at java.util.zip.ZipFile.read(Native Method)
at java.util.zip.ZipFile.access$1400(ZipFile.java:60)
at java.util.zip.ZipFile$ZipFileInputStream.read(ZipFile.java:717)
at java.util.zip.ZipFile$ZipFileInflaterInputStream.fill(ZipFile.java:419)
at java.util.zip.InflaterInputStream.read(InflaterInputStream.java:158)
at java.io.FilterInputStream.read(FilterInputStream.java:133)
at java.io.FilterInputStream.read(FilterInputStream.java:107)
at org.robolectric.util.Util.copy(Util.java:21)
at org.robolectric.util.Util.readBytes(Util.java:38)
at org.robolectric.internal.bytecode.SandboxClassLoader.getByteCode(SandboxClassLoader.java:163)
... 52 more


A cursory Google points towards corrupt jars but this leaves me puzzled since the Docker environment is 'fresh' and the jars are resolved on each build (when caching isn't enabled...which it isn't). The Robolectric test configuration associated with this stack is;

@Config(constants = BuildConfig.class, sdk = 21)
@RunWith(CustomRobolectricRunner.class)
public class ResourcesTest {...}


Probably not the best test looking at it....but many other, better tests fail. The Docker image has Android SDK 21 (as another post suggested was the reason).

How can I (a) prove or disprove that it is a 'corrupted jar' (I assume the Robolectric jar?) and (b) take remedial action?

The Dockerfile being used can be viewed here.

Answer Source

So, my own CircleCI 2.0 build started to fail with something similar after the solution graduated from beta and in diagnosing and correcting that build, it lead me to the answer for this. This is a memory issue. The container is running out of memory and not failing gracefully. There were a few other signals that this was the case and I had started playing with JAVA_OPTS/GRADLE_OPTS settings. In the end specifying explicitly not to use the daemon and controlling the number of JVM forks solved it;

--no-daemon --max-workers 2

This thread has other permutations of the memory issues you will encounter with Docker + Java + Gradle + Android. A bit painful, but worth it.