/* * Copyright (C) 2018 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.functional.producers.cancellation; import static com.google.common.truth.Truth.assertThat; import static java.util.concurrent.TimeUnit.MILLISECONDS; import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.MoreExecutors; import dagger.BindsInstance; import dagger.producers.CancellationPolicy; import dagger.producers.CancellationPolicy.Propagation; import dagger.producers.ProducerModule; import dagger.producers.Produces; import dagger.producers.Production; import dagger.producers.ProductionComponent; import dagger.producers.ProductionSubcomponent; import java.util.concurrent.Executor; import javax.inject.Named; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; /** * Tests for parent production components with a {@code CancellationPolicy} that allows subcomponent * cancellation to propagate to them */ @RunWith(JUnit4.class) public final class CancellationPolicyTest { @ProducerModule(subcomponents = Child.class) static class ParentModule { private final ProducerTester tester; ParentModule(ProducerTester tester) { this.tester = tester; } @Produces @Named("a") ListenableFuture produceA() { return tester.start("a"); } } interface Parent { @Named("a") ListenableFuture a(); Child.Builder childBuilder(); interface Builder

> { B module(ParentModule module); @BindsInstance B executor(@Production Executor executor); P build(); } } @CancellationPolicy(fromSubcomponents = Propagation.PROPAGATE) @ProductionComponent(modules = ParentModule.class) interface PropagatingParent extends Parent { @ProductionComponent.Builder interface Builder extends Parent.Builder {} } @CancellationPolicy(fromSubcomponents = Propagation.IGNORE) @ProductionComponent(modules = ParentModule.class) interface NonPropagatingParent extends Parent { @ProductionComponent.Builder interface Builder extends Parent.Builder {} } @ProducerModule static class ChildModule { private final ProducerTester tester; ChildModule(ProducerTester tester) { this.tester = tester; } @Produces @Named("b") ListenableFuture b(@Named("a") String a) { return tester.start("b"); } } @ProductionSubcomponent(modules = ChildModule.class) interface Child { @Named("b") ListenableFuture b(); @ProductionSubcomponent.Builder interface Builder { Builder module(ChildModule module); Child build(); } } private final ProducerTester tester = new ProducerTester(); @Test public void propagatingParent_childCancellationPropagatesToParent() { PropagatingParent parent = DaggerCancellationPolicyTest_PropagatingParent.builder() .module(new ParentModule(tester)) .executor(MoreExecutors.directExecutor()) .build(); ListenableFuture a = parent.a(); Child child = parent.childBuilder().module(new ChildModule(tester)).build(); ListenableFuture b = child.b(); tester.assertStarted("a").only(); assertThat(a.isDone()).isFalse(); assertThat(b.isDone()).isFalse(); assertThat(b.cancel(true)).isTrue(); assertThat(b.isCancelled()).isTrue(); tester.assertCancelled("a"); assertThat(a.isCancelled()).isTrue(); } @Test public void nonPropagatingParent_childCancellationDoesNotPropagateToParent() throws Exception { // This test is basically just checking that when the parent has fromSubcomponents = IGNORE, it // behaves the same as having no @CancellationPolicy at all (as tested in // ProducerSubcomponentCancellationTester) NonPropagatingParent parent = DaggerCancellationPolicyTest_NonPropagatingParent.builder() .module(new ParentModule(tester)) .executor(MoreExecutors.directExecutor()) .build(); ListenableFuture a = parent.a(); Child child = parent.childBuilder().module(new ChildModule(tester)).build(); ListenableFuture b = child.b(); tester.assertStarted("a").only(); assertThat(a.isDone()).isFalse(); assertThat(b.isDone()).isFalse(); assertThat(b.cancel(true)).isTrue(); assertThat(b.isCancelled()).isTrue(); tester.assertNotCancelled("a"); assertThat(a.isDone()).isFalse(); tester.complete("a"); assertThat(a.isDone()).isTrue(); assertThat(a.get(1, MILLISECONDS)).isEqualTo("completed"); tester.assertNotStarted("b"); } }