/*
* Catroid: An on-device visual programming system for Android devices
* Copyright (C) 2010-2016 The Catrobat Team
* (<http://developer.catrobat.org/credits>)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* An additional term exception under section 7 of the GNU Affero
* General Public License, version 3, is available at
* http://developer.catrobat.org/license_additional_term
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.catrobat.catroid.test.scratchconverter;
import android.net.Uri;
import android.test.AndroidTestCase;
import com.google.android.gms.common.images.WebImage;
import com.koushikdutta.async.callback.CompletedCallback;
import com.koushikdutta.async.future.Future;
import com.koushikdutta.async.http.AsyncHttpClient;
import com.koushikdutta.async.http.AsyncHttpClient.WebSocketConnectCallback;
import com.koushikdutta.async.http.WebSocket;
import org.catrobat.catroid.common.Constants;
import org.catrobat.catroid.scratchconverter.Client;
import org.catrobat.catroid.scratchconverter.Client.ConvertCallback;
import org.catrobat.catroid.scratchconverter.Client.DownloadCallback;
import org.catrobat.catroid.scratchconverter.ClientException;
import org.catrobat.catroid.scratchconverter.WebSocketClient;
import org.catrobat.catroid.scratchconverter.protocol.Job;
import org.catrobat.catroid.scratchconverter.protocol.MessageListener;
import org.catrobat.catroid.scratchconverter.protocol.WebSocketMessageListener;
import org.catrobat.catroid.scratchconverter.protocol.command.AuthenticateCommand;
import org.catrobat.catroid.scratchconverter.protocol.command.RetrieveInfoCommand;
import org.catrobat.catroid.scratchconverter.protocol.command.ScheduleJobCommand;
import org.catrobat.catroid.scratchconverter.protocol.message.base.ClientIDMessage;
import org.catrobat.catroid.scratchconverter.protocol.message.base.ErrorMessage;
import org.catrobat.catroid.scratchconverter.protocol.message.base.InfoMessage;
import org.catrobat.catroid.test.utils.Reflection;
import org.mockito.Mockito;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import java.lang.reflect.Field;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyFloat;
import static org.mockito.Matchers.anyLong;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.verifyZeroInteractions;
public class WebSocketClientTest extends AndroidTestCase {
private static final int VALID_CLIENT_ID = 1;
private MessageListener messageListenerMock;
private AsyncHttpClient asyncHttpClientMock;
private WebSocketClient webSocketClient;
@Override
protected void setUp() throws Exception {
super.setUp();
System.setProperty("dexmaker.dexcache", getContext().getCacheDir().getPath());
messageListenerMock = Mockito.mock(WebSocketMessageListener.class);
asyncHttpClientMock = Mockito.mock(AsyncHttpClient.class);
webSocketClient = new WebSocketClient(VALID_CLIENT_ID, messageListenerMock);
}
//------------------------------------------------------------------------------------------------------------------
// Connect, Disconnect & Authenticate tests
//------------------------------------------------------------------------------------------------------------------
public void testAsyncConnectAndAuthenticateWhetherWebSocketObjectSettersAreCalledCorrectly() {
final WebSocket webSocketMock = Mockito.mock(WebSocket.class);
final Client.ConnectAuthCallback connectAuthCallbackMock = Mockito.mock(Client.ConnectAuthCallback.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument must not be null", invocation.getArguments()[0]);
assertEquals("First argument must be equal to MessageListener instance",
messageListenerMock, invocation.getArguments()[0]);
return null;
}
}).when(webSocketMock).setStringCallback(any(WebSocket.StringCallback.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument must not be null", invocation.getArguments()[0]);
assertEquals("First argument must be equal to WebSocketClient instance",
webSocketClient, invocation.getArguments()[0]);
return null;
}
}).when(webSocketMock).setClosedCallback(any(CompletedCallback.class));
doAnswer(new Answer<Future<WebSocket>>() {
@Override
public Future<WebSocket> answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of websocket() call must not be null", invocation.getArguments()[0]);
assertNull("Expecting second argument of websocket() call to be null", invocation.getArguments()[1]);
assertNotNull("Third argument of websocket() call must not be null", invocation.getArguments()[2]);
assertEquals("Wrong WebSocket-URL given", Constants.SCRATCH_CONVERTER_WEB_SOCKET,
invocation.getArguments()[0]);
// call connectCallback.onCompleted(...)
((WebSocketConnectCallback) invocation.getArguments()[2]).onCompleted(null, webSocketMock);
verify(webSocketMock, times(1)).setStringCallback(any(WebSocket.StringCallback.class));
verify(webSocketMock, times(1)).setClosedCallback(any(CompletedCallback.class));
verify(webSocketMock, times(1)).send(anyString());
verifyNoMoreInteractions(connectAuthCallbackMock);
verifyNoMoreInteractions(webSocketMock);
return null;
}
}).when(asyncHttpClientMock).websocket(anyString(), anyString(), any(WebSocketConnectCallback.class));
// run the test
webSocketClient.setAsyncHttpClient(asyncHttpClientMock);
webSocketClient.connectAndAuthenticate(connectAuthCallbackMock);
verify(asyncHttpClientMock, times(1)).websocket(anyString(), anyString(), any(WebSocketConnectCallback.class));
verifyNoMoreInteractions(asyncHttpClientMock);
}
public void testAsyncConnectAndAuthenticateSuccessfullyConnectedAndAuthenticatedWithValidClientID()
throws NoSuchFieldException, IllegalAccessException {
final long expectedClientID = VALID_CLIENT_ID;
Reflection.setPrivateField(WebSocketClient.class, webSocketClient, "clientID", expectedClientID);
final AuthenticateCommand expectedAuthenticateCommand = new AuthenticateCommand(expectedClientID);
final Client.ConnectAuthCallback connectAuthCallbackMock = Mockito.mock(Client.ConnectAuthCallback.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
final long newClientID = (long) invocation.getArguments()[0];
assertFalse("Successfully connected and authenticated but Client state is wrong",
webSocketClient.isClosed());
assertTrue("Successfully connected and authenticated but Client state is wrong",
webSocketClient.isConnected());
assertTrue("Successfully connected and authenticated but Client state is wrong",
webSocketClient.isAuthenticated());
assertEquals("Already valid client ID changed unexpectedly by the server!",
expectedClientID, newClientID);
return null;
}
}).when(connectAuthCallbackMock).onSuccess(anyLong());
final WebSocket webSocketMock = Mockito.mock(WebSocket.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of send() call must not be null", invocation.getArguments()[0]);
final String jsonCommand = (String) invocation.getArguments()[0];
assertEquals("Unexpected or invalid command! Expected AuthenticateCommand",
expectedAuthenticateCommand.toJson().toString(), jsonCommand);
// simulate authentication response message from server:
assertTrue("Client seems to be not connected! Cannot send authentication response message",
webSocketClient.isConnected());
webSocketClient.onBaseMessage(new ClientIDMessage(expectedClientID));
verify(connectAuthCallbackMock, times(1)).onSuccess(any(Long.class));
verifyNoMoreInteractions(connectAuthCallbackMock);
return null;
}
}).when(webSocketMock).send(any(String.class));
doAnswer(new Answer<Future<WebSocket>>() {
@Override
public Future<WebSocket> answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of websocket() call must not be null", invocation.getArguments()[0]);
assertNull("Expecting second argument of websocket() call to be null", invocation.getArguments()[1]);
assertNotNull("Third argument of websocket() call must not be null", invocation.getArguments()[2]);
assertEquals("Wrong WebSocket-URL given", Constants.SCRATCH_CONVERTER_WEB_SOCKET,
invocation.getArguments()[0]);
// call connectCallback.onCompleted(...)
WebSocketConnectCallback connectCallback = (WebSocketConnectCallback) invocation.getArguments()[2];
connectCallback.onCompleted(null, webSocketMock);
verify(webSocketMock, times(1)).setStringCallback(any(WebSocket.StringCallback.class));
verify(webSocketMock, times(1)).setClosedCallback(any(CompletedCallback.class));
verify(webSocketMock, times(1)).send(anyString());
verifyNoMoreInteractions(connectAuthCallbackMock);
verifyNoMoreInteractions(webSocketMock);
return null;
}
}).when(asyncHttpClientMock).websocket(anyString(), anyString(), any(WebSocketConnectCallback.class));
// run the test
webSocketClient.setAsyncHttpClient(asyncHttpClientMock);
webSocketClient.connectAndAuthenticate(connectAuthCallbackMock);
}
public void testAsyncConnectAndAuthenticateSuccessfullyConnectedAndAuthenticatedWithInvalidClientID()
throws NoSuchFieldException, IllegalAccessException {
final long unexpectedInvalidClientID = Client.INVALID_CLIENT_ID;
Reflection.setPrivateField(WebSocketClient.class, webSocketClient, "clientID", unexpectedInvalidClientID);
final AuthenticateCommand expectedAuthenticateCommand = new AuthenticateCommand(unexpectedInvalidClientID);
final Client.ConnectAuthCallback connectAuthCallbackMock = Mockito.mock(Client.ConnectAuthCallback.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
final long newClientID = (long) invocation.getArguments()[0];
assertFalse("Successfully connected and authenticated but Client state is wrong",
webSocketClient.isClosed());
assertTrue("Successfully connected and authenticated but Client state is wrong",
webSocketClient.isConnected());
assertTrue("Successfully connected and authenticated but Client state is wrong",
webSocketClient.isAuthenticated());
assertTrue("Server accepted and sent INVALID client ID back! This should not happen!",
newClientID != unexpectedInvalidClientID);
return null;
}
}).when(connectAuthCallbackMock).onSuccess(anyLong());
final WebSocket webSocketMock = Mockito.mock(WebSocket.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of send() call must not be null", invocation.getArguments()[0]);
final String jsonCommand = (String) invocation.getArguments()[0];
assertEquals("Unexpected or invalid command! Expected AuthenticateCommand",
expectedAuthenticateCommand.toJson().toString(), jsonCommand);
// simulate authentication response message from server:
assertTrue("Client seems to be not connected! Cannot send authentication response message",
webSocketClient.isConnected());
webSocketClient.onBaseMessage(new ClientIDMessage(VALID_CLIENT_ID));
verify(connectAuthCallbackMock, times(1)).onSuccess(anyLong());
verifyNoMoreInteractions(connectAuthCallbackMock);
return null;
}
}).when(webSocketMock).send(anyString());
doAnswer(new Answer<Future<WebSocket>>() {
@Override
public Future<WebSocket> answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of websocket() call must not be null", invocation.getArguments()[0]);
assertNull("Expecting second argument of websocket() call to be null", invocation.getArguments()[1]);
assertNotNull("Third argument of websocket() call must not be null", invocation.getArguments()[2]);
assertEquals("Wrong WebSocket-URL given", Constants.SCRATCH_CONVERTER_WEB_SOCKET,
invocation.getArguments()[0]);
// call connectCallback.onCompleted(...)
WebSocketConnectCallback connectCallback = (WebSocketConnectCallback) invocation.getArguments()[2];
connectCallback.onCompleted(null, webSocketMock);
verify(webSocketMock, times(1)).setStringCallback(any(WebSocket.StringCallback.class));
verify(webSocketMock, times(1)).setClosedCallback(any(CompletedCallback.class));
verify(webSocketMock, times(1)).send(anyString());
verifyNoMoreInteractions(connectAuthCallbackMock);
verifyNoMoreInteractions(webSocketMock);
return null;
}
}).when(asyncHttpClientMock).websocket(anyString(), anyString(), any(WebSocketConnectCallback.class));
// run the test
webSocketClient.setAsyncHttpClient(asyncHttpClientMock);
webSocketClient.connectAndAuthenticate(connectAuthCallbackMock);
}
public void testAsyncConnectAndAuthenticateConnectionFailed() {
final String expectedCancelExceptionMessage = "Successfully canceled the connection by the server!";
final Client.ConnectAuthCallback connectAuthCallbackMock = Mockito.mock(Client.ConnectAuthCallback.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
final ClientException ex = (ClientException) invocation.getArguments()[0];
assertEquals("Unexpected closedExceptionMessage message",
"java.lang.Exception: " + expectedCancelExceptionMessage, ex.getMessage());
assertTrue("Client not did not close connection correctly", webSocketClient.isClosed());
return null;
}
}).when(connectAuthCallbackMock).onConnectionFailure(any(ClientException.class));
// mocking asyncHttpClient.websocket(...)
doAnswer(new Answer<Future<WebSocket>>() {
@Override
public Future<WebSocket> answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of websocket() call must not be null", invocation.getArguments()[0]);
assertNull("Expecting second argument of websocket() call to be null", invocation.getArguments()[1]);
assertNotNull("Third argument of websocket() call must not be null", invocation.getArguments()[2]);
assertEquals("Wrong WebSocket-URL given",
Constants.SCRATCH_CONVERTER_WEB_SOCKET, invocation.getArguments()[0]);
// call connectCallback.onCompleted(...)
WebSocketConnectCallback connectCallback = (WebSocketConnectCallback) invocation.getArguments()[2];
// simulate that the connection failed!
final WebSocket webSocketMock = Mockito.mock(WebSocket.class);
connectCallback.onCompleted(new Exception(expectedCancelExceptionMessage), webSocketMock);
verify(connectAuthCallbackMock, times(1)).onConnectionFailure(any(ClientException.class));
verifyNoMoreInteractions(connectAuthCallbackMock);
verifyZeroInteractions(webSocketMock);
return null;
}
}).when(asyncHttpClientMock).websocket(anyString(), anyString(), any(WebSocketConnectCallback.class));
// run the test
webSocketClient.setAsyncHttpClient(asyncHttpClientMock);
webSocketClient.connectAndAuthenticate(connectAuthCallbackMock);
}
public void testAsyncConnectAndAuthenticateAuthenticationFailed() throws NoSuchFieldException,
IllegalAccessException {
final long clientID = VALID_CLIENT_ID;
Reflection.setPrivateField(WebSocketClient.class, webSocketClient, "clientID", clientID);
final String expectedErrorMessage = "Authentication failed!";
final Client.ConnectAuthCallback connectAuthCallbackMock = Mockito.mock(Client.ConnectAuthCallback.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
final ClientException ex = (ClientException) invocation.getArguments()[0];
assertEquals("Wrong error message received", expectedErrorMessage, ex.getMessage());
assertTrue("Wrong client state", webSocketClient.isConnected());
assertFalse("Wrong client state", webSocketClient.isAuthenticated());
return null;
}
}).when(connectAuthCallbackMock).onAuthenticationFailure(any(ClientException.class));
final WebSocket webSocketMock = Mockito.mock(WebSocket.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
// simulate authentication failed response message from server:
webSocketClient.onBaseMessage(new ErrorMessage(expectedErrorMessage));
verify(connectAuthCallbackMock, times(1)).onAuthenticationFailure(any(ClientException.class));
verifyNoMoreInteractions(connectAuthCallbackMock);
return null;
}
}).when(webSocketMock).send(any(String.class));
doAnswer(new Answer<Future<WebSocket>>() {
@Override
public Future<WebSocket> answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of websocket() call must not be null", invocation.getArguments()[0]);
assertNull("Expecting second argument of websocket() call to be null", invocation.getArguments()[1]);
assertNotNull("Third argument of websocket() call must not be null", invocation.getArguments()[2]);
assertEquals("Wrong WebSocket-URL given",
Constants.SCRATCH_CONVERTER_WEB_SOCKET, invocation.getArguments()[0]);
// call connectCallback.onCompleted(...)
WebSocketConnectCallback connectCallback = (WebSocketConnectCallback) invocation.getArguments()[2];
connectCallback.onCompleted(null, webSocketMock);
verify(webSocketMock, times(1)).setStringCallback(any(WebSocket.StringCallback.class));
verify(webSocketMock, times(1)).setClosedCallback(any(CompletedCallback.class));
verify(webSocketMock, times(1)).send(anyString());
verifyNoMoreInteractions(connectAuthCallbackMock);
verifyNoMoreInteractions(webSocketMock);
return null;
}
}).when(asyncHttpClientMock).websocket(anyString(), anyString(), any(WebSocketConnectCallback.class));
// run the test
webSocketClient.setAsyncHttpClient(asyncHttpClientMock);
webSocketClient.connectAndAuthenticate(connectAuthCallbackMock);
}
public void testServerClosedConnectionAfterConnectionIsEstablished()
throws NoSuchFieldException, IllegalAccessException {
final String expectedClosedExceptionMessage = "Successfully closed the connection!";
final Client.ConnectAuthCallback connectAuthCallbackMock = Mockito.mock(Client.ConnectAuthCallback.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
final ClientException ex = (ClientException) invocation.getArguments()[0];
assertTrue("Successfully closed connection but Client state is wrong", webSocketClient.isClosed());
assertEquals("Unexpected exception message",
"java.lang.Exception: " + expectedClosedExceptionMessage, ex.getMessage());
return null;
}
}).when(connectAuthCallbackMock).onConnectionClosed(any(ClientException.class));
webSocketClient.setAsyncHttpClient(asyncHttpClientMock);
Reflection.setPrivateField(WebSocketClient.class, webSocketClient, "state", Client.State.CONNECTED_AUTHENTICATED);
Field connectAuthCallbackField = WebSocketClient.class.getDeclaredField("connectAuthCallback");
connectAuthCallbackField.setAccessible(true);
connectAuthCallbackField.set(webSocketClient, connectAuthCallbackMock);
// now simulate that server closes the connection:
webSocketClient.onCompleted(new Exception(expectedClosedExceptionMessage));
verify(connectAuthCallbackMock, times(1)).onConnectionClosed(any(ClientException.class));
verifyNoMoreInteractions(connectAuthCallbackMock);
}
public void testClientClosedConnection() throws InterruptedException, NoSuchFieldException, IllegalAccessException {
final String expectedExceptionMessage = "Client closed connection successfully!";
final Client.ConnectAuthCallback connectAuthCallbackMock = Mockito.mock(Client.ConnectAuthCallback.class);
final WebSocket webSocketMock = Mockito.mock(WebSocket.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
webSocketClient.onCompleted(new Exception(expectedExceptionMessage));
verify(connectAuthCallbackMock, times(1)).onConnectionClosed(any(ClientException.class));
verifyNoMoreInteractions(connectAuthCallbackMock);
return null;
}
}).when(webSocketMock).close();
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
final ClientException ex = (ClientException) invocation.getArguments()[0];
assertTrue("Successfully closed connection but Client state is wrong", webSocketClient.isClosed());
assertEquals("Unexpected exception message",
"java.lang.Exception: " + expectedExceptionMessage, ex.getMessage());
return null;
}
}).when(connectAuthCallbackMock).onConnectionClosed(any(ClientException.class));
webSocketClient.setAsyncHttpClient(asyncHttpClientMock);
Reflection.setPrivateField(WebSocketClient.class, webSocketClient, "state", Client.State.CONNECTED_AUTHENTICATED);
Field newField = WebSocketClient.class.getDeclaredField("webSocket");
newField.setAccessible(true);
newField.set(webSocketClient, webSocketMock);
Field connectAuthCallbackField = WebSocketClient.class.getDeclaredField("connectAuthCallback");
connectAuthCallbackField.setAccessible(true);
connectAuthCallbackField.set(webSocketClient, connectAuthCallbackMock);
webSocketClient.close();
verify(webSocketMock, times(1)).close();
}
//------------------------------------------------------------------------------------------------------------------
// Command tests
//------------------------------------------------------------------------------------------------------------------
public void testSendRetrieveInfoCommand() throws NoSuchFieldException, IllegalAccessException {
final RetrieveInfoCommand expectedRetrieveInfoCommand = new RetrieveInfoCommand();
final WebSocket webSocketMock = Mockito.mock(WebSocket.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of send() call must not be null", invocation.getArguments()[0]);
final String jsonCommand = (String) invocation.getArguments()[0];
assertEquals("Unexpected or invalid command! Expected RetrieveInfoCommand",
expectedRetrieveInfoCommand.toJson().toString(), jsonCommand);
return null;
}
}).when(webSocketMock).send(any(String.class));
Reflection.setPrivateField(WebSocketClient.class, webSocketClient, "state", Client.State.CONNECTED_AUTHENTICATED);
Field newField = WebSocketClient.class.getDeclaredField("webSocket");
newField.setAccessible(true);
newField.set(webSocketClient, webSocketMock);
webSocketClient.retrieveInfo();
verify(webSocketMock, times(1)).send(any(String.class));
verifyNoMoreInteractions(webSocketMock);
}
public void testSendScheduleJobCommand() throws NoSuchFieldException, IllegalAccessException {
final long expectedJobID = 1;
final boolean expectedForceValue = false;
final boolean expectedVerboseValue = false;
final String expectedProgramTitle = "Test program";
final WebImage expectedProgramImage = new WebImage(Uri.parse("http://www.catrobat.org/images/logo.png"));
final ScheduleJobCommand expectedScheduleJobCommand = new ScheduleJobCommand(expectedJobID,
expectedForceValue, expectedVerboseValue);
final ConvertCallback convertCallbackMock = Mockito.mock(ConvertCallback.class);
doAnswer(new Answer<Boolean>() {
@Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of scheduleJob() call must not be null", invocation.getArguments()[0]);
assertNotNull("Third argument of scheduleJob() call must not be null", invocation.getArguments()[2]);
final Job job = (Job) invocation.getArguments()[0];
assertEquals("Unexpected jobID given", expectedJobID, job.getJobID());
assertEquals("Unexpected program title given", expectedProgramTitle, job.getTitle());
assertEquals("Unexpected program image given", expectedProgramImage, job.getImage());
assertEquals("Force value changed unexpectedly", expectedForceValue, invocation.getArguments()[1]);
assertEquals("ConvertCallback expected", convertCallbackMock, invocation.getArguments()[2]);
return true;
}
}).when(messageListenerMock).scheduleJob(any(Job.class), any(Boolean.class), any(ConvertCallback.class));
final WebSocket webSocketMock = Mockito.mock(WebSocket.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of send() call must not be null", invocation.getArguments()[0]);
final String jsonCommand = (String) invocation.getArguments()[0];
assertEquals("Unexpected or invalid command! Expected RetrieveInfoCommand",
expectedScheduleJobCommand.toJson().toString(), jsonCommand);
return null;
}
}).when(webSocketMock).send(any(String.class));
Reflection.setPrivateField(WebSocketClient.class, webSocketClient, "state", Client.State.CONNECTED_AUTHENTICATED);
Field newField = WebSocketClient.class.getDeclaredField("webSocket");
newField.setAccessible(true);
newField.set(webSocketClient, webSocketMock);
webSocketClient.setConvertCallback(convertCallbackMock);
webSocketClient.convertProgram(expectedJobID, expectedProgramTitle, expectedProgramImage,
expectedVerboseValue, expectedForceValue);
verifyZeroInteractions(convertCallbackMock);
verify(webSocketMock, times(1)).send(anyString());
verifyNoMoreInteractions(webSocketMock);
}
//------------------------------------------------------------------------------------------------------------------
// Event tests
//------------------------------------------------------------------------------------------------------------------
public void testReceivedOnBaseMessageEventWithInfoMessage() throws NoSuchFieldException, IllegalAccessException {
final Job expectedUnscheduledJob = new Job(1, "Test program",
new WebImage(Uri.parse("http://www.catrobat.org/images/logo.png")));
expectedUnscheduledJob.setState(Job.State.UNSCHEDULED);
final Job expectedFinishedJob = new Job(2, "Test program2", null);
expectedFinishedJob.setState(Job.State.FINISHED);
final String expectedDownloadURL = Constants.SCRATCH_CONVERTER_BASE_URL
+ "/download?job_id=2&client_id=" + VALID_CLIENT_ID + "&fname=Test%20program2";
expectedFinishedJob.setDownloadURL(expectedDownloadURL);
final float expectedCatrobatLanguageVersion = Constants.CURRENT_CATROBAT_LANGUAGE_VERSION;
final Job[] expectedJobs = new Job[] { expectedUnscheduledJob, expectedFinishedJob };
final InfoMessage infoMessage = new InfoMessage(expectedCatrobatLanguageVersion, expectedJobs);
final DownloadCallback downloadCallbackMock = Mockito.mock(DownloadCallback.class);
final ConvertCallback convertCallbackMock = Mockito.mock(ConvertCallback.class);
doAnswer(new Answer<DownloadCallback>() {
int counter = 0;
@Override
public DownloadCallback answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of restoreJobIfRunning() call must not be null",
invocation.getArguments()[0]);
assertNotNull("Second argument of restoreJobIfRunning() call must not be null",
invocation.getArguments()[1]);
final Job job = (Job) invocation.getArguments()[0];
final ConvertCallback convertCallback = (ConvertCallback) invocation.getArguments()[1];
assertEquals("Given job title differs from expected", job.getTitle(), expectedJobs[counter].getTitle());
assertEquals("ConvertCallback is not equal to the corresponding mock object",
expectedJobs[counter], job);
assertEquals("ConvertCallback is not equal to the corresponding mock object",
convertCallbackMock, convertCallback);
counter++;
if (job.getState() == Job.State.FINISHED) {
return downloadCallbackMock;
}
return null;
}
}).when(messageListenerMock).restoreJobIfRunning(any(Job.class), any(ConvertCallback.class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of onInfo() call must not be null", invocation.getArguments()[0]);
assertNotNull("Second argument of onInfo() call must not be null", invocation.getArguments()[1]);
final float catrobatLanguageVersion = (float) invocation.getArguments()[0];
final Job[] jobs = (Job[]) invocation.getArguments()[1];
assertEquals("Unexpected catrobat language version",
expectedCatrobatLanguageVersion, catrobatLanguageVersion);
assertEquals("Unexpected number of jobs given", expectedJobs.length, jobs.length);
for (int i = 0; i < jobs.length; i++) {
final Job job = jobs[i];
final Job expectedJob = expectedJobs[i];
assertEquals("Unexpected job given", expectedJob, job);
}
return null;
}
}).when(convertCallbackMock).onInfo(any(Float.class), any(Job[].class));
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of onInfo() call must not be null", invocation.getArguments()[0]);
assertNotNull("Second argument of onInfo() call must not be null", invocation.getArguments()[1]);
final Job finishedJob = (Job) invocation.getArguments()[0];
final DownloadCallback downloadCallback = (DownloadCallback)
invocation.getArguments()[1];
final String downloadURL = (String) invocation.getArguments()[2];
assertEquals("DownloadCallback must be", downloadCallback, downloadCallbackMock);
assertEquals("Invalid download URL given", expectedDownloadURL, downloadURL);
assertEquals("Unexpected job given", expectedFinishedJob, finishedJob);
return null;
}
}).when(convertCallbackMock).onConversionAlreadyFinished(any(Job.class), any(DownloadCallback.class),
any(String.class));
Reflection.setPrivateField(WebSocketClient.class, webSocketClient, "state", Client.State.CONNECTED_AUTHENTICATED);
webSocketClient.setConvertCallback(convertCallbackMock);
webSocketClient.onBaseMessage(infoMessage);
verify(convertCallbackMock, times(1)).onInfo(anyFloat(), any(Job[].class));
verify(convertCallbackMock, times(1)).onConversionAlreadyFinished(any(Job.class),
any(DownloadCallback.class), anyString());
verifyNoMoreInteractions(convertCallbackMock);
verify(messageListenerMock, times(1)).setBaseMessageHandler(eq(webSocketClient));
verify(messageListenerMock, times(2)).restoreJobIfRunning(any(Job.class), any(ConvertCallback.class));
verifyNoMoreInteractions(messageListenerMock);
}
public void testReceivedOnBaseMessageEventWithErrorMessageWhenClientIsNotConnected()
throws NoSuchFieldException, IllegalAccessException {
final String expectedErrorMessageString = "Error message successfully received";
final ErrorMessage expectedErrorMessage = new ErrorMessage(expectedErrorMessageString);
final Client.ConnectAuthCallback connectAuthCallbackMock = Mockito.mock(Client.ConnectAuthCallback.class);
final ConvertCallback convertCallbackMock = Mockito.mock(ConvertCallback.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of onError() call must not be null", invocation.getArguments()[0]);
final String errorMessageString = (String) invocation.getArguments()[0];
assertEquals("Wrong error message given!", expectedErrorMessageString, errorMessageString);
return null;
}
}).when(convertCallbackMock).onError(any(String.class));
Reflection.setPrivateField(WebSocketClient.class, webSocketClient, "state", Client.State.NOT_CONNECTED);
Field connectAuthCallbackField = WebSocketClient.class.getDeclaredField("connectAuthCallback");
connectAuthCallbackField.setAccessible(true);
connectAuthCallbackField.set(webSocketClient, connectAuthCallbackMock);
webSocketClient.setConvertCallback(convertCallbackMock);
webSocketClient.onBaseMessage(expectedErrorMessage);
verify(connectAuthCallbackMock, times(0)).onAuthenticationFailure(any(ClientException.class));
verifyNoMoreInteractions(connectAuthCallbackMock);
verify(convertCallbackMock, times(1)).onError(anyString());
verifyNoMoreInteractions(convertCallbackMock);
}
public void testReceivedOnBaseMessageEventWithErrorMessageWhenClientIsConnectedButNotAuthenticated()
throws NoSuchFieldException, IllegalAccessException {
final String expectedErrorMessageString = "Error message successfully received";
final ErrorMessage expectedErrorMessage = new ErrorMessage(expectedErrorMessageString);
final Client.ConnectAuthCallback connectAuthCallbackMock = Mockito.mock(Client.ConnectAuthCallback.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
final ClientException ex = (ClientException) invocation.getArguments()[0];
assertEquals("Wrong error message received", expectedErrorMessageString, ex.getMessage());
assertTrue("Wrong client state", webSocketClient.isConnected());
assertFalse("Wrong client state", webSocketClient.isAuthenticated());
return null;
}
}).when(connectAuthCallbackMock).onAuthenticationFailure(any(ClientException.class));
final ConvertCallback convertCallbackMock = Mockito.mock(ConvertCallback.class);
Field field = WebSocketClient.class.getDeclaredField("state");
field.setAccessible(true);
field.set(webSocketClient, Client.State.CONNECTED);
Field connectAuthCallbackField = WebSocketClient.class.getDeclaredField("connectAuthCallback");
connectAuthCallbackField.setAccessible(true);
connectAuthCallbackField.set(webSocketClient, connectAuthCallbackMock);
webSocketClient.setConvertCallback(convertCallbackMock);
webSocketClient.onBaseMessage(expectedErrorMessage);
verify(connectAuthCallbackMock, times(1)).onAuthenticationFailure(any(ClientException.class));
verifyNoMoreInteractions(convertCallbackMock);
verifyZeroInteractions(convertCallbackMock);
}
public void testReceivedOnBaseMessageEventWithErrorMessageWhenClientIsConnectedAndAuthenticated()
throws NoSuchFieldException, IllegalAccessException {
final String expectedErrorMessageString = "Error message successfully received";
final ErrorMessage expectedErrorMessage = new ErrorMessage(expectedErrorMessageString);
final Client.ConnectAuthCallback connectAuthCallbackMock = Mockito.mock(Client.ConnectAuthCallback.class);
final ConvertCallback convertCallbackMock = Mockito.mock(ConvertCallback.class);
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNull("Expecting job to be null here", invocation.getArguments()[0]);
assertNotNull("ClientException must not be null", invocation.getArguments()[1]);
final ClientException ex = (ClientException) invocation.getArguments()[1];
assertEquals("Wrong error message received", expectedErrorMessageString, ex.getMessage());
assertTrue("Wrong client state", webSocketClient.isConnected());
assertTrue("Wrong client state", webSocketClient.isAuthenticated());
return null;
}
}).when(convertCallbackMock).onConversionFailure(any(Job.class), any(ClientException.class));
Field field = WebSocketClient.class.getDeclaredField("state");
field.setAccessible(true);
field.set(webSocketClient, Client.State.CONNECTED_AUTHENTICATED);
Field connectAuthCallbackField = WebSocketClient.class.getDeclaredField("connectAuthCallback");
connectAuthCallbackField.setAccessible(true);
connectAuthCallbackField.set(webSocketClient, connectAuthCallbackMock);
webSocketClient.setConvertCallback(convertCallbackMock);
webSocketClient.onBaseMessage(expectedErrorMessage);
verify(connectAuthCallbackMock, times(0)).onAuthenticationFailure(any(ClientException.class));
verifyNoMoreInteractions(connectAuthCallbackMock);
verify(convertCallbackMock, times(1)).onConversionFailure(any(Job.class), any(ClientException.class));
verifyNoMoreInteractions(convertCallbackMock);
}
public void testReceivedUserCanceledConversionEvent() throws NoSuchFieldException, IllegalAccessException {
final long expectedJobID = 1;
doAnswer(new Answer<Void>() {
@Override
public Void answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of send() call must not be null", invocation.getArguments()[0]);
final Long jobID = (Long) invocation.getArguments()[0];
assertEquals("Unexpected jobID given", expectedJobID, jobID.longValue());
return null;
}
}).when(messageListenerMock).onUserCanceledConversion(any(Long.class));
Field field = WebSocketClient.class.getDeclaredField("state");
field.setAccessible(true);
field.set(webSocketClient, Client.State.CONNECTED_AUTHENTICATED);
webSocketClient.onUserCanceledConversion(expectedJobID);
verify(messageListenerMock, times(1)).setBaseMessageHandler(eq(webSocketClient));
verify(messageListenerMock, times(1)).onUserCanceledConversion(any(Long.class));
verifyNoMoreInteractions(messageListenerMock);
}
//------------------------------------------------------------------------------------------------------------------
// Wrapper method tests
//------------------------------------------------------------------------------------------------------------------
public void testIsJobInProgressWrapperCalled() {
final long expectedJobID1 = 1;
final long expectedJobID2 = 2;
doAnswer(new Answer<Boolean>() {
@Override
public Boolean answer(InvocationOnMock invocation) throws Throwable {
assertNotNull("First argument of send() call must not be null", invocation.getArguments()[0]);
final Long jobID = (Long) invocation.getArguments()[0];
if (jobID.longValue() == expectedJobID1) {
return true;
} else if (jobID.longValue() == expectedJobID2) {
return false;
} else {
fail("Unexpected jobID given: " + jobID);
return false;
}
}
}).when(messageListenerMock).isJobInProgress(any(Long.class));
assertTrue("Expecting job to be in progress", webSocketClient.isJobInProgress(expectedJobID1));
assertFalse("Expecting job to be NOT in progress", webSocketClient.isJobInProgress(expectedJobID2));
verify(messageListenerMock, times(1)).setBaseMessageHandler(eq(webSocketClient));
verify(messageListenerMock, times(2)).isJobInProgress(any(Long.class));
verifyNoMoreInteractions(messageListenerMock);
}
public void testGetNumberOfJobsInProgressWrapperCalled() {
final int firstNumberOfJobsResult = 1;
final int secondNumberOfJobsResult = 2;
doAnswer(new Answer<Integer>() {
int counter = 0;
@Override
public Integer answer(InvocationOnMock invocation) throws Throwable {
if (counter++ == 0) {
return firstNumberOfJobsResult;
} else {
return secondNumberOfJobsResult;
}
}
}).when(messageListenerMock).getNumberOfJobsInProgress();
assertEquals("Unexpected number of jobs in progress returned for first call",
webSocketClient.getNumberOfJobsInProgress(), firstNumberOfJobsResult);
assertEquals("Unexpected number of jobs in progress returned for second call",
webSocketClient.getNumberOfJobsInProgress(), secondNumberOfJobsResult);
verify(messageListenerMock, times(1)).setBaseMessageHandler(eq(webSocketClient));
verify(messageListenerMock, times(2)).getNumberOfJobsInProgress();
verifyNoMoreInteractions(messageListenerMock);
}
}