• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
21 import com.google.common.util.concurrent.ListenableFuture;
22 import com.google.common.util.concurrent.MoreExecutors;
23 import dagger.functional.producers.cancellation.CancellationComponent.Dependency;
24 import org.junit.Test;
25 import org.junit.runner.RunWith;
26 import org.junit.runners.JUnit4;
27 
28 /** Tests cancellation of tasks in production components. */
29 @RunWith(JUnit4.class)
30 public class ProducerCancellationTest {
31 
32   private final ProducerTester tester = new ProducerTester();
33   private final CancellationComponent component =
34       DaggerCancellationComponent.builder()
35           .module(new CancellationModule(tester))
36           .dependency(new Dependency(tester))
37           .executor(MoreExecutors.directExecutor())
38           .build();
39 
40   @Test
initialState()41   public void initialState() {
42     tester.assertNoStartedNodes();
43   }
44 
45   @Test
cancellingOneEntryPoint_cancelsAllRunningNodes()46   public void cancellingOneEntryPoint_cancelsAllRunningNodes() {
47     ListenableFuture<String> entryPoint1 = component.entryPoint1();
48     tester.assertStarted("leaf2", "leaf3").only();
49 
50     assertThat(entryPoint1.cancel(true)).isTrue();
51     assertThat(entryPoint1.isCancelled()).isTrue();
52 
53     tester.assertCancelled("leaf2", "leaf3").only();
54 
55     // The other entry points were also cancelled in the process, from the user's perspective.
56     assertThat(component.entryPoint2().get().isCancelled()).isTrue();
57     assertThat(component.entryPoint3().isCancelled()).isTrue();
58 
59     // The underlying tasks weren't actually started, even though we just requested them above,
60     // because the node was cancelled already along with the component.
61     tester.assertNotStarted("entryPoint2", "entryPoint3");
62   }
63 
64   @SuppressWarnings({"CheckReturnValue", "FutureReturnValueIgnored"})
65   @Test
cancellingNonEntryPointProducer_doesNotCancelUnderlyingTask()66   public void cancellingNonEntryPointProducer_doesNotCancelUnderlyingTask() {
67     ListenableFuture<String> entryPoint1 = component.entryPoint1();
68     tester.assertStarted("leaf2", "leaf3").only();
69 
70     tester.complete("leaf2", "leaf3");
71 
72     tester.assertStarted("bar");
73 
74     // foo's dependencies are complete, but it is not yet started because baz depends on
75     // Producer<foo>, so it won't be started until baz calls get() on it.
76     // baz not started yet because it needs bar to complete first.
77     tester.assertNotStarted("foo", "baz");
78 
79     // Complete bar, triggering baz to run. It calls get() on the foo Producer, so that also starts
80     // once its dependency leaf1 is complete.
81     tester.complete("bar", "leaf1");
82     tester.assertStarted("baz", "foo");
83 
84     // baz then cancelled the foo Producer's future, but that didn't cancel the underlying task.
85     tester.assertNotCancelled("foo");
86 
87     // If we cancel the entry point, that does cancel the task.
88     entryPoint1.cancel(true);
89     tester.assertCancelled("foo");
90   }
91 
92   @SuppressWarnings({"CheckReturnValue", "FutureReturnValueIgnored"})
93   @Test
cancellingProducerFromComponentDependency_cancelsUnderlyingTask()94   public void cancellingProducerFromComponentDependency_cancelsUnderlyingTask() {
95     // Start leaf2/leaf3 tasks.
96     component.entryPoint1();
97     tester.assertStarted("leaf2", "leaf3").only();
98     tester.assertNotCancelled("leaf2", "leaf3");
99 
100     // Nothing's requested dependencyFuture yet.
101     tester.assertNotStarted("dependencyFuture");
102 
103     // entryPoint3 injects Producer of dependency future, then cancels that future. Then also
104     // returns that future as the entry point.
105     ListenableFuture<String> entryPoint = component.entryPoint3();
106 
107     tester.assertStarted("dependencyFuture");
108     tester.assertCancelled("dependencyFuture");
109 
110     // Even though the entry point future returned from the component is not the dependency future
111     // itself, the cancellation should have propagated out to it and cancelled it.
112     assertThat(entryPoint.isCancelled()).isTrue();
113 
114     // And that cancellation should have cancelled the other tasks running in the component.
115     tester.assertCancelled("leaf2", "leaf3");
116   }
117 }
118