• 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 static com.google.common.truth.Truth.assertThat;
8 
9 import androidx.test.ext.junit.runners.AndroidJUnit4;
10 import androidx.test.filters.SmallTest;
11 
12 import org.junit.After;
13 import org.junit.Before;
14 import org.junit.Rule;
15 import org.junit.Test;
16 import org.junit.runner.RunWith;
17 
18 import org.chromium.base.test.util.DoNotBatch;
19 import org.chromium.net.CronetTestRule.CronetImplementation;
20 import org.chromium.net.CronetTestRule.IgnoreFor;
21 import org.chromium.net.impl.CronetUploadDataStream;
22 import org.chromium.net.impl.CronetUrlRequest;
23 
24 import java.util.Arrays;
25 import java.util.List;
26 import java.util.concurrent.ExecutorService;
27 import java.util.concurrent.Executors;
28 
29 /**
30  * Tests that directly drive {@code CronetUploadDataStream} and {@code UploadDataProvider} to
31  * simulate different ordering of reset, init, read, and rewind calls.
32  */
33 @DoNotBatch(reason = "crbug/1459563")
34 @RunWith(AndroidJUnit4.class)
35 @IgnoreFor(
36         implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM},
37         reason = "crbug.com/1494845: Testing internals of the native implementation")
38 public class CronetUploadTest {
39     @Rule public final CronetTestRule mTestRule = CronetTestRule.withAutomaticEngineStartup();
40 
41     private TestDrivenDataProvider mDataProvider;
42     private CronetUploadDataStream mUploadDataStream;
43     private TestUploadDataStreamHandler mHandler;
44 
45     @Before
46     @SuppressWarnings({"PrimitiveArrayPassedToVarargsMethod", "ArraysAsListPrimitiveArray"})
setUp()47     public void setUp() throws Exception {
48         ExecutorService executor = Executors.newSingleThreadExecutor();
49         List<byte[]> reads = Arrays.asList("hello".getBytes());
50         mDataProvider = new TestDrivenDataProvider(executor, reads);
51 
52         // Creates a no-op CronetUrlRequest, which is not used to drive CronetUploadDataStream.
53         TestUrlRequestCallback callback = new TestUrlRequestCallback();
54         UrlRequest.Builder builder =
55                 mTestRule
56                         .getTestFramework()
57                         .getEngine()
58                         .newUrlRequestBuilder(
59                                 "https://no-op.url", callback, callback.getExecutor());
60         UrlRequest urlRequest = builder.build();
61 
62         mUploadDataStream =
63                 new CronetUploadDataStream(mDataProvider, executor, (CronetUrlRequest) urlRequest);
64         mHandler =
65                 new TestUploadDataStreamHandler(
66                         mTestRule.getTestFramework().getContext(),
67                         mUploadDataStream.createUploadDataStreamForTesting());
68     }
69 
70     @After
tearDown()71     public void tearDown() throws Exception {
72         // Destroy handler's native objects.
73         mHandler.destroyNativeObjects();
74     }
75 
76     /**
77      * Tests that after some data is read, init triggers a rewind, and that before the rewind
78      * completes, init blocks.
79      */
80     @Test
81     @SmallTest
testInitTriggersRewindAndInitBeforeRewindCompletes()82     public void testInitTriggersRewindAndInitBeforeRewindCompletes() throws Exception {
83         // Init completes synchronously and read succeeds.
84         assertThat(mHandler.init()).isTrue();
85         mHandler.read();
86         mDataProvider.waitForReadRequest();
87         mHandler.checkReadCallbackNotInvoked();
88         mDataProvider.onReadSucceeded(mUploadDataStream);
89         mHandler.waitForReadComplete();
90         mDataProvider.assertReadNotPending();
91         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(0);
92         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(1);
93         assertThat(mHandler.getData()).isEqualTo("hello");
94 
95         // Reset and then init, which should trigger a rewind.
96         mHandler.reset();
97         assertThat(mHandler.getData()).isEmpty();
98         assertThat(mHandler.init()).isFalse();
99         mDataProvider.waitForRewindRequest();
100         mHandler.checkInitCallbackNotInvoked();
101 
102         // Before rewind completes, reset and init should block.
103         mHandler.reset();
104         assertThat(mHandler.init()).isFalse();
105 
106         // Signal rewind completes, and wait for init to complete.
107         mHandler.checkInitCallbackNotInvoked();
108         mDataProvider.onRewindSucceeded(mUploadDataStream);
109         mHandler.waitForInitComplete();
110         mDataProvider.assertRewindNotPending();
111 
112         // Read should complete successfully since init has completed.
113         mHandler.read();
114         mDataProvider.waitForReadRequest();
115         mHandler.checkReadCallbackNotInvoked();
116         mDataProvider.onReadSucceeded(mUploadDataStream);
117         mHandler.waitForReadComplete();
118         mDataProvider.assertReadNotPending();
119         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(1);
120         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(2);
121         assertThat(mHandler.getData()).isEqualTo("hello");
122     }
123 
124     /**
125      * Tests that after some data is read, init triggers a rewind, and that after the rewind
126      * completes, init does not block.
127      */
128     @Test
129     @SmallTest
testInitTriggersRewindAndInitAfterRewindCompletes()130     public void testInitTriggersRewindAndInitAfterRewindCompletes() throws Exception {
131         // Init completes synchronously and read succeeds.
132         assertThat(mHandler.init()).isTrue();
133         mHandler.read();
134         mDataProvider.waitForReadRequest();
135         mHandler.checkReadCallbackNotInvoked();
136         mDataProvider.onReadSucceeded(mUploadDataStream);
137         mHandler.waitForReadComplete();
138         mDataProvider.assertReadNotPending();
139         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(0);
140         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(1);
141         assertThat(mHandler.getData()).isEqualTo("hello");
142 
143         // Reset and then init, which should trigger a rewind.
144         mHandler.reset();
145         assertThat(mHandler.getData()).isEmpty();
146         assertThat(mHandler.init()).isFalse();
147         mDataProvider.waitForRewindRequest();
148         mHandler.checkInitCallbackNotInvoked();
149 
150         // Signal rewind completes, and wait for init to complete.
151         mDataProvider.onRewindSucceeded(mUploadDataStream);
152         mHandler.waitForInitComplete();
153         mDataProvider.assertRewindNotPending();
154 
155         // Reset and init should not block, since rewind has completed.
156         mHandler.reset();
157         assertThat(mHandler.init()).isTrue();
158 
159         // Read should complete successfully since init has completed.
160         mHandler.read();
161         mDataProvider.waitForReadRequest();
162         mHandler.checkReadCallbackNotInvoked();
163         mDataProvider.onReadSucceeded(mUploadDataStream);
164         mHandler.waitForReadComplete();
165         mDataProvider.assertReadNotPending();
166         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(1);
167         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(2);
168         assertThat(mHandler.getData()).isEqualTo("hello");
169     }
170 
171     /** Tests that if init before read completes, a rewind is triggered when read completes. */
172     @Test
173     @SmallTest
testReadCompleteTriggerRewind()174     public void testReadCompleteTriggerRewind() throws Exception {
175         // Reset and init before read completes.
176         assertThat(mHandler.init()).isTrue();
177         mHandler.read();
178         mDataProvider.waitForReadRequest();
179         mHandler.checkReadCallbackNotInvoked();
180         mHandler.reset();
181         // Init should return asynchronously, since there is a pending read.
182         assertThat(mHandler.init()).isFalse();
183         mDataProvider.assertRewindNotPending();
184         mHandler.checkInitCallbackNotInvoked();
185         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(0);
186         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(1);
187         assertThat(mHandler.getData()).isEmpty();
188 
189         // Read completes should trigger a rewind.
190         mDataProvider.onReadSucceeded(mUploadDataStream);
191         mDataProvider.waitForRewindRequest();
192         mHandler.checkInitCallbackNotInvoked();
193         mDataProvider.onRewindSucceeded(mUploadDataStream);
194         mHandler.waitForInitComplete();
195         mDataProvider.assertRewindNotPending();
196         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(1);
197         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(1);
198         assertThat(mHandler.getData()).isEmpty();
199     }
200 
201     /**
202      * Tests that when init again after rewind completes, no additional rewind is triggered. This
203      * test is the same as testReadCompleteTriggerRewind except that this test invokes reset and
204      * init again in the end.
205      */
206     @Test
207     @SmallTest
testReadCompleteTriggerRewindOnlyOneRewind()208     public void testReadCompleteTriggerRewindOnlyOneRewind() throws Exception {
209         testReadCompleteTriggerRewind();
210         // Reset and Init again, no rewind should happen.
211         mHandler.reset();
212         assertThat(mHandler.init()).isTrue();
213         mDataProvider.assertRewindNotPending();
214         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(1);
215         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(1);
216         assertThat(mHandler.getData()).isEmpty();
217     }
218 
219     /**
220      * Tests that if reset before read completes, no rewind is triggered, and that a following init
221      * triggers rewind.
222      */
223     @Test
224     @SmallTest
testResetBeforeReadCompleteAndInitTriggerRewind()225     public void testResetBeforeReadCompleteAndInitTriggerRewind() throws Exception {
226         // Reset before read completes. Rewind is not triggered.
227         assertThat(mHandler.init()).isTrue();
228         mHandler.read();
229         mDataProvider.waitForReadRequest();
230         mHandler.checkReadCallbackNotInvoked();
231         mHandler.reset();
232         mDataProvider.onReadSucceeded(mUploadDataStream);
233         mDataProvider.assertRewindNotPending();
234         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(0);
235         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(1);
236         assertThat(mHandler.getData()).isEmpty();
237 
238         // Init should trigger a rewind.
239         assertThat(mHandler.init()).isFalse();
240         mDataProvider.waitForRewindRequest();
241         mHandler.checkInitCallbackNotInvoked();
242         mDataProvider.onRewindSucceeded(mUploadDataStream);
243         mHandler.waitForInitComplete();
244         mDataProvider.assertRewindNotPending();
245         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(1);
246         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(1);
247         assertThat(mHandler.getData()).isEmpty();
248     }
249 
250     /**
251      * Tests that there is no crash when native CronetUploadDataStream is destroyed while read is
252      * pending. The test is racy since the read could complete either before or after the Java
253      * CronetUploadDataStream's onDestroyUploadDataStream() method is invoked. However, the test
254      * should pass either way, though we are interested in the latter case.
255      */
256     @Test
257     @SmallTest
testDestroyNativeStreamBeforeReadComplete()258     public void testDestroyNativeStreamBeforeReadComplete() throws Exception {
259         // Start a read and wait for it to be pending.
260         assertThat(mHandler.init()).isTrue();
261         mHandler.read();
262         mDataProvider.waitForReadRequest();
263         mHandler.checkReadCallbackNotInvoked();
264 
265         // Destroy the C++ TestUploadDataStreamHandler. The handler will then
266         // destroy the C++ CronetUploadDataStream it owns on the network thread.
267         // That will result in calling the Java CronetUploadDataSteam's
268         // onUploadDataStreamDestroyed() method on its executor thread, which
269         // will then destroy the CronetUploadDataStreamAdapter.
270         mHandler.destroyNativeObjects();
271 
272         // Make the read complete should not encounter a crash.
273         mDataProvider.onReadSucceeded(mUploadDataStream);
274 
275         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(0);
276         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(1);
277     }
278 
279     /**
280      * Tests that there is no crash when native CronetUploadDataStream is destroyed while rewind is
281      * pending. The test is racy since rewind could complete either before or after the Java
282      * CronetUploadDataStream's onDestroyUploadDataStream() method is invoked. However, the test
283      * should pass either way, though we are interested in the latter case.
284      */
285     @Test
286     @SmallTest
testDestroyNativeStreamBeforeRewindComplete()287     public void testDestroyNativeStreamBeforeRewindComplete() throws Exception {
288         // Start a read and wait for it to complete.
289         assertThat(mHandler.init()).isTrue();
290         mHandler.read();
291         mDataProvider.waitForReadRequest();
292         mHandler.checkReadCallbackNotInvoked();
293         mDataProvider.onReadSucceeded(mUploadDataStream);
294         mHandler.waitForReadComplete();
295         mDataProvider.assertReadNotPending();
296         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(0);
297         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(1);
298         assertThat(mHandler.getData()).isEqualTo("hello");
299 
300         // Reset and then init, which should trigger a rewind.
301         mHandler.reset();
302         assertThat(mHandler.getData()).isEmpty();
303         assertThat(mHandler.init()).isFalse();
304         mDataProvider.waitForRewindRequest();
305         mHandler.checkInitCallbackNotInvoked();
306 
307         // Destroy the C++ TestUploadDataStreamHandler. The handler will then
308         // destroy the C++ CronetUploadDataStream it owns on the network thread.
309         // That will result in calling the Java CronetUploadDataSteam's
310         // onUploadDataStreamDestroyed() method on its executor thread, which
311         // will then destroy the CronetUploadDataStreamAdapter.
312         mHandler.destroyNativeObjects();
313 
314         // Signal rewind completes, and wait for init to complete.
315         mDataProvider.onRewindSucceeded(mUploadDataStream);
316 
317         assertThat(mDataProvider.getNumRewindCalls()).isEqualTo(1);
318         assertThat(mDataProvider.getNumReadCalls()).isEqualTo(1);
319     }
320 }
321