package org.robolectric; import org.junit.Test; import org.junit.runner.RunWith; import org.robolectric.annotation.Implementation; import org.robolectric.annotation.Implements; import org.robolectric.annotation.RealObject; import org.robolectric.annotation.internal.Instrument; import org.robolectric.internal.SandboxTestRunner; import org.robolectric.shadow.api.Shadow; import org.robolectric.internal.ShadowExtractor; import org.robolectric.internal.bytecode.SandboxConfig; import java.lang.reflect.Field; import static org.assertj.core.api.Assertions.assertThat; @RunWith(SandboxTestRunner.class) public class ThreadSafetyTest { @Test @SandboxConfig(shadows = {InstrumentedThreadShadow.class}) public void shadowCreationShouldBeThreadsafe() throws Exception { Field field = InstrumentedThread.class.getDeclaredField("shadowFromOtherThread"); field.setAccessible(true); for (int i = 0; i < 100; i++) { // :-( InstrumentedThread instrumentedThread = new InstrumentedThread(); instrumentedThread.start(); Object shadowFromThisThread = ShadowExtractor.extract(instrumentedThread); instrumentedThread.join(); Object shadowFromOtherThread = field.get(instrumentedThread); assertThat(shadowFromThisThread).isSameAs(shadowFromOtherThread); } } @Instrument public static class InstrumentedThread extends Thread { InstrumentedThreadShadow shadowFromOtherThread; @Override public void run() { shadowFromOtherThread = (InstrumentedThreadShadow) ShadowExtractor.extract(this); } } @Implements(InstrumentedThread.class) public static class InstrumentedThreadShadow { @RealObject InstrumentedThread realObject; @Implementation public void run() { Shadow.directlyOn(realObject, InstrumentedThread.class, "run"); } } }