2 * Copyright (c) 2014 Cisco Systems, Inc. and others. All rights reserved.
4 * This program and the accompanying materials are made available under the
5 * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6 * and is available at http://www.eclipse.org/legal/epl-v10.html
9 package org.opendaylight.controller.netconf.nettyutil.handler.ssh.client;
11 import static org.junit.Assert.fail;
12 import static org.mockito.Matchers.any;
13 import static org.mockito.Matchers.anyBoolean;
14 import static org.mockito.Matchers.anyObject;
15 import static org.mockito.Matchers.anyString;
16 import static org.mockito.Mockito.doAnswer;
17 import static org.mockito.Mockito.doNothing;
18 import static org.mockito.Mockito.doReturn;
19 import static org.mockito.Mockito.doThrow;
20 import static org.mockito.Mockito.mock;
21 import static org.mockito.Mockito.times;
22 import static org.mockito.Mockito.verify;
23 import static org.mockito.Mockito.verifyNoMoreInteractions;
24 import static org.mockito.Mockito.verifyZeroInteractions;
25 import com.google.common.util.concurrent.FutureCallback;
26 import com.google.common.util.concurrent.Futures;
27 import com.google.common.util.concurrent.ListenableFuture;
28 import com.google.common.util.concurrent.SettableFuture;
29 import io.netty.buffer.ByteBuf;
30 import io.netty.buffer.Unpooled;
31 import io.netty.channel.Channel;
32 import io.netty.channel.ChannelHandlerContext;
33 import io.netty.channel.ChannelPromise;
34 import java.io.IOException;
35 import java.net.SocketAddress;
36 import org.apache.sshd.ClientChannel;
37 import org.apache.sshd.ClientSession;
38 import org.apache.sshd.SshClient;
39 import org.apache.sshd.client.channel.ChannelSubsystem;
40 import org.apache.sshd.client.future.AuthFuture;
41 import org.apache.sshd.client.future.ConnectFuture;
42 import org.apache.sshd.client.future.OpenFuture;
43 import org.apache.sshd.common.future.CloseFuture;
44 import org.apache.sshd.common.future.SshFuture;
45 import org.apache.sshd.common.future.SshFutureListener;
46 import org.apache.sshd.common.io.IoInputStream;
47 import org.apache.sshd.common.io.IoOutputStream;
48 import org.apache.sshd.common.io.IoReadFuture;
49 import org.apache.sshd.common.io.IoWriteFuture;
50 import org.apache.sshd.common.util.Buffer;
51 import org.junit.After;
52 import org.junit.Before;
53 import org.junit.Ignore;
54 import org.junit.Test;
55 import org.mockito.Matchers;
56 import org.mockito.Mock;
57 import org.mockito.MockitoAnnotations;
58 import org.mockito.invocation.InvocationOnMock;
59 import org.mockito.stubbing.Answer;
60 import org.opendaylight.controller.netconf.nettyutil.handler.ssh.authentication.AuthenticationHandler;
62 public class AsyncSshHandlerTest {
65 private SshClient sshClient;
67 private AuthenticationHandler authHandler;
69 private ChannelHandlerContext ctx;
71 private Channel channel;
73 private SocketAddress remoteAddress;
75 private SocketAddress localAddress;
77 private AsyncSshHandler asyncSshHandler;
79 private SshFutureListener<ConnectFuture> sshConnectListener;
80 private SshFutureListener<AuthFuture> sshAuthListener;
81 private SshFutureListener<OpenFuture> sshChannelOpenListener;
83 private ChannelPromise promise;
86 public void setUp() throws Exception {
87 MockitoAnnotations.initMocks(this);
94 promise = getMockedPromise();
96 asyncSshHandler = new AsyncSshHandler(authHandler, sshClient);
100 public void tearDown() throws Exception {
101 sshConnectListener = null;
102 sshAuthListener = null;
103 sshChannelOpenListener = null;
105 asyncSshHandler.close(ctx, getMockedPromise());
108 private void stubAuth() throws IOException {
109 doReturn("usr").when(authHandler).getUsername();
111 final AuthFuture authFuture = mock(AuthFuture.class);
112 Futures.addCallback(stubAddListener(authFuture), new SuccessFutureListener<AuthFuture>() {
114 public void onSuccess(final SshFutureListener<AuthFuture> result) {
115 sshAuthListener = result;
118 doReturn(authFuture).when(authHandler).authenticate(any(ClientSession.class));
121 @SuppressWarnings("unchecked")
122 private <T extends SshFuture<T>> ListenableFuture<SshFutureListener<T>> stubAddListener(final T future) {
123 final SettableFuture<SshFutureListener<T>> listenerSettableFuture = SettableFuture.create();
125 doAnswer(new Answer<Object>() {
127 public Object answer(final InvocationOnMock invocation) throws Throwable {
128 listenerSettableFuture.set((SshFutureListener<T>) invocation.getArguments()[0]);
131 }).when(future).addListener(any(SshFutureListener.class));
133 return listenerSettableFuture;
136 private void stubRemoteAddress() {
137 doReturn("remote").when(remoteAddress).toString();
140 private void stubCtx() {
141 doReturn(channel).when(ctx).channel();
142 doReturn(ctx).when(ctx).fireChannelActive();
143 doReturn(ctx).when(ctx).fireChannelInactive();
144 doReturn(ctx).when(ctx).fireChannelRead(anyObject());
145 doReturn(getMockedPromise()).when(ctx).newPromise();
148 private void stubChannel() {
149 doReturn("channel").when(channel).toString();
152 private void stubSshClient() {
153 doNothing().when(sshClient).start();
154 final ConnectFuture connectFuture = mock(ConnectFuture.class);
155 Futures.addCallback(stubAddListener(connectFuture), new SuccessFutureListener<ConnectFuture>() {
157 public void onSuccess(final SshFutureListener<ConnectFuture> result) {
158 sshConnectListener = result;
161 doReturn(connectFuture).when(sshClient).connect("usr", remoteAddress);
165 public void testConnectSuccess() throws Exception {
166 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
168 final IoInputStream asyncOut = getMockedIoInputStream();
169 final IoOutputStream asyncIn = getMockedIoOutputStream();
170 final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
171 final ClientSession sshSession = getMockedSshSession(subsystemChannel);
172 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
174 sshConnectListener.operationComplete(connectFuture);
175 sshAuthListener.operationComplete(getSuccessAuthFuture());
176 sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
178 verify(subsystemChannel).setStreaming(ClientChannel.Streaming.Async);
180 verify(promise).setSuccess();
181 verifyNoMoreInteractions(promise);
182 verify(ctx).fireChannelActive();
186 public void testRead() throws Exception {
187 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
189 final IoInputStream asyncOut = getMockedIoInputStream();
190 final IoOutputStream asyncIn = getMockedIoOutputStream();
191 final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
192 final ClientSession sshSession = getMockedSshSession(subsystemChannel);
193 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
195 sshConnectListener.operationComplete(connectFuture);
196 sshAuthListener.operationComplete(getSuccessAuthFuture());
197 sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
199 verify(ctx).fireChannelRead(any(ByteBuf.class));
203 public void testReadClosed() throws Exception {
204 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
206 final IoInputStream asyncOut = getMockedIoInputStream();
207 final IoReadFuture mockedReadFuture = asyncOut.read(null);
209 Futures.addCallback(stubAddListener(mockedReadFuture), new SuccessFutureListener<IoReadFuture>() {
211 public void onSuccess(final SshFutureListener<IoReadFuture> result) {
212 doReturn(new IllegalStateException()).when(mockedReadFuture).getException();
213 doReturn(mockedReadFuture).when(mockedReadFuture).removeListener(Matchers.<SshFutureListener<IoReadFuture>>any());
214 doReturn(true).when(asyncOut).isClosing();
215 doReturn(true).when(asyncOut).isClosed();
216 result.operationComplete(mockedReadFuture);
220 final IoOutputStream asyncIn = getMockedIoOutputStream();
221 final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
222 final ClientSession sshSession = getMockedSshSession(subsystemChannel);
223 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
225 sshConnectListener.operationComplete(connectFuture);
226 sshAuthListener.operationComplete(getSuccessAuthFuture());
227 sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
229 verify(ctx).fireChannelInactive();
233 public void testReadFail() throws Exception {
234 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
236 final IoInputStream asyncOut = getMockedIoInputStream();
237 final IoReadFuture mockedReadFuture = asyncOut.read(null);
239 Futures.addCallback(stubAddListener(mockedReadFuture), new SuccessFutureListener<IoReadFuture>() {
241 public void onSuccess(final SshFutureListener<IoReadFuture> result) {
242 doReturn(new IllegalStateException()).when(mockedReadFuture).getException();
243 doReturn(mockedReadFuture).when(mockedReadFuture).removeListener(Matchers.<SshFutureListener<IoReadFuture>>any());
244 result.operationComplete(mockedReadFuture);
248 final IoOutputStream asyncIn = getMockedIoOutputStream();
249 final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
250 final ClientSession sshSession = getMockedSshSession(subsystemChannel);
251 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
253 sshConnectListener.operationComplete(connectFuture);
254 sshAuthListener.operationComplete(getSuccessAuthFuture());
255 sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
257 verify(ctx).fireChannelInactive();
261 public void testWrite() throws Exception {
262 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
264 final IoInputStream asyncOut = getMockedIoInputStream();
265 final IoOutputStream asyncIn = getMockedIoOutputStream();
266 final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
267 final ClientSession sshSession = getMockedSshSession(subsystemChannel);
268 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
270 sshConnectListener.operationComplete(connectFuture);
271 sshAuthListener.operationComplete(getSuccessAuthFuture());
272 sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
274 final ChannelPromise writePromise = getMockedPromise();
275 asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0, 1, 2, 3, 4, 5}), writePromise);
277 verify(writePromise).setSuccess();
281 public void testWriteClosed() throws Exception {
282 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
284 final IoInputStream asyncOut = getMockedIoInputStream();
285 final IoOutputStream asyncIn = getMockedIoOutputStream();
287 final IoWriteFuture ioWriteFuture = asyncIn.write(null);
289 Futures.addCallback(stubAddListener(ioWriteFuture), new SuccessFutureListener<IoWriteFuture>() {
291 public void onSuccess(final SshFutureListener<IoWriteFuture> result) {
292 doReturn(false).when(ioWriteFuture).isWritten();
293 doReturn(new IllegalStateException()).when(ioWriteFuture).getException();
294 doReturn(true).when(asyncIn).isClosing();
295 doReturn(true).when(asyncIn).isClosed();
296 result.operationComplete(ioWriteFuture);
300 final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
301 final ClientSession sshSession = getMockedSshSession(subsystemChannel);
302 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
304 sshConnectListener.operationComplete(connectFuture);
305 sshAuthListener.operationComplete(getSuccessAuthFuture());
306 sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
308 final ChannelPromise writePromise = getMockedPromise();
309 asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0,1,2,3,4,5}), writePromise);
311 verify(writePromise).setFailure(any(Throwable.class));
315 public void testWritePendingOne() throws Exception {
316 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
318 final IoInputStream asyncOut = getMockedIoInputStream();
319 final IoOutputStream asyncIn = getMockedIoOutputStream();
320 final IoWriteFuture ioWriteFuture = asyncIn.write(null);
322 final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
323 final ClientSession sshSession = getMockedSshSession(subsystemChannel);
324 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
326 sshConnectListener.operationComplete(connectFuture);
327 sshAuthListener.operationComplete(getSuccessAuthFuture());
328 sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
330 final ChannelPromise firstWritePromise = getMockedPromise();
332 // intercept listener for first write, so we can invoke successful write later thus simulate pending of the first write
333 final ListenableFuture<SshFutureListener<IoWriteFuture>> firstWriteListenerFuture = stubAddListener(ioWriteFuture);
334 asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0,1,2,3,4,5}), firstWritePromise);
335 final SshFutureListener<IoWriteFuture> firstWriteListener = firstWriteListenerFuture.get();
336 // intercept second listener, this is the listener for pending write for the pending write to know when pending state ended
337 final ListenableFuture<SshFutureListener<IoWriteFuture>> pendingListener = stubAddListener(ioWriteFuture);
339 final ChannelPromise secondWritePromise = getMockedPromise();
340 // now make write throw pending exception
341 doThrow(org.apache.sshd.common.io.WritePendingException.class).when(asyncIn).write(any(Buffer.class));
342 asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0, 1, 2, 3, 4, 5}), secondWritePromise);
344 doReturn(ioWriteFuture).when(asyncIn).write(any(Buffer.class));
346 verifyZeroInteractions(firstWritePromise, secondWritePromise);
348 // make first write stop pending
349 firstWriteListener.operationComplete(ioWriteFuture);
351 // notify listener for second write that pending has ended
352 pendingListener.get().operationComplete(ioWriteFuture);
354 // verify both write promises successful
355 verify(firstWritePromise).setSuccess();
356 verify(secondWritePromise).setSuccess();
359 @Ignore("Pending queue is not limited")
361 public void testWritePendingMax() throws Exception {
362 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
364 final IoInputStream asyncOut = getMockedIoInputStream();
365 final IoOutputStream asyncIn = getMockedIoOutputStream();
366 final IoWriteFuture ioWriteFuture = asyncIn.write(null);
368 final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
369 final ClientSession sshSession = getMockedSshSession(subsystemChannel);
370 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
372 sshConnectListener.operationComplete(connectFuture);
373 sshAuthListener.operationComplete(getSuccessAuthFuture());
374 sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
376 final ChannelPromise firstWritePromise = getMockedPromise();
378 // intercept listener for first write, so we can invoke successful write later thus simulate pending of the first write
379 final ListenableFuture<SshFutureListener<IoWriteFuture>> firstWriteListenerFuture = stubAddListener(ioWriteFuture);
380 asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0,1,2,3,4,5}), firstWritePromise);
382 final ChannelPromise secondWritePromise = getMockedPromise();
383 // now make write throw pending exception
384 doThrow(org.apache.sshd.common.io.WritePendingException.class).when(asyncIn).write(any(Buffer.class));
385 for (int i = 0; i < 1001; i++) {
386 asyncSshHandler.write(ctx, Unpooled.copiedBuffer(new byte[]{0, 1, 2, 3, 4, 5}), secondWritePromise);
389 verify(secondWritePromise, times(1)).setFailure(any(Throwable.class));
393 public void testDisconnect() throws Exception {
394 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
396 final IoInputStream asyncOut = getMockedIoInputStream();
397 final IoOutputStream asyncIn = getMockedIoOutputStream();
398 final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
399 final ClientSession sshSession = getMockedSshSession(subsystemChannel);
400 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
402 sshConnectListener.operationComplete(connectFuture);
403 sshAuthListener.operationComplete(getSuccessAuthFuture());
404 sshChannelOpenListener.operationComplete(getSuccessOpenFuture());
406 final ChannelPromise disconnectPromise = getMockedPromise();
407 asyncSshHandler.disconnect(ctx, disconnectPromise);
409 verify(sshSession).close(anyBoolean());
410 verify(disconnectPromise).setSuccess();
411 verify(ctx).fireChannelInactive();
414 private OpenFuture getSuccessOpenFuture() {
415 final OpenFuture failedOpenFuture = mock(OpenFuture.class);
416 doReturn(true).when(failedOpenFuture).isOpened();
417 return failedOpenFuture;
420 private AuthFuture getSuccessAuthFuture() {
421 final AuthFuture authFuture = mock(AuthFuture.class);
422 doReturn(true).when(authFuture).isSuccess();
426 private ConnectFuture getSuccessConnectFuture(final ClientSession sshSession) {
427 final ConnectFuture connectFuture = mock(ConnectFuture.class);
428 doReturn(true).when(connectFuture).isConnected();
430 doReturn(sshSession).when(connectFuture).getSession();
431 return connectFuture;
434 private ClientSession getMockedSshSession(final ChannelSubsystem subsystemChannel) throws IOException {
435 final ClientSession sshSession = mock(ClientSession.class);
437 doReturn("sshSession").when(sshSession).toString();
438 doReturn("serverVersion").when(sshSession).getServerVersion();
439 doReturn(false).when(sshSession).isClosed();
440 doReturn(false).when(sshSession).isClosing();
441 final CloseFuture closeFuture = mock(CloseFuture.class);
442 Futures.addCallback(stubAddListener(closeFuture), new SuccessFutureListener<CloseFuture>() {
444 public void onSuccess(final SshFutureListener<CloseFuture> result) {
445 doReturn(true).when(closeFuture).isClosed();
446 result.operationComplete(closeFuture);
449 doReturn(closeFuture).when(sshSession).close(false);
451 doReturn(subsystemChannel).when(sshSession).createSubsystemChannel(anyString());
456 private ChannelSubsystem getMockedSubsystemChannel(final IoInputStream asyncOut, final IoOutputStream asyncIn) throws IOException {
457 final ChannelSubsystem subsystemChannel = mock(ChannelSubsystem.class);
458 doReturn("subsystemChannel").when(subsystemChannel).toString();
460 doNothing().when(subsystemChannel).setStreaming(any(ClientChannel.Streaming.class));
461 final OpenFuture openFuture = mock(OpenFuture.class);
463 Futures.addCallback(stubAddListener(openFuture), new SuccessFutureListener<OpenFuture>() {
465 public void onSuccess(final SshFutureListener<OpenFuture> result) {
466 sshChannelOpenListener = result;
470 doReturn(asyncOut).when(subsystemChannel).getAsyncOut();
472 doReturn(openFuture).when(subsystemChannel).open();
473 doReturn(asyncIn).when(subsystemChannel).getAsyncIn();
474 return subsystemChannel;
477 private IoOutputStream getMockedIoOutputStream() {
478 final IoOutputStream mock = mock(IoOutputStream.class);
479 final IoWriteFuture ioWriteFuture = mock(IoWriteFuture.class);
480 doReturn(ioWriteFuture).when(ioWriteFuture).addListener(Matchers.<SshFutureListener<IoWriteFuture>>any());
481 doReturn(true).when(ioWriteFuture).isWritten();
483 Futures.addCallback(stubAddListener(ioWriteFuture), new SuccessFutureListener<IoWriteFuture>() {
485 public void onSuccess(final SshFutureListener<IoWriteFuture> result) {
486 result.operationComplete(ioWriteFuture);
490 doReturn(ioWriteFuture).when(mock).write(any(Buffer.class));
491 doReturn(false).when(mock).isClosed();
492 doReturn(false).when(mock).isClosing();
496 private IoInputStream getMockedIoInputStream() {
497 final IoInputStream mock = mock(IoInputStream.class);
498 final IoReadFuture ioReadFuture = mock(IoReadFuture.class);
499 doReturn(null).when(ioReadFuture).getException();
500 doReturn(ioReadFuture).when(ioReadFuture).removeListener(Matchers.<SshFutureListener<IoReadFuture>>any());
501 doReturn(5).when(ioReadFuture).getRead();
502 doReturn(new Buffer(new byte[]{0, 1, 2, 3, 4})).when(ioReadFuture).getBuffer();
503 doReturn(ioReadFuture).when(ioReadFuture).addListener(Matchers.<SshFutureListener<IoReadFuture>>any());
505 // Always success for read
506 Futures.addCallback(stubAddListener(ioReadFuture), new SuccessFutureListener<IoReadFuture>() {
508 public void onSuccess(final SshFutureListener<IoReadFuture> result) {
509 result.operationComplete(ioReadFuture);
513 doReturn(ioReadFuture).when(mock).read(any(Buffer.class));
514 doReturn(false).when(mock).isClosed();
515 doReturn(false).when(mock).isClosing();
520 public void testConnectFailOpenChannel() throws Exception {
521 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
523 final IoInputStream asyncOut = getMockedIoInputStream();
524 final IoOutputStream asyncIn = getMockedIoOutputStream();
525 final ChannelSubsystem subsystemChannel = getMockedSubsystemChannel(asyncOut, asyncIn);
526 final ClientSession sshSession = getMockedSshSession(subsystemChannel);
527 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
529 sshConnectListener.operationComplete(connectFuture);
531 sshAuthListener.operationComplete(getSuccessAuthFuture());
533 verify(subsystemChannel).setStreaming(ClientChannel.Streaming.Async);
536 sshChannelOpenListener.operationComplete(getFailedOpenFuture());
537 fail("Exception expected");
538 } catch (final Exception e) {
539 verify(promise).setFailure(any(Throwable.class));
540 verifyNoMoreInteractions(promise);
541 // TODO should ctx.channelInactive be called if we throw exception ?
546 public void testConnectFailAuth() throws Exception {
547 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
549 final ClientSession sshSession = mock(ClientSession.class);
550 doReturn(true).when(sshSession).isClosed();
551 final ConnectFuture connectFuture = getSuccessConnectFuture(sshSession);
553 sshConnectListener.operationComplete(connectFuture);
555 final AuthFuture authFuture = getFailedAuthFuture();
558 sshAuthListener.operationComplete(authFuture);
559 fail("Exception expected");
560 } catch (final Exception e) {
561 verify(promise).setFailure(any(Throwable.class));
562 verifyNoMoreInteractions(promise);
563 // TODO should ctx.channelInactive be called ?
567 private AuthFuture getFailedAuthFuture() {
568 final AuthFuture authFuture = mock(AuthFuture.class);
569 doReturn(false).when(authFuture).isSuccess();
570 doReturn(new IllegalStateException()).when(authFuture).getException();
574 private OpenFuture getFailedOpenFuture() {
575 final OpenFuture authFuture = mock(OpenFuture.class);
576 doReturn(false).when(authFuture).isOpened();
577 doReturn(new IllegalStateException()).when(authFuture).getException();
582 public void testConnectFail() throws Exception {
583 asyncSshHandler.connect(ctx, remoteAddress, localAddress, promise);
585 final ConnectFuture connectFuture = getFailedConnectFuture();
587 sshConnectListener.operationComplete(connectFuture);
588 fail("Exception expected");
589 } catch (final Exception e) {
590 verify(promise).setFailure(any(Throwable.class));
591 verifyNoMoreInteractions(promise);
592 // TODO should ctx.channelInactive be called ?
596 private ConnectFuture getFailedConnectFuture() {
597 final ConnectFuture connectFuture = mock(ConnectFuture.class);
598 doReturn(false).when(connectFuture).isConnected();
599 doReturn(new IllegalStateException()).when(connectFuture).getException();
600 return connectFuture;
603 private ChannelPromise getMockedPromise() {
604 final ChannelPromise promise = mock(ChannelPromise.class);
605 doReturn(promise).when(promise).setSuccess();
606 doReturn(promise).when(promise).setFailure(any(Throwable.class));
610 private static abstract class SuccessFutureListener<T extends SshFuture<T>> implements FutureCallback<SshFutureListener<T>> {
613 public abstract void onSuccess(final SshFutureListener<T> result);
616 public void onFailure(final Throwable t) {
617 throw new RuntimeException(t);