1 // Copyright 2019 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.net.test;
6 
7 import static com.google.common.truth.Truth.assertThat;
8 import static com.google.common.truth.Truth.assertWithMessage;
9 
10 import static org.junit.Assert.assertThrows;
11 
12 import static org.chromium.net.truth.UrlResponseInfoSubject.assertThat;
13 
14 import android.os.ConditionVariable;
15 
16 import androidx.test.core.app.ApplicationProvider;
17 import androidx.test.ext.junit.runners.AndroidJUnit4;
18 import androidx.test.filters.SmallTest;
19 
20 import com.google.common.collect.Maps;
21 
22 import org.junit.After;
23 import org.junit.Before;
24 import org.junit.Test;
25 import org.junit.runner.RunWith;
26 
27 import org.chromium.net.CronetEngine;
28 import org.chromium.net.CronetException;
29 import org.chromium.net.InlineExecutionProhibitedException;
30 import org.chromium.net.TestUploadDataProvider;
31 import org.chromium.net.TestUrlRequestCallback;
32 import org.chromium.net.TestUrlRequestCallback.ResponseStep;
33 import org.chromium.net.UploadDataProvider;
34 import org.chromium.net.UploadDataSink;
35 import org.chromium.net.UrlRequest;
36 import org.chromium.net.UrlRequest.Status;
37 import org.chromium.net.UrlRequest.StatusListener;
38 import org.chromium.net.UrlResponseInfo;
39 import org.chromium.net.apihelpers.UploadDataProviders;
40 
41 import java.io.IOException;
42 import java.net.URI;
43 import java.nio.ByteBuffer;
44 import java.util.ArrayList;
45 import java.util.Arrays;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.Objects;
49 import java.util.concurrent.Executor;
50 import java.util.concurrent.Executors;
51 import java.util.concurrent.atomic.AtomicBoolean;
52 
53 /** Test functionality of FakeUrlRequest. */
54 @RunWith(AndroidJUnit4.class)
55 public class FakeUrlRequestTest {
56     private CronetEngine mFakeCronetEngine;
57     private FakeCronetController mFakeCronetController;
58 
checkStatus(FakeUrlRequest request, final int expectedStatus)59     private static void checkStatus(FakeUrlRequest request, final int expectedStatus) {
60         ConditionVariable foundStatus = new ConditionVariable();
61         request.getStatus(
62                 new StatusListener() {
63                     @Override
64                     public void onStatus(int status) {
65                         assertThat(status).isEqualTo(expectedStatus);
66                         foundStatus.open();
67                     }
68                 });
69         foundStatus.block();
70     }
71 
72     // Asserts that read happens a specified number of times
assertReadCalled( int numberOfTimes, TestUrlRequestCallback callback, UrlRequest request)73     private static void assertReadCalled(
74             int numberOfTimes, TestUrlRequestCallback callback, UrlRequest request) {
75         for (int i = 1; i <= numberOfTimes; i++) {
76             callback.startNextRead(request);
77             callback.waitForNextStep();
78             assertWithMessage(
79                             "Expected read to happen "
80                                     + numberOfTimes
81                                     + " times but got "
82                                     + i
83                                     + " times.")
84                     .that(callback.mResponseStep)
85                     .isEqualTo(ResponseStep.ON_READ_COMPLETED);
86         }
87     }
88 
89     private static class EchoBodyResponseMatcher implements ResponseMatcher {
90         private final String mUrl;
91 
EchoBodyResponseMatcher(String url)92         EchoBodyResponseMatcher(String url) {
93             mUrl = url;
94         }
95 
EchoBodyResponseMatcher()96         EchoBodyResponseMatcher() {
97             this(null);
98         }
99 
100         @Override
getMatchingResponse( String url, String httpMethod, List<Map.Entry<String, String>> headers, byte[] body)101         public FakeUrlResponse getMatchingResponse(
102                 String url,
103                 String httpMethod,
104                 List<Map.Entry<String, String>> headers,
105                 byte[] body) {
106             if (mUrl == null || mUrl.equals(url)) {
107                 return new FakeUrlResponse.Builder().setResponseBody(body).build();
108             }
109             return null;
110         }
111     }
112 
113     @Before
setUp()114     public void setUp() {
115         mFakeCronetController = new FakeCronetController();
116         mFakeCronetEngine =
117                 mFakeCronetController
118                         .newFakeCronetEngineBuilder(ApplicationProvider.getApplicationContext())
119                         .build();
120     }
121 
122     @After
tearDown()123     public void tearDown() {
124         mFakeCronetEngine.shutdown();
125     }
126 
127     @Test
128     @SmallTest
testDefaultResponse()129     public void testDefaultResponse() {
130         // Setup the basic response.
131         String responseText = "response text";
132         FakeUrlResponse response =
133                 new FakeUrlResponse.Builder().setResponseBody(responseText.getBytes()).build();
134         String url = "www.response.com";
135         mFakeCronetController.addResponseForUrl(response, url);
136 
137         // Run the request.
138         TestUrlRequestCallback callback = new TestUrlRequestCallback();
139         callback.setAutoAdvance(false);
140         FakeUrlRequest request =
141                 (FakeUrlRequest)
142                         mFakeCronetEngine
143                                 .newUrlRequestBuilder(url, callback, callback.getExecutor())
144                                 .build();
145         request.start();
146         callback.waitForNextStep();
147 
148         // Verify correct callback methods called and correct response returned.
149         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
150         callback.startNextRead(request);
151         callback.waitForNextStep();
152 
153         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_READ_COMPLETED);
154         callback.startNextRead(request);
155         callback.waitForNextStep();
156 
157         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
158         callback.blockForDone();
159 
160         assertThat(responseText).isEqualTo(callback.mResponseAsString);
161     }
162 
163     @Test
164     @SmallTest
testBuilderChecks()165     public void testBuilderChecks() {
166         TestUrlRequestCallback callback = new TestUrlRequestCallback();
167         NullPointerException e =
168                 assertThrows(
169                         NullPointerException.class,
170                         () ->
171                                 mFakeCronetEngine.newUrlRequestBuilder(
172                                         null, callback, callback.getExecutor()));
173         assertThat(e).hasMessageThat().isEqualTo("URL is required.");
174 
175         e =
176                 assertThrows(
177                         NullPointerException.class,
178                         () ->
179                                 mFakeCronetEngine.newUrlRequestBuilder(
180                                         "url", null, callback.getExecutor()));
181         assertThat(e).hasMessageThat().isEqualTo("Callback is required.");
182 
183         e =
184                 assertThrows(
185                         NullPointerException.class,
186                         () -> mFakeCronetEngine.newUrlRequestBuilder("url", callback, null));
187         assertThat(e).hasMessageThat().isEqualTo("Executor is required.");
188         // Verify successful creation doesn't throw.
189         mFakeCronetEngine.newUrlRequestBuilder("url", callback, callback.getExecutor());
190     }
191 
192     @Test
193     @SmallTest
testSetHttpMethodWhenNullFails()194     public void testSetHttpMethodWhenNullFails() {
195         TestUrlRequestCallback callback = new TestUrlRequestCallback();
196         FakeUrlRequest request =
197                 (FakeUrlRequest)
198                         mFakeCronetEngine
199                                 .newUrlRequestBuilder("url", callback, callback.getExecutor())
200                                 .build();
201         // Check exception thrown for null method.
202         NullPointerException e =
203                 assertThrows(NullPointerException.class, () -> request.setHttpMethod(null));
204         assertThat(e).hasMessageThat().isEqualTo("Method is required.");
205     }
206 
207     @Test
208     @SmallTest
testSetHttpMethodWhenInvalidFails()209     public void testSetHttpMethodWhenInvalidFails() {
210         TestUrlRequestCallback callback = new TestUrlRequestCallback();
211         FakeUrlRequest request =
212                 (FakeUrlRequest)
213                         mFakeCronetEngine
214                                 .newUrlRequestBuilder("url", callback, callback.getExecutor())
215                                 .build();
216 
217         // Check exception thrown for invalid method.
218         String method = "BADMETHOD";
219         IllegalArgumentException e =
220                 assertThrows(IllegalArgumentException.class, () -> request.setHttpMethod(method));
221         assertThat(e).hasMessageThat().isEqualTo("Invalid http method: " + method);
222     }
223 
224     @Test
225     @SmallTest
testSetHttpMethodSetsMethodToCorrectMethod()226     public void testSetHttpMethodSetsMethodToCorrectMethod() {
227         TestUrlRequestCallback callback = new TestUrlRequestCallback();
228         FakeUrlRequest request =
229                 (FakeUrlRequest)
230                         mFakeCronetEngine
231                                 .newUrlRequestBuilder("url", callback, callback.getExecutor())
232                                 .build();
233         String testMethod = "PUT";
234         // Use an atomic because it is set in an inner class. We do not actually need atomic for a
235         // multi-threaded operation here.
236         AtomicBoolean foundMethod = new AtomicBoolean();
237 
238         mFakeCronetController.addResponseMatcher(
239                 new ResponseMatcher() {
240                     @Override
241                     public FakeUrlResponse getMatchingResponse(
242                             String url,
243                             String httpMethod,
244                             List<Map.Entry<String, String>> headers,
245                             byte[] body) {
246                         assertThat(httpMethod).isEqualTo(testMethod);
247                         foundMethod.set(true);
248                         // It doesn't matter if a response is actually returned.
249                         return null;
250                     }
251                 });
252 
253         // Check no exception for correct method.
254         request.setHttpMethod(testMethod);
255 
256         // Run the request so that the ResponseMatcher we set is checked.
257         request.start();
258         callback.blockForDone();
259 
260         assertThat(foundMethod.get()).isTrue();
261     }
262 
263     @Test
264     @SmallTest
testAddHeader()265     public void testAddHeader() {
266         TestUrlRequestCallback callback = new TestUrlRequestCallback();
267         FakeUrlRequest request =
268                 (FakeUrlRequest)
269                         mFakeCronetEngine
270                                 .newUrlRequestBuilder("TEST_URL", callback, callback.getExecutor())
271                                 .build();
272         String headerKey = "HEADERNAME";
273         String headerValue = "HEADERVALUE";
274         request.addHeader(headerKey, headerValue);
275         // Use an atomic because it is set in an inner class. We do not actually need atomic for a
276         // multi-threaded operation here.
277         AtomicBoolean foundEntry = new AtomicBoolean();
278         mFakeCronetController.addResponseMatcher(
279                 new ResponseMatcher() {
280                     @Override
281                     public FakeUrlResponse getMatchingResponse(
282                             String url,
283                             String httpMethod,
284                             List<Map.Entry<String, String>> headers,
285                             byte[] body) {
286                         assertThat(headers)
287                                 .containsExactly(Maps.immutableEntry(headerKey, headerValue));
288                         foundEntry.set(true);
289                         // It doesn't matter if a response is actually returned.
290                         return null;
291                     }
292                 });
293         // Run the request so that the ResponseMatcher we set is checked.
294         request.start();
295         callback.blockForDone();
296 
297         assertThat(foundEntry.get()).isTrue();
298     }
299 
300     @Test
301     @SmallTest
testRequestDoesNotStartWhenEngineShutDown()302     public void testRequestDoesNotStartWhenEngineShutDown() {
303         TestUrlRequestCallback callback = new TestUrlRequestCallback();
304         FakeUrlRequest request =
305                 (FakeUrlRequest)
306                         mFakeCronetEngine
307                                 .newUrlRequestBuilder("TEST_URL", callback, callback.getExecutor())
308                                 .build();
309 
310         mFakeCronetEngine.shutdown();
311         IllegalStateException e = assertThrows(IllegalStateException.class, request::start);
312         assertThat(e)
313                 .hasMessageThat()
314                 .isEqualTo("This request's CronetEngine is already shutdown.");
315     }
316 
317     @Test
318     @SmallTest
testRequestStopsWhenCanceled()319     public void testRequestStopsWhenCanceled() {
320         TestUrlRequestCallback callback = new TestUrlRequestCallback();
321         FakeUrlRequest request =
322                 (FakeUrlRequest)
323                         mFakeCronetEngine
324                                 .newUrlRequestBuilder("TEST_URL", callback, callback.getExecutor())
325                                 .build();
326         callback.setAutoAdvance(false);
327         request.start();
328         callback.waitForNextStep();
329         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
330 
331         request.cancel();
332         callback.waitForNextStep();
333         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_CANCELED);
334         assertThat(callback.mResponseAsString).isEmpty();
335     }
336 
337     @Test
338     @SmallTest
testRecievedByteCountInUrlResponseInfoIsEqualToResponseLength()339     public void testRecievedByteCountInUrlResponseInfoIsEqualToResponseLength() {
340         // Setup the basic response.
341         String responseText = "response text";
342         FakeUrlResponse response =
343                 new FakeUrlResponse.Builder().setResponseBody(responseText.getBytes()).build();
344         String url = "TEST_URL";
345         mFakeCronetController.addResponseForUrl(response, url);
346 
347         // Run the request.
348         TestUrlRequestCallback callback = new TestUrlRequestCallback();
349         FakeUrlRequest request =
350                 (FakeUrlRequest)
351                         mFakeCronetEngine
352                                 .newUrlRequestBuilder(url, callback, callback.getExecutor())
353                                 .build();
354         request.start();
355         callback.blockForDone();
356 
357         assertThat(callback.getResponseInfoWithChecks())
358                 .hasReceivedByteCountThat()
359                 .isEqualTo(responseText.length());
360     }
361 
362     @Test
363     @SmallTest
testRedirectResponse()364     public void testRedirectResponse() {
365         // Setup the basic response.
366         String responseText = "response text";
367         String redirectLocation = "/redirect_location";
368         FakeUrlResponse response =
369                 new FakeUrlResponse.Builder()
370                         .setResponseBody(responseText.getBytes())
371                         .addHeader("location", redirectLocation)
372                         .setHttpStatusCode(300)
373                         .build();
374 
375         String url = "TEST_URL";
376         mFakeCronetController.addResponseForUrl(response, url);
377 
378         String redirectText = "redirect text";
379         FakeUrlResponse redirectToResponse =
380                 new FakeUrlResponse.Builder().setResponseBody(redirectText.getBytes()).build();
381         String redirectUrl = URI.create(url).resolve(redirectLocation).toString();
382 
383         mFakeCronetController.addResponseForUrl(redirectToResponse, redirectUrl);
384 
385         // Run the request.
386         TestUrlRequestCallback callback = new TestUrlRequestCallback();
387         FakeUrlRequest request =
388                 (FakeUrlRequest)
389                         mFakeCronetEngine
390                                 .newUrlRequestBuilder(url, callback, callback.getExecutor())
391                                 .build();
392         request.start();
393         callback.blockForDone();
394 
395         // Verify response from redirected URL is returned.
396         assertThat(callback.mResponseAsString).isEqualTo(redirectText);
397     }
398 
399     @Test
400     @SmallTest
testRedirectResponseWithNoHeaderFails()401     public void testRedirectResponseWithNoHeaderFails() {
402         // Setup the basic response.
403         String responseText = "response text";
404         FakeUrlResponse response =
405                 new FakeUrlResponse.Builder()
406                         .setResponseBody(responseText.getBytes())
407                         .setHttpStatusCode(300)
408                         .build();
409 
410         String url = "TEST_URL";
411         mFakeCronetController.addResponseForUrl(response, url);
412 
413         // Run the request.
414         TestUrlRequestCallback callback = new TestUrlRequestCallback();
415         FakeUrlRequest request =
416                 (FakeUrlRequest)
417                         mFakeCronetEngine
418                                 .newUrlRequestBuilder(url, callback, callback.getExecutor())
419                                 .build();
420         request.start();
421         callback.blockForDone();
422 
423         // Verify response from redirected URL is returned.
424         assertThat(callback.mResponseStep).isEqualTo(TestUrlRequestCallback.ResponseStep.ON_FAILED);
425     }
426 
427     @Test
428     @SmallTest
testResponseLongerThanBuffer()429     public void testResponseLongerThanBuffer() {
430         // Build a long response string that is 3x the buffer size.
431         final int bufferStringLengthMultiplier = 3;
432         TestUrlRequestCallback callback = new TestUrlRequestCallback();
433         callback.setAutoAdvance(false);
434         String longResponseString =
435                 new String(new char[callback.mReadBufferSize * bufferStringLengthMultiplier]);
436 
437         String longResponseUrl = "https://www.longResponseUrl.com";
438 
439         FakeUrlResponse reallyLongResponse =
440                 new FakeUrlResponse.Builder()
441                         .setResponseBody(longResponseString.getBytes())
442                         .build();
443         mFakeCronetController.addResponseForUrl(reallyLongResponse, longResponseUrl);
444 
445         FakeUrlRequest request =
446                 (FakeUrlRequest)
447                         mFakeCronetEngine
448                                 .newUrlRequestBuilder(
449                                         longResponseUrl, callback, callback.getExecutor())
450                                 .build();
451         request.start();
452         callback.waitForNextStep();
453         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
454 
455         // Asserts that read happens bufferStringLengthMultiplier times
456         assertReadCalled(bufferStringLengthMultiplier, callback, request);
457 
458         callback.startNextRead(request);
459         callback.waitForNextStep();
460         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
461         assertThat(Objects.equals(callback.mResponseAsString, longResponseString)).isTrue();
462     }
463 
464     @Test
465     @SmallTest
testStatusInvalidBeforeStart()466     public void testStatusInvalidBeforeStart() {
467         TestUrlRequestCallback callback = new TestUrlRequestCallback();
468         FakeUrlRequest request =
469                 (FakeUrlRequest)
470                         mFakeCronetEngine
471                                 .newUrlRequestBuilder("URL", callback, callback.getExecutor())
472                                 .build();
473 
474         checkStatus(request, Status.INVALID);
475         request.start();
476         callback.blockForDone();
477     }
478 
479     @Test
480     @SmallTest
testStatusIdleWhenWaitingForRead()481     public void testStatusIdleWhenWaitingForRead() {
482         TestUrlRequestCallback callback = new TestUrlRequestCallback();
483         callback.setAutoAdvance(false);
484         FakeUrlRequest request =
485                 (FakeUrlRequest)
486                         mFakeCronetEngine
487                                 .newUrlRequestBuilder("URL", callback, callback.getExecutor())
488                                 .build();
489         request.start();
490         checkStatus(request, Status.IDLE);
491         callback.setAutoAdvance(true);
492         callback.startNextRead(request);
493         callback.blockForDone();
494     }
495 
496     @Test
497     @SmallTest
testStatusIdleWhenWaitingForRedirect()498     public void testStatusIdleWhenWaitingForRedirect() {
499         TestUrlRequestCallback callback = new TestUrlRequestCallback();
500         callback.setAutoAdvance(false);
501         String initialURL = "initialURL";
502         String secondURL = "secondURL";
503         mFakeCronetController.addRedirectResponse(secondURL, initialURL);
504         FakeUrlRequest request =
505                 (FakeUrlRequest)
506                         mFakeCronetEngine
507                                 .newUrlRequestBuilder(initialURL, callback, callback.getExecutor())
508                                 .build();
509 
510         request.start();
511         callback.waitForNextStep();
512         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RECEIVED_REDIRECT);
513         checkStatus(request, Status.IDLE);
514         callback.setAutoAdvance(true);
515         request.followRedirect();
516         callback.blockForDone();
517     }
518 
519     @Test
520     @SmallTest
testStatusInvalidWhenDone()521     public void testStatusInvalidWhenDone() {
522         TestUrlRequestCallback callback = new TestUrlRequestCallback();
523         FakeUrlRequest request =
524                 (FakeUrlRequest)
525                         mFakeCronetEngine
526                                 .newUrlRequestBuilder("URL", callback, callback.getExecutor())
527                                 .build();
528         request.start();
529         callback.blockForDone();
530         checkStatus(request, Status.INVALID);
531     }
532 
533     @Test
534     @SmallTest
testIsDoneWhenComplete()535     public void testIsDoneWhenComplete() {
536         TestUrlRequestCallback callback = new TestUrlRequestCallback();
537         FakeUrlRequest request =
538                 (FakeUrlRequest)
539                         mFakeCronetEngine
540                                 .newUrlRequestBuilder("", callback, callback.getExecutor())
541                                 .build();
542 
543         request.start();
544         callback.blockForDone();
545 
546         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
547         assertThat(request.isDone()).isTrue();
548     }
549 
550     @Test
551     @SmallTest
testSetUploadDataProviderAfterStart()552     public void testSetUploadDataProviderAfterStart() {
553         TestUrlRequestCallback callback = new TestUrlRequestCallback();
554         FakeUrlRequest request =
555                 (FakeUrlRequest)
556                         mFakeCronetEngine
557                                 .newUrlRequestBuilder("", callback, callback.getExecutor())
558                                 .addHeader("Content-Type", "useless/string")
559                                 .build();
560         String body = "body";
561         request.setUploadDataProvider(
562                 UploadDataProviders.create(body.getBytes()), callback.getExecutor());
563         request.start();
564         // Must wait for the request to prevent a race in the State since it is reported in the
565         // error.
566         callback.blockForDone();
567 
568         IllegalStateException e =
569                 assertThrows(
570                         IllegalStateException.class,
571                         () ->
572                                 request.setUploadDataProvider(
573                                         UploadDataProviders.create(body.getBytes()),
574                                         callback.getExecutor()));
575         assertThat(e).hasMessageThat().isEqualTo("Request is already started. State is: 7");
576     }
577 
578     @Test
579     @SmallTest
testUrlChainIsCorrectForSuccessRequest()580     public void testUrlChainIsCorrectForSuccessRequest() {
581         TestUrlRequestCallback callback = new TestUrlRequestCallback();
582         String testUrl = "TEST_URL";
583         List<String> expectedUrlChain = new ArrayList<>();
584         expectedUrlChain.add(testUrl);
585         FakeUrlRequest request =
586                 (FakeUrlRequest)
587                         mFakeCronetEngine
588                                 .newUrlRequestBuilder(testUrl, callback, callback.getExecutor())
589                                 .build();
590         request.start();
591         callback.blockForDone();
592 
593         assertThat(callback.getResponseInfoWithChecks())
594                 .hasUrlChainThat()
595                 .isEqualTo(expectedUrlChain);
596     }
597 
598     @Test
599     @SmallTest
testUrlChainIsCorrectForRedirectRequest()600     public void testUrlChainIsCorrectForRedirectRequest() {
601         TestUrlRequestCallback callback = new TestUrlRequestCallback();
602         String testUrl1 = "TEST_URL1";
603         String testUrl2 = "TEST_URL2";
604         mFakeCronetController.addRedirectResponse(testUrl2, testUrl1);
605         List<String> expectedUrlChain = new ArrayList<>();
606         expectedUrlChain.add(testUrl1);
607         expectedUrlChain.add(testUrl2);
608         FakeUrlRequest request =
609                 (FakeUrlRequest)
610                         mFakeCronetEngine
611                                 .newUrlRequestBuilder(testUrl1, callback, callback.getExecutor())
612                                 .build();
613         request.start();
614         callback.blockForDone();
615 
616         assertThat(callback.getResponseInfoWithChecks())
617                 .hasUrlChainThat()
618                 .isEqualTo(expectedUrlChain);
619     }
620 
621     @Test
622     @SmallTest
testResponseCodeCorrect()623     public void testResponseCodeCorrect() {
624         TestUrlRequestCallback callback = new TestUrlRequestCallback();
625         String testUrl = "TEST_URL";
626         int expectedResponseCode = 208;
627         mFakeCronetController.addResponseForUrl(
628                 new FakeUrlResponse.Builder().setHttpStatusCode(expectedResponseCode).build(),
629                 testUrl);
630         FakeUrlRequest request =
631                 (FakeUrlRequest)
632                         mFakeCronetEngine
633                                 .newUrlRequestBuilder(testUrl, callback, callback.getExecutor())
634                                 .build();
635         request.start();
636         callback.blockForDone();
637 
638         assertThat(callback.getResponseInfoWithChecks())
639                 .hasHttpStatusCodeThat()
640                 .isEqualTo(expectedResponseCode);
641     }
642 
643     @Test
644     @SmallTest
testResponseTextCorrect()645     public void testResponseTextCorrect() {
646         TestUrlRequestCallback callback = new TestUrlRequestCallback();
647         String testUrl = "TEST_URL";
648         int expectedResponseCode = 208;
649         String expectedResponseText = "Already Reported";
650         mFakeCronetController.addResponseForUrl(
651                 new FakeUrlResponse.Builder().setHttpStatusCode(expectedResponseCode).build(),
652                 testUrl);
653         FakeUrlRequest request =
654                 (FakeUrlRequest)
655                         mFakeCronetEngine
656                                 .newUrlRequestBuilder(testUrl, callback, callback.getExecutor())
657                                 .build();
658         request.start();
659         callback.blockForDone();
660 
661         assertThat(callback.getResponseInfoWithChecks())
662                 .hasHttpStatusTextThat()
663                 .isEqualTo(expectedResponseText);
664     }
665 
666     @Test
667     @SmallTest
testResponseWasCachedCorrect()668     public void testResponseWasCachedCorrect() {
669         TestUrlRequestCallback callback = new TestUrlRequestCallback();
670         String testUrl = "TEST_URL";
671         mFakeCronetController.addResponseForUrl(
672                 new FakeUrlResponse.Builder().setWasCached(true).build(), testUrl);
673         FakeUrlRequest request =
674                 (FakeUrlRequest)
675                         mFakeCronetEngine
676                                 .newUrlRequestBuilder(testUrl, callback, callback.getExecutor())
677                                 .build();
678         request.start();
679         callback.blockForDone();
680 
681         assertThat(callback.getResponseInfoWithChecks()).wasCached();
682     }
683 
684     @Test
685     @SmallTest
testResponseNegotiatedProtocolCorrect()686     public void testResponseNegotiatedProtocolCorrect() {
687         TestUrlRequestCallback callback = new TestUrlRequestCallback();
688         String testUrl = "TEST_URL";
689         String expectedNegotiatedProtocol = "TEST_NEGOTIATED_PROTOCOL";
690         mFakeCronetController.addResponseForUrl(
691                 new FakeUrlResponse.Builder()
692                         .setNegotiatedProtocol(expectedNegotiatedProtocol)
693                         .build(),
694                 testUrl);
695         FakeUrlRequest request =
696                 (FakeUrlRequest)
697                         mFakeCronetEngine
698                                 .newUrlRequestBuilder(testUrl, callback, callback.getExecutor())
699                                 .build();
700         request.start();
701         callback.blockForDone();
702 
703         assertThat(callback.getResponseInfoWithChecks())
704                 .hasNegotiatedProtocolThat()
705                 .isEqualTo(expectedNegotiatedProtocol);
706     }
707 
708     @Test
709     @SmallTest
testResponseProxyServerCorrect()710     public void testResponseProxyServerCorrect() {
711         TestUrlRequestCallback callback = new TestUrlRequestCallback();
712         String testUrl = "TEST_URL";
713         String expectedProxyServer = "TEST_PROXY_SERVER";
714         mFakeCronetController.addResponseForUrl(
715                 new FakeUrlResponse.Builder().setProxyServer(expectedProxyServer).build(), testUrl);
716         FakeUrlRequest request =
717                 (FakeUrlRequest)
718                         mFakeCronetEngine
719                                 .newUrlRequestBuilder(testUrl, callback, callback.getExecutor())
720                                 .build();
721         request.start();
722         callback.blockForDone();
723 
724         assertThat(callback.getResponseInfoWithChecks())
725                 .hasProxyServerThat()
726                 .isEqualTo(expectedProxyServer);
727     }
728 
729     @Test
730     @SmallTest
testDirectExecutorDisabledByDefault()731     public void testDirectExecutorDisabledByDefault() {
732         TestUrlRequestCallback callback = new TestUrlRequestCallback();
733         callback.setAllowDirectExecutor(true);
734         Executor myExecutor =
735                 new Executor() {
736                     @Override
737                     public void execute(Runnable command) {
738                         command.run();
739                     }
740                 };
741         FakeUrlRequest request =
742                 (FakeUrlRequest)
743                         mFakeCronetEngine.newUrlRequestBuilder("url", callback, myExecutor).build();
744 
745         request.start();
746         callback.blockForDone();
747 
748         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED);
749         // Checks that the exception from {@link DirectPreventingExecutor} was successfully returned
750         // to the callback in the onFailed method.
751         assertThat(callback.mError)
752                 .hasCauseThat()
753                 .isInstanceOf(InlineExecutionProhibitedException.class);
754     }
755 
756     @Test
757     @SmallTest
testLotsOfCallsToReadDoesntOverflow()758     public void testLotsOfCallsToReadDoesntOverflow() {
759         TestUrlRequestCallback callback = new TestUrlRequestCallback();
760         callback.setAutoAdvance(false);
761         // Make the buffer size small so there are lots of calls to read().
762         callback.mReadBufferSize = 1;
763         String testUrl = "TEST_URL";
764         int responseLength = 1024;
765         byte[] byteArray = new byte[responseLength];
766         Arrays.fill(byteArray, (byte) 1);
767         String longResponseString = new String(byteArray);
768         mFakeCronetController.addResponseForUrl(
769                 new FakeUrlResponse.Builder()
770                         .setResponseBody(longResponseString.getBytes())
771                         .build(),
772                 testUrl);
773         FakeUrlRequest request =
774                 (FakeUrlRequest)
775                         mFakeCronetEngine
776                                 .newUrlRequestBuilder(testUrl, callback, callback.getExecutor())
777                                 .allowDirectExecutor()
778                                 .build();
779         request.start();
780         callback.waitForNextStep();
781         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
782 
783         // Asserts that read happens responseLength times
784         assertReadCalled(responseLength, callback, request);
785 
786         callback.startNextRead(request);
787         callback.waitForNextStep();
788         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
789         assertThat(callback.mResponseAsString).isEqualTo(longResponseString);
790     }
791 
792     @Test
793     @SmallTest
testLotsOfCallsToReadDoesntOverflowWithDirectExecutor()794     public void testLotsOfCallsToReadDoesntOverflowWithDirectExecutor() {
795         TestUrlRequestCallback callback = new TestUrlRequestCallback();
796         callback.setAutoAdvance(false);
797         callback.setAllowDirectExecutor(true);
798         // Make the buffer size small so there are lots of calls to read().
799         callback.mReadBufferSize = 1;
800         String testUrl = "TEST_URL";
801         int responseLength = 1024;
802         byte[] byteArray = new byte[responseLength];
803         Arrays.fill(byteArray, (byte) 1);
804         String longResponseString = new String(byteArray);
805         Executor myExecutor =
806                 new Executor() {
807                     @Override
808                     public void execute(Runnable command) {
809                         command.run();
810                     }
811                 };
812         mFakeCronetController.addResponseForUrl(
813                 new FakeUrlResponse.Builder()
814                         .setResponseBody(longResponseString.getBytes())
815                         .build(),
816                 testUrl);
817         FakeUrlRequest request =
818                 (FakeUrlRequest)
819                         mFakeCronetEngine
820                                 .newUrlRequestBuilder(testUrl, callback, myExecutor)
821                                 .allowDirectExecutor()
822                                 .build();
823         request.start();
824         callback.waitForNextStep();
825         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
826 
827         // Asserts that read happens buffer length x multiplier times
828         assertReadCalled(responseLength, callback, request);
829 
830         callback.startNextRead(request);
831         callback.waitForNextStep();
832         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
833         assertThat(callback.mResponseAsString).isEqualTo(longResponseString);
834     }
835 
836     @Test
837     @SmallTest
testDoubleReadFails()838     public void testDoubleReadFails() throws Exception {
839         UrlRequest.Callback callback = new StubCallback();
840         FakeUrlRequest request =
841                 (FakeUrlRequest)
842                         mFakeCronetEngine
843                                 .newUrlRequestBuilder(
844                                         "url", callback, Executors.newSingleThreadExecutor())
845                                 .build();
846         ByteBuffer buffer = ByteBuffer.allocateDirect(32 * 1024);
847 
848         request.start();
849         request.read(buffer);
850 
851         IllegalStateException e =
852                 assertThrows(IllegalStateException.class, () -> request.read(buffer));
853         assertThat(e).hasMessageThat().isEqualTo("Invalid state transition - expected 4 but was 7");
854     }
855 
856     @Test
857     @SmallTest
testReadWhileRedirectingFails()858     public void testReadWhileRedirectingFails() {
859         TestUrlRequestCallback callback = new TestUrlRequestCallback();
860         callback.setAutoAdvance(false);
861         String url = "url";
862         FakeUrlRequest request =
863                 (FakeUrlRequest)
864                         mFakeCronetEngine
865                                 .newUrlRequestBuilder(url, callback, callback.getExecutor())
866                                 .build();
867         mFakeCronetController.addRedirectResponse("location", url);
868         request.start();
869         IllegalStateException e =
870                 assertThrows(IllegalStateException.class, () -> callback.startNextRead(request));
871         assertThat(e).hasMessageThat().isEqualTo("Invalid state transition - expected 4 but was 3");
872         callback.waitForNextStep();
873         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RECEIVED_REDIRECT);
874         callback.setAutoAdvance(true);
875         request.followRedirect();
876         callback.blockForDone();
877     }
878 
879     @Test
880     @SmallTest
testShuttingDownCronetEngineWithActiveRequestFails()881     public void testShuttingDownCronetEngineWithActiveRequestFails() {
882         TestUrlRequestCallback callback = new TestUrlRequestCallback();
883         callback.setAutoAdvance(false);
884         String url = "url";
885         FakeUrlRequest request =
886                 (FakeUrlRequest)
887                         mFakeCronetEngine
888                                 .newUrlRequestBuilder(url, callback, callback.getExecutor())
889                                 .build();
890         request.start();
891         IllegalStateException e =
892                 assertThrows(IllegalStateException.class, mFakeCronetEngine::shutdown);
893         assertThat(e).hasMessageThat().isEqualTo("Cannot shutdown with running requests.");
894         callback.waitForNextStep();
895         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED);
896         callback.setAutoAdvance(true);
897         callback.startNextRead(request);
898         callback.blockForDone();
899         mFakeCronetEngine.shutdown();
900     }
901 
902     @Test
903     @SmallTest
testDefaultResponseIs404()904     public void testDefaultResponseIs404() {
905         TestUrlRequestCallback callback = new TestUrlRequestCallback();
906         String url = "url";
907         FakeUrlRequest request =
908                 (FakeUrlRequest)
909                         mFakeCronetEngine
910                                 .newUrlRequestBuilder(url, callback, callback.getExecutor())
911                                 .build();
912 
913         request.start();
914         callback.blockForDone();
915 
916         assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(404);
917     }
918 
919     @Test
920     @SmallTest
testUploadSetDataProviderChecksForNullUploadDataProvider()921     public void testUploadSetDataProviderChecksForNullUploadDataProvider() throws Exception {
922         TestUrlRequestCallback callback = new TestUrlRequestCallback();
923         String url = "url";
924         FakeUrlRequest.Builder builder =
925                 (FakeUrlRequest.Builder)
926                         mFakeCronetEngine.newUrlRequestBuilder(
927                                 url, callback, callback.getExecutor());
928 
929         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
930 
931         NullPointerException e =
932                 assertThrows(
933                         NullPointerException.class,
934                         () -> builder.setUploadDataProvider(null, callback.getExecutor()));
935         assertThat(e).hasMessageThat().isEqualTo("Invalid UploadDataProvider.");
936     }
937 
938     @Test
939     @SmallTest
testUploadSetDataProviderChecksForContentTypeHeader()940     public void testUploadSetDataProviderChecksForContentTypeHeader() throws Exception {
941         TestUrlRequestCallback callback = new TestUrlRequestCallback();
942         String url = "url";
943         FakeUrlRequest.Builder builder =
944                 (FakeUrlRequest.Builder)
945                         mFakeCronetEngine.newUrlRequestBuilder(
946                                 url, callback, callback.getExecutor());
947 
948         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
949 
950         TestUploadDataProvider dataProvider =
951                 new TestUploadDataProvider(
952                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
953         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
954         IllegalArgumentException e =
955                 assertThrows(IllegalArgumentException.class, () -> builder.build().start());
956         assertThat(e)
957                 .hasMessageThat()
958                 .isEqualTo("Requests with upload data must have a Content-Type.");
959     }
960 
961     @Test
962     @SmallTest
testUploadWithEmptyBody()963     public void testUploadWithEmptyBody() throws Exception {
964         TestUrlRequestCallback callback = new TestUrlRequestCallback();
965         String url = "url";
966         FakeUrlRequest.Builder builder =
967                 (FakeUrlRequest.Builder)
968                         mFakeCronetEngine.newUrlRequestBuilder(
969                                 url, callback, callback.getExecutor());
970         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
971         TestUploadDataProvider dataProvider =
972                 new TestUploadDataProvider(
973                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
974         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
975         builder.addHeader("Content-Type", "useless/string");
976         builder.build().start();
977         callback.blockForDone();
978 
979         assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200);
980         assertThat(callback.mResponseAsString).isEmpty();
981         dataProvider.assertClosed();
982     }
983 
984     @Test
985     @SmallTest
testUploadSync()986     public void testUploadSync() throws Exception {
987         TestUrlRequestCallback callback = new TestUrlRequestCallback();
988         String url = "url";
989         String body = "test";
990         FakeUrlRequest.Builder builder =
991                 (FakeUrlRequest.Builder)
992                         mFakeCronetEngine.newUrlRequestBuilder(
993                                 url, callback, callback.getExecutor());
994         TestUploadDataProvider dataProvider =
995                 new TestUploadDataProvider(
996                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
997 
998         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
999         dataProvider.addRead(body.getBytes());
1000         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1001         builder.addHeader("Content-Type", "useless/string");
1002         builder.build().start();
1003         callback.blockForDone();
1004         dataProvider.assertClosed();
1005 
1006         assertThat(dataProvider.getUploadedLength()).isEqualTo(4);
1007         assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
1008         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
1009 
1010         assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200);
1011         assertThat(callback.mResponseAsString).isEqualTo("test");
1012     }
1013 
1014     @Test
1015     @SmallTest
testUploadSyncReadWrongState()1016     public void testUploadSyncReadWrongState() throws Exception {
1017         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1018         String url = "url";
1019         String body = "test";
1020         callback.setAutoAdvance(false);
1021         FakeUrlRequest.Builder builder =
1022                 (FakeUrlRequest.Builder)
1023                         mFakeCronetEngine.newUrlRequestBuilder(
1024                                 url, callback, callback.getExecutor());
1025         TestUploadDataProvider dataProvider =
1026                 new TestUploadDataProvider(
1027                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1028 
1029         // Add a redirect response so the request keeps the UploadDataProvider open while waiting
1030         // to follow the redirect.
1031         mFakeCronetController.addRedirectResponse("newUrl", url);
1032         dataProvider.addRead(body.getBytes());
1033         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1034         builder.addHeader("Content-Type", "useless/string");
1035         FakeUrlRequest request = (FakeUrlRequest) builder.build();
1036         request.start();
1037         callback.waitForNextStep();
1038 
1039         IllegalStateException e =
1040                 assertThrows(
1041                         IllegalStateException.class,
1042                         () -> {
1043                             synchronized (request.mLock) {
1044                                 request.mFakeDataSink.onReadSucceeded(false);
1045                             }
1046                         });
1047 
1048         assertThat(e)
1049                 .hasMessageThat()
1050                 .isEqualTo("onReadSucceeded() called when not awaiting a read result; in state: 2");
1051         request.cancel();
1052     }
1053 
1054     @Test
1055     @SmallTest
testUploadSyncRewindWrongState()1056     public void testUploadSyncRewindWrongState() throws Exception {
1057         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1058         String url = "url";
1059         String body = "test";
1060         callback.setAutoAdvance(false);
1061         FakeUrlRequest.Builder builder =
1062                 (FakeUrlRequest.Builder)
1063                         mFakeCronetEngine.newUrlRequestBuilder(
1064                                 url, callback, callback.getExecutor());
1065         TestUploadDataProvider dataProvider =
1066                 new TestUploadDataProvider(
1067                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1068 
1069         // Add a redirect response so the request keeps the UploadDataProvider open while waiting
1070         // to follow the redirect.
1071         mFakeCronetController.addRedirectResponse("newUrl", url);
1072         dataProvider.addRead(body.getBytes());
1073         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1074         builder.addHeader("Content-Type", "useless/string");
1075         FakeUrlRequest request = (FakeUrlRequest) builder.build();
1076         request.start();
1077         callback.waitForNextStep();
1078 
1079         IllegalStateException e =
1080                 assertThrows(
1081                         IllegalStateException.class,
1082                         () -> {
1083                             synchronized (request.mLock) {
1084                                 request.mFakeDataSink.onRewindSucceeded();
1085                             }
1086                         });
1087         assertThat(e)
1088                 .hasMessageThat()
1089                 .isEqualTo("onRewindSucceeded() called when not awaiting a rewind; in state: 2");
1090         request.cancel();
1091     }
1092 
1093     @Test
1094     @SmallTest
testUploadMultiplePiecesSync()1095     public void testUploadMultiplePiecesSync() throws Exception {
1096         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1097         String url = "url";
1098         FakeUrlRequest.Builder builder =
1099                 (FakeUrlRequest.Builder)
1100                         mFakeCronetEngine.newUrlRequestBuilder(
1101                                 url, callback, callback.getExecutor());
1102         TestUploadDataProvider dataProvider =
1103                 new TestUploadDataProvider(
1104                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1105 
1106         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1107         dataProvider.addRead("Y".getBytes());
1108         dataProvider.addRead("et ".getBytes());
1109         dataProvider.addRead("another ".getBytes());
1110         dataProvider.addRead("test".getBytes());
1111         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1112         builder.addHeader("Content-Type", "useless/string");
1113         builder.build().start();
1114         callback.blockForDone();
1115         dataProvider.assertClosed();
1116 
1117         assertThat(dataProvider.getUploadedLength()).isEqualTo(16);
1118         assertThat(dataProvider.getNumReadCalls()).isEqualTo(4);
1119         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
1120 
1121         assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200);
1122         assertThat(callback.mResponseAsString).isEqualTo("Yet another test");
1123     }
1124 
1125     @Test
1126     @SmallTest
testUploadMultiplePiecesAsync()1127     public void testUploadMultiplePiecesAsync() throws Exception {
1128         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1129         String url = "url";
1130         FakeUrlRequest.Builder builder =
1131                 (FakeUrlRequest.Builder)
1132                         mFakeCronetEngine.newUrlRequestBuilder(
1133                                 url, callback, callback.getExecutor());
1134         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1135         TestUploadDataProvider dataProvider =
1136                 new TestUploadDataProvider(
1137                         TestUploadDataProvider.SuccessCallbackMode.ASYNC, callback.getExecutor());
1138         dataProvider.addRead("Y".getBytes());
1139         dataProvider.addRead("et ".getBytes());
1140         dataProvider.addRead("another ".getBytes());
1141         dataProvider.addRead("test".getBytes());
1142 
1143         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1144         builder.addHeader("Content-Type", "useless/string");
1145         builder.build().start();
1146         callback.blockForDone();
1147         dataProvider.assertClosed();
1148 
1149         assertThat(dataProvider.getUploadedLength()).isEqualTo(16);
1150         assertThat(dataProvider.getNumReadCalls()).isEqualTo(4);
1151         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
1152 
1153         assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200);
1154         assertThat(callback.mResponseAsString).isEqualTo("Yet another test");
1155     }
1156 
1157     @Test
1158     @SmallTest
testUploadChangesDefaultMethod()1159     public void testUploadChangesDefaultMethod() throws Exception {
1160         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1161         String url = "url";
1162         FakeUrlRequest.Builder builder =
1163                 (FakeUrlRequest.Builder)
1164                         mFakeCronetEngine.newUrlRequestBuilder(
1165                                 url, callback, callback.getExecutor());
1166         mFakeCronetController.addResponseMatcher(
1167                 new ResponseMatcher() {
1168                     @Override
1169                     public FakeUrlResponse getMatchingResponse(
1170                             String url,
1171                             String httpMethod,
1172                             List<Map.Entry<String, String>> headers,
1173                             byte[] body) {
1174                         return new FakeUrlResponse.Builder()
1175                                 .setResponseBody(httpMethod.getBytes())
1176                                 .build();
1177                     }
1178                 });
1179         TestUploadDataProvider dataProvider =
1180                 new TestUploadDataProvider(
1181                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1182         dataProvider.addRead("test".getBytes());
1183         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1184         builder.addHeader("Content-Type", "useless/string");
1185         builder.build().start();
1186         callback.blockForDone();
1187         dataProvider.assertClosed();
1188 
1189         assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200);
1190         assertThat(callback.mResponseAsString).isEqualTo("POST");
1191     }
1192 
1193     @Test
1194     @SmallTest
testUploadWithSetMethod()1195     public void testUploadWithSetMethod() throws Exception {
1196         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1197         String url = "url";
1198         FakeUrlRequest.Builder builder =
1199                 (FakeUrlRequest.Builder)
1200                         mFakeCronetEngine.newUrlRequestBuilder(
1201                                 url, callback, callback.getExecutor());
1202         mFakeCronetController.addResponseMatcher(
1203                 new ResponseMatcher() {
1204                     @Override
1205                     public FakeUrlResponse getMatchingResponse(
1206                             String url,
1207                             String httpMethod,
1208                             List<Map.Entry<String, String>> headers,
1209                             byte[] body) {
1210                         return new FakeUrlResponse.Builder()
1211                                 .setResponseBody(httpMethod.getBytes())
1212                                 .build();
1213                     }
1214                 });
1215         final String method = "PUT";
1216         builder.setHttpMethod(method);
1217 
1218         TestUploadDataProvider dataProvider =
1219                 new TestUploadDataProvider(
1220                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1221         dataProvider.addRead("test".getBytes());
1222         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1223         builder.addHeader("Content-Type", "useless/string");
1224         builder.build().start();
1225         callback.blockForDone();
1226         dataProvider.assertClosed();
1227 
1228         assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200);
1229         assertThat(callback.mResponseAsString).isEqualTo("PUT");
1230     }
1231 
1232     @Test
1233     @SmallTest
testUploadRedirectSync()1234     public void testUploadRedirectSync() throws Exception {
1235         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1236         String redirectUrl = "redirectUrl";
1237         String echoBodyUrl = "echobody";
1238         FakeUrlRequest.Builder builder =
1239                 (FakeUrlRequest.Builder)
1240                         mFakeCronetEngine.newUrlRequestBuilder(
1241                                 redirectUrl, callback, callback.getExecutor());
1242         mFakeCronetController.addRedirectResponse(echoBodyUrl, redirectUrl);
1243         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher(echoBodyUrl));
1244 
1245         TestUploadDataProvider dataProvider =
1246                 new TestUploadDataProvider(
1247                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1248         dataProvider.addRead("test".getBytes());
1249         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1250         builder.addHeader("Content-Type", "useless/string");
1251         builder.build().start();
1252         callback.blockForDone();
1253         dataProvider.assertClosed();
1254 
1255         // 1 read call before the rewind, 1 after.
1256         assertThat(dataProvider.getNumReadCalls()).isEqualTo(2);
1257         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1);
1258 
1259         assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200);
1260         assertThat(callback.mResponseAsString).isEqualTo("test");
1261     }
1262 
1263     @Test
1264     @SmallTest
testUploadRedirectAsync()1265     public void testUploadRedirectAsync() throws Exception {
1266         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1267         String redirectUrl = "redirectUrl";
1268         String echoBodyUrl = "echobody";
1269         FakeUrlRequest.Builder builder =
1270                 (FakeUrlRequest.Builder)
1271                         mFakeCronetEngine.newUrlRequestBuilder(
1272                                 redirectUrl, callback, callback.getExecutor());
1273         mFakeCronetController.addRedirectResponse(echoBodyUrl, redirectUrl);
1274         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher(echoBodyUrl));
1275 
1276         TestUploadDataProvider dataProvider =
1277                 new TestUploadDataProvider(
1278                         TestUploadDataProvider.SuccessCallbackMode.ASYNC, callback.getExecutor());
1279         dataProvider.addRead("test".getBytes());
1280         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1281         builder.addHeader("Content-Type", "useless/string");
1282         builder.build().start();
1283         callback.blockForDone();
1284         dataProvider.assertClosed();
1285 
1286         // 1 read call before the rewind, 1 after.
1287         assertThat(dataProvider.getNumReadCalls()).isEqualTo(2);
1288         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1);
1289 
1290         assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200);
1291         assertThat(callback.mResponseAsString).isEqualTo("test");
1292     }
1293 
1294     @Test
1295     @SmallTest
testUploadWithBadLength()1296     public void testUploadWithBadLength() throws Exception {
1297         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1298         String url = "url";
1299         FakeUrlRequest.Builder builder =
1300                 (FakeUrlRequest.Builder)
1301                         mFakeCronetEngine.newUrlRequestBuilder(
1302                                 url, callback, callback.getExecutor());
1303         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1304 
1305         TestUploadDataProvider dataProvider =
1306                 new TestUploadDataProvider(
1307                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()) {
1308                     @Override
1309                     public long getLength() throws IOException {
1310                         return 1;
1311                     }
1312 
1313                     @Override
1314                     public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer)
1315                             throws IOException {
1316                         byteBuffer.put("12".getBytes());
1317                         uploadDataSink.onReadSucceeded(false);
1318                     }
1319                 };
1320 
1321         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1322         builder.addHeader("Content-Type", "useless/string");
1323         builder.build().start();
1324         callback.blockForDone();
1325         dataProvider.assertClosed();
1326 
1327         assertThat(callback.mError)
1328                 .hasMessageThat()
1329                 .contains("Exception received from UploadDataProvider");
1330         assertThat(callback.mError)
1331                 .hasCauseThat()
1332                 .hasMessageThat()
1333                 .contains("Read upload data length 2 exceeds expected length 1");
1334         assertThat(callback.getResponseInfo()).isNull();
1335     }
1336 
1337     @Test
1338     @SmallTest
testUploadWithBadLengthBufferAligned()1339     public void testUploadWithBadLengthBufferAligned() throws Exception {
1340         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1341         String url = "url";
1342         FakeUrlRequest.Builder builder =
1343                 (FakeUrlRequest.Builder)
1344                         mFakeCronetEngine.newUrlRequestBuilder(
1345                                 url, callback, callback.getExecutor());
1346         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1347 
1348         TestUploadDataProvider dataProvider =
1349                 new TestUploadDataProvider(
1350                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor()) {
1351                     @Override
1352                     public long getLength() throws IOException {
1353                         return 8191;
1354                     }
1355 
1356                     @Override
1357                     public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer)
1358                             throws IOException {
1359                         byteBuffer.put("0123456789abcdef".getBytes());
1360                         uploadDataSink.onReadSucceeded(false);
1361                     }
1362                 };
1363         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1364         builder.addHeader("Content-Type", "useless/string");
1365         builder.build().start();
1366         callback.blockForDone();
1367         dataProvider.assertClosed();
1368         assertThat(callback.mError)
1369                 .hasMessageThat()
1370                 .contains("Exception received from UploadDataProvider");
1371         assertThat(callback.mError)
1372                 .hasCauseThat()
1373                 .hasMessageThat()
1374                 .contains("Read upload data length 8192 exceeds expected length 8191");
1375         assertThat(callback.getResponseInfo()).isNull();
1376     }
1377 
1378     @Test
1379     @SmallTest
testUploadLengthFailSync()1380     public void testUploadLengthFailSync() throws Exception {
1381         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1382         String url = "url";
1383         FakeUrlRequest.Builder builder =
1384                 (FakeUrlRequest.Builder)
1385                         mFakeCronetEngine.newUrlRequestBuilder(
1386                                 url, callback, callback.getExecutor());
1387         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1388 
1389         TestUploadDataProvider dataProvider =
1390                 new TestUploadDataProvider(
1391                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1392         dataProvider.setLengthFailure();
1393         // This will never be read, but if the length is 0, read may never be
1394         // called.
1395         dataProvider.addRead("test".getBytes());
1396         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1397         builder.addHeader("Content-Type", "useless/string");
1398         builder.build().start();
1399         callback.blockForDone();
1400         dataProvider.assertClosed();
1401 
1402         assertThat(dataProvider.getNumReadCalls()).isEqualTo(0);
1403         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
1404 
1405         assertThat(callback.mError)
1406                 .hasMessageThat()
1407                 .contains("Exception received from UploadDataProvider");
1408         assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync length failure");
1409         assertThat(callback.getResponseInfo()).isNull();
1410     }
1411 
1412     @Test
1413     @SmallTest
testUploadReadFailSync()1414     public void testUploadReadFailSync() throws Exception {
1415         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1416         String url = "url";
1417         FakeUrlRequest.Builder builder =
1418                 (FakeUrlRequest.Builder)
1419                         mFakeCronetEngine.newUrlRequestBuilder(
1420                                 url, callback, callback.getExecutor());
1421         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1422 
1423         TestUploadDataProvider dataProvider =
1424                 new TestUploadDataProvider(
1425                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1426         dataProvider.setReadFailure(
1427                 /* readFailIndex= */ 0, TestUploadDataProvider.FailMode.CALLBACK_SYNC);
1428         // This will never be read, but if the length is 0, read may never be
1429         // called.
1430         dataProvider.addRead("test".getBytes());
1431         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1432         builder.addHeader("Content-Type", "useless/string");
1433         builder.build().start();
1434         callback.blockForDone();
1435         dataProvider.assertClosed();
1436 
1437         assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
1438         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
1439 
1440         assertThat(callback.mError)
1441                 .hasMessageThat()
1442                 .contains("Exception received from UploadDataProvider");
1443         assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync read failure");
1444         assertThat(callback.getResponseInfo()).isNull();
1445     }
1446 
1447     @Test
1448     @SmallTest
testUploadReadFailAsync()1449     public void testUploadReadFailAsync() throws Exception {
1450         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1451         String url = "url";
1452         FakeUrlRequest.Builder builder =
1453                 (FakeUrlRequest.Builder)
1454                         mFakeCronetEngine.newUrlRequestBuilder(
1455                                 url, callback, callback.getExecutor());
1456         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1457 
1458         TestUploadDataProvider dataProvider =
1459                 new TestUploadDataProvider(
1460                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1461         dataProvider.setReadFailure(
1462                 /* readFailIndex= */ 0, TestUploadDataProvider.FailMode.CALLBACK_ASYNC);
1463         // This will never be read, but if the length is 0, read may never be
1464         // called.
1465         dataProvider.addRead("test".getBytes());
1466         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1467         builder.addHeader("Content-Type", "useless/string");
1468         builder.build().start();
1469         callback.blockForDone();
1470         dataProvider.assertClosed();
1471 
1472         assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
1473         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
1474 
1475         assertThat(callback.mError)
1476                 .hasMessageThat()
1477                 .contains("Exception received from UploadDataProvider");
1478         assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Async read failure");
1479         assertThat(callback.getResponseInfo()).isNull();
1480     }
1481 
1482     @Test
1483     @SmallTest
testUploadReadFailThrown()1484     public void testUploadReadFailThrown() throws Exception {
1485         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1486         String url = "url";
1487         FakeUrlRequest.Builder builder =
1488                 (FakeUrlRequest.Builder)
1489                         mFakeCronetEngine.newUrlRequestBuilder(
1490                                 url, callback, callback.getExecutor());
1491         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1492 
1493         TestUploadDataProvider dataProvider =
1494                 new TestUploadDataProvider(
1495                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1496         dataProvider.setReadFailure(/* readFailIndex= */ 0, TestUploadDataProvider.FailMode.THROWN);
1497         // This will never be read, but if the length is 0, read may never be
1498         // called.
1499         dataProvider.addRead("test".getBytes());
1500         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1501         builder.addHeader("Content-Type", "useless/string");
1502         builder.build().start();
1503         callback.blockForDone();
1504         dataProvider.assertClosed();
1505 
1506         assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
1507         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
1508 
1509         assertThat(callback.mError)
1510                 .hasMessageThat()
1511                 .contains("Exception received from UploadDataProvider");
1512         assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Thrown read failure");
1513         assertThat(callback.getResponseInfo()).isNull();
1514     }
1515 
1516     /** This test uses a direct executor for upload, and non direct for callbacks */
1517     @Test
1518     @SmallTest
testDirectExecutorUploadProhibitedByDefault()1519     public void testDirectExecutorUploadProhibitedByDefault() throws Exception {
1520         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1521         Executor myExecutor =
1522                 new Executor() {
1523                     @Override
1524                     public void execute(Runnable command) {
1525                         command.run();
1526                     }
1527                 };
1528         String url = "url";
1529         FakeUrlRequest.Builder builder =
1530                 (FakeUrlRequest.Builder)
1531                         mFakeCronetEngine.newUrlRequestBuilder(
1532                                 url, callback, callback.getExecutor());
1533         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1534 
1535         TestUploadDataProvider dataProvider =
1536                 new TestUploadDataProvider(
1537                         TestUploadDataProvider.SuccessCallbackMode.SYNC, myExecutor);
1538         // This will never be read, but if the length is 0, read may never be
1539         // called.
1540         dataProvider.addRead("test".getBytes());
1541         builder.setUploadDataProvider(dataProvider, myExecutor);
1542         builder.addHeader("Content-Type", "useless/string");
1543         builder.build().start();
1544         callback.blockForDone();
1545 
1546         assertThat(dataProvider.getNumReadCalls()).isEqualTo(0);
1547         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
1548 
1549         assertThat(callback.mError)
1550                 .hasMessageThat()
1551                 .contains("Exception received from UploadDataProvider");
1552         assertThat(callback.mError)
1553                 .hasCauseThat()
1554                 .hasMessageThat()
1555                 .contains("Inline execution is prohibited for this request");
1556         assertThat(callback.getResponseInfo()).isNull();
1557     }
1558 
1559     /** This test uses a direct executor for callbacks, and non direct for upload */
1560     @Test
1561     @SmallTest
testDirectExecutorProhibitedByDefault()1562     public void testDirectExecutorProhibitedByDefault() throws Exception {
1563         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1564         // Everything submitted to this executor will be executed immediately on the thread
1565         // that submitted the Runnable (blocking it until the runnable completes)
1566         Executor directExecutor =
1567                 new Executor() {
1568                     @Override
1569                     public void execute(Runnable command) {
1570                         command.run();
1571                     }
1572                 };
1573         String url = "url";
1574         FakeUrlRequest.Builder builder =
1575                 (FakeUrlRequest.Builder)
1576                         mFakeCronetEngine.newUrlRequestBuilder(url, callback, directExecutor);
1577         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1578 
1579         TestUploadDataProvider dataProvider =
1580                 new TestUploadDataProvider(
1581                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1582         // This will never be read, but if the length is 0, read may never be
1583         // called.
1584         dataProvider.addRead("test".getBytes());
1585         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1586         builder.addHeader("Content-Type", "useless/string");
1587         builder.build().start();
1588         callback.blockForDone();
1589         assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
1590         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(0);
1591         assertThat(callback.mError).hasMessageThat().contains("Exception posting task to executor");
1592         assertThat(callback.mError)
1593                 .hasCauseThat()
1594                 .hasMessageThat()
1595                 .contains("Inline execution is prohibited for this request");
1596         assertThat(callback.getResponseInfo()).isNull();
1597         dataProvider.assertClosed();
1598     }
1599 
1600     @Test
1601     @SmallTest
testDirectExecutorAllowed()1602     public void testDirectExecutorAllowed() throws Exception {
1603         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1604         callback.setAllowDirectExecutor(true);
1605         Executor myExecutor =
1606                 new Executor() {
1607                     @Override
1608                     public void execute(Runnable command) {
1609                         command.run();
1610                     }
1611                 };
1612         String url = "url";
1613         FakeUrlRequest.Builder builder =
1614                 (FakeUrlRequest.Builder)
1615                         mFakeCronetEngine.newUrlRequestBuilder(
1616                                 url, callback, callback.getExecutor());
1617         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1618         UploadDataProvider dataProvider = UploadDataProviders.create("test".getBytes());
1619         builder.setUploadDataProvider(dataProvider, myExecutor);
1620         builder.addHeader("Content-Type", "useless/string");
1621         builder.allowDirectExecutor();
1622         builder.build().start();
1623         callback.blockForDone();
1624 
1625         if (callback.mOnErrorCalled) {
1626             throw callback.mError;
1627         }
1628 
1629         assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200);
1630         assertThat(callback.mResponseAsString).isEqualTo("test");
1631     }
1632 
1633     @Test
1634     @SmallTest
testUploadRewindFailSync()1635     public void testUploadRewindFailSync() throws Exception {
1636         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1637         String redirectUrl = "redirectUrl";
1638         String echoBodyUrl = "echobody";
1639         FakeUrlRequest.Builder builder =
1640                 (FakeUrlRequest.Builder)
1641                         mFakeCronetEngine.newUrlRequestBuilder(
1642                                 redirectUrl, callback, callback.getExecutor());
1643         mFakeCronetController.addRedirectResponse(echoBodyUrl, redirectUrl);
1644         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher(echoBodyUrl));
1645 
1646         TestUploadDataProvider dataProvider =
1647                 new TestUploadDataProvider(
1648                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1649         dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.CALLBACK_SYNC);
1650         dataProvider.addRead("test".getBytes());
1651         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1652         builder.addHeader("Content-Type", "useless/string");
1653         builder.build().start();
1654         callback.blockForDone();
1655         dataProvider.assertClosed();
1656 
1657         assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
1658         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1);
1659 
1660         assertThat(callback.mError)
1661                 .hasMessageThat()
1662                 .contains("Exception received from UploadDataProvider");
1663         assertThat(callback.mError).hasCauseThat().hasMessageThat().contains("Sync rewind failure");
1664         assertThat(callback.getResponseInfo()).isNull();
1665     }
1666 
1667     @Test
1668     @SmallTest
testUploadRewindFailAsync()1669     public void testUploadRewindFailAsync() throws Exception {
1670         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1671         String redirectUrl = "redirectUrl";
1672         String echoBodyUrl = "echobody";
1673         FakeUrlRequest.Builder builder =
1674                 (FakeUrlRequest.Builder)
1675                         mFakeCronetEngine.newUrlRequestBuilder(
1676                                 redirectUrl, callback, callback.getExecutor());
1677         mFakeCronetController.addRedirectResponse(echoBodyUrl, redirectUrl);
1678         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher(echoBodyUrl));
1679 
1680         TestUploadDataProvider dataProvider =
1681                 new TestUploadDataProvider(
1682                         TestUploadDataProvider.SuccessCallbackMode.ASYNC, callback.getExecutor());
1683         dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.CALLBACK_ASYNC);
1684         dataProvider.addRead("test".getBytes());
1685         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1686         builder.addHeader("Content-Type", "useless/string");
1687         builder.build().start();
1688         callback.blockForDone();
1689         dataProvider.assertClosed();
1690 
1691         assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
1692         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1);
1693 
1694         assertThat(callback.mError)
1695                 .hasMessageThat()
1696                 .contains("Exception received from UploadDataProvider");
1697         assertThat(callback.mError)
1698                 .hasCauseThat()
1699                 .hasMessageThat()
1700                 .contains("Async rewind failure");
1701         assertThat(callback.getResponseInfo()).isNull();
1702     }
1703 
1704     @Test
1705     @SmallTest
testUploadRewindFailThrown()1706     public void testUploadRewindFailThrown() throws Exception {
1707         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1708         String redirectUrl = "redirectUrl";
1709         String echoBodyUrl = "echobody";
1710         FakeUrlRequest.Builder builder =
1711                 (FakeUrlRequest.Builder)
1712                         mFakeCronetEngine.newUrlRequestBuilder(
1713                                 redirectUrl, callback, callback.getExecutor());
1714         mFakeCronetController.addRedirectResponse(echoBodyUrl, redirectUrl);
1715         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher(echoBodyUrl));
1716 
1717         TestUploadDataProvider dataProvider =
1718                 new TestUploadDataProvider(
1719                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1720         dataProvider.setRewindFailure(TestUploadDataProvider.FailMode.THROWN);
1721         dataProvider.addRead("test".getBytes());
1722         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1723         builder.addHeader("Content-Type", "useless/string");
1724         builder.build().start();
1725         callback.blockForDone();
1726         dataProvider.assertClosed();
1727 
1728         assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
1729         assertThat(dataProvider.getNumRewindCalls()).isEqualTo(1);
1730 
1731         assertThat(callback.mError)
1732                 .hasMessageThat()
1733                 .contains("Exception received from UploadDataProvider");
1734         assertThat(callback.mError)
1735                 .hasCauseThat()
1736                 .hasMessageThat()
1737                 .contains("Thrown rewind failure");
1738         assertThat(callback.getResponseInfo()).isNull();
1739     }
1740 
1741     @Test
1742     @SmallTest
testUploadChunked()1743     public void testUploadChunked() throws Exception {
1744         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1745         String url = "url";
1746         FakeUrlRequest.Builder builder =
1747                 (FakeUrlRequest.Builder)
1748                         mFakeCronetEngine.newUrlRequestBuilder(
1749                                 url, callback, callback.getExecutor());
1750         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1751 
1752         TestUploadDataProvider dataProvider =
1753                 new TestUploadDataProvider(
1754                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1755         dataProvider.addRead("test hello".getBytes());
1756         dataProvider.setChunked(true);
1757         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1758         builder.addHeader("Content-Type", "useless/string");
1759 
1760         assertThat(dataProvider.getUploadedLength()).isEqualTo(-1);
1761 
1762         builder.build().start();
1763         callback.blockForDone();
1764         dataProvider.assertClosed();
1765 
1766         // 1 read call for one data chunk.
1767         assertThat(dataProvider.getNumReadCalls()).isEqualTo(1);
1768         assertThat(callback.mResponseAsString).isEqualTo("test hello");
1769     }
1770 
1771     @Test
1772     @SmallTest
testUploadChunkedLastReadZeroLengthBody()1773     public void testUploadChunkedLastReadZeroLengthBody() throws Exception {
1774         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1775         String url = "url";
1776         FakeUrlRequest.Builder builder =
1777                 (FakeUrlRequest.Builder)
1778                         mFakeCronetEngine.newUrlRequestBuilder(
1779                                 url, callback, callback.getExecutor());
1780         mFakeCronetController.addResponseMatcher(new EchoBodyResponseMatcher());
1781 
1782         TestUploadDataProvider dataProvider =
1783                 new TestUploadDataProvider(
1784                         TestUploadDataProvider.SuccessCallbackMode.SYNC, callback.getExecutor());
1785         // Add 3 reads. The last read has a 0-length body.
1786         dataProvider.addRead("hello there".getBytes());
1787         dataProvider.addRead("!".getBytes());
1788         dataProvider.addRead("".getBytes());
1789         dataProvider.setChunked(true);
1790         builder.setUploadDataProvider(dataProvider, callback.getExecutor());
1791         builder.addHeader("Content-Type", "useless/string");
1792 
1793         assertThat(dataProvider.getUploadedLength()).isEqualTo(-1);
1794 
1795         builder.build().start();
1796         callback.blockForDone();
1797         dataProvider.assertClosed();
1798 
1799         // 2 read call for the first two data chunks, and 1 for final chunk.
1800         assertThat(dataProvider.getNumReadCalls()).isEqualTo(3);
1801         assertThat(callback.mResponseAsString).isEqualTo("hello there!");
1802     }
1803 
1804     @Test
1805     @SmallTest
testCancelBeforeStart_doesNotCrash()1806     public void testCancelBeforeStart_doesNotCrash() {
1807         // Setup the basic response.
1808         String responseText = "response text";
1809         String url = "TEST_URL";
1810         FakeUrlResponse response =
1811                 new FakeUrlResponse.Builder().setResponseBody(responseText.getBytes()).build();
1812         mFakeCronetController.addResponseForUrl(response, url);
1813         TestUrlRequestCallback callback = new TestUrlRequestCallback();
1814         FakeUrlRequest request =
1815                 (FakeUrlRequest)
1816                         mFakeCronetEngine
1817                                 .newUrlRequestBuilder(url, callback, callback.getExecutor())
1818                                 .build();
1819 
1820         request.cancel();
1821         request.start();
1822         callback.blockForDone();
1823 
1824         assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_SUCCEEDED);
1825     }
1826 
1827     /** A Cronet callback that does nothing. */
1828     static class StubCallback extends UrlRequest.Callback {
1829         @Override
onRedirectReceived( org.chromium.net.UrlRequest urlRequest, UrlResponseInfo urlResponseInfo, String s)1830         public void onRedirectReceived(
1831                 org.chromium.net.UrlRequest urlRequest,
1832                 UrlResponseInfo urlResponseInfo,
1833                 String s) {}
1834 
1835         @Override
onResponseStarted( org.chromium.net.UrlRequest urlRequest, UrlResponseInfo urlResponseInfo)1836         public void onResponseStarted(
1837                 org.chromium.net.UrlRequest urlRequest, UrlResponseInfo urlResponseInfo) {}
1838 
1839         @Override
onReadCompleted( org.chromium.net.UrlRequest urlRequest, UrlResponseInfo urlResponseInfo, ByteBuffer byteBuffer)1840         public void onReadCompleted(
1841                 org.chromium.net.UrlRequest urlRequest,
1842                 UrlResponseInfo urlResponseInfo,
1843                 ByteBuffer byteBuffer) {}
1844 
1845         @Override
onSucceeded( org.chromium.net.UrlRequest urlRequest, UrlResponseInfo urlResponseInfo)1846         public void onSucceeded(
1847                 org.chromium.net.UrlRequest urlRequest, UrlResponseInfo urlResponseInfo) {}
1848 
1849         @Override
onFailed( org.chromium.net.UrlRequest urlRequest, UrlResponseInfo urlResponseInfo, CronetException e)1850         public void onFailed(
1851                 org.chromium.net.UrlRequest urlRequest,
1852                 UrlResponseInfo urlResponseInfo,
1853                 CronetException e) {}
1854     }
1855 }
1856