diff options
author | Anonymous <no-reply@google.com> | 2020-10-02 16:21:32 -0700 |
---|---|---|
committer | Jeff Davidson <jpd@google.com> | 2020-10-07 01:33:22 +0000 |
commit | b6bd7aa39db62c517d97ff0a97d8d073bdf83a47 (patch) | |
tree | b9b7a6b9928136e83fda62aaa8b8887df88839b5 /src/test/java/com/android/volley/cronet/CronetHttpStackTest.java | |
parent | dc9062756c4835ac62ade2f3fb8ace20e8dbf3a6 (diff) | |
download | volley-b6bd7aa39db62c517d97ff0a97d8d073bdf83a47.tar.gz |
Import of Volley from GitHub to AOSP.
Includes AsyncRequestQueue and asynchronous infrastructure, but the Cronet-based components are
excluded from Android.bp as Cronet is not broadly exposed for application usage.
- ae701b33e718782e914108f9b6d1f1bb5f1ab7f8 Fix empty responses when content length is unknown. by Jeff Davidson <jpd@google.com>
- 055fc535255367e5556d6274aadcbb5cd3be5ed3 Add hook to allow customization of UrlRequest.Builder. by Jeff Davidson <jpd@google.com>
- bc64707525d43b2ddc1cad08a3445cc34111c47f Set Content-Type header for requests with bodies. (#371) by Jeff Davidson <jpd@google.com>
- 4083c43bcfc8cc31be709574f6d1b923c2bf869d Add cURL logging support to CronetHttpStack. (#370) by Jeff Davidson <jpd@google.com>
- 46a4f9040126940be79b10633be470dea32958a3 Fix dependencies from base Volley package to toolbox pack... by Jeff Davidson <jpd@google.com>
- cde6d4367210819f357b1397d17b4656b773a0d9 Provide a ScheduledExecutorService to AsyncRequestQueue/A... by Jeff Davidson <jpd@google.com>
- f582ee6f7b7067b6bce21239f904f3066b778dad Move CronetHttpStack tests to the expected location. (#368) by Jeff Davidson <jpd@google.com>
- a4954c6661b01a5950f973d873b8e21d2a5dcc40 Revert DiskBasedCache. (#366) by Jeff Davidson <jpd@google.com>
- 7b0a3119b499989c31611a8fb944015aa9a400c5 AsyncRequestQueue implementation + some tests (#361) by sphill99 <s.phill99@gmail.com>
- d777e8a7bc4136b311913178021894adec73bfdb Created builder for DiskBasedAsyncCache (#360) by sphill99 <s.phill99@gmail.com>
- 3ff2427da1db5c5a46c1dfa0aaf7c097258be7ce AsyncNetwork implementation as well as unit testing (#357) by sphill99 <s.phill99@gmail.com>
- bb85de2f41a1bf9fe8b1ef15a41a9057947a0724 Added licensing headers for all newly created files (#358) by sphill99 <s.phill99@gmail.com>
- 82c828d65a8f317a55a1a7b067a60df8fe566a02 AsyncHttpStack and Cronet implementation. Limited unit te... by sphill99 <s.phill99@gmail.com>
- d0fb8dea486cc9b164cf140661225160dd5454d7 Implementations for rest of AsyncCache, tests for cache m... by sphill99 <s.phill99@gmail.com>
- 94e3a4da70cf1803426de6a2579676966c670d78 Include license in generated POM file. (#352) by Jeff Davidson <jpd@google.com>
- cae1570bc5d96cd2b840b39e5c742a397c76b45f Put implementation for async cache that uses callback met... by sphill99 <s.phill99@gmail.com>
- a791d09a6747ca4923352405b20c229eafad5866 Get Implementation in AsyncCache that takes in a callback... by sphill99 <s.phill99@gmail.com>
- 504e41a1c749ebbf3b63b6ab58533c8219cb6df1 Retry on NoConnectionError (#342) by sphill99 <s.phill99@gmail.com>
- 40d4ffb7ec5dab4cfb32d6d19db8f45419742671 Fixed testCompile and testApi deprecations (#341) by sphill99 <s.phill99@gmail.com>
- 7c767521db6aab17cb706d114570a557dc1c1efa Fix Android Studio project import of Volley. (#335) by Jeff Davidson <jpd@google.com>
GitOrigin-RevId: ae701b33e718782e914108f9b6d1f1bb5f1ab7f8
Change-Id: I8f5792f679e88591c213156dcdd13482105e1c53
Diffstat (limited to 'src/test/java/com/android/volley/cronet/CronetHttpStackTest.java')
-rw-r--r-- | src/test/java/com/android/volley/cronet/CronetHttpStackTest.java | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/src/test/java/com/android/volley/cronet/CronetHttpStackTest.java b/src/test/java/com/android/volley/cronet/CronetHttpStackTest.java new file mode 100644 index 0000000..cedb6ff --- /dev/null +++ b/src/test/java/com/android/volley/cronet/CronetHttpStackTest.java @@ -0,0 +1,381 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.volley.cronet; + +import static org.junit.Assert.assertEquals; +import static org.mockito.ArgumentMatchers.anyString; +import static org.mockito.Mockito.never; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +import com.android.volley.Header; +import com.android.volley.cronet.CronetHttpStack.CurlCommandLogger; +import com.android.volley.mock.TestRequest; +import com.android.volley.toolbox.AsyncHttpStack.OnRequestComplete; +import com.android.volley.toolbox.UrlRewriter; +import com.google.common.collect.ImmutableMap; +import com.google.common.util.concurrent.MoreExecutors; +import java.io.UnsupportedEncodingException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; +import org.chromium.net.CronetEngine; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.ArgumentCaptor; +import org.mockito.Mock; +import org.mockito.MockitoAnnotations; +import org.robolectric.RobolectricTestRunner; +import org.robolectric.RuntimeEnvironment; + +@RunWith(RobolectricTestRunner.class) +public class CronetHttpStackTest { + @Mock private CurlCommandLogger mMockCurlCommandLogger; + @Mock private OnRequestComplete mMockOnRequestComplete; + @Mock private UrlRewriter mMockUrlRewriter; + + // A fake would be ideal here, but Cronet doesn't (yet) provide one, and at the moment we aren't + // exercising the full response flow. + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private CronetEngine mMockCronetEngine; + + @Before + public void setUp() { + MockitoAnnotations.initMocks(this); + } + + @Test + public void curlLogging_disabled() { + CronetHttpStack stack = + createStack( + new Consumer<CronetHttpStack.Builder>() { + @Override + public void accept(CronetHttpStack.Builder builder) { + // Default parameters should not enable cURL logging. + } + }); + + stack.executeRequest( + new TestRequest.Get(), ImmutableMap.<String, String>of(), mMockOnRequestComplete); + + verify(mMockCurlCommandLogger, never()).logCurlCommand(anyString()); + } + + @Test + public void curlLogging_simpleTextRequest() { + CronetHttpStack stack = + createStack( + new Consumer<CronetHttpStack.Builder>() { + @Override + public void accept(CronetHttpStack.Builder builder) { + builder.setCurlLoggingEnabled(true); + } + }); + + stack.executeRequest( + new TestRequest.Get(), ImmutableMap.<String, String>of(), mMockOnRequestComplete); + + ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class); + verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture()); + assertEquals("curl -X GET \"http://foo.com\"", curlCommandCaptor.getValue()); + } + + @Test + public void curlLogging_rewrittenUrl() { + CronetHttpStack stack = + createStack( + new Consumer<CronetHttpStack.Builder>() { + @Override + public void accept(CronetHttpStack.Builder builder) { + builder.setCurlLoggingEnabled(true) + .setUrlRewriter(mMockUrlRewriter); + } + }); + when(mMockUrlRewriter.rewriteUrl("http://foo.com")).thenReturn("http://bar.com"); + + stack.executeRequest( + new TestRequest.Get(), ImmutableMap.<String, String>of(), mMockOnRequestComplete); + + ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class); + verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture()); + assertEquals("curl -X GET \"http://bar.com\"", curlCommandCaptor.getValue()); + } + + @Test + public void curlLogging_headers_withoutTokens() { + CronetHttpStack stack = + createStack( + new Consumer<CronetHttpStack.Builder>() { + @Override + public void accept(CronetHttpStack.Builder builder) { + builder.setCurlLoggingEnabled(true); + } + }); + + stack.executeRequest( + new TestRequest.Delete() { + @Override + public Map<String, String> getHeaders() { + return ImmutableMap.of( + "SomeHeader", "SomeValue", + "Authorization", "SecretToken"); + } + }, + ImmutableMap.of("SomeOtherHeader", "SomeValue"), + mMockOnRequestComplete); + + ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class); + verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture()); + // NOTE: Header order is stable because the implementation uses a TreeMap. + assertEquals( + "curl -X DELETE --header \"Authorization: [REDACTED]\" " + + "--header \"SomeHeader: SomeValue\" " + + "--header \"SomeOtherHeader: SomeValue\" \"http://foo.com\"", + curlCommandCaptor.getValue()); + } + + @Test + public void curlLogging_headers_withTokens() { + CronetHttpStack stack = + createStack( + new Consumer<CronetHttpStack.Builder>() { + @Override + public void accept(CronetHttpStack.Builder builder) { + builder.setCurlLoggingEnabled(true) + .setLogAuthTokensInCurlCommands(true); + } + }); + + stack.executeRequest( + new TestRequest.Delete() { + @Override + public Map<String, String> getHeaders() { + return ImmutableMap.of( + "SomeHeader", "SomeValue", + "Authorization", "SecretToken"); + } + }, + ImmutableMap.of("SomeOtherHeader", "SomeValue"), + mMockOnRequestComplete); + + ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class); + verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture()); + // NOTE: Header order is stable because the implementation uses a TreeMap. + assertEquals( + "curl -X DELETE --header \"Authorization: SecretToken\" " + + "--header \"SomeHeader: SomeValue\" " + + "--header \"SomeOtherHeader: SomeValue\" \"http://foo.com\"", + curlCommandCaptor.getValue()); + } + + @Test + public void curlLogging_textRequest() { + CronetHttpStack stack = + createStack( + new Consumer<CronetHttpStack.Builder>() { + @Override + public void accept(CronetHttpStack.Builder builder) { + builder.setCurlLoggingEnabled(true); + } + }); + + stack.executeRequest( + new TestRequest.PostWithBody() { + @Override + public byte[] getBody() { + try { + return "hello".getBytes("UTF-8"); + } catch (UnsupportedEncodingException e) { + throw new RuntimeException(e); + } + } + + @Override + public String getBodyContentType() { + return "text/plain; charset=UTF-8"; + } + }, + ImmutableMap.<String, String>of(), + mMockOnRequestComplete); + + ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class); + verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture()); + assertEquals( + "curl -X POST " + + "--header \"Content-Type: text/plain; charset=UTF-8\" \"http://foo.com\" " + + "--data-ascii \"hello\"", + curlCommandCaptor.getValue()); + } + + @Test + public void curlLogging_gzipTextRequest() { + CronetHttpStack stack = + createStack( + new Consumer<CronetHttpStack.Builder>() { + @Override + public void accept(CronetHttpStack.Builder builder) { + builder.setCurlLoggingEnabled(true); + } + }); + + stack.executeRequest( + new TestRequest.PostWithBody() { + @Override + public byte[] getBody() { + return new byte[] {1, 2, 3, 4, 5}; + } + + @Override + public String getBodyContentType() { + return "text/plain"; + } + + @Override + public Map<String, String> getHeaders() { + return ImmutableMap.of("Content-Encoding", "gzip, identity"); + } + }, + ImmutableMap.<String, String>of(), + mMockOnRequestComplete); + + ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class); + verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture()); + assertEquals( + "echo 'AQIDBAU=' | base64 -d > /tmp/$$.bin; curl -X POST " + + "--header \"Content-Encoding: gzip, identity\" " + + "--header \"Content-Type: text/plain\" \"http://foo.com\" " + + "--data-binary @/tmp/$$.bin", + curlCommandCaptor.getValue()); + } + + @Test + public void curlLogging_binaryRequest() { + CronetHttpStack stack = + createStack( + new Consumer<CronetHttpStack.Builder>() { + @Override + public void accept(CronetHttpStack.Builder builder) { + builder.setCurlLoggingEnabled(true); + } + }); + + stack.executeRequest( + new TestRequest.PostWithBody() { + @Override + public byte[] getBody() { + return new byte[] {1, 2, 3, 4, 5}; + } + + @Override + public String getBodyContentType() { + return "application/octet-stream"; + } + }, + ImmutableMap.<String, String>of(), + mMockOnRequestComplete); + + ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class); + verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture()); + assertEquals( + "echo 'AQIDBAU=' | base64 -d > /tmp/$$.bin; curl -X POST " + + "--header \"Content-Type: application/octet-stream\" \"http://foo.com\" " + + "--data-binary @/tmp/$$.bin", + curlCommandCaptor.getValue()); + } + + @Test + public void curlLogging_largeRequest() { + CronetHttpStack stack = + createStack( + new Consumer<CronetHttpStack.Builder>() { + @Override + public void accept(CronetHttpStack.Builder builder) { + builder.setCurlLoggingEnabled(true); + } + }); + + stack.executeRequest( + new TestRequest.PostWithBody() { + @Override + public byte[] getBody() { + return new byte[2048]; + } + + @Override + public String getBodyContentType() { + return "application/octet-stream"; + } + }, + ImmutableMap.<String, String>of(), + mMockOnRequestComplete); + + ArgumentCaptor<String> curlCommandCaptor = ArgumentCaptor.forClass(String.class); + verify(mMockCurlCommandLogger).logCurlCommand(curlCommandCaptor.capture()); + assertEquals( + "curl -X POST " + + "--header \"Content-Type: application/octet-stream\" \"http://foo.com\" " + + "[REQUEST BODY TOO LARGE TO INCLUDE]", + curlCommandCaptor.getValue()); + } + + @Test + public void getHeadersEmptyTest() { + List<Map.Entry<String, String>> list = new ArrayList<>(); + List<Header> actual = CronetHttpStack.getHeaders(list); + List<Header> expected = new ArrayList<>(); + assertEquals(expected, actual); + } + + @Test + public void getHeadersNonEmptyTest() { + Map<String, String> headers = new HashMap<>(); + for (int i = 1; i < 5; i++) { + headers.put("key" + i, "value" + i); + } + List<Map.Entry<String, String>> list = new ArrayList<>(headers.entrySet()); + List<Header> actual = CronetHttpStack.getHeaders(list); + List<Header> expected = new ArrayList<>(); + for (int i = 1; i < 5; i++) { + expected.add(new Header("key" + i, "value" + i)); + } + assertHeaderListsEqual(expected, actual); + } + + private void assertHeaderListsEqual(List<Header> expected, List<Header> actual) { + assertEquals(expected.size(), actual.size()); + for (int i = 0; i < expected.size(); i++) { + assertEquals(expected.get(i).getName(), actual.get(i).getName()); + assertEquals(expected.get(i).getValue(), actual.get(i).getValue()); + } + } + + private CronetHttpStack createStack(Consumer<CronetHttpStack.Builder> stackEditor) { + CronetHttpStack.Builder builder = + new CronetHttpStack.Builder(RuntimeEnvironment.application) + .setCronetEngine(mMockCronetEngine) + .setCurlCommandLogger(mMockCurlCommandLogger); + stackEditor.accept(builder); + CronetHttpStack stack = builder.build(); + stack.setBlockingExecutor(MoreExecutors.newDirectExecutorService()); + stack.setNonBlockingExecutor(MoreExecutors.newDirectExecutorService()); + return stack; + } +} |