package com.todoroo.astrid.gtasks; import java.util.Date; import android.accounts.Account; import android.accounts.AccountManager; import android.accounts.AccountManagerFuture; import android.content.Intent; import android.os.Bundle; import com.google.api.client.googleapis.extensions.android2.auth.GoogleAccountManager; import com.google.api.services.tasks.v1.model.Tasks; import com.todoroo.andlib.data.TodorooCursor; import com.todoroo.andlib.service.Autowired; import com.todoroo.andlib.service.ContextManager; import com.todoroo.andlib.sql.Criterion; import com.todoroo.andlib.sql.Query; import com.todoroo.andlib.utility.AndroidUtilities; import com.todoroo.andlib.utility.DateUtilities; import com.todoroo.andlib.utility.Preferences; import com.todoroo.astrid.data.Metadata; import com.todoroo.astrid.data.Task; import com.todoroo.astrid.gtasks.api.GtasksApiUtilities; import com.todoroo.astrid.gtasks.api.GtasksService; import com.todoroo.astrid.gtasks.auth.GtasksTokenValidator; import com.todoroo.astrid.gtasks.sync.GtasksSyncProvider; import com.todoroo.astrid.service.MetadataService; import com.todoroo.astrid.service.TaskService; import com.todoroo.astrid.test.DatabaseTestCase; @SuppressWarnings("nls") public class GtasksNewSyncTest extends DatabaseTestCase { private static GtasksService gtasksService; private GtasksSyncProvider syncProvider; private static boolean initialized = false; private static String DEFAULT_LIST = "@default"; private static final String TEST_ACCOUNT = "sync_tester2@astrid.com"; private static final long TIME_BETWEEN_SYNCS = 3000l; @Autowired TaskService taskService; @Autowired MetadataService metadataService; @Autowired GtasksMetadataService gtasksMetadataService; @Autowired GtasksPreferenceService gtasksPreferenceService; /* * Basic creation tests */ public void testTaskCreatedLocally() { String title = "Astrid task 1"; Task localTask = createNewLocalTask(title); whenInvokeSync(); assertTaskExistsRemotely(localTask, title); } public void testTaskCreatedRemotely() throws Exception { String title = "Gtasks task 1"; com.google.api.services.tasks.v1.model.Task remoteTask = new com.google.api.services.tasks.v1.model.Task(); remoteTask.title = title; remoteTask = gtasksService.createGtask(DEFAULT_LIST, remoteTask); whenInvokeSync(); assertTaskExistsLocally(remoteTask, title); } /* * Title editing tests */ public void testTitleChangedLocally() throws Exception { String title = "Astrid task 2"; Task localTask = createNewLocalTask(title); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); //Set new title on local task String newTitle = "Astrid task 2 edited"; localTask.setValue(Task.TITLE, newTitle); taskService.save(localTask); whenInvokeSync(); //Refetch remote task and assert that both local and remote titles match expected localTask = refetchLocalTask(localTask); remoteTask = refetchRemoteTask(remoteTask); assertEquals(newTitle, localTask.getValue(Task.TITLE)); assertEquals(newTitle, remoteTask.title); } public void testTitleChangedRemotely() throws Exception { String title = "Astrid task 3"; Task localTask = createNewLocalTask(title); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); //Set new title on remote task String newRemoteTitle = "Task 3 edited on gtasks"; remoteTask.title = newRemoteTitle; gtasksService.updateGtask(DEFAULT_LIST, remoteTask); whenInvokeSync(); //Refetch local/remote tasks, assert that both titles match expected remoteTask = refetchRemoteTask(remoteTask); localTask = refetchLocalTask(localTask); assertEquals(newRemoteTitle, remoteTask.title); assertEquals(newRemoteTitle, localTask.getValue(Task.TITLE)); } public void testDateChangedLocally() throws Exception { Task localTask = createLocalTaskForDateTests(" locally"); String title = localTask.getValue(Task.TITLE); long startDate = localTask.getValue(Task.DUE_DATE); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); localTask = refetchLocalTask(localTask); assertEquals(startDate, localTask.getValue(Task.DUE_DATE).longValue()); assertEquals(startDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0)); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); //Set new due date on local task long newDueDate = new Date(116, 1, 8).getTime(); localTask.setValue(Task.DUE_DATE, newDueDate); taskService.save(localTask); whenInvokeSync(); //Refetch remote task and assert that both tasks match expected due date localTask = refetchLocalTask(localTask); remoteTask = refetchRemoteTask(remoteTask); assertEquals(newDueDate, localTask.getValue(Task.DUE_DATE).longValue()); assertEquals(newDueDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0)); } public void testDateChangedRemotely() throws Exception { Task localTask = createLocalTaskForDateTests(" remotely"); String title = localTask.getValue(Task.TITLE); long startDate = localTask.getValue(Task.DUE_DATE); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); localTask = refetchLocalTask(localTask); assertEquals(startDate, localTask.getValue(Task.DUE_DATE).longValue()); assertEquals(startDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0)); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); //Set new due date on remote task long newDueDate = new Date(116, 1, 8).getTime(); remoteTask.due = GtasksApiUtilities.unixTimeToGtasksTime(newDueDate); gtasksService.updateGtask(DEFAULT_LIST, remoteTask); whenInvokeSync(); //Refetch remote task and assert that both tasks match expected due date localTask = refetchLocalTask(localTask); remoteTask = refetchRemoteTask(remoteTask); assertEquals(newDueDate, localTask.getValue(Task.DUE_DATE).longValue()); assertEquals(newDueDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0)); } public void testDateChangedBoth_ChooseLocal() throws Exception { Task localTask = createLocalTaskForDateTests(" remotely"); String title = localTask.getValue(Task.TITLE); long startDate = localTask.getValue(Task.DUE_DATE); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); localTask = refetchLocalTask(localTask); assertEquals(startDate, localTask.getValue(Task.DUE_DATE).longValue()); assertEquals(startDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0)); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); //Set new due date on remote task first long newLocalDate = new Date(128, 5, 11).getTime(); long newRemoteDate = new Date(121, 5, 25).getTime(); remoteTask.due = GtasksApiUtilities.unixTimeToGtasksTime(newRemoteDate); gtasksService.updateGtask(DEFAULT_LIST, remoteTask); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); localTask.setValue(Task.DUE_DATE, newLocalDate); taskService.save(localTask); whenInvokeSync(); //Refetch both and assert that due dates match the one we set to local (more recent) localTask = refetchLocalTask(localTask); remoteTask = refetchRemoteTask(remoteTask); assertEquals(newLocalDate, localTask.getValue(Task.DUE_DATE).longValue()); assertEquals(newLocalDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0)); } public void DISABLED_testDateChangedBoth_ChooseRemote() throws Exception { Task localTask = createLocalTaskForDateTests(" remotely"); String title = localTask.getValue(Task.TITLE); long startDate = localTask.getValue(Task.DUE_DATE); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); localTask = refetchLocalTask(localTask); assertEquals(startDate, localTask.getValue(Task.DUE_DATE).longValue()); assertEquals(startDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0)); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); //Set new due date on local task first long newLocalDate = new Date(128, 5, 11).getTime(); long newRemoteDate = new Date(121, 5, 25).getTime(); localTask.setValue(Task.DUE_DATE, newLocalDate); taskService.save(localTask); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); remoteTask.due = GtasksApiUtilities.unixTimeToGtasksTime(newRemoteDate); gtasksService.updateGtask(DEFAULT_LIST, remoteTask); whenInvokeSync(); //Refetch both and assert that due dates match the one we set to local (more recent) localTask = refetchLocalTask(localTask); remoteTask = refetchRemoteTask(remoteTask); assertEquals(newLocalDate, localTask.getValue(Task.DUE_DATE).longValue()); assertEquals(newLocalDate, GtasksApiUtilities.gtasksDueTimeToUnixTime(remoteTask.due, 0)); } /* * Helper method for due date tests */ private Task createLocalTaskForDateTests(String addToTitle) { Task localTask = createNewLocalTask("Due date will change" + addToTitle); long dueDate = new Date(115, 2, 14).getTime(); localTask.setValue(Task.DUE_DATE, dueDate); taskService.save(localTask); return localTask; } public void testNoteEditedLocally() throws Exception { Task localTask = createLocalTaskForNoteTests(" locally"); String title = localTask.getValue(Task.TITLE); String originalNote = localTask.getValue(Task.NOTES); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); assertEquals(originalNote, localTask.getValue(Task.NOTES)); assertEquals(originalNote, remoteTask.notes); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); String newNote = "New local note"; localTask.setValue(Task.NOTES, newNote); taskService.save(localTask); whenInvokeSync(); localTask = refetchLocalTask(localTask); remoteTask = refetchRemoteTask(remoteTask); assertEquals(newNote, localTask.getValue(Task.NOTES)); assertEquals(newNote, remoteTask.notes); } public void testNoteEditedRemotely() throws Exception { Task localTask = createLocalTaskForNoteTests(" remotely"); String title = localTask.getValue(Task.TITLE); String originalNote = localTask.getValue(Task.NOTES); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); assertEquals(originalNote, localTask.getValue(Task.NOTES)); assertEquals(originalNote, remoteTask.notes); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); String newNote = "New remote note"; remoteTask.notes = newNote; gtasksService.updateGtask(DEFAULT_LIST, remoteTask); whenInvokeSync(); localTask = refetchLocalTask(localTask); remoteTask = refetchRemoteTask(remoteTask); assertEquals(newNote, localTask.getValue(Task.NOTES)); assertEquals(newNote, remoteTask.notes); } public void DISABLED_testNoteEditedBoth() throws Exception { Task localTask = createLocalTaskForNoteTests(" remotely"); String title = localTask.getValue(Task.TITLE); String originalNote = localTask.getValue(Task.NOTES); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); assertEquals(originalNote, localTask.getValue(Task.NOTES)); assertEquals(originalNote, remoteTask.notes); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); String newLocalNote = "New local note"; String newRemoteNote = "New remote note"; localTask.setValue(Task.NOTES, newLocalNote); taskService.save(localTask); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); remoteTask.notes = newRemoteNote; gtasksService.updateGtask(DEFAULT_LIST, remoteTask); whenInvokeSync(); localTask = refetchLocalTask(localTask); remoteTask = refetchRemoteTask(remoteTask); System.err.println("Local note: " + localTask.getValue(Task.NOTES)); System.err.println("Remote note: " + remoteTask.notes); } private Task createLocalTaskForNoteTests(String addToTitle) { Task localTask = createNewLocalTask("Note will change" + addToTitle); String note = "Original note"; localTask.setValue(Task.NOTES, note); taskService.save(localTask); return localTask; } /* * Completion tests */ public void testTaskCompletedLocally() throws Exception { String title = "Will complete locally"; Task localTask = createNewLocalTask(title); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); long completion = DateUtilities.now(); localTask.setValue(Task.COMPLETION_DATE, completion); taskService.save(localTask); whenInvokeSync(); localTask = refetchLocalTask(localTask); remoteTask = refetchRemoteTask(remoteTask); assertEquals(completion, localTask.getValue(Task.COMPLETION_DATE).longValue()); assertEquals(GtasksApiUtilities.unixTimeToGtasksTime(completion), GtasksApiUtilities.gtasksCompletedTimeStringToLocalTimeString(remoteTask.completed)); assertEquals("completed", remoteTask.status); } public void testTaskCompletedRemotely() throws Exception { String title = "Will complete remotely"; Task localTask = createNewLocalTask(title); whenInvokeSync(); com.google.api.services.tasks.v1.model.Task remoteTask = assertTaskExistsRemotely(localTask, title); AndroidUtilities.sleepDeep(TIME_BETWEEN_SYNCS); long completion = DateUtilities.now(); remoteTask.status = "completed"; remoteTask.completed = GtasksApiUtilities.unixTimeToGtasksTime(completion); gtasksService.updateGtask(DEFAULT_LIST, remoteTask); whenInvokeSync(); localTask = refetchLocalTask(localTask); remoteTask = refetchRemoteTask(remoteTask); assertEquals(completion, localTask.getValue(Task.COMPLETION_DATE).longValue()); assertEquals(GtasksApiUtilities.unixTimeToGtasksTime(completion), GtasksApiUtilities.gtasksCompletedTimeStringToLocalTimeString(remoteTask.completed)); assertEquals("completed", remoteTask.status); } private com.google.api.services.tasks.v1.model.Task assertTaskExistsRemotely(Task localTask, String title) { //Get the corresponding remote id for a local task Metadata metadata = gtasksMetadataService.getTaskMetadata(localTask.getId()); String taskId = metadata.getValue(GtasksMetadata.ID); String listId = metadata.getValue(GtasksMetadata.LIST_ID); //Fetch the remote task belonging to that id com.google.api.services.tasks.v1.model.Task remoteTask = null; try { remoteTask = gtasksService.getGtask(listId, taskId); } catch (Exception e) { e.printStackTrace(); fail("Failed to find remote task " + taskId); } //Do a basic title match assertNotNull(remoteTask); assertEquals(title, localTask.getValue(Task.TITLE)); assertEquals(title, remoteTask.title); return remoteTask; } private Task assertTaskExistsLocally(com.google.api.services.tasks.v1.model.Task remoteTask, String title) { long localId = localIdForTask(remoteTask); //Fetch the local task from the database Task localTask = taskService.fetchById(localId, Task.PROPERTIES); assertNotNull(localTask); assertEquals(title, remoteTask.title); assertEquals(title, localTask.getValue(Task.TITLE)); return localTask; } private Task refetchLocalTask(Task localTask) { return taskService.fetchById(localTask.getValue(Task.ID), Task.PROPERTIES); } private com.google.api.services.tasks.v1.model.Task refetchRemoteTask(com.google.api.services.tasks.v1.model.Task remoteTask) throws Exception { return gtasksService.getGtask(GtasksApiUtilities.extractListIdFromSelfLink(remoteTask), remoteTask.id); } private long localIdForTask(com.google.api.services.tasks.v1.model.Task remoteTask) { TodorooCursor cursor = metadataService.query(Query.select(Metadata.TASK). where(Criterion.and(Metadata.KEY.eq(GtasksMetadata.METADATA_KEY), GtasksMetadata.ID.eq(remoteTask.id)))); try { assertEquals(1, cursor.getCount()); cursor.moveToFirst(); return cursor.get(Metadata.TASK); } finally { cursor.close(); } } //Create a new Astrid task and save it to the database private Task createNewLocalTask(String title) { Task task = new Task(); task.setValue(Task.TITLE, title); taskService.save(task); return task; } //Perform a synchronization private void whenInvokeSync() { syncProvider.synchronize(getContext()); gtasksService = syncProvider.getGtasksService(); //This is to prevent token mismatch; the sync provider invalidates the old one } @Override protected void setUp() throws Exception { super.setUp(); if (!initialized) { initializeTestService(); } setupTestList(); syncProvider = new GtasksSyncProvider(); } private void initializeTestService() throws Exception { GoogleAccountManager manager = new GoogleAccountManager(ContextManager.getContext()); Account[] accounts = manager.getAccounts(); Account toUse = null; for (Account a : accounts) { if (a.name.equals(TEST_ACCOUNT)) { toUse = a; break; } } if (toUse == null) { toUse = accounts[0]; } Preferences.setString(GtasksPreferenceService.PREF_USER_NAME, toUse.name); AccountManagerFuture accountManagerFuture = manager.manager.getAuthToken(toUse, "oauth2:https://www.googleapis.com/auth/tasks", true, null, null); Bundle authTokenBundle = accountManagerFuture.getResult(); if (authTokenBundle.containsKey(AccountManager.KEY_INTENT)) { Intent i = (Intent) authTokenBundle.get(AccountManager.KEY_INTENT); ContextManager.getContext().startActivity(i); return; } String authToken = authTokenBundle.getString(AccountManager.KEY_AUTHTOKEN); authToken = GtasksTokenValidator.validateAuthToken(authToken); gtasksPreferenceService.setToken(authToken); gtasksService = new GtasksService(authToken); initialized = true; } private void setupTestList() throws Exception { Tasks defaultListTasks = gtasksService.getAllGtasksFromListId(DEFAULT_LIST, false); if (defaultListTasks.items != null) { for (com.google.api.services.tasks.v1.model.Task t : defaultListTasks.items) { gtasksService.deleteGtask(DEFAULT_LIST, t.id); } } } }