private static ZumoTest CreateSyncConflictTest(bool autoResolve) { var testName = "Offline - dealing with conflicts - " + (autoResolve ? "client resolves conflicts" : "push fails after conflicts"); bool resolveConflictsOnClient = autoResolve; return(new ZumoTest(testName, async delegate(ZumoTest test) { DateTime now = DateTime.UtcNow; int seed = now.Year * 10000 + now.Month * 100 + now.Day; test.AddLog("Using random seed: {0}", seed); Random rndGen = new Random(seed); var offlineReadyClient = CreateClient(); var localStore = new MobileServiceSQLiteStore(StoreFileName); test.AddLog("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>(test, offlineReadyClient, conflictHandlingPolicy); await offlineReadyClient.SyncContext.InitializeAsync(localStore, handler); } else { await offlineReadyClient.SyncContext.InitializeAsync(localStore); } test.AddLog("Initialized the store and sync context"); var localTable = offlineReadyClient.GetSyncTable <OfflineReadyItem>(); var remoteTable = offlineReadyClient.GetTable <OfflineReadyItem>(); await localTable.PurgeAsync(); test.AddLog("Removed all items from the local table"); var item = new OfflineReadyItem(rndGen); await remoteTable.InsertAsync(item); test.AddLog("Inserted the item to the remote store:", item); var pullQuery = "$filter=id eq '" + item.Id + "'"; await localTable.PullAsync(null, pullQuery); test.AddLog("Changing the item on the server"); item.Age++; await remoteTable.UpdateAsync(item); test.AddLog("Updated the item: {0}", item); var localItem = await localTable.LookupAsync(item.Id); test.AddLog("Retrieved the item from the local table, now updating it"); localItem.Date = localItem.Date.AddDays(1); await localTable.UpdateAsync(localItem); test.AddLog("Updated the item on the local table"); test.AddLog("Now trying to pull changes from the server (will trigger a push)"); bool testResult = true; try { await localTable.PullAsync(null, pullQuery); if (!autoResolve) { test.AddLog("Error, pull (push) should have caused a conflict, but none happened."); testResult = false; } else { var expectedMergedItem = conflictHandlingPolicy(localItem, item); var localMergedItem = await localTable.LookupAsync(item.Id); if (localMergedItem.Equals(expectedMergedItem)) { test.AddLog("Item was merged correctly."); } else { test.AddLog("Error, item not merged correctly. Expected: {0}, Actual: {1}", expectedMergedItem, localMergedItem); testResult = false; } } } catch (MobileServicePushFailedException ex) { test.AddLog("Push exception: {0}", ex); if (autoResolve) { test.AddLog("Error, push should have succeeded."); testResult = false; } else { test.AddLog("Expected exception was thrown."); } } test.AddLog("Cleaning up"); await localTable.DeleteAsync(item); test.AddLog("Local table cleaned up. Now sync'ing once more"); await offlineReadyClient.SyncContext.PushAsync(); test.AddLog("Done"); return testResult; }, ZumoTestGlobals.RuntimeFeatureNames.STRING_ID_TABLES)); }
private async Task CreateSyncConflict(bool autoResolve) { ClearStore(); bool resolveConflictsOnClient = autoResolve; DateTime now = DateTime.UtcNow; int seed = now.Year * 10000 + now.Month * 100 + now.Day; Random rndGen = new Random(seed); var offlineReadyClient = CreateClient(); var localStore = new MobileServiceSQLiteStore(StoreFileName); 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); } var localTable = offlineReadyClient.GetSyncTable <OfflineReadyItem>(); var remoteTable = offlineReadyClient.GetTable <OfflineReadyItem>(); await localTable.PurgeAsync(); var item = new OfflineReadyItem(rndGen); await remoteTable.InsertAsync(item); var pullQuery = "$filter=id eq '" + item.Id + "'"; await localTable.PullAsync(null, pullQuery); item.Age++; await remoteTable.UpdateAsync(item); var localItem = await localTable.LookupAsync(item.Id); localItem.Date = localItem.Date.AddDays(1); await localTable.UpdateAsync(localItem); 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); Assert.Equal(expectedMergedItem, localMergedItem); } } catch (MobileServicePushFailedException) { if (autoResolve) { errorMessage = "Error, push should have succeeded."; } } await localTable.DeleteAsync(item); await offlineReadyClient.SyncContext.PushAsync(); localStore.Dispose(); ClearStore(); Assert.True(String.IsNullOrEmpty(errorMessage)); }
private static ZumoTest CreateBasicTest() { return(new ZumoTest("Basic offline scenario", async delegate(ZumoTest test) { DateTime now = DateTime.UtcNow; int seed = now.Year * 10000 + now.Month * 100 + now.Day; test.AddLog("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); test.AddLog("Defined the table on the local store"); localStore.DefineTable <OfflineReadyItem>(); await offlineReadyClient.SyncContext.InitializeAsync(localStore); test.AddLog("Initialized the store and sync context"); var localTable = offlineReadyClient.GetSyncTable <OfflineReadyItem>(); var remoteTable = offlineReadyClient.GetTable <OfflineReadyItem>(); var item = new OfflineReadyItem(rndGen); await localTable.InsertAsync(item); test.AddLog("Inserted the item to the local store:", item); test.AddLog("Validating that the item is not in the server table"); try { requestsSentToServer++; await remoteTable.LookupAsync(item.Id); test.AddLog("Error, item is present in the server"); return false; } catch (MobileServiceInvalidOperationException ex) { test.AddLog("Ok, item is not in the server: {0}", ex.Message); } Func <int, bool> validateRequestCount = expectedCount => { test.AddLog("So far {0} requests sent to the server", handler.RequestCount); if (handler.RequestCount != expectedCount) { test.AddLog("Error, expected {0} requests to have been sent to the server", expectedCount); return false; } else { return true; } }; if (!validateRequestCount(requestsSentToServer)) { return false; } test.AddLog("Pushing changes to the server"); await offlineReadyClient.SyncContext.PushAsync(); requestsSentToServer++; if (!validateRequestCount(requestsSentToServer)) { return false; } test.AddLog("Push done; now verifying that item is in the server"); var serverItem = await remoteTable.LookupAsync(item.Id); requestsSentToServer++; test.AddLog("Retrieved item from server: {0}", serverItem); if (serverItem.Equals(item)) { test.AddLog("Items are the same"); } else { test.AddLog("Items are different. Local: {0}; remote: {1}", item, serverItem); return false; } test.AddLog("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); test.AddLog("Item has been updated"); var newItem = new OfflineReadyItem(rndGen); test.AddLog("Adding a new item to the local table: {0}", newItem); await localTable.InsertAsync(newItem); if (!validateRequestCount(requestsSentToServer)) { return false; } test.AddLog("Pushing the new changes to the server"); await offlineReadyClient.SyncContext.PushAsync(); requestsSentToServer += 2; if (!validateRequestCount(requestsSentToServer)) { return false; } test.AddLog("Push done. Verifying changes on the server"); serverItem = await remoteTable.LookupAsync(item.Id); requestsSentToServer++; if (serverItem.Equals(item)) { test.AddLog("Updated items are the same"); } else { test.AddLog("Items are different. Local: {0}; remote: {1}", item, serverItem); return false; } serverItem = await remoteTable.LookupAsync(newItem.Id); requestsSentToServer++; if (serverItem.Equals(newItem)) { test.AddLog("New inserted item is the same"); } else { test.AddLog("Items are different. Local: {0}; remote: {1}", item, serverItem); return false; } test.AddLog("Cleaning up"); await localTable.DeleteAsync(item); await localTable.DeleteAsync(newItem); test.AddLog("Local table cleaned up. Now sync'ing once more"); await offlineReadyClient.SyncContext.PushAsync(); requestsSentToServer += 2; if (!validateRequestCount(requestsSentToServer)) { return false; } test.AddLog("Done"); return true; }, ZumoTestGlobals.RuntimeFeatureNames.STRING_ID_TABLES)); }
public async Task BasicOfflineTest() { ClearStore(); DateTime now = DateTime.UtcNow; int seed = now.Year * 10000 + now.Month * 100 + now.Day; Random rndGen = new Random(seed); CountingHandler handler = new CountingHandler(); var requestsSentToServer = 0; var offlineReadyClient = CreateClient(handler); var localStore = new MobileServiceSQLiteStore(StoreFileName); localStore.DefineTable <OfflineReadyItem>(); await offlineReadyClient.SyncContext.InitializeAsync(localStore); var localTable = offlineReadyClient.GetSyncTable <OfflineReadyItem>(); var remoteTable = offlineReadyClient.GetTable <OfflineReadyItem>(); var item = new OfflineReadyItem(rndGen); try { await localTable.InsertAsync(item); await Assert.ThrowsAsync <MobileServiceInvalidOperationException>(async() => { requestsSentToServer++; await remoteTable.LookupAsync(item.Id); }); Func <int, bool> validateRequestCount = expectedCount => (handler.RequestCount == expectedCount); Assert.True(validateRequestCount(requestsSentToServer)); await offlineReadyClient.SyncContext.PushAsync(); requestsSentToServer++; Assert.True(validateRequestCount(requestsSentToServer)); var serverItem = await remoteTable.LookupAsync(item.Id); requestsSentToServer++; Assert.Equal(serverItem, item); 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); var newItem = new OfflineReadyItem(rndGen); await localTable.InsertAsync(newItem); Assert.True(validateRequestCount(requestsSentToServer)); await offlineReadyClient.SyncContext.PushAsync(); requestsSentToServer += 2; Assert.True(validateRequestCount(requestsSentToServer)); serverItem = await remoteTable.LookupAsync(item.Id); requestsSentToServer++; Assert.Equal(serverItem, item); serverItem = await remoteTable.LookupAsync(newItem.Id); requestsSentToServer++; Assert.Equal(serverItem, newItem); await localTable.DeleteAsync(item); await localTable.DeleteAsync(newItem); await offlineReadyClient.SyncContext.PushAsync(); requestsSentToServer += 2; Assert.True(validateRequestCount(requestsSentToServer)); } catch (MobileServicePushFailedException) { throw; } finally { localStore.Dispose(); ClearStore(); } }