1*f585d8a3SJacky Wang /* 2*f585d8a3SJacky Wang * Copyright (C) 2016 The Dagger Authors. 3*f585d8a3SJacky Wang * 4*f585d8a3SJacky Wang * Licensed under the Apache License, Version 2.0 (the "License"); 5*f585d8a3SJacky Wang * you may not use this file except in compliance with the License. 6*f585d8a3SJacky Wang * You may obtain a copy of the License at 7*f585d8a3SJacky Wang * 8*f585d8a3SJacky Wang * http://www.apache.org/licenses/LICENSE-2.0 9*f585d8a3SJacky Wang * 10*f585d8a3SJacky Wang * Unless required by applicable law or agreed to in writing, software 11*f585d8a3SJacky Wang * distributed under the License is distributed on an "AS IS" BASIS, 12*f585d8a3SJacky Wang * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13*f585d8a3SJacky Wang * See the License for the specific language governing permissions and 14*f585d8a3SJacky Wang * limitations under the License. 15*f585d8a3SJacky Wang */ 16*f585d8a3SJacky Wang 17*f585d8a3SJacky Wang package dagger.internal; 18*f585d8a3SJacky Wang 19*f585d8a3SJacky Wang import static com.google.common.truth.Truth.assertThat; 20*f585d8a3SJacky Wang import static org.junit.Assert.fail; 21*f585d8a3SJacky Wang 22*f585d8a3SJacky Wang import com.google.common.collect.Lists; 23*f585d8a3SJacky Wang import com.google.common.collect.Sets; 24*f585d8a3SJacky Wang import com.google.common.util.concurrent.Uninterruptibles; 25*f585d8a3SJacky Wang import dagger.Lazy; 26*f585d8a3SJacky Wang import java.util.List; 27*f585d8a3SJacky Wang import java.util.Set; 28*f585d8a3SJacky Wang import java.util.concurrent.Callable; 29*f585d8a3SJacky Wang import java.util.concurrent.CountDownLatch; 30*f585d8a3SJacky Wang import java.util.concurrent.ExecutorService; 31*f585d8a3SJacky Wang import java.util.concurrent.Executors; 32*f585d8a3SJacky Wang import java.util.concurrent.Future; 33*f585d8a3SJacky Wang import java.util.concurrent.atomic.AtomicInteger; 34*f585d8a3SJacky Wang import java.util.concurrent.atomic.AtomicReference; 35*f585d8a3SJacky Wang import org.junit.Test; 36*f585d8a3SJacky Wang import org.junit.runner.RunWith; 37*f585d8a3SJacky Wang import org.junit.runners.JUnit4; 38*f585d8a3SJacky Wang 39*f585d8a3SJacky Wang @RunWith(JUnit4.class) 40*f585d8a3SJacky Wang public class DoubleCheckTest { 41*f585d8a3SJacky Wang @Test provider_nullPointerException()42*f585d8a3SJacky Wang public void provider_nullPointerException() { 43*f585d8a3SJacky Wang try { 44*f585d8a3SJacky Wang DoubleCheck.provider(null); 45*f585d8a3SJacky Wang fail(); 46*f585d8a3SJacky Wang } catch (NullPointerException expected) { 47*f585d8a3SJacky Wang } 48*f585d8a3SJacky Wang } 49*f585d8a3SJacky Wang 50*f585d8a3SJacky Wang @Test lazy_nullPointerException()51*f585d8a3SJacky Wang public void lazy_nullPointerException() { 52*f585d8a3SJacky Wang try { 53*f585d8a3SJacky Wang DoubleCheck.lazy(null); 54*f585d8a3SJacky Wang fail(); 55*f585d8a3SJacky Wang } catch (NullPointerException expected) { 56*f585d8a3SJacky Wang } 57*f585d8a3SJacky Wang } 58*f585d8a3SJacky Wang 59*f585d8a3SJacky Wang private static final Provider<Object> DOUBLE_CHECK_OBJECT_PROVIDER = 60*f585d8a3SJacky Wang DoubleCheck.provider(Object::new); 61*f585d8a3SJacky Wang 62*f585d8a3SJacky Wang @Test doubleWrapping_provider()63*f585d8a3SJacky Wang public void doubleWrapping_provider() { 64*f585d8a3SJacky Wang assertThat(DoubleCheck.provider(DOUBLE_CHECK_OBJECT_PROVIDER)) 65*f585d8a3SJacky Wang .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER); 66*f585d8a3SJacky Wang } 67*f585d8a3SJacky Wang 68*f585d8a3SJacky Wang @Test doubleWrapping_lazy()69*f585d8a3SJacky Wang public void doubleWrapping_lazy() { 70*f585d8a3SJacky Wang assertThat(DoubleCheck.lazy(DOUBLE_CHECK_OBJECT_PROVIDER)) 71*f585d8a3SJacky Wang .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER); 72*f585d8a3SJacky Wang } 73*f585d8a3SJacky Wang 74*f585d8a3SJacky Wang @Test get()75*f585d8a3SJacky Wang public void get() throws Exception { 76*f585d8a3SJacky Wang int numThreads = 10; 77*f585d8a3SJacky Wang ExecutorService executor = Executors.newFixedThreadPool(numThreads); 78*f585d8a3SJacky Wang 79*f585d8a3SJacky Wang final CountDownLatch latch = new CountDownLatch(numThreads); 80*f585d8a3SJacky Wang LatchedProvider provider = new LatchedProvider(latch); 81*f585d8a3SJacky Wang final Lazy<Object> lazy = DoubleCheck.lazy(provider); 82*f585d8a3SJacky Wang 83*f585d8a3SJacky Wang List<Callable<Object>> tasks = Lists.newArrayListWithCapacity(numThreads); 84*f585d8a3SJacky Wang for (int i = 0; i < numThreads; i++) { 85*f585d8a3SJacky Wang tasks.add( 86*f585d8a3SJacky Wang () -> { 87*f585d8a3SJacky Wang latch.countDown(); 88*f585d8a3SJacky Wang return lazy.get(); 89*f585d8a3SJacky Wang }); 90*f585d8a3SJacky Wang } 91*f585d8a3SJacky Wang 92*f585d8a3SJacky Wang List<Future<Object>> futures = executor.invokeAll(tasks); 93*f585d8a3SJacky Wang 94*f585d8a3SJacky Wang assertThat(provider.provisions.get()).isEqualTo(1); 95*f585d8a3SJacky Wang Set<Object> results = Sets.newIdentityHashSet(); 96*f585d8a3SJacky Wang for (Future<Object> future : futures) { 97*f585d8a3SJacky Wang results.add(future.get()); 98*f585d8a3SJacky Wang } 99*f585d8a3SJacky Wang assertThat(results).hasSize(1); 100*f585d8a3SJacky Wang } 101*f585d8a3SJacky Wang 102*f585d8a3SJacky Wang private static class LatchedProvider implements Provider<Object> { 103*f585d8a3SJacky Wang final AtomicInteger provisions; 104*f585d8a3SJacky Wang final CountDownLatch latch; 105*f585d8a3SJacky Wang LatchedProvider(CountDownLatch latch)106*f585d8a3SJacky Wang LatchedProvider(CountDownLatch latch) { 107*f585d8a3SJacky Wang this.latch = latch; 108*f585d8a3SJacky Wang this.provisions = new AtomicInteger(); 109*f585d8a3SJacky Wang } 110*f585d8a3SJacky Wang 111*f585d8a3SJacky Wang @Override get()112*f585d8a3SJacky Wang public Object get() { 113*f585d8a3SJacky Wang if (latch != null) { 114*f585d8a3SJacky Wang Uninterruptibles.awaitUninterruptibly(latch); 115*f585d8a3SJacky Wang } 116*f585d8a3SJacky Wang provisions.incrementAndGet(); 117*f585d8a3SJacky Wang return new Object(); 118*f585d8a3SJacky Wang } 119*f585d8a3SJacky Wang } 120*f585d8a3SJacky Wang reentranceWithoutCondition_throwsStackOverflow()121*f585d8a3SJacky Wang @Test public void reentranceWithoutCondition_throwsStackOverflow() { 122*f585d8a3SJacky Wang final AtomicReference<Provider<Object>> doubleCheckReference = 123*f585d8a3SJacky Wang new AtomicReference<>(); 124*f585d8a3SJacky Wang Provider<Object> doubleCheck = DoubleCheck.provider(() -> doubleCheckReference.get().get()); 125*f585d8a3SJacky Wang doubleCheckReference.set(doubleCheck); 126*f585d8a3SJacky Wang try { 127*f585d8a3SJacky Wang doubleCheck.get(); 128*f585d8a3SJacky Wang fail(); 129*f585d8a3SJacky Wang } catch (StackOverflowError expected) {} 130*f585d8a3SJacky Wang } 131*f585d8a3SJacky Wang reentranceReturningSameInstance()132*f585d8a3SJacky Wang @Test public void reentranceReturningSameInstance() { 133*f585d8a3SJacky Wang final AtomicReference<Provider<Object>> doubleCheckReference = 134*f585d8a3SJacky Wang new AtomicReference<>(); 135*f585d8a3SJacky Wang final AtomicInteger invocationCount = new AtomicInteger(); 136*f585d8a3SJacky Wang final Object object = new Object(); 137*f585d8a3SJacky Wang Provider<Object> doubleCheck = DoubleCheck.provider(() -> { 138*f585d8a3SJacky Wang if (invocationCount.incrementAndGet() == 1) { 139*f585d8a3SJacky Wang doubleCheckReference.get().get(); 140*f585d8a3SJacky Wang } 141*f585d8a3SJacky Wang return object; 142*f585d8a3SJacky Wang }); 143*f585d8a3SJacky Wang doubleCheckReference.set(doubleCheck); 144*f585d8a3SJacky Wang assertThat(doubleCheck.get()).isSameInstanceAs(object); 145*f585d8a3SJacky Wang } 146*f585d8a3SJacky Wang reentranceReturningDifferentInstances_throwsIllegalStateException()147*f585d8a3SJacky Wang @Test public void reentranceReturningDifferentInstances_throwsIllegalStateException() { 148*f585d8a3SJacky Wang final AtomicReference<Provider<Object>> doubleCheckReference = 149*f585d8a3SJacky Wang new AtomicReference<>(); 150*f585d8a3SJacky Wang final AtomicInteger invocationCount = new AtomicInteger(); 151*f585d8a3SJacky Wang Provider<Object> doubleCheck = DoubleCheck.provider(() -> { 152*f585d8a3SJacky Wang if (invocationCount.incrementAndGet() == 1) { 153*f585d8a3SJacky Wang doubleCheckReference.get().get(); 154*f585d8a3SJacky Wang } 155*f585d8a3SJacky Wang return new Object(); 156*f585d8a3SJacky Wang }); 157*f585d8a3SJacky Wang doubleCheckReference.set(doubleCheck); 158*f585d8a3SJacky Wang try { 159*f585d8a3SJacky Wang doubleCheck.get(); 160*f585d8a3SJacky Wang fail(); 161*f585d8a3SJacky Wang } catch (IllegalStateException expected) {} 162*f585d8a3SJacky Wang } 163*f585d8a3SJacky Wang 164*f585d8a3SJacky Wang @Test instanceFactoryAsLazyDoesNotWrap()165*f585d8a3SJacky Wang public void instanceFactoryAsLazyDoesNotWrap() { 166*f585d8a3SJacky Wang Factory<Object> factory = InstanceFactory.create(new Object()); 167*f585d8a3SJacky Wang assertThat(DoubleCheck.lazy(factory)).isSameInstanceAs(factory); 168*f585d8a3SJacky Wang } 169*f585d8a3SJacky Wang } 170