| Index: components/cronet/android/test/javatests/src/org/chromium/cronet_test_apk/TestDataProvider.java
|
| diff --git a/components/cronet/android/test/javatests/src/org/chromium/cronet_test_apk/TestDataProvider.java b/components/cronet/android/test/javatests/src/org/chromium/cronet_test_apk/TestDataProvider.java
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..140bc99b8b11ba4a43cba6dc49c92b5050fa72ac
|
| --- /dev/null
|
| +++ b/components/cronet/android/test/javatests/src/org/chromium/cronet_test_apk/TestDataProvider.java
|
| @@ -0,0 +1,244 @@
|
| +// Copyright 2014 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +package org.chromium.cronet_test_apk;
|
| +
|
| +import org.chromium.net.UploadDataProvider;
|
| +import org.chromium.net.UploadDataSink;
|
| +
|
| +import java.nio.ByteBuffer;
|
| +import java.util.ArrayList;
|
| +import java.util.concurrent.Executor;
|
| +
|
| +class TestUploadDataProvider implements UploadDataProvider {
|
| + // Indicates whether all success callbacks are synchronous or asynchronous.
|
| + // Doesn't apply to errors.
|
| + enum SuccessCallbackMode {
|
| + SYNC,
|
| + ASYNC
|
| + };
|
| +
|
| + // Indicates whether failures should throw exceptions, invoke callbacks
|
| + // synchronously, or invoke callback asynchronously.
|
| + enum FailMode {
|
| + NONE,
|
| + THROWN,
|
| + CALLBACK_SYNC,
|
| + CALLBACK_ASYNC
|
| + };
|
| +
|
| + private ArrayList<byte[]> mReads = new ArrayList<byte[]>();
|
| + private final SuccessCallbackMode mSuccessCallbackMode;
|
| + private final Executor mExecutor;
|
| +
|
| + private boolean mChunked = false;
|
| +
|
| + // 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 int mNumReadCalls = 0;
|
| + private int mNumRewindCalls = 0;
|
| +
|
| + private int mNextRead = 0;
|
| + private boolean mStarted = false;
|
| + private boolean mReadPending = false;
|
| + private boolean mRewindPending = false;
|
| + // Used to ensure there are no read/rewind requests after a failure.
|
| + private boolean mFailed = false;
|
| +
|
| + TestUploadDataProvider(SuccessCallbackMode successCallbackMode,
|
| + 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 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() {
|
| + 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) {
|
| + int currentReadCall = mNumReadCalls;
|
| + ++mNumReadCalls;
|
| + assertIdle();
|
| +
|
| + if (maybeFailRead(currentReadCall, uploadDataSink)) {
|
| + mFailed = true;
|
| + return;
|
| + }
|
| +
|
| + mReadPending = true;
|
| + mStarted = true;
|
| +
|
| + if (mNextRead != mReads.size()) {
|
| + if (byteBuffer.capacity() < mReads.get(mNextRead).length) {
|
| + throw new IllegalStateException(
|
| + "Read buffer smaller than expected");
|
| + }
|
| + byteBuffer.put(mReads.get(mNextRead));
|
| + byteBuffer.flip();
|
| + ++mNextRead;
|
| + } else if (mChunked && mNextRead == mReads.size()) {
|
| + byteBuffer.flip();
|
| + } else {
|
| + throw new IllegalStateException(
|
| + "Too many reads: " + mNextRead);
|
| + }
|
| +
|
| + Runnable completeRunnable = new Runnable() {
|
| + @Override
|
| + public void run() {
|
| + mReadPending = false;
|
| + uploadDataSink.onReadSucceeded(false);
|
| + }
|
| + };
|
| + if (mSuccessCallbackMode == SuccessCallbackMode.SYNC) {
|
| + completeRunnable.run();
|
| + } else {
|
| + mExecutor.execute(completeRunnable);
|
| + }
|
| + }
|
| +
|
| + public void rewind(final UploadDataSink uploadDataSink) {
|
| + ++mNumRewindCalls;
|
| + 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;
|
| + }
|
| + }
|
| +}
|
|
|