• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.net;
6 
7 import android.os.ConditionVariable;
8 
9 import java.io.IOException;
10 import java.nio.ByteBuffer;
11 import java.util.List;
12 import java.util.concurrent.Executor;
13 
14 /**
15  * An UploadDataProvider that allows tests to invoke {@code onReadSucceeded}
16  * and {@code onRewindSucceeded} on the UploadDataSink directly.
17  * Chunked mode is not supported here, since the main interest is to test
18  * different order of init/read/rewind calls.
19  */
20 class TestDrivenDataProvider extends UploadDataProvider {
21     private final Executor mExecutor;
22     private final List<byte[]> mReads;
23     private final ConditionVariable mWaitForReadRequest = new ConditionVariable();
24     private final ConditionVariable mWaitForRewindRequest = new ConditionVariable();
25     // Lock used to synchronize access to mReadPending and mRewindPending.
26     private final Object mLock = new Object();
27 
28     private int mNextRead;
29 
30     // Only accessible when holding mLock.
31 
32     private boolean mReadPending;
33     private boolean mRewindPending;
34     private int mNumRewindCalls;
35     private int mNumReadCalls;
36 
37     /**
38      * Constructor.
39      * @param Executor executor. Executor to run callbacks of UploadDataSink.
40      * @param List<byte[]> reads. Results to be returned by successful read
41      *            requests. Returned bytes must all fit within the read buffer
42      *            provided by Cronet. After a rewind, if there is one, all reads
43      *            will be repeated.
44      */
TestDrivenDataProvider(Executor executor, List<byte[]> reads)45     TestDrivenDataProvider(Executor executor, List<byte[]> reads) {
46         mExecutor = executor;
47         mReads = reads;
48     }
49 
50     // Called by UploadDataSink on the main thread.
51     @Override
getLength()52     public long getLength() {
53         long length = 0;
54         for (byte[] read : mReads) {
55             length += read.length;
56         }
57         return length;
58     }
59 
60     // Called by UploadDataSink on the executor thread.
61     @Override
read(final UploadDataSink uploadDataSink, final ByteBuffer byteBuffer)62     public void read(final UploadDataSink uploadDataSink, final ByteBuffer byteBuffer)
63             throws IOException {
64         synchronized (mLock) {
65             ++mNumReadCalls;
66             assertIdle();
67 
68             mReadPending = true;
69             if (mNextRead != mReads.size()) {
70                 if ((byteBuffer.limit() - byteBuffer.position()) < mReads.get(mNextRead).length) {
71                     throw new IllegalStateException("Read buffer smaller than expected.");
72                 }
73                 byteBuffer.put(mReads.get(mNextRead));
74                 ++mNextRead;
75             } else {
76                 throw new IllegalStateException("Too many reads: " + mNextRead);
77             }
78             mWaitForReadRequest.open();
79         }
80     }
81 
82     // Called by UploadDataSink on the executor thread.
83     @Override
rewind(final UploadDataSink uploadDataSink)84     public void rewind(final UploadDataSink uploadDataSink) throws IOException {
85         synchronized (mLock) {
86             ++mNumRewindCalls;
87             assertIdle();
88 
89             if (mNextRead == 0) {
90                 // Should never try and rewind when rewinding does nothing.
91                 throw new IllegalStateException("Unexpected rewind when already at beginning");
92             }
93             mRewindPending = true;
94             mNextRead = 0;
95             mWaitForRewindRequest.open();
96         }
97     }
98 
99     // Called by test fixture on the main thread.
onReadSucceeded(final UploadDataSink uploadDataSink)100     public void onReadSucceeded(final UploadDataSink uploadDataSink) {
101         Runnable completeRunnable =
102                 new Runnable() {
103                     @Override
104                     public void run() {
105                         synchronized (mLock) {
106                             if (!mReadPending) {
107                                 throw new IllegalStateException("No read pending.");
108                             }
109                             mReadPending = false;
110                             uploadDataSink.onReadSucceeded(false);
111                         }
112                     }
113                 };
114         mExecutor.execute(completeRunnable);
115     }
116 
117     // Called by test fixture on the main thread.
onRewindSucceeded(final UploadDataSink uploadDataSink)118     public void onRewindSucceeded(final UploadDataSink uploadDataSink) {
119         Runnable completeRunnable =
120                 new Runnable() {
121                     @Override
122                     public void run() {
123                         synchronized (mLock) {
124                             if (!mRewindPending) {
125                                 throw new IllegalStateException("No rewind pending.");
126                             }
127                             mRewindPending = false;
128                             uploadDataSink.onRewindSucceeded();
129                         }
130                     }
131                 };
132         mExecutor.execute(completeRunnable);
133     }
134 
135     // Called by test fixture on the main thread.
getNumReadCalls()136     public int getNumReadCalls() {
137         synchronized (mLock) {
138             return mNumReadCalls;
139         }
140     }
141 
142     // Called by test fixture on the main thread.
getNumRewindCalls()143     public int getNumRewindCalls() {
144         synchronized (mLock) {
145             return mNumRewindCalls;
146         }
147     }
148 
149     // Called by test fixture on the main thread.
waitForReadRequest()150     public void waitForReadRequest() {
151         mWaitForReadRequest.block();
152     }
153 
154     // Called by test fixture on the main thread.
resetWaitForReadRequest()155     public void resetWaitForReadRequest() {
156         mWaitForReadRequest.close();
157     }
158 
159     // Called by test fixture on the main thread.
waitForRewindRequest()160     public void waitForRewindRequest() {
161         mWaitForRewindRequest.block();
162     }
163 
164     // Called by test fixture on the main thread.
assertReadNotPending()165     public void assertReadNotPending() {
166         synchronized (mLock) {
167             if (mReadPending) {
168                 throw new IllegalStateException("Read is pending.");
169             }
170         }
171     }
172 
173     // Called by test fixture on the main thread.
assertRewindNotPending()174     public void assertRewindNotPending() {
175         synchronized (mLock) {
176             if (mRewindPending) {
177                 throw new IllegalStateException("Rewind is pending.");
178             }
179         }
180     }
181 
182     /** Helper method to ensure no read or rewind is in progress. */
assertIdle()183     private void assertIdle() {
184         assertReadNotPending();
185         assertRewindNotPending();
186     }
187 }
188