View Javadoc

1   // Copyright (C) 2004 - 2012 Philip Aston
2   // All rights reserved.
3   //
4   // This file is part of The Grinder software distribution. Refer to
5   // the file LICENSE which is part of The Grinder distribution for
6   // licensing details. The Grinder distribution is available on the
7   // Internet at http://grinder.sourceforge.net/
8   //
9   // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
13  // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
14  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
17  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
18  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
19  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
20  // OF THE POSSIBILITY OF SUCH DAMAGE.
21  
22  package net.grinder.console.communication;
23  
24  import java.util.Arrays;
25  import java.util.Comparator;
26  import java.util.HashMap;
27  import java.util.Map;
28  import java.util.Timer;
29  import java.util.TimerTask;
30  
31  import junit.framework.TestCase;
32  import net.grinder.common.processidentity.ProcessIdentity;
33  import net.grinder.common.processidentity.ProcessReport;
34  import net.grinder.common.processidentity.WorkerIdentity;
35  import net.grinder.common.processidentity.WorkerProcessReport;
36  import net.grinder.console.common.processidentity.StubAgentProcessReport;
37  import net.grinder.console.common.processidentity.StubWorkerProcessReport;
38  import net.grinder.console.communication.ProcessControl.ProcessReports;
39  import net.grinder.console.communication.ProcessStatusImplementation.AgentAndWorkers;
40  import net.grinder.engine.agent.StubAgentIdentity;
41  import net.grinder.messages.console.AgentAndCacheReport;
42  import net.grinder.testutility.AssertUtilities;
43  import net.grinder.testutility.CallData;
44  import net.grinder.testutility.RandomStubFactory;
45  import net.grinder.util.AllocateLowestNumber;
46  
47  
48  /**
49   * Unit test case for {@link ProcessStatusImplementation}.
50   *
51   * @author Philip Aston
52   */
53  public class TestProcessStatusImplementation extends TestCase {
54    private final ProcessReportComparator m_processReportComparator =
55      new ProcessReportComparator();
56  
57    private final Comparator<ProcessReports> m_processReportsComparator =
58      new ProcessReportsComparator();
59  
60    private final MyTimer m_timer = new MyTimer();
61  
62    private final RandomStubFactory<AllocateLowestNumber>
63      m_allocateLowestNumberStubFactory =
64        RandomStubFactory.create(AllocateLowestNumber.class);
65    private final AllocateLowestNumber m_allocateLowestNumber =
66      m_allocateLowestNumberStubFactory.getStub();
67  
68    protected void tearDown() {
69      m_timer.cancel();
70    }
71  
72    public void testConstruction() throws Exception {
73      new ProcessStatusImplementation(m_timer, m_allocateLowestNumber);
74  
75      assertEquals(2, m_timer.getNumberOfScheduledTasks());
76  
77      m_allocateLowestNumberStubFactory.assertNoMoreCalls();
78    }
79  
80    public void testUpdate() throws Exception {
81  
82      final RandomStubFactory<ProcessControl.Listener> listenerStubFactory =
83        RandomStubFactory.create(ProcessControl.Listener.class);
84  
85      final ProcessStatusImplementation processStatusSet =
86        new ProcessStatusImplementation(m_timer, m_allocateLowestNumber);
87  
88      final TimerTask updateTask = m_timer.getTaskByPeriod(500L);
89  
90      processStatusSet.addListener(listenerStubFactory.getStub());
91  
92      updateTask.run();
93      listenerStubFactory.assertNoMoreCalls();
94      m_allocateLowestNumberStubFactory.assertNoMoreCalls();
95  
96      updateTask.run();
97      listenerStubFactory.assertNoMoreCalls();
98      m_allocateLowestNumberStubFactory.assertNoMoreCalls();
99  
100     final StubAgentIdentity agentIdentity = new StubAgentIdentity("agent");
101     final WorkerIdentity workerIdentity =
102       agentIdentity.createWorkerIdentity();
103 
104     final WorkerProcessReport workerProcessReport =
105       new StubWorkerProcessReport(workerIdentity,
106                                   ProcessReport.State.RUNNING,
107                                   3,
108                                   5);
109 
110     processStatusSet.addWorkerStatusReport(workerProcessReport);
111     m_allocateLowestNumberStubFactory.assertSuccess("add", Object.class);
112     m_allocateLowestNumberStubFactory.assertNoMoreCalls();
113 
114     updateTask.run();
115     final CallData callData =
116       listenerStubFactory.assertSuccess(
117         "update",
118         new ProcessControl.ProcessReports[0].getClass());
119 
120     final ProcessControl.ProcessReports[] processReportsArray =
121       (ProcessControl.ProcessReports[])callData.getParameters()[0];
122 
123     assertEquals(1, processReportsArray.length);
124     final WorkerProcessReport[] workerProcessReports =
125       processReportsArray[0].getWorkerProcessReports();
126     assertEquals(1, workerProcessReports.length);
127     assertEquals(workerProcessReport, workerProcessReports[0]);
128 
129     updateTask.run();
130     listenerStubFactory.assertNoMoreCalls();
131     m_allocateLowestNumberStubFactory.assertNoMoreCalls();
132   }
133 
134   public void testUpdateWithManyProcessStatusesAndFlush() throws Exception {
135     final RandomStubFactory<ProcessControl.Listener> listenerStubFactory =
136       RandomStubFactory.create(ProcessControl.Listener.class);
137 
138     final ProcessStatusImplementation processStatus =
139       new ProcessStatusImplementation(m_timer, m_allocateLowestNumber);
140 
141     final TimerTask updateTask = m_timer.getTaskByPeriod(500L);
142     final TimerTask flushTask = m_timer.getTaskByPeriod(2000L);
143 
144     processStatus.addListener(listenerStubFactory.getStub());
145 
146     updateTask.run();
147     listenerStubFactory.assertNoMoreCalls();
148     m_allocateLowestNumberStubFactory.assertNoMoreCalls();
149 
150     final StubAgentIdentity agentIdentityA =
151       new StubAgentIdentity("Agent A");
152     final WorkerIdentity workerIdentityA1 =
153       agentIdentityA.createWorkerIdentity();
154     final WorkerIdentity workerIdentityA2 =
155       agentIdentityA.createWorkerIdentity();
156     final WorkerIdentity workerIdentityA3 =
157       agentIdentityA.createWorkerIdentity();
158     final WorkerIdentity workerIdentityA4 =
159         agentIdentityA.createWorkerIdentity();
160     assertEquals(3, workerIdentityA4.getNumber());
161 
162     final StubAgentIdentity agentIdentityB =
163       new StubAgentIdentity("Agent B");
164     final WorkerIdentity workerIdentityB1 =
165       agentIdentityB.createWorkerIdentity();
166     assertEquals(0, workerIdentityB1.getNumber());
167 
168     final WorkerProcessReport[] workerProcessReportArray = {
169       new StubWorkerProcessReport(
170         workerIdentityA3, ProcessReport.State.STARTED, 1, 1),
171       new StubWorkerProcessReport(
172         workerIdentityA4, ProcessReport.State.STARTED, 1, 1),
173       new StubWorkerProcessReport(
174         workerIdentityA3, ProcessReport.State.RUNNING, 5, 10),
175       new StubWorkerProcessReport(
176         workerIdentityB1, ProcessReport.State.RUNNING, 1, 1),
177       new StubWorkerProcessReport(
178         workerIdentityA2, ProcessReport.State.FINISHED, 1, 1),
179       new StubWorkerProcessReport(
180         workerIdentityA1, ProcessReport.State.FINISHED, 3, 10),
181     };
182 
183     for (int i = 0; i < workerProcessReportArray.length; ++i) {
184       processStatus.addWorkerStatusReport(workerProcessReportArray[i]);
185     }
186 
187     assertEquals(2, processStatus.getNumberOfLiveAgents());
188     m_allocateLowestNumberStubFactory.assertSuccess("add", Object.class);
189     m_allocateLowestNumberStubFactory.assertSuccess("add", Object.class);
190     m_allocateLowestNumberStubFactory.assertNoMoreCalls();
191 
192     updateTask.run();
193 
194     final CallData callData =
195       listenerStubFactory.assertSuccess(
196         "update",
197         new ProcessControl.ProcessReports[0].getClass());
198 
199     final ProcessControl.ProcessReports[] processReports =
200       (ProcessControl.ProcessReports[])callData.getParameters()[0];
201     Arrays.sort(processReports, m_processReportsComparator);
202 
203     assertEquals(2, processReports.length);
204 
205     final WorkerProcessReport[] agent1WorkerReports =
206       processReports[0].getWorkerProcessReports();
207     Arrays.sort(agent1WorkerReports, m_processReportComparator);
208 
209     final WorkerProcessReport[] expectedAgent1WorkerProcessReports = {
210       new StubWorkerProcessReport(
211         workerIdentityA4, ProcessReport.State.STARTED, 1, 1),
212       new StubWorkerProcessReport(
213         workerIdentityA3, ProcessReport.State.RUNNING, 5, 10),
214       new StubWorkerProcessReport(
215         workerIdentityA1, ProcessReport.State.FINISHED, 3, 10),
216       new StubWorkerProcessReport(
217         workerIdentityA2, ProcessReport.State.FINISHED, 1, 1),
218     };
219 
220     AssertUtilities.assertArraysEqual(expectedAgent1WorkerProcessReports,
221                                       agent1WorkerReports);
222 
223     final WorkerProcessReport[] agent2WorkerReports =
224       processReports[1].getWorkerProcessReports();
225     Arrays.sort(agent2WorkerReports, m_processReportComparator);
226 
227     final WorkerProcessReport[] expectedAgent2WorkerProcessReports = {
228         new StubWorkerProcessReport(
229           workerIdentityB1, ProcessReport.State.RUNNING, 1, 1),
230       };
231 
232     AssertUtilities.assertArraysEqual(expectedAgent2WorkerProcessReports,
233                                       agent2WorkerReports);
234 
235     updateTask.run();
236     listenerStubFactory.assertNoMoreCalls();
237 
238     // Nothing's changed, reports are new, first flush should do nothing.
239     flushTask.run();
240     updateTask.run();
241     listenerStubFactory.assertNoMoreCalls();
242     m_allocateLowestNumberStubFactory.assertNoMoreCalls();
243 
244     final StubAgentIdentity agentIdentityC =
245       new StubAgentIdentity("Agent C");
246     final WorkerIdentity workerIdentityC1 =
247       agentIdentityC.createWorkerIdentity();
248 
249     final WorkerProcessReport[] processStatusArray2 = {
250       new StubWorkerProcessReport(
251         workerIdentityB1, ProcessReport.State.RUNNING, 1, 1),
252       new StubWorkerProcessReport(
253         workerIdentityA1, ProcessReport.State.RUNNING, 5, 10),
254       new StubWorkerProcessReport(
255         workerIdentityC1, ProcessReport.State.FINISHED, 1, 1),
256     };
257 
258     for (int i = 0; i < processStatusArray2.length; ++i) {
259       processStatus.addWorkerStatusReport(processStatusArray2[i]);
260     }
261 
262     assertEquals(3, processStatus.getNumberOfLiveAgents());
263     m_allocateLowestNumberStubFactory.assertSuccess("add", Object.class);
264     m_allocateLowestNumberStubFactory.assertNoMoreCalls();
265 
266     processStatus.addAgentStatusReport(
267       new StubAgentProcessReport(agentIdentityA,
268                                  ProcessReport.State.RUNNING));
269     processStatus.addAgentStatusReport(
270       new StubAgentProcessReport(agentIdentityB,
271                                  ProcessReport.State.RUNNING));
272 
273     assertEquals(3, processStatus.getNumberOfLiveAgents());
274     m_allocateLowestNumberStubFactory.assertNoMoreCalls();
275 
276     // Second flush will remove processes that haven't reported.
277     // It won't remove any agents, because there's been at least one
278     // report for each.
279     flushTask.run();
280     updateTask.run();
281 
282     final CallData callData2 =
283       listenerStubFactory.assertSuccess(
284         "update",
285         new ProcessControl.ProcessReports[0].getClass());
286 
287     final ProcessControl.ProcessReports[] processReports2 =
288       (ProcessControl.ProcessReports[])callData2.getParameters()[0];
289     Arrays.sort(processReports2, m_processReportsComparator);
290 
291     assertEquals(3, processReports2.length);
292     m_allocateLowestNumberStubFactory.assertNoMoreCalls();
293 
294     final WorkerProcessReport[] expectedAgent1WorkerProcessReports2 = {
295       new StubWorkerProcessReport(
296         workerIdentityA1, ProcessReport.State.RUNNING, 5, 10),
297     };
298 
299     AssertUtilities.assertArraysEqual(
300       expectedAgent1WorkerProcessReports2,
301       processReports2[0].getWorkerProcessReports());
302 
303     final WorkerProcessReport[] expectedAgent2WorkerProcessReports2 = {
304       new StubWorkerProcessReport(
305         workerIdentityB1, ProcessReport.State.RUNNING, 1, 1),
306     };
307 
308     AssertUtilities.assertArraysEqual(
309       expectedAgent2WorkerProcessReports2,
310       processReports2[1].getWorkerProcessReports());
311 
312     final WorkerProcessReport[] expectedAgent3WorkerProcessReports2 = {
313       new StubWorkerProcessReport(
314         workerIdentityC1, ProcessReport.State.FINISHED, 1, 1),
315     };
316 
317     AssertUtilities.assertArraysEqual(
318       expectedAgent3WorkerProcessReports2,
319       processReports2[2].getWorkerProcessReports());
320 
321     updateTask.run();
322     listenerStubFactory.assertNoMoreCalls();
323 
324     // Third flush.
325     flushTask.run();
326 
327     assertEquals(0, processStatus.getNumberOfLiveAgents());
328     m_allocateLowestNumberStubFactory.assertSuccess("remove", Object.class);
329     m_allocateLowestNumberStubFactory.assertSuccess("remove", Object.class);
330     m_allocateLowestNumberStubFactory.assertSuccess("remove", Object.class);
331     m_allocateLowestNumberStubFactory.assertNoMoreCalls();
332   }
333 
334   public void testAgentAndWorkers() throws Exception {
335     final ProcessStatusImplementation processStatusSet =
336       new ProcessStatusImplementation(m_timer, m_allocateLowestNumber);
337 
338     final StubAgentIdentity agentIdentity =
339       new StubAgentIdentity("agent");
340 
341     final AgentAndWorkers agentAndWorkers =
342       processStatusSet.new AgentAndWorkers(agentIdentity);
343 
344     final AgentAndCacheReport initialReport =
345       agentAndWorkers.getAgentProcessReport();
346 
347     assertEquals(agentIdentity, initialReport.getAgentIdentity());
348 
349     assertNull(initialReport.getCacheHighWaterMark());
350   }
351 
352   private static final class MyTimer extends Timer {
353     private final Map<Long, TimerTask> m_taskByPeriod =
354       new HashMap<Long, TimerTask>();
355 
356     private int m_numberOfScheduledTasks;
357 
358     MyTimer() {
359       super(true);
360     }
361 
362     public void schedule(TimerTask timerTask, long delay, long period) {
363       assertEquals(0, delay);
364 
365       m_taskByPeriod.put(new Long(period), timerTask);
366       ++m_numberOfScheduledTasks;
367     }
368 
369     public TimerTask getTaskByPeriod(long period) {
370       return m_taskByPeriod.get(new Long(period));
371     }
372 
373     public int getNumberOfScheduledTasks() {
374       return m_numberOfScheduledTasks;
375     }
376   }
377 
378 
379   private static final class ProcessReportComparator
380     implements Comparator<ProcessReport> {
381 
382     public int compare(ProcessReport processReport1,
383                        ProcessReport processReport2) {
384       final int compareState =
385         processReport1.getState().compareTo(processReport2.getState());
386 
387       if (compareState == 0) {
388         final ProcessIdentity identity1 =
389           processReport1.getProcessAddress().getIdentity();
390         final ProcessIdentity identity2 =
391           processReport2.getProcessAddress().getIdentity();
392 
393         return identity1.getName().compareTo(identity2.getName());
394       }
395       else {
396         return compareState;
397       }
398     }
399   }
400 
401   private final class ProcessReportsComparator
402     implements Comparator<ProcessReports> {
403 
404     public int compare(ProcessReports o1, ProcessReports o2) {
405       return m_processReportComparator.compare(o1.getAgentProcessReport(),
406                                                o2.getAgentProcessReport());
407     }
408   }
409 }