• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 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.monitoring;
18 
19 import static com.google.common.truth.Truth.assertThat;
20 import static org.junit.Assert.fail;
21 import static org.mockito.ArgumentMatchers.any;
22 import static org.mockito.Mockito.inOrder;
23 import static org.mockito.Mockito.mock;
24 import static org.mockito.Mockito.verifyNoMoreInteractions;
25 import static org.mockito.Mockito.when;
26 
27 import com.google.common.base.Throwables;
28 import com.google.common.collect.ImmutableList;
29 import com.google.common.collect.ImmutableSet;
30 import com.google.common.util.concurrent.ListenableFuture;
31 import com.google.common.util.concurrent.SettableFuture;
32 import dagger.producers.monitoring.ProducerMonitor;
33 import dagger.producers.monitoring.ProducerToken;
34 import dagger.producers.monitoring.ProductionComponentMonitor;
35 import java.util.LinkedHashMap;
36 import java.util.Map;
37 import java.util.concurrent.ExecutionException;
38 import java.util.concurrent.Executors;
39 import org.junit.Before;
40 import org.junit.Test;
41 import org.junit.runner.RunWith;
42 import org.junit.runners.JUnit4;
43 import org.mockito.InOrder;
44 import org.mockito.Mock;
45 import org.mockito.MockitoAnnotations;
46 
47 /** Tests for production components using monitoring. */
48 @RunWith(JUnit4.class)
49 public final class MonitoringTest {
50   @Mock private ProductionComponentMonitor.Factory componentMonitorFactory;
51   @Mock private StringStub server1;
52   @Mock private StringStub server2;
53   private SettableFuture<String> server1Future;
54   private SettableFuture<String> server2Future;
55   private FakeProductionComponentMonitor componentMonitor;
56 
57   @Before
setUp()58   public void setUp() {
59     MockitoAnnotations.initMocks(this);
60     componentMonitor = new FakeProductionComponentMonitor();
61     when(componentMonitorFactory.create(any())).thenReturn(componentMonitor);
62     server1Future = SettableFuture.create();
63     server2Future = SettableFuture.create();
64     when(server1.run(any(String.class))).thenReturn(server1Future);
65     when(server2.run(any(String.class))).thenReturn(server2Future);
66   }
67 
68   @Test
basicMonitoring()69   public void basicMonitoring() throws Exception {
70     MonitoredComponent component =
71         DaggerMonitoredComponent.builder()
72             .monitoringModule(new MonitoringModule(componentMonitorFactory))
73             .stubModule(new StubModule(server1, server2))
74             .build();
75     ListenableFuture<String> output = component.output();
76     assertThat(componentMonitor.monitors).hasSize(3);
77     ImmutableList<Map.Entry<ProducerToken, ProducerMonitor>> entries =
78         ImmutableList.copyOf(componentMonitor.monitors.entrySet());
79     assertThat(entries.get(0).getKey().toString()).contains("CallServer2");
80     assertThat(entries.get(1).getKey().toString()).contains("CallServer1");
81     assertThat(entries.get(2).getKey().toString()).contains("RequestData");
82 
83     ProducerMonitor callServer2Monitor = entries.get(0).getValue();
84     ProducerMonitor callServer1Monitor = entries.get(1).getValue();
85     ProducerMonitor requestDataMonitor = entries.get(2).getValue();
86 
87     InOrder inOrder = inOrder(requestDataMonitor, callServer1Monitor, callServer2Monitor);
88     inOrder.verify(callServer2Monitor).requested();
89     inOrder.verify(callServer1Monitor).requested();
90     inOrder.verify(requestDataMonitor).requested();
91     inOrder.verify(requestDataMonitor).ready();
92     inOrder.verify(requestDataMonitor).methodStarting();
93     inOrder.verify(requestDataMonitor).methodFinished();
94     inOrder.verify(requestDataMonitor).succeeded("Hello, World!");
95     inOrder.verify(callServer1Monitor).ready();
96     inOrder.verify(callServer1Monitor).methodStarting();
97     inOrder.verify(callServer1Monitor).methodFinished();
98     verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
99 
100     server1Future.set("server 1 response");
101     inOrder.verify(callServer1Monitor).succeeded("server 1 response");
102     inOrder.verify(callServer2Monitor).ready();
103     inOrder.verify(callServer2Monitor).methodStarting();
104     inOrder.verify(callServer2Monitor).methodFinished();
105     verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
106 
107     server2Future.set("server 2 response");
108     inOrder.verify(callServer2Monitor).succeeded("server 2 response");
109     verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
110     assertThat(output.get()).isEqualTo("server 2 response");
111   }
112 
113   @Test
basicMonitoringWithFailure()114   public void basicMonitoringWithFailure() throws Exception {
115     MonitoredComponent component =
116         DaggerMonitoredComponent.builder()
117             .monitoringModule(new MonitoringModule(componentMonitorFactory))
118             .stubModule(new StubModule(server1, server2))
119             .build();
120     ListenableFuture<String> output = component.output();
121     assertThat(componentMonitor.monitors).hasSize(3);
122     ImmutableList<Map.Entry<ProducerToken, ProducerMonitor>> entries =
123         ImmutableList.copyOf(componentMonitor.monitors.entrySet());
124     assertThat(entries.get(0).getKey().toString()).contains("CallServer2");
125     assertThat(entries.get(1).getKey().toString()).contains("CallServer1");
126     assertThat(entries.get(2).getKey().toString()).contains("RequestData");
127 
128     ProducerMonitor callServer2Monitor = entries.get(0).getValue();
129     ProducerMonitor callServer1Monitor = entries.get(1).getValue();
130     ProducerMonitor requestDataMonitor = entries.get(2).getValue();
131 
132     InOrder inOrder = inOrder(requestDataMonitor, callServer1Monitor, callServer2Monitor);
133     inOrder.verify(callServer2Monitor).requested();
134     inOrder.verify(callServer1Monitor).requested();
135     inOrder.verify(requestDataMonitor).requested();
136     inOrder.verify(requestDataMonitor).ready();
137     inOrder.verify(requestDataMonitor).methodStarting();
138     inOrder.verify(requestDataMonitor).methodFinished();
139     inOrder.verify(requestDataMonitor).succeeded("Hello, World!");
140     inOrder.verify(callServer1Monitor).ready();
141     inOrder.verify(callServer1Monitor).methodStarting();
142     inOrder.verify(callServer1Monitor).methodFinished();
143     verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
144 
145     RuntimeException cause = new RuntimeException("monkey");
146     server1Future.setException(cause);
147     inOrder.verify(callServer1Monitor).failed(cause);
148     inOrder.verify(callServer2Monitor).ready();
149     inOrder.verify(callServer2Monitor).failed(any(Throwable.class));
150     verifyNoMoreInteractions(requestDataMonitor, callServer1Monitor, callServer2Monitor);
151     try {
152       output.get();
153       fail();
154     } catch (ExecutionException e) {
155       assertThat(Throwables.getRootCause(e)).isSameInstanceAs(cause);
156     }
157   }
158 
159   private static final class FakeProductionComponentMonitor extends ProductionComponentMonitor {
160     final Map<ProducerToken, ProducerMonitor> monitors = new LinkedHashMap<>();
161 
162     @Override
producerMonitorFor(ProducerToken token)163     public ProducerMonitor producerMonitorFor(ProducerToken token) {
164       ProducerMonitor monitor = mock(ProducerMonitor.class);
165       monitors.put(token, monitor);
166       return monitor;
167     }
168   }
169 
170   @Test
monitoringWithThreads()171   public void monitoringWithThreads() throws Exception {
172     ThreadRecordingProductionComponentMonitor componentMonitor =
173         new ThreadRecordingProductionComponentMonitor();
174     when(componentMonitorFactory.create(any())).thenReturn(componentMonitor);
175 
176     ThreadMonitoredComponent component =
177         DaggerThreadMonitoredComponent.builder()
178             .monitoringModule(new MonitoringModule(componentMonitorFactory))
179             .executorModule(new ExecutorModule(Executors.newFixedThreadPool(10)))
180             .build();
181     ThreadAccumulator threadAccumulator = component.threadAccumulator().get();
182 
183     assertThat(componentMonitor.monitors).hasSize(3);
184     ImmutableList<Map.Entry<ProducerToken, ThreadRecordingProducerMonitor>> entries =
185         ImmutableList.copyOf(componentMonitor.monitors.entrySet());
186 
187     assertThat(entries.get(0).getKey().toString()).contains("EntryPoint");
188     ThreadRecordingProducerMonitor entryPointMonitor = entries.get(0).getValue();
189     assertThat(entries.get(1).getKey().toString()).contains("Required");
190     ThreadRecordingProducerMonitor requiredMonitor = entries.get(1).getValue();
191     assertThat(entries.get(2).getKey().toString()).contains("Deferred");
192     ThreadRecordingProducerMonitor deferredMonitor = entries.get(2).getValue();
193 
194     // The entry point producer was requested from the main thread, then ran in its own thread.
195     assertThat(entryPointMonitor.requestedThreadId).isEqualTo(Thread.currentThread().getId());
196     assertThat(entryPointMonitor.startingThreadId)
197         .isEqualTo(threadAccumulator.threadId("entryPoint"));
198     assertThat(entryPointMonitor.finishedThreadId)
199         .isEqualTo(threadAccumulator.threadId("entryPoint"));
200 
201     // The deferred producer was requested by the required producer, then ran in its own thread.
202     assertThat(deferredMonitor.requestedThreadId).isEqualTo(threadAccumulator.threadId("required"));
203     assertThat(deferredMonitor.startingThreadId).isEqualTo(threadAccumulator.threadId("deferred"));
204     assertThat(deferredMonitor.finishedThreadId).isEqualTo(threadAccumulator.threadId("deferred"));
205 
206     // The required producer was requested by the entry point producer, then ran in its own thread.
207     assertThat(requiredMonitor.requestedThreadId).isEqualTo(entryPointMonitor.requestedThreadId);
208     assertThat(requiredMonitor.startingThreadId).isEqualTo(threadAccumulator.threadId("required"));
209     assertThat(requiredMonitor.finishedThreadId).isEqualTo(threadAccumulator.threadId("required"));
210 
211     // Each producer ran in a distinct thread.
212     ImmutableSet<Long> threadIds =
213         ImmutableSet.of(
214             Thread.currentThread().getId(),
215             threadAccumulator.threadId("required"),
216             threadAccumulator.threadId("deferred"),
217             threadAccumulator.threadId("entryPoint"));
218     assertThat(threadIds).hasSize(4);
219   }
220 
221   private static final class ThreadRecordingProductionComponentMonitor
222       extends ProductionComponentMonitor {
223     final Map<ProducerToken, ThreadRecordingProducerMonitor> monitors = new LinkedHashMap<>();
224 
225     @Override
producerMonitorFor(ProducerToken token)226     public ProducerMonitor producerMonitorFor(ProducerToken token) {
227       ThreadRecordingProducerMonitor monitor = new ThreadRecordingProducerMonitor();
228       monitors.put(token, monitor);
229       return monitor;
230     }
231   }
232 
233   private static final class ThreadRecordingProducerMonitor extends ProducerMonitor {
234     private long requestedThreadId;
235     private long startingThreadId;
236     private long finishedThreadId;
237 
238     @Override
requested()239     public void requested() {
240       requestedThreadId = Thread.currentThread().getId();
241     }
242 
243     @Override
methodStarting()244     public void methodStarting() {
245       startingThreadId = Thread.currentThread().getId();
246     }
247 
248     @Override
methodFinished()249     public void methodFinished() {
250       finishedThreadId = Thread.currentThread().getId();
251     }
252   }
253 }
254