/// <summary>
        /// The default result handler for the <see cref="AddTaskCollectionResultHandler"/> behavior. This handler
        /// treats success and 'TaskExists' errors as successful, retries server errors (HTTP 5xx), and throws
        /// <see cref="AddTaskCollectionTerminatedException"/> on client error (HTTP 4xx).
        /// </summary>
        /// <param name="addTaskResult">The result of a single Add Task operation.</param>
        /// <param name="cancellationToken">The cancellation token associated with the AddTaskCollection operation.</param>
        /// <returns>An <see cref="AddTaskResultStatus"/> which indicates whether the <paramref name="addTaskResult"/>
        /// is classified as a success or as requiring a retry.</returns>
        public static AddTaskResultStatus DefaultAddTaskCollectionResultHandler(AddTaskResult addTaskResult, CancellationToken cancellationToken)
        {
            if (addTaskResult == null)
            {
                throw new ArgumentNullException("addTaskResult");
            }

            AddTaskResultStatus status = AddTaskResultStatus.Success;

            if (addTaskResult.Error != null)
            {
                //Check status code
                if (addTaskResult.Status == AddTaskStatus.ServerError)
                {
                    status = AddTaskResultStatus.Retry;
                }
                else if (addTaskResult.Status == AddTaskStatus.ClientError && addTaskResult.Error.Code == BatchErrorCodeStrings.TaskExists)
                {
                    status = AddTaskResultStatus.Success; //Count TaskExists as a success always
                }
                else
                {
                    //Anything else is a failure -- abort the work flow
                    throw new AddTaskCollectionTerminatedException(addTaskResult);
                }
            }
            return(status);
        }
        /// <summary>
        /// Processes a set AddTaskResults from the protocol and groups them according to the results of the AddTaskResultHandler
        /// </summary>
        /// <param name="addTaskResults"></param>
        /// <param name="taskMap">Dictionary of task name to task object instance for the specific protocol response.</param>
        private void ProcessProtocolAddTaskResults(
            IEnumerable <Protocol.Models.TaskAddResult> addTaskResults,
            IReadOnlyDictionary <string, TrackedCloudTask> taskMap)
        {
            foreach (Protocol.Models.TaskAddResult protoAddTaskResult in addTaskResults)
            {
                string           taskId      = protoAddTaskResult.TaskId;
                TrackedCloudTask trackedTask = taskMap[taskId];

                AddTaskResult omResult = new AddTaskResult(trackedTask.Task, trackedTask.RetryCount, protoAddTaskResult);

                //We know that there must be at least one AddTaskResultHandler so the below ForEach will always be called
                //at least once.
                AddTaskResultStatus status = AddTaskResultStatus.Success; //The default is success to avoid infinite retry

                //Call the customer defined result handler
                foreach (var resultHandlerFunction in this._addTaskResultHandlerCollection)
                {
                    status = resultHandlerFunction(omResult, this._parallelOptions.CancellationToken);
                }

                if (status == AddTaskResultStatus.Retry)
                {
                    //Increment retry count
                    trackedTask.IncrementRetryCount();

                    //TODO: There is nothing stopping the user from marking all tasks as Retry and never exiting this work flow...
                    //TODO: In that case maybe we should forcibly abort them after some # of attempts?
                    this._remainingTasksToAdd.Enqueue(trackedTask);
                }
            }
        }
        public async Task Bug1360227_AddTasksBatchConfirmResultHandlerTaskReadOnly()
        {
            const string testName = "Bug1360227_ConfirmResultHandlerTaskReadOnly";

            Func <AddTaskResult, CancellationToken, AddTaskResultStatus> resultHandlerFunc = (result, token) =>
            {
                //Count everything as a success
                AddTaskResultStatus resultAction = AddTaskResultStatus.Success;

                //Try to set a property of the cloud task
                InvalidOperationException e = TestUtilities.AssertThrows <InvalidOperationException>(() =>
                                                                                                     result.Task.Constraints = new TaskConstraints(TimeSpan.FromSeconds(5), null, null));

                Assert.Contains("Write access is not allowed.", e.Message);

                //Try to call a method of a CloudTask
                //TODO: This should be blocked but isn't right now...
                //try
                //{
                //    result.Task.Terminate();
                //    Debug.Fail("Should not have gotten here");
                //}
                //catch (Exception e)
                //{
                //    Console.WriteLine(e);
                //    //Swallow this exception as it is expected
                //}

                return(resultAction);
            };

            await SynchronizationContextHelper.RunTestAsync(async() =>
            {
                using (BatchClient batchCli = await TestUtilities.OpenBatchClientFromEnvironmentAsync())
                {
                    BatchClientParallelOptions parallelOptions = new BatchClientParallelOptions()
                    {
                        MaxDegreeOfParallelism = 2
                    };

                    await this.AddTasksSimpleTestAsync(
                        batchCli,
                        testName,
                        55,
                        parallelOptions,
                        resultHandlerFunc).ConfigureAwait(false);
                }
            },
                                                            TestTimeout);
        }