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.Manifest; 20 import android.adservices.adselection.AdSelectionConfig; 21 import android.adservices.clients.adselection.AdSelectionClient; 22 import android.adservices.clients.customaudience.AdvertisingCustomAudienceClient; 23 import android.adservices.common.AdData; 24 import android.adservices.common.AdSelectionSignals; 25 import android.adservices.common.AdTechIdentifier; 26 import android.adservices.customaudience.CustomAudience; 27 import android.adservices.customaudience.TrustedBiddingData; 28 import android.adservices.test.scenario.adservices.utils.MockWebServerRule; 29 import android.adservices.test.scenario.adservices.utils.MockWebServerRuleFactory; 30 import android.adservices.test.scenario.adservices.utils.SelectAdsFlagRule; 31 import android.content.Context; 32 import android.net.Uri; 33 import android.platform.test.rule.CleanPackageRule; 34 import android.platform.test.rule.KillAppsRule; 35 import android.platform.test.scenario.annotation.Scenario; 36 import android.provider.DeviceConfig; 37 38 import androidx.test.core.app.ApplicationProvider; 39 import androidx.test.platform.app.InstrumentationRegistry; 40 41 import com.android.adservices.common.AdservicesTestHelper; 42 import com.android.adservices.common.CompatAdServicesTestUtils; 43 import com.android.modules.utils.build.SdkLevel; 44 45 import com.google.mockwebserver.Dispatcher; 46 import com.google.mockwebserver.MockResponse; 47 import com.google.mockwebserver.RecordedRequest; 48 49 import org.junit.AfterClass; 50 import org.junit.Before; 51 import org.junit.BeforeClass; 52 import org.junit.Rule; 53 import org.junit.rules.RuleChain; 54 import org.junit.runner.RunWith; 55 import org.junit.runners.JUnit4; 56 57 import java.time.Duration; 58 import java.time.Instant; 59 import java.util.ArrayList; 60 import java.util.Arrays; 61 import java.util.Collections; 62 import java.util.List; 63 import java.util.Map; 64 import java.util.concurrent.Executor; 65 import java.util.concurrent.Executors; 66 67 @Scenario 68 @RunWith(JUnit4.class) 69 public class AbstractPerfTest { 70 71 public static final Duration CUSTOM_AUDIENCE_EXPIRE_IN = Duration.ofDays(1); 72 public static final Instant VALID_ACTIVATION_TIME = Instant.now(); 73 public static final Instant VALID_EXPIRATION_TIME = 74 VALID_ACTIVATION_TIME.plus(CUSTOM_AUDIENCE_EXPIRE_IN); 75 public static final String VALID_NAME = "testCustomAudienceName"; 76 public static final AdSelectionSignals VALID_USER_BIDDING_SIGNALS = 77 AdSelectionSignals.fromString("{'valid': 'yep', 'opaque': 'definitely'}"); 78 public static final String VALID_TRUSTED_BIDDING_URI_PATH = "/trusted/bidding/"; 79 public static final ArrayList<String> VALID_TRUSTED_BIDDING_KEYS = 80 new ArrayList<>(Arrays.asList("example", "valid", "list", "of", "keys")); 81 public static final AdTechIdentifier SELLER = AdTechIdentifier.fromString("localhost"); 82 // Uri Constants 83 public static final String DECISION_LOGIC_PATH = "/decisionFragment"; 84 public static final String TRUSTED_SCORING_SIGNAL_PATH = "/trustedScoringSignalsFragment"; 85 public static final String CUSTOM_AUDIENCE_SHIRT = "ca_shirt"; 86 public static final String CUSTOM_AUDIENCE_SHOES = "ca_shoe"; 87 // TODO(b/244530379) Make compatible with multiple buyers 88 public static final AdTechIdentifier BUYER_1 = AdTechIdentifier.fromString("localhost"); 89 public static final List<AdTechIdentifier> CUSTOM_AUDIENCE_BUYERS = 90 Collections.singletonList(BUYER_1); 91 public static final AdSelectionSignals AD_SELECTION_SIGNALS = 92 AdSelectionSignals.fromString("{\"ad_selection_signals\":1}"); 93 public static final AdSelectionSignals SELLER_SIGNALS = 94 AdSelectionSignals.fromString("{\"test_seller_signals\":1}"); 95 public static final Map<AdTechIdentifier, AdSelectionSignals> PER_BUYER_SIGNALS = 96 Map.of(BUYER_1, AdSelectionSignals.fromString("{\"buyer_signals\":1}")); 97 protected static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool(); 98 // Time allowed by current test setup for APIs to respond 99 // setting a large value for perf testing, to avoid failing for large datasets 100 protected static final int API_RESPONSE_TIMEOUT_SECONDS = 100; 101 protected static final String BUYER_BIDDING_LOGIC_URI_PATH = "/buyer/bidding/logic/"; 102 protected static final String SELLER_REPORTING_PATH = "/reporting/seller"; 103 protected static final String BUYER_REPORTING_PATH = "/reporting/buyer"; 104 protected static final String DEFAULT_DECISION_LOGIC_JS = 105 "function scoreAd(ad, bid, auction_config, seller_signals," 106 + " trusted_scoring_signals, contextual_signal, user_signal," 107 + " custom_audience_signal) { \n" 108 + " return {'status': 0, 'score': bid };\n" 109 + "}\n" 110 + "function reportResult(ad_selection_config, render_uri, bid," 111 + " contextual_signals) { \n" 112 + " return {'status': 0, 'results': {'signals_for_buyer':" 113 + " '{\"signals_for_buyer\":1}', 'reporting_uri': '" 114 + SELLER_REPORTING_PATH 115 + "' } };\n" 116 + "}"; 117 protected static final String DEFAULT_BIDDING_LOGIC_JS = 118 "function generateBid(ad, auction_signals, per_buyer_signals," 119 + " trusted_bidding_signals, contextual_signals," 120 + " custom_audience_signals) { \n" 121 + " return {'status': 0, 'ad': ad, 'bid': ad.metadata.result };\n" 122 + "}\n" 123 + "function reportWin(ad_selection_signals, per_buyer_signals," 124 + " signals_for_buyer, contextual_signals, custom_audience_signals) { \n" 125 + " return {'status': 0, 'results': {'reporting_uri': '" 126 + BUYER_REPORTING_PATH 127 + "' } };\n" 128 + "}"; 129 protected static final String CALCULATION_INTENSE_JS = 130 "for (let i = 1; i < 1000000000; i++) {\n" + " Math.sqrt(i);\n" + "}"; 131 protected static final String MEMORY_INTENSE_JS = 132 "var a = []\n" + "for (let i = 0; i < 10000; i++) {\n" + " a.push(i);" + "}"; 133 134 protected static final AdSelectionSignals TRUSTED_SCORING_SIGNALS = 135 AdSelectionSignals.fromString( 136 "{\n" 137 + "\t\"render_uri_1\": \"signals_for_1\",\n" 138 + "\t\"render_uri_2\": \"signals_for_2\"\n" 139 + "}"); 140 protected static final AdSelectionSignals TRUSTED_BIDDING_SIGNALS = 141 AdSelectionSignals.fromString( 142 "{\n" 143 + "\t\"example\": \"example\",\n" 144 + "\t\"valid\": \"Also valid\",\n" 145 + "\t\"list\": \"list\",\n" 146 + "\t\"of\": \"of\",\n" 147 + "\t\"keys\": \"trusted bidding signal Values\"\n" 148 + "}"); 149 protected static final String AD_URI_PREFIX = "/adverts/123/"; 150 protected static final int DELAY_TO_AVOID_THROTTLE_MS = 1001; 151 protected final Context mContext = ApplicationProvider.getApplicationContext(); 152 protected final AdSelectionClient mAdSelectionClient = 153 new AdSelectionClient.Builder() 154 .setContext(mContext) 155 .setExecutor(CALLBACK_EXECUTOR) 156 .build(); 157 protected final AdvertisingCustomAudienceClient mCustomAudienceClient = 158 new AdvertisingCustomAudienceClient.Builder() 159 .setContext(mContext) 160 .setExecutor(CALLBACK_EXECUTOR) 161 .build(); 162 @Rule public MockWebServerRule mMockWebServerRule = MockWebServerRuleFactory.createForHttps(); 163 protected Dispatcher mDefaultDispatcher; 164 165 // Per-test method rules, run in the given order. 166 @Rule 167 public RuleChain rules = 168 RuleChain.outerRule( 169 new KillAppsRule( 170 AdservicesTestHelper.getAdServicesPackageName(mContext))) 171 .around( 172 // CleanPackageRule should not execute after each test method because 173 // there's a chance it interferes with ShowmapSnapshotListener snapshot 174 // at the end of the test, impacting collection of memory metrics for 175 // AdServices process. 176 new CleanPackageRule( 177 AdservicesTestHelper.getAdServicesPackageName(mContext), 178 /* clearOnStarting = */ true, 179 /* clearOnFinished = */ false)) 180 .around(new SelectAdsFlagRule()); 181 182 @BeforeClass setupBeforeClass()183 public static void setupBeforeClass() { 184 InstrumentationRegistry.getInstrumentation() 185 .getUiAutomation() 186 .adoptShellPermissionIdentity(Manifest.permission.WRITE_DEVICE_CONFIG); 187 // TODO(b/245585645) Mark true for the heap size enforcement after installing M105 library 188 DeviceConfig.setProperty( 189 DeviceConfig.NAMESPACE_ADSERVICES, 190 "fledge_js_isolate_enforce_max_heap_size", 191 "false", 192 true); 193 } 194 195 @AfterClass tearDownAfterClass()196 public static void tearDownAfterClass() { 197 if (!SdkLevel.isAtLeastT()) { 198 CompatAdServicesTestUtils.resetFlagsToDefault(); 199 } 200 } 201 getUri(String name, String path)202 public static Uri getUri(String name, String path) { 203 return Uri.parse("https://" + name + path); 204 } 205 206 @Before setup()207 public void setup() throws InterruptedException { 208 mDefaultDispatcher = 209 new Dispatcher() { 210 @Override 211 public MockResponse dispatch(RecordedRequest request) { 212 if (DECISION_LOGIC_PATH.equals(request.getPath())) { 213 return new MockResponse().setBody(DEFAULT_DECISION_LOGIC_JS); 214 } else if (BUYER_BIDDING_LOGIC_URI_PATH.equals(request.getPath())) { 215 return new MockResponse().setBody(DEFAULT_BIDDING_LOGIC_JS); 216 } else if (BUYER_REPORTING_PATH.equals(request.getPath()) 217 || SELLER_REPORTING_PATH.equals(request.getPath())) { 218 return new MockResponse().setBody(""); 219 } else if (request.getPath().startsWith(TRUSTED_SCORING_SIGNAL_PATH)) { 220 return new MockResponse().setBody(TRUSTED_SCORING_SIGNALS.toString()); 221 } else if (request.getPath().startsWith(VALID_TRUSTED_BIDDING_URI_PATH)) { 222 return new MockResponse().setBody(TRUSTED_BIDDING_SIGNALS.toString()); 223 } 224 return new MockResponse().setResponseCode(404); 225 } 226 }; 227 } 228 createCustomAudience( final AdTechIdentifier buyer, String name, List<Double> bids, Instant activationTime, Instant expirationTime)229 protected CustomAudience createCustomAudience( 230 final AdTechIdentifier buyer, 231 String name, 232 List<Double> bids, 233 Instant activationTime, 234 Instant expirationTime) { 235 // Generate ads for with bids provided 236 List<AdData> ads = new ArrayList<>(); 237 238 // Create ads with the custom audience name and bid number as the ad URI 239 // Add the bid value to the metadata 240 for (int i = 0; i < bids.size(); i++) { 241 ads.add( 242 new AdData.Builder() 243 .setRenderUri( 244 getUri( 245 buyer.toString(), 246 AD_URI_PREFIX + name + "/ad" + (i + 1))) 247 .setMetadata("{\"result\":" + bids.get(i) + "}") 248 .build()); 249 } 250 251 return new CustomAudience.Builder() 252 .setBuyer(buyer) 253 .setName(name) 254 .setActivationTime(activationTime) 255 .setExpirationTime(expirationTime) 256 .setDailyUpdateUri(getValidDailyUpdateUriByBuyer(buyer)) 257 .setUserBiddingSignals(VALID_USER_BIDDING_SIGNALS) 258 .setTrustedBiddingData(getValidTrustedBiddingDataByBuyer(buyer)) 259 .setBiddingLogicUri(mMockWebServerRule.uriForPath(BUYER_BIDDING_LOGIC_URI_PATH)) 260 .setAds(ads) 261 .build(); 262 } 263 createCustomAudience( final AdTechIdentifier buyer, String name, List<Double> bids)264 protected CustomAudience createCustomAudience( 265 final AdTechIdentifier buyer, String name, List<Double> bids) { 266 return createCustomAudience( 267 buyer, name, bids, VALID_ACTIVATION_TIME, VALID_EXPIRATION_TIME); 268 } 269 createAdSelectionConfig()270 protected AdSelectionConfig createAdSelectionConfig() { 271 return new AdSelectionConfig.Builder() 272 .setSeller(SELLER) 273 .setDecisionLogicUri(mMockWebServerRule.uriForPath(DECISION_LOGIC_PATH)) 274 .setCustomAudienceBuyers(CUSTOM_AUDIENCE_BUYERS) 275 .setAdSelectionSignals(AD_SELECTION_SIGNALS) 276 .setSellerSignals(SELLER_SIGNALS) 277 .setPerBuyerSignals(PER_BUYER_SIGNALS) 278 .setTrustedScoringSignalsUri( 279 mMockWebServerRule.uriForPath(TRUSTED_SCORING_SIGNAL_PATH)) 280 // TODO(b/244530379) Make compatible with multiple buyers 281 .setCustomAudienceBuyers(Collections.singletonList(BUYER_1)) 282 .build(); 283 } 284 createExpectedWinningUri( AdTechIdentifier buyer, String customAudienceName, int adNumber)285 protected Uri createExpectedWinningUri( 286 AdTechIdentifier buyer, String customAudienceName, int adNumber) { 287 return getUri(buyer.toString(), AD_URI_PREFIX + customAudienceName + "/ad" + adNumber); 288 } 289 290 // TODO(b/244530379) Make compatible with multiple buyers getValidDailyUpdateUriByBuyer(AdTechIdentifier buyer)291 protected Uri getValidDailyUpdateUriByBuyer(AdTechIdentifier buyer) { 292 return mMockWebServerRule.uriForPath("/update"); 293 } 294 getValidTrustedBiddingDataByBuyer(AdTechIdentifier buyer)295 protected TrustedBiddingData getValidTrustedBiddingDataByBuyer(AdTechIdentifier buyer) { 296 return new TrustedBiddingData.Builder() 297 .setTrustedBiddingKeys(VALID_TRUSTED_BIDDING_KEYS) 298 .setTrustedBiddingUri(getValidTrustedBiddingUriByBuyer(buyer)) 299 .build(); 300 } 301 302 // TODO(b/244530379) Make compatible with multiple buyers getValidTrustedBiddingUriByBuyer(AdTechIdentifier buyer)303 protected Uri getValidTrustedBiddingUriByBuyer(AdTechIdentifier buyer) { 304 return mMockWebServerRule.uriForPath(VALID_TRUSTED_BIDDING_URI_PATH); 305 } 306 addDelayToAvoidThrottle()307 protected void addDelayToAvoidThrottle() throws InterruptedException { 308 Thread.sleep(DELAY_TO_AVOID_THROTTLE_MS); 309 } 310 } 311