/*
* Copyright 2004-2006 Stefan Reuter
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package org.asteriskjava.manager.internal;
import static org.easymock.EasyMock.createMock;
import static org.easymock.EasyMock.expect;
import static org.easymock.EasyMock.replay;
import static org.easymock.EasyMock.verify;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.asteriskjava.AsteriskVersion;
import org.asteriskjava.manager.AuthenticationFailedException;
import org.asteriskjava.manager.ManagerConnectionState;
import org.asteriskjava.manager.ManagerEventListener;
import org.asteriskjava.manager.TimeoutException;
import org.asteriskjava.manager.action.CommandAction;
import org.asteriskjava.manager.action.PingAction;
import org.asteriskjava.manager.action.StatusAction;
import org.asteriskjava.manager.event.ConnectEvent;
import org.asteriskjava.manager.event.DisconnectEvent;
import org.asteriskjava.manager.event.ManagerEvent;
import org.asteriskjava.manager.event.NewChannelEvent;
import org.asteriskjava.manager.response.ManagerResponse;
import org.asteriskjava.util.SocketConnectionFacade;
import org.junit.Before;
import org.junit.Test;
public class ManagerConnectionImplTest
{
protected SocketConnectionFacade mockSocket;
protected ManagerWriterMock mockWriter;
protected ManagerReaderMock mockReader;
protected MockedManagerConnectionImpl mc;
@Before
public void setUp() throws Exception
{
mockWriter = new ManagerWriterMock();
mockWriter.setExpectedUsername("username");
// md5 sum of 12345password
mockWriter.setExpectedKey("40b1b887502902a8ce61a16e44630f7c");
mockReader = new ManagerReaderMock();
mockSocket = createMock(SocketConnectionFacade.class);
mc = new MockedManagerConnectionImpl(mockReader, mockWriter, mockSocket);
mockWriter.setDispatcher(mc);
}
@Test
public void testDefaultConstructor()
{
assertEquals("Invalid default hostname", "localhost", mc.getHostname());
assertEquals("Invalid default port", 5038, mc.getPort());
}
@Test
public void testRegisterUserEventClass()
{
ManagerReader managerReader;
managerReader = createMock(ManagerReader.class);
managerReader.registerEventClass(MyUserEvent.class);
replay(managerReader);
mc = new MockedManagerConnectionImpl(managerReader, mockWriter, mockSocket);
mc.registerUserEventClass(MyUserEvent.class);
assertEquals("unexpected call to createSocket", 0, mc.createSocketCalls);
assertEquals("unexpected call to createWriter", 0, mc.createWriterCalls);
assertEquals("createReader not called 1 time", 1, mc.createReaderCalls);
verify(managerReader);
}
@Test
public void testLogin() throws Exception
{
MockedManagerEventListener listener;
long startTime;
long endTime;
long duration;
listener = new MockedManagerEventListener();
replay(mockSocket);
mc.setUsername("username");
mc.setPassword("password");
mc.addEventListener(listener);
mc.setDefaultResponseTimeout(5000);
startTime = System.currentTimeMillis();
mc.login();
endTime = System.currentTimeMillis();
duration = endTime - startTime;
assertEquals("createSocket not called 1 time", 1, mc.createSocketCalls);
assertEquals("createWriter not called 1 time", 1, mc.createWriterCalls);
assertEquals("createReader not called 1 time", 1, mc.createReaderCalls);
assertEquals("challenge action not sent 1 time", 1, mockWriter.challengeActionsSent);
assertEquals("login action not sent 1 time", 1, mockWriter.loginActionsSent);
assertEquals("unexpected other actions sent", 0, mockWriter.otherActionsSent);
assertEquals("setSocket() not called 1 time", 1, mockReader.setSocketCalls);
// Some time for the reader thread to be started. Otherwise run() might
// not yet have been
// called.
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
// ugly hack to make this work when the thread is interrupted coz a
// response has been received but the ManagerConnection was not yet
// sleeping
Thread.sleep(100);
}
assertEquals("run() not called 1 time", 1, mockReader.runCalls);
assertEquals("unexpected call to die()", 0, mockReader.dieCalls);
assertEquals("state is not CONNECTED", ManagerConnectionState.CONNECTED, mc.getState());
assertEquals("must have handled exactly one events", 1, listener.eventsHandled.size());
/*
assertTrue(
"first event handled must be a ProtocolIdentifierReceivedEvent",
listener.eventsHandled.get(0) instanceof ProtocolIdentifierReceivedEvent);
*/
assertTrue("event handled must be a ConnectEvent",
listener.eventsHandled.get(0) instanceof ConnectEvent);
verify(mockSocket);
assertTrue("login() took longer than 2 second, probably a notify error (duration was " + duration + " is msec)", duration <= 2000);
}
@Test
public void testLoginIncorrectKey() throws Exception
{
mockSocket.close();
replay(mockSocket);
mockWriter.setExpectedUsername("username");
// md5 sum of 12345password
mockWriter.setExpectedKey("40b1b887502902a8ce61a16e44630f7c");
mc.setUsername("username");
mc.setPassword("wrong password");
try
{
mc.login();
fail("No AuthenticationFailedException thrown");
}
catch (AuthenticationFailedException e)
{
}
assertEquals("createSocket not called 1 time", 1, mc.createSocketCalls);
assertEquals("createWriter not called 1 time", 1, mc.createWriterCalls);
assertEquals("createReader not called 1 time", 1, mc.createReaderCalls);
assertEquals("challenge action not sent 1 time", 1, mockWriter.challengeActionsSent);
assertEquals("login action not sent 1 time", 1, mockWriter.loginActionsSent);
assertEquals("unexpected other actions sent", 0, mockWriter.otherActionsSent);
assertEquals("setSocket() not called 1 time", 1, mockReader.setSocketCalls);
// Some time for the reader thread to be started. Otherwise run() might
// not yet have been
// called.
try
{
Thread.sleep(100);
}
catch (InterruptedException e)
{
// ugly hack to make this work when the thread is interrupted coz a
// response has been received but the ManagerConnection was not yet
// sleeping
Thread.sleep(100);
}
assertEquals("run() not called 1 time", 1, mockReader.runCalls);
assertEquals("unexpected call to die()", 0, mockReader.dieCalls);
verify(mockSocket);
}
@Test
public void testLoginIOExceptionOnConnect() throws Exception
{
replay(mockSocket);
mc.setThrowIOExceptionOnFirstSocketCreate(true);
try
{
mc.login();
fail("No IOException thrown");
}
catch (IOException e)
{
}
assertEquals("createSocket not called 1 time", 1, mc.createSocketCalls);
assertEquals("createWriter not called 1 time", 1, mc.createWriterCalls);
assertEquals("createReader not called 1 time", 1, mc.createReaderCalls);
assertEquals("unexpected challenge action sent", 0, mockWriter.challengeActionsSent);
assertEquals("unexpected login action sent", 0, mockWriter.loginActionsSent);
assertEquals("unexpected other actions sent", 0, mockWriter.otherActionsSent);
assertEquals("unexpected call to setSocket()", 0, mockReader.setSocketCalls);
assertEquals("unexpected call to run()", 0, mockReader.runCalls);
assertEquals("unexpected call to die()", 0, mockReader.dieCalls);
verify(mockSocket);
}
@Test
public void testLoginTimeoutOnConnect() throws Exception
{
mc.setDefaultResponseTimeout(50);
mockSocket.close();
replay(mockSocket);
// provoke timeout
mockWriter.setSendProtocolIdentifierReceivedEvent(false);
try
{
mc.login();
fail("No TimeoutException on login()");
}
catch (TimeoutException e)
{
assertEquals("Timeout waiting for protocol identifier", e.getMessage());
}
assertEquals("createSocket not called 1 time", 1, mc.createSocketCalls);
assertEquals("createWriter not called 1 time", 1, mc.createWriterCalls);
assertEquals("createReader not called 1 time", 1, mc.createReaderCalls);
assertEquals("unexpected challenge action sent", 0, mockWriter.challengeActionsSent);
assertEquals("unexpected login action sent", 0, mockWriter.loginActionsSent);
assertEquals("unexpected other actions sent", 0, mockWriter.otherActionsSent);
assertEquals("setSocket() not called 1 time", 1, mockReader.setSocketCalls);
// Some time for the reader thread to be started. Otherwise run() might
// not yet have been
// called.
Thread.sleep(10);
assertEquals("run() not called 1 time", 1, mockReader.runCalls);
assertEquals("unexpected call to die()", 0, mockReader.dieCalls);
verify(mockSocket);
}
@Test
public void testLoginTimeoutOnChallengeAction() throws Exception
{
mc.setDefaultResponseTimeout(200);
mockSocket.close();
replay(mockSocket);
// provoke timeout
mockWriter.setSendResponse(false);
try
{
mc.login();
fail("No TimeoutException on login()");
}
catch (AuthenticationFailedException e)
{
assertEquals("Unable to send challenge action", e.getMessage());
assertEquals("Timeout waiting for response to Challenge", e.getCause().getMessage());
assertTrue(e.getCause() instanceof TimeoutException);
}
assertEquals("createSocket not called 1 time", 1, mc.createSocketCalls);
assertEquals("createWriter not called 1 time", 1, mc.createWriterCalls);
assertEquals("createReader not called 1 time", 1, mc.createReaderCalls);
assertEquals("challenge action not sent 1 time", 1, mockWriter.challengeActionsSent);
assertEquals("unexpected login action sent", 0, mockWriter.loginActionsSent);
assertEquals("unexpected other actions sent", 0, mockWriter.otherActionsSent);
assertEquals("setSocket() not called 1 time", 1, mockReader.setSocketCalls);
// Some time for the reader thread to be started. Otherwise run() might
// not yet have been
// called.
Thread.sleep(10);
assertEquals("run() not called 1 time", 1, mockReader.runCalls);
assertEquals("unexpected call to die()", 0, mockReader.dieCalls);
verify(mockSocket);
}
@Test
public void testLogoffWhenConnected() throws Exception
{
mockSocket.close();
replay(mockSocket);
// fake connect
mc.connect();
mc.setState(ManagerConnectionState.CONNECTED);
mc.logoff();
assertEquals("logoff action not sent 1 time", 1,
mockWriter.logoffActionsSent);
verify(mockSocket);
}
@Test
public void testLogoffWhenNotConnected() throws Exception
{
replay(mockSocket);
try
{
mc.logoff();
fail("Expected IllegalStateException when calling logoff when not connected");
}
catch (IllegalStateException e)
{
// fine
}
assertEquals("unexpected logoff action sent", 0, mockWriter.logoffActionsSent);
verify(mockSocket);
}
@Test
public void testSendActionWithNullAction() throws Exception
{
// fake connect
mc.connect();
try
{
mc.sendAction(null);
fail("No IllgealArgumentException thrown");
}
catch (IllegalArgumentException e)
{
}
}
@Test
public void testSendActionWhenNotConnected() throws Exception
{
StatusAction statusAction;
statusAction = new StatusAction();
try
{
mc.sendAction(statusAction);
fail("No IllegalStateException thrown");
}
catch (IllegalStateException e)
{
}
}
@Test
public void testSendAction() throws Exception
{
StatusAction statusAction;
ManagerResponse response;
statusAction = new StatusAction();
statusAction.setActionId("123");
// fake connect
mc.connect();
mc.setState(ManagerConnectionState.CONNECTED);
response = mc.sendAction(statusAction);
assertEquals("incorrect actionId in action", "123", statusAction.getActionId());
assertEquals("incorrect actionId in response", "123", response.getActionId());
assertEquals("incorrect response", "Success", response.getResponse());
assertEquals("other actions not sent 1 time", 1, mockWriter.otherActionsSent);
}
@Test
public void testSendActionTimeout() throws Exception
{
StatusAction statusAction;
statusAction = new StatusAction();
statusAction.setActionId("123");
mc.setDefaultResponseTimeout(200);
// fake connect
mc.connect();
mc.setState(ManagerConnectionState.CONNECTED);
// provoke timeout
mockWriter.setSendResponse(false);
try
{
mc.sendAction(statusAction);
fail("No TimeoutException thrown");
}
catch (TimeoutException e)
{
}
assertEquals("other actions not sent 1 time", 1, mockWriter.otherActionsSent);
}
@Test
public void testDispatchResponseUnexpectedResponse()
{
ManagerResponse response;
response = new ManagerResponse();
// internalActionId: 123_0
response.setActionId("123_0-abc");
response.setResponse("Success");
// expected result is ignoring the response and logging
mc.dispatchResponse(response);
}
@Test
public void testDispatchResponseMissingInternalActionId()
{
ManagerResponse response;
response = new ManagerResponse();
response.setActionId("abc");
response.setResponse("Success");
// expected result is ignoring the response and logging
mc.dispatchResponse(response);
}
@Test
public void testDispatchResponseNullActionId()
{
ManagerResponse response;
response = new ManagerResponse();
response.setActionId(null);
response.setResponse("Success");
// expected result is ignoring the response and logging
mc.dispatchResponse(response);
}
@Test
public void testDispatchResponseNullResponse()
{
// expected result is ignoring and logging
mc.dispatchResponse(null);
}
@Test
public void testReconnect() throws Exception
{
DisconnectEvent disconnectEvent;
replay(mockSocket);
disconnectEvent = new DisconnectEvent(this);
// fake successful login
mc.setState(ManagerConnectionState.CONNECTED);
mc.setUsername("username");
mc.setPassword("password");
mc.dispatchEvent(disconnectEvent);
// wait for reconnect thread to do its work
Thread.sleep(1000);
assertEquals("createSocket not called 1 time", 1, mc.createSocketCalls);
assertEquals("createWriter not called 1 time", 1, mc.createWriterCalls);
assertEquals("createReader not called 1 time", 1, mc.createReaderCalls);
assertEquals("challenge action not sent 1 time", 1, mockWriter.challengeActionsSent);
assertEquals("login action not sent 1 time", 1, mockWriter.loginActionsSent);
assertEquals("unexpected other actions sent", 0, mockWriter.otherActionsSent);
assertEquals("state is not CONNECTED", ManagerConnectionState.CONNECTED, mc.getState());
verify(mockSocket);
}
@Test
public void testReconnectWithIOException() throws Exception
{
DisconnectEvent disconnectEvent;
replay(mockSocket);
disconnectEvent = new DisconnectEvent(this);
// fake successful login
mc.setState(ManagerConnectionState.CONNECTED);
mc.setThrowIOExceptionOnFirstSocketCreate(true);
mc.setUsername("username");
mc.setPassword("password");
mc.dispatchEvent(disconnectEvent);
// wait for reconnect thread to do its work
Thread.sleep(1000);
assertEquals("createSocket not called 1 time", 2, mc.createSocketCalls);
assertEquals("createWriter not called 1 time", 1, mc.createWriterCalls);
assertEquals("createReader not called 1 time", 1, mc.createReaderCalls);
assertEquals("challenge action not sent 1 time", 1, mockWriter.challengeActionsSent);
assertEquals("login action not sent 1 time", 1, mockWriter.loginActionsSent);
assertEquals("unexpected other actions sent", 0, mockWriter.otherActionsSent);
assertEquals("state is not CONNECTED", ManagerConnectionState.CONNECTED, mc.getState());
verify(mockSocket);
}
@Test
public void testReconnectWithTimeoutException() throws Exception
{
DisconnectEvent disconnectEvent;
mockSocket.close();
replay(mockSocket);
disconnectEvent = new DisconnectEvent(this);
// fake successful login
mc.setState(ManagerConnectionState.CONNECTED);
mc.setThrowTimeoutExceptionOnFirstLogin(true);
mc.setUsername("username");
mc.setPassword("password");
mc.dispatchEvent(disconnectEvent);
// wait for reconnect thread to do its work
Thread.sleep(1000);
assertEquals("createSocket not called 2 time", 2, mc.createSocketCalls);
assertEquals("createWriter not called 1 time", 1, mc.createWriterCalls);
assertEquals("createReader not called 1 time", 1, mc.createReaderCalls);
assertEquals("challenge action not sent 1 time", 1, mockWriter.challengeActionsSent);
assertEquals("login action not sent 1 time", 1, mockWriter.loginActionsSent);
assertEquals("unexpected other actions sent", 0, mockWriter.otherActionsSent);
assertEquals("state is not CONNECTED", ManagerConnectionState.CONNECTED, mc.getState());
verify(mockSocket);
}
@SuppressWarnings("unchecked")
@Test
public void testDispatchEventWithMultipleEventHandlers()
{
final int count = 20;
ManagerEvent event;
final List<Integer> list;
// verify that event handlers are called in the correct order
event = new NewChannelEvent(this);
list = createMock(List.class);
for (int i = 0; i < count; i++)
{
final int index = i;
expect(list.add(index)).andReturn(true);
mc.addEventListener(new ManagerEventListener()
{
public void onManagerEvent(ManagerEvent event)
{
list.add(index);
}
});
}
replay(list);
mc.dispatchEvent(event);
verify(list);
}
@Test
public void testIsShowVersionCommandAction()
{
assertTrue(mc.isShowVersionCommandAction(new CommandAction("show version")));
assertTrue(mc.isShowVersionCommandAction(new CommandAction("core show version")));
assertTrue(mc.isShowVersionCommandAction(new CommandAction("core show version foo bar")));
assertFalse(mc.isShowVersionCommandAction(new CommandAction("foo show version")));
assertFalse(mc.isShowVersionCommandAction(new PingAction()));
}
private class MockedManagerEventListener implements ManagerEventListener
{
List<ManagerEvent> eventsHandled;
public MockedManagerEventListener()
{
this.eventsHandled = new ArrayList<ManagerEvent>();
}
public void onManagerEvent(ManagerEvent event)
{
eventsHandled.add(event);
}
}
private class MockedManagerConnectionImpl
extends
ManagerConnectionImpl
{
ManagerReader mockReader;
ManagerWriter mockWriter;
SocketConnectionFacade mockSocket;
private boolean throwIOExceptionOnFirstSocketCreate = false;
private boolean throwTimeoutExceptionOnFirstLogin = false;
public int createReaderCalls = 0;
public int createWriterCalls = 0;
public int createSocketCalls = 0;
public int loginCalls = 0;
public MockedManagerConnectionImpl(ManagerReader mockReader,
ManagerWriter mockWriter, SocketConnectionFacade mockSocket)
{
super();
this.mockReader = mockReader;
this.mockWriter = mockWriter;
this.mockSocket = mockSocket;
}
public void setThrowTimeoutExceptionOnFirstLogin(boolean b)
{
this.throwTimeoutExceptionOnFirstLogin = b;
}
public void setThrowIOExceptionOnFirstSocketCreate(boolean b)
{
this.throwIOExceptionOnFirstSocketCreate = b;
}
@Override
public String getUsername()
{
return username;
}
@Override
public String getPassword()
{
return password;
}
public void setState(ManagerConnectionState state)
{
this.state = state;
}
@Override
protected ManagerReader createReader(Dispatcher d, Object source)
{
createReaderCalls++;
return mockReader;
}
@Override
protected ManagerWriter createWriter()
{
createWriterCalls++;
return mockWriter;
}
@Override
protected SocketConnectionFacade createSocket() throws IOException
{
createSocketCalls++;
if (throwIOExceptionOnFirstSocketCreate && createSocketCalls == 1)
{
throw new IOException();
}
return mockSocket;
}
@Override
protected void doLogin(long timeout, String events) throws IOException,
AuthenticationFailedException, TimeoutException
{
loginCalls++;
if (throwTimeoutExceptionOnFirstLogin && loginCalls == 1)
{
disconnect();
throw new TimeoutException("Provoked timeout");
}
super.doLogin(timeout, events);
}
@Override
protected AsteriskVersion determineVersion()
{
return null;
}
}
}