/// <summary>
        /// Tests that throwing an exception of type toThrow from the http handler causes the push sync to be aborted
        /// </summary>
        /// <param name="toThrow">The exception to simulate coming from http layer</param>
        /// <param name="expectedStatus">The expected status of push operation as reported in PushCompletionResult and PushFailedException</param>
        /// <returns></returns>
        private async Task TestPushAbort(Exception toThrow, MobileServicePushStatus expectedStatus)
        {
            bool thrown = false;
            var hijack = new TestHttpHandler();
            hijack.OnSendingRequest = req =>
            {
                if (!thrown)
                {
                    thrown = true;
                    throw toThrow;
                }
                return Task.FromResult(req);
            };

            var operationHandler = new MobileServiceSyncHandlerMock();

            hijack.SetResponseContent("{\"id\":\"abc\",\"String\":\"Hey\"}");
            IMobileServiceClient service = new MobileServiceClient("http://www.test.com", "secret...", hijack);
            await service.SyncContext.InitializeAsync(new MobileServiceLocalStoreMock(), operationHandler);

            IMobileServiceSyncTable<StringIdType> table = service.GetSyncTable<StringIdType>();

            var item = new StringIdType() { Id = "an id", String = "what?" };
            await table.InsertAsync(item);

            Assert.AreEqual(service.SyncContext.PendingOperations, 1L);

            MobileServicePushFailedException ex = await ThrowsAsync<MobileServicePushFailedException>(service.SyncContext.PushAsync);

            Assert.AreEqual(ex.PushResult.Status, expectedStatus);
            Assert.AreEqual(ex.PushResult.Errors.Count(), 0);

            Assert.AreEqual(operationHandler.PushCompletionResult.Status, expectedStatus);

            // the insert operation is still in queue
            Assert.AreEqual(service.SyncContext.PendingOperations, 1L);

            await service.SyncContext.PushAsync();

            Assert.AreEqual(service.SyncContext.PendingOperations, 0L);
            Assert.AreEqual(operationHandler.PushCompletionResult.Status, MobileServicePushStatus.Complete);
        }
        private async Task FinalizePush(OperationBatch batch, MobileServicePushStatus batchStatus, IEnumerable<MobileServiceTableOperationError> syncErrors)
        {
            var result = new MobileServicePushCompletionResult(syncErrors, batchStatus);

            try
            {
                await batch.SyncHandler.OnPushCompleteAsync(result);

                // now that we've successfully given the errors to user, we can delete them from store
                await batch.DeleteErrorsAsync();
            }
            catch (Exception ex)
            {
                batch.OtherErrors.Add(ex);
            }

            if (batchStatus != MobileServicePushStatus.Complete || batch.HasErrors(syncErrors))
            {
                List<MobileServiceTableOperationError> unhandledSyncErrors = syncErrors.Where(e => !e.Handled).ToList();

                Exception inner;
                if (batch.OtherErrors.Count == 1)
                {
                    inner = batch.OtherErrors[0];
                }
                else
                {
                    inner = batch.OtherErrors.Any() ? new AggregateException(batch.OtherErrors) : null;
                }

                // create a new result with only unhandled errors
                result = new MobileServicePushCompletionResult(unhandledSyncErrors, batchStatus);
                this.TaskSource.TrySetException(new MobileServicePushFailedException(result, inner));
            }
            else
            {
                this.TaskSource.SetResult(0);
            }
        }
 /// <summary>
 /// Initializes a new instance of <see cref="MobileServicePushCompletionResult"/>
 /// </summary>
 /// <param name="errors">Collection of errors that occured on executing operation on remote table.</param>
 /// <param name="status">The state in which push completed.</param>
 public MobileServicePushCompletionResult(IEnumerable<MobileServiceTableOperationError> errors, MobileServicePushStatus status)
 {
     this.Errors = new ReadOnlyCollection<MobileServiceTableOperationError>(errors as IList<MobileServiceTableOperationError> ?? new List<MobileServiceTableOperationError>(errors));
     this.Status = status;
 }
        /// <summary>
        /// Changes the status of the operation batch to aborted with the specified reason.
        /// </summary>
        /// <param name="reason">Status value that respresents the reason of abort.</param>
        public void Abort(MobileServicePushStatus reason)
        {
            Debug.Assert(reason != MobileServicePushStatus.Complete);

            this.AbortReason = reason;
        }
        /// <summary>
        /// Changes the status of the operation batch to aborted with the specified reason.
        /// </summary>
        /// <param name="reason">Status value that respresents the reason of abort.</param>
        public void Abort(MobileServicePushStatus reason)
        {
            Debug.Assert(reason != MobileServicePushStatus.Complete);

            this.AbortReason = reason;
        }
 /// <summary>
 /// Initializes a new instance of <see cref="MobileServicePushCompletionResult"/>
 /// </summary>
 /// <param name="errors">Collection of errors that occured on executing operation on remote table.</param>
 /// <param name="status">The state in which push completed.</param>
 public MobileServicePushCompletionResult(IEnumerable <MobileServiceTableOperationError> errors, MobileServicePushStatus status)
 {
     this.Errors = new ReadOnlyCollection <MobileServiceTableOperationError>(errors as IList <MobileServiceTableOperationError> ?? new List <MobileServiceTableOperationError>(errors));
     this.Status = status;
 }