You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
171 lines
5.2 KiB
171 lines
5.2 KiB
/*
|
|
* 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);
|
|
}
|
|
}
|