/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at
* src/com/vodafone360/people/VODAFONE.LICENSE.txt or
* http://github.com/360/360-Engine-for-Android
* See the License for the specific language governing permissions and
* limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each file and
* include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the fields
* enclosed by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2010 Vodafone Sales & Services Ltd. All rights reserved.
* Use is subject to license terms.
*/
package com.vodafone360.people.tests.engine.contactsync;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import android.app.Instrumentation;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.os.MessageQueue;
import android.test.InstrumentationTestCase;
import android.test.suitebuilder.annotation.Suppress;
import android.util.Log;
import com.vodafone360.people.MainApplication;
import com.vodafone360.people.database.DatabaseHelper;
import com.vodafone360.people.datatypes.BaseDataType;
import com.vodafone360.people.datatypes.ContactChanges;
import com.vodafone360.people.engine.IEngineEventCallback;
import com.vodafone360.people.engine.EngineManager.EngineId;
import com.vodafone360.people.engine.contactsync.BaseSyncProcessor;
import com.vodafone360.people.engine.contactsync.ContactSyncEngine;
import com.vodafone360.people.engine.contactsync.IContactSyncCallback;
import com.vodafone360.people.engine.contactsync.ProcessorFactory;
import com.vodafone360.people.engine.contactsync.ContactSyncEngine.IContactSyncObserver;
import com.vodafone360.people.engine.contactsync.ContactSyncEngine.Mode;
import com.vodafone360.people.engine.contactsync.ContactSyncEngine.State;
import com.vodafone360.people.service.ServiceStatus;
import com.vodafone360.people.service.ServiceUiRequest;
import com.vodafone360.people.service.agent.NetworkAgent;
import com.vodafone360.people.service.agent.NetworkAgent.AgentState;
import com.vodafone360.people.service.io.ResponseQueue;
import com.vodafone360.people.service.io.ResponseQueue.DecodedResponse;
import com.vodafone360.people.tests.engine.EngineTestFramework;
import com.vodafone360.people.tests.engine.IEngineTestFrameworkObserver;
public class ContactSyncEngineTest extends InstrumentationTestCase {
private static final String LOG_TAG = "ContactSyncEngineTest";
/**
* The main application handle.
*/
private MainApplication mApplication;
/**
* The engine test framework handle.
*/
private EngineTestFramework mEngineTester;
/**
* The contact sync engine handle.
*/
private ContactSyncEngine mContactSyncEngine;
@Override
protected void setUp() throws Exception {
Log.i(LOG_TAG, "**** setUp() begin ****");
super.setUp();
// delete the database
getInstrumentation().getTargetContext().deleteDatabase(getDatabaseName());
// create an application instance
mApplication = (MainApplication)Instrumentation.newApplication(MainApplication.class,
getInstrumentation().getTargetContext());
mApplication.onCreate();
Log.i(LOG_TAG, "**** setUp() end ****");
}
@Override
protected void tearDown() throws Exception {
Log.i(LOG_TAG, "**** tearDown() begin ****");
mContactSyncEngine.onDestroy();
if (mEngineTester != null)
mEngineTester.stopEventThread();
if (mApplication != null) {
mApplication.onTerminate();
}
mApplication = null;
mEngineTester = null;
mContactSyncEngine = null;
// always call at the end
super.tearDown();
Log.i(LOG_TAG, "**** tearDown() end ****");
}
/**
* Sets up the test framework.
*
* @param factory the factory used by the ContactSyncEngine
* @param observer the test framework observer
*/
private void setUpContactSyncEngineTestFramework(IEngineTestFrameworkObserver observer,
ProcessorFactory factory) {
mEngineTester = new EngineTestFramework(observer);
mContactSyncEngine = new ContactSyncEngine(mApplication.getApplicationContext(),
mEngineTester, mApplication.getDatabase(), factory);
mContactSyncEngine.onCreate();
mEngineTester.setEngine(mContactSyncEngine);
}
/**
* Sets up the ContactSyncEngine without the test framework.
*
* @param eventCallback the engine event callback
* @param factory the factory used by the ContactSyncEngine
*/
private void minimalEngineSetup(IEngineEventCallback eventCallback, ProcessorFactory factory) {
mContactSyncEngine = new ContactSyncEngine(mApplication.getApplicationContext(),
eventCallback, mApplication.getDatabase(), factory);
mContactSyncEngine.onCreate();
}
/**
* Checks that life cycle methods do not crash.
*/
@Suppress
public void testLifecycle() {
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase();
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
return new DummySyncProcessor(mContactSyncEngine, null);
}
};
minimalEngineSetup(engineEventCallback, factory);
mContactSyncEngine.onDestroy();
}
/**
* Verifies that the first time sync can perform correctly with dummy
* replies doing no modifications.
*/
@Suppress
// Breaks tests.
public void testFirstTimeSync_dummyReplies() {
Log.i(LOG_TAG, "**** testFirstTimeSync_dummyReplies ****");
final FirstTimeSyncFrameworkHandler handler = new FirstTimeSyncFrameworkHandler();
setUpContactSyncEngineTestFramework(handler, null);
handler.setContactSyncEngine(mContactSyncEngine);
NetworkAgent.setAgentState(AgentState.CONNECTED);
mContactSyncEngine.addUiStartFullSync();
ServiceStatus status = mEngineTester.waitForEvent();
Log.d(LOG_TAG, "AFTER waitForEvent()");
assertEquals(ServiceStatus.SUCCESS, status);
}
/**
* Verifies that the first time sync triggers a call to the correct
* processors and in the right order.
*/
@Suppress
// Breaks tests.
public void testFirstTimeSync_dummyProcessors() {
// list of the processors in calling order
final ArrayList<Integer> processorTypeList = new ArrayList<Integer>();
// list of expected processors in the right calling order
final ArrayList<Integer> expectedTypeList = new ArrayList<Integer>();
// set the expected processors
expectedTypeList.add(new Integer(ProcessorFactory.DOWNLOAD_SERVER_CONTACTS));
expectedTypeList.add(new Integer(ProcessorFactory.FETCH_NATIVE_CONTACTS));
expectedTypeList.add(new Integer(ProcessorFactory.SYNC_ME_PROFILE));
expectedTypeList.add(new Integer(ProcessorFactory.UPLOAD_SERVER_CONTACTS));
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
Log.i(LOG_TAG, "create(), type=" + type);
processorTypeList.add(new Integer(type));
return new DummySyncProcessor(mContactSyncEngine, null);
}
};
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase();
minimalEngineSetup(engineEventCallback, factory);
NetworkAgent.setAgentState(NetworkAgent.AgentState.CONNECTED);
mContactSyncEngine.addUiStartFullSync();
mContactSyncEngine.run();
// check the processors order
assertTrue(processorTypeList.equals(expectedTypeList));
}
/**
* Verifies that events are fired after UI requests.
*/
@Suppress
// Breaks tests.
public void testUiRequestCompleteEvent_fullSync() {
Log.i(LOG_TAG, "**** testUiRequestCompleteEvent_fullSync() begin ****");
final UiEventCall uiEventCall = new UiEventCall();
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase() {
@Override
public void onUiEvent(ServiceUiRequest event, int request, int status, Object data) {
Log
.i(LOG_TAG, "onUiEvent: " + event + ", " + request + ", " + status + ", "
+ data);
uiEventCall.event = event.ordinal();
uiEventCall.request = request;
uiEventCall.status = status;
uiEventCall.data = data;
}
};
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
Log.i(LOG_TAG, "create(), type=" + type);
return new DummySyncProcessor(mContactSyncEngine, null);
}
};
minimalEngineSetup(engineEventCallback, factory);
NetworkAgent.setAgentState(NetworkAgent.AgentState.CONNECTED);
long nextRuntime = mContactSyncEngine.getNextRunTime();
// should be equal to -1 because first time sync has not been yet
// started
assertEquals(-1, nextRuntime);
// set the connection to be fine
NetworkAgent.setAgentState(AgentState.CONNECTED);
// ask for a full sync
mContactSyncEngine.addUiStartFullSync();
nextRuntime = mContactSyncEngine.getNextRunTime();
assertEquals(0, nextRuntime);
mContactSyncEngine.run();
// check that first time sync is completed
assertEquals(ServiceUiRequest.UI_REQUEST_COMPLETE.ordinal(), uiEventCall.event);
assertEquals(uiEventCall.status, ServiceStatus.SUCCESS.ordinal());
Log.i(LOG_TAG, "**** testUiRequestCompleteEvent_fullSync() end ****");
}
/**
* Verifies that server sync completes correctly.
*/
@Suppress
// Breaks tests.
public void testUiRequestCompleteEvent_serverSync() {
Log.i(LOG_TAG, "**** testUiRequestCompleteEvent_serverSync() begin ****");
final UiEventCall uiEventCall = new UiEventCall();
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase() {
@Override
public void onUiEvent(ServiceUiRequest event, int request, int status, Object data) {
Log
.i(LOG_TAG, "onUiEvent: " + event + ", " + request + ", " + status + ", "
+ data);
uiEventCall.event = event.ordinal();
uiEventCall.request = request;
uiEventCall.status = status;
uiEventCall.data = data;
}
};
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
Log.i(LOG_TAG, "create(), type=" + type);
return new DummySyncProcessor(mContactSyncEngine, null);
}
};
minimalEngineSetup(engineEventCallback, factory);
NetworkAgent.setAgentState(NetworkAgent.AgentState.CONNECTED);
long nextRuntime = mContactSyncEngine.getNextRunTime();
// should be equal to -1 because first time sync has not been yet
// started
assertEquals(-1, nextRuntime);
// set the connection to be fine
NetworkAgent.setAgentState(AgentState.CONNECTED);
// ask for a full sync
mContactSyncEngine.addUiStartFullSync();
nextRuntime = mContactSyncEngine.getNextRunTime();
assertEquals(0, nextRuntime);
mContactSyncEngine.run();
// check that first time sync is completed
assertEquals(ServiceUiRequest.UI_REQUEST_COMPLETE.ordinal(), uiEventCall.event);
assertEquals(uiEventCall.status, ServiceStatus.SUCCESS.ordinal());
// check that first time sync is completed
assertEquals(ServiceUiRequest.UI_REQUEST_COMPLETE.ordinal(), uiEventCall.event);
assertEquals(uiEventCall.status, ServiceStatus.SUCCESS.ordinal());
Log.i(LOG_TAG, "**** testUiRequestCompleteEvent_serverSync() end ****");
}
/**
* Checks that the engine events are correctly sent to listeners.
*/
@Suppress
// Breaks tests.
public void testEventCallback() {
Log.i(LOG_TAG, "**** testEventCallback() begin ****");
// Set up the expected events
final ArrayList<ContactSyncObserver.ContactSyncStateChanged> expectedCssc = new ArrayList<ContactSyncObserver.ContactSyncStateChanged>();
final ArrayList<ContactSyncObserver.ProgressEvent> expectedPe = new ArrayList<ContactSyncObserver.ProgressEvent>();
final ArrayList<ContactSyncObserver.SyncComplete> expectedSc = new ArrayList<ContactSyncObserver.SyncComplete>();
setupExpectedFirstTimeSyncObserverCalls(expectedCssc, expectedPe, expectedSc);
final UiEventCall uiEventCall = new UiEventCall();
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase() {
@Override
public void onUiEvent(ServiceUiRequest event, int request, int status, Object data) {
Log
.i(LOG_TAG, "onUiEvent: " + event + ", " + request + ", " + status + ", "
+ data);
uiEventCall.event = event.ordinal();
uiEventCall.request = request;
uiEventCall.status = status;
uiEventCall.data = data;
}
};
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
Log.i(LOG_TAG, "create(), type=" + type);
return new DummySyncProcessor(mContactSyncEngine, null);
}
};
final ContactSyncObserver observer = new ContactSyncObserver();
minimalEngineSetup(engineEventCallback, factory);
NetworkAgent.setAgentState(NetworkAgent.AgentState.CONNECTED);
// add a listener
mContactSyncEngine.addEventCallback(observer);
// ask for a full sync
mContactSyncEngine.addUiStartFullSync();
// perform the sync
mContactSyncEngine.run();
// compare the retrieved events with the expected ones
assertTrue(expectedCssc.equals(observer.mCsscList));
assertTrue(expectedPe.equals(observer.mPeList));
assertTrue(expectedSc.equals(observer.mScList));
Log.i(LOG_TAG, "**** testEventCallback() end ****");
}
/**
* Checks the the method isFirstTimeSyncComplete() returns the correct
* values.
*/
@Suppress
// Breaks tests.
public void testIsFirstTimeSyncComplete() {
Log.i(LOG_TAG, "**** testIsFirstTimeSyncComplete() begin ****");
final UiEventCall uiEventCall = new UiEventCall();
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase() {
@Override
public void onUiEvent(ServiceUiRequest event, int request, int status, Object data) {
Log
.i(LOG_TAG, "onUiEvent: " + event + ", " + request + ", " + status + ", "
+ data);
uiEventCall.event = event.ordinal();
uiEventCall.request = request;
uiEventCall.status = status;
uiEventCall.data = data;
}
};
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
Log.i(LOG_TAG, "create(), type=" + type);
return new DummySyncProcessor(mContactSyncEngine, null);
}
};
minimalEngineSetup(engineEventCallback, factory);
NetworkAgent.setAgentState(NetworkAgent.AgentState.CONNECTED);
long nextRuntime = mContactSyncEngine.getNextRunTime();
// should be equal to -1 because first time sync has not been yet
// started
assertEquals(-1, nextRuntime);
// set the connection to be fine
NetworkAgent.setAgentState(AgentState.CONNECTED);
// first time sync has not been done yet
assertEquals(false, mContactSyncEngine.isFirstTimeSyncComplete());
// ask for a full sync
mContactSyncEngine.addUiStartFullSync();
nextRuntime = mContactSyncEngine.getNextRunTime();
assertEquals(0, nextRuntime);
// first time sync has still not been done yet
assertEquals(false, mContactSyncEngine.isFirstTimeSyncComplete());
mContactSyncEngine.run();
// check that first time sync is completed
assertEquals(ServiceUiRequest.UI_REQUEST_COMPLETE.ordinal(), uiEventCall.event);
assertEquals(uiEventCall.status, ServiceStatus.SUCCESS.ordinal());
// first time sync has been done
assertEquals(true, mContactSyncEngine.isFirstTimeSyncComplete());
Log.i(LOG_TAG, "**** testIsFirstTimeSyncComplete() end ****");
}
/**
* Checks that nothing is scheduled before the first time sync has been
* completed.
*/
@Suppress
public void testAutoSyncTimer() {
Log.i(LOG_TAG, "**** testAutoSyncTimer() begin ****");
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase() {
@Override
public void onUiEvent(ServiceUiRequest event, int request, int status, Object data) {
Log
.i(LOG_TAG, "onUiEvent: " + event + ", " + request + ", " + status + ", "
+ data);
}
};
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
Log.i(LOG_TAG, "create(), type=" + type);
return new DummySyncProcessor(mContactSyncEngine, null);
}
};
minimalEngineSetup(engineEventCallback, factory);
// set the connection to be fine
NetworkAgent.setAgentState(NetworkAgent.AgentState.CONNECTED);
// should be equal to -1 because first time sync has not been yet
// started
assertEquals(-1, mContactSyncEngine.getNextRunTime());
mContactSyncEngine.run();
assertEquals(-1, mContactSyncEngine.getNextRunTime());
mContactSyncEngine.run();
assertEquals(-1, mContactSyncEngine.getNextRunTime());
Log.i(LOG_TAG, "**** testAutoSyncTimer() end ****");
}
/**
* Checks that background sync is performed after the first time sync.
*/
@Suppress
// Breaks tests.
public void testBackgroundSync() {
Log.i(LOG_TAG, "**** testBackgroundSync() begin ****");
final ArrayList<ProcessorLog> processorLogs = new ArrayList<ProcessorLog>();
final UiEventCall uiEventCall = new UiEventCall();
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase() {
@Override
public void onUiEvent(ServiceUiRequest event, int request, int status, Object data) {
Log
.i(LOG_TAG, "onUiEvent: " + event + ", " + request + ", " + status + ", "
+ data);
uiEventCall.event = event.ordinal();
uiEventCall.request = request;
uiEventCall.status = status;
uiEventCall.data = data;
}
};
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
Log.i(LOG_TAG, "create(), type=" + type);
ProcessorLog log = new ProcessorLog();
log.type = type;
log.time = System.currentTimeMillis();
processorLogs.add(log);
return new DummySyncProcessor(mContactSyncEngine, null);
}
};
minimalEngineSetup(engineEventCallback, factory);
// set the connection to be fine
NetworkAgent.setAgentState(NetworkAgent.AgentState.CONNECTED);
long nextRuntime = mContactSyncEngine.getNextRunTime();
// should be equal to -1 because first time sync has not been yet
// started
assertEquals(-1, mContactSyncEngine.getNextRunTime());
// force a first time sync
mContactSyncEngine.addUiStartFullSync();
nextRuntime = mContactSyncEngine.getNextRunTime();
// next runtime should be now
assertEquals(0, nextRuntime);
// perform the first time sync
mContactSyncEngine.run();
// check that first time sync is completed
assertEquals(ServiceUiRequest.UI_REQUEST_COMPLETE.ordinal(), uiEventCall.event);
assertEquals(uiEventCall.status, ServiceStatus.SUCCESS.ordinal());
// check that a thumbnail sync is scheduled for now
nextRuntime = mContactSyncEngine.getNextRunTime();
assertEquals(0, nextRuntime);
// reset the processor logs
processorLogs.clear();
// get the thumbnail sync to be run
mContactSyncEngine.run();
// check processor calls
ProcessorLog log;
assertEquals(2, processorLogs.size());
log = processorLogs.get(0);
assertEquals(ProcessorFactory.DOWNLOAD_SERVER_THUMBNAILS, log.type);
log = processorLogs.get(1);
assertEquals(ProcessorFactory.UPLOAD_SERVER_THUMBNAILS, log.type);
// check that native sync is scheduled for now
nextRuntime = mContactSyncEngine.getNextRunTime();
assertEquals(0, nextRuntime);
// reset the processor logs
processorLogs.clear();
// get the native sync to be run
mContactSyncEngine.run();
// check processor calls
assertEquals(1, processorLogs.size());
log = processorLogs.get(0);
assertEquals(ProcessorFactory.UPDATE_NATIVE_CONTACTS, log.type);
// check that nothing else is scheduled
nextRuntime = mContactSyncEngine.getNextRunTime();
assertEquals(-1, nextRuntime);
/*
* long startingTime = System.currentTimeMillis(); long duration =
* 60000; long currentTime, timeToWait; while( (currentTime =
* System.currentTimeMillis()) < (startingTime + duration) ) {
* nextRuntime = mContactSyncEngine.getNextRunTime(); timeToWait =
* nextRuntime > currentTime ? (nextRuntime-currentTime) : 0;
* Log.e(LOG_TAG,
* "testBackgroundSyncAfterFirstTimeSync(), timeToWait ="+timeToWait);
* if (timeToWait > 0) { try { synchronized (this) { wait(timeToWait); }
* } catch(Exception e) { Log.e(LOG_TAG,
* "testBackgroundSyncAfterFirstTimeSync(), error while waiting: "+e); }
* } Log.e(LOG_TAG,
* "testBackgroundSyncAfterFirstTimeSync(), calling run()");
* mContactSyncEngine.run(); }
*/
Log.i(LOG_TAG, "**** testBackgroundSync() end ****");
}
/**
* Checks different sync request that come from: -first time sync -fake
* database change event -full sync -server sync
*/
@Suppress
// Breaks tests.
public void testAllSyncs() {
Log.i(LOG_TAG, "**** testAllSyncs() begin ****");
final ArrayList<ProcessorLog> processorLogs = new ArrayList<ProcessorLog>();
final UiEventCall uiEventCall = new UiEventCall();
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase() {
@Override
public void onUiEvent(ServiceUiRequest event, int request, int status, Object data) {
Log
.i(LOG_TAG, "onUiEvent: " + event + ", " + request + ", " + status + ", "
+ data);
uiEventCall.event = event.ordinal();
uiEventCall.request = request;
uiEventCall.status = status;
uiEventCall.data = data;
}
};
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
Log.i(LOG_TAG, "create(), type=" + type);
ProcessorLog log = new ProcessorLog();
log.type = type;
log.time = System.currentTimeMillis();
processorLogs.add(log);
return new DummySyncProcessor(mContactSyncEngine, null);
}
};
minimalEngineSetup(engineEventCallback, factory);
// set the connection to be fine
NetworkAgent.setAgentState(NetworkAgent.AgentState.CONNECTED);
long nextRuntime = mContactSyncEngine.getNextRunTime();
// should be equal to -1 because first time sync has not been yet
// started
assertEquals(-1, mContactSyncEngine.getNextRunTime());
// force a first time sync
mContactSyncEngine.addUiStartFullSync();
nextRuntime = mContactSyncEngine.getNextRunTime();
// next runtime should be now
assertEquals(0, nextRuntime);
// perform the first time sync
mContactSyncEngine.run();
// check that first time sync is completed
assertEquals(ServiceUiRequest.UI_REQUEST_COMPLETE.ordinal(), uiEventCall.event);
assertEquals(uiEventCall.status, ServiceStatus.SUCCESS.ordinal());
// check that a thumbnail sync is scheduled for now
nextRuntime = mContactSyncEngine.getNextRunTime();
assertEquals(0, nextRuntime);
// reset the processor logs
processorLogs.clear();
// get the thumbnail sync to be run
mContactSyncEngine.run();
// check processor calls
ProcessorLog log;
assertEquals(2, processorLogs.size());
log = processorLogs.get(0);
assertEquals(ProcessorFactory.DOWNLOAD_SERVER_THUMBNAILS, log.type);
log = processorLogs.get(1);
assertEquals(ProcessorFactory.UPLOAD_SERVER_THUMBNAILS, log.type);
// check that native sync is scheduled for now
nextRuntime = mContactSyncEngine.getNextRunTime();
assertEquals(0, nextRuntime);
// reset the processor logs
processorLogs.clear();
// get the native sync to be run
mContactSyncEngine.run();
// check processor calls
assertEquals(1, processorLogs.size());
log = processorLogs.get(0);
assertEquals(ProcessorFactory.UPDATE_NATIVE_CONTACTS, log.type);
// reset the processor logs
processorLogs.clear();
// request a full sync
mContactSyncEngine.addUiStartFullSync();
nextRuntime = mContactSyncEngine.getNextRunTime();
// next runtime should be now
assertEquals(0, nextRuntime);
// perform the full sync
mContactSyncEngine.run();
// check that first time sync is completed
assertEquals(ServiceUiRequest.UI_REQUEST_COMPLETE.ordinal(), uiEventCall.event);
assertEquals(uiEventCall.status, ServiceStatus.SUCCESS.ordinal());
// get the thumbnail sync to be run
mContactSyncEngine.run();
// get the native sync to be run
mContactSyncEngine.run();
// get the thumbnail sync to be run
mContactSyncEngine.run();
// check nothing to be done
nextRuntime = mContactSyncEngine.getNextRunTime();
assertEquals(-1, nextRuntime);
// fake a database change notification
Handler handler = getContactSyncEngineHandler(mContactSyncEngine);
Message msg = new Message();
msg.what = ServiceUiRequest.DATABASE_CHANGED_EVENT.ordinal();
msg.arg1 = DatabaseHelper.DatabaseChangeType.CONTACTS.ordinal();
msg.arg2 = 0;
handler.sendMessage(msg);
final Looper looper = Looper.myLooper();
final MessageQueue queue = Looper.myQueue();
queue.addIdleHandler(new MessageQueue.IdleHandler() {
@Override
public boolean queueIdle() {
// message has been processed and the looper is now in idle
// state
// quit the loop() otherwise we would not be able to carry on
looper.quit();
return false;
}
});
// get the message processed by the thread event loop
Looper.loop();
// check sync is scheduled within 30 seconds
nextRuntime = mContactSyncEngine.getNextRunTime();
assertTrue(isValueInsideErrorMargin(30000, nextRuntime - System.currentTimeMillis(), 5));
final long timeBeforeWait = System.currentTimeMillis();
// reset the processor logs
processorLogs.clear();
// call run() and check that nothing is performed
mContactSyncEngine.run();
assertEquals(0, processorLogs.size());
// wait until we get the nextRuntime to now
boolean isNextRuntimeNow = false;
while (!isNextRuntimeNow) {
try {
long timeToWait = mContactSyncEngine.getNextRunTime();
timeToWait = (timeToWait <= 0) ? 0 : timeToWait - System.currentTimeMillis();
if (timeToWait > 0) {
synchronized (this) {
Log.i(LOG_TAG, "timeToWait=" + timeToWait);
wait(timeToWait);
}
}
if (mContactSyncEngine.getNextRunTime() < System.currentTimeMillis()) {
isNextRuntimeNow = true;
}
} catch (Exception e) {
Log.e(LOG_TAG, "testAllSyncs(): error while waiting for the runtime now");
}
}
long timeAfterWait = System.currentTimeMillis();
// check that we have waited about 30 seconds
assertTrue(isValueInsideErrorMargin(timeAfterWait - timeBeforeWait, 30000, 5));
// call run() until the sync is performed
final long startTime = System.currentTimeMillis();
while (processorLogs.size() < 7) {
if (System.currentTimeMillis() - startTime > TEST_TIMEOUT) {
fail("It seems that the engine is stuck, the processor logs should contain 7 objects by now!");
}
mContactSyncEngine.run();
}
// check processor calls
assertEquals(6, processorLogs.size());
log = processorLogs.get(0);
assertEquals(ProcessorFactory.DOWNLOAD_SERVER_CONTACTS, log.type);
log = processorLogs.get(1);
assertEquals(ProcessorFactory.UPLOAD_SERVER_CONTACTS, log.type);
log = processorLogs.get(2);
assertEquals(ProcessorFactory.DOWNLOAD_SERVER_THUMBNAILS, log.type);
log = processorLogs.get(3);
assertEquals(ProcessorFactory.UPLOAD_SERVER_THUMBNAILS, log.type);
log = processorLogs.get(4);
assertEquals(ProcessorFactory.SYNC_ME_PROFILE, log.type);
log = processorLogs.get(5);
assertEquals(ProcessorFactory.UPDATE_NATIVE_CONTACTS, log.type);
Log.i(LOG_TAG, "**** testAllSyncs() end ****");
}
/**
* Tests that the native sync is scheduled and performed after a first time
* sync then a re-instantiation of the ContactSyncEngine.
*/
@Suppress
// Breaks tests.
public void testNativeSync_newEngineInstantiation() {
Log.i(LOG_TAG, "**** testNativeSync_newEngineInstantiation() begin ****");
final ArrayList<ProcessorLog> processorLogs = new ArrayList<ProcessorLog>();
final UiEventCall uiEventCall = new UiEventCall();
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase() {
@Override
public void onUiEvent(ServiceUiRequest event, int request, int status, Object data) {
Log
.i(LOG_TAG, "onUiEvent: " + event + ", " + request + ", " + status + ", "
+ data);
uiEventCall.event = event.ordinal();
uiEventCall.request = request;
uiEventCall.status = status;
uiEventCall.data = data;
}
};
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
Log.i(LOG_TAG, "create(), type=" + type);
ProcessorLog log = new ProcessorLog();
log.type = type;
log.time = System.currentTimeMillis();
processorLogs.add(log);
return new DummySyncProcessor(mContactSyncEngine, null);
}
};
minimalEngineSetup(engineEventCallback, factory);
// set the connection to be fine
NetworkAgent.setAgentState(NetworkAgent.AgentState.CONNECTED);
long nextRuntime = mContactSyncEngine.getNextRunTime();
// should be equal to -1 because first time sync has not been yet
// started
assertEquals(-1, mContactSyncEngine.getNextRunTime());
// force a first time sync
mContactSyncEngine.addUiStartFullSync();
nextRuntime = mContactSyncEngine.getNextRunTime();
// next runtime should be now
assertEquals(0, nextRuntime);
// perform the first time sync
mContactSyncEngine.run();
// check that first time sync is completed
assertEquals(ServiceUiRequest.UI_REQUEST_COMPLETE.ordinal(), uiEventCall.event);
assertEquals(uiEventCall.status, ServiceStatus.SUCCESS.ordinal());
// destroy the engine
mContactSyncEngine.onDestroy();
mContactSyncEngine = null;
// create a new ContactSyncEngine
minimalEngineSetup(engineEventCallback, factory);
processorLogs.clear();
// check sync is scheduled within 30 seconds
nextRuntime = mContactSyncEngine.getNextRunTime();
assertTrue(isValueInsideErrorMargin(30000, nextRuntime - System.currentTimeMillis(), 5));
final long timeBeforeWait = System.currentTimeMillis();
// call run() and check that nothing is performed
mContactSyncEngine.run();
assertEquals(0, processorLogs.size());
// wait until we get the nextRuntime to now
boolean isNextRuntimeNow = false;
while (!isNextRuntimeNow) {
try {
long timeToWait = mContactSyncEngine.getNextRunTime();
timeToWait = (timeToWait <= 0) ? 0 : timeToWait - System.currentTimeMillis();
if (timeToWait > 0) {
synchronized (this) {
Log.i(LOG_TAG, "timeToWait=" + timeToWait);
wait(timeToWait);
}
}
if (mContactSyncEngine.getNextRunTime() < System.currentTimeMillis()) {
isNextRuntimeNow = true;
}
} catch (Exception e) {
Log
.e(LOG_TAG,
"testAllSyncsAfterFirstTimeSync(): error while waiting for the runtime now");
}
}
long timeAfterWait = System.currentTimeMillis();
// check that we have waited about 30 seconds
assertTrue(isValueInsideErrorMargin(timeAfterWait - timeBeforeWait, 30000, 5));
// call run() until the sync is performed
final long startTime = System.currentTimeMillis();
while (processorLogs.size() < 3) {
if (System.currentTimeMillis() - startTime > TEST_TIMEOUT) {
fail("It seems that the engine is stuck, the processor logs should contain 3 objects by now!");
}
mContactSyncEngine.run();
}
// check processor calls
ProcessorLog log;
assertEquals(3, processorLogs.size());
log = processorLogs.get(0);
assertEquals(ProcessorFactory.FETCH_NATIVE_CONTACTS, log.type);
log = processorLogs.get(1);
assertEquals(ProcessorFactory.UPLOAD_SERVER_CONTACTS, log.type);
log = processorLogs.get(2);
assertEquals(ProcessorFactory.UPDATE_NATIVE_CONTACTS, log.type);
Log.i(LOG_TAG, "**** testNativeSync_newEngineInstantiation() end ****");
}
/**
* Tests the sync is cancelled in case we remove user data.
*/
@Suppress
// Breaks tests.
public void testCancelSync() {
Log.i(LOG_TAG, "**** testNativeSync_newEngineInstantiation() begin ****");
final ArrayList<ProcessorLog> processorLogs = new ArrayList<ProcessorLog>();
final UiEventCall uiEventCall = new UiEventCall();
final ProcessorLog processorLog = new ProcessorLog();
final IEngineEventCallback engineEventCallback = new HelperClasses.EngineCallbackBase() {
@Override
public void onUiEvent(ServiceUiRequest event, int request, int status, Object data) {
Log
.i(LOG_TAG, "onUiEvent: " + event + ", " + request + ", " + status + ", "
+ data);
uiEventCall.event = event.ordinal();
uiEventCall.request = request;
uiEventCall.status = status;
uiEventCall.data = data;
}
};
final ProcessorFactory factory = new ProcessorFactory() {
@Override
public BaseSyncProcessor create(int type, IContactSyncCallback callback,
DatabaseHelper dbHelper) {
Log.i(LOG_TAG, "create(), type=" + type);
ProcessorLog log = new ProcessorLog();
log.type = type;
log.time = System.currentTimeMillis();
processorLogs.add(log);
return new BaseSyncProcessor(mContactSyncEngine, null) {
@Override
protected void doCancel() {
// cancel the job
processorLog.type = 1;
}
@Override
protected void doStart() {
// set a "timeout" to be called back immediately
setTimeout(0);
processorLog.type = 2;
}
@Override
public void onTimeoutEvent() {
// set the job as completed
Log.i(LOG_TAG, "onTimeoutEvent()");
complete(ServiceStatus.SUCCESS);
processorLog.type = 3;
}
@Override
public void processCommsResponse(DecodedResponse resp) {
// we don't need this case in this test
processorLog.type = 4;
}
};
}
};
minimalEngineSetup(engineEventCallback, factory);
// set the connection to be fine
NetworkAgent.setAgentState(NetworkAgent.AgentState.CONNECTED);
long nextRuntime = mContactSyncEngine.getNextRunTime();
// should be equal to -1 because first time sync has not been yet
// started
assertEquals(-1, nextRuntime);
// force a first time sync
mContactSyncEngine.addUiStartFullSync();
processorLog.type = 0;
// start performing the sync
mContactSyncEngine.run();
// the first processor should have started
assertTrue(processorLog.type == 2);
// this will cancel any sync
mContactSyncEngine.onReset();
// get the engine to perform a cancel on the current processor
mContactSyncEngine.run();
assertTrue(processorLog.type == 1);
// check that the engine cancelled the sync
assertEquals(ServiceUiRequest.UI_REQUEST_COMPLETE.ordinal(), uiEventCall.event);
assertEquals(uiEventCall.status, ServiceStatus.USER_CANCELLED.ordinal());
}
// //////////////////////////////
// HELPER METHODS AND CLASSES //
// //////////////////////////////
/**
* Timeout value used during loops that may freeze.
*/
private final static long TEST_TIMEOUT = 10000;
/**
* Tells whether or not a value is contained within an acceptable error
* margin.
*
* @return true if below the error margin, false if not
*/
private boolean isValueInsideErrorMargin(long expected, long measured, int errorMargin) {
long margin = expected * errorMargin / 100;
return (Math.abs(expected - measured) < margin);
}
/**
* Gets the database name via reflection feature.
*
* @return the database name
*/
private String getDatabaseName() {
try {
Field dbName = DatabaseHelper.class.getDeclaredField("DATABASE_NAME");
dbName.setAccessible(true);
return (String)dbName.get(null);
} catch (Exception e) {
Log.e(LOG_TAG, "getDatabaseName(), error retrieving the database name... => " + e);
}
return null;
}
/**
* Gets the Handler used by the ContactSyncEngine via reflection feature.
*
* @param engine the ContactSyncEngine
* @return the ContactSyncEngine Handler
*/
private Handler getContactSyncEngineHandler(ContactSyncEngine engine) {
try {
Field handlerField = ContactSyncEngine.class.getDeclaredField("mDbChangeHandler");
handlerField.setAccessible(true);
Handler handler = (Handler)handlerField.get(engine);
return handler;
} catch (Exception e) {
Log.e(LOG_TAG, "getDatabaseName(), error retrieving the database name... => " + e);
}
return null;
}
/**
* Base class for tests using the test framework.
*/
private static class ContactSyncFrameworkBase implements IEngineTestFrameworkObserver {
protected ContactSyncEngine.State mState = ContactSyncEngine.State.IDLE;
protected ContactSyncEngine mEngine;
public void setContactSyncEngine(ContactSyncEngine engine) {
mEngine = engine;
}
@Override
public void onEngineException(Exception exp) {
}
@Override
public void reportBackToEngine(int reqId, EngineId engine) {
}
}
/**
* Handles all the needed responses depending on the engine state.
*/
private static class FirstTimeSyncFrameworkHandler extends ContactSyncFrameworkBase {
public FirstTimeSyncFrameworkHandler() {
mState = ContactSyncEngine.State.FETCHING_SERVER_CONTACTS;
}
@Override
synchronized public void reportBackToEngine(int reqId, EngineId engine) {
Log.d("TAG", "reportBackToEngine(), reqId=" + reqId + ", engine=" + engine);
ResponseQueue respQueue = ResponseQueue.getInstance();
List<BaseDataType> data = new ArrayList<BaseDataType>();
switch (mState) {
case FETCHING_SERVER_CONTACTS:
Log.d(LOG_TAG, "reportBackToEngine(): state=FETCHING_SERVER_CONTACTS");
// Set the next expected state
mState = ContactSyncEngine.State.FETCHING_NATIVE_CONTACTS;
// Give it an empty server contacts list
ContactChanges contactChanges = new ContactChanges();
contactChanges.mNumberOfPages = 0;
contactChanges.mVersionAnchor = 0;
data.add(contactChanges);
respQueue.addToResponseQueue(new DecodedResponse(reqId, data, engine, DecodedResponse.ResponseType.GET_CONTACTCHANGES_RESPONSE.ordinal()));
mEngine.onCommsInMessage();
break;
case FETCHING_NATIVE_CONTACTS:
Log.d(LOG_TAG, "reportBackToEngine(): state=FETCHING_NATIVE_CONTACTS");
// Set the next expected state
mState = ContactSyncEngine.State.UPDATING_SERVER_CONTACTS;
break;
case UPDATING_SERVER_CONTACTS:
ContactChanges serverContactChanges = new ContactChanges();
serverContactChanges.mNumberOfPages = 0;
serverContactChanges.mVersionAnchor = 0;
data.add(serverContactChanges);
respQueue.addToResponseQueue(new DecodedResponse(reqId, data, engine, DecodedResponse.ResponseType.BULKUPDATE_CONTACTS_RESPONSE.ordinal()));
mEngine.onCommsInMessage();
Log.d(LOG_TAG, "reportBackToEngine(): state=UPDATING_SERVER_CONTACTS");
break;
case IDLE:
Log.d(LOG_TAG, "reportBackToEngine(): state=IDLE");
/*
* StatusMsg msg = new StatusMsg(); msg.mStatus = true;
* msg.mCode = "ok"; data.add(msg);
* respQueue.addToResponseQueue(reqId, data, engine);
* mEngine.onCommsInMessage();
*/
break;
default:
Log.d(LOG_TAG, "reportBackToEngine(): state=default");
}
}
}
/**
* Dummy implementation of a Processor used by the ContactSyncEngine.
*/
private static class DummySyncProcessor extends BaseSyncProcessor {
protected DummySyncProcessor(IContactSyncCallback callback, DatabaseHelper db) {
super(callback, db);
}
@Override
protected void doCancel() {
Log.d(LOG_TAG, "doCancel");
}
@Override
protected void doStart() {
Log.d(LOG_TAG, "doStart");
complete(ServiceStatus.SUCCESS);
}
@Override
public void processCommsResponse(DecodedResponse resp) {
Log.d(LOG_TAG, "processCommsResponse");
complete(ServiceStatus.SUCCESS);
}
}
/**
* Class used to log the calls to the different methods of
* IContactSyncObserver.
*/
private static class ContactSyncObserver implements IContactSyncObserver {
public static class ContactSyncStateChanged {
Mode mode;
State oldState;
State newState;
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof ContactSyncStateChanged))
return false;
final ContactSyncStateChanged oContactSyncStateChanged = (ContactSyncStateChanged)o;
return (oContactSyncStateChanged.mode == mode)
&& (oContactSyncStateChanged.oldState == oldState)
&& (oContactSyncStateChanged.newState == newState);
}
}
public static class ProgressEvent {
State currentState;
int percent;
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof ProgressEvent))
return false;
final ProgressEvent oProgressEvent = (ProgressEvent)o;
return (oProgressEvent.currentState == currentState)
&& (oProgressEvent.percent == percent);
}
}
public static class SyncComplete {
ServiceStatus status;
@Override
public boolean equals(Object o) {
if (o == null || !(o instanceof SyncComplete))
return false;
final SyncComplete oSyncComplete = (SyncComplete)o;
return oSyncComplete.status == status;
}
}
public ArrayList<ContactSyncStateChanged> mCsscList = new ArrayList<ContactSyncStateChanged>();
public ArrayList<ProgressEvent> mPeList = new ArrayList<ProgressEvent>();
public ArrayList<SyncComplete> mScList = new ArrayList<SyncComplete>();
/**
* Resets all the logs.
*/
public void reset() {
mCsscList.clear();
mPeList.clear();
mScList.clear();
}
@Override
public void onContactSyncStateChange(Mode mode, State oldState, State newState) {
Log.d(LOG_TAG, "onContactSyncStateChange(" + mode + ", " + oldState + ", " + newState
+ ")");
ContactSyncStateChanged cssc = new ContactSyncStateChanged();
cssc.mode = mode;
cssc.oldState = oldState;
cssc.newState = newState;
mCsscList.add(cssc);
}
@Override
public void onProgressEvent(State currentState, int percent) {
Log.d(LOG_TAG, "onProgressEvent(" + currentState + ", " + percent + ")");
ProgressEvent pe = new ProgressEvent();
pe.currentState = currentState;
pe.percent = percent;
mPeList.add(pe);
}
@Override
public void onSyncComplete(ServiceStatus status) {
Log.d(LOG_TAG, "onSyncComplete(" + status + ")");
SyncComplete sc = new SyncComplete();
sc.status = status;
mScList.add(sc);
}
}
/**
* Sets the expected calls during a first time sync to the provided lists.
*
* @param cssc the list of expected calls to onContactSyncStateChanged
* @param pe the list of expected calls to onProgressEvent
* @param sc the list of expected calls to onSyncComplete
*/
private void setupExpectedFirstTimeSyncObserverCalls(
ArrayList<ContactSyncObserver.ContactSyncStateChanged> csscList,
ArrayList<ContactSyncObserver.ProgressEvent> peList,
ArrayList<ContactSyncObserver.SyncComplete> scList) {
// setting up expected ContactSyncStateChanged
ContactSyncObserver.ContactSyncStateChanged cssc = new ContactSyncObserver.ContactSyncStateChanged();
cssc.mode = Mode.FULL_SYNC_FIRST_TIME;
cssc.oldState = State.IDLE;
cssc.newState = State.FETCHING_SERVER_CONTACTS;
csscList.add(cssc);
cssc = new ContactSyncObserver.ContactSyncStateChanged();
cssc.mode = Mode.FULL_SYNC_FIRST_TIME;
cssc.oldState = State.FETCHING_SERVER_CONTACTS;
cssc.newState = State.FETCHING_NATIVE_CONTACTS;
csscList.add(cssc);
cssc = new ContactSyncObserver.ContactSyncStateChanged();
cssc.mode = Mode.FULL_SYNC_FIRST_TIME;
cssc.oldState = State.FETCHING_NATIVE_CONTACTS;
cssc.newState = State.UPDATING_SERVER_CONTACTS;
csscList.add(cssc);
cssc = new ContactSyncObserver.ContactSyncStateChanged();
cssc.mode = Mode.FULL_SYNC_FIRST_TIME;
cssc.oldState = State.UPDATING_SERVER_CONTACTS;
cssc.newState = State.IDLE;
csscList.add(cssc);
// setting up expected ProgressEvent
ContactSyncObserver.ProgressEvent pe = new ContactSyncObserver.ProgressEvent();
pe = new ContactSyncObserver.ProgressEvent();
pe.currentState = State.FETCHING_SERVER_CONTACTS;
pe.percent = 0;
peList.add(pe);
pe = new ContactSyncObserver.ProgressEvent();
pe.currentState = State.FETCHING_SERVER_CONTACTS;
pe.percent = 100;
peList.add(pe);
pe = new ContactSyncObserver.ProgressEvent();
pe.currentState = State.FETCHING_NATIVE_CONTACTS;
pe.percent = 0;
peList.add(pe);
pe = new ContactSyncObserver.ProgressEvent();
pe.currentState = State.FETCHING_NATIVE_CONTACTS;
pe.percent = 100;
peList.add(pe);
pe = new ContactSyncObserver.ProgressEvent();
pe.currentState = State.UPDATING_SERVER_CONTACTS;
pe.percent = 0;
peList.add(pe);
pe = new ContactSyncObserver.ProgressEvent();
pe.currentState = State.UPDATING_SERVER_CONTACTS;
pe.percent = 100;
peList.add(pe);
// setting up expected SyncComplete
ContactSyncObserver.SyncComplete sc = new ContactSyncObserver.SyncComplete();
sc.status = ServiceStatus.SUCCESS;
scList.add(sc);
}
/**
* Class holding parameters from a UI event.
*/
private static class UiEventCall {
int event;
int request;
int status;
Object data;
/**
* Resets the values from the last UI event.
*/
public void reset() {
event = -1;
request = -1;
status = -1;
data = null;
}
}
/**
* Class holding a started processor log.
*/
private static class ProcessorLog {
/**
* The processor type.
*/
int type;
/**
* The time when the processor was started.
*/
long time;
}
}