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