Merge "Add more test coverage for UrlRequest and ConnectionMigrationOptions." am: a23a74a232
Original change: https://android-review.googlesource.com/c/platform/packages/modules/Connectivity/+/2472109
Change-Id: I2bfb057a39a8ae273aa8bfe0d291a9ea8089c185
Signed-off-by: Automerger Merge Worker <[email protected]>
diff --git a/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt b/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt
index 279bcc8..77cb30e 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt
+++ b/Cronet/tests/cts/src/android/net/http/cts/ConnectionMigrationOptionsTest.kt
@@ -18,6 +18,7 @@
import android.net.http.ConnectionMigrationOptions
import android.net.http.ConnectionMigrationOptions.MIGRATION_OPTION_ENABLED
+import android.net.http.ConnectionMigrationOptions.MIGRATION_OPTION_UNSPECIFIED
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlin.test.Test
import kotlin.test.assertEquals
@@ -27,6 +28,16 @@
class ConnectionMigrationOptionsTest {
@Test
+ fun testConnectionMigrationOptions_defaultValues() {
+ val options =
+ ConnectionMigrationOptions.Builder().build()
+
+ assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.allowNonDefaultNetworkUsageEnabled)
+ assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.defaultNetworkMigrationEnabled)
+ assertEquals(MIGRATION_OPTION_UNSPECIFIED, options.pathDegradationMigrationEnabled)
+ }
+
+ @Test
fun testConnectionMigrationOptions_enableDefaultNetworkMigration_returnSetValue() {
val options =
ConnectionMigrationOptions.Builder()
@@ -45,4 +56,13 @@
assertEquals(MIGRATION_OPTION_ENABLED, options.pathDegradationMigrationEnabled)
}
+
+ @Test
+ fun testConnectionMigrationOptions_allowNonDefaultNetworkUsage_returnSetValue() {
+ val options =
+ ConnectionMigrationOptions.Builder()
+ .setAllowNonDefaultNetworkUsageEnabled(MIGRATION_OPTION_ENABLED).build()
+
+ assertEquals(MIGRATION_OPTION_ENABLED, options.allowNonDefaultNetworkUsageEnabled)
+ }
}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
index 19742e5..a364e29 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
@@ -19,6 +19,8 @@
import static android.net.http.cts.util.TestUtilsKt.assertOKStatusCode;
import static android.net.http.cts.util.TestUtilsKt.skipIfNoInternetConnection;
+import static com.google.common.truth.Truth.assertThat;
+
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.greaterThan;
import static org.junit.Assert.assertEquals;
@@ -40,15 +42,19 @@
import android.net.http.cts.util.TestUploadDataProvider;
import android.net.http.cts.util.TestUrlRequestCallback;
import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
+import android.net.http.cts.util.UploadDataProviders;
import androidx.test.core.app.ApplicationProvider;
import androidx.test.ext.junit.runners.AndroidJUnit4;
+import com.google.common.base.Strings;
+
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
+import java.net.URLEncoder;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ArrayBlockingQueue;
@@ -279,6 +285,31 @@
assertTrue(e.getCause().getMessage().contains("full"));
}
+ @Test
+ public void testUrlRequestPost_withRedirect() throws Exception {
+ String body = Strings.repeat(
+ "Hello, this is a really interesting body, so write this 100 times.", 100);
+
+ String redirectUrlParameter =
+ URLEncoder.encode(mTestServer.getEchoBodyUrl(), "UTF-8");
+ createUrlRequestBuilder(
+ String.format(
+ "%s/alt_redirect?dest=%s&statusCode=307",
+ mTestServer.getBaseUri(),
+ redirectUrlParameter))
+ .setHttpMethod("POST")
+ .addHeader("Content-Type", "text/plain")
+ .setUploadDataProvider(
+ UploadDataProviders.create(body.getBytes(StandardCharsets.UTF_8)),
+ mCallback.getExecutor())
+ .build()
+ .start();
+ mCallback.expectCallback(ResponseStep.ON_SUCCEEDED);
+
+ assertOKStatusCode(mCallback.mResponseInfo);
+ assertThat(mCallback.mResponseAsString).isEqualTo(body);
+ }
+
private static class StubUrlRequestCallback extends UrlRequest.Callback {
@Override
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java b/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java
new file mode 100644
index 0000000..889f8f2
--- /dev/null
+++ b/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2023 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 android.net.http.cts.util;
+
+import android.net.http.UploadDataProvider;
+import android.net.http.UploadDataSink;
+import android.os.ParcelFileDescriptor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.nio.ByteBuffer;
+import java.nio.channels.FileChannel;
+
+/**
+ * Provides implementations of {@link UploadDataProvider} for common use cases. Corresponds to
+ * {@code android.net.http.apihelpers.UploadDataProviders} which is not an exposed API.
+ */
+public final class UploadDataProviders {
+ /**
+ * Uploads an entire file.
+ *
+ * @param file The file to upload
+ * @return A new UploadDataProvider for the given file
+ */
+ public static UploadDataProvider create(final File file) {
+ return new FileUploadProvider(() -> new FileInputStream(file).getChannel());
+ }
+
+ /**
+ * Uploads an entire file, closing the descriptor when it is no longer needed.
+ *
+ * @param fd The file descriptor to upload
+ * @throws IllegalArgumentException if {@code fd} is not a file.
+ * @return A new UploadDataProvider for the given file descriptor
+ */
+ public static UploadDataProvider create(final ParcelFileDescriptor fd) {
+ return new FileUploadProvider(() -> {
+ if (fd.getStatSize() != -1) {
+ return new ParcelFileDescriptor.AutoCloseInputStream(fd).getChannel();
+ } else {
+ fd.close();
+ throw new IllegalArgumentException("Not a file: " + fd);
+ }
+ });
+ }
+
+ /**
+ * Uploads a ByteBuffer, from the current {@code buffer.position()} to {@code buffer.limit()}
+ *
+ * @param buffer The data to upload
+ * @return A new UploadDataProvider for the given buffer
+ */
+ public static UploadDataProvider create(ByteBuffer buffer) {
+ return new ByteBufferUploadProvider(buffer.slice());
+ }
+
+ /**
+ * Uploads {@code length} bytes from {@code data}, starting from {@code offset}
+ *
+ * @param data Array containing data to upload
+ * @param offset Offset within data to start with
+ * @param length Number of bytes to upload
+ * @return A new UploadDataProvider for the given data
+ */
+ public static UploadDataProvider create(byte[] data, int offset, int length) {
+ return new ByteBufferUploadProvider(ByteBuffer.wrap(data, offset, length).slice());
+ }
+
+ /**
+ * Uploads the contents of {@code data}
+ *
+ * @param data Array containing data to upload
+ * @return A new UploadDataProvider for the given data
+ */
+ public static UploadDataProvider create(byte[] data) {
+ return create(data, 0, data.length);
+ }
+
+ private interface FileChannelProvider {
+ FileChannel getChannel() throws IOException;
+ }
+
+ private static final class FileUploadProvider extends UploadDataProvider {
+ private volatile FileChannel mChannel;
+ private final FileChannelProvider mProvider;
+ /** Guards initialization of {@code mChannel} */
+ private final Object mLock = new Object();
+
+ private FileUploadProvider(FileChannelProvider provider) {
+ this.mProvider = provider;
+ }
+
+ @Override
+ public long getLength() throws IOException {
+ return getChannel().size();
+ }
+
+ @Override
+ public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) throws IOException {
+ if (!byteBuffer.hasRemaining()) {
+ throw new IllegalStateException("Cronet passed a buffer with no bytes remaining");
+ }
+ FileChannel channel = getChannel();
+ int bytesRead = 0;
+ while (bytesRead == 0) {
+ int read = channel.read(byteBuffer);
+ if (read == -1) {
+ break;
+ } else {
+ bytesRead += read;
+ }
+ }
+ uploadDataSink.onReadSucceeded(false);
+ }
+
+ @Override
+ public void rewind(UploadDataSink uploadDataSink) throws IOException {
+ getChannel().position(0);
+ uploadDataSink.onRewindSucceeded();
+ }
+
+ /**
+ * Lazily initializes the channel so that a blocking operation isn't performed
+ * on a non-executor thread.
+ */
+ private FileChannel getChannel() throws IOException {
+ if (mChannel == null) {
+ synchronized (mLock) {
+ if (mChannel == null) {
+ mChannel = mProvider.getChannel();
+ }
+ }
+ }
+ return mChannel;
+ }
+
+ @Override
+ public void close() throws IOException {
+ FileChannel channel = mChannel;
+ if (channel != null) {
+ channel.close();
+ }
+ }
+ }
+
+ private static final class ByteBufferUploadProvider extends UploadDataProvider {
+ private final ByteBuffer mUploadBuffer;
+
+ private ByteBufferUploadProvider(ByteBuffer uploadBuffer) {
+ this.mUploadBuffer = uploadBuffer;
+ }
+
+ @Override
+ public long getLength() {
+ return mUploadBuffer.limit();
+ }
+
+ @Override
+ public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) {
+ if (!byteBuffer.hasRemaining()) {
+ throw new IllegalStateException("Cronet passed a buffer with no bytes remaining");
+ }
+ if (byteBuffer.remaining() >= mUploadBuffer.remaining()) {
+ byteBuffer.put(mUploadBuffer);
+ } else {
+ int oldLimit = mUploadBuffer.limit();
+ mUploadBuffer.limit(mUploadBuffer.position() + byteBuffer.remaining());
+ byteBuffer.put(mUploadBuffer);
+ mUploadBuffer.limit(oldLimit);
+ }
+ uploadDataSink.onReadSucceeded(false);
+ }
+
+ @Override
+ public void rewind(UploadDataSink uploadDataSink) {
+ mUploadBuffer.position(0);
+ uploadDataSink.onRewindSucceeded();
+ }
+ }
+
+ // Prevent instantiation
+ private UploadDataProviders() {}
+}