public void TestUploadProgress()
        {
            int chunkSize = 200;
            var payload   = Encoding.UTF8.GetBytes(UploadTestData);

            var handler = new MultipleChunksMessageHandler(true, ServerError.None, payload.Length, chunkSize);

            using (var service = new MockClientService(new BaseClientService.Initializer()
            {
                HttpClientFactory = new MockHttpClientFactory(handler)
            }))
            {
                var stream = new MemoryStream(payload);
                var upload = new MockResumableUpload(service, stream, "text/plain");
                upload.ChunkSize = chunkSize;

                var progressEvents = new List <IUploadProgress>();
                upload.ProgressChanged += (progress) =>
                {
                    progressEvents.Add(progress);
                };

                upload.Upload();

                // Starting (1) + Uploading (2) + Completed (1)
                Assert.That(progressEvents.Count, Is.EqualTo(4));
                Assert.That(progressEvents[0].Status, Is.EqualTo(UploadStatus.Starting));
                Assert.That(progressEvents[1].Status, Is.EqualTo(UploadStatus.Uploading));
                Assert.That(progressEvents[2].Status, Is.EqualTo(UploadStatus.Uploading));
                Assert.That(progressEvents[3].Status, Is.EqualTo(UploadStatus.Completed));
            }
        }
Пример #2
0
        public void Download_Error_PlaintextResponse(string responseText)
        {
            var downloadUri = "http://www.sample.com";
            var chunkSize   = 100;

            var handler = new MultipleChunksMessageHandler {
                ErrorResponse = responseText
            };

            handler.StatusCode = HttpStatusCode.NotFound;
            handler.ChunkSize  = chunkSize;
            // The media downloader adds the parameter...
            handler.DownloadUri = new Uri(downloadUri + "?alt=media");

            using (var service = CreateMockClientService(handler))
            {
                var downloader = new MediaDownloader(service);
                downloader.ChunkSize = chunkSize;
                IList <IDownloadProgress> progressList = new List <IDownloadProgress>();
                downloader.ProgressChanged += (p) =>
                {
                    progressList.Add(p);
                };

                var outputStream = new MemoryStream();
                downloader.Download(downloadUri, outputStream);

                var lastProgress = progressList.LastOrDefault();
                Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Failed));
                GoogleApiException exception = (GoogleApiException)lastProgress.Exception;
                Assert.That(exception.HttpStatusCode, Is.EqualTo(handler.StatusCode));
                Assert.That(exception.Message, Is.EqualTo(responseText));
                Assert.IsNull(exception.Error);
            }
        }
        /// <summary> Test helper to test a fail uploading by with the given server error.</summary>
        private void SubtestChunkUploadFail(ServerError error)
        {
            int chunkSize = 100;
            var payload   = Encoding.UTF8.GetBytes(UploadTestData);

            var handler = new MultipleChunksMessageHandler(true, error, payload.Length,
                                                           chunkSize, true);

            using (var service = new MockClientService(new BaseClientService.Initializer()
            {
                HttpClientFactory = new MockHttpClientFactory(handler)
            }))
            {
                var stream = new MemoryStream(payload);
                var upload = new MockResumableUpload(service, stream, "text/plain");
                upload.ChunkSize = chunkSize;

                IUploadProgress lastProgressStatus = null;
                upload.ProgressChanged += (p) =>
                {
                    lastProgressStatus = p;
                };
                upload.Upload();

                var exepctedCalls = MultipleChunksMessageHandler.ErrorOnCall +
                                    service.HttpClient.MessageHandler.NumTries - 1;
                Assert.That(handler.Calls, Is.EqualTo(exepctedCalls));
                Assert.NotNull(lastProgressStatus);
                Assert.NotNull(lastProgressStatus.Exception);
                Assert.That(lastProgressStatus.Status, Is.EqualTo(UploadStatus.Failed));
            }
        }
        /// <summary> Helper test to test canceling media upload in the middle.</summary>
        /// <param name="cancelRequest">The request index to cancel.</param>
        private void TestChunkUploadFail_Cancel(int cancelRequest)
        {
            int chunkSize = 100;
            var payload   = Encoding.UTF8.GetBytes(UploadTestData);

            var handler = new MultipleChunksMessageHandler(true, ServerError.None, payload.Length, chunkSize, false);

            handler.CancellationTokenSource = new CancellationTokenSource();
            handler.CancelRequestNum        = cancelRequest;
            using (var service = new MockClientService(new BaseClientService.Initializer()
            {
                HttpClientFactory = new MockHttpClientFactory(handler)
            }))
            {
                var stream = new MemoryStream(payload);
                var upload = new MockResumableUpload(service, stream, "text/plain");
                upload.ChunkSize = chunkSize;

                try
                {
                    var result = upload.UploadAsync(handler.CancellationTokenSource.Token).Result;
                    Assert.Fail();
                }
                catch (AggregateException ae)
                {
                    Assert.IsInstanceOf <TaskCanceledException>(ae.InnerException);
                }

                Assert.That(handler.Calls, Is.EqualTo(cancelRequest));
            }
        }
Пример #5
0
        public void Download_Error()
        {
            var downloadUri = "http://www.sample.com?alt=media";
            var chunkSize   = 100;

            // reset the steam
            StreamContent.Position = 0;

            var handler = new MultipleChunksMessageHandler();

            handler.StatusCode  = HttpStatusCode.BadRequest;
            handler.ChunkSize   = chunkSize;
            handler.DownloadUri = new Uri(downloadUri);

            using (var service = new MockClientService(new BaseClientService.Initializer()
            {
                HttpClientFactory = new MockHttpClientFactory(handler)
            }))
            {
                var downloader = new MediaDownloader(service);
                downloader.ChunkSize = chunkSize;
                IList <IDownloadProgress> progressList = new List <IDownloadProgress>();
                downloader.ProgressChanged += (p) =>
                {
                    progressList.Add(p);
                };

                var outputStream = new MemoryStream();
                downloader.Download(downloadUri, outputStream);

                var lastProgress = progressList.LastOrDefault();
                Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Failed));
            }
        }
        private void SubtestTestChunkUpload(bool knownSize, ServerError error = ServerError.None)
        {
            // -If error is none - there isn't any error, so there should be 6 calls (1 to initialize and 5 successful
            // upload requests.
            // -If error isn't supported by the media upload (4xx) - the upload fails.
            // Otherwise, we simulate server 503 error or exception, as following:
            // On the 4th chunk (when server received bytes 300-399), we mimic an error.
            // In the next request we expect the client to send the content range header with "bytes */[size]", and
            // server return that the upload was interrupted after 299 bytes. From that point the server works as
            // expected, and received the last chunks successfully
            int chunkSize = 100;
            var payload   = Encoding.UTF8.GetBytes(UploadTestData);

            var handler = new MultipleChunksMessageHandler(knownSize, error, payload.Length, chunkSize);

            using (var service = new MockClientService(new BaseClientService.Initializer()
            {
                HttpClientFactory = new MockHttpClientFactory(handler)
            }))
            {
                var stream = knownSize ? new MemoryStream(payload) : new UnknownSizeMemoryStream(payload);
                var upload = new MockResumableUpload(service, stream, "text/plain");
                upload.ChunkSize = chunkSize;

                IUploadProgress lastProgress = null;
                upload.ProgressChanged += (p) => lastProgress = p;
                upload.Upload();

                Assert.NotNull(lastProgress);

                if (error == ServerError.NotFound)
                {
                    // upload fails
                    Assert.That(lastProgress.Status, Is.EqualTo(UploadStatus.Failed));
                    Assert.That(handler.Calls, Is.EqualTo(MultipleChunksMessageHandler.ErrorOnCall));
                    Assert.True(lastProgress.Exception.Message.Contains(
                                    @"Message[Login Required] Location[Authorization - header] Reason[required] Domain[global]"),
                                "Error message is invalid");
                }
                else
                {
                    Assert.That(lastProgress.Status, Is.EqualTo(UploadStatus.Completed));
                    Assert.That(payload, Is.EqualTo(handler.ReceivedData.ToArray()));
                    // if server doesn't supports errors there should be 6 request - 1 initialize request and 5
                    // requests to upload data (the whole data send is divided to 5 chunks).
                    // if server supports errors, there should be an additional 2 requests (1 to query where the upload
                    // was interrupted and 1 to resend the data)
                    Assert.That(handler.Calls, error != ServerError.None ? Is.EqualTo(8) : Is.EqualTo(6));
                }
            }
        }
        /// <summary>Test helper to test a fail uploading by with the given server error.</summary>
        /// <param name="error">The error kind.</param>
        /// <param name="resume">Whether we should resume uploading the stream after the failure.</param>
        /// <param name="errorOnResume">Whether the first call after resuming should fail.</param>
        private void SubtestChunkUploadFail(ServerError error, bool resume = false, bool errorOnResume = false)
        {
            int chunkSize = 100;
            var payload   = Encoding.UTF8.GetBytes(UploadTestData);

            var handler = new MultipleChunksMessageHandler(true, error, payload.Length, chunkSize, true);

            using (var service = new MockClientService(new BaseClientService.Initializer()
            {
                HttpClientFactory = new MockHttpClientFactory(handler)
            }))
            {
                var stream = new MemoryStream(payload);
                var upload = new MockResumableUpload(service, stream, "text/plain");
                upload.chunkSize = chunkSize;

                IUploadProgress lastProgressStatus = null;
                upload.ProgressChanged += (p) =>
                {
                    lastProgressStatus = p;
                };
                upload.Upload();

                // Upload should fail.
                var exepctedCalls = MultipleChunksMessageHandler.ErrorOnCall +
                                    service.HttpClient.MessageHandler.NumTries - 1;
                Assert.That(handler.Calls, Is.EqualTo(exepctedCalls));
                Assert.NotNull(lastProgressStatus);
                Assert.NotNull(lastProgressStatus.Exception);
                Assert.That(lastProgressStatus.Status, Is.EqualTo(UploadStatus.Failed));

                if (resume)
                {
                    // Hack the handler, so when calling the resume method the upload should succeeded.
                    handler.ResumeFromCall = exepctedCalls + 1;
                    handler.ErrorOnResume  = errorOnResume;

                    upload.Resume();

                    // The first request after resuming is to query the server where the media upload was interrupted.
                    // If errorOnResume is true, the server's first response will be 503.
                    exepctedCalls += MultipleChunksMessageHandler.CallAfterResume + 1 + (errorOnResume ? 1 : 0);
                    Assert.That(handler.Calls, Is.EqualTo(exepctedCalls));
                    Assert.NotNull(lastProgressStatus);
                    Assert.Null(lastProgressStatus.Exception);
                    Assert.That(lastProgressStatus.Status, Is.EqualTo(UploadStatus.Completed));
                    Assert.That(payload, Is.EqualTo(handler.ReceivedData.ToArray()));
                }
            }
        }
        public void Download_Error_JsonResponse()
        {
            var downloadUri = "http://www.sample.com";
            var chunkSize   = 100;
            var error       = new RequestError {
                Code = 12345, Message = "Text", Errors = new[] { new SingleError {
                                                                     Message = "Nested error"
                                                                 } }
            };
            var response = new StandardResponse <object> {
                Error = error
            };
            var responseText = new NewtonsoftJsonSerializer().Serialize(response);

            var handler = new MultipleChunksMessageHandler {
                ErrorResponse = responseText
            };

            handler.StatusCode = HttpStatusCode.BadRequest;
            handler.ChunkSize  = chunkSize;
            // The media downloader adds the parameter...
            handler.DownloadUri = new Uri(downloadUri + "?alt=media");

            using (var service = CreateMockClientService(handler))
            {
                var downloader = new MediaDownloader(service);
                downloader.ChunkSize = chunkSize;
                IList <IDownloadProgress> progressList = new List <IDownloadProgress>();
                downloader.ProgressChanged += (p) =>
                {
                    progressList.Add(p);
                };

                var outputStream = new MemoryStream();
                downloader.Download(downloadUri, outputStream);

                var lastProgress = progressList.LastOrDefault();
                Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Failed));
                GoogleApiException exception = (GoogleApiException)lastProgress.Exception;
                Assert.That(exception.HttpStatusCode, Is.EqualTo(handler.StatusCode));
                // Just a smattering of checks - if these two pass, it's surely okay.
                Assert.That(exception.Error.Code, Is.EqualTo(error.Code));
                Assert.That(exception.Error.Errors[0].Message, Is.EqualTo(error.Errors[0].Message));
            }
        }
        /// <summary>Tests a single upload request.</summary>
        /// <param name="knownSize">Defines if the stream size is known</param>
        /// <param name="expectedCalls">How many HTTP calls should be made to the server</param>
        /// <param name="error">Defines the type of error this test tests. The default value is none</param>
        /// <param name="chunkSize">Defines the size of a chunk</param>
        /// <param name="readBytesOnError">How many bytes the server reads when it returns 5xx</param>
        private void SubtestTestChunkUpload(bool knownSize, int expectedCalls, ServerError error = ServerError.None,
                                            int chunkSize = 100, int readBytesOnError = 0)
        {
            // If an error isn't supported by the media upload (4xx) - the upload fails.
            // Otherwise, we simulate server 503 error or exception, as following:
            // On the 3th chunk (4th chunk including the initial request), we mimic an error.
            // In the next request we expect the client to send the content range header with "bytes */[size]", and
            // server return that the upload was interrupted after x bytes.
            // From that point the server works as expected, and received the last chunks successfully
            var payload = Encoding.UTF8.GetBytes(UploadTestData);

            var handler = new MultipleChunksMessageHandler(knownSize, error, payload.Length, chunkSize);

            handler.ReadBytesOnError = readBytesOnError;
            using (var service = new MockClientService(new BaseClientService.Initializer()
            {
                HttpClientFactory = new MockHttpClientFactory(handler)
            }))
            {
                var stream = knownSize ? new MemoryStream(payload) : new UnknownSizeMemoryStream(payload);
                var upload = new MockResumableUpload(service, stream, "text/plain");
                upload.chunkSize = chunkSize;

                IUploadProgress lastProgress = null;
                upload.ProgressChanged += (p) => lastProgress = p;
                upload.Upload();

                Assert.NotNull(lastProgress);

                if (error == ServerError.NotFound)
                {
                    // Upload fails.
                    Assert.That(lastProgress.Status, Is.EqualTo(UploadStatus.Failed));
                    Assert.True(lastProgress.Exception.Message.Contains(
                                    @"Message[Login Required] Location[Authorization - header] Reason[required] Domain[global]"),
                                "Error message is invalid");
                }
                else
                {
                    Assert.That(lastProgress.Status, Is.EqualTo(UploadStatus.Completed));
                    Assert.That(payload, Is.EqualTo(handler.ReceivedData.ToArray()));
                }
                Assert.That(handler.Calls, Is.EqualTo(expectedCalls));
            }
        }
        /// <summary>A helper test to test sync and async downloads.</summary>
        /// <param name="chunkSize">The chunk size for each part.</param>
        /// <param name="sync">Indicates if this download should be synchronously or asynchronously.</param>
        /// <param name="cancelRequest">Defines the request index to cancel the download request.</param>
        /// <param name="downloadUri">The URI which contains the media to download.</param>
        private void Subtest_Download_Chunks(int chunkSize, bool sync = true, int cancelRequest = 0,
                                             string downloadUri       = "http://www.sample.com")
        {
            var handler = new MultipleChunksMessageHandler(MediaContent);

            handler.StatusCode  = HttpStatusCode.OK;
            handler.ChunkSize   = chunkSize;
            handler.DownloadUri = new Uri(downloadUri +
                                          (downloadUri.Contains("?") ? "&" : "?") + "alt=media");

            // support cancellation
            if (cancelRequest > 0)
            {
                handler.CancelRequestNum = cancelRequest;
            }
            handler.CancellationTokenSource = new CancellationTokenSource();

            int expectedCalls = (int)Math.Ceiling((double)MediaContent.Length / chunkSize);

            using (var service = CreateMockClientService(handler))
            {
                var downloader = new MediaDownloader(service);
                downloader.ChunkSize = chunkSize;
                IList <IDownloadProgress> progressList = new List <IDownloadProgress>();
                downloader.ProgressChanged += (p) =>
                {
                    progressList.Add(p);
                };

                var outputStream = new MemoryStream();
                if (sync)
                {
                    downloader.Download(downloadUri, outputStream);
                }
                else
                {
                    try
                    {
                        var result = downloader.DownloadAsync(downloadUri, outputStream,
                                                              handler.CancellationTokenSource.Token).Result;
                        Assert.AreEqual(0, handler.CancelRequestNum);
                    }
                    catch (AggregateException ex)
                    {
                        Assert.IsInstanceOf <TaskCanceledException>(ex.InnerException);
                    }
                }

                var lastProgress = progressList.LastOrDefault();
                if (cancelRequest > 0)
                {
                    // the download was interrupted in the middle
                    Assert.That(handler.Calls, Is.EqualTo(cancelRequest));
                    // last request should fail
                    Assert.NotNull(lastProgress);
                    Assert.NotNull(lastProgress.Exception);
                    Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Failed));
                    Assert.That(lastProgress.BytesDownloaded, Is.EqualTo(chunkSize * cancelRequest));
                }
                else
                {
                    // the download succeeded
                    Assert.That(handler.Calls, Is.EqualTo(expectedCalls));
                    Assert.NotNull(lastProgress);
                    Assert.Null(lastProgress.Exception);
                    Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Completed));
                    Assert.That(lastProgress.BytesDownloaded, Is.EqualTo(MediaContent.Length));

                    byte[] actual = outputStream.ToArray();
                    CollectionAssert.AreEqual(MediaContent, actual);
                }
            }
        }
        public void Download_Error_PlaintextResponse()
        {
            var downloadUri = "http://www.sample.com";
            var chunkSize = 100;
            var responseText = "Not Found";

            var handler = new MultipleChunksMessageHandler { ErrorResponse = responseText };
            handler.StatusCode = HttpStatusCode.NotFound;
            handler.ChunkSize = chunkSize;
            // The media downloader adds the parameter...
            handler.DownloadUri = new Uri(downloadUri + "?alt=media");

            using (var service = CreateMockClientService(handler))
            {
                var downloader = new MediaDownloader(service);
                downloader.ChunkSize = chunkSize;
                IList<IDownloadProgress> progressList = new List<IDownloadProgress>();
                downloader.ProgressChanged += (p) =>
                {
                    progressList.Add(p);
                };

                var outputStream = new MemoryStream();
                downloader.Download(downloadUri, outputStream);

                var lastProgress = progressList.LastOrDefault();
                Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Failed));
                GoogleApiException exception = (GoogleApiException) lastProgress.Exception;
                Assert.That(exception.HttpStatusCode, Is.EqualTo(handler.StatusCode));
                Assert.That(exception.Message, Is.EqualTo(responseText));
                Assert.IsNull(exception.Error);
            }
        }
        public void Download_Error_JsonResponse()
        {
            var downloadUri = "http://www.sample.com";
            var chunkSize = 100;
            var error = new RequestError { Code = 12345, Message = "Text", Errors = new[] { new SingleError { Message = "Nested error" } } };
            var response = new StandardResponse<object> { Error = error };
            var responseText = new NewtonsoftJsonSerializer().Serialize(response);

            var handler = new MultipleChunksMessageHandler { ErrorResponse = responseText };
            handler.StatusCode = HttpStatusCode.BadRequest;
            handler.ChunkSize = chunkSize;
            // The media downloader adds the parameter...
            handler.DownloadUri = new Uri(downloadUri + "?alt=media");

            using (var service = CreateMockClientService(handler))
            {
                var downloader = new MediaDownloader(service);
                downloader.ChunkSize = chunkSize;
                IList<IDownloadProgress> progressList = new List<IDownloadProgress>();
                downloader.ProgressChanged += (p) =>
                    {
                        progressList.Add(p);
                    };

                var outputStream = new MemoryStream();
                downloader.Download(downloadUri, outputStream);

                var lastProgress = progressList.LastOrDefault();
                Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Failed));
                GoogleApiException exception = (GoogleApiException) lastProgress.Exception;
                Assert.That(exception.HttpStatusCode, Is.EqualTo(handler.StatusCode));
                // Just a smattering of checks - if these two pass, it's surely okay.
                Assert.That(exception.Error.Code, Is.EqualTo(error.Code));
                Assert.That(exception.Error.Errors[0].Message, Is.EqualTo(error.Errors[0].Message));
            }
        }
        /// <summary>A helper test to test sync and async downloads.</summary>
        /// <param name="chunkSize">The chunk size for each part.</param>
        /// <param name="sync">Indicates if this download should be synchronously or asynchronously.</param>
        /// <param name="cancelRequest">Defines the request index to cancel the download request.</param>
        /// <param name="downloadUri">The URI which contains the media to download.</param>
        private void Subtest_Download_Chunks(int chunkSize, bool sync = true, int cancelRequest = 0,
            string downloadUri = "http://www.sample.com")
        {
            var handler = new MultipleChunksMessageHandler(MediaContent);
            handler.StatusCode = HttpStatusCode.OK;
            handler.ChunkSize = chunkSize;
            handler.DownloadUri = new Uri(downloadUri +
                (downloadUri.Contains("?") ? "&" : "?") + "alt=media");

            // support cancellation
            if (cancelRequest > 0)
            {
                handler.CancelRequestNum = cancelRequest;
            }
            handler.CancellationTokenSource = new CancellationTokenSource();

            int expectedCalls = (int)Math.Ceiling((double) MediaContent.Length / chunkSize);
            using (var service = CreateMockClientService(handler))
            {
                var downloader = new MediaDownloader(service);
                downloader.ChunkSize = chunkSize;
                IList<IDownloadProgress> progressList = new List<IDownloadProgress>();
                downloader.ProgressChanged += (p) =>
                {
                    progressList.Add(p);
                };

                var outputStream = new MemoryStream();
                if (sync)
                {
                    downloader.Download(downloadUri, outputStream);
                }
                else
                {
                    try
                    {
                        var result = downloader.DownloadAsync(downloadUri, outputStream,
                            handler.CancellationTokenSource.Token).Result;
                        Assert.AreEqual(0, handler.CancelRequestNum);
                    }
                    catch (AggregateException ex)
                    {
                        Assert.IsInstanceOf<TaskCanceledException>(ex.InnerException);
                    }
                }

                var lastProgress = progressList.LastOrDefault();
                if (cancelRequest > 0)
                {
                    // the download was interrupted in the middle
                    Assert.That(handler.Calls, Is.EqualTo(cancelRequest));
                    // last request should fail
                    Assert.NotNull(lastProgress);
                    Assert.NotNull(lastProgress.Exception);
                    Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Failed));
                    Assert.That(lastProgress.BytesDownloaded, Is.EqualTo(chunkSize * cancelRequest));
                }
                else
                {
                    // the download succeeded
                    Assert.That(handler.Calls, Is.EqualTo(expectedCalls));
                    Assert.NotNull(lastProgress);
                    Assert.Null(lastProgress.Exception);
                    Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Completed));
                    Assert.That(lastProgress.BytesDownloaded, Is.EqualTo(MediaContent.Length));

                    byte[] actual = outputStream.ToArray();
                    CollectionAssert.AreEqual(MediaContent, actual);
                }
            }
        }
Пример #14
0
        /// <summary>A helper test to test sync and async downloads.</summary>
        /// <param name="chunkSize">The chunk size for each part.</param>
        /// <param name="sync">Indicates if this download should be synchronously or asynchronously.</param>
        /// <param name="cancelRequest">Defines the request index to cancel the download request.</param>
        private void Subtest_Download_Chunks(int chunkSize, bool sync = true, int cancelRequest = 0)
        {
            // reset the steam
            StreamContent.Position = 0;

            string downloadUri = "http://www.sample.com";
            var    handler     = new MultipleChunksMessageHandler();

            handler.ChunkSize   = chunkSize;
            handler.DownloadUri = new Uri(downloadUri + "?alt=media");

            // support cancellation
            if (cancelRequest > 0)
            {
                handler.CancelRequestNum = cancelRequest;
            }
            handler.CancellationTokenSource = new CancellationTokenSource();

            int expectedCalls = (int)Math.Ceiling((double)StreamContent.Length / chunkSize);

            using (var service = new MockClientService(new BaseClientService.Initializer()
            {
                HttpClientFactory = new MockHttpClientFactory(handler)
            }))
            {
                var downloader = new MediaDownloader(service);
                downloader.ChunkSize = chunkSize;
                IList <IDownloadProgress> progressList = new List <IDownloadProgress>();
                downloader.ProgressChanged += (p) =>
                {
                    progressList.Add(p);
                };

                var outputStream = new MemoryStream();
                if (sync)
                {
                    downloader.Download(downloadUri, outputStream);
                    Assert.AreEqual(handler.ThreadId, Thread.CurrentThread.ManagedThreadId);
                }
                else
                {
                    var task = downloader.DownloadAsync(downloadUri, outputStream,
                                                        handler.CancellationTokenSource.Token);
                    try
                    {
                        task.Wait();
                        Assert.AreEqual(cancelRequest, 0);
                    }
                    catch (AggregateException ex)
                    {
                        Assert.That(ex.InnerException, Is.InstanceOf <TaskCanceledException>());
                        Assert.AreNotEqual(cancelRequest, 0);
                    }
                    Assert.AreNotEqual(handler.ThreadId, Thread.CurrentThread.ManagedThreadId);
                }

                var lastProgress = progressList.LastOrDefault();
                if (cancelRequest > 0)
                {
                    // the download was interrupted in the middle
                    Assert.That(handler.Calls, Is.EqualTo(cancelRequest));
                    // last request should be failed
                    Assert.NotNull(lastProgress);
                    Assert.NotNull(lastProgress.Exception);
                    Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Failed));
                    Assert.That(lastProgress.BytesDownloaded, Is.EqualTo(chunkSize * cancelRequest));
                }
                else
                {
                    // the download succeeded
                    Assert.That(handler.Calls, Is.EqualTo(expectedCalls));
                    Assert.NotNull(lastProgress);
                    Assert.Null(lastProgress.Exception);
                    Assert.That(lastProgress.Status, Is.EqualTo(DownloadStatus.Completed));
                    Assert.That(lastProgress.BytesDownloaded, Is.EqualTo(StreamContent.Length));

                    byte[] read = new byte[1000];
                    outputStream.Position = 0;
                    int length = outputStream.Read(read, 0, 1000);
                    Assert.That(Encoding.UTF8.GetString(read, 0, length), Is.EqualTo(MediaContent));
                }
            }
        }