1 // Copyright 2014 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 import static com.google.common.truth.TruthJUnit.assume; 9 10 import static org.junit.Assert.assertThrows; 11 12 import static org.chromium.net.CronetEngine.Builder.HTTP_CACHE_IN_MEMORY; 13 import static org.chromium.net.CronetTestRule.getTestStorage; 14 import static org.chromium.net.truth.UrlResponseInfoSubject.assertThat; 15 16 import android.net.Network; 17 import android.os.Build; 18 import android.os.Bundle; 19 import android.os.ConditionVariable; 20 import android.os.Handler; 21 import android.os.Looper; 22 import android.os.Process; 23 24 import androidx.test.ext.junit.runners.AndroidJUnit4; 25 import androidx.test.filters.SmallTest; 26 import com.android.testutils.SkipPresubmit; 27 28 import org.jni_zero.JNINamespace; 29 import org.jni_zero.NativeMethods; 30 import org.json.JSONObject; 31 import org.junit.After; 32 import org.junit.Before; 33 import org.junit.Rule; 34 import org.junit.Test; 35 import org.junit.runner.RunWith; 36 37 import org.chromium.base.Log; 38 import org.chromium.base.PathUtils; 39 import org.chromium.base.test.util.DoNotBatch; 40 import org.chromium.net.CronetTestRule.CronetImplementation; 41 import org.chromium.net.CronetTestRule.DisableAutomaticNetLog; 42 import org.chromium.net.CronetTestRule.IgnoreFor; 43 import org.chromium.net.CronetTestRule.RequiresMinAndroidApi; 44 import org.chromium.net.CronetTestRule.RequiresMinApi; 45 import org.chromium.net.NetworkChangeNotifierAutoDetect.ConnectivityManagerDelegate; 46 import org.chromium.net.TestUrlRequestCallback.ResponseStep; 47 import org.chromium.net.httpflags.BaseFeature; 48 import org.chromium.net.httpflags.FlagValue; 49 import org.chromium.net.httpflags.Flags; 50 import org.chromium.net.impl.CronetExceptionImpl; 51 import org.chromium.net.impl.CronetLibraryLoader; 52 import org.chromium.net.impl.CronetManifest; 53 import org.chromium.net.impl.CronetManifestInterceptor; 54 import org.chromium.net.impl.CronetUrlRequestContext; 55 import org.chromium.net.impl.ImplVersion; 56 import org.chromium.net.impl.NativeCronetEngineBuilderImpl; 57 import org.chromium.net.impl.NetworkExceptionImpl; 58 59 import java.io.BufferedReader; 60 import java.io.File; 61 import java.io.FileReader; 62 import java.net.URL; 63 import java.nio.ByteBuffer; 64 import java.util.Arrays; 65 import java.util.UUID; 66 import java.util.concurrent.Callable; 67 import java.util.concurrent.Executor; 68 import java.util.concurrent.FutureTask; 69 import java.util.concurrent.atomic.AtomicReference; 70 71 /** Test CronetEngine. */ 72 @DoNotBatch(reason = "crbug/1459563") 73 @RunWith(AndroidJUnit4.class) 74 @JNINamespace("cronet") 75 public class CronetUrlRequestContextTest { 76 @Rule public final CronetTestRule mTestRule = CronetTestRule.withManualEngineStartup(); 77 78 private static final String TAG = "CronetUrlReqCtxTest"; 79 // URLs used for tests. 80 private static final String MOCK_CRONET_TEST_FAILED_URL = "http://mock.failed.request/-2"; 81 private static final String MOCK_CRONET_TEST_SUCCESS_URL = "http://mock.http/success.txt"; 82 private static final int MAX_FILE_SIZE = 1000000000; 83 84 private String mUrl; 85 private String mUrl404; 86 private String mUrl500; 87 88 @Before setUp()89 public void setUp() throws Exception { 90 NativeTestServer.startNativeTestServer(mTestRule.getTestFramework().getContext()); 91 mUrl = NativeTestServer.getSuccessURL(); 92 mUrl404 = NativeTestServer.getNotFoundURL(); 93 mUrl500 = NativeTestServer.getServerErrorURL(); 94 } 95 96 @After tearDown()97 public void tearDown() throws Exception { 98 NativeTestServer.shutdownNativeTestServer(); 99 } 100 101 class RequestThread extends Thread { 102 public TestUrlRequestCallback mCallback; 103 104 final String mUrl; 105 final ConditionVariable mRunBlocker; 106 RequestThread(String url, ConditionVariable runBlocker)107 public RequestThread(String url, ConditionVariable runBlocker) { 108 mUrl = url; 109 mRunBlocker = runBlocker; 110 } 111 112 @Override run()113 public void run() { 114 mRunBlocker.block(); 115 ExperimentalCronetEngine cronetEngine = 116 mTestRule 117 .getTestFramework() 118 .createNewSecondaryBuilder(mTestRule.getTestFramework().getContext()) 119 .build(); 120 try { 121 mCallback = new TestUrlRequestCallback(); 122 UrlRequest.Builder urlRequestBuilder = 123 cronetEngine.newUrlRequestBuilder(mUrl, mCallback, mCallback.getExecutor()); 124 urlRequestBuilder.build().start(); 125 mCallback.blockForDone(); 126 } finally { 127 cronetEngine.shutdown(); 128 } 129 } 130 } 131 132 /** Callback that shutdowns the request context when request has succeeded or failed. */ 133 static class ShutdownTestUrlRequestCallback extends TestUrlRequestCallback { 134 private final CronetEngine mCronetEngine; 135 private final ConditionVariable mCallbackCompletionBlock = new ConditionVariable(); 136 ShutdownTestUrlRequestCallback(CronetEngine cronetEngine)137 ShutdownTestUrlRequestCallback(CronetEngine cronetEngine) { 138 mCronetEngine = cronetEngine; 139 } 140 141 @Override onSucceeded(UrlRequest request, UrlResponseInfo info)142 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 143 super.onSucceeded(request, info); 144 mCronetEngine.shutdown(); 145 mCallbackCompletionBlock.open(); 146 } 147 148 @Override onFailed(UrlRequest request, UrlResponseInfo info, CronetException error)149 public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) { 150 super.onFailed(request, info, error); 151 mCronetEngine.shutdown(); 152 mCallbackCompletionBlock.open(); 153 } 154 155 // Wait for request completion callback. blockForCallbackToComplete()156 void blockForCallbackToComplete() { 157 mCallbackCompletionBlock.block(); 158 } 159 } 160 setReadHttpFlagsInManifest(boolean value)161 private void setReadHttpFlagsInManifest(boolean value) { 162 Bundle metaData = new Bundle(); 163 metaData.putBoolean(CronetManifest.READ_HTTP_FLAGS_META_DATA_KEY, value); 164 mTestRule.getTestFramework().interceptContext(new CronetManifestInterceptor(metaData)); 165 } 166 setLogFlag(String marker, String appId, String minVersion)167 private void setLogFlag(String marker, String appId, String minVersion) { 168 FlagValue.ConstrainedValue.Builder constrainedValueBuilder = 169 FlagValue.ConstrainedValue.newBuilder() 170 .setStringValue("Test log flag value " + marker); 171 if (appId != null) { 172 constrainedValueBuilder.setAppId(appId); 173 } 174 if (minVersion != null) { 175 constrainedValueBuilder.setMinVersion(minVersion); 176 } 177 mTestRule 178 .getTestFramework() 179 .setHttpFlags( 180 Flags.newBuilder() 181 .putFlags( 182 CronetLibraryLoader.LOG_FLAG_NAME, 183 FlagValue.newBuilder() 184 .addConstrainedValues(constrainedValueBuilder) 185 .build()) 186 .build()); 187 } 188 runOneRequest()189 private void runOneRequest() { 190 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 191 CronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 192 UrlRequest.Builder urlRequestBuilder = 193 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 194 urlRequestBuilder.build().start(); 195 callback.blockForDone(); 196 } 197 runRequestWhileExpectingLog(String marker, boolean shouldBeLogged)198 private void runRequestWhileExpectingLog(String marker, boolean shouldBeLogged) 199 throws Exception { 200 try (LogcatCapture logcatSink = 201 new LogcatCapture( 202 Arrays.asList( 203 Log.normalizeTag(CronetLibraryLoader.TAG + ":I"), 204 Log.normalizeTag(TAG + ":I"), 205 "chromium:I"))) { 206 // Use the engine at least once to ensure we do not race against Cronet initialization. 207 runOneRequest(); 208 209 String stopMarker = UUID.randomUUID().toString(); 210 Log.i(TAG, "%s --- ENGINE STARTED ---", stopMarker); 211 212 if (shouldBeLogged) { 213 while (true) { 214 String line = logcatSink.readLine(); 215 assertThat(line).doesNotContain(stopMarker); 216 if (line.contains(marker)) break; 217 } 218 while (!logcatSink.readLine().contains(stopMarker)) {} 219 } else { 220 while (true) { 221 String line = logcatSink.readLine(); 222 assertThat(line).doesNotContain(marker); 223 if (line.contains(stopMarker)) break; 224 } 225 } 226 } 227 } 228 229 @Test 230 @SmallTest 231 @IgnoreFor( 232 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 233 reason = 234 "HTTP flags are only supported on native Cronet for now. " 235 + "crbug.com/1495401: Emulator image does not have HttpFlags code yet") testHttpFlagsAreLoaded()236 public void testHttpFlagsAreLoaded() throws Exception { 237 setReadHttpFlagsInManifest(true); 238 String marker = UUID.randomUUID().toString(); 239 setLogFlag(marker, /* appId= */ null, /* minVersion= */ null); 240 runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ true); 241 } 242 243 @Test 244 @SmallTest 245 @IgnoreFor( 246 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 247 reason = 248 "HTTP flags are only supported on native Cronet for now. " 249 + "crbug.com/1495401: Emulator image does not have HttpFlags code yet") testHttpFlagsAreNotLoadedIfDisabledInManifest()250 public void testHttpFlagsAreNotLoadedIfDisabledInManifest() throws Exception { 251 setReadHttpFlagsInManifest(false); 252 String marker = UUID.randomUUID().toString(); 253 setLogFlag(marker, /* appId= */ null, /* minVersion= */ null); 254 runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ false); 255 } 256 257 @Test 258 @SmallTest 259 @IgnoreFor( 260 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 261 reason = 262 "HTTP flags are only supported on native Cronet for now. " 263 + "crbug.com/1495401: Emulator image does not have HttpFlags code yet") testHttpFlagsNotAppliedIfAppIdDoesntMatch()264 public void testHttpFlagsNotAppliedIfAppIdDoesntMatch() throws Exception { 265 setReadHttpFlagsInManifest(true); 266 String marker = UUID.randomUUID().toString(); 267 setLogFlag(marker, /* appId= */ "org.chromium.fake.app.id", /* minVersion= */ null); 268 runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ false); 269 } 270 271 @Test 272 @SmallTest 273 @IgnoreFor( 274 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 275 reason = 276 "HTTP flags are only supported on native Cronet for now. " 277 + "crbug.com/1495401: Emulator image does not have HttpFlags code yet") testHttpFlagsAppliedIfAppIdMatches()278 public void testHttpFlagsAppliedIfAppIdMatches() throws Exception { 279 setReadHttpFlagsInManifest(true); 280 String marker = UUID.randomUUID().toString(); 281 setLogFlag( 282 marker, 283 /* appId= */ mTestRule.getTestFramework().getContext().getPackageName(), 284 /* minVersion= */ ImplVersion.getCronetVersion()); 285 runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ true); 286 } 287 288 @Test 289 @SmallTest 290 @IgnoreFor( 291 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 292 reason = 293 "HTTP flags are only supported on native Cronet for now. " 294 + "crbug.com/1495401: Emulator image does not have HttpFlags code yet") testHttpFlagsNotAppliedIfBelowMinVersion()295 public void testHttpFlagsNotAppliedIfBelowMinVersion() throws Exception { 296 setReadHttpFlagsInManifest(true); 297 String marker = UUID.randomUUID().toString(); 298 setLogFlag(marker, /* appId= */ null, /* minVersion= */ "999999.0.0.0"); 299 runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ false); 300 } 301 302 @Test 303 @SmallTest 304 @IgnoreFor( 305 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 306 reason = 307 "HTTP flags are only supported on native Cronet for now. " 308 + "crbug.com/1495401: Emulator image does not have HttpFlags code yet") testHttpFlagsAppliedIfAtMinVersion()309 public void testHttpFlagsAppliedIfAtMinVersion() throws Exception { 310 setReadHttpFlagsInManifest(true); 311 String marker = UUID.randomUUID().toString(); 312 setLogFlag(marker, /* appId= */ null, /* minVersion= */ ImplVersion.getCronetVersion()); 313 runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ true); 314 } 315 316 @Test 317 @SmallTest 318 @IgnoreFor( 319 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 320 reason = 321 "HTTP flags are only supported on native Cronet for now. " 322 + "crbug.com/1495401: Emulator image does not have HttpFlags code yet") testHttpFlagsAppliedIfAboveMinVersion()323 public void testHttpFlagsAppliedIfAboveMinVersion() throws Exception { 324 setReadHttpFlagsInManifest(true); 325 String marker = UUID.randomUUID().toString(); 326 setLogFlag(marker, /* appId= */ null, /* minVersion= */ "100.0.0.0"); 327 runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ true); 328 } 329 setChromiumBaseFeatureLogFlag(boolean enable, String marker)330 private void setChromiumBaseFeatureLogFlag(boolean enable, String marker) { 331 var flags = 332 Flags.newBuilder() 333 .putFlags( 334 BaseFeature.FLAG_PREFIX + "CronetLogMe", 335 FlagValue.newBuilder() 336 .addConstrainedValues( 337 FlagValue.ConstrainedValue.newBuilder() 338 .setBoolValue(enable)) 339 .build()) 340 .putFlags( 341 BaseFeature.FLAG_PREFIX 342 + "CronetLogMe" 343 + BaseFeature.PARAM_DELIMITER 344 + "message", 345 FlagValue.newBuilder() 346 .addConstrainedValues( 347 FlagValue.ConstrainedValue.newBuilder() 348 .setStringValue(marker)) 349 .build()) 350 .build(); 351 mTestRule.getTestFramework().setHttpFlags(flags); 352 } 353 354 @Test 355 @SmallTest 356 @IgnoreFor( 357 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 358 reason = 359 "HTTP flags are only supported on native Cronet for now. " 360 + "crbug.com/1495401: Emulator image does not have HttpFlags code yet") testBaseFeatureFlagsOverridesEnabled()361 public void testBaseFeatureFlagsOverridesEnabled() throws Exception { 362 setReadHttpFlagsInManifest(true); 363 String marker = UUID.randomUUID().toString(); 364 setChromiumBaseFeatureLogFlag(true, marker); 365 runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ true); 366 } 367 368 @Test 369 @SmallTest 370 @IgnoreFor( 371 implementations = {CronetImplementation.FALLBACK}, 372 reason = "HTTP flags are only supported on native Cronet for now") testBaseFeatureFlagsOverridesDisabled()373 public void testBaseFeatureFlagsOverridesDisabled() throws Exception { 374 setReadHttpFlagsInManifest(true); 375 String marker = UUID.randomUUID().toString(); 376 setChromiumBaseFeatureLogFlag(false, marker); 377 runRequestWhileExpectingLog(marker, /* shouldBeLogged= */ false); 378 } 379 380 @Test 381 @SmallTest 382 @SuppressWarnings("deprecation") testConfigUserAgent()383 public void testConfigUserAgent() throws Exception { 384 String userAgentName = "User-Agent"; 385 String userAgentValue = "User-Agent-Value"; 386 387 mTestRule 388 .getTestFramework() 389 .applyEngineBuilderPatch((builder) -> builder.setUserAgent(userAgentValue)); 390 391 CronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 392 NativeTestServer.shutdownNativeTestServer(); // startNativeTestServer returns false if it's 393 // already running 394 assertThat( 395 NativeTestServer.startNativeTestServer( 396 mTestRule.getTestFramework().getContext())) 397 .isTrue(); 398 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 399 UrlRequest.Builder urlRequestBuilder = 400 cronetEngine.newUrlRequestBuilder( 401 NativeTestServer.getEchoHeaderURL(userAgentName), 402 callback, 403 callback.getExecutor()); 404 urlRequestBuilder.build().start(); 405 callback.blockForDone(); 406 assertThat(callback.mResponseAsString).isEqualTo(userAgentValue); 407 } 408 409 @Test 410 @SmallTest 411 @IgnoreFor( 412 implementations = {CronetImplementation.FALLBACK}, 413 reason = "Fallback implementation does not check for outstanding requests") testShutdown()414 public void testShutdown() throws Exception { 415 CronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 416 ShutdownTestUrlRequestCallback callback = new ShutdownTestUrlRequestCallback(cronetEngine); 417 // Block callback when response starts to verify that shutdown fails 418 // if there are active requests. 419 callback.setAutoAdvance(false); 420 UrlRequest.Builder urlRequestBuilder = 421 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 422 UrlRequest urlRequest = urlRequestBuilder.build(); 423 urlRequest.start(); 424 425 Exception e = assertThrows(Exception.class, cronetEngine::shutdown); 426 assertThat(e).hasMessageThat().matches("Cannot shutdown with (running|active) requests."); 427 428 callback.waitForNextStep(); 429 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED); 430 431 e = assertThrows(Exception.class, cronetEngine::shutdown); 432 assertThat(e).hasMessageThat().matches("Cannot shutdown with (running|active) requests."); 433 434 callback.startNextRead(urlRequest); 435 436 callback.waitForNextStep(); 437 438 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_READ_COMPLETED); 439 e = assertThrows(Exception.class, cronetEngine::shutdown); 440 assertThat(e).hasMessageThat().matches("Cannot shutdown with (running|active) requests."); 441 442 // May not have read all the data, in theory. Just enable auto-advance 443 // and finish the request. 444 callback.setAutoAdvance(true); 445 callback.startNextRead(urlRequest); 446 callback.blockForDone(); 447 callback.blockForCallbackToComplete(); 448 callback.shutdownExecutor(); 449 } 450 451 @Test 452 @SmallTest 453 @IgnoreFor( 454 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 455 reason = "Tests native implementation internals") testShutdownDuringInit()456 public void testShutdownDuringInit() throws Exception { 457 final ConditionVariable block = new ConditionVariable(false); 458 459 // Post a task to main thread to block until shutdown is called to test 460 // scenario when shutdown is called right after construction before 461 // context is fully initialized on the main thread. 462 Runnable blockingTask = 463 new Runnable() { 464 @Override 465 public void run() { 466 block.block(); 467 } 468 }; 469 // Ensure that test is not running on the main thread. 470 assertThat(Looper.getMainLooper()).isNotEqualTo(Looper.myLooper()); 471 new Handler(Looper.getMainLooper()).post(blockingTask); 472 473 // Create new request context, but its initialization on the main thread 474 // will be stuck behind blockingTask. 475 CronetUrlRequestContext cronetEngine = 476 (CronetUrlRequestContext) 477 mTestRule 478 .getTestFramework() 479 .createNewSecondaryBuilder( 480 mTestRule.getTestFramework().getContext()) 481 .build(); 482 // Unblock the main thread, so context gets initialized and shutdown on 483 // it. 484 block.open(); 485 // Shutdown will wait for init to complete on main thread. 486 cronetEngine.shutdown(); 487 // Verify that context is shutdown. 488 Exception e = assertThrows(Exception.class, cronetEngine::getUrlRequestContextAdapter); 489 assertThat(e).hasMessageThat().isEqualTo("Engine is shut down."); 490 } 491 492 @Test 493 @SmallTest 494 @IgnoreFor( 495 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 496 reason = "Tests native implementation internals") testInitAndShutdownOnMainThread()497 public void testInitAndShutdownOnMainThread() throws Exception { 498 final ConditionVariable block = new ConditionVariable(false); 499 500 // Post a task to main thread to init and shutdown on the main thread. 501 Runnable blockingTask = 502 () -> { 503 // Create new request context, loading the library. 504 final CronetUrlRequestContext cronetEngine = 505 (CronetUrlRequestContext) 506 mTestRule 507 .getTestFramework() 508 .createNewSecondaryBuilder( 509 mTestRule.getTestFramework().getContext()) 510 .build(); 511 // Shutdown right after init. 512 cronetEngine.shutdown(); 513 // Verify that context is shutdown. 514 Exception e = 515 assertThrows( 516 Exception.class, cronetEngine::getUrlRequestContextAdapter); 517 assertThat(e).hasMessageThat().isEqualTo("Engine is shut down."); 518 block.open(); 519 }; 520 new Handler(Looper.getMainLooper()).post(blockingTask); 521 // Wait for shutdown to complete on main thread. 522 block.block(); 523 } 524 525 @Test 526 @SmallTest 527 @IgnoreFor( 528 implementations = {CronetImplementation.FALLBACK}, 529 reason = "JavaCronetEngine doesn't support throwing on repeat shutdown()") testMultipleShutdown()530 public void testMultipleShutdown() throws Exception { 531 CronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 532 cronetEngine.shutdown(); 533 Exception e = assertThrows(Exception.class, cronetEngine::shutdown); 534 assertThat(e).hasMessageThat().isEqualTo("Engine is shut down."); 535 } 536 537 @Test 538 @SmallTest testShutdownAfterError()539 public void testShutdownAfterError() throws Exception { 540 CronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 541 ShutdownTestUrlRequestCallback callback = new ShutdownTestUrlRequestCallback(cronetEngine); 542 UrlRequest.Builder urlRequestBuilder = 543 cronetEngine.newUrlRequestBuilder( 544 MOCK_CRONET_TEST_FAILED_URL, callback, callback.getExecutor()); 545 urlRequestBuilder.build().start(); 546 callback.blockForDone(); 547 assertThat(callback.mOnErrorCalled).isTrue(); 548 callback.blockForCallbackToComplete(); 549 callback.shutdownExecutor(); 550 } 551 552 @Test 553 @SmallTest 554 @IgnoreFor( 555 implementations = {CronetImplementation.FALLBACK}, 556 reason = "JavaCronetEngine doesn't support throwing on shutdown()") testShutdownAfterCancel()557 public void testShutdownAfterCancel() throws Exception { 558 CronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 559 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 560 // Block callback when response starts to verify that shutdown fails 561 // if there are active requests. 562 callback.setAutoAdvance(false); 563 UrlRequest.Builder urlRequestBuilder = 564 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 565 UrlRequest urlRequest = urlRequestBuilder.build(); 566 urlRequest.start(); 567 568 Exception e = assertThrows(Exception.class, cronetEngine::shutdown); 569 assertThat(e).hasMessageThat().matches("Cannot shutdown with (running|active) requests."); 570 571 callback.waitForNextStep(); 572 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED); 573 urlRequest.cancel(); 574 } 575 576 @Test 577 @SmallTest 578 @IgnoreFor( 579 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 580 reason = "Tests native implementation internals") 581 @RequiresMinAndroidApi(Build.VERSION_CODES.M) // Multi-network API is supported from Marshmallow testNetworkBoundContextLifetime()582 public void testNetworkBoundContextLifetime() throws Exception { 583 // Multi-network API is available starting from Android Lollipop. 584 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 585 ConnectivityManagerDelegate delegate = 586 new ConnectivityManagerDelegate(mTestRule.getTestFramework().getContext()); 587 Network defaultNetwork = delegate.getDefaultNetwork(); 588 assume().that(defaultNetwork).isNotNull(); 589 590 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 591 // Allows to check the underlying network-bound context state while the request is in 592 // progress. 593 callback.setAutoAdvance(false); 594 595 ExperimentalUrlRequest.Builder urlRequestBuilder = 596 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 597 urlRequestBuilder.bindToNetwork(defaultNetwork.getNetworkHandle()); 598 UrlRequest urlRequest = urlRequestBuilder.build(); 599 assertThat(ApiHelper.doesContextExistForNetwork(cronetEngine, defaultNetwork)).isFalse(); 600 urlRequest.start(); 601 assertThat(ApiHelper.doesContextExistForNetwork(cronetEngine, defaultNetwork)).isTrue(); 602 603 // Resume callback execution. 604 callback.waitForNextStep(); 605 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED); 606 callback.setAutoAdvance(true); 607 callback.startNextRead(urlRequest); 608 callback.blockForDone(); 609 assertThat(callback.mError).isNull(); 610 611 // The default network should still be active, hence the underlying network-bound context 612 // should still be there. 613 assertThat(ApiHelper.doesContextExistForNetwork(cronetEngine, defaultNetwork)).isTrue(); 614 615 // Fake disconnect event for the default network, this should destroy the underlying 616 // network-bound context. 617 FutureTask<Void> task = 618 new FutureTask<Void>( 619 new Callable<Void>() { 620 @Override 621 public Void call() { 622 NetworkChangeNotifier.fakeNetworkDisconnected( 623 defaultNetwork.getNetworkHandle()); 624 return null; 625 } 626 }); 627 CronetLibraryLoader.postToInitThread(task); 628 task.get(); 629 assertThat(ApiHelper.doesContextExistForNetwork(cronetEngine, defaultNetwork)).isFalse(); 630 } 631 632 @Test 633 @SmallTest 634 @IgnoreFor( 635 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 636 reason = "Tests native implementation internals") 637 @RequiresMinAndroidApi(Build.VERSION_CODES.M) // Multi-network API is supported from Marshmallow testNetworkBoundRequestCancel()638 public void testNetworkBoundRequestCancel() throws Exception { 639 // Upon a network disconnection, NCN posts a tasks onto the network thread that calls 640 // CronetContext::NetworkTasks::OnNetworkDisconnected. 641 // Calling urlRequest.cancel() also, after some hoops, ends up in a posted tasks onto the 642 // network thread that calls CronetURLRequest::NetworkTasks::Destroy. 643 // Depending on their implementation this can lead to UAF, this test is here to prevent that 644 // from being introduced in the future. 645 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 646 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 647 callback.setAutoAdvance(false); 648 ExperimentalUrlRequest.Builder urlRequestBuilder = 649 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 650 ConnectivityManagerDelegate delegate = 651 new ConnectivityManagerDelegate(mTestRule.getTestFramework().getContext()); 652 Network defaultNetwork = delegate.getDefaultNetwork(); 653 assume().that(defaultNetwork).isNotNull(); 654 655 urlRequestBuilder.bindToNetwork(defaultNetwork.getNetworkHandle()); 656 UrlRequest urlRequest = urlRequestBuilder.build(); 657 658 assertThat(ApiHelper.doesContextExistForNetwork(cronetEngine, defaultNetwork)).isFalse(); 659 urlRequest.start(); 660 assertThat(ApiHelper.doesContextExistForNetwork(cronetEngine, defaultNetwork)).isTrue(); 661 662 callback.waitForNextStep(); 663 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_RESPONSE_STARTED); 664 assertThat(ApiHelper.doesContextExistForNetwork(cronetEngine, defaultNetwork)).isTrue(); 665 // Cronet registers for NCN notifications on the init thread (see 666 // CronetLibraryLoader#ensureInitializedOnInitThread), hence we need to trigger fake 667 // notifications from there. 668 CronetLibraryLoader.postToInitThread( 669 new Runnable() { 670 @Override 671 public void run() { 672 NetworkChangeNotifier.fakeNetworkDisconnected( 673 defaultNetwork.getNetworkHandle()); 674 // Queue cancel after disconnect event. 675 urlRequest.cancel(); 676 } 677 }); 678 // Wait until the cancel call propagates (this would block undefinitely without that since 679 // we previously set auto advance to false). 680 callback.blockForDone(); 681 // mError should be null due to urlRequest.cancel(). 682 assertThat(callback.mError).isNull(); 683 // urlRequest.cancel(); should destroy the underlying network bound context. 684 assertThat(ApiHelper.doesContextExistForNetwork(cronetEngine, defaultNetwork)).isFalse(); 685 } 686 687 @Test 688 @RequiresMinAndroidApi(Build.VERSION_CODES.M) testBindToInvalidNetworkFails()689 public void testBindToInvalidNetworkFails() { 690 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 691 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 692 if (mTestRule.implementationUnderTest() == CronetImplementation.AOSP_PLATFORM) { 693 // HttpEngine#bindToNetwork requires an android.net.Network object. So, in this case, it 694 // will be the wrapper layer that will fail to translate that to a Network, not 695 // something in net's code. Hence, the failure will manifest itself at bind time, not at 696 // request execution time. 697 // Note: this will never happen in prod, as translation failure can only happen if we're 698 // given a fake networkHandle. 699 assertThrows( 700 IllegalArgumentException.class, 701 () -> cronetEngine.bindToNetwork(-150 /* invalid network handle */)); 702 return; 703 } 704 705 cronetEngine.bindToNetwork(-150 /* invalid network handle */); 706 ExperimentalUrlRequest.Builder builder = 707 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 708 builder.build().start(); 709 callback.blockForDone(); 710 711 assertThat(callback.mError).isNotNull(); 712 if (mTestRule.implementationUnderTest() == CronetImplementation.FALLBACK) { 713 assertThat(callback.mError).isInstanceOf(CronetExceptionImpl.class); 714 assertThat(callback.mError).hasCauseThat().isInstanceOf(NetworkExceptionImpl.class); 715 } else { 716 assertThat(callback.mError).isInstanceOf(NetworkExceptionImpl.class); 717 } 718 } 719 720 @Test 721 @RequiresMinAndroidApi(Build.VERSION_CODES.M) testBindToDefaultNetworkSucceeds()722 public void testBindToDefaultNetworkSucceeds() { 723 ConnectivityManagerDelegate delegate = 724 new ConnectivityManagerDelegate(mTestRule.getTestFramework().getContext()); 725 Network defaultNetwork = delegate.getDefaultNetwork(); 726 assume().that(defaultNetwork).isNotNull(); 727 728 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 729 cronetEngine.bindToNetwork(defaultNetwork.getNetworkHandle()); 730 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 731 ExperimentalUrlRequest.Builder builder = 732 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 733 builder.build().start(); 734 callback.blockForDone(); 735 736 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 737 } 738 739 @Test 740 @SmallTest 741 @IgnoreFor( 742 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 743 reason = "NetLog is supported only by the native implementation") 744 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") testNetLog()745 public void testNetLog() throws Exception { 746 File directory = new File(PathUtils.getDataDirectory()); 747 File file = File.createTempFile("cronet", "json", directory); 748 CronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 749 // Start NetLog immediately after the request context is created to make 750 // sure that the call won't crash the app even when the native request 751 // context is not fully initialized. See crbug.com/470196. 752 cronetEngine.startNetLogToFile(file.getPath(), false); 753 754 // Start a request. 755 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 756 UrlRequest.Builder urlRequestBuilder = 757 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 758 urlRequestBuilder.build().start(); 759 callback.blockForDone(); 760 cronetEngine.stopNetLog(); 761 assertThat(file.exists()).isTrue(); 762 assertThat(file.length()).isNotEqualTo(0); 763 assertThat(hasBytesInNetLog(file)).isFalse(); 764 assertThat(file.delete()).isTrue(); 765 assertThat(file.exists()).isFalse(); 766 } 767 768 @Test 769 @SmallTest 770 @IgnoreFor( 771 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 772 reason = "NetLog is supported only by the native implementation") 773 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") testBoundedFileNetLog()774 public void testBoundedFileNetLog() throws Exception { 775 File directory = new File(PathUtils.getDataDirectory()); 776 File netLogDir = new File(directory, "NetLog" + System.currentTimeMillis()); 777 assertThat(netLogDir.exists()).isFalse(); 778 assertThat(netLogDir.mkdir()).isTrue(); 779 File logFile = new File(netLogDir, "netlog.json"); 780 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 781 // Start NetLog immediately after the request context is created to make 782 // sure that the call won't crash the app even when the native request 783 // context is not fully initialized. See crbug.com/470196. 784 cronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 785 786 // Start a request. 787 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 788 UrlRequest.Builder urlRequestBuilder = 789 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 790 urlRequestBuilder.build().start(); 791 callback.blockForDone(); 792 cronetEngine.stopNetLog(); 793 assertThat(logFile.exists()).isTrue(); 794 assertThat(logFile.length()).isNotEqualTo(0); 795 assertThat(hasBytesInNetLog(logFile)).isFalse(); 796 FileUtils.recursivelyDeleteFile(netLogDir); 797 assertThat(netLogDir.exists()).isFalse(); 798 } 799 800 @Test 801 @SmallTest 802 @IgnoreFor( 803 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 804 reason = "NetLog is supported only by the native implementation") 805 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") 806 // Tests that if stopNetLog is not explicitly called, CronetEngine.shutdown() 807 // will take care of it. crbug.com/623701. testNoStopNetLog()808 public void testNoStopNetLog() throws Exception { 809 File directory = new File(PathUtils.getDataDirectory()); 810 File file = File.createTempFile("cronet", "json", directory); 811 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 812 cronetEngine.startNetLogToFile(file.getPath(), false); 813 814 // Start a request. 815 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 816 UrlRequest.Builder urlRequestBuilder = 817 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 818 urlRequestBuilder.build().start(); 819 callback.blockForDone(); 820 // Shut down the engine without calling stopNetLog. 821 cronetEngine.shutdown(); 822 assertThat(file.exists()).isTrue(); 823 assertThat(file.length()).isNotEqualTo(0); 824 assertThat(hasBytesInNetLog(file)).isFalse(); 825 assertThat(file.delete()).isTrue(); 826 assertThat(file.exists()).isFalse(); 827 } 828 829 @Test 830 @SmallTest 831 @IgnoreFor( 832 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 833 reason = "NetLog is supported only by the native implementation") 834 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") 835 // Tests that if stopNetLog is not explicitly called, CronetEngine.shutdown() 836 // will take care of it. crbug.com/623701. testNoStopBoundedFileNetLog()837 public void testNoStopBoundedFileNetLog() throws Exception { 838 File directory = new File(PathUtils.getDataDirectory()); 839 File netLogDir = new File(directory, "NetLog" + System.currentTimeMillis()); 840 assertThat(netLogDir.exists()).isFalse(); 841 assertThat(netLogDir.mkdir()).isTrue(); 842 File logFile = new File(netLogDir, "netlog.json"); 843 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 844 cronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 845 846 // Start a request. 847 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 848 UrlRequest.Builder urlRequestBuilder = 849 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 850 urlRequestBuilder.build().start(); 851 callback.blockForDone(); 852 // Shut down the engine without calling stopNetLog. 853 cronetEngine.shutdown(); 854 assertThat(logFile.exists()).isTrue(); 855 assertThat(logFile.length()).isNotEqualTo(0); 856 857 FileUtils.recursivelyDeleteFile(netLogDir); 858 assertThat(netLogDir.exists()).isFalse(); 859 } 860 861 @Test 862 @SmallTest 863 @SkipPresubmit(reason = "b/293141085 flaky test") 864 @IgnoreFor( 865 implementations = {CronetImplementation.AOSP_PLATFORM}, 866 reason = "ActiveRequestCount is not available in AOSP") testGetActiveRequestCount()867 public void testGetActiveRequestCount() throws Exception { 868 CronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 869 TestUrlRequestCallback callback1 = new TestUrlRequestCallback(); 870 TestUrlRequestCallback callback2 = new TestUrlRequestCallback(); 871 callback1.setAutoAdvance(false); 872 callback2.setAutoAdvance(false); 873 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 874 UrlRequest request1 = 875 cronetEngine.newUrlRequestBuilder(mUrl, callback1, callback1.getExecutor()).build(); 876 UrlRequest request2 = 877 cronetEngine.newUrlRequestBuilder(mUrl, callback2, callback2.getExecutor()).build(); 878 request1.start(); 879 request2.start(); 880 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(2); 881 callback1.waitForNextStep(); 882 callback1.setAutoAdvance(true); 883 callback1.startNextRead(request1); 884 callback1.blockForDone(); 885 waitForActiveRequestCount(cronetEngine, 1); 886 callback2.waitForNextStep(); 887 callback2.setAutoAdvance(true); 888 callback2.startNextRead(request2); 889 callback2.blockForDone(); 890 waitForActiveRequestCount(cronetEngine, 0); 891 } 892 893 @Test 894 @SmallTest 895 @IgnoreFor( 896 implementations = {CronetImplementation.AOSP_PLATFORM}, 897 reason = "ActiveRequestCount is not available in AOSP") testGetActiveRequestCountOnReachingSucceeded()898 public void testGetActiveRequestCountOnReachingSucceeded() throws Exception { 899 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 900 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 901 callback.setAutoAdvance(false); 902 callback.setBlockOnTerminalState(true); 903 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 904 UrlRequest request = 905 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()).build(); 906 request.start(); 907 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 908 callback.waitForNextStep(); 909 callback.startNextRead(request); 910 callback.waitForNextStep(); 911 callback.startNextRead(request); 912 callback.blockForDone(); 913 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 914 callback.setBlockOnTerminalState(false); 915 waitForActiveRequestCount(cronetEngine, 0); 916 } 917 918 @Test 919 @SmallTest 920 @IgnoreFor( 921 implementations = {CronetImplementation.AOSP_PLATFORM}, 922 reason = "ActiveRequestCount is not available in AOSP") testGetActiveRequestCountOnReachingCancel()923 public void testGetActiveRequestCountOnReachingCancel() throws Exception { 924 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 925 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 926 callback.setAutoAdvance(false); 927 callback.setBlockOnTerminalState(true); 928 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 929 UrlRequest request = 930 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()).build(); 931 request.start(); 932 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 933 request.cancel(); 934 callback.blockForDone(); 935 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 936 callback.setBlockOnTerminalState(false); 937 assertThat(callback.mOnCanceledCalled).isTrue(); 938 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_CANCELED); 939 waitForActiveRequestCount(cronetEngine, 0); 940 } 941 942 @Test 943 @SmallTest 944 @IgnoreFor( 945 implementations = {CronetImplementation.AOSP_PLATFORM}, 946 reason = "ActiveRequestCount is not available in AOSP") testGetActiveRequestCountOnReachingFail()947 public void testGetActiveRequestCountOnReachingFail() throws Exception { 948 final String badUrl = "www.unreachable-url.com"; 949 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 950 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 951 callback.setAutoAdvance(false); 952 callback.setBlockOnTerminalState(true); 953 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 954 UrlRequest request = 955 cronetEngine.newUrlRequestBuilder(badUrl, callback, callback.getExecutor()).build(); 956 request.start(); 957 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 958 callback.blockForDone(); 959 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 960 callback.setBlockOnTerminalState(false); 961 assertThat(callback.mOnErrorCalled).isTrue(); 962 assertThat(callback.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); 963 waitForActiveRequestCount(cronetEngine, 0); 964 } 965 966 @Test 967 @SmallTest 968 @IgnoreFor( 969 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 970 reason = 971 "crbug.com/1494901: Broken for JavaCronetEngine. " 972 + "ActiveRequestCount is not available in AOSP") testGetActiveRequestCountOnDoubleStart()973 public void testGetActiveRequestCountOnDoubleStart() throws Exception { 974 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 975 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 976 callback.setAutoAdvance(false); 977 UrlRequest request = 978 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()).build(); 979 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 980 request.start(); 981 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 982 assertThrows(Exception.class, request::start); 983 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 984 callback.setAutoAdvance(true); 985 callback.blockForDone(); 986 waitForActiveRequestCount(cronetEngine, 0); 987 } 988 989 @Test 990 @SmallTest 991 @IgnoreFor( 992 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 993 reason = 994 "JavaCronetEngine currently never throws directly from start. " 995 + "ActiveRequestCount is not available in AOSP") testGetActiveRequestCountOnInvalidRequest()996 public void testGetActiveRequestCountOnInvalidRequest() throws Exception { 997 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 998 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 999 UrlRequest request = 1000 cronetEngine 1001 .newUrlRequestBuilder("", callback, callback.getExecutor()) 1002 .setHttpMethod("") 1003 .build(); 1004 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 1005 assertThrows(Exception.class, request::start); 1006 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 1007 } 1008 1009 @Test 1010 @SmallTest 1011 @IgnoreFor( 1012 implementations = {CronetImplementation.AOSP_PLATFORM}, 1013 reason = "ActiveRequestCount is not available in AOSP") testGetActiveRequestCountWithCancel()1014 public void testGetActiveRequestCountWithCancel() throws Exception { 1015 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1016 TestUrlRequestCallback callback1 = new TestUrlRequestCallback(); 1017 TestUrlRequestCallback callback2 = new TestUrlRequestCallback(); 1018 callback1.setAutoAdvance(false); 1019 callback2.setAutoAdvance(false); 1020 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 1021 UrlRequest request1 = 1022 cronetEngine.newUrlRequestBuilder(mUrl, callback1, callback1.getExecutor()).build(); 1023 UrlRequest request2 = 1024 cronetEngine.newUrlRequestBuilder(mUrl, callback2, callback2.getExecutor()).build(); 1025 request1.start(); 1026 request2.start(); 1027 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(2); 1028 request1.cancel(); 1029 callback1.blockForDone(); 1030 assertThat(callback1.mOnCanceledCalled).isTrue(); 1031 assertThat(callback1.mResponseStep).isEqualTo(ResponseStep.ON_CANCELED); 1032 waitForActiveRequestCount(cronetEngine, 1); 1033 callback2.waitForNextStep(); 1034 callback2.setAutoAdvance(true); 1035 callback2.startNextRead(request2); 1036 callback2.blockForDone(); 1037 waitForActiveRequestCount(cronetEngine, 0); 1038 } 1039 1040 @Test 1041 @SmallTest 1042 @SkipPresubmit(reason = "b/293141085 flaky test") 1043 @IgnoreFor( 1044 implementations = {CronetImplementation.AOSP_PLATFORM}, 1045 reason = "ActiveRequestCount is not available in AOSP") testGetActiveRequestCountWithError()1046 public void testGetActiveRequestCountWithError() throws Exception { 1047 final String badUrl = "www.unreachable-url.com"; 1048 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1049 TestUrlRequestCallback callback1 = new TestUrlRequestCallback(); 1050 TestUrlRequestCallback callback2 = new TestUrlRequestCallback(); 1051 callback1.setAutoAdvance(false); 1052 callback1.setBlockOnTerminalState(true); 1053 callback2.setAutoAdvance(false); 1054 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 1055 UrlRequest request1 = 1056 cronetEngine 1057 .newUrlRequestBuilder(badUrl, callback1, callback1.getExecutor()) 1058 .build(); 1059 UrlRequest request2 = 1060 cronetEngine.newUrlRequestBuilder(mUrl, callback2, callback2.getExecutor()).build(); 1061 request1.start(); 1062 request2.start(); 1063 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(2); 1064 callback1.setBlockOnTerminalState(false); 1065 callback1.blockForDone(); 1066 assertThat(callback1.mOnErrorCalled).isTrue(); 1067 assertThat(callback1.mResponseStep).isEqualTo(ResponseStep.ON_FAILED); 1068 waitForActiveRequestCount(cronetEngine, 1); 1069 callback2.waitForNextStep(); 1070 callback2.setAutoAdvance(true); 1071 callback2.startNextRead(request2); 1072 callback2.blockForDone(); 1073 waitForActiveRequestCount(cronetEngine, 0); 1074 } 1075 1076 @Test 1077 @SmallTest 1078 @IgnoreFor( 1079 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1080 reason = "Request finished listeners are only supported by native Cronet") testGetActiveRequestCountOnRequestFinishedListener()1081 public void testGetActiveRequestCountOnRequestFinishedListener() throws Exception { 1082 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1083 TestRequestFinishedListener requestFinishedListener = new TestRequestFinishedListener(); 1084 requestFinishedListener.blockListener(); 1085 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1086 UrlRequest request = 1087 cronetEngine 1088 .newUrlRequestBuilder(mUrl, callback, callback.getExecutor()) 1089 .setRequestFinishedListener(requestFinishedListener) 1090 .build(); 1091 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 1092 request.start(); 1093 callback.blockForDone(); 1094 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 1095 requestFinishedListener.blockUntilDone(); 1096 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 1097 requestFinishedListener.unblockListener(); 1098 waitForActiveRequestCount(cronetEngine, 0); 1099 } 1100 1101 @Test 1102 @SmallTest 1103 @IgnoreFor( 1104 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1105 reason = "Request finished listeners are only supported by native Cronet") testGetActiveRequestCountOnThrowingRequestFinishedListener()1106 public void testGetActiveRequestCountOnThrowingRequestFinishedListener() throws Exception { 1107 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1108 TestRequestFinishedListener requestFinishedListener = new TestRequestFinishedListener(); 1109 requestFinishedListener.makeListenerThrow(); 1110 requestFinishedListener.blockListener(); 1111 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1112 UrlRequest request = 1113 cronetEngine 1114 .newUrlRequestBuilder(mUrl, callback, callback.getExecutor()) 1115 .setRequestFinishedListener(requestFinishedListener) 1116 .build(); 1117 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 1118 request.start(); 1119 callback.blockForDone(); 1120 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 1121 requestFinishedListener.blockUntilDone(); 1122 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 1123 requestFinishedListener.unblockListener(); 1124 waitForActiveRequestCount(cronetEngine, 0); 1125 } 1126 1127 @Test 1128 @SmallTest 1129 @IgnoreFor( 1130 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1131 reason = "Request finished listeners are only supported by native Cronet") testGetActiveRequestCountOnThrowingEngineRequestFinishedListener()1132 public void testGetActiveRequestCountOnThrowingEngineRequestFinishedListener() 1133 throws Exception { 1134 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1135 TestRequestFinishedListener requestFinishedListener = new TestRequestFinishedListener(); 1136 requestFinishedListener.makeListenerThrow(); 1137 requestFinishedListener.blockListener(); 1138 cronetEngine.addRequestFinishedListener(requestFinishedListener); 1139 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1140 UrlRequest request = 1141 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()).build(); 1142 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 1143 request.start(); 1144 callback.blockForDone(); 1145 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 1146 requestFinishedListener.blockUntilDone(); 1147 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 1148 requestFinishedListener.unblockListener(); 1149 waitForActiveRequestCount(cronetEngine, 0); 1150 } 1151 1152 @Test 1153 @SmallTest 1154 @IgnoreFor( 1155 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1156 reason = "Request finished listeners are only supported by native Cronet") testGetActiveRequestCountOnEngineRequestFinishedListener()1157 public void testGetActiveRequestCountOnEngineRequestFinishedListener() throws Exception { 1158 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1159 TestRequestFinishedListener requestFinishedListener = new TestRequestFinishedListener(); 1160 requestFinishedListener.blockListener(); 1161 cronetEngine.addRequestFinishedListener(requestFinishedListener); 1162 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1163 UrlRequest request = 1164 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()).build(); 1165 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(0); 1166 request.start(); 1167 callback.blockForDone(); 1168 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 1169 requestFinishedListener.blockUntilDone(); 1170 assertThat(cronetEngine.getActiveRequestCount()).isEqualTo(1); 1171 requestFinishedListener.unblockListener(); 1172 waitForActiveRequestCount(cronetEngine, 0); 1173 } 1174 1175 @Test 1176 @SmallTest 1177 @IgnoreFor( 1178 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1179 reason = "NetLog is supported only by the native implementation") 1180 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") 1181 // Tests that NetLog contains events emitted by all live CronetEngines. testNetLogContainEventsFromAllLiveEngines()1182 public void testNetLogContainEventsFromAllLiveEngines() throws Exception { 1183 File directory = new File(PathUtils.getDataDirectory()); 1184 File file1 = File.createTempFile("cronet1", "json", directory); 1185 File file2 = File.createTempFile("cronet2", "json", directory); 1186 CronetEngine cronetEngine1 = 1187 mTestRule 1188 .getTestFramework() 1189 .createNewSecondaryBuilder(mTestRule.getTestFramework().getContext()) 1190 .build(); 1191 CronetEngine cronetEngine2 = 1192 mTestRule 1193 .getTestFramework() 1194 .createNewSecondaryBuilder(mTestRule.getTestFramework().getContext()) 1195 .build(); 1196 1197 cronetEngine1.startNetLogToFile(file1.getPath(), false); 1198 cronetEngine2.startNetLogToFile(file2.getPath(), false); 1199 1200 // Warm CronetEngine and make sure both CronetUrlRequestContexts are 1201 // initialized before testing the logs. 1202 makeRequestAndCheckStatus(cronetEngine1, mUrl, 200); 1203 makeRequestAndCheckStatus(cronetEngine2, mUrl, 200); 1204 1205 // Use cronetEngine1 to make a request to mUrl404. 1206 makeRequestAndCheckStatus(cronetEngine1, mUrl404, 404); 1207 1208 // Use cronetEngine2 to make a request to mUrl500. 1209 makeRequestAndCheckStatus(cronetEngine2, mUrl500, 500); 1210 1211 cronetEngine1.stopNetLog(); 1212 cronetEngine2.stopNetLog(); 1213 assertThat(file1.exists()).isTrue(); 1214 assertThat(file2.exists()).isTrue(); 1215 // Make sure both files contain the two requests made separately using 1216 // different engines. 1217 assertThat(containsStringInNetLog(file1, mUrl404)).isTrue(); 1218 assertThat(containsStringInNetLog(file1, mUrl500)).isTrue(); 1219 assertThat(containsStringInNetLog(file2, mUrl404)).isTrue(); 1220 assertThat(containsStringInNetLog(file2, mUrl500)).isTrue(); 1221 assertThat(file1.delete()).isTrue(); 1222 assertThat(file2.delete()).isTrue(); 1223 } 1224 1225 @Test 1226 @SmallTest 1227 @IgnoreFor( 1228 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1229 reason = "NetLog is supported only by the native implementation") 1230 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") 1231 // Tests that NetLog contains events emitted by all live CronetEngines. testBoundedFileNetLogContainEventsFromAllLiveEngines()1232 public void testBoundedFileNetLogContainEventsFromAllLiveEngines() throws Exception { 1233 File directory = new File(PathUtils.getDataDirectory()); 1234 File netLogDir1 = new File(directory, "NetLog1" + System.currentTimeMillis()); 1235 assertThat(netLogDir1.exists()).isFalse(); 1236 assertThat(netLogDir1.mkdir()).isTrue(); 1237 File netLogDir2 = new File(directory, "NetLog2" + System.currentTimeMillis()); 1238 assertThat(netLogDir2.exists()).isFalse(); 1239 assertThat(netLogDir2.mkdir()).isTrue(); 1240 File logFile1 = new File(netLogDir1, "netlog.json"); 1241 File logFile2 = new File(netLogDir2, "netlog.json"); 1242 1243 CronetEngine cronetEngine1 = 1244 mTestRule 1245 .getTestFramework() 1246 .createNewSecondaryBuilder(mTestRule.getTestFramework().getContext()) 1247 .build(); 1248 CronetEngine cronetEngine2 = 1249 mTestRule 1250 .getTestFramework() 1251 .createNewSecondaryBuilder(mTestRule.getTestFramework().getContext()) 1252 .build(); 1253 1254 cronetEngine1.startNetLogToDisk(netLogDir1.getPath(), false, MAX_FILE_SIZE); 1255 cronetEngine2.startNetLogToDisk(netLogDir2.getPath(), false, MAX_FILE_SIZE); 1256 1257 // Warm CronetEngine and make sure both CronetUrlRequestContexts are 1258 // initialized before testing the logs. 1259 makeRequestAndCheckStatus(cronetEngine1, mUrl, 200); 1260 makeRequestAndCheckStatus(cronetEngine2, mUrl, 200); 1261 1262 // Use cronetEngine1 to make a request to mUrl404. 1263 makeRequestAndCheckStatus(cronetEngine1, mUrl404, 404); 1264 1265 // Use cronetEngine2 to make a request to mUrl500. 1266 makeRequestAndCheckStatus(cronetEngine2, mUrl500, 500); 1267 1268 cronetEngine1.stopNetLog(); 1269 cronetEngine2.stopNetLog(); 1270 1271 assertThat(logFile1.exists()).isTrue(); 1272 assertThat(logFile2.exists()).isTrue(); 1273 assertThat(logFile1.length()).isNotEqualTo(0); 1274 assertThat(logFile2.length()).isNotEqualTo(0); 1275 1276 // Make sure both files contain the two requests made separately using 1277 // different engines. 1278 assertThat(containsStringInNetLog(logFile1, mUrl404)).isTrue(); 1279 assertThat(containsStringInNetLog(logFile1, mUrl500)).isTrue(); 1280 assertThat(containsStringInNetLog(logFile2, mUrl404)).isTrue(); 1281 assertThat(containsStringInNetLog(logFile2, mUrl500)).isTrue(); 1282 1283 FileUtils.recursivelyDeleteFile(netLogDir1); 1284 assertThat(netLogDir1.exists()).isFalse(); 1285 FileUtils.recursivelyDeleteFile(netLogDir2); 1286 assertThat(netLogDir2.exists()).isFalse(); 1287 } 1288 createCronetEngineWithCache(int cacheType)1289 private CronetEngine createCronetEngineWithCache(int cacheType) { 1290 CronetEngine.Builder builder = 1291 mTestRule 1292 .getTestFramework() 1293 .createNewSecondaryBuilder(mTestRule.getTestFramework().getContext()); 1294 if (cacheType == CronetEngine.Builder.HTTP_CACHE_DISK 1295 || cacheType == CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP) { 1296 builder.setStoragePath(getTestStorage(mTestRule.getTestFramework().getContext())); 1297 } 1298 builder.enableHttpCache(cacheType, 100 * 1024); 1299 // Don't check the return value here, because startNativeTestServer() returns false when the 1300 // NativeTestServer is already running and this method needs to be called twice without 1301 // shutting down the NativeTestServer in between. 1302 NativeTestServer.startNativeTestServer(mTestRule.getTestFramework().getContext()); 1303 return builder.build(); 1304 } 1305 1306 @Test 1307 @SmallTest 1308 @SkipPresubmit(reason = "b/293141085 flaky test") 1309 @IgnoreFor( 1310 implementations = {CronetImplementation.FALLBACK}, 1311 reason = "Fallback implementation does not have a network thread.") 1312 // Tests that if CronetEngine is shut down on the network thread, an appropriate exception 1313 // is thrown. testShutDownEngineOnNetworkThread()1314 public void testShutDownEngineOnNetworkThread() throws Exception { 1315 final CronetEngine cronetEngine = 1316 createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_DISK); 1317 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1318 // Make a request to a cacheable resource. 1319 checkRequestCaching(cronetEngine, url, false); 1320 1321 final AtomicReference<Throwable> thrown = new AtomicReference<>(); 1322 // Shut down the server. 1323 NativeTestServer.shutdownNativeTestServer(); 1324 class CancelUrlRequestCallback extends TestUrlRequestCallback { 1325 @Override 1326 public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { 1327 super.onResponseStarted(request, info); 1328 request.cancel(); 1329 // Shut down CronetEngine immediately after request is destroyed. 1330 try { 1331 cronetEngine.shutdown(); 1332 } catch (Exception e) { 1333 thrown.set(e); 1334 } 1335 } 1336 1337 @Override 1338 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 1339 // onSucceeded will not happen, because the request is canceled 1340 // after sending first read and the executor is single threaded. 1341 throw new AssertionError("Unexpected"); 1342 } 1343 1344 @Override 1345 public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) { 1346 throw new AssertionError("Unexpected"); 1347 } 1348 } 1349 Executor directExecutor = 1350 new Executor() { 1351 @Override 1352 public void execute(Runnable command) { 1353 command.run(); 1354 } 1355 }; 1356 CancelUrlRequestCallback callback = new CancelUrlRequestCallback(); 1357 callback.setAllowDirectExecutor(true); 1358 UrlRequest.Builder urlRequestBuilder = 1359 cronetEngine.newUrlRequestBuilder(url, callback, directExecutor); 1360 urlRequestBuilder.allowDirectExecutor(); 1361 urlRequestBuilder.build().start(); 1362 callback.blockForDone(); 1363 assertThat(thrown.get()).isInstanceOf(RuntimeException.class); 1364 cronetEngine.shutdown(); 1365 } 1366 1367 @Test 1368 @SmallTest 1369 @SkipPresubmit(reason = "b/293141085 flaky test") 1370 @IgnoreFor( 1371 implementations = {CronetImplementation.FALLBACK}, 1372 reason = "Fallback implementation has no support for caches") 1373 // Tests that if CronetEngine is shut down when reading from disk cache, 1374 // there isn't a crash. See crbug.com/486120. testShutDownEngineWhenReadingFromDiskCache()1375 public void testShutDownEngineWhenReadingFromDiskCache() throws Exception { 1376 final CronetEngine cronetEngine = 1377 createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_DISK); 1378 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1379 // Make a request to a cacheable resource. 1380 checkRequestCaching(cronetEngine, url, false); 1381 1382 // Shut down the server. 1383 NativeTestServer.shutdownNativeTestServer(); 1384 class CancelUrlRequestCallback extends TestUrlRequestCallback { 1385 @Override 1386 public void onResponseStarted(UrlRequest request, UrlResponseInfo info) { 1387 super.onResponseStarted(request, info); 1388 request.cancel(); 1389 // Shut down CronetEngine immediately after request is destroyed. 1390 cronetEngine.shutdown(); 1391 } 1392 1393 @Override 1394 public void onSucceeded(UrlRequest request, UrlResponseInfo info) { 1395 // onSucceeded will not happen, because the request is canceled 1396 // after sending first read and the executor is single threaded. 1397 throw new RuntimeException("Unexpected"); 1398 } 1399 1400 @Override 1401 public void onFailed(UrlRequest request, UrlResponseInfo info, CronetException error) { 1402 throw new RuntimeException("Unexpected"); 1403 } 1404 } 1405 CancelUrlRequestCallback callback = new CancelUrlRequestCallback(); 1406 UrlRequest.Builder urlRequestBuilder = 1407 cronetEngine.newUrlRequestBuilder(url, callback, callback.getExecutor()); 1408 urlRequestBuilder.build().start(); 1409 callback.blockForDone(); 1410 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1411 assertThat(callback.getResponseInfoWithChecks()).wasCached(); 1412 assertThat(callback.mOnCanceledCalled).isTrue(); 1413 } 1414 1415 @Test 1416 @SmallTest 1417 @IgnoreFor( 1418 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1419 reason = "NetLog is supported only by the native implementation") 1420 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") testNetLogAfterShutdown()1421 public void testNetLogAfterShutdown() throws Exception { 1422 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1423 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1424 UrlRequest.Builder urlRequestBuilder = 1425 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1426 urlRequestBuilder.build().start(); 1427 callback.blockForDone(); 1428 cronetEngine.shutdown(); 1429 1430 File directory = new File(PathUtils.getDataDirectory()); 1431 File file = File.createTempFile("cronet", "json", directory); 1432 1433 Exception e = 1434 assertThrows( 1435 Exception.class, 1436 () -> cronetEngine.startNetLogToFile(file.getPath(), false)); 1437 assertThat(e).hasMessageThat().isEqualTo("Engine is shut down."); 1438 assertThat(hasBytesInNetLog(file)).isFalse(); 1439 assertThat(file.delete()).isTrue(); 1440 assertThat(file.exists()).isFalse(); 1441 } 1442 1443 @Test 1444 @SmallTest 1445 @IgnoreFor( 1446 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1447 reason = "NetLog is supported only by the native implementation") 1448 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") testBoundedFileNetLogAfterShutdown()1449 public void testBoundedFileNetLogAfterShutdown() throws Exception { 1450 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1451 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1452 UrlRequest.Builder urlRequestBuilder = 1453 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1454 urlRequestBuilder.build().start(); 1455 callback.blockForDone(); 1456 cronetEngine.shutdown(); 1457 1458 File directory = new File(PathUtils.getDataDirectory()); 1459 File netLogDir = new File(directory, "NetLog" + System.currentTimeMillis()); 1460 assertThat(netLogDir.exists()).isFalse(); 1461 assertThat(netLogDir.mkdir()).isTrue(); 1462 File logFile = new File(netLogDir, "netlog.json"); 1463 Exception e = 1464 assertThrows( 1465 Exception.class, 1466 () -> 1467 cronetEngine.startNetLogToDisk( 1468 netLogDir.getPath(), false, MAX_FILE_SIZE)); 1469 assertThat(e).hasMessageThat().isEqualTo("Engine is shut down."); 1470 assertThat(logFile.exists()).isFalse(); 1471 FileUtils.recursivelyDeleteFile(netLogDir); 1472 assertThat(netLogDir.exists()).isFalse(); 1473 } 1474 1475 @Test 1476 @SmallTest 1477 @IgnoreFor( 1478 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1479 reason = "NetLog is supported only by the native implementation") 1480 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") testNetLogStartMultipleTimes()1481 public void testNetLogStartMultipleTimes() throws Exception { 1482 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1483 File directory = new File(PathUtils.getDataDirectory()); 1484 File file = File.createTempFile("cronet", "json", directory); 1485 // Start NetLog multiple times. 1486 cronetEngine.startNetLogToFile(file.getPath(), false); 1487 cronetEngine.startNetLogToFile(file.getPath(), false); 1488 cronetEngine.startNetLogToFile(file.getPath(), false); 1489 cronetEngine.startNetLogToFile(file.getPath(), false); 1490 // Start a request. 1491 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1492 UrlRequest.Builder urlRequestBuilder = 1493 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1494 urlRequestBuilder.build().start(); 1495 callback.blockForDone(); 1496 cronetEngine.stopNetLog(); 1497 assertThat(file.exists()).isTrue(); 1498 assertThat(file.length()).isNotEqualTo(0); 1499 assertThat(hasBytesInNetLog(file)).isFalse(); 1500 assertThat(file.delete()).isTrue(); 1501 assertThat(file.exists()).isFalse(); 1502 } 1503 1504 @Test 1505 @SmallTest 1506 @IgnoreFor( 1507 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1508 reason = "NetLog is supported only by the native implementation") 1509 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") testBoundedFileNetLogStartMultipleTimes()1510 public void testBoundedFileNetLogStartMultipleTimes() throws Exception { 1511 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1512 File directory = new File(PathUtils.getDataDirectory()); 1513 File netLogDir = new File(directory, "NetLog" + System.currentTimeMillis()); 1514 assertThat(netLogDir.exists()).isFalse(); 1515 assertThat(netLogDir.mkdir()).isTrue(); 1516 File logFile = new File(netLogDir, "netlog.json"); 1517 // Start NetLog multiple times. This should be equivalent to starting NetLog 1518 // once. Each subsequent start (without calling stopNetLog) should be a no-op. 1519 cronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 1520 cronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 1521 cronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 1522 cronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 1523 // Start a request. 1524 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1525 UrlRequest.Builder urlRequestBuilder = 1526 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1527 urlRequestBuilder.build().start(); 1528 callback.blockForDone(); 1529 cronetEngine.stopNetLog(); 1530 assertThat(logFile.exists()).isTrue(); 1531 assertThat(logFile.length()).isNotEqualTo(0); 1532 assertThat(hasBytesInNetLog(logFile)).isFalse(); 1533 FileUtils.recursivelyDeleteFile(netLogDir); 1534 assertThat(netLogDir.exists()).isFalse(); 1535 } 1536 1537 @Test 1538 @SmallTest 1539 @IgnoreFor( 1540 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1541 reason = "NetLog is supported only by the native implementation") 1542 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") testNetLogStopMultipleTimes()1543 public void testNetLogStopMultipleTimes() throws Exception { 1544 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1545 File directory = new File(PathUtils.getDataDirectory()); 1546 File file = File.createTempFile("cronet", "json", directory); 1547 cronetEngine.startNetLogToFile(file.getPath(), false); 1548 // Start a request. 1549 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1550 UrlRequest.Builder urlRequestBuilder = 1551 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1552 urlRequestBuilder.build().start(); 1553 callback.blockForDone(); 1554 // Stop NetLog multiple times. 1555 cronetEngine.stopNetLog(); 1556 cronetEngine.stopNetLog(); 1557 cronetEngine.stopNetLog(); 1558 cronetEngine.stopNetLog(); 1559 cronetEngine.stopNetLog(); 1560 assertThat(file.exists()).isTrue(); 1561 assertThat(file.length()).isNotEqualTo(0); 1562 assertThat(hasBytesInNetLog(file)).isFalse(); 1563 assertThat(file.delete()).isTrue(); 1564 assertThat(file.exists()).isFalse(); 1565 } 1566 1567 @Test 1568 @SmallTest 1569 @IgnoreFor( 1570 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1571 reason = "NetLog is supported only by the native implementation") 1572 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") testBoundedFileNetLogStopMultipleTimes()1573 public void testBoundedFileNetLogStopMultipleTimes() throws Exception { 1574 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1575 File directory = new File(PathUtils.getDataDirectory()); 1576 File netLogDir = new File(directory, "NetLog" + System.currentTimeMillis()); 1577 assertThat(netLogDir.exists()).isFalse(); 1578 assertThat(netLogDir.mkdir()).isTrue(); 1579 File logFile = new File(netLogDir, "netlog.json"); 1580 cronetEngine.startNetLogToDisk(netLogDir.getPath(), false, MAX_FILE_SIZE); 1581 // Start a request. 1582 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1583 UrlRequest.Builder urlRequestBuilder = 1584 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1585 urlRequestBuilder.build().start(); 1586 callback.blockForDone(); 1587 // Stop NetLog multiple times. This should be equivalent to stopping NetLog once. 1588 // Each subsequent stop (without calling startNetLogToDisk first) should be a no-op. 1589 cronetEngine.stopNetLog(); 1590 cronetEngine.stopNetLog(); 1591 cronetEngine.stopNetLog(); 1592 cronetEngine.stopNetLog(); 1593 cronetEngine.stopNetLog(); 1594 assertThat(logFile.exists()).isTrue(); 1595 assertThat(logFile.length()).isNotEqualTo(0); 1596 assertThat(hasBytesInNetLog(logFile)).isFalse(); 1597 FileUtils.recursivelyDeleteFile(netLogDir); 1598 assertThat(netLogDir.exists()).isFalse(); 1599 } 1600 1601 @Test 1602 @SmallTest 1603 @IgnoreFor( 1604 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1605 reason = "NetLog is supported only by the native implementation") 1606 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") testNetLogWithBytes()1607 public void testNetLogWithBytes() throws Exception { 1608 File directory = new File(PathUtils.getDataDirectory()); 1609 File file = File.createTempFile("cronet", "json", directory); 1610 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1611 // Start NetLog with logAll as true. 1612 cronetEngine.startNetLogToFile(file.getPath(), true); 1613 // Start a request. 1614 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1615 UrlRequest.Builder urlRequestBuilder = 1616 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1617 urlRequestBuilder.build().start(); 1618 callback.blockForDone(); 1619 cronetEngine.stopNetLog(); 1620 assertThat(file.exists()).isTrue(); 1621 assertThat(file.length()).isNotEqualTo(0); 1622 assertThat(hasBytesInNetLog(file)).isTrue(); 1623 assertThat(file.delete()).isTrue(); 1624 assertThat(file.exists()).isFalse(); 1625 } 1626 1627 @Test 1628 @SmallTest 1629 @IgnoreFor( 1630 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1631 reason = "NetLog is supported only by the native implementation") 1632 @DisableAutomaticNetLog(reason = "Test is targeting NetLog") testBoundedFileNetLogWithBytes()1633 public void testBoundedFileNetLogWithBytes() throws Exception { 1634 File directory = new File(PathUtils.getDataDirectory()); 1635 File netLogDir = new File(directory, "NetLog" + System.currentTimeMillis()); 1636 assertThat(netLogDir.exists()).isFalse(); 1637 assertThat(netLogDir.mkdir()).isTrue(); 1638 File logFile = new File(netLogDir, "netlog.json"); 1639 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1640 // Start NetLog with logAll as true. 1641 cronetEngine.startNetLogToDisk(netLogDir.getPath(), true, MAX_FILE_SIZE); 1642 // Start a request. 1643 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1644 UrlRequest.Builder urlRequestBuilder = 1645 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1646 urlRequestBuilder.build().start(); 1647 callback.blockForDone(); 1648 cronetEngine.stopNetLog(); 1649 1650 assertThat(logFile.exists()).isTrue(); 1651 assertThat(logFile.length()).isNotEqualTo(0); 1652 assertThat(hasBytesInNetLog(logFile)).isTrue(); 1653 FileUtils.recursivelyDeleteFile(netLogDir); 1654 assertThat(netLogDir.exists()).isFalse(); 1655 } 1656 hasBytesInNetLog(File logFile)1657 private boolean hasBytesInNetLog(File logFile) throws Exception { 1658 return containsStringInNetLog(logFile, "\"bytes\""); 1659 } 1660 containsStringInNetLog(File logFile, String content)1661 private boolean containsStringInNetLog(File logFile, String content) throws Exception { 1662 BufferedReader logReader = new BufferedReader(new FileReader(logFile)); 1663 try { 1664 String logLine; 1665 while ((logLine = logReader.readLine()) != null) { 1666 if (logLine.contains(content)) { 1667 return true; 1668 } 1669 } 1670 return false; 1671 } finally { 1672 logReader.close(); 1673 } 1674 } 1675 1676 /** 1677 * Helper method to make a request to {@code url}, wait for it to complete, and check that the 1678 * status code is the same as {@code expectedStatusCode}. 1679 */ makeRequestAndCheckStatus( CronetEngine engine, String url, int expectedStatusCode)1680 private void makeRequestAndCheckStatus( 1681 CronetEngine engine, String url, int expectedStatusCode) { 1682 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1683 UrlRequest request = 1684 engine.newUrlRequestBuilder(url, callback, callback.getExecutor()).build(); 1685 request.start(); 1686 callback.blockForDone(); 1687 assertThat(callback.getResponseInfoWithChecks()) 1688 .hasHttpStatusCodeThat() 1689 .isEqualTo(expectedStatusCode); 1690 } 1691 checkRequestCaching(CronetEngine engine, String url, boolean expectCached)1692 private void checkRequestCaching(CronetEngine engine, String url, boolean expectCached) { 1693 checkRequestCaching(engine, url, expectCached, false); 1694 } 1695 checkRequestCaching( CronetEngine engine, String url, boolean expectCached, boolean disableCache)1696 private void checkRequestCaching( 1697 CronetEngine engine, String url, boolean expectCached, boolean disableCache) { 1698 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1699 UrlRequest.Builder urlRequestBuilder = 1700 engine.newUrlRequestBuilder(url, callback, callback.getExecutor()); 1701 if (disableCache) { 1702 urlRequestBuilder.disableCache(); 1703 } 1704 urlRequestBuilder.build().start(); 1705 callback.blockForDone(); 1706 assertThat(callback.getResponseInfoWithChecks().wasCached()).isEqualTo(expectCached); 1707 assertThat(callback.mResponseAsString).isEqualTo("this is a cacheable file\n"); 1708 } 1709 1710 @Test 1711 @SmallTest 1712 @IgnoreFor( 1713 implementations = {CronetImplementation.FALLBACK}, 1714 reason = "No caches support for fallback implementation") testEnableHttpCacheDisabled()1715 public void testEnableHttpCacheDisabled() throws Exception { 1716 CronetEngine cronetEngine = 1717 createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_DISABLED); 1718 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1719 checkRequestCaching(cronetEngine, url, false); 1720 checkRequestCaching(cronetEngine, url, false); 1721 checkRequestCaching(cronetEngine, url, false); 1722 cronetEngine.shutdown(); 1723 } 1724 1725 @Test 1726 @SmallTest 1727 @IgnoreFor( 1728 implementations = {CronetImplementation.FALLBACK}, 1729 reason = "No caches support for fallback implementation") testEnableHttpCacheInMemory()1730 public void testEnableHttpCacheInMemory() throws Exception { 1731 CronetEngine cronetEngine = 1732 createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_IN_MEMORY); 1733 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1734 checkRequestCaching(cronetEngine, url, false); 1735 checkRequestCaching(cronetEngine, url, true); 1736 NativeTestServer.shutdownNativeTestServer(); 1737 checkRequestCaching(cronetEngine, url, true); 1738 cronetEngine.shutdown(); 1739 } 1740 1741 @Test 1742 @SmallTest 1743 @IgnoreFor( 1744 implementations = {CronetImplementation.FALLBACK}, 1745 reason = "No caches support for fallback implementation") testEnableHttpCacheDisk()1746 public void testEnableHttpCacheDisk() throws Exception { 1747 CronetEngine cronetEngine = 1748 createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_DISK); 1749 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1750 checkRequestCaching(cronetEngine, url, false); 1751 checkRequestCaching(cronetEngine, url, true); 1752 NativeTestServer.shutdownNativeTestServer(); 1753 checkRequestCaching(cronetEngine, url, true); 1754 cronetEngine.shutdown(); 1755 } 1756 1757 @Test 1758 @SmallTest 1759 @SkipPresubmit(reason = "b/293141085 flaky test") 1760 @IgnoreFor( 1761 implementations = {CronetImplementation.FALLBACK}, 1762 reason = "No caches support for fallback implementation") testNoConcurrentDiskUsage()1763 public void testNoConcurrentDiskUsage() throws Exception { 1764 CronetEngine cronetEngine = 1765 createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_DISK); 1766 1767 IllegalStateException e = 1768 assertThrows( 1769 IllegalStateException.class, 1770 () -> createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_DISK)); 1771 assertThat(e).hasMessageThat().isEqualTo("Disk cache storage path already in use"); 1772 1773 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1774 checkRequestCaching(cronetEngine, url, false); 1775 checkRequestCaching(cronetEngine, url, true); 1776 NativeTestServer.shutdownNativeTestServer(); 1777 checkRequestCaching(cronetEngine, url, true); 1778 cronetEngine.shutdown(); 1779 } 1780 1781 @Test 1782 @SmallTest 1783 @SkipPresubmit(reason = "b/293141085 flaky test") 1784 @IgnoreFor( 1785 implementations = {CronetImplementation.FALLBACK}, 1786 reason = "No caches support for fallback implementation") testEnableHttpCacheDiskNoHttp()1787 public void testEnableHttpCacheDiskNoHttp() throws Exception { 1788 CronetEngine cronetEngine = 1789 createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP); 1790 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1791 checkRequestCaching(cronetEngine, url, false); 1792 checkRequestCaching(cronetEngine, url, false); 1793 checkRequestCaching(cronetEngine, url, false); 1794 1795 // Make a new CronetEngine and try again to make sure the response didn't get cached on the 1796 // first request. See https://crbug.com/743232. 1797 cronetEngine.shutdown(); 1798 cronetEngine = createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_DISK_NO_HTTP); 1799 checkRequestCaching(cronetEngine, url, false); 1800 checkRequestCaching(cronetEngine, url, false); 1801 checkRequestCaching(cronetEngine, url, false); 1802 cronetEngine.shutdown(); 1803 } 1804 1805 @Test 1806 @SmallTest 1807 @IgnoreFor( 1808 implementations = {CronetImplementation.FALLBACK}, 1809 reason = "No caches support for fallback implementation") testDisableCache()1810 public void testDisableCache() throws Exception { 1811 CronetEngine cronetEngine = 1812 createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_DISK); 1813 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1814 1815 // When cache is disabled, making a request does not write to the cache. 1816 checkRequestCaching( 1817 cronetEngine, url, false, true 1818 /** disable cache */ 1819 ); 1820 checkRequestCaching(cronetEngine, url, false); 1821 1822 // When cache is enabled, the second request is cached. 1823 checkRequestCaching( 1824 cronetEngine, url, false, true 1825 /** disable cache */ 1826 ); 1827 checkRequestCaching(cronetEngine, url, true); 1828 1829 // Shut down the server, next request should have a cached response. 1830 NativeTestServer.shutdownNativeTestServer(); 1831 checkRequestCaching(cronetEngine, url, true); 1832 1833 // Cache is disabled after server is shut down, request should fail. 1834 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1835 UrlRequest.Builder urlRequestBuilder = 1836 cronetEngine.newUrlRequestBuilder(url, callback, callback.getExecutor()); 1837 urlRequestBuilder.disableCache(); 1838 urlRequestBuilder.build().start(); 1839 callback.blockForDone(); 1840 assertThat(callback.mError) 1841 .hasMessageThat() 1842 .contains("Exception in CronetUrlRequest: net::ERR_CONNECTION_REFUSED"); 1843 cronetEngine.shutdown(); 1844 } 1845 1846 @Test 1847 @SmallTest 1848 @IgnoreFor( 1849 implementations = {CronetImplementation.FALLBACK}, 1850 reason = "No caches support for fallback implementation") testEnableHttpCacheDiskNewEngine()1851 public void testEnableHttpCacheDiskNewEngine() throws Exception { 1852 CronetEngine cronetEngine = 1853 createCronetEngineWithCache(CronetEngine.Builder.HTTP_CACHE_DISK); 1854 String url = NativeTestServer.getFileURL("/cacheable.txt"); 1855 checkRequestCaching(cronetEngine, url, false); 1856 checkRequestCaching(cronetEngine, url, true); 1857 NativeTestServer.shutdownNativeTestServer(); 1858 checkRequestCaching(cronetEngine, url, true); 1859 1860 // Shutdown original context and create another that uses the same cache. 1861 cronetEngine.shutdown(); 1862 cronetEngine = 1863 mTestRule 1864 .getTestFramework() 1865 .enableDiskCache( 1866 mTestRule 1867 .getTestFramework() 1868 .createNewSecondaryBuilder( 1869 mTestRule.getTestFramework().getContext())) 1870 .build(); 1871 checkRequestCaching(cronetEngine, url, true); 1872 cronetEngine.shutdown(); 1873 } 1874 1875 @Test 1876 @SmallTest testInitEngineAndStartRequest()1877 public void testInitEngineAndStartRequest() { 1878 // Immediately make a request after initializing the engine. 1879 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1880 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1881 UrlRequest.Builder urlRequestBuilder = 1882 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1883 urlRequestBuilder.build().start(); 1884 callback.blockForDone(); 1885 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 1886 } 1887 1888 @Test 1889 @SmallTest testInitEngineStartTwoRequests()1890 public void testInitEngineStartTwoRequests() throws Exception { 1891 // Make two requests after initializing the context. 1892 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1893 int[] statusCodes = {0, 0}; 1894 String[] urls = {mUrl, mUrl404}; 1895 for (int i = 0; i < 2; i++) { 1896 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1897 UrlRequest.Builder urlRequestBuilder = 1898 cronetEngine.newUrlRequestBuilder(urls[i], callback, callback.getExecutor()); 1899 urlRequestBuilder.build().start(); 1900 callback.blockForDone(); 1901 statusCodes[i] = callback.getResponseInfoWithChecks().getHttpStatusCode(); 1902 } 1903 assertThat(statusCodes).asList().containsExactly(200, 404).inOrder(); 1904 } 1905 1906 @Test 1907 @SmallTest testInitTwoEnginesSimultaneously()1908 public void testInitTwoEnginesSimultaneously() throws Exception { 1909 // Threads will block on runBlocker to ensure simultaneous execution. 1910 ConditionVariable runBlocker = new ConditionVariable(false); 1911 RequestThread thread1 = new RequestThread(mUrl, runBlocker); 1912 RequestThread thread2 = new RequestThread(mUrl404, runBlocker); 1913 1914 thread1.start(); 1915 thread2.start(); 1916 runBlocker.open(); 1917 thread1.join(); 1918 thread2.join(); 1919 assertThat(thread1.mCallback.getResponseInfoWithChecks()) 1920 .hasHttpStatusCodeThat() 1921 .isEqualTo(200); 1922 assertThat(thread2.mCallback.getResponseInfoWithChecks()) 1923 .hasHttpStatusCodeThat() 1924 .isEqualTo(404); 1925 } 1926 1927 @Test 1928 @SmallTest testInitTwoEnginesInSequence()1929 public void testInitTwoEnginesInSequence() throws Exception { 1930 ConditionVariable runBlocker = new ConditionVariable(true); 1931 RequestThread thread1 = new RequestThread(mUrl, runBlocker); 1932 RequestThread thread2 = new RequestThread(mUrl404, runBlocker); 1933 1934 thread1.start(); 1935 thread1.join(); 1936 thread2.start(); 1937 thread2.join(); 1938 assertThat(thread1.mCallback.getResponseInfoWithChecks()) 1939 .hasHttpStatusCodeThat() 1940 .isEqualTo(200); 1941 assertThat(thread2.mCallback.getResponseInfoWithChecks()) 1942 .hasHttpStatusCodeThat() 1943 .isEqualTo(404); 1944 } 1945 1946 @Test 1947 @SmallTest testInitDifferentEngines()1948 public void testInitDifferentEngines() throws Exception { 1949 // Test that concurrently instantiating Cronet context's upon various 1950 // different versions of the same Android Context does not cause crashes 1951 // like crbug.com/453845 1952 CronetEngine firstEngine = 1953 mTestRule 1954 .getTestFramework() 1955 .createNewSecondaryBuilder(mTestRule.getTestFramework().getContext()) 1956 .build(); 1957 CronetEngine secondEngine = 1958 mTestRule 1959 .getTestFramework() 1960 .createNewSecondaryBuilder(mTestRule.getTestFramework().getContext()) 1961 .build(); 1962 CronetEngine thirdEngine = 1963 mTestRule 1964 .getTestFramework() 1965 .createNewSecondaryBuilder(mTestRule.getTestFramework().getContext()) 1966 .build(); 1967 firstEngine.shutdown(); 1968 secondEngine.shutdown(); 1969 thirdEngine.shutdown(); 1970 } 1971 1972 @Test 1973 @SmallTest 1974 @IgnoreFor( 1975 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 1976 reason = "Global metrics delta is supported only by the native implementation") testGetGlobalMetricsDeltas()1977 public void testGetGlobalMetricsDeltas() throws Exception { 1978 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 1979 1980 byte[] delta1 = cronetEngine.getGlobalMetricsDeltas(); 1981 1982 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 1983 UrlRequest.Builder builder = 1984 cronetEngine.newUrlRequestBuilder(mUrl, callback, callback.getExecutor()); 1985 builder.build().start(); 1986 callback.blockForDone(); 1987 // Fetch deltas on a different thread the second time to make sure this is permitted. 1988 // See crbug.com/719448 1989 FutureTask<byte[]> task = 1990 new FutureTask<byte[]>( 1991 new Callable<byte[]>() { 1992 @Override 1993 public byte[] call() { 1994 return cronetEngine.getGlobalMetricsDeltas(); 1995 } 1996 }); 1997 new Thread(task).start(); 1998 byte[] delta2 = task.get(); 1999 assertThat(delta2).isNotEmpty(); 2000 assertThat(delta2).isNotEqualTo(delta1); 2001 } 2002 2003 @Test 2004 @SmallTest 2005 @IgnoreFor( 2006 implementations = {CronetImplementation.FALLBACK}, 2007 reason = "Deliberate manual creation of native engines") testCronetEngineBuilderConfig()2008 public void testCronetEngineBuilderConfig() throws Exception { 2009 // This is to prompt load of native library. 2010 mTestRule.getTestFramework().startEngine(); 2011 // Verify CronetEngine.Builder config is passed down accurately to native code. 2012 ExperimentalCronetEngine.Builder builder = 2013 new ExperimentalCronetEngine.Builder(mTestRule.getTestFramework().getContext()); 2014 builder.enableHttp2(false); 2015 builder.enableQuic(true); 2016 builder.addQuicHint("example.com", 12, 34); 2017 builder.enableHttpCache(HTTP_CACHE_IN_MEMORY, 54321); 2018 builder.setUserAgent("efgh"); 2019 builder.setExperimentalOptions(""); 2020 builder.setStoragePath(getTestStorage(mTestRule.getTestFramework().getContext())); 2021 builder.enablePublicKeyPinningBypassForLocalTrustAnchors(false); 2022 CronetUrlRequestContextTestJni.get() 2023 .verifyUrlRequestContextConfig( 2024 CronetUrlRequestContext.createNativeUrlRequestContextConfig( 2025 CronetTestUtil.getCronetEngineBuilderImpl(builder)), 2026 getTestStorage(mTestRule.getTestFramework().getContext())); 2027 } 2028 2029 @Test 2030 @SmallTest 2031 @IgnoreFor( 2032 implementations = {CronetImplementation.FALLBACK}, 2033 reason = "Deliberate manual creation of native engines") testCronetEngineQuicOffConfig()2034 public void testCronetEngineQuicOffConfig() throws Exception { 2035 // This is to prompt load of native library. 2036 mTestRule.getTestFramework().startEngine(); 2037 // Verify CronetEngine.Builder config is passed down accurately to native code. 2038 ExperimentalCronetEngine.Builder builder = 2039 new ExperimentalCronetEngine.Builder(mTestRule.getTestFramework().getContext()); 2040 builder.enableHttp2(false); 2041 // QUIC is on by default. Disabling it here to make sure the built config can correctly 2042 // reflect the change. 2043 builder.enableQuic(false); 2044 builder.enableHttpCache(HTTP_CACHE_IN_MEMORY, 54321); 2045 builder.setExperimentalOptions(""); 2046 builder.setUserAgent("efgh"); 2047 builder.setStoragePath(getTestStorage(mTestRule.getTestFramework().getContext())); 2048 builder.enablePublicKeyPinningBypassForLocalTrustAnchors(false); 2049 CronetUrlRequestContextTestJni.get() 2050 .verifyUrlRequestContextQuicOffConfig( 2051 CronetUrlRequestContext.createNativeUrlRequestContextConfig( 2052 CronetTestUtil.getCronetEngineBuilderImpl(builder)), 2053 getTestStorage(mTestRule.getTestFramework().getContext())); 2054 } 2055 2056 private static class TestBadLibraryLoader extends CronetEngine.Builder.LibraryLoader { 2057 private boolean mWasCalled; 2058 2059 @Override loadLibrary(String libName)2060 public void loadLibrary(String libName) { 2061 // Report that this method was called, but don't load the library 2062 mWasCalled = true; 2063 } 2064 wasCalled()2065 boolean wasCalled() { 2066 return mWasCalled; 2067 } 2068 } 2069 2070 @Test 2071 @SmallTest 2072 @IgnoreFor( 2073 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 2074 reason = "LibraryLoader is supported only by the native implementation") testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider()2075 public void testSetLibraryLoaderIsEnforcedByDefaultEmbeddedProvider() throws Exception { 2076 CronetEngine.Builder builder = 2077 new CronetEngine.Builder(mTestRule.getTestFramework().getContext()); 2078 TestBadLibraryLoader loader = new TestBadLibraryLoader(); 2079 builder.setLibraryLoader(loader); 2080 2081 assertThrows( 2082 "Native library should not be loaded", UnsatisfiedLinkError.class, builder::build); 2083 assertThat(loader.wasCalled()).isTrue(); 2084 2085 // The init thread is started *before* the library is loaded, so the init thread is running 2086 // despite the library loading failure. Init thread initialization can race against test 2087 // cleanup (e.g. Context access). We work around the issue by ensuring test cleanup will 2088 // call shutdown() on a real engine, which will block until the init thread initialization 2089 // is done. 2090 mTestRule.getTestFramework().startEngine(); 2091 } 2092 2093 @Test 2094 @SmallTest 2095 @IgnoreFor( 2096 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 2097 reason = "LibraryLoader is supported only by the native implementation") testSetLibraryLoaderIsIgnoredInNativeCronetEngineBuilderImpl()2098 public void testSetLibraryLoaderIsIgnoredInNativeCronetEngineBuilderImpl() throws Exception { 2099 CronetEngine.Builder builder = 2100 new CronetEngine.Builder( 2101 new NativeCronetEngineBuilderImpl( 2102 mTestRule.getTestFramework().getContext())); 2103 TestBadLibraryLoader loader = new TestBadLibraryLoader(); 2104 builder.setLibraryLoader(loader); 2105 CronetEngine engine = builder.build(); 2106 assertThat(engine).isNotNull(); 2107 assertThat(loader.wasCalled()).isFalse(); 2108 engine.shutdown(); 2109 } 2110 2111 // Creates a CronetEngine on another thread and then one on the main thread. This shouldn't 2112 // crash. 2113 @Test 2114 @SmallTest testThreadedStartup()2115 public void testThreadedStartup() throws Exception { 2116 final ConditionVariable otherThreadDone = new ConditionVariable(); 2117 final ConditionVariable uiThreadDone = new ConditionVariable(); 2118 new Handler(Looper.getMainLooper()) 2119 .post( 2120 new Runnable() { 2121 @Override 2122 public void run() { 2123 final ExperimentalCronetEngine.Builder builder = 2124 mTestRule 2125 .getTestFramework() 2126 .createNewSecondaryBuilder( 2127 mTestRule.getTestFramework().getContext()); 2128 new Thread() { 2129 @Override 2130 public void run() { 2131 CronetEngine cronetEngine = builder.build(); 2132 otherThreadDone.open(); 2133 cronetEngine.shutdown(); 2134 } 2135 }.start(); 2136 otherThreadDone.block(); 2137 builder.build().shutdown(); 2138 uiThreadDone.open(); 2139 } 2140 }); 2141 assertThat(uiThreadDone.block(1000)).isTrue(); 2142 } 2143 2144 @Test 2145 @SmallTest 2146 @IgnoreFor( 2147 implementations = {CronetImplementation.FALLBACK, CronetImplementation.AOSP_PLATFORM}, 2148 reason = "JSON experimental options are supported only by the native implementation") testHostResolverRules()2149 public void testHostResolverRules() throws Exception { 2150 String resolverTestHostname = "some-weird-hostname"; 2151 URL testUrl = new URL(mUrl); 2152 JSONObject hostResolverRules = 2153 new JSONObject() 2154 .put( 2155 "host_resolver_rules", 2156 "MAP " + resolverTestHostname + " " + testUrl.getHost()); 2157 mTestRule 2158 .getTestFramework() 2159 .applyEngineBuilderPatch( 2160 (builder) -> { 2161 JSONObject experimentalOptions = 2162 new JSONObject().put("HostResolverRules", hostResolverRules); 2163 builder.setExperimentalOptions(experimentalOptions.toString()); 2164 }); 2165 2166 ExperimentalCronetEngine cronetEngine = mTestRule.getTestFramework().startEngine(); 2167 2168 TestUrlRequestCallback callback = new TestUrlRequestCallback(); 2169 URL requestUrl = 2170 new URL("http", resolverTestHostname, testUrl.getPort(), testUrl.getFile()); 2171 UrlRequest.Builder urlRequestBuilder = 2172 cronetEngine.newUrlRequestBuilder( 2173 requestUrl.toString(), callback, callback.getExecutor()); 2174 urlRequestBuilder.build().start(); 2175 callback.blockForDone(); 2176 assertThat(callback.getResponseInfoWithChecks()).hasHttpStatusCodeThat().isEqualTo(200); 2177 } 2178 2179 /** Runs {@code r} on {@code engine}'s network thread. */ postToNetworkThread(final CronetEngine engine, final Runnable r)2180 private static void postToNetworkThread(final CronetEngine engine, final Runnable r) { 2181 // Works by requesting an invalid URL which results in onFailed() being called, which is 2182 // done through a direct executor which causes onFailed to be run on the network thread. 2183 Executor directExecutor = 2184 new Executor() { 2185 @Override 2186 public void execute(Runnable runable) { 2187 runable.run(); 2188 } 2189 }; 2190 UrlRequest.Callback callback = 2191 new UrlRequest.Callback() { 2192 @Override 2193 public void onRedirectReceived( 2194 UrlRequest request, 2195 UrlResponseInfo responseInfo, 2196 String newLocationUrl) {} 2197 2198 @Override 2199 public void onResponseStarted( 2200 UrlRequest request, UrlResponseInfo responseInfo) {} 2201 2202 @Override 2203 public void onReadCompleted( 2204 UrlRequest request, 2205 UrlResponseInfo responseInfo, 2206 ByteBuffer byteBuffer) {} 2207 2208 @Override 2209 public void onSucceeded(UrlRequest request, UrlResponseInfo responseInfo) {} 2210 2211 @Override 2212 public void onFailed( 2213 UrlRequest request, 2214 UrlResponseInfo responseInfo, 2215 CronetException error) { 2216 r.run(); 2217 } 2218 }; 2219 engine.newUrlRequestBuilder("", callback, directExecutor).build().start(); 2220 } 2221 2222 /** @returns the thread priority of {@code engine}'s network thread. */ 2223 private static class ApiHelper { doesContextExistForNetwork(CronetEngine engine, Network network)2224 public static boolean doesContextExistForNetwork(CronetEngine engine, Network network) 2225 throws Exception { 2226 FutureTask<Boolean> task = 2227 new FutureTask<Boolean>( 2228 new Callable<Boolean>() { 2229 @Override 2230 public Boolean call() { 2231 return CronetTestUtil.doesURLRequestContextExistForTesting( 2232 engine, network); 2233 } 2234 }); 2235 postToNetworkThread(engine, task); 2236 return task.get(); 2237 } 2238 } 2239 2240 /** @returns the thread priority of {@code engine}'s network thread. */ getThreadPriority(CronetEngine engine)2241 private int getThreadPriority(CronetEngine engine) throws Exception { 2242 FutureTask<Integer> task = 2243 new FutureTask<Integer>( 2244 new Callable<Integer>() { 2245 @Override 2246 public Integer call() { 2247 return Process.getThreadPriority(Process.myTid()); 2248 } 2249 }); 2250 postToNetworkThread(engine, task); 2251 return task.get(); 2252 } 2253 2254 /** 2255 * Cronet does not currently provide an API to wait for the active request count to change. We 2256 * can't just wait for the terminal callback to fire because Cronet updates the count some time 2257 * *after* we return from the callback. We hack around this by polling the active request count 2258 * in a loop. 2259 */ waitForActiveRequestCount(CronetEngine engine, int expectedCount)2260 private static void waitForActiveRequestCount(CronetEngine engine, int expectedCount) 2261 throws Exception { 2262 while (engine.getActiveRequestCount() != expectedCount) Thread.sleep(100); 2263 } 2264 2265 @Test 2266 @SmallTest 2267 @RequiresMinApi(6) // setThreadPriority added in API 6: crrev.com/472449 2268 @IgnoreFor( 2269 implementations = {CronetImplementation.AOSP_PLATFORM}, 2270 reason = "ThreadPriority is not available in AOSP") testCronetEngineThreadPriority()2271 public void testCronetEngineThreadPriority() throws Exception { 2272 ExperimentalCronetEngine.Builder builder = 2273 mTestRule 2274 .getTestFramework() 2275 .createNewSecondaryBuilder(mTestRule.getTestFramework().getContext()); 2276 // Try out of bounds thread priorities. 2277 IllegalArgumentException e = 2278 assertThrows(IllegalArgumentException.class, () -> builder.setThreadPriority(-21)); 2279 assertThat(e).hasMessageThat().isEqualTo("Thread priority invalid"); 2280 2281 e = assertThrows(IllegalArgumentException.class, () -> builder.setThreadPriority(20)); 2282 assertThat(e).hasMessageThat().isEqualTo("Thread priority invalid"); 2283 2284 // Test that valid thread priority range (-20..19) is working. 2285 for (int threadPriority = -20; threadPriority < 20; threadPriority++) { 2286 builder.setThreadPriority(threadPriority); 2287 CronetEngine engine = builder.build(); 2288 try { 2289 assertThat(getThreadPriority(engine)).isEqualTo(threadPriority); 2290 } finally { 2291 engine.shutdown(); 2292 } 2293 } 2294 } 2295 2296 @NativeMethods("cronet_tests") 2297 interface Natives { 2298 // Verifies that CronetEngine.Builder config from testCronetEngineBuilderConfig() is 2299 // properly translated to a native UrlRequestContextConfig. verifyUrlRequestContextConfig(long config, String storagePath)2300 void verifyUrlRequestContextConfig(long config, String storagePath); 2301 2302 // Verifies that CronetEngine.Builder config from testCronetEngineQuicOffConfig() is 2303 // properly translated to a native UrlRequestContextConfig and QUIC is turned off. verifyUrlRequestContextQuicOffConfig(long config, String storagePath)2304 void verifyUrlRequestContextQuicOffConfig(long config, String storagePath); 2305 } 2306 } 2307