1 /* 2 * Copyright (C) 2022 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 android.adservices.test.scenario.adservices.fledge; 18 19 import android.adservices.adselection.AdSelectionOutcome; 20 import android.adservices.adselection.ReportImpressionRequest; 21 import android.adservices.common.AdData; 22 import android.adservices.common.AdSelectionSignals; 23 import android.adservices.common.AdTechIdentifier; 24 import android.adservices.customaudience.CustomAudience; 25 import android.adservices.customaudience.TrustedBiddingData; 26 import android.net.Uri; 27 import android.platform.test.scenario.annotation.Scenario; 28 29 import org.junit.Assert; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.junit.runners.JUnit4; 33 34 import java.time.Duration; 35 import java.time.Instant; 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.List; 39 import java.util.concurrent.TimeUnit; 40 41 @Scenario 42 @RunWith(JUnit4.class) 43 public class LimitPerfTests extends AbstractPerfTest { 44 45 private static final String URL_PREFIX = "https://"; 46 private static final String BUYER = "localhost"; 47 48 @Test test_joinBigCustomAudience()49 public void test_joinBigCustomAudience() throws Exception { 50 joinAndLeaveNBigCas(1); 51 } 52 53 @Test test_join100BigCustomAudiences()54 public void test_join100BigCustomAudiences() throws Exception { 55 // Will take 3+ minutes to run 56 joinAndLeaveNBigCas(100); 57 } 58 59 @Test test_auctionBigCa()60 public void test_auctionBigCa() throws Exception { 61 auctionNBigCas(1); 62 } 63 64 @Test test_auction10BigCas()65 public void test_auction10BigCas() throws Exception { 66 auctionNBigCas(10); 67 } 68 69 @Test test_auction100BigCas()70 public void test_auction100BigCas() throws Exception { 71 // Will take 3+ minutes to run 72 auctionNBigCas(100); 73 } 74 75 @Test test_100Auctions100BigCas()76 public void test_100Auctions100BigCas() throws Exception { 77 // Will take 5+ minutes to run 78 nAuctionsMBigCas(100, 100); 79 } 80 81 auctionNBigCas(int n)82 private void auctionNBigCas(int n) throws Exception { 83 nAuctionsMBigCas(1, n); 84 } 85 nAuctionsMBigCas(int n, int m)86 private void nAuctionsMBigCas(int n, int m) throws Exception { 87 List<CustomAudience> caList = createNBigCas(m); 88 mMockWebServerRule.startMockWebServer(mDefaultDispatcher); 89 joinAll(caList); 90 91 for (int i = 0; i < n; i++) { 92 AdSelectionOutcome outcome = 93 mAdSelectionClient 94 .selectAds(createAdSelectionConfig()) 95 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 96 97 // Check that a valid URL won 98 Assert.assertEquals(BUYER, outcome.getRenderUri().getHost()); 99 100 ReportImpressionRequest reportImpressionRequest = 101 new ReportImpressionRequest( 102 outcome.getAdSelectionId(), createAdSelectionConfig()); 103 // Performing reporting, and asserting that no exception is thrown 104 mAdSelectionClient 105 .reportImpression(reportImpressionRequest) 106 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 107 } 108 109 leaveAll(caList); 110 } 111 joinAndLeaveNBigCas(int n)112 private void joinAndLeaveNBigCas(int n) throws Exception { 113 List<CustomAudience> caList = createNBigCas(n); 114 joinAll(caList); 115 leaveAll(caList); 116 } 117 joinAll(List<CustomAudience> caList)118 private void joinAll(List<CustomAudience> caList) throws Exception { 119 for (CustomAudience ca : caList) { 120 mCustomAudienceClient 121 .joinCustomAudience(ca) 122 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 123 } 124 } 125 leaveAll(List<CustomAudience> caList)126 private void leaveAll(List<CustomAudience> caList) throws Exception { 127 for (CustomAudience ca : caList) { 128 mCustomAudienceClient 129 .leaveCustomAudience(ca.getBuyer(), ca.getName()) 130 .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); 131 } 132 } 133 createNBigCas(int n)134 private List<CustomAudience> createNBigCas(int n) { 135 List<CustomAudience> caList = new ArrayList<>(); 136 for (int i = 0; i < n; i++) { 137 caList.add(createBigCustomAudience("" + i, i + 1)); 138 } 139 return caList; 140 } 141 /** 142 * Creates as large of a CA as possible with a name prefixed by nameStart 143 * 144 * @param nameStart The start of the CA name, the name will be padded out the maximum length 145 * @param bid How much all the ads in the CA should bid 146 * @return The large CA. 147 */ createBigCustomAudience(String nameStart, int bid)148 private CustomAudience createBigCustomAudience(String nameStart, int bid) { 149 int minUrlSize = URL_PREFIX.length() + BUYER.length(); 150 Uri trustedBiddingUri = 151 getValidTrustedBiddingUriByBuyer(AdTechIdentifier.fromString(BUYER)); 152 return new CustomAudience.Builder() 153 // CA names are limited to 200 bytes 154 .setName(nameStart + repeatCompatImpl("a", 200 - nameStart.length())) 155 .setActivationTime(Instant.now()) 156 .setExpirationTime(Instant.now().plus(Duration.ofDays(1))) 157 // Daily update and bidding URLS are limited to 400 bytes 158 .setDailyUpdateUri(nBitUriFromAdtech(BUYER, 400)) 159 .setBiddingLogicUri( 160 nBitUriFromUri( 161 mMockWebServerRule.uriForPath(BUYER_BIDDING_LOGIC_URI_PATH), 400)) 162 // User bidding signals are limited to 10,000 bytes 163 .setUserBiddingSignals(nBitAdSelectionSignals(10000)) 164 /* TrustedBidding signals are limited to 10 KiB. We're adding as many keys objects 165 * as possible to increase object overhead. 166 */ 167 .setTrustedBiddingData( 168 new TrustedBiddingData.Builder() 169 .setTrustedBiddingUri(trustedBiddingUri) 170 .setTrustedBiddingKeys( 171 Collections.nCopies( 172 10000 - trustedBiddingUri.toString().length(), "a")) 173 .build()) 174 .setBuyer(AdTechIdentifier.fromString(BUYER)) 175 // Maximum size is 10,000 bytes for each ad and 100 ads. Making 100 ads of size 100. 176 .setAds( 177 Collections.nCopies( 178 100, 179 new AdData.Builder() 180 .setRenderUri(nBitUriFromAdtech(BUYER, minUrlSize)) 181 .setMetadata( 182 nBitJsonWithFields( 183 100 - minUrlSize, "\"result\": " + bid)) 184 .build())) 185 .build(); 186 } 187 nBitAdSelectionSignals(int n)188 private AdSelectionSignals nBitAdSelectionSignals(int n) { 189 return AdSelectionSignals.fromString(nBitJson(n)); 190 } 191 nBitJson(int n)192 private String nBitJson(int n) { 193 if (n < 8) { 194 throw new IllegalArgumentException("n too small"); 195 } 196 return "{\"a\":\"" + repeatCompatImpl("a", n - 8) + "\"}"; 197 } 198 nBitJsonWithFields(int n, String fields)199 private String nBitJsonWithFields(int n, String fields) { 200 if (n < 8) { 201 throw new IllegalArgumentException("n too small"); 202 } 203 204 return "{" 205 + fields 206 + ",\"a\":\"" 207 + repeatCompatImpl("a", n - (9 + fields.length())) 208 + "\"}"; 209 } 210 nBitUriFromAdtech(String adtech, int n)211 private Uri nBitUriFromAdtech(String adtech, int n) { 212 String uriStart = URL_PREFIX + adtech; 213 if (n < uriStart.length()) { 214 throw new IllegalArgumentException("n too small "); 215 } else if (n == uriStart.length()) { 216 return Uri.parse(uriStart); 217 } else { 218 return Uri.parse(uriStart + "#" + repeatCompatImpl("a", n - 3 - uriStart.length())); 219 } 220 } 221 nBitUriFromUri(Uri uri, int n)222 private Uri nBitUriFromUri(Uri uri, int n) { 223 if (n < uri.toString().length()) { 224 throw new IllegalArgumentException("n too small "); 225 } else if (n == uri.toString().length()) { 226 return uri; 227 } else { 228 return Uri.parse(uri + "#" + repeatCompatImpl("a", n - 3 - uri.toString().length())); 229 } 230 } 231 232 /** 233 * Since we run the test on both Android S and T, this util method provides a 234 * backward-compatible way to concatenate a string N times without using Java 11 repeat String 235 * method. 236 */ repeatCompatImpl(String str, int numTimes)237 private static String repeatCompatImpl(String str, int numTimes) { 238 StringBuilder sb = new StringBuilder(); 239 240 for (int num = 0; num < numTimes; num++) { 241 sb.append(str); 242 } 243 244 return sb.toString(); 245 } 246 } 247