Merge "Add more test coverage for UrlRequest and ConnectionMigrationOptions."
This commit is contained in:
@@ -18,6 +18,7 @@ package android.net.http.cts
|
|||||||
|
|
||||||
import android.net.http.ConnectionMigrationOptions
|
import android.net.http.ConnectionMigrationOptions
|
||||||
import android.net.http.ConnectionMigrationOptions.MIGRATION_OPTION_ENABLED
|
import android.net.http.ConnectionMigrationOptions.MIGRATION_OPTION_ENABLED
|
||||||
|
import android.net.http.ConnectionMigrationOptions.MIGRATION_OPTION_UNSPECIFIED
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
import kotlin.test.Test
|
import kotlin.test.Test
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@@ -26,6 +27,16 @@ import org.junit.runner.RunWith
|
|||||||
@RunWith(AndroidJUnit4::class)
|
@RunWith(AndroidJUnit4::class)
|
||||||
class ConnectionMigrationOptionsTest {
|
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
|
@Test
|
||||||
fun testConnectionMigrationOptions_enableDefaultNetworkMigration_returnSetValue() {
|
fun testConnectionMigrationOptions_enableDefaultNetworkMigration_returnSetValue() {
|
||||||
val options =
|
val options =
|
||||||
@@ -45,4 +56,13 @@ class ConnectionMigrationOptionsTest {
|
|||||||
|
|
||||||
assertEquals(MIGRATION_OPTION_ENABLED, options.pathDegradationMigrationEnabled)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ package android.net.http.cts;
|
|||||||
import static android.net.http.cts.util.TestUtilsKt.assertOKStatusCode;
|
import static android.net.http.cts.util.TestUtilsKt.assertOKStatusCode;
|
||||||
import static android.net.http.cts.util.TestUtilsKt.skipIfNoInternetConnection;
|
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.MatcherAssert.assertThat;
|
||||||
import static org.hamcrest.Matchers.greaterThan;
|
import static org.hamcrest.Matchers.greaterThan;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -40,15 +42,19 @@ import android.net.http.cts.util.TestStatusListener;
|
|||||||
import android.net.http.cts.util.TestUploadDataProvider;
|
import android.net.http.cts.util.TestUploadDataProvider;
|
||||||
import android.net.http.cts.util.TestUrlRequestCallback;
|
import android.net.http.cts.util.TestUrlRequestCallback;
|
||||||
import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
|
import android.net.http.cts.util.TestUrlRequestCallback.ResponseStep;
|
||||||
|
import android.net.http.cts.util.UploadDataProviders;
|
||||||
|
|
||||||
import androidx.test.core.app.ApplicationProvider;
|
import androidx.test.core.app.ApplicationProvider;
|
||||||
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
import androidx.test.ext.junit.runners.AndroidJUnit4;
|
||||||
|
|
||||||
|
import com.google.common.base.Strings;
|
||||||
|
|
||||||
import org.junit.After;
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
import org.junit.runner.RunWith;
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import java.net.URLEncoder;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.concurrent.ArrayBlockingQueue;
|
import java.util.concurrent.ArrayBlockingQueue;
|
||||||
@@ -279,6 +285,31 @@ public class UrlRequestTest {
|
|||||||
assertTrue(e.getCause().getMessage().contains("full"));
|
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 {
|
private static class StubUrlRequestCallback extends UrlRequest.Callback {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -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() {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user