171 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Java
		
	
	
	
			
		
		
	
	
			171 lines
		
	
	
		
			5.2 KiB
		
	
	
	
		
			Java
		
	
	
	
| /*
 | |
|  * Copyright (C) 2016 The Dagger Authors.
 | |
|  *
 | |
|  * Licensed under the Apache License, Version 2.0 (the "License");
 | |
|  * you may not use this file except in compliance with the License.
 | |
|  * You may obtain a copy of the License at
 | |
|  *
 | |
|  * http://www.apache.org/licenses/LICENSE-2.0
 | |
|  *
 | |
|  * Unless required by applicable law or agreed to in writing, software
 | |
|  * distributed under the License is distributed on an "AS IS" BASIS,
 | |
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | |
|  * See the License for the specific language governing permissions and
 | |
|  * limitations under the License.
 | |
|  */
 | |
| 
 | |
| package dagger.internal;
 | |
| 
 | |
| import static com.google.common.truth.Truth.assertThat;
 | |
| import static org.junit.Assert.fail;
 | |
| 
 | |
| import com.google.common.collect.Lists;
 | |
| import com.google.common.collect.Sets;
 | |
| import com.google.common.util.concurrent.Uninterruptibles;
 | |
| import dagger.Lazy;
 | |
| import java.util.List;
 | |
| import java.util.Set;
 | |
| import java.util.concurrent.Callable;
 | |
| import java.util.concurrent.CountDownLatch;
 | |
| import java.util.concurrent.ExecutorService;
 | |
| import java.util.concurrent.Executors;
 | |
| import java.util.concurrent.Future;
 | |
| import java.util.concurrent.atomic.AtomicInteger;
 | |
| import java.util.concurrent.atomic.AtomicReference;
 | |
| import javax.inject.Provider;
 | |
| import org.junit.Test;
 | |
| import org.junit.runner.RunWith;
 | |
| import org.junit.runners.JUnit4;
 | |
| 
 | |
| @RunWith(JUnit4.class)
 | |
| public class DoubleCheckTest {
 | |
|   @Test
 | |
|   public void provider_nullPointerException() {
 | |
|     try {
 | |
|       DoubleCheck.provider(null);
 | |
|       fail();
 | |
|     } catch (NullPointerException expected) {
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   @Test
 | |
|   public void lazy_nullPointerException() {
 | |
|     try {
 | |
|       DoubleCheck.lazy(null);
 | |
|       fail();
 | |
|     } catch (NullPointerException expected) {
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   private static final Provider<Object> DOUBLE_CHECK_OBJECT_PROVIDER =
 | |
|       DoubleCheck.provider(Object::new);
 | |
| 
 | |
|   @Test
 | |
|   public void doubleWrapping_provider() {
 | |
|     assertThat(DoubleCheck.provider(DOUBLE_CHECK_OBJECT_PROVIDER))
 | |
|         .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER);
 | |
|   }
 | |
| 
 | |
|   @Test
 | |
|   public void doubleWrapping_lazy() {
 | |
|     assertThat(DoubleCheck.lazy(DOUBLE_CHECK_OBJECT_PROVIDER))
 | |
|         .isSameInstanceAs(DOUBLE_CHECK_OBJECT_PROVIDER);
 | |
|   }
 | |
| 
 | |
|   @Test
 | |
|   public void get() throws Exception {
 | |
|     int numThreads = 10;
 | |
|     ExecutorService executor = Executors.newFixedThreadPool(numThreads);
 | |
| 
 | |
|     final CountDownLatch latch = new CountDownLatch(numThreads);
 | |
|     LatchedProvider provider = new LatchedProvider(latch);
 | |
|     final Lazy<Object> lazy = DoubleCheck.lazy(provider);
 | |
| 
 | |
|     List<Callable<Object>> tasks = Lists.newArrayListWithCapacity(numThreads);
 | |
|     for (int i = 0; i < numThreads; i++) {
 | |
|       tasks.add(
 | |
|           () -> {
 | |
|             latch.countDown();
 | |
|             return lazy.get();
 | |
|           });
 | |
|     }
 | |
| 
 | |
|     List<Future<Object>> futures = executor.invokeAll(tasks);
 | |
| 
 | |
|     assertThat(provider.provisions.get()).isEqualTo(1);
 | |
|     Set<Object> results = Sets.newIdentityHashSet();
 | |
|     for (Future<Object> future : futures) {
 | |
|       results.add(future.get());
 | |
|     }
 | |
|     assertThat(results).hasSize(1);
 | |
|   }
 | |
| 
 | |
|   private static class LatchedProvider implements Provider<Object> {
 | |
|     final AtomicInteger provisions;
 | |
|     final CountDownLatch latch;
 | |
| 
 | |
|     LatchedProvider(CountDownLatch latch) {
 | |
|       this.latch = latch;
 | |
|       this.provisions = new AtomicInteger();
 | |
|     }
 | |
| 
 | |
|     @Override
 | |
|     public Object get() {
 | |
|       if (latch != null) {
 | |
|         Uninterruptibles.awaitUninterruptibly(latch);
 | |
|       }
 | |
|       provisions.incrementAndGet();
 | |
|       return new Object();
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   @Test public void reentranceWithoutCondition_throwsStackOverflow() {
 | |
|     final AtomicReference<Provider<Object>> doubleCheckReference =
 | |
|         new AtomicReference<>();
 | |
|     Provider<Object> doubleCheck = DoubleCheck.provider(() -> doubleCheckReference.get().get());
 | |
|     doubleCheckReference.set(doubleCheck);
 | |
|     try {
 | |
|       doubleCheck.get();
 | |
|       fail();
 | |
|     } catch (StackOverflowError expected) {}
 | |
|   }
 | |
| 
 | |
|   @Test public void reentranceReturningSameInstance() {
 | |
|     final AtomicReference<Provider<Object>> doubleCheckReference =
 | |
|         new AtomicReference<>();
 | |
|     final AtomicInteger invocationCount = new AtomicInteger();
 | |
|     final Object object = new Object();
 | |
|     Provider<Object> doubleCheck = DoubleCheck.provider(() -> {
 | |
|         if (invocationCount.incrementAndGet() == 1) {
 | |
|          doubleCheckReference.get().get();
 | |
|        }
 | |
|        return object;
 | |
|      });
 | |
|     doubleCheckReference.set(doubleCheck);
 | |
|     assertThat(doubleCheck.get()).isSameInstanceAs(object);
 | |
|   }
 | |
| 
 | |
|   @Test public void reentranceReturningDifferentInstances_throwsIllegalStateException() {
 | |
|     final AtomicReference<Provider<Object>> doubleCheckReference =
 | |
|         new AtomicReference<>();
 | |
|     final AtomicInteger invocationCount = new AtomicInteger();
 | |
|     Provider<Object> doubleCheck = DoubleCheck.provider(() -> {
 | |
|        if (invocationCount.incrementAndGet() == 1) {
 | |
|          doubleCheckReference.get().get();
 | |
|        }
 | |
|        return new Object();
 | |
|      });
 | |
|     doubleCheckReference.set(doubleCheck);
 | |
|     try {
 | |
|       doubleCheck.get();
 | |
|       fail();
 | |
|     } catch (IllegalStateException expected) {}
 | |
|   }
 | |
| 
 | |
|   @Test
 | |
|   public void instanceFactoryAsLazyDoesNotWrap() {
 | |
|     Factory<Object> factory = InstanceFactory.create(new Object());
 | |
|     assertThat(DoubleCheck.lazy(factory)).isSameInstanceAs(factory);
 | |
|   }
 | |
| }
 |