• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 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.impl;
6 
7 import static android.os.Process.THREAD_PRIORITY_BACKGROUND;
8 import static android.os.Process.THREAD_PRIORITY_DEFAULT;
9 
10 import static org.junit.Assert.assertEquals;
11 import static org.junit.Assert.assertFalse;
12 import static org.junit.Assert.assertNotEquals;
13 import static org.junit.Assert.assertNotNull;
14 import static org.junit.Assert.assertTrue;
15 
16 import static android.net.http.HttpEngine.Builder.HTTP_CACHE_DISK_NO_HTTP;
17 
18 import android.content.Context;
19 import android.net.http.HeaderBlock;
20 import android.net.http.HttpEngine;
21 import android.os.Build;
22 import android.os.ConditionVariable;
23 
24 import androidx.test.filters.SmallTest;
25 
26 import org.json.JSONException;
27 import org.json.JSONObject;
28 import org.junit.After;
29 import org.junit.Before;
30 import org.junit.Rule;
31 import org.junit.Test;
32 import org.junit.rules.RuleChain;
33 import org.junit.runner.RunWith;
34 import org.junit.runners.JUnit4;
35 
36 import org.chromium.net.CronetLoggerTestRule;
37 import org.chromium.net.CronetTestRule;
38 import org.chromium.net.CronetTestRule.CronetTestFramework;
39 import org.chromium.net.CronetTestRule.OnlyRunNativeCronet;
40 import org.chromium.net.CronetTestRule.RequiresMinAndroidApi;
41 import android.net.http.ExperimentalHttpEngine;
42 import org.chromium.net.NativeTestServer;
43 import org.chromium.net.TestUrlRequestCallback;
44 import android.net.http.UrlRequest;
45 import org.chromium.net.impl.CronetEngineBuilderImpl.HttpCacheMode;
46 import org.chromium.net.impl.CronetLogger.CronetEngineBuilderInfo;
47 import org.chromium.net.impl.CronetLogger.CronetSource;
48 import org.chromium.net.impl.CronetLogger.CronetTrafficInfo;
49 import org.chromium.net.impl.CronetLogger.CronetVersion;
50 
51 import java.time.Duration;
52 import java.util.AbstractMap;
53 import java.util.Arrays;
54 import java.util.Collections;
55 import java.util.HashMap;
56 import java.util.List;
57 import java.util.Locale;
58 import java.util.Map;
59 import java.util.concurrent.atomic.AtomicInteger;
60 import java.util.concurrent.atomic.AtomicReference;
61 
62 /**
63  * Test logging functionalities.
64  */
65 @RunWith(JUnit4.class)
66 @RequiresMinAndroidApi(Build.VERSION_CODES.O)
67 public final class CronetLoggerTest {
68     private final CronetTestRule mTestRule = new CronetTestRule();
69     private final CronetLoggerTestRule mLoggerTestRule = new CronetLoggerTestRule(new TestLogger());
70 
71     @Rule
72     public final RuleChain chain = RuleChain.outerRule(mTestRule).around(mLoggerTestRule);
73 
74     private TestLogger mTestLogger;
75     private Context mContext;
76     private CronetTestFramework mTestFramework;
77 
78     /**
79      * Records the last engine creation (and traffic info) call it has received.
80      */
81     public static final class TestLogger extends CronetLogger {
82         private AtomicInteger mCallsToLogCronetEngineCreation = new AtomicInteger();
83         private AtomicInteger mCallsToLogCronetTrafficInfo = new AtomicInteger();
84         private AtomicInteger mCronetEngineId = new AtomicInteger();
85         private AtomicInteger mCronetRequestId = new AtomicInteger();
86         private AtomicReference<CronetTrafficInfo> mTrafficInfo = new AtomicReference<>();
87         private AtomicReference<CronetEngineBuilderInfo> mBuilderInfo = new AtomicReference<>();
88         private AtomicReference<CronetVersion> mVersion = new AtomicReference<>();
89         private AtomicReference<CronetSource> mSource = new AtomicReference<>();
90         private final ConditionVariable mBlock = new ConditionVariable();
91 
92         @Override
logCronetEngineCreation(int cronetEngineId, CronetEngineBuilderInfo engineBuilderInfo, CronetVersion version, CronetSource source)93         public void logCronetEngineCreation(int cronetEngineId,
94                 CronetEngineBuilderInfo engineBuilderInfo, CronetVersion version,
95                 CronetSource source) {
96             mCallsToLogCronetEngineCreation.incrementAndGet();
97             mCronetEngineId.set(cronetEngineId);
98             mBuilderInfo.set(engineBuilderInfo);
99             mVersion.set(version);
100             mSource.set(source);
101         }
102 
103         @Override
logCronetTrafficInfo(int cronetEngineId, CronetTrafficInfo trafficInfo)104         public void logCronetTrafficInfo(int cronetEngineId, CronetTrafficInfo trafficInfo) {
105             mCallsToLogCronetTrafficInfo.incrementAndGet();
106             mCronetRequestId.set(cronetEngineId);
107             mTrafficInfo.set(trafficInfo);
108             mBlock.open();
109         }
110 
callsToLogCronetTrafficInfo()111         public int callsToLogCronetTrafficInfo() {
112             return mCallsToLogCronetTrafficInfo.get();
113         }
114 
callsToLogCronetEngineCreation()115         public int callsToLogCronetEngineCreation() {
116             return mCallsToLogCronetEngineCreation.get();
117         }
118 
waitForLogCronetTrafficInfo()119         public void waitForLogCronetTrafficInfo() {
120             mBlock.block();
121             mBlock.close();
122         }
123 
getLastCronetEngineId()124         public int getLastCronetEngineId() {
125             return mCronetEngineId.get();
126         }
127 
getLastCronetRequestId()128         public int getLastCronetRequestId() {
129             return mCronetRequestId.get();
130         }
131 
getLastCronetTrafficInfo()132         public CronetTrafficInfo getLastCronetTrafficInfo() {
133             return mTrafficInfo.get();
134         }
135 
getLastCronetEngineBuilderInfo()136         public CronetEngineBuilderInfo getLastCronetEngineBuilderInfo() {
137             return mBuilderInfo.get();
138         }
139 
getLastCronetVersion()140         public CronetVersion getLastCronetVersion() {
141             return mVersion.get();
142         }
143 
getLastCronetSource()144         public CronetSource getLastCronetSource() {
145             return mSource.get();
146         }
147     }
148 
149     @Before
setUp()150     public void setUp() {
151         mContext = CronetTestRule.getContext();
152         mTestFramework = mTestRule.buildCronetTestFramework();
153         mTestLogger = (TestLogger) mLoggerTestRule.mTestLogger;
154         assertTrue(NativeTestServer.startNativeTestServer(mContext));
155     }
156 
157     @After
tearDown()158     public void tearDown() {
159         mTestLogger = null;
160         NativeTestServer.shutdownNativeTestServer();
161         mTestFramework.shutdownEngine();
162     }
163 
164     @Test
165     @SmallTest
testCronetEngineInfoCreation()166     public void testCronetEngineInfoCreation() {
167         CronetEngineBuilderImpl builder = new NativeCronetEngineBuilderImpl(mContext);
168         CronetEngineBuilderInfo builderInfo = new CronetEngineBuilderInfo(builder);
169         assertEquals(builder.publicKeyPinningBypassForLocalTrustAnchorsEnabled(),
170                 builderInfo.isPublicKeyPinningBypassForLocalTrustAnchorsEnabled());
171         assertEquals(builder.getUserAgent(), builderInfo.getUserAgent());
172         assertEquals(builder.storagePath(), builderInfo.getStoragePath());
173         assertEquals(builder.quicEnabled(), builderInfo.isQuicEnabled());
174         assertEquals(builder.http2Enabled(), builderInfo.isHttp2Enabled());
175         assertEquals(builder.brotliEnabled(), builderInfo.isBrotliEnabled());
176         assertEquals(builder.httpCacheMode(), builderInfo.getHttpCacheMode());
177         assertEquals(builder.experimentalOptions(), builderInfo.getExperimentalOptions());
178         assertEquals(builder.networkQualityEstimatorEnabled(),
179                 builderInfo.isNetworkQualityEstimatorEnabled());
180         assertEquals(builder.threadPriority(THREAD_PRIORITY_BACKGROUND),
181                 builderInfo.getThreadPriority());
182     }
183 
184     @Test
185     @SmallTest
testCronetVersionCreation()186     public void testCronetVersionCreation() {
187         final int major = 100;
188         final int minor = 0;
189         final int build = 1;
190         final int patch = 33;
191         final String version = String.format(Locale.US, "%d.%d.%d.%d", major, minor, build, patch);
192         final CronetVersion parsedVersion = new CronetVersion(version);
193         assertEquals(major, parsedVersion.getMajorVersion());
194         assertEquals(minor, parsedVersion.getMinorVersion());
195         assertEquals(build, parsedVersion.getBuildVersion());
196         assertEquals(patch, parsedVersion.getPatchVersion());
197     }
198 
199     @Test
200     @SmallTest
testHttpCacheModeEnum()201     public void testHttpCacheModeEnum() {
202         final int publicBuilderHttpCacheModes[] = {HttpEngine.Builder.HTTP_CACHE_DISABLED,
203                 HttpEngine.Builder.HTTP_CACHE_IN_MEMORY,
204                 HttpEngine.Builder.HTTP_CACHE_DISK_NO_HTTP, HttpEngine.Builder.HTTP_CACHE_DISK};
205         for (int publicBuilderHttpCacheMode : publicBuilderHttpCacheModes) {
206             HttpCacheMode cacheModeEnum =
207                     HttpCacheMode.fromPublicBuilderCacheMode(publicBuilderHttpCacheMode);
208             assertEquals(publicBuilderHttpCacheMode, cacheModeEnum.toPublicBuilderCacheMode());
209         }
210     }
211 
212     @Test
213     @SmallTest
testSetLoggerForTesting()214     public void testSetLoggerForTesting() {
215         CronetLogger logger = CronetLoggerFactory.createLogger(mContext, null);
216         assertEquals(0, mTestLogger.callsToLogCronetTrafficInfo());
217         assertEquals(0, mTestLogger.callsToLogCronetEngineCreation());
218 
219         // We don't care about what's being logged.
220         logger.logCronetTrafficInfo(0, null);
221         assertEquals(1, mTestLogger.callsToLogCronetTrafficInfo());
222         assertEquals(0, mTestLogger.callsToLogCronetEngineCreation());
223         logger.logCronetEngineCreation(0, null, null, null);
224         assertEquals(1, mTestLogger.callsToLogCronetTrafficInfo());
225         assertEquals(1, mTestLogger.callsToLogCronetEngineCreation());
226     }
227 
228     @Test
229     @SmallTest
230     @OnlyRunNativeCronet
testTelemetryDefaultDisabled()231     public void testTelemetryDefaultDisabled() throws JSONException {
232         final String url = NativeTestServer.getEchoBodyURL();
233 
234         TestUrlRequestCallback callback = new TestUrlRequestCallback();
235         HttpEngine engine = mTestFramework.startEngine();
236         UrlRequest.Builder requestBuilder =
237                 engine.newUrlRequestBuilder(url, callback, callback.getExecutor());
238         UrlRequest request = requestBuilder.build();
239         request.start();
240         callback.blockForDone();
241 
242         // Test-logger should be bypassed.
243         assertEquals(0, mTestLogger.callsToLogCronetEngineCreation());
244         assertEquals(0, mTestLogger.callsToLogCronetTrafficInfo());
245     }
246 
247     @Test
248     @SmallTest
249     @OnlyRunNativeCronet
testEngineCreation()250     public void testEngineCreation() throws JSONException {
251         JSONObject staleDns = new JSONObject()
252                                       .put("enable", true)
253                                       .put("delay_ms", 0)
254                                       .put("allow_other_network", true)
255                                       .put("persist_to_disk", true)
256                                       .put("persist_delay_ms", 0);
257         final JSONObject jsonExperimentalOptions =
258                 new JSONObject().put("StaleDNS", staleDns).put("enable_telemetry", true);
259         final String experimentalOptions = jsonExperimentalOptions.toString();
260         final boolean isPublicKeyPinningBypassForLocalTrustAnchorsEnabled = false;
261         final String userAgent = "myUserAgent";
262         final String storagePath = CronetTestRule.getTestStorage(mContext);
263         final boolean isQuicEnabled = true;
264         final boolean isHttp2Enabled = false;
265         final boolean isBrotliEnabled = true;
266         final int cacheMode = HTTP_CACHE_DISK_NO_HTTP;
267         final boolean isNetworkQualityEstimatorEnabled = true;
268         final int threadPriority = THREAD_PRIORITY_DEFAULT;
269 
270         ExperimentalHttpEngine.Builder builder =
271                 (ExperimentalHttpEngine.Builder) mTestFramework.mBuilder;
272 
273         builder.setExperimentalOptions(experimentalOptions);
274         builder.setEnablePublicKeyPinningBypassForLocalTrustAnchors(
275                 isPublicKeyPinningBypassForLocalTrustAnchorsEnabled);
276         builder.setUserAgent(userAgent);
277         builder.setStoragePath(storagePath);
278         builder.setEnableQuic(isQuicEnabled);
279         builder.setEnableHttp2(isHttp2Enabled);
280         builder.setEnableBrotli(isBrotliEnabled);
281         builder.setEnableHttpCache(cacheMode, 0);
282 
283         builder.enableNetworkQualityEstimator(isNetworkQualityEstimatorEnabled);
284         builder.setThreadPriority(threadPriority);
285 
286         mTestFramework.startEngine();
287         final CronetEngineBuilderInfo builderInfo = mTestLogger.getLastCronetEngineBuilderInfo();
288         final CronetVersion version = mTestLogger.getLastCronetVersion();
289         final CronetSource source = mTestLogger.getLastCronetSource();
290 
291         assertEquals(isPublicKeyPinningBypassForLocalTrustAnchorsEnabled,
292                 builderInfo.isPublicKeyPinningBypassForLocalTrustAnchorsEnabled());
293         assertEquals(userAgent, builderInfo.getUserAgent());
294         assertEquals(storagePath, builderInfo.getStoragePath());
295         assertEquals(isQuicEnabled, builderInfo.isQuicEnabled());
296         assertEquals(isHttp2Enabled, builderInfo.isHttp2Enabled());
297         assertEquals(isBrotliEnabled, builderInfo.isBrotliEnabled());
298         assertEquals(cacheMode, builderInfo.getHttpCacheMode());
299         assertEquals(experimentalOptions, builderInfo.getExperimentalOptions());
300         assertEquals(
301                isNetworkQualityEstimatorEnabled, builderInfo.isNetworkQualityEstimatorEnabled());
302         assertEquals(threadPriority, builderInfo.getThreadPriority());
303         assertEquals(ImplVersion.getCronetVersion(), version.toString());
304         if (mTestRule.testingJavaImpl()) {
305             assertEquals(CronetSource.CRONET_SOURCE_FALLBACK, source);
306         } else {
307             assertEquals(CronetSource.CRONET_SOURCE_STATICALLY_LINKED, source);
308         }
309 
310         assertEquals(1, mTestLogger.callsToLogCronetEngineCreation());
311         assertEquals(0, mTestLogger.callsToLogCronetTrafficInfo());
312     }
313 
314     @Test
315     @SmallTest
316     @OnlyRunNativeCronet
testEngineCreationAndTrafficInfoEngineId()317     public void testEngineCreationAndTrafficInfoEngineId() throws Exception {
318         JSONObject jsonExperimentalOptions = new JSONObject().put("enable_telemetry", true);
319         final String experimentalOptions = jsonExperimentalOptions.toString();
320         final String url = "www.example.com";
321         ExperimentalHttpEngine.Builder builder =
322                 (ExperimentalHttpEngine.Builder) mTestFramework.mBuilder;
323         builder.setExperimentalOptions(experimentalOptions);
324         HttpEngine engine = mTestFramework.startEngine();
325         final int engineId = mTestLogger.getLastCronetEngineId();
326 
327         TestUrlRequestCallback callback1 = new TestUrlRequestCallback();
328         UrlRequest.Builder requestBuilder1 =
329                 engine.newUrlRequestBuilder(url, callback1, callback1.getExecutor());
330         UrlRequest request1 = requestBuilder1.build();
331         TestUrlRequestCallback callback2 = new TestUrlRequestCallback();
332         UrlRequest.Builder requestBuilder2 =
333                 engine.newUrlRequestBuilder(url, callback2, callback2.getExecutor());
334         UrlRequest request2 = requestBuilder2.build();
335 
336         request1.start();
337         callback1.blockForDone();
338         mTestLogger.waitForLogCronetTrafficInfo();
339         final int request1Id = mTestLogger.getLastCronetRequestId();
340 
341         request2.start();
342         callback2.blockForDone();
343         mTestLogger.waitForLogCronetTrafficInfo();
344         final int request2Id = mTestLogger.getLastCronetRequestId();
345 
346         assertEquals(engineId, request1Id);
347         assertEquals(engineId, request2Id);
348 
349         assertEquals(1, mTestLogger.callsToLogCronetEngineCreation());
350         assertEquals(2, mTestLogger.callsToLogCronetTrafficInfo());
351     }
352 
353     @Test
354     @SmallTest
355     @OnlyRunNativeCronet
testMultipleEngineCreationAndTrafficInfoEngineId()356     public void testMultipleEngineCreationAndTrafficInfoEngineId() throws Exception {
357         JSONObject jsonExperimentalOptions = new JSONObject().put("enable_telemetry", true);
358         final String experimentalOptions = jsonExperimentalOptions.toString();
359         final String url = "www.example.com";
360         ExperimentalHttpEngine.Builder engineBuilder =
361                 (ExperimentalHttpEngine.Builder) mTestFramework.mBuilder;
362         engineBuilder.setExperimentalOptions(experimentalOptions);
363 
364         HttpEngine engine1 = engineBuilder.build();
365         final int engine1Id = mTestLogger.getLastCronetEngineId();
366         HttpEngine engine2 = engineBuilder.build();
367         final int engine2Id = mTestLogger.getLastCronetEngineId();
368 
369         TestUrlRequestCallback callback1 = new TestUrlRequestCallback();
370         UrlRequest.Builder requestBuilder1 =
371                 engine1.newUrlRequestBuilder(url, callback1, callback1.getExecutor());
372         UrlRequest request1 = requestBuilder1.build();
373         TestUrlRequestCallback callback2 = new TestUrlRequestCallback();
374         UrlRequest.Builder requestBuilder2 =
375                 engine2.newUrlRequestBuilder(url, callback2, callback2.getExecutor());
376         UrlRequest request2 = requestBuilder2.build();
377 
378         request1.start();
379         callback1.blockForDone();
380         mTestLogger.waitForLogCronetTrafficInfo();
381         final int request1Id = mTestLogger.getLastCronetRequestId();
382 
383         request2.start();
384         callback2.blockForDone();
385         mTestLogger.waitForLogCronetTrafficInfo();
386         final int request2Id = mTestLogger.getLastCronetRequestId();
387 
388         assertEquals(engine1Id, request1Id);
389         assertEquals(engine2Id, request2Id);
390 
391         assertEquals(2, mTestLogger.callsToLogCronetEngineCreation());
392         assertEquals(2, mTestLogger.callsToLogCronetTrafficInfo());
393 
394         engine1.shutdown();
395         engine2.shutdown();
396     }
397 
398     @Test
399     @SmallTest
400     @OnlyRunNativeCronet
testSuccessfulRequestNative()401     public void testSuccessfulRequestNative() throws Exception {
402         JSONObject jsonExperimentalOptions = new JSONObject().put("enable_telemetry", true);
403         final String experimentalOptions = jsonExperimentalOptions.toString();
404         final String url = NativeTestServer.getEchoBodyURL();
405         ExperimentalHttpEngine.Builder engineBuilder =
406                 (ExperimentalHttpEngine.Builder) mTestFramework.mBuilder;
407         engineBuilder.setExperimentalOptions(experimentalOptions);
408         HttpEngine engine = mTestFramework.startEngine();
409 
410         TestUrlRequestCallback callback = new TestUrlRequestCallback();
411         UrlRequest.Builder requestBuilder =
412                 engine.newUrlRequestBuilder(url, callback, callback.getExecutor());
413         UrlRequest request = requestBuilder.build();
414         request.start();
415         callback.blockForDone();
416         assertFalse(callback.mOnCanceledCalled);
417         assertFalse(callback.mOnErrorCalled);
418         mTestLogger.waitForLogCronetTrafficInfo();
419 
420         final CronetTrafficInfo trafficInfo = mTestLogger.getLastCronetTrafficInfo();
421         assertEquals(0, trafficInfo.getRequestHeaderSizeInBytes());
422         assertNotEquals(0, trafficInfo.getRequestBodySizeInBytes());
423         assertNotEquals(0, trafficInfo.getResponseHeaderSizeInBytes());
424         assertNotEquals(0, trafficInfo.getResponseBodySizeInBytes());
425         assertEquals(200, trafficInfo.getResponseStatusCode());
426         assertNotEquals(Duration.ofSeconds(0), trafficInfo.getHeadersLatency());
427         assertNotEquals(Duration.ofSeconds(0), trafficInfo.getTotalLatency());
428         assertNotNull(trafficInfo.getNegotiatedProtocol());
429         assertFalse(trafficInfo.wasConnectionMigrationAttempted());
430         assertFalse(trafficInfo.didConnectionMigrationSucceed());
431 
432         assertEquals(1, mTestLogger.callsToLogCronetEngineCreation());
433         assertEquals(1, mTestLogger.callsToLogCronetTrafficInfo());
434     }
435 
436     @Test
437     @SmallTest
438     @OnlyRunNativeCronet
testFailedRequestNative()439     public void testFailedRequestNative() throws Exception {
440         JSONObject jsonExperimentalOptions = new JSONObject().put("enable_telemetry", true);
441         final String url = "www.unreachable-url.com";
442         final String experimentalOptions = jsonExperimentalOptions.toString();
443         ExperimentalHttpEngine.Builder engineBuilder =
444                 (ExperimentalHttpEngine.Builder) mTestFramework.mBuilder;
445         engineBuilder.setExperimentalOptions(experimentalOptions);
446         HttpEngine engine = mTestFramework.startEngine();
447 
448         TestUrlRequestCallback callback = new TestUrlRequestCallback();
449         UrlRequest.Builder requestBuilder =
450                 engine.newUrlRequestBuilder(url, callback, callback.getExecutor());
451         UrlRequest request = requestBuilder.build();
452         request.start();
453         callback.blockForDone();
454         assertFalse(callback.mOnCanceledCalled);
455         assertTrue(callback.mOnErrorCalled);
456         mTestLogger.waitForLogCronetTrafficInfo();
457 
458         final CronetTrafficInfo trafficInfo = mTestLogger.getLastCronetTrafficInfo();
459         assertEquals(0, trafficInfo.getRequestHeaderSizeInBytes());
460         assertEquals(0, trafficInfo.getRequestBodySizeInBytes());
461         assertEquals(0, trafficInfo.getResponseHeaderSizeInBytes());
462         assertEquals(0, trafficInfo.getResponseBodySizeInBytes());
463         // When a request fails before hitting the server all these values won't be populated in
464         // the actual code. Check that the logger sets them to some known defaults before
465         // logging.
466         assertEquals(0, trafficInfo.getResponseStatusCode());
467         assertEquals("", trafficInfo.getNegotiatedProtocol());
468         assertFalse(trafficInfo.wasConnectionMigrationAttempted());
469         assertFalse(trafficInfo.didConnectionMigrationSucceed());
470 
471         assertEquals(1, mTestLogger.callsToLogCronetEngineCreation());
472         assertEquals(1, mTestLogger.callsToLogCronetTrafficInfo());
473     }
474 
475     @Test
476     @SmallTest
477     @OnlyRunNativeCronet
testCanceledRequestNative()478     public void testCanceledRequestNative() throws Exception {
479         JSONObject jsonExperimentalOptions = new JSONObject().put("enable_telemetry", true);
480         final String experimentalOptions = jsonExperimentalOptions.toString();
481         final String url = NativeTestServer.getEchoBodyURL();
482         ExperimentalHttpEngine.Builder engineBuilder =
483                 (ExperimentalHttpEngine.Builder) mTestFramework.mBuilder;
484         engineBuilder.setExperimentalOptions(experimentalOptions);
485         HttpEngine engine = mTestFramework.startEngine();
486 
487         TestUrlRequestCallback callback = new TestUrlRequestCallback();
488         callback.setAutoAdvance(false);
489         UrlRequest.Builder requestBuilder =
490                 engine.newUrlRequestBuilder(url, callback, callback.getExecutor());
491         UrlRequest request = requestBuilder.build();
492         request.start();
493         request.cancel();
494         callback.blockForDone();
495         assertTrue(callback.mOnCanceledCalled);
496         assertFalse(callback.mOnErrorCalled);
497         mTestLogger.waitForLogCronetTrafficInfo();
498 
499         final CronetTrafficInfo trafficInfo = mTestLogger.getLastCronetTrafficInfo();
500         assertEquals(0, trafficInfo.getRequestHeaderSizeInBytes());
501         assertEquals(0, trafficInfo.getRequestBodySizeInBytes());
502         assertEquals(0, trafficInfo.getResponseHeaderSizeInBytes());
503         assertEquals(0, trafficInfo.getResponseBodySizeInBytes());
504         // When a request fails before hitting the server all these values won't be populated in
505         // the actual code. Check that the logger sets them to some known defaults before
506         // logging.
507         assertEquals(0, trafficInfo.getResponseStatusCode());
508         assertEquals("", trafficInfo.getNegotiatedProtocol());
509         assertFalse(trafficInfo.wasConnectionMigrationAttempted());
510         assertFalse(trafficInfo.didConnectionMigrationSucceed());
511 
512         assertEquals(1, mTestLogger.callsToLogCronetEngineCreation());
513         assertEquals(1, mTestLogger.callsToLogCronetTrafficInfo());
514     }
515 
516     @Test
517     @SmallTest
518     @OnlyRunNativeCronet
testEmptyHeadersSizeNative()519     public void testEmptyHeadersSizeNative() {
520         Map<String, List<String>> headers = Collections.emptyMap();
521         assertEquals(0, CronetUrlRequest.estimateHeadersSizeInBytes(headers));
522         headers = null;
523         assertEquals(0, CronetUrlRequest.estimateHeadersSizeInBytes(headers));
524 
525         HeaderBlock headerBlock = asHeaderBlock(new CronetUrlRequest.HeadersList());
526         assertEquals(0, CronetUrlRequest.estimateHeadersSizeInBytes(headerBlock));
527         headerBlock = null;
528         assertEquals(0, CronetUrlRequest.estimateHeadersSizeInBytes(headerBlock));
529     }
530 
531     @Test
532     @SmallTest
533     @OnlyRunNativeCronet
testNonEmptyHeadersSizeNative()534     public void testNonEmptyHeadersSizeNative() {
535         Map<String, List<String>> headers = new HashMap<String, List<String>>() {
536             {
537                 put("header1", Arrays.asList("value1", "value2")); // 7 + 6 + 6 = 19
538                 put("header2", null); // 19 + 7 = 26
539                 put("header3", Collections.emptyList()); // 26 + 7 + 0 = 33
540                 put(null, Arrays.asList("")); // 33 + 0 + 0 = 33
541             }
542         };
543         assertEquals(33, CronetUrlRequest.estimateHeadersSizeInBytes(headers));
544 
545         CronetUrlRequest.HeadersList headersList = new CronetUrlRequest.HeadersList();
546         headersList.add(new AbstractMap.SimpleImmutableEntry<String, String>(
547                 "header1", "value1") // 7 + 6 = 13
548         );
549         headersList.add(new AbstractMap.SimpleImmutableEntry<String, String>(
550                 "header1", "value2") // 13 + 7 + 6 = 26
551         );
552         headersList.add(new AbstractMap.SimpleImmutableEntry<String, String>(
553                 "header2", null) // 26 + 7 + 0 = 33
554         );
555         headersList.add(
556                 new AbstractMap.SimpleImmutableEntry<String, String>(null, "") // 33 + 0 + 0 = 33
557         );
558         assertEquals(33, CronetUrlRequest.estimateHeadersSizeInBytes(asHeaderBlock(headersList)));
559     }
560 
asHeaderBlock(List<Map.Entry<String, String>> headers)561     private static HeaderBlock asHeaderBlock(List<Map.Entry<String, String>> headers) {
562         return new HeaderBlockImpl(headers);
563     }
564 }
565