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