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;
     }