public override bool Equals(object obj) { const double acceptableDifference = 1e-6; OfflineReadyItem other = obj as OfflineReadyItem; if (other == null) { return(false); } if (this.Age != other.Age) { return(false); } if (!this.Date.ToUniversalTime().Equals(other.Date.ToUniversalTime())) { return(false); } if (this.Flag != other.Flag) { return(false); } if (this.Name != other.Name) { return(false); } if (Math.Abs(this.FloatingNumber - other.FloatingNumber) > acceptableDifference) { return(false); } return(true); }
private async Task CreateSyncConflict(bool autoResolve) { await ClearStore(); bool resolveConflictsOnClient = autoResolve; DateTime now = DateTime.UtcNow; int seed = now.Year * 10000 + now.Month * 100 + now.Day; Log("Using random seed: {0}", seed); Random rndGen = new Random(seed); var offlineReadyClient = CreateClient(); var localStore = new MobileServiceSQLiteStore(StoreFileName); Log("Defined the table on the local store"); localStore.DefineTable <OfflineReadyItem>(); ConflictResolvingSyncHandler <OfflineReadyItem> .ConflictResolution conflictHandlingPolicy; conflictHandlingPolicy = (client, server) => new OfflineReadyItem { Id = client.Id, Age = Math.Max(client.Age, server.Age), Date = client.Date > server.Date ? client.Date : server.Date, Flag = client.Flag || server.Flag, FloatingNumber = Math.Max(client.FloatingNumber, server.FloatingNumber), Name = client.Name }; if (resolveConflictsOnClient) { var handler = new ConflictResolvingSyncHandler <OfflineReadyItem>(this, offlineReadyClient, conflictHandlingPolicy); await offlineReadyClient.SyncContext.InitializeAsync(localStore, handler); } else { await offlineReadyClient.SyncContext.InitializeAsync(localStore); } Log("Initialized the store and sync context"); var localTable = offlineReadyClient.GetSyncTable <OfflineReadyItem>(); var remoteTable = offlineReadyClient.GetTable <OfflineReadyItem>(); await localTable.PurgeAsync(); Log("Removed all items from the local table"); var item = new OfflineReadyItem(rndGen); await remoteTable.InsertAsync(item); Log("Inserted the item to the remote store:", item); var pullQuery = "$filter=id eq '" + item.Id + "'"; await localTable.PullAsync(null, pullQuery); Log("Changing the item on the server"); item.Age++; await remoteTable.UpdateAsync(item); Log("Updated the item: {0}", item); var localItem = await localTable.LookupAsync(item.Id); Log("Retrieved the item from the local table, now updating it"); localItem.Date = localItem.Date.AddDays(1); await localTable.UpdateAsync(localItem); Log("Updated the item on the local table"); Log("Now trying to pull changes from the server (will trigger a push)"); string errorMessage = string.Empty; try { await localTable.PullAsync(null, pullQuery); if (!autoResolve) { errorMessage = "Error, pull (push) should have caused a conflict, but none happened."; } else { var expectedMergedItem = conflictHandlingPolicy(localItem, item); var localMergedItem = await localTable.LookupAsync(item.Id); if (localMergedItem.Equals(expectedMergedItem)) { Log("Item was merged correctly."); } else { errorMessage = string.Format("Error, item not merged correctly. Expected: {0}, Actual: {1}", expectedMergedItem, localMergedItem); } } } catch (MobileServicePushFailedException ex) { Log("Push exception: {0}", ex); if (autoResolve) { errorMessage = "Error, push should have succeeded."; } else { Log("Expected exception was thrown."); } } Log("Cleaning up"); await localTable.DeleteAsync(item); Log("Local table cleaned up. Now sync'ing once more"); await offlineReadyClient.SyncContext.PushAsync(); Log("Done"); localStore.Dispose(); await ClearStore(); if (!String.IsNullOrEmpty(errorMessage)) { Assert.Fail(errorMessage); } }
private async Task BasicOfflineTest() { await ClearStore(); DateTime now = DateTime.UtcNow; int seed = now.Year * 10000 + now.Month * 100 + now.Day; Log("Using random seed: {0}", seed); Random rndGen = new Random(seed); CountingHandler handler = new CountingHandler(); var requestsSentToServer = 0; var offlineReadyClient = CreateClient(handler); var localStore = new MobileServiceSQLiteStore(StoreFileName); Log("Defined the table on the local store"); localStore.DefineTable <OfflineReadyItem>(); await offlineReadyClient.SyncContext.InitializeAsync(localStore); Log("Initialized the store and sync context"); var localTable = offlineReadyClient.GetSyncTable <OfflineReadyItem>(); var remoteTable = offlineReadyClient.GetTable <OfflineReadyItem>(); var item = new OfflineReadyItem(rndGen); try { await localTable.InsertAsync(item); Log("Inserted the item to the local store:", item); Log("Validating that the item is not in the server table"); try { requestsSentToServer++; await remoteTable.LookupAsync(item.Id); Assert.Fail("Error, item is present in the server"); } catch (MobileServiceInvalidOperationException ex) { Log("Ok, item is not in the server: {0}", ex.Message); } Func <int, bool> validateRequestCount = expectedCount => { Log("So far {0} requests sent to the server", handler.RequestCount); if (handler.RequestCount != expectedCount) { Log("Error, expected {0} requests to have been sent to the server", expectedCount); return(false); } else { return(true); } }; if (!validateRequestCount(requestsSentToServer)) { Assert.Fail(string.Format("Error, expected {0} requests to have been sent to the server", requestsSentToServer)); } Log("Pushing changes to the server"); await offlineReadyClient.SyncContext.PushAsync(); requestsSentToServer++; if (!validateRequestCount(requestsSentToServer)) { Assert.Fail(string.Format("Error, expected {0} requests to have been sent to the server", requestsSentToServer)); } Log("Push done; now verifying that item is in the server"); var serverItem = await remoteTable.LookupAsync(item.Id); requestsSentToServer++; Log("Retrieved item from server: {0}", serverItem); if (serverItem.Equals(item)) { Log("Items are the same"); } else { Assert.Fail(string.Format("Items are different. Local: {0}; remote: {1}", item, serverItem)); } Log("Now updating the item locally"); item.Flag = !item.Flag; item.Age++; item.Date = new DateTime(now.Year, now.Month, now.Day, now.Hour, now.Minute, now.Second, now.Millisecond, DateTimeKind.Utc); await localTable.UpdateAsync(item); Log("Item has been updated"); var newItem = new OfflineReadyItem(rndGen); Log("Adding a new item to the local table: {0}", newItem); await localTable.InsertAsync(newItem); if (!validateRequestCount(requestsSentToServer)) { Assert.Fail(string.Format("Error, expected {0} requests to have been sent to the server", requestsSentToServer)); } Log("Pushing the new changes to the server"); await offlineReadyClient.SyncContext.PushAsync(); requestsSentToServer += 2; if (!validateRequestCount(requestsSentToServer)) { Assert.Fail(string.Format("Error, expected {0} requests to have been sent to the server", requestsSentToServer)); } Log("Push done. Verifying changes on the server"); serverItem = await remoteTable.LookupAsync(item.Id); requestsSentToServer++; if (serverItem.Equals(item)) { Log("Updated items are the same"); } else { Assert.Fail(string.Format("Items are different. Local: {0}; remote: {1}", item, serverItem)); } serverItem = await remoteTable.LookupAsync(newItem.Id); requestsSentToServer++; if (serverItem.Equals(newItem)) { Log("New inserted item is the same"); } else { Assert.Fail(string.Format("Items are different. Local: {0}; remote: {1}", item, serverItem)); } Log("Cleaning up"); await localTable.DeleteAsync(item); await localTable.DeleteAsync(newItem); Log("Local table cleaned up. Now sync'ing once more"); await offlineReadyClient.SyncContext.PushAsync(); requestsSentToServer += 2; if (!validateRequestCount(requestsSentToServer)) { Assert.Fail(string.Format("Error, expected {0} requests to have been sent to the server", requestsSentToServer)); } Log("Done"); } catch (MobileServicePushFailedException ex) { Log("Push Result status from MobileServicePushFailedException: " + ex.PushResult.Status); throw; } finally { localStore.Dispose(); ClearStore().Wait(); } }