content: package com.vodafone360.people.tests.engine.contactsync;
  
  import java.util.ArrayList;
  import java.util.List;
  
  import android.test.AndroidTestCase;
  import android.test.suitebuilder.annotation.Suppress;
  import android.text.format.Time;
  
  import com.vodafone360.people.database.DatabaseHelper;
  import com.vodafone360.people.database.tables.ContactDetailsTable;
  import com.vodafone360.people.datatypes.Contact;
  import com.vodafone360.people.datatypes.ContactDetail;
  import com.vodafone360.people.datatypes.VCardHelper;
  import com.vodafone360.people.datatypes.VCardHelper.Name;
  import com.vodafone360.people.datatypes.VCardHelper.Organisation;
  import com.vodafone360.people.datatypes.VCardHelper.PostalAddress;
  import com.vodafone360.people.engine.BaseEngine;
  import com.vodafone360.people.engine.contactsync.ContactChange;
  import com.vodafone360.people.engine.contactsync.IContactSyncCallback;
  import com.vodafone360.people.engine.contactsync.NativeContactsApi;
  import com.vodafone360.people.engine.contactsync.SyncStatus;
  import com.vodafone360.people.engine.contactsync.UpdateNativeContacts;
  import com.vodafone360.people.engine.contactsync.NativeContactsApi.Account;
  import com.vodafone360.people.service.ServiceStatus;
  import com.vodafone360.people.tests.TestModule;
  import com.vodafone360.people.utils.VersionUtils;
  
  /**
   * JUnit tests for the UpdateNativeContacts processor.
   */
  @Suppress
  public class UpdateNativeContactsTest  extends AndroidTestCase {
      
      /**
       * The people database name.
       */
      private final String DATABASE_NAME = "peopletest.db";
      
      /**
       * The DatabaseHelper used to create the people database.
       */
      private DatabaseHelper mDatabaseHelper = null;
      
      /**
       * The 360 People account type.
       */
      private final static String PEOPLE_ACCOUNT_TYPE = "com.vodafone360.people.android.account";
      
      /**
       * A test People account.
       */
      private final static Account PEOPLE_ACCOUNT = new Account("mypeoplelogin", PEOPLE_ACCOUNT_TYPE);
      
      /**
       * A ContactSyncCallback implementation.
       */
      private ContactSyncCallback mContactSyncCallback;
      
      /**
       * The TestModule used to generate contacts.
       */
      private final TestModule mTestModule = new TestModule();
      
      @Override
      protected void setUp() throws Exception {
  
          super.setUp();
          createDatabase();
          NativeContactsApi.createInstance(getContext());
          if(VersionUtils.is2XPlatform()) {
              // Add Account for the case where we are in 2.X
              NativeContactsApi.getInstance().addPeopleAccount(PEOPLE_ACCOUNT.getName());
              Thread.sleep(100);
          }
          mContactSyncCallback = new ContactSyncCallback();
      }
  
      @Override
      protected void tearDown() throws Exception {
          
          clearDatabase(mDatabaseHelper);
          mDatabaseHelper = null;
          NativeContactsApiTestHelper.getInstance(getContext()).wipeNab();
          mContactSyncCallback = null;
          
          super.tearDown();
      }
      
      /**
       * Creates the database with all the tables.
       */
      private void createDatabase() {
  
          mDatabaseHelper = new DatabaseHelper(getContext(), DATABASE_NAME);
      }
      
      /**
       * Clears the people database.
       */
      private void clearDatabase(DatabaseHelper dbh) {
  
          dbh.close();
          getContext().deleteDatabase(DATABASE_NAME);
      }
      
      /**
       * Tests the instantiation of the UpdateNativeContacts class.
       */
      public void testConstructor() {
          
          UpdateNativeContacts processor = new UpdateNativeContacts(mContactSyncCallback, mDatabaseHelper); 
      }
      
      /**
       * Tests running the processor with an empty database.
       */
      public void testRunWithEmptyDatabase() {
          
          UpdateNativeContacts processor = new UpdateNativeContacts(mContactSyncCallback, mDatabaseHelper);
          processor.start();
          assertEquals(1, mContactSyncCallback.mProcessorComplete.size());
          ContactSyncCallback.ProcessorComplete pc = mContactSyncCallback.mProcessorComplete.get(0);
          assertEquals(ServiceStatus.SUCCESS, pc.status);
      }
  
      /**
       * Tests the export of new syncable contacts.
       */
      public void testExportingNewContacts() {
          
          final int CONTACTS_COUNT = 10;
          
          UpdateNativeContacts processor = new UpdateNativeContacts(mContactSyncCallback, mDatabaseHelper);
          
          // check that there are no contacts to sync
          long[] syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(null, syncableIds);
          
          // put some contacts that need to be synchronized to native side
          feedSyncableContactsInDatabase(CONTACTS_COUNT);
  
          // check the count of the contacts to sync
          syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(CONTACTS_COUNT, syncableIds.length);
          
          // run the UpdateNativeContacts processor until it finishes or it times out
          runUpdateNativeContactsProcessor(processor);
          
          // check the contacts count on native side
          final NativeContactsApi nca = NativeContactsApi.getInstance();
          long[] ids = null;
          if (VersionUtils.is2XPlatform()) {
              
              ids = nca.getContactIds(PEOPLE_ACCOUNT);
          } else {
              ids = nca.getContactIds(null);
          }
          assertEquals(CONTACTS_COUNT, ids.length);
          
          // check that no more contact is syncable as the export is done
          syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(null, syncableIds);
      }
      
      /**
       * Tests the export of new syncable contacts.
       */
      @Suppress
      public void testExportingDeletedContacts() {
          
          final int CONTACTS_COUNT = 30;
          
          UpdateNativeContacts processor = new UpdateNativeContacts(mContactSyncCallback, mDatabaseHelper);
          
          // check that there are no contacts to sync
          long[] syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(null, syncableIds);
          
          // put some contacts that need to be synchronized to native side
          feedSyncableContactsInDatabase(CONTACTS_COUNT);
  
          // check the count of the contacts to sync
          syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(CONTACTS_COUNT, syncableIds.length);
          
          // run the UpdateNativeContacts processor until it finishes or it times out
          runUpdateNativeContactsProcessor(processor);
          
          // check the contacts count on native side
          final NativeContactsApi nca = NativeContactsApi.getInstance();
          long[] ids = null;
          if (VersionUtils.is2XPlatform()) {
              
              ids = nca.getContactIds(PEOPLE_ACCOUNT);
          } else {
              ids = nca.getContactIds(null);
          }
          assertEquals(CONTACTS_COUNT, ids.length);
          
          // check that no more contact is syncable as the export is done
          syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(null, syncableIds);
          
          // delete 6 of the exported contacts
          mDatabaseHelper.deleteContact(1);
          mDatabaseHelper.deleteContact(3);
          mDatabaseHelper.deleteContact(5);
          mDatabaseHelper.deleteContact(15);
          mDatabaseHelper.deleteContact(20);
          mDatabaseHelper.deleteContact(26);
          
          // check the count of the contacts to sync
          syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(6, syncableIds.length);
          
          // run the UpdateNativeContacts processor until it finishes or it times out
          processor = new UpdateNativeContacts(mContactSyncCallback, mDatabaseHelper);
          mContactSyncCallback.mProcessorComplete.clear();
          runUpdateNativeContactsProcessor(processor);
          
          // check the contacts count on native side
          if (VersionUtils.is2XPlatform()) {
              
              ids = nca.getContactIds(PEOPLE_ACCOUNT);
          } else {
              ids = nca.getContactIds(null);
          }
          assertEquals(CONTACTS_COUNT-6, ids.length);
          
          // check that no more contact is syncable as the export is done
          syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(null, syncableIds);
      }
      
      /**
       * Tests the export of a new syncable contact with all the possible details combinations.
       */
      public void testExportingContactAllDetails() {
          
          UpdateNativeContacts processor = new UpdateNativeContacts(mContactSyncCallback, mDatabaseHelper);
          
          // check that there are no contacts to sync
          long[] syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(null, syncableIds);
          
          // put a contact that need to be synchronized to native side and that contains
          // all the possible details that can be synchronized
          final Contact contact = feedOneSyncableContact();
  
          // check the count of the contacts to sync
          syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(1, syncableIds.length);
          
          // run the UpdateNativeContacts processor until it finishes or it times out
          runUpdateNativeContactsProcessor(processor);
          
          // check that the contact on native side is equivalent
          final NativeContactsApi nca = NativeContactsApi.getInstance();
          long[] ids = null;
          if (VersionUtils.is2XPlatform()) {
              
              ids = nca.getContactIds(PEOPLE_ACCOUNT);
          } else {
              ids = nca.getContactIds(null);
          }
          assertEquals(1, ids.length);
          
          // check that no more contact is syncable as the export is done
          syncableIds = mDatabaseHelper.getNativeSyncableContactsLocalIds();
          assertEquals(null, syncableIds);
          
          final ContactChange[] contactChanges = nca.getContact(ids[0]);
          
          assertTrue(compareContactWithContactChange(contact, contactChanges));
      }
      
      /**
       * Compares a Contact details with an array of ContactChange.
       * 
       * @param contact the Contact to compare
       * @param contactChange the array of ContactChange to compare
       * @return true if the Contact is equivalent to the array of ContactChange, false otherwise
       */
      private boolean compareContactWithContactChange(Contact contact, ContactChange[] contactChanges) {
          
          final NativeContactsApi nca = NativeContactsApi.getInstance();
          final List<ContactDetail> details = contact.details;
          
          for (int i = 0; i < details.size(); i++) {
              
              final ContactDetail detail = details.get(i); 
              if (nca.isKeySupported(ContactDetailsTable.mapInternalKeyToContactChangeKey(detail.key.ordinal()))) {
                  
                  // check if there is a corresponding ContactChange
                  if (!hasEquivalentContactChange(contactChanges, detail)) {
                      return false;
                  }
              }
          }
          
          return true;
      }
      
      /**
       * Checks if a ContactDetail has an equivalent ContactChange.
       * 
       * @param contactChanges the array of ContactChange where to search
       * @param detail the ContactDetail to for which we have to find an equivalent
       * @return true if an equivalent ContactChange is found, false otherwise
       */
      private boolean hasEquivalentContactChange(ContactChange[] contactChanges, ContactDetail detail) {
          
          for (int i = 0; i < contactChanges.length; i++) {
              
              final ContactChange change = contactChanges[i];
              
              if (change.getValue().equals(detail.value)) {
                  
                  boolean same = true;
                  // the corresponding ContactChange is found
                  // check also the keys
                  if (ContactDetailsTable.mapContactChangeKeyToInternalKey(change.getKey()) != detail.key.ordinal()) {
                      same = false;
                  }
                  // check the type
                  if (ContactDetailsTable.mapContactChangeFlagToInternalType(change.getFlags()) != detail.keyType.ordinal()) {
                      same = false;
                  }
                  // check the preferred flag
                  if (ContactDetailsTable.mapContactChangeFlagToInternalOrder(change.getFlags()) != detail.order) {
                      same = false;
                  }
                  
                  return same;
              }
          }
          
          return false;
      }
      
      /**
       * Runs the UpdateNativeContacts processor until it completes its task.
       * 
       * @param processor the UpdateNativeContacts to run
       */
      private void runUpdateNativeContactsProcessor(UpdateNativeContacts processor) {
          
          final long TIMEOUT = 60 * 1000; // 1 minute
          final long startTime = System.currentTimeMillis();
          final ArrayList<ContactSyncCallback.ProcessorComplete> pc = mContactSyncCallback.mProcessorComplete;
          
          processor.start();
          
          while (pc.size() == 0) {
              
              if (System.currentTimeMillis() - startTime > TIMEOUT) {
                  
                  fail();
              }
              processor.onTimeoutEvent();
          }
          
          assertEquals(ServiceStatus.SUCCESS, pc.get(0).status);
      }
      
      /**
       * Feeds the People database with random contacts.
       * 
       * @param contactsCount the number of contact to create
       */
      private void feedSyncableContactsInDatabase(int contactsCount) {
          
          for (int i = 0; i < contactsCount; i++) {
              
              final Contact contact = mTestModule.createDummyContactData();
              contact.synctophone = true;
              mDatabaseHelper.addContact(contact);
          }
      }
      
      /**
       * Feeds the People database with a contact containing all the possible details
       * that can be synced on native side.
       * 
       * @return the created Contact
       */
      private Contact feedOneSyncableContact() {
          
          final Contact contact = new Contact();
          contact.synctophone = true; // set it syncable to native side
          
          contact.aboutMe = "xxx xxyyyy";
          contact.friendOfMine = false;
          contact.gender = 1;
          
          // add a VCARD_NAME
          ContactDetail detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_NAME;
          detail.keyType = ContactDetail.DetailKeyTypes.UNKNOWN;
          detail.order = ContactDetail.ORDER_NORMAL;
          Name name = new Name();
          name.firstname = "Firstname";
          name.midname = "Midname";
          name.surname = "Surname";
          name.suffixes = "Suffixes";
          name.title = "Title";
          detail.value = VCardHelper.makeName(name); // a VCARD_NAME
          contact.details.add(detail);
          
          // add a VCARD_NICKNAME
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_NICKNAME;
          detail.keyType = ContactDetail.DetailKeyTypes.UNKNOWN;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "My Nickname";
          contact.details.add(detail);
          
          // add a birthday VCARD_DATE + BIRTHDAY type 
          detail = new ContactDetail();
          detail.order = ContactDetail.ORDER_NORMAL;
          Time time = new Time();
          time.set(1, 1, 2010);
          detail.setDate(time, ContactDetail.DetailKeyTypes.BIRTHDAY);
          contact.details.add(detail);
          
          // add emails (Work, Home, Other)
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_EMAIL;
          detail.keyType = ContactDetail.DetailKeyTypes.HOME;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "email@home.test";
          contact.details.add(detail);
          
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_EMAIL;
          detail.keyType = ContactDetail.DetailKeyTypes.WORK;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "email@work.test";
          contact.details.add(detail);
          
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_EMAIL;
          detail.keyType = ContactDetail.DetailKeyTypes.UNKNOWN;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "email@other.test";
          contact.details.add(detail);
          
          // add phones VCARD_PHONE (Home, Cell, Work, Fax, Other)
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_PHONE;
          detail.keyType = ContactDetail.DetailKeyTypes.HOME;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "+33111111";
          contact.details.add(detail);
          
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_PHONE;
          detail.keyType = ContactDetail.DetailKeyTypes.CELL;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "+33222222";
          contact.details.add(detail);
          
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_PHONE;
          detail.keyType = ContactDetail.DetailKeyTypes.WORK;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "+33333333";
          contact.details.add(detail);
          
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_PHONE;
          detail.keyType = ContactDetail.DetailKeyTypes.FAX;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "+33444444";
          contact.details.add(detail);
          
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_PHONE;
          detail.keyType = ContactDetail.DetailKeyTypes.UNKNOWN;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "+33555555";
          contact.details.add(detail);
          
          // add a preferred detail since all the others are set to normal
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_PHONE;
          detail.keyType = ContactDetail.DetailKeyTypes.HOME;
          detail.order = ContactDetail.ORDER_PREFERRED;
          detail.value = "+33666666";
          contact.details.add(detail);
          
          // add VCARD_ADDRESS (Home, Work, Other)
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_ADDRESS;
          detail.keyType = ContactDetail.DetailKeyTypes.HOME;
          detail.order = ContactDetail.ORDER_NORMAL;
          PostalAddress address = new PostalAddress();
          address.addressLine1 = "home address line 1";
          address.addressLine2 = "home address line 2";
          address.city = "home city";
          address.country = "home country";
          address.county = "home county";
          address.postCode = "home postcode";
          address.postOfficeBox = "home po box";
          detail.value = VCardHelper.makePostalAddress(address);
          contact.details.add(detail);
          
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_ADDRESS;
          detail.keyType = ContactDetail.DetailKeyTypes.WORK;
          detail.order = ContactDetail.ORDER_NORMAL;
          address = new PostalAddress();
          address.addressLine1 = "work address line 1";
          address.addressLine2 = "work address line 2";
          address.city = "work city";
          address.country = "work country";
          address.county = "work county";
          address.postCode = "work postcode";
          address.postOfficeBox = "work po box";
          detail.value = VCardHelper.makePostalAddress(address);
          contact.details.add(detail);
          
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_ADDRESS;
          detail.keyType = ContactDetail.DetailKeyTypes.UNKNOWN;
          detail.order = ContactDetail.ORDER_NORMAL;
          address = new PostalAddress();
          address.addressLine1 = "other address line 1";
          address.addressLine2 = "other address line 2";
          address.city = "other city";
          address.country = "other country";
          address.county = "other county";
          address.postCode = "other postcode";
          address.postOfficeBox = "other po box";
          detail.value = VCardHelper.makePostalAddress(address);
          contact.details.add(detail);
          
          // add a VCARD_URL
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_URL;
          detail.keyType = ContactDetail.DetailKeyTypes.UNKNOWN;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "http://myurl.test";
          contact.details.add(detail);
          
          // add a VCARD_NOTE
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_NOTE;
          detail.keyType = ContactDetail.DetailKeyTypes.UNKNOWN;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "a note";
          contact.details.add(detail);
          
          // add a VCARD_ORG
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_ORG;
          detail.keyType = ContactDetail.DetailKeyTypes.UNKNOWN;
          detail.order = ContactDetail.ORDER_NORMAL;
          Organisation org = new Organisation();
          org.name = "company name";
          org.unitNames.add("department");
          detail.value = VCardHelper.makeOrg(org);
          contact.details.add(detail);
          
          // add a VCARD_TITLE
          detail = new ContactDetail();
          detail.key = ContactDetail.DetailKeys.VCARD_TITLE;
          detail.keyType = ContactDetail.DetailKeyTypes.UNKNOWN;
          detail.order = ContactDetail.ORDER_NORMAL;
          detail.value = "title";
          contact.details.add(detail);
          
          mDatabaseHelper.addContact(contact);
          
          return contact;
      }
      
      /**
       * A ContactSyncCallback implementation that stores all the calls to onProcessorComplete(). 
       */
      private static class ContactSyncCallback implements IContactSyncCallback {
          
          /**
           * holds the parameters from onProcessorComplete() call
           */
          public static class ProcessorComplete {
              
              public ProcessorComplete(ServiceStatus status, String failureList, Object data) {
                  
                  this.status = status;
                  this.failureList = failureList;
                  this.data = data;
              }
              
              public ServiceStatus status;
              public String failureList;
              public Object data;
          }
          
          /**
           * List of all the calls to onProcessorComplete() with the parameters.
           */
          public ArrayList<ProcessorComplete> mProcessorComplete = new ArrayList<ProcessorComplete>(1);
          
          @Override
          public BaseEngine getEngine() {
              // TODO Auto-generated method stub
              return null;
          }
      
          @Override
          public void onDatabaseChanged() {
              // TODO Auto-generated method stub
              
          }
      
          @Override
          public void onProcessorComplete(ServiceStatus status, String failureList, Object data) {
              
              mProcessorComplete.add(new ProcessorComplete(status, failureList, data));
          }
      
          @Override
          public void setActiveRequestId(int reqId) {
              // TODO Auto-generated method stub
              
          }
      
          @Override
          public void setSyncStatus(SyncStatus syncStatus) {
              // TODO Auto-generated method stub
              
          }
      
          @Override
          public void setTimeout(long timeout) {
              // TODO Auto-generated method stub
              
          }
      }
  }
  