1 /* 2 * Copyright (C) 2024 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.odp.module.common.http; 18 19 import static com.google.common.truth.Truth.assertThat; 20 21 import static org.junit.Assert.assertEquals; 22 23 import static java.nio.charset.StandardCharsets.UTF_8; 24 25 import com.android.modules.utils.testing.ExtendedMockitoRule; 26 27 import com.google.common.util.concurrent.MoreExecutors; 28 29 import org.junit.Rule; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.junit.runners.JUnit4; 33 import org.mockito.Spy; 34 import org.mockito.quality.Strictness; 35 36 import java.io.IOException; 37 import java.util.Arrays; 38 import java.util.HashMap; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.concurrent.atomic.AtomicInteger; 42 43 @RunWith(JUnit4.class) 44 public final class HttpClientTest { 45 @Rule 46 public final ExtendedMockitoRule extendedMockitoRule = 47 new ExtendedMockitoRule.Builder(this).setStrictness(Strictness.LENIENT).build(); 48 49 private static final int DEFAULT_RETRY_LIMIT = 3; 50 private static final int HTTP_UNAVAILABLE = 503; 51 private static final int HTTP_OK = 200; 52 53 @Spy 54 private HttpClient mHttpClient = 55 new HttpClient(DEFAULT_RETRY_LIMIT, MoreExecutors.newDirectExecutorService()); 56 57 @Test testPerformGetRequestFailsWithRetry()58 public void testPerformGetRequestFailsWithRetry() throws Exception { 59 String failureMessage = "FAIL!"; 60 OdpHttpResponse testFailedResponse = 61 new OdpHttpResponse.Builder() 62 .setHeaders(new HashMap<>()) 63 .setPayload(failureMessage.getBytes(UTF_8)) 64 .setStatusCode(HTTP_UNAVAILABLE) 65 .build(); 66 TestHttpIOSupplier testSupplier = new TestHttpIOSupplier(testFailedResponse); 67 68 OdpHttpResponse returnedResponse = mHttpClient.performRequestWithRetry(testSupplier); 69 70 assertEquals(DEFAULT_RETRY_LIMIT, testSupplier.mCallCount.get()); 71 assertThat(returnedResponse.getStatusCode()).isEqualTo(HTTP_UNAVAILABLE); 72 assertThat(returnedResponse.getHeaders()).isEmpty(); 73 assertThat(returnedResponse.getPayload()).isEqualTo(failureMessage.getBytes(UTF_8)); 74 } 75 76 @Test testPerformGetRequestSuccessWithRetry()77 public void testPerformGetRequestSuccessWithRetry() throws Exception { 78 Map<String, List<String>> mockHeaders = new HashMap<>(); 79 mockHeaders.put("Header1", Arrays.asList("Value1")); 80 String failureMessage = "FAIL!"; 81 String successMessage = "Success!"; 82 OdpHttpResponse testFailedResponse = 83 new OdpHttpResponse.Builder() 84 .setHeaders(new HashMap<>()) 85 .setPayload(failureMessage.getBytes(UTF_8)) 86 .setStatusCode(HTTP_UNAVAILABLE) 87 .build(); 88 OdpHttpResponse testSuccessfulResponse = 89 new OdpHttpResponse.Builder() 90 .setPayload(successMessage.getBytes(UTF_8)) 91 .setStatusCode(HTTP_OK) 92 .setHeaders(mockHeaders) 93 .build(); 94 TestHttpIOSupplier testSupplier = 95 new TestHttpIOSupplier( 96 testSuccessfulResponse, testFailedResponse, DEFAULT_RETRY_LIMIT - 1); 97 98 OdpHttpResponse response = mHttpClient.performRequestWithRetry(testSupplier); 99 100 assertEquals(DEFAULT_RETRY_LIMIT, testSupplier.mCallCount.get()); 101 assertThat(response.getStatusCode()).isEqualTo(HTTP_OK); 102 assertThat(response.getHeaders()).isEqualTo(mockHeaders); 103 } 104 105 private static final class TestHttpIOSupplier 106 implements HttpClient.HttpIOSupplier<OdpHttpResponse> { 107 private final AtomicInteger mCallCount = new AtomicInteger(0); 108 109 private final OdpHttpResponse mSuccessfulResponse; 110 private final OdpHttpResponse mFailedResponse; 111 private final int mNumFailedCalls; 112 TestHttpIOSupplier(OdpHttpResponse failedResponse)113 private TestHttpIOSupplier(OdpHttpResponse failedResponse) { 114 this(null, failedResponse, 0); 115 } 116 TestHttpIOSupplier( OdpHttpResponse successfulResponse, OdpHttpResponse failedResponse, int numFailedCalls)117 private TestHttpIOSupplier( 118 OdpHttpResponse successfulResponse, 119 OdpHttpResponse failedResponse, 120 int numFailedCalls) { 121 this.mSuccessfulResponse = successfulResponse; 122 this.mFailedResponse = failedResponse; 123 this.mNumFailedCalls = numFailedCalls; 124 } 125 126 @Override get()127 public OdpHttpResponse get() throws IOException { 128 int callCount = mCallCount.incrementAndGet(); 129 if (mSuccessfulResponse == null) { 130 return mFailedResponse; 131 } 132 133 return callCount > mNumFailedCalls ? mSuccessfulResponse : mFailedResponse; 134 } 135 } 136 } 137