1 /* 2 * Copyright (C) 2018 The Dagger Authors. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package dagger.functional.producers.cancellation; 18 19 import static com.google.common.truth.Truth.assertThat; 20 import static java.util.concurrent.TimeUnit.MILLISECONDS; 21 22 import com.google.common.util.concurrent.ListenableFuture; 23 import com.google.common.util.concurrent.MoreExecutors; 24 import dagger.BindsInstance; 25 import dagger.producers.CancellationPolicy; 26 import dagger.producers.CancellationPolicy.Propagation; 27 import dagger.producers.ProducerModule; 28 import dagger.producers.Produces; 29 import dagger.producers.Production; 30 import dagger.producers.ProductionComponent; 31 import dagger.producers.ProductionSubcomponent; 32 import java.util.concurrent.Executor; 33 import javax.inject.Named; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 import org.junit.runners.JUnit4; 37 38 /** 39 * Tests for parent production components with a {@code CancellationPolicy} that allows subcomponent 40 * cancellation to propagate to them 41 */ 42 @RunWith(JUnit4.class) 43 public final class CancellationPolicyTest { 44 45 @ProducerModule(subcomponents = Child.class) 46 static class ParentModule { 47 private final ProducerTester tester; 48 ParentModule(ProducerTester tester)49 ParentModule(ProducerTester tester) { 50 this.tester = tester; 51 } 52 53 @Produces 54 @Named("a") produceA()55 ListenableFuture<String> produceA() { 56 return tester.start("a"); 57 } 58 } 59 60 interface Parent { 61 @Named("a") a()62 ListenableFuture<String> a(); 63 childBuilder()64 Child.Builder childBuilder(); 65 66 interface Builder<P extends Parent, B extends Builder<P, B>> { module(ParentModule module)67 B module(ParentModule module); 68 69 @BindsInstance executor(@roduction Executor executor)70 B executor(@Production Executor executor); 71 build()72 P build(); 73 } 74 } 75 76 @CancellationPolicy(fromSubcomponents = Propagation.PROPAGATE) 77 @ProductionComponent(modules = ParentModule.class) 78 interface PropagatingParent extends Parent { 79 @ProductionComponent.Builder 80 interface Builder extends Parent.Builder<PropagatingParent, Builder> {} 81 } 82 83 @CancellationPolicy(fromSubcomponents = Propagation.IGNORE) 84 @ProductionComponent(modules = ParentModule.class) 85 interface NonPropagatingParent extends Parent { 86 @ProductionComponent.Builder 87 interface Builder extends Parent.Builder<NonPropagatingParent, Builder> {} 88 } 89 90 @ProducerModule 91 static class ChildModule { 92 private final ProducerTester tester; 93 ChildModule(ProducerTester tester)94 ChildModule(ProducerTester tester) { 95 this.tester = tester; 96 } 97 98 @Produces 99 @Named("b") b(@amed"a") String a)100 ListenableFuture<String> b(@Named("a") String a) { 101 return tester.start("b"); 102 } 103 } 104 105 @ProductionSubcomponent(modules = ChildModule.class) 106 interface Child { 107 @Named("b") b()108 ListenableFuture<String> b(); 109 110 @ProductionSubcomponent.Builder 111 interface Builder { module(ChildModule module)112 Builder module(ChildModule module); 113 build()114 Child build(); 115 } 116 } 117 118 private final ProducerTester tester = new ProducerTester(); 119 120 @Test propagatingParent_childCancellationPropagatesToParent()121 public void propagatingParent_childCancellationPropagatesToParent() { 122 PropagatingParent parent = 123 DaggerCancellationPolicyTest_PropagatingParent.builder() 124 .module(new ParentModule(tester)) 125 .executor(MoreExecutors.directExecutor()) 126 .build(); 127 ListenableFuture<String> a = parent.a(); 128 129 Child child = parent.childBuilder().module(new ChildModule(tester)).build(); 130 131 ListenableFuture<String> b = child.b(); 132 133 tester.assertStarted("a").only(); 134 135 assertThat(a.isDone()).isFalse(); 136 assertThat(b.isDone()).isFalse(); 137 138 assertThat(b.cancel(true)).isTrue(); 139 assertThat(b.isCancelled()).isTrue(); 140 141 tester.assertCancelled("a"); 142 143 assertThat(a.isCancelled()).isTrue(); 144 } 145 146 @Test nonPropagatingParent_childCancellationDoesNotPropagateToParent()147 public void nonPropagatingParent_childCancellationDoesNotPropagateToParent() throws Exception { 148 // This test is basically just checking that when the parent has fromSubcomponents = IGNORE, it 149 // behaves the same as having no @CancellationPolicy at all (as tested in 150 // ProducerSubcomponentCancellationTester) 151 NonPropagatingParent parent = 152 DaggerCancellationPolicyTest_NonPropagatingParent.builder() 153 .module(new ParentModule(tester)) 154 .executor(MoreExecutors.directExecutor()) 155 .build(); 156 ListenableFuture<String> a = parent.a(); 157 158 Child child = parent.childBuilder().module(new ChildModule(tester)).build(); 159 160 ListenableFuture<String> b = child.b(); 161 162 tester.assertStarted("a").only(); 163 164 assertThat(a.isDone()).isFalse(); 165 assertThat(b.isDone()).isFalse(); 166 167 assertThat(b.cancel(true)).isTrue(); 168 assertThat(b.isCancelled()).isTrue(); 169 170 tester.assertNotCancelled("a"); 171 172 assertThat(a.isDone()).isFalse(); 173 174 tester.complete("a"); 175 assertThat(a.isDone()).isTrue(); 176 assertThat(a.get(1, MILLISECONDS)).isEqualTo("completed"); 177 178 tester.assertNotStarted("b"); 179 } 180 } 181