Migrate CTS test users of UploadDataProvider to a single canonical impl.
As agreed previously we should only have a single implementation of an
upload data provider in tests, unless the details of interactions
between the provider and Cronet internals are tested.
Test: atest CtsNetHttpTestCases
Change-Id: I00ea3fab56a23ae58e9a07db61c1cabf58c566fc
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 e4949d3..422f4d5 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/UrlRequestTest.java
@@ -34,13 +34,11 @@
import android.net.http.HttpException;
import android.net.http.InlineExecutionProhibitedException;
import android.net.http.UploadDataProvider;
-import android.net.http.UploadDataSink;
import android.net.http.UrlRequest;
import android.net.http.UrlRequest.Status;
import android.net.http.UrlResponseInfo;
import android.net.http.cts.util.HttpCtsTestServer;
import android.net.http.cts.util.TestStatusListener;
-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;
@@ -140,14 +138,11 @@
}
@Test
- public void testUrlRequestPost_EchoRequestBody() throws Exception {
+ public void testUrlRequestPost_EchoRequestBody() {
String testData = "test";
UrlRequest.Builder builder = createUrlRequestBuilder(mTestServer.getEchoBodyUrl());
- TestUploadDataProvider dataProvider =
- new TestUploadDataProvider(
- TestUploadDataProvider.SuccessCallbackMode.SYNC, mCallback.getExecutor());
- dataProvider.addRead(testData.getBytes());
+ UploadDataProvider dataProvider = UploadDataProviders.create(testData);
builder.setUploadDataProvider(dataProvider, mCallback.getExecutor());
builder.addHeader("Content-Type", "text/html");
builder.build().start();
@@ -155,7 +150,6 @@
assertOKStatusCode(mCallback.mResponseInfo);
assertEquals(testData, mCallback.mResponseAsString);
- dataProvider.assertClosed();
}
@Test
@@ -170,7 +164,7 @@
callback.setAllowDirectExecutor(true);
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback);
- UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test");
+ UploadDataProvider dataProvider = UploadDataProviders.create("test");
builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR);
builder.addHeader("Content-Type", "text/plain;charset=UTF-8");
builder.setDirectExecutorAllowed(true);
@@ -193,7 +187,7 @@
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
mTestServer.getEchoBodyUrl(), Executors.newSingleThreadExecutor(), callback);
- UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test");
+ UploadDataProvider dataProvider = UploadDataProviders.create("test");
builder.setUploadDataProvider(dataProvider, DIRECT_EXECUTOR)
.addHeader("Content-Type", "text/plain;charset=UTF-8")
@@ -213,7 +207,7 @@
UrlRequest.Builder builder = mHttpEngine.newUrlRequestBuilder(
mTestServer.getEchoBodyUrl(), DIRECT_EXECUTOR, callback);
- UploadDataProvider dataProvider = InMemoryUploadDataProvider.fromUtf8String("test");
+ UploadDataProvider dataProvider = UploadDataProviders.create("test");
builder.setUploadDataProvider(dataProvider, Executors.newSingleThreadExecutor())
.addHeader("Content-Type", "text/plain;charset=UTF-8")
@@ -415,39 +409,4 @@
throw new UnsupportedOperationException();
}
}
-
- private static class InMemoryUploadDataProvider extends UploadDataProvider {
- private final byte[] mBody;
- private int mNextChunkStartIndex = 0;
-
- private InMemoryUploadDataProvider(byte[] body) {
- this.mBody = body;
- }
-
- static InMemoryUploadDataProvider fromUtf8String(String body) {
- return new InMemoryUploadDataProvider(body.getBytes(StandardCharsets.UTF_8));
- }
-
- @Override
- public long getLength() {
- return mBody.length;
- }
-
- @Override
- public void read(UploadDataSink uploadDataSink, ByteBuffer byteBuffer) {
- if (mNextChunkStartIndex >= getLength()) {
- throw new IllegalStateException("Body of known length is exhausted");
- }
- int nextChunkSize =
- Math.min(mBody.length - mNextChunkStartIndex, byteBuffer.remaining());
- byteBuffer.put(mBody, mNextChunkStartIndex, nextChunkSize);
- mNextChunkStartIndex += nextChunkSize;
- uploadDataSink.onReadSucceeded(false);
- }
-
- @Override
- public void rewind(UploadDataSink uploadDataSink) {
- mNextChunkStartIndex = 0;
- }
- }
}
diff --git a/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java b/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java
deleted file mode 100644
index d047828..0000000
--- a/Cronet/tests/cts/src/android/net/http/cts/util/TestUploadDataProvider.java
+++ /dev/null
@@ -1,294 +0,0 @@
-/*
- * Copyright (C) 2022 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.ConditionVariable;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.ClosedChannelException;
-import java.util.ArrayList;
-import java.util.concurrent.Executor;
-import java.util.concurrent.atomic.AtomicBoolean;
-
-/** An UploadDataProvider implementation used in tests. */
-public class TestUploadDataProvider extends UploadDataProvider {
- // Indicates whether all success callbacks are synchronous or asynchronous.
- // Doesn't apply to errors.
- public enum SuccessCallbackMode {
- SYNC,
- ASYNC
- }
-
- // Indicates whether failures should throw exceptions, invoke callbacks
- // synchronously, or invoke callback asynchronously.
- public enum FailMode {
- NONE,
- THROWN,
- CALLBACK_SYNC,
- CALLBACK_ASYNC
- }
-
- private final ArrayList<byte[]> mReads = new ArrayList<byte[]>();
- private final SuccessCallbackMode mSuccessCallbackMode;
- private final Executor mExecutor;
-
- private boolean mChunked;
-
- // Index of read to fail on.
- private int mReadFailIndex = -1;
- // Indicates how to fail on a read.
- private FailMode mReadFailMode = FailMode.NONE;
-
- private FailMode mRewindFailMode = FailMode.NONE;
-
- private FailMode mLengthFailMode = FailMode.NONE;
-
- private int mNumReadCalls;
- private int mNumRewindCalls;
-
- private int mNextRead;
- private boolean mStarted;
- private boolean mReadPending;
- private boolean mRewindPending;
- // Used to ensure there are no read/rewind requests after a failure.
- private boolean mFailed;
-
- private final AtomicBoolean mClosed = new AtomicBoolean(false);
- private final ConditionVariable mAwaitingClose = new ConditionVariable(false);
-
- public TestUploadDataProvider(
- SuccessCallbackMode successCallbackMode, final Executor executor) {
- mSuccessCallbackMode = successCallbackMode;
- mExecutor = executor;
- }
-
- // Adds the result to be returned by a successful read request. The
- // returned bytes must all fit within the read buffer provided by Cronet.
- // After a rewind, if there is one, all reads will be repeated.
- public void addRead(byte[] read) {
- if (mStarted) {
- throw new IllegalStateException("Adding bytes after read");
- }
- mReads.add(read);
- }
-
- public void setReadFailure(int readFailIndex, FailMode readFailMode) {
- mReadFailIndex = readFailIndex;
- mReadFailMode = readFailMode;
- }
-
- public void setLengthFailure() {
- mLengthFailMode = FailMode.THROWN;
- }
-
- public void setRewindFailure(FailMode rewindFailMode) {
- mRewindFailMode = rewindFailMode;
- }
-
- public void setChunked(boolean chunked) {
- mChunked = chunked;
- }
-
- public int getNumReadCalls() {
- return mNumReadCalls;
- }
-
- public int getNumRewindCalls() {
- return mNumRewindCalls;
- }
-
- /** Returns the cumulative length of all data added by calls to addRead. */
- @Override
- public long getLength() throws IOException {
- if (mClosed.get()) {
- throw new ClosedChannelException();
- }
- if (mLengthFailMode == FailMode.THROWN) {
- throw new IllegalStateException("Sync length failure");
- }
- return getUploadedLength();
- }
-
- public long getUploadedLength() {
- if (mChunked) {
- return -1;
- }
- long length = 0;
- for (byte[] read : mReads) {
- length += read.length;
- }
- return length;
- }
-
- @Override
- public void read(final UploadDataSink uploadDataSink, final ByteBuffer byteBuffer)
- throws IOException {
- int currentReadCall = mNumReadCalls;
- ++mNumReadCalls;
- if (mClosed.get()) {
- throw new ClosedChannelException();
- }
- assertIdle();
-
- if (maybeFailRead(currentReadCall, uploadDataSink)) {
- mFailed = true;
- return;
- }
-
- mReadPending = true;
- mStarted = true;
-
- final boolean finalChunk = (mChunked && mNextRead == mReads.size() - 1);
- if (mNextRead < mReads.size()) {
- if ((byteBuffer.limit() - byteBuffer.position()) < mReads.get(mNextRead).length) {
- throw new IllegalStateException("Read buffer smaller than expected.");
- }
- byteBuffer.put(mReads.get(mNextRead));
- ++mNextRead;
- } else {
- throw new IllegalStateException("Too many reads: " + mNextRead);
- }
-
- Runnable completeRunnable =
- new Runnable() {
- @Override
- public void run() {
- mReadPending = false;
- uploadDataSink.onReadSucceeded(finalChunk);
- }
- };
- if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
- completeRunnable.run();
- } else {
- mExecutor.execute(completeRunnable);
- }
- }
-
- @Override
- public void rewind(final UploadDataSink uploadDataSink) throws IOException {
- ++mNumRewindCalls;
- if (mClosed.get()) {
- throw new ClosedChannelException();
- }
- assertIdle();
-
- if (maybeFailRewind(uploadDataSink)) {
- mFailed = true;
- return;
- }
-
- if (mNextRead == 0) {
- // Should never try and rewind when rewinding does nothing.
- throw new IllegalStateException("Unexpected rewind when already at beginning");
- }
-
- mRewindPending = true;
- mNextRead = 0;
-
- Runnable completeRunnable =
- new Runnable() {
- @Override
- public void run() {
- mRewindPending = false;
- uploadDataSink.onRewindSucceeded();
- }
- };
- if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
- completeRunnable.run();
- } else {
- mExecutor.execute(completeRunnable);
- }
- }
-
- private void assertIdle() {
- if (mReadPending) {
- throw new IllegalStateException("Unexpected operation during read");
- }
- if (mRewindPending) {
- throw new IllegalStateException("Unexpected operation during rewind");
- }
- if (mFailed) {
- throw new IllegalStateException("Unexpected operation after failure");
- }
- }
-
- private boolean maybeFailRead(int readIndex, final UploadDataSink uploadDataSink) {
- if (readIndex != mReadFailIndex) return false;
-
- switch (mReadFailMode) {
- case THROWN:
- throw new IllegalStateException("Thrown read failure");
- case CALLBACK_SYNC:
- uploadDataSink.onReadError(new IllegalStateException("Sync read failure"));
- return true;
- case CALLBACK_ASYNC:
- Runnable errorRunnable =
- new Runnable() {
- @Override
- public void run() {
- uploadDataSink.onReadError(
- new IllegalStateException("Async read failure"));
- }
- };
- mExecutor.execute(errorRunnable);
- return true;
- default:
- return false;
- }
- }
-
- private boolean maybeFailRewind(final UploadDataSink uploadDataSink) {
- switch (mRewindFailMode) {
- case THROWN:
- throw new IllegalStateException("Thrown rewind failure");
- case CALLBACK_SYNC:
- uploadDataSink.onRewindError(new IllegalStateException("Sync rewind failure"));
- return true;
- case CALLBACK_ASYNC:
- Runnable errorRunnable =
- new Runnable() {
- @Override
- public void run() {
- uploadDataSink.onRewindError(
- new IllegalStateException("Async rewind failure"));
- }
- };
- mExecutor.execute(errorRunnable);
- return true;
- default:
- return false;
- }
- }
-
- @Override
- public void close() throws IOException {
- if (!mClosed.compareAndSet(false, true)) {
- throw new AssertionError("Closed twice");
- }
- mAwaitingClose.open();
- }
-
- public void assertClosed() {
- mAwaitingClose.block(5000);
- if (!mClosed.get()) {
- throw new AssertionError("Was not closed");
- }
- }
-}
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
index 889f8f2..3b90fa0 100644
--- a/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java
+++ b/Cronet/tests/cts/src/android/net/http/cts/util/UploadDataProviders.java
@@ -25,6 +25,7 @@
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
+import java.nio.charset.StandardCharsets;
/**
* Provides implementations of {@link UploadDataProvider} for common use cases. Corresponds to
@@ -91,6 +92,16 @@
return create(data, 0, data.length);
}
+ /**
+ * Uploads the UTF-8 representation of {@code data}
+ *
+ * @param data String containing data to upload
+ * @return A new UploadDataProvider for the given data
+ */
+ public static UploadDataProvider create(String data) {
+ return create(data.getBytes(StandardCharsets.UTF_8));
+ }
+
private interface FileChannelProvider {
FileChannel getChannel() throws IOException;
}