/*
* Funambol is a mobile platform developed by Funambol, Inc.
* Copyright (C) 2003 - 2008 Funambol, Inc.
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU Affero General Public License version 3 as published by
* the Free Software Foundation with the addition of the following permission
* added to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED
* WORK IN WHICH THE COPYRIGHT IS OWNED BY FUNAMBOL, FUNAMBOL DISCLAIMS THE
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
*
* 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 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 or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301 USA.
*
* You can contact Funambol, Inc. headquarters at 643 Bair Island Road, Suite
* 305, Redwood City, CA 94063, USA, or at email address info@funambol.com.
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License version 3.
*
* In accordance with Section 7(b) of the GNU Affero General Public License
* version 3, these Appropriate Legal Notices must retain the display of the
* "Powered by Funambol" logo. If the display of the logo is not reasonably
* feasible for technical reasons, the Appropriate Legal Notices must display
* the words "Powered by Funambol".
*/
package com.funambol.push;
import com.funambol.util.ConsoleAppender;
import com.funambol.util.Log;
import java.io.IOException;
import junit.framework.*;
/**
* This tests use a inner class that extends CTPService in order to be used as a
* mock object for the tests.
*/
public class CTPServiceTest extends TestCase implements CTPListener {
CTPServiceTester cst = null;
private boolean fastMode = false;
public CTPServiceTest(String name) {
super(name);
Log.initLog(new ConsoleAppender(), Log.DEBUG);
String mode = System.getProperty("mode");
if (mode != null && "fast".equals(mode)) {
fastMode = true;
}
}
public void setUp() {
cst = new CTPServiceTester();
cst.setCTPListener(this);
}
public void tearDown() {
cst = null;
}
/**
* Test the setConfig of the CTP service
*/
public void testSetConfig() throws Exception {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testSetConfig]");
}
PushConfig pc1 = new PushConfig();
PushConfig pc2 = new PushConfig();
PushConfig pc3 = new PushConfig();
cst.setConfig(pc1);
cst.setConfig(pc2);
cst.setConfig(pc3);
assertTrue(cst.getConfig().equals(pc3));
}
/**
* Test the getConfig of the CTP service
*/
public void testGetConfig() throws Exception {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testGetConfig]");
}
PushConfig pc = new PushConfig();
cst.setConfig(pc);
assertTrue(cst.getConfig().equals(pc));
}
/**
* Test the getInstance of the CTP service
*/
public void testGetInstance() throws Exception {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testGetInstance]");
}
assertTrue(cst instanceof CTPServiceTester);
}
/**
* Test the isRunning of the CTP service
*/
public void testIsRunning() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testIsRunning]");
}
boolean init = cst.isRunning()==false;
cst.startService(new PushConfig());
Thread.sleep(2000);
assertTrue(cst.isRunning()&&init);
cst.stopService();
}
/**
* Test the restartService of the CTP service
*/
public void testRestartService() throws Exception {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testRestartService]");
}
if (fastMode) {
return;
}
cst.restartService(new PushConfig());
Thread.sleep(2000);
assertTrue(cst.state==cst.LISTENING);
cst.stopService();
}
/**
* Test the restartService of the CTP service
*/
public void testRestartServiceNoConnection() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testRestartServiceNoConnection]");
}
PushConfig pc = new PushConfig();
cst.setConfig(pc);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Starting service");
}
cst.startService();
Thread.sleep(2000);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("is running?");
}
assertTrue(cst.isRunning());
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Simulate network loss");
}
cst.simulateNetworkLoss(10000);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Restart the service");
}
cst.restartService(new PushConfig());
// Let it run
Thread.sleep(2000);
assertTrue(cst.state==cst.LISTENING);
cst.stopService();
}
/**
* Test the startService of the CTP service
*/
public void testStartService() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testStartService]");
}
cst.startService(new PushConfig());
try {
// assertion not really useful due to threads usage
Thread.sleep(2000);
} catch (InterruptedException ex) {
}
assertTrue(cst.state==cst.LISTENING);
cst.stopService();
}
/**
* Test the setOfflineMode of the CTP service
*/
public void testSetOfflineMode() throws Exception {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testSetOfflineMode]");
}
if (fastMode) {
return;
}
cst.startService();
cst.setOfflineMode(true);
assertTrue(cst.isOfflineMode());
cst.stopService();
}
/**
* Test the isOfflineMode of the CTP service
*/
public void testIsOfflineMode() throws Exception {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testIsOfflineMode]");
}
cst.setOfflineMode(false);
assertTrue(!cst.isOfflineMode());
}
/**
* Test the isOfflineMode of the CTP service
*/
public void testIsNotOfflineMode() throws Exception {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testIsNotOfflineMode]");
}
cst.setOfflineMode(true);
assertTrue(cst.isOfflineMode());
}
/**
* Test the stopService of the CTP service
*/
public void testStopService() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testStopService]");
}
cst.startService(new PushConfig());
try {
// assertion not really useful due to threads usage
Thread.sleep(2000);
} catch (InterruptedException ex) {
}
cst.stopService();
assertTrue(cst.state==cst.DISCONNECTED);
}
/**
* Test the forceDisconnect of the CTP service
*/
public void testForceDisconnect() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testForceDisconnect]");
}
cst.startService(new PushConfig());
try {
// assertion not really useful due to threads usage
Thread.sleep(2000);
} catch (InterruptedException ex) {
}
cst.stopService();
cst.forceDisconnect();
assertTrue(cst.state==cst.DISCONNECTED);
}
/**
* Test the getCTPStringState method
*/
public void testGetCTPStringState() throws AssertionFailedError {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testGetCTPStringState]");
}
cst.state = cst.DISCONNECTED;
boolean result = "Disconnected".equals(cst.getCTPStringState());
cst.state = cst.CONNECTING;
result = "Connecting...".equals(cst.getCTPStringState());
cst.state = cst.CONNECTED;
result = "Connected".equals(cst.getCTPStringState());
cst.state = cst.AUTHENTICATING;
result = "Authenticating...".equals(cst.getCTPStringState());
cst.state = cst.AUTHENTICATED;
result = "Authenticated".equals(cst.getCTPStringState());
cst.state = cst.LISTENING;
result = "SAN Listening...".equals(cst.getCTPStringState());
assertTrue(result);
}
/**
* Test the isPushActive method
*/
public void testIsPushActive() throws AssertionFailedError {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testIsPushActive]");
}
boolean init = cst.isPushActive()==false;
cst.startService(new PushConfig());
try {
// assertion not really useful due to threads usage
Thread.sleep(2000);
} catch (InterruptedException ex) {
}
boolean result = init && cst.isPushActive();
assertTrue(result);
cst.stopService();
}
public void testRetryPeriod() throws Exception {
if (fastMode) {
return;
}
// Start the service and then disable network. The test
// checks that the service wait an increasing amount of time between
// successive connection attempts.
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testRetryPeriod]");
}
PushConfig pc = new PushConfig();
// Hearbeat rate increased to 2 secs for tests purposes
pc.setCtpReady(2);
cst.startService(pc);
Thread.sleep(5000);
// Wait for six minutes
cst.simulateNetworkLoss(100);
Thread.sleep(8 * 60 * 1000);
// Check if it did not try too many connections
int connAttempt = cst.getConnAttempt();
assertTrue(connAttempt == 5);
cst.stopService();
}
public void testLongLatencyError() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testLongLatencyError]");
}
PushConfig pc = new PushConfig();
// Hearbeat rate increased to 2 secs for tests purposes
pc.setCtpReady(2);
cst.startService(pc);
Thread.sleep(5000);
// Simulate a network error with 20 seconds timeout
cst.simulateNetworkLoss(20 * 1000);
// Wait for one minutes
Thread.sleep(2 * 60 * 1000);
// Check if it did not try too many connections
int connAttempt = cst.getConnAttempt();
assertTrue(connAttempt == 3);
cst.stopService();
}
public void testFailingAuthentication1() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testFailingAuthentication1]");
}
PushConfig pc = new PushConfig();
cst.simulateAuthenticationResult(cst.UNAUTHORIZED);
cst.startService(pc);
Thread.sleep(5000);
// Check if it did not try too many auth attempts
int authAttempt = cst.getAuthAttempt();
assertTrue(authAttempt == 1);
cst.stopService();
}
public void testFailingAuthentication2() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testFailingAuthentication2]");
}
PushConfig pc = new PushConfig();
cst.simulateAuthenticationResult(cst.FORBIDDEN);
cst.startService(pc);
Thread.sleep(5000);
// Check if it did not try too many auth attempts
int authAttempt = cst.getAuthAttempt();
assertTrue(authAttempt == 1);
cst.stopService();
}
public void testShakyConnection1() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("[testShakyConnection1]");
}
PushConfig pc = new PushConfig();
pc.setCtpReady(2);
// Hearbeat rate increased to 2 secs for tests purposes
cst.startService(pc);
// We should be listening here
// TODO how do we check that
for(int i=0;i<5;++i) {
Thread.sleep(2000);
cst.simulateNetworkLoss(100);
Thread.sleep(1000);
cst.restoreNetwork();
}
cst.stopService();
}
public class CTPServiceTester extends CTPService {
private boolean network = true;
private int timeout = 0;
private boolean receiveOk = false;
private int authAttempt = 0;
private int connectionAttempt = 0;
private boolean closed = false;
private int authResult = ST_OK;
public static final int UNAUTHORIZED = 0;
public static final int FORBIDDEN = 1;
private void simulateNetworkError() throws IOException {
try {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Simulating failing IO with timeout: " + timeout);
}
int t = 0;
while (t < timeout) {
Thread.sleep(100);
t += 100;
if (closed) {
closed = false;
throw new IOException("Closed IO channel");
}
}
} catch(InterruptedException ie) {}
throw new IOException("Simulated Network IO Error");
}
/**
* Overrides the connect method in order to giv e mock object on the
* IO stream
*/
protected void connect(int retry) throws IOException {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest]connect");
}
connectionAttempt++;
if (network) {
Thread.yield();
state=CONNECTED;
} else {
simulateNetworkError();
}
}
/**
* Overrides the connect method in order to giv e mock object on the
* IO stream
*/
protected void disconnect() {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest]disconnect");
}
Thread.yield();
state=DISCONNECTED;
}
/**
* Overrides the connect method in order to giv e mock object on the
* IO stream
*/
protected void closeConnection() {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest]closeConnection");
}
closed=true;
Thread.yield();
}
/**
* Overrides the connect method in order to giv e mock object on the
* IO stream
*/
protected CTPMessage receiveMessage() throws IOException {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest]receivemessage");
}
if (network) {
// Wait until we receive the OK
while (!receiveOk) {
Thread.yield();
if (!network || closed) {
closed = false;
simulateNetworkError();
}
}
receiveOk = false;
CTPMessage ctpmsg = new CTPMessage();
ctpmsg.setCommand(ST_OK);
return ctpmsg;
} else {
simulateNetworkError();
return null;
}
}
protected void sendMessage(CTPMessage message) throws IOException {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest]sendmessage");
}
if (network) {
if (message.getCommand() == CM_READY) {
receiveOk = true;
}
Thread.yield();
} else {
simulateNetworkError();
}
}
protected CTPMessage receiveMessageWithTimeout() throws IOException {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest]receivemessagewithTO");
}
if (network) {
Thread.yield();
} else {
simulateNetworkError();
}
return new CTPMessage();
}
public int getAuthAttempt() {
return authAttempt;
}
public int getConnAttempt() {
return connectionAttempt;
}
protected int authenticate() throws IOException {
authAttempt++;
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest]authenticate: " + authAttempt);
}
if (network) {
state = AUTHENTICATED;
Thread.yield();
} else {
simulateNetworkError();
}
return authResult;
}
public void simulateNetworkLoss(int timeout) {
network = false;
this.timeout = timeout;
}
public void restoreNetwork() {
network = true;
}
public void simulateAuthenticationResult(int authResult) {
switch (authResult) {
case UNAUTHORIZED:
this.authResult = ST_UNAUTHORIZED;
break;
case FORBIDDEN:
this.authResult = ST_FORBIDDEN;
break;
default:
this.authResult = authResult;
break;
}
}
}
public void CTPDisconnected() {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest-listener]Disconnected");
}
}
public void CTPConnecting() {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest-listener]Connecting");
}
}
public void CTPConnected() {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest-listener]Connected");
}
}
public void CTPAuthenticating() {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest-listener]Authenticating");
}
}
public void CTPAuthenticated() {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest-listener]Authenticated");
}
}
public void CTPListening() {
if (Log.isLoggable(Log.INFO)) {
Log.info("[CTPServiceTest-listener]Listening");
}
}
}