/* Copyright (c) Microsoft Open Technologies, Inc. All Rights Reserved See License.txt in the project root for license information. */ package microsoft.aspnet.signalr.client.test.integration.tests; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.UUID; import java.util.concurrent.Semaphore; import com.google.gson.JsonElement; import com.google.gson.JsonPrimitive; import microsoft.aspnet.signalr.client.Action; import microsoft.aspnet.signalr.client.ConnectionState; import microsoft.aspnet.signalr.client.Credentials; import microsoft.aspnet.signalr.client.ErrorCallback; import microsoft.aspnet.signalr.client.MessageReceivedHandler; import microsoft.aspnet.signalr.client.StateChangedCallback; import microsoft.aspnet.signalr.client.http.Request; import microsoft.aspnet.signalr.client.hubs.HubConnection; import microsoft.aspnet.signalr.client.hubs.HubException; import microsoft.aspnet.signalr.client.hubs.HubProxy; import microsoft.aspnet.signalr.client.test.integration.ApplicationContext; import microsoft.aspnet.signalr.client.test.integration.TransportType; import microsoft.aspnet.signalr.client.test.integration.framework.ExpectedValueException; import microsoft.aspnet.signalr.client.test.integration.framework.TestCase; import microsoft.aspnet.signalr.client.test.integration.framework.TestGroup; import microsoft.aspnet.signalr.client.test.integration.framework.TestResult; import microsoft.aspnet.signalr.client.test.integration.framework.TestStatus; import microsoft.aspnet.signalr.client.test.integration.framework.Util; import microsoft.aspnet.signalr.client.transport.ClientTransport; public class MiscTests extends TestGroup { private static final String INTEGRATION_TESTS_HUB_NAME = "integrationTestsHub"; private TestCase createBasicConnectionFlowTest(String name, final TransportType transportType) { TestCase test = new TestCase() { private InteralTestData testData; @Override public TestResult executeTest() { try { final HubConnection connection = ApplicationContext.createHubConnection(); ClientTransport transport = ApplicationContext.createTransport(transportType); testData = new InteralTestData(); testData.connectionStates.add(connection.getState()); connection.stateChanged(new StateChangedCallback() { @Override public void stateChanged(ConnectionState oldState, ConnectionState newState) { testData.connectionStates.add(newState); } }); connection.received(new MessageReceivedHandler() { @Override public void onMessageReceived(JsonElement json) { testData.receivedMessages.add(json); } }); final Semaphore semaphore = new Semaphore(0); HubProxy proxy = connection.createHubProxy(INTEGRATION_TESTS_HUB_NAME); connection.closed(new Runnable() { @Override public void run() { testData.connectionWasClosed = true; semaphore.release(); } }); proxy.subscribe(new Object() { @SuppressWarnings("unused") public void Echo(String data) { testData.receivedData.add(data); } }); String data = UUID.randomUUID().toString(); connection.start(transport).get(); proxy.setState("myVar", new JsonPrimitive(1)); proxy.invoke("echo", data); proxy.invoke("updateState", "myVar", 2); ApplicationContext.sleep(); connection.stop(); semaphore.acquire(); ApplicationContext.sleep(); TestResult result = new TestResult(); result.setStatus(TestStatus.Passed); result.setTestCase(this); //validations if (!Util.compareArrays( new ConnectionState[] { ConnectionState.Disconnected, ConnectionState.Connecting, ConnectionState.Connected, ConnectionState.Disconnected}, testData.connectionStates.toArray())) { return createResultFromException(new Exception("The connection states were incorrect")); } if (testData.receivedMessages.size() == 0) { return createResultFromException(new Exception("Messages not received")); } if (!testData.connectionWasClosed) { return createResultFromException(new Exception("Conneciton was not closed")); } if (testData.receivedData.size() != 1 || !testData.receivedData.get(0).toString().equals(data)) { return createResultFromException(new Exception("Invalid received data")); } // pending: validate tracing messages return result; } catch (Exception e) { return createResultFromException(e); } } }; test.setName(name); return test; } class InteralTestData { List<ConnectionState> connectionStates = new ArrayList<ConnectionState>(); List<JsonElement> receivedMessages = new ArrayList<JsonElement>(); boolean connectionWasClosed = false; List<Throwable> errors = new ArrayList<Throwable>(); List<Object> receivedData = new ArrayList<Object>(); } private TestCase createErrorHandledAndConnectionContinuesTest(String name, final TransportType transportType) { TestCase test = new TestCase() { InteralTestData testData; @Override public TestResult executeTest() { try { HubConnection connection = ApplicationContext.createHubConnection(); ClientTransport transport = ApplicationContext.createTransport(transportType); testData = new InteralTestData(); connection.received(new MessageReceivedHandler() { @Override public void onMessageReceived(JsonElement json) { testData.receivedMessages.add(json); } }); connection.error(new ErrorCallback() { @Override public void onError(Throwable error) { testData.errors.add(error); } }); HubProxy proxy = connection.createHubProxy(INTEGRATION_TESTS_HUB_NAME); proxy.subscribe(new Object() { @SuppressWarnings("unused") public void echo(String data) { testData.receivedData.add(data); } }); String data = UUID.randomUUID().toString(); connection.start(transport).get(); proxy.invoke("triggerError"); ApplicationContext.sleep(); proxy.invoke("echo", data); ApplicationContext.sleep(); connection.stop(); ApplicationContext.sleep(); TestResult result = new TestResult(); result.setStatus(TestStatus.Passed); result.setTestCase(this); //validations if (testData.receivedMessages.size() == 0) { return createResultFromException(new Exception("Messages expected")); } if (testData.errors.size() != 1 || testData.errors.get(0).getClass() != HubException.class) { return createResultFromException(new Exception("Expected one error")); } if (testData.receivedData.size() != 1 || !testData.receivedData.get(0).toString().equals(data)) { return createResultFromException(new Exception("Invalid received data")); } // pending: validate tracing messages return result; } catch (Exception e) { return createResultFromException(e); } } }; test.setName(name); return test; } private TestCase createMessagesToGroupsTest(String name, final TransportType transportType) { TestCase test = new TestCase() { InteralTestData testData; @Override public TestResult executeTest() { try { HubConnection connection = ApplicationContext.createHubConnection(); ClientTransport transport = ApplicationContext.createTransport(transportType); testData = new InteralTestData(); HubProxy proxy = connection.createHubProxy(INTEGRATION_TESTS_HUB_NAME); proxy.subscribe(new Object() { @SuppressWarnings("unused") public void echo(String data) { testData.receivedData.add(data); } }); connection.start(transport).get(); proxy.invoke("sendMessageToGroup", "group1", "message1").get(); ApplicationContext.sleep(); proxy.invoke("joinGroup", "group1").get(); ApplicationContext.sleep(); proxy.invoke("sendMessageToGroup", "group1", "message2").get(); ApplicationContext.sleep(); proxy.invoke("leaveGroup", "group1").get(); ApplicationContext.sleep(); proxy.invoke("sendMessageToGroup", "group1", "message3").get(); ApplicationContext.sleep(); connection.stop(); ApplicationContext.sleep(); TestResult result = new TestResult(); result.setStatus(TestStatus.Passed); result.setTestCase(this); //validations if (testData.receivedData.size() != 1 || !testData.receivedData.get(0).equals("message2")) { return createResultFromException(new Exception("Expected only one message with value 'message2'")); } return result; } catch (Exception e) { return createResultFromException(e); } } }; test.setName(name); return test; } private TestCase createDisconnectServerTest(String name, final TransportType transportType) { TestCase test = new TestCase() { InteralTestData testData; @Override public TestResult executeTest() { try { HubConnection connection = ApplicationContext.createHubConnection(); ClientTransport transport = ApplicationContext.createTransport(transportType); testData = new InteralTestData(); connection.reconnecting(new Runnable() { @Override public void run() { testData.connectionStates.add(ConnectionState.Reconnecting); } }); connection.start(transport).get(); ApplicationContext.showMessage("Break connection with the server").get(); long current = Calendar.getInstance().getTimeInMillis(); while (Calendar.getInstance().getTimeInMillis() - current < 60 * 1000) { if (connection.getState() == ConnectionState.Disconnected) { break; } } TestResult result = new TestResult(); result.setStatus(TestStatus.Passed); result.setTestCase(this); ApplicationContext.showMessage("Enable connection with the server").get(); //validations if (connection.getState() != ConnectionState.Disconnected) { return createResultFromException(new Exception("Connection should be disconnected")); } if (!testData.connectionStates.contains(ConnectionState.Reconnecting)) { return createResultFromException(new Exception("The client should have tried to reconnect")); } return result; } catch (Exception e) { return createResultFromException(e); } } }; test.setName(name); return test; } private TestCase createReconnectServerTest(String name, final TransportType transportType) { TestCase test = new TestCase() { InteralTestData testData; @Override public TestResult executeTest() { try { HubConnection connection = ApplicationContext.createHubConnection(); ClientTransport transport = ApplicationContext.createTransport(transportType); testData = new InteralTestData(); connection.reconnecting(new Runnable() { @Override public void run() { testData.connectionStates.add(ConnectionState.Reconnecting); } }); connection.start(transport).get(); ApplicationContext.showMessage("Break connection with the server for 10 seconds and re-enable it").get(); ApplicationContext.sleep(10); TestResult result = new TestResult(); result.setStatus(TestStatus.Passed); result.setTestCase(this); //validations if (connection.getState() != ConnectionState.Connected) { return createResultFromException(new Exception("Connection should be connected")); } connection.disconnect(); if (!testData.connectionStates.contains(ConnectionState.Reconnecting)) { return createResultFromException(new Exception("The client should have tried to reconnect")); } return result; } catch (Exception e) { return createResultFromException(e); } } }; test.setName(name); return test; } private TestCase createConnectToUnavailableServerTest(String name, final TransportType transportType) { TestCase test = new TestCase() { InteralTestData testData; @Override public TestResult executeTest() { try { HubConnection connection = ApplicationContext.createHubConnectionWithInvalidURL(); ClientTransport transport = ApplicationContext.createTransport(transportType); testData = new InteralTestData(); connection.start(transport) .onError(new ErrorCallback() { @Override public void onError(Throwable error) { testData.errors.add(error); } }); ApplicationContext.sleep(); TestResult result = new TestResult(); result.setStatus(TestStatus.Passed); result.setTestCase(this); //validations if (connection.getState() != ConnectionState.Disconnected) { return createResultFromException(new Exception("Connection should be disconnected")); } if (testData.errors.size() == 0) { return createResultFromException(new Exception("Exception should have been thrown")); } return result; } catch (Exception e) { return createResultFromException(e); } } }; test.setName(name); return test; } private TestCase createPendingCallbacksAbortedTest(String name, final TransportType transportType) { TestCase test = new TestCase() { InteralTestData testData; @Override public TestResult executeTest() { try { HubConnection connection = ApplicationContext.createHubConnection(); ClientTransport transport = ApplicationContext.createTransport(transportType); testData = new InteralTestData(); HubProxy proxy = connection.createHubProxy(INTEGRATION_TESTS_HUB_NAME); connection.start(transport).get(); proxy.invoke(String.class, "waitAndReturn", 20) .done(new Action<String>() { @Override public void run(String obj) throws Exception { testData.receivedData.add(obj); } }) .onError(new ErrorCallback() { @Override public void onError(Throwable error) { testData.errors.add(error); } }); connection.stop(); ApplicationContext.sleep(15); TestResult result = new TestResult(); result.setStatus(TestStatus.Passed); result.setTestCase(this); //validations if (System.getProperty("java.runtime.name").toLowerCase().contains("android")) { // outside android, java lib might not break the connection soon enough to avoid receiving the message callback if (testData.receivedData.size() != 0) { return createResultFromException(new Exception("No result should have been received")); } } if (testData.errors.size() == 0) { return createResultFromException(new Exception("Exception should have been thrown when connection was closed")); } return result; } catch (Exception e) { return createResultFromException(e); } } }; test.setName(name); return test; } private TestCase createCheckHeaderTest(String name, final TransportType transportType) { TestCase test = new TestCase() { @Override public TestResult executeTest() { try { HubConnection connection = ApplicationContext.createHubConnection(); ClientTransport transport = ApplicationContext.createTransport(transportType); final String headerName = UUID.randomUUID().toString(); final String headerValue = UUID.randomUUID().toString(); connection.setCredentials(new Credentials() { @Override public void prepareRequest(Request request) { request.addHeader(headerName, headerValue); } }); HubProxy proxy = connection.createHubProxy(INTEGRATION_TESTS_HUB_NAME); connection.start(transport).get(); String retValue = proxy.invoke(String.class, "HeaderData", headerName).get(); connection.stop(); TestResult result = new TestResult(); result.setStatus(TestStatus.Passed); result.setTestCase(this); //validations if (!headerValue.equals(retValue)) { return createResultFromException(new ExpectedValueException(headerName, retValue)); } return result; } catch (Exception e) { return createResultFromException(e); } } }; test.setName(name); return test; } public MiscTests() { super("SignalR tests"); for (TransportType transportType : TransportType.values()) { this.addTest(createBasicConnectionFlowTest("Basic connection flow - " + transportType.name(), transportType)); } for (TransportType transportType : TransportType.values()) { this.addTest(createMessagesToGroupsTest("Join and leave groups - " + transportType.name(), transportType)); } for (TransportType transportType : TransportType.values()) { this.addTest(createErrorHandledAndConnectionContinuesTest("Error handled and connection continues - " + transportType.name(), transportType)); } for (TransportType transportType : TransportType.values()) { this.addTest(createDisconnectServerTest("Disconnect server after connection - " + transportType.name(), transportType)); } for (TransportType transportType : TransportType.values()) { this.addTest(createReconnectServerTest("Reconnect server after brief disconnection - " + transportType.name(), transportType)); } for (TransportType transportType : TransportType.values()) { this.addTest(createConnectToUnavailableServerTest("Connecto to unavailable server - " + transportType.name(), transportType)); } for (TransportType transportType : TransportType.values()) { this.addTest(createPendingCallbacksAbortedTest("Pending callbacks aborted - " + transportType.name(), transportType)); } for (TransportType transportType : TransportType.values()) { this.addTest(createCheckHeaderTest("Check headers - " + transportType.name(), transportType)); } } }