1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22 package net.grinder.engine.agent;
23
24 import static net.grinder.testutility.SocketUtilities.findFreePort;
25 import static org.junit.Assert.assertTrue;
26 import static org.mockito.Matchers.contains;
27 import static org.mockito.Matchers.isA;
28 import static org.mockito.Mockito.reset;
29 import static org.mockito.Mockito.timeout;
30 import static org.mockito.Mockito.verify;
31 import static org.mockito.Mockito.verifyNoMoreInteractions;
32
33 import java.io.File;
34 import java.io.IOException;
35 import java.io.InputStream;
36
37 import net.grinder.common.GrinderProperties;
38 import net.grinder.communication.Acceptor;
39 import net.grinder.communication.CommunicationException;
40 import net.grinder.communication.ConnectionIdentity;
41 import net.grinder.communication.ConnectionType;
42 import net.grinder.communication.FanOutServerSender;
43 import net.grinder.communication.Message;
44 import net.grinder.communication.Sender;
45 import net.grinder.communication.ServerReceiver;
46 import net.grinder.communication.StreamReceiver;
47 import net.grinder.engine.agent.DebugThreadWorker.IsolateGrinderProcessRunner;
48 import net.grinder.messages.agent.ResetGrinderMessage;
49 import net.grinder.messages.agent.StartGrinderMessage;
50 import net.grinder.messages.agent.StopGrinderMessage;
51 import net.grinder.testutility.AbstractJUnit4FileTestCase;
52
53 import org.junit.After;
54 import org.junit.Before;
55 import org.junit.Test;
56 import org.mockito.Mock;
57 import org.mockito.MockitoAnnotations;
58 import org.slf4j.Logger;
59
60
61
62
63
64
65
66
67 public class TestAgentImplementation extends AbstractJUnit4FileTestCase {
68
69 @Mock private Logger m_logger;
70
71 @Before public void setUp() throws Exception {
72 DebugThreadWorkerFactory.setIsolatedRunnerClass(TestRunner.class.getName());
73 MockitoAnnotations.initMocks(this);
74 }
75
76 @After public void tearDown() throws Exception {
77 super.tearDown();
78 DebugThreadWorkerFactory.setIsolatedRunnerClass(null);
79 }
80
81 @Test public void testConstruction() throws Exception {
82 final File propertyFile = new File(getDirectory(), "properties");
83 final Agent agent = new AgentImplementation(m_logger, propertyFile, true);
84 agent.shutdown();
85
86 verify(m_logger).info("finished");
87 verifyNoMoreInteractions(m_logger);
88 }
89
90 @Test public void testRunDefaultProperties() throws Exception {
91
92 final File propertyFile = new File("grinder.properties");
93 propertyFile.deleteOnExit();
94
95 final File relativeScriptFile = new File("script/blah");
96 relativeScriptFile.deleteOnExit();
97 relativeScriptFile.getParentFile().mkdirs();
98 relativeScriptFile.getParentFile().deleteOnExit();
99 relativeScriptFile.createNewFile();
100
101 try {
102 final GrinderProperties properties = new GrinderProperties(propertyFile);
103
104 final Agent agent = new AgentImplementation(m_logger, null, true);
105
106 verifyNoMoreInteractions(m_logger);
107
108 agent.run();
109
110 verify(m_logger).info(contains("The Grinder"));
111 verify(m_logger).warn(contains("proceeding"),
112 contains("Failed to connect"));
113 verify(m_logger).error(contains("does not exist"));
114 verifyNoMoreInteractions(m_logger);
115 reset(m_logger);
116
117 properties.setBoolean("grinder.useConsole", false);
118 properties.save();
119
120 properties.setFile("grinder.script", relativeScriptFile);
121 properties.setInt("grinder.processes", 0);
122 properties.save();
123
124 agent.run();
125
126 verify(m_logger).info(contains("The Grinder"));
127 verify(m_logger).info(contains("command line"),
128 isA(WorkerProcessCommandLine.class));
129 verifyNoMoreInteractions(m_logger);
130 reset(m_logger);
131
132 properties.setFile("grinder.logDirectory",
133 getDirectory().getAbsoluteFile());
134 properties.save();
135
136 agent.run();
137
138 verify(m_logger).info(contains("The Grinder"));
139 verify(m_logger).info(contains("command line"),
140 isA(WorkerProcessCommandLine.class));
141 reset(m_logger);
142
143 agent.shutdown();
144 }
145 finally {
146 assertTrue(propertyFile.delete());
147 assertTrue(relativeScriptFile.delete());
148 assertTrue(relativeScriptFile.getParentFile().delete());
149 }
150 }
151
152 @Test public void testRun() throws Exception {
153 final File propertyFile = new File(getDirectory(), "properties");
154 final GrinderProperties properties = new GrinderProperties(propertyFile);
155
156 final Agent agent = new AgentImplementation(m_logger, propertyFile, true);
157
158 verifyNoMoreInteractions(m_logger);
159
160 agent.run();
161
162 verify(m_logger).info(contains("The Grinder"));
163 verify(m_logger).warn(contains("proceeding"),
164 contains("Failed to connect"));
165 verify(m_logger).error(contains("does not exist"));
166 verifyNoMoreInteractions(m_logger);
167 reset(m_logger);
168
169 properties.setBoolean("grinder.useConsole", false);
170 properties.save();
171
172 agent.run();
173
174 verify(m_logger).info(contains("The Grinder"));
175 verify(m_logger).error(contains("does not exist"));
176 verifyNoMoreInteractions(m_logger);
177 reset(m_logger);
178
179 final File scriptFile = new File(getDirectory(), "script");
180 assertTrue(scriptFile.createNewFile());
181
182 final File badFile = new File(scriptFile.getAbsoluteFile(), "blah");
183 properties.setFile("grinder.script", badFile);
184 properties.save();
185
186 agent.run();
187
188 verify(m_logger).info(contains("The Grinder"));
189 verify(m_logger).error(contains("does not exist"));
190 verifyNoMoreInteractions(m_logger);
191 reset(m_logger);
192
193 properties.setFile("grinder.script", scriptFile);
194 properties.setInt("grinder.processes", 0);
195 properties.save();
196
197 agent.run();
198
199 verify(m_logger).info(contains("The Grinder"));
200 verify(m_logger).info(contains("command line"),
201 isA(WorkerProcessCommandLine.class));
202 verifyNoMoreInteractions(m_logger);
203 reset(m_logger);
204
205 properties.setBoolean("grinder.debug.singleprocess", true);
206 properties.save();
207
208 agent.run();
209
210 verify(m_logger).info(contains("The Grinder"));
211 verify(m_logger).info(contains("threads rather than processes"));
212 verifyNoMoreInteractions(m_logger);
213 reset(m_logger);
214
215 properties.setProperty("grinder.jvm.arguments", "-Dsome_stuff=blah");
216 properties.save();
217
218 agent.run();
219
220 verify(m_logger).info(contains("The Grinder"));
221 verify(m_logger).info(contains("threads rather than processes"));
222 verify(m_logger).warn(contains("grinder.jvm.arguments"),
223 contains("some_stuff"));
224 verifyNoMoreInteractions(m_logger);
225 reset(m_logger);
226
227 agent.shutdown();
228
229 verify(m_logger).info(contains("finished"));
230 verifyNoMoreInteractions(m_logger);
231 }
232
233 @Test public void testWithConsole() throws Exception {
234 final ConsoleStub console = new ConsoleStub() {
235 public void onConnect() throws Exception {
236
237 verify(m_logger).info(contains("The Grinder"));
238
239 verify(m_logger, timeout(5000)).info(contains("connected"),
240 contains("localhost"));
241 verify(m_logger, timeout(5000)).info(contains("waiting"));
242
243
244 reset(m_logger);
245 final GrinderProperties grinderProperties = new GrinderProperties();
246 getSender().send(new StartGrinderMessage(grinderProperties, 99));
247
248 verify(m_logger, timeout(5000)).info("received a start message");
249
250 verify(m_logger, timeout(5000)).error(contains("grinder.py"));
251
252 verify(m_logger, timeout(5000)).info(contains("waiting"));
253
254
255 reset(m_logger);
256 getSender().send(new StartGrinderMessage(grinderProperties, 99));
257
258 verify(m_logger, timeout(5000)).info("received a start message");
259
260 verify(m_logger, timeout(5000)).info(contains("The Grinder"));
261
262 verify(m_logger, timeout(5000)).error(contains("grinder.py"));
263
264 verify(m_logger, timeout(5000)).info(contains("waiting"));
265
266
267 reset(m_logger);
268 getSender().send(new ResetGrinderMessage());
269
270 verify(m_logger, timeout(5000)).info("received a reset message");
271
272
273 verify(m_logger, timeout(5000)).info(contains("The Grinder"));
274
275 verify(m_logger, timeout(5000)).info(contains("waiting"));
276
277
278 reset(m_logger);
279 grinderProperties.setFile(GrinderProperties.SCRIPT, new File("foo.py"));
280 getSender().send(new StartGrinderMessage(grinderProperties, 99));
281
282 verify(m_logger, timeout(5000)).info("received a start message");
283
284 verify(m_logger, timeout(5000)).error(contains("foo.py"));
285
286 verify(m_logger, timeout(5000)).info(contains("waiting"));
287
288
289 getSender().send(new StopGrinderMessage());
290 }
291 };
292
293 final File propertyFile = new File(getDirectory(), "properties");
294 final GrinderProperties properties = new GrinderProperties(propertyFile);
295
296 final Agent agent = new AgentImplementation(m_logger, propertyFile, true);
297
298 properties.setInt("grinder.consolePort", console.getPort());
299 properties.save();
300
301 agent.run();
302
303 console.shutdown();
304
305 verify(m_logger).info("received a stop message");
306
307 verify(m_logger, timeout(5000)).info("communication shut down");
308
309 agent.shutdown();
310
311 verify(m_logger).info("finished");
312
313 verifyNoMoreInteractions(m_logger);
314 }
315
316 @Test public void testRampUp() throws Exception {
317 final ConsoleStub console = new ConsoleStub() {
318 public void onConnect() throws Exception {
319
320 verify(m_logger).info(contains("The Grinder"));
321
322 verify(m_logger, timeout(5000)).info(contains("connected"),
323 contains("localhost"));
324
325 verify(m_logger, timeout(5000)).info(contains("waiting"));
326
327
328 reset(m_logger);
329 final GrinderProperties grinderProperties = new GrinderProperties();
330 getSender().send(new StartGrinderMessage(grinderProperties, 99));
331
332 verify(m_logger, timeout(5000)).info("received a start message");
333
334 verify(m_logger, timeout(5000)).info(contains("DEBUG MODE"));
335
336
337 verify(m_logger, timeout(5000).times(10)).info(contains("started"));
338
339
340 reset(m_logger);
341 getSender().send(new ResetGrinderMessage());
342
343 verify(m_logger, timeout(5000)).info(contains("reset"));
344
345 verify(m_logger, timeout(5000)).info(contains("The Grinder"));
346
347 verify(m_logger, timeout(5000)).info(contains("waiting"));
348
349
350 reset(m_logger);
351 grinderProperties.setInt("grinder.initialProcesses", 10);
352
353 getSender().send(new StartGrinderMessage(grinderProperties, 99));
354
355 verify(m_logger, timeout(5000)).info("received a start message");
356
357 verify(m_logger, timeout(5000)).info(contains("DEBUG MODE"));
358
359
360 verify(m_logger, timeout(5000).times(10)).info(contains("started"));
361
362
363 getSender().send(new StopGrinderMessage());
364 }
365 };
366
367 final File propertyFile = new File(getDirectory(), "properties");
368 final GrinderProperties properties = new GrinderProperties(propertyFile);
369
370 final Agent agent = new AgentImplementation(m_logger, propertyFile, true);
371
372 final File script = new File(getDirectory(), "grinder.py");
373 assertTrue(script.createNewFile());
374
375 properties.setInt("grinder.consolePort", console.getPort());
376 properties.setInt("grinder.initialProcesses", 0);
377 properties.setInt("grinder.processes", 10);
378 properties.setInt("grinder.processIncrement", 1);
379 properties.setInt("grinder.processIncrementInterval", 10);
380 properties.setBoolean("grinder.debug.singleprocess", true);
381 properties.setFile("grinder.script", script);
382 properties.save();
383
384 agent.run();
385
386 console.shutdown();
387 agent.shutdown();
388 }
389
390 @Test public void testReconnect() throws Exception {
391 final File propertyFile = new File(getDirectory(), "properties");
392 final GrinderProperties properties = new GrinderProperties(propertyFile);
393
394 final boolean[] secondConsoleContacted = new boolean[1];
395
396 final GrinderProperties startProperties = new GrinderProperties();
397
398 final ConsoleStub console2 = new ConsoleStub() {
399 public void onConnect() throws Exception {
400
401 startProperties.setFile("grinder.script", new File("not there"));
402
403 getSender().send(new StartGrinderMessage(startProperties, 99));
404
405 synchronized (secondConsoleContacted) {
406 secondConsoleContacted[0] = true;
407 secondConsoleContacted.notifyAll();
408 }
409
410 getSender().send(new StopGrinderMessage());
411 }
412 };
413
414 final ConsoleStub console1 = new ConsoleStub() {
415 public void onConnect() throws Exception {
416 startProperties.setInt("grinder.consolePort", console2.getPort());
417
418 getSender().send(new StartGrinderMessage(startProperties, 22));
419 }
420 };
421
422 properties.setInt("grinder.consolePort", console1.getPort());
423 properties.save();
424
425 final Agent agent = new AgentImplementation(m_logger, propertyFile, true);
426
427 agent.run();
428
429 synchronized (secondConsoleContacted) {
430 final long start = System.currentTimeMillis();
431
432 while (!secondConsoleContacted[0] &&
433 System.currentTimeMillis() < start + 10000) {
434 secondConsoleContacted.wait(500);
435 }
436 }
437
438 assertTrue(secondConsoleContacted[0]);
439
440 console1.shutdown();
441 console2.shutdown();
442
443 agent.shutdown();
444 }
445
446 private abstract class ConsoleStub {
447 private final Acceptor m_acceptor;
448 private final ServerReceiver m_receiver;
449 private final Sender m_sender;
450
451 public ConsoleStub() throws CommunicationException, IOException {
452 final int port = findFreePort();
453
454 m_acceptor = new Acceptor("", port, 1, null);
455 m_receiver = new ServerReceiver();
456 m_receiver.receiveFrom(
457 m_acceptor, new ConnectionType[] { ConnectionType.AGENT }, 1, 10, 1000);
458 m_sender = new FanOutServerSender(m_acceptor, ConnectionType.AGENT, 3);
459
460 m_acceptor.addListener(ConnectionType.AGENT, new Acceptor.Listener() {
461 public void connectionAccepted(ConnectionType connectionType,
462 ConnectionIdentity connection) {
463 try {
464 onConnect();
465 }
466 catch (Throwable e) {
467 e.printStackTrace();
468 }
469 }
470
471 public void connectionClosed(ConnectionType connectionType,
472 ConnectionIdentity connection) { }
473 });
474 }
475
476 public int getPort() {
477 return m_acceptor.getPort();
478 }
479
480 public final void shutdown() throws CommunicationException {
481 m_acceptor.shutdown();
482 m_receiver.shutdown();
483 getSender().shutdown();
484 }
485
486 public final Sender getSender() {
487 return m_sender;
488 }
489
490 public abstract void onConnect() throws Exception;
491 }
492
493 public static class TestRunner implements IsolateGrinderProcessRunner {
494
495 public int run(InputStream in) {
496 try {
497 final StreamReceiver receiver = new StreamReceiver(in);
498 while (true) {
499 final Message message = receiver.waitForMessage();
500 if (message == null ||
501 message instanceof ResetGrinderMessage ||
502 message instanceof StopGrinderMessage) {
503 return 0;
504 }
505 }
506 }
507 catch (Exception e) {
508 e.printStackTrace();
509 return -1;
510 }
511 }
512 }
513 }