/*
* 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.client.push;
import com.funambol.util.ConsoleAppender;
import com.funambol.util.Log;
import junit.framework.*;
/**
* This is a test for the SyncScheduler class.
*/
public class SyncSchedulerTest extends TestCase {
//Change the following if you want to speed up the tests
public static final long ONE_SECOND = 100; // in milliseconds
private final long ONE_MINUTE = ONE_SECOND*60;
//Sync wait delay
private final long WAIT_DELAY = ONE_SECOND/10;
private final long[] RETRY_DELAY = {ONE_MINUTE,
ONE_MINUTE*5,
ONE_MINUTE*15};
//Specify the tolerable delay of a sync start
private final long TOLERATED_DELAY = ONE_SECOND*30;
private final long UI_CALLER_INTERVAL = 0;
private final long LISTENER_CALLER_INTERVAL = ONE_MINUTE;
private final long SCHEDULER_CALLER_INTERVAL = 0;
private final int CONTACT_SS = 0;
private final int EVENT_SS = 1;
private final int TASK_SS = 2;
private final int NOTE_SS = 3;
private final int PHOTO_SS = 4;
private SyncScheduler scheduler;
private TestSyncSchedulerListener slistener;
private TestSyncRequest request;
private boolean assertingSync = false;
private boolean fastMode = false;
/** Creates a new instance of SyncSchedulerTest */
public SyncSchedulerTest(String name) {
super(name);
Log.initLog(new ConsoleAppender(), Log.TRACE);
String mode = System.getProperty("mode");
if (mode != null && "fast".equals(mode)) {
fastMode = true;
}
}
public void setUp() {
slistener = new TestSyncSchedulerListener(5*ONE_SECOND);
scheduler = new SyncScheduler(slistener);
}
public void tearDown() {
scheduler = null;
}
/** Test a single SyncRequest */
public void testSingleRequest(Object item, long interval) throws Exception {
Log.debug("==== Running testSingleRequest test. Interval="
+ interval + " ====");
Object[] content = {item};
// setting up a sync request
request = new TestSyncRequest(content[0], interval);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
assertSync(content, interval);
}
/** Test multiple SyncRequests of the same type, from the same caller */
public void testMultipleRequests(Object item, int num, long delay, long interval)
throws Exception {
Log.debug("==== Running testMultipleRequests test. Interval="
+ interval + " ====");
Object[] content = {item};
// setting up a sync request
request = new TestSyncRequest(content[0], interval);
for(int i=0; i<num; i++) {
Thread.sleep(delay);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
}
assertSync(content, interval);
}
/** Test two delayed (e.g. delay > 5min) SyncRequests */
public void testLongDelayedRequests(Object item, long delay, long interval)
throws Exception {
if (fastMode) {
return;
}
Log.debug("==== Running testLongDelayedRequests test. Interval="
+ interval + " ====");
Object[] content = {item};
// setting up a sync request
request = new TestSyncRequest(content[0], interval);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
assertSyncWithThread(content, interval);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Waiting " + delay/1000 + " seconds...");
}
Thread.sleep(delay);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
assertSync(content, interval);
}
/** Test two short delayed (e.g. delay = 30 sec) SyncRequests */
public void testShortDelayedRequests(Object item, long delay, long interval)
throws Exception {
if (fastMode) {
return;
}
Log.debug("==== Running testShortDelayedRequests test. Interval="
+ interval + " ====");
Object[] content = {item};
// setting up a sync request
request = new TestSyncRequest(content[0], interval);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
Thread.sleep(delay);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
assertSync(content, interval);
}
/**
* Test mixed SyncRequests case #1
* - test a sync request of all the sources from UI
*/
public void testMixedRequests1() throws Exception {
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("==== Running testMixedRequests1 test ====");
}
// set a SyncSchedulerListener with 0 failures and 5 seconds duration
Object[] content = {getTestSyncSource(CONTACT_SS),
getTestSyncSource(EVENT_SS),
getTestSyncSource(TASK_SS),
getTestSyncSource(NOTE_SS),
getTestSyncSource(PHOTO_SS)};
request = new TestSyncRequest(content[0], UI_CALLER_INTERVAL);
for(int i=1; i<content.length; i++) {
request.addRequestContent(content[i]);
}
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
assertSync(content, UI_CALLER_INTERVAL);
}
/**
* Test mixed SyncRequests case #2
* - 5 mergeable sync requests from listeners (3 contacts, 2 notes)
* with 1 second delay.
*/
public void testMixedRequests2() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("==== Running testMixedRequests2 test ====");
}
Object contact_ss = getTestSyncSource(CONTACT_SS);
Object note_ss = getTestSyncSource(NOTE_SS);
slistener.setDuration(ONE_SECOND);
Object[] content = {contact_ss,
contact_ss,
contact_ss,
note_ss,
note_ss};
Object[] expcontent = {contact_ss, note_ss};
for(int i=0; i<content.length; i++) {
// setting up a sync request
//
request = new TestSyncRequest(content[i], LISTENER_CALLER_INTERVAL);
Thread.sleep(ONE_SECOND);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
}
assertSync(expcontent, LISTENER_CALLER_INTERVAL );
}
/**
* Test mixed SyncRequests case #3
* - 3 mergeable sync requests of contacts
* - 10min delay
* - 2 mergeable sync requests of notes
*/
public void testMixedRequests3() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("==== Running testMixedRequests3 test ====");
}
Object contact_ss = getTestSyncSource(CONTACT_SS);
Object note_ss = getTestSyncSource(NOTE_SS);
Object[] content1 = {contact_ss,
contact_ss,
contact_ss};
Object[] expcontent1 = {contact_ss};
Object[] content2 = {note_ss,
note_ss};
Object[] expcontent2 = {note_ss};
for(int i=0; i<content1.length; i++) {
// setting up a sync request
request = new TestSyncRequest(content1[i], LISTENER_CALLER_INTERVAL);
Thread.sleep(ONE_SECOND);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
}
assertSyncWithThread(expcontent1, LISTENER_CALLER_INTERVAL);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Waiting " + ONE_MINUTE*2/1000 + " seconds...");
}
Thread.sleep(ONE_MINUTE*2);
for(int i=0; i<content2.length; i++) {
// setting up a sync request
request = new TestSyncRequest(content2[i], LISTENER_CALLER_INTERVAL);
Thread.sleep(ONE_SECOND);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
}
assertSync(expcontent2, LISTENER_CALLER_INTERVAL);
}
/**
* Test mixed SyncRequests case #4
* - 3 mergeable sync requests of contacts
* - 60sec delay
* - 1 (mergeable) sync request of event
* - 1 (mergeable) sync request of photo
*/
public void testMixedRequests4() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("==== Running testMixedRequests4 test ====");
}
Object contact_ss = getTestSyncSource(CONTACT_SS);
Object event_ss = getTestSyncSource(EVENT_SS);
Object[] content1 = {contact_ss,
contact_ss,
contact_ss};
Object[] content2 = {event_ss};
Object[] expcontentNotMergeable1 = {contact_ss};
Object[] expcontentNotMergeable2 = {event_ss};
for(int i=0; i<content1.length; i++) {
// setting up a sync request
request = new TestSyncRequest(content1[i], LISTENER_CALLER_INTERVAL);
Thread.sleep(ONE_SECOND);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
}
assertSyncWithThread(expcontentNotMergeable1, LISTENER_CALLER_INTERVAL);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Waiting " + ONE_SECOND*60/1000 + " seconds...");
}
Thread.sleep(ONE_SECOND*60);
for(int i=0; i<content2.length; i++) {
// setting up a sync request
request = new TestSyncRequest(content2[i], LISTENER_CALLER_INTERVAL);
Thread.sleep(ONE_SECOND);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
}
assertSync(expcontentNotMergeable2, LISTENER_CALLER_INTERVAL);
}
/**
* Test mixed SyncRequests case #5
* - 1 sync request of contacts
* - 30sec delay
* - sync all sources from UI
*/
public void testMixedRequests5() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("==== Running testMixedRequests5 test ====");
}
Object contact_ss = getTestSyncSource(CONTACT_SS);
Object[] content1 = {contact_ss};
Object[] content2 = {contact_ss,
getTestSyncSource(EVENT_SS),
getTestSyncSource(TASK_SS),
getTestSyncSource(NOTE_SS),
getTestSyncSource(PHOTO_SS)};
request = new TestSyncRequest(content1[0], LISTENER_CALLER_INTERVAL);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Waiting " + ONE_SECOND*3/100 + " seconds...");
}
Thread.sleep(ONE_SECOND*30);
request = new TestSyncRequest(content2[0], UI_CALLER_INTERVAL);
for(int i=1; i<content2.length; i++) {
request.addRequestContent(content2[i]);
}
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
assertSync(content2, UI_CALLER_INTERVAL);
}
/**
* Test mixed SyncRequests case #6
* - 1 sync request of contacts (sync after 1 minute delay)
* - 10sec delay
* - 1 not mergeable sync request of events (sync after 2 minutes delay)
*/
public void testMixedRequests6() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("==== Running testMixedRequests6 test ====");
}
Object contact_ss = getTestSyncSource(CONTACT_SS);
Object event_ss = getTestSyncSource(EVENT_SS);
Object[] content = {contact_ss, event_ss};
request = new TestSyncRequest(content[0], ONE_MINUTE*1);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Waiting " + ONE_SECOND/100 + " seconds...");
}
Thread.sleep(ONE_SECOND*10);
request = new TestSyncRequest(content[1], ONE_MINUTE*2);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
assertSync(content, ONE_MINUTE*2);
}
/**
* Test conflict SyncRequests case #1
* - mergeable sync requests of events/tasks from UI (2min duration)
* - 30sec delay
* - mergaeble sync request of contacts from listener (start after 1min)
* - 1min delay
* - contacts sync from listener fails
* - retry after 5min ok
*/
public void testConflictRequests1() throws Exception {
if (fastMode) {
return;
}
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("==== Running testConflictRequests1 test ====");
}
slistener.setDuration(5*ONE_SECOND);
Object[] uiContent = {getTestSyncSource(EVENT_SS),
getTestSyncSource(TASK_SS)};
Object[] listenerContent = {getTestSyncSource(CONTACT_SS)};
// setting up a sync request
request = new TestSyncRequest(uiContent[0], UI_CALLER_INTERVAL);
request.addRequestContent(uiContent[1]);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
assertSyncWithThread(uiContent, UI_CALLER_INTERVAL);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Waiting " + ONE_SECOND*3/100 + " seconds...");
}
Thread.sleep(ONE_SECOND*30);
slistener.setDuration(5*ONE_SECOND);
for(int i=0; i<listenerContent.length; i++) {
// setting up a sync request
request = new TestSyncRequest(listenerContent[i], LISTENER_CALLER_INTERVAL);
Thread.sleep(ONE_SECOND);
scheduler.addRequest(request);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Sync Request added");
}
}
assertSync(listenerContent, LISTENER_CALLER_INTERVAL);
}
/**
* It's expected that the SyncSchedulerListener syncs the expected content
* within the expected delay.
*/
private void assertSync(Object[] expectedContent, long expectedDelay)
throws Exception {
if (fastMode) {
return;
}
long startTime = System.currentTimeMillis();
// wait untill another assertSync is running
while(assertingSync) {
try {
Thread.sleep(WAIT_DELAY);
} catch(Exception e) {
Log.error("Exception during thread sleep: " + e.toString());
}
}
assertingSync = true;
// wait untill the sync starts
while(!slistener.getSyncStarted()) {
try {
Thread.sleep(WAIT_DELAY);
} catch(Exception e) {
Log.error("Exception during thread sleep: " + e.toString());
}
}
// syncdelay represents the delay between the sync request and the effective
// sync start
long syncDelay = System.currentTimeMillis() - startTime;
// verify the sync delay
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Verifying the sync delay=" + syncDelay +" (" + expectedDelay + " is expected)");
}
assertTrue((syncDelay >= expectedDelay - TOLERATED_DELAY) &&
(syncDelay <= expectedDelay + TOLERATED_DELAY));
// wait untill the sync ends
while(!slistener.getSyncEnded()) {
try {
Thread.sleep(WAIT_DELAY);
} catch(Exception e) {
Log.error("Exception during thread sleep: " + e.toString());
}
}
// check if the synced contents are the same of what is expected
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Verifying the synced content");
}
Object[] syncedContent = slistener.getSyncedContent();
int verifiedContentCount = 0;
assertEquals(expectedContent.length, syncedContent.length);
for(int i=0; i<expectedContent.length; i++) {
for(int j=0; j<syncedContent.length; j++) {
if(expectedContent[i].equals(syncedContent[j])) {
verifiedContentCount++;
break;
}
}
}
assertEquals(expectedContent.length, verifiedContentCount);
if (Log.isLoggable(Log.DEBUG)) {
Log.debug("Synced content verified.");
}
assertingSync = false;
}
/** Start a new Thread for the assertSync check */
private void assertSyncWithThread(final Object[] expectedContent,
final long expectedDelay) throws Exception {
Thread t = new Thread() {
public void run() {
try {
assertSync(expectedContent, expectedDelay );
} catch (Exception e) {
}
}
};
t.start();
}
/** Get a test SyncSource */
private String getTestSyncSource(int ssid) {
String res = "SyncSource_" + ssid;
return res;
}
}