diff options
Diffstat (limited to 'adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/LimitPerfTests.java')
-rw-r--r-- | adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/LimitPerfTests.java | 609 |
1 files changed, 609 insertions, 0 deletions
diff --git a/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/LimitPerfTests.java b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/LimitPerfTests.java new file mode 100644 index 0000000000..0b15e94d60 --- /dev/null +++ b/adservices/tests/perf/src/android/adservices/test/scenario/adservices/fledge/LimitPerfTests.java @@ -0,0 +1,609 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.adservices.test.scenario.adservices.fledge; + +import android.Manifest; +import android.adservices.adselection.AdSelectionConfig; +import android.adservices.adselection.AdSelectionOutcome; +import android.adservices.adselection.ReportImpressionRequest; +import android.adservices.clients.adselection.AdSelectionClient; +import android.adservices.clients.customaudience.AdvertisingCustomAudienceClient; +import android.adservices.common.AdData; +import android.adservices.common.AdSelectionSignals; +import android.adservices.common.AdTechIdentifier; +import android.adservices.customaudience.CustomAudience; +import android.adservices.customaudience.TrustedBiddingData; +import android.adservices.test.scenario.adservices.utils.MockWebServerRule; +import android.adservices.test.scenario.adservices.utils.MockWebServerRuleFactory; +import android.content.Context; +import android.net.Uri; +import android.platform.test.scenario.annotation.Scenario; +import android.provider.DeviceConfig; + +import androidx.test.core.app.ApplicationProvider; +import androidx.test.platform.app.InstrumentationRegistry; + +import com.android.compatibility.common.util.ShellUtils; + +import com.google.common.collect.ImmutableList; +import com.google.mockwebserver.Dispatcher; +import com.google.mockwebserver.MockResponse; +import com.google.mockwebserver.RecordedRequest; + +import org.junit.Assert; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Rule; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +import java.time.Duration; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; + +@Scenario +@RunWith(JUnit4.class) +public class LimitPerfTests { + + // The number of ms to sleep after killing the adservices process so it has time to recover + public static final long SLEEP_MS_AFTER_KILL = 2000L; + // Command to kill the adservices process + public static final String KILL_ADSERVICES_CMD = + "su 0 killall -9 com.google.android.adservices.api"; + // Command prevent activity manager from backing off on restarting the adservices process + public static final String DISABLE_ADSERVICES_BACKOFF_CMD = + "am service-restart-backoff disable com.google.android.adservices.api"; + + public static final Duration CUSTOM_AUDIENCE_EXPIRE_IN = Duration.ofDays(1); + public static final Instant VALID_ACTIVATION_TIME = Instant.now(); + public static final Instant VALID_EXPIRATION_TIME = + VALID_ACTIVATION_TIME.plus(CUSTOM_AUDIENCE_EXPIRE_IN); + public static final String VALID_NAME = "testCustomAudienceName"; + public static final AdSelectionSignals VALID_USER_BIDDING_SIGNALS = + AdSelectionSignals.fromString("{'valid': 'yep', 'opaque': 'definitely'}"); + public static final String VALID_TRUSTED_BIDDING_URI_PATH = "/trusted/bidding/"; + public static final ArrayList<String> VALID_TRUSTED_BIDDING_KEYS = + new ArrayList<>(Arrays.asList("example", "valid", "list", "of", "keys")); + public static final AdTechIdentifier SELLER = AdTechIdentifier.fromString("localhost"); + // Uri Constants + public static final String DECISION_LOGIC_PATH = "/decisionFragment"; + public static final String TRUSTED_SCORING_SIGNAL_PATH = "/trustedScoringSignalsFragment"; + public static final String CUSTOM_AUDIENCE_SHIRT = "ca_shirt"; + public static final String CUSTOM_AUDIENCE_SHOES = "ca_shoe"; + // TODO(b/244530379) Make compatible with multiple buyers + public static final AdTechIdentifier BUYER_1 = AdTechIdentifier.fromString("localhost"); + public static final List<AdTechIdentifier> CUSTOM_AUDIENCE_BUYERS = + Collections.singletonList(BUYER_1); + public static final AdSelectionSignals AD_SELECTION_SIGNALS = + AdSelectionSignals.fromString("{\"ad_selection_signals\":1}"); + public static final AdSelectionSignals SELLER_SIGNALS = + AdSelectionSignals.fromString("{\"test_seller_signals\":1}"); + public static final Map<AdTechIdentifier, AdSelectionSignals> PER_BUYER_SIGNALS = + Map.of(BUYER_1, AdSelectionSignals.fromString("{\"buyer_signals\":1}")); + private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool(); + // Time allowed by current test setup for APIs to respond + // setting a large value for perf testing, to avoid failing for large datasets + private static final int API_RESPONSE_TIMEOUT_SECONDS = 100; + private static final String BUYER_BIDDING_LOGIC_URI_PATH = "/buyer/bidding/logic/"; + private static final String SELLER_REPORTING_PATH = "/reporting/seller"; + private static final String BUYER_REPORTING_PATH = "/reporting/buyer"; + private static final String DEFAULT_DECISION_LOGIC_JS = + "function scoreAd(ad, bid, auction_config, seller_signals," + + " trusted_scoring_signals, contextual_signal, user_signal," + + " custom_audience_signal) { \n" + + " return {'status': 0, 'score': bid };\n" + + "}\n" + + "function reportResult(ad_selection_config, render_uri, bid," + + " contextual_signals) { \n" + + " return {'status': 0, 'results': {'signals_for_buyer':" + + " '{\"signals_for_buyer\":1}', 'reporting_uri': '" + + SELLER_REPORTING_PATH + + "' } };\n" + + "}"; + private static final String DEFAULT_BIDDING_LOGIC_JS = + "function generateBid(ad, auction_signals, per_buyer_signals," + + " trusted_bidding_signals, contextual_signals," + + " custom_audience_signals) { \n" + + " return {'status': 0, 'ad': ad, 'bid': ad.metadata.result };\n" + + "}\n" + + "function reportWin(ad_selection_signals, per_buyer_signals," + + " signals_for_buyer, contextual_signals, custom_audience_signals) { \n" + + " return {'status': 0, 'results': {'reporting_uri': '" + + BUYER_REPORTING_PATH + + "' } };\n" + + "}"; + private static final String CALCULATION_INTENSE_JS = + "for (let i = 1; i < 1000000000; i++) {\n" + " Math.sqrt(i);\n" + "}"; + private static final String MEMORY_INTENSE_JS = + "var a = []\n" + "for (let i = 0; i < 10000; i++) {\n" + " a.push(i);" + "}"; + + private static final AdSelectionSignals TRUSTED_SCORING_SIGNALS = + AdSelectionSignals.fromString( + "{\n" + + "\t\"render_uri_1\": \"signals_for_1\",\n" + + "\t\"render_uri_2\": \"signals_for_2\"\n" + + "}"); + private static final AdSelectionSignals TRUSTED_BIDDING_SIGNALS = + AdSelectionSignals.fromString( + "{\n" + + "\t\"example\": \"example\",\n" + + "\t\"valid\": \"Also valid\",\n" + + "\t\"list\": \"list\",\n" + + "\t\"of\": \"of\",\n" + + "\t\"keys\": \"trusted bidding signal Values\"\n" + + "}"); + private static final String AD_URI_PREFIX = "/adverts/123/"; + private static final int DELAY_TO_AVOID_THROTTLE_MS = 1001; + protected final Context mContext = ApplicationProvider.getApplicationContext(); + private final AdSelectionClient mAdSelectionClient = + new AdSelectionClient.Builder() + .setContext(mContext) + .setExecutor(CALLBACK_EXECUTOR) + .build(); + private final AdvertisingCustomAudienceClient mCustomAudienceClient = + new AdvertisingCustomAudienceClient.Builder() + .setContext(mContext) + .setExecutor(CALLBACK_EXECUTOR) + .build(); + @Rule public MockWebServerRule mMockWebServerRule = MockWebServerRuleFactory.createForHttps(); + private Dispatcher mDefaultDispatcher; + + @BeforeClass + public static void setupBeforeClass() { + // Disable backoff since we will be killing the process between tests + ShellUtils.runShellCommand(DISABLE_ADSERVICES_BACKOFF_CMD); + InstrumentationRegistry.getInstrumentation() + .getUiAutomation() + .adoptShellPermissionIdentity(Manifest.permission.WRITE_DEVICE_CONFIG); + // TODO(b/245585645) Mark true for the heap size enforcement after installing M105 library + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ADSERVICES, + "fledge_js_isolate_enforce_max_heap_size", + "false", + true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ADSERVICES, "disable_fledge_enrollment_check", "true", true); + DeviceConfig.setProperty( + DeviceConfig.NAMESPACE_ADSERVICES, "ppapi_app_allow_list", "*", true); + } + + public static Uri getUri(String name, String path) { + return Uri.parse("https://" + name + path); + } + + @Before + public void setup() throws InterruptedException { + ShellUtils.runShellCommand(KILL_ADSERVICES_CMD); + Thread.sleep(SLEEP_MS_AFTER_KILL); + mDefaultDispatcher = + new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) { + if (DECISION_LOGIC_PATH.equals(request.getPath())) { + return new MockResponse().setBody(DEFAULT_DECISION_LOGIC_JS); + } else if (BUYER_BIDDING_LOGIC_URI_PATH.equals(request.getPath())) { + return new MockResponse().setBody(DEFAULT_BIDDING_LOGIC_JS); + } else if (BUYER_REPORTING_PATH.equals(request.getPath()) + || SELLER_REPORTING_PATH.equals(request.getPath())) { + return new MockResponse().setBody(""); + } else if (request.getPath().startsWith(TRUSTED_SCORING_SIGNAL_PATH)) { + return new MockResponse().setBody(TRUSTED_SCORING_SIGNALS.toString()); + } else if (request.getPath().startsWith(VALID_TRUSTED_BIDDING_URI_PATH)) { + return new MockResponse().setBody(TRUSTED_BIDDING_SIGNALS.toString()); + } + return new MockResponse().setResponseCode(404); + } + }; + } + + @Test + public void test_joinCustomAudience_success() throws Exception { + CustomAudience ca = + createCustomAudience( + BUYER_1, CUSTOM_AUDIENCE_SHOES, Collections.singletonList(1.0)); + addDelayToAvoidThrottle(); + mCustomAudienceClient + .joinCustomAudience(ca) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + addDelayToAvoidThrottle(); + mCustomAudienceClient + .leaveCustomAudience(ca.getBuyer(), ca.getName()) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + + @Test + public void testAdSelectionAndReporting_normalFlow_success() throws Exception { + List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); + List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); + CustomAudience customAudience1 = + createCustomAudience(BUYER_1, CUSTOM_AUDIENCE_SHOES, bidsForBuyer1); + + // TODO(b/244530379) Make compatible with multiple buyers + CustomAudience customAudience2 = + createCustomAudience(BUYER_1, CUSTOM_AUDIENCE_SHIRT, bidsForBuyer2); + List<CustomAudience> customAudienceList = Arrays.asList(customAudience1, customAudience2); + + mMockWebServerRule.startMockWebServer(mDefaultDispatcher); + + for (CustomAudience ca : customAudienceList) { + addDelayToAvoidThrottle(); + mCustomAudienceClient + .joinCustomAudience(ca) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + + // Running ad selection and asserting that the outcome is returned in < 10 seconds + addDelayToAvoidThrottle(); + AdSelectionOutcome outcome = + mAdSelectionClient + .selectAds(createAdSelectionConfig()) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + // The winning ad should be ad3 from CA shirt + Assert.assertEquals( + "Ad selection outcome is not expected", + createExpectedWinningUri(BUYER_1, CUSTOM_AUDIENCE_SHIRT, 3), + outcome.getRenderUri()); + + ReportImpressionRequest reportImpressionRequest = + new ReportImpressionRequest(outcome.getAdSelectionId(), createAdSelectionConfig()); + + // Performing reporting, and asserting that no exception is thrown + addDelayToAvoidThrottle(); + mAdSelectionClient + .reportImpression(reportImpressionRequest) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + // Cleanup + for (CustomAudience ca : customAudienceList) { + addDelayToAvoidThrottle(DELAY_TO_AVOID_THROTTLE_MS); + mCustomAudienceClient + .leaveCustomAudience(ca.getBuyer(), ca.getName()) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + } + + @Test + public void testAdSelectionAndReporting_executionHeavyJS_success() throws Exception { + List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); + List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); + CustomAudience customAudience1 = + createCustomAudience(BUYER_1, CUSTOM_AUDIENCE_SHOES, bidsForBuyer1); + + // TODO(b/244530379) Make compatible with multiple buyers + CustomAudience customAudience2 = + createCustomAudience(BUYER_1, CUSTOM_AUDIENCE_SHIRT, bidsForBuyer2); + List<CustomAudience> customAudienceList = Arrays.asList(customAudience1, customAudience2); + + String calculation_intense_logic_js = + "function scoreAd(ad, bid, auction_config, seller_signals," + + " trusted_scoring_signals, contextual_signal, user_signal," + + " custom_audience_signal) { \n" + + CALCULATION_INTENSE_JS + + " return {'status': 0, 'score': bid };\n" + + "}\n" + + "function reportResult(ad_selection_config, render_uri, bid," + + " contextual_signals) { \n" + + " return {'status': 0, 'results': {'signals_for_buyer':" + + " '{\"signals_for_buyer\":1}', 'reporting_uri': '" + + SELLER_REPORTING_PATH + + "' } };\n" + + "}"; + + Dispatcher dispatcher = + new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) { + if (DECISION_LOGIC_PATH.equals(request.getPath())) { + return new MockResponse().setBody(calculation_intense_logic_js); + } else if (BUYER_BIDDING_LOGIC_URI_PATH.equals(request.getPath())) { + return new MockResponse().setBody(DEFAULT_BIDDING_LOGIC_JS); + } else if (BUYER_REPORTING_PATH.equals(request.getPath()) + || SELLER_REPORTING_PATH.equals(request.getPath())) { + return new MockResponse().setBody(""); + } else if (request.getPath().startsWith(TRUSTED_SCORING_SIGNAL_PATH)) { + return new MockResponse().setBody(TRUSTED_SCORING_SIGNALS.toString()); + } else if (request.getPath().startsWith(VALID_TRUSTED_BIDDING_URI_PATH)) { + return new MockResponse().setBody(TRUSTED_BIDDING_SIGNALS.toString()); + } + return new MockResponse().setResponseCode(404); + } + }; + + mMockWebServerRule.startMockWebServer(dispatcher); + + for (CustomAudience ca : customAudienceList) { + addDelayToAvoidThrottle(); + mCustomAudienceClient + .joinCustomAudience(ca) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + + // Running ad selection and asserting that the outcome is returned in < 10 seconds + addDelayToAvoidThrottle(); + AdSelectionOutcome outcome = + mAdSelectionClient + .selectAds(createAdSelectionConfig()) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + // The winning ad should be ad3 from CA shirt + Assert.assertEquals( + "Ad selection outcome is not expected", + createExpectedWinningUri(BUYER_1, CUSTOM_AUDIENCE_SHIRT, 3), + outcome.getRenderUri()); + + ReportImpressionRequest reportImpressionRequest = + new ReportImpressionRequest(outcome.getAdSelectionId(), createAdSelectionConfig()); + + // Performing reporting, and asserting that no exception is thrown + addDelayToAvoidThrottle(); + mAdSelectionClient + .reportImpression(reportImpressionRequest) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + // Cleanup + for (CustomAudience ca : customAudienceList) { + addDelayToAvoidThrottle(DELAY_TO_AVOID_THROTTLE_MS); + mCustomAudienceClient + .leaveCustomAudience(ca.getBuyer(), ca.getName()) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + } + + @Test + public void testAdSelectionAndReporting_memoryHeavyJS_success() throws Exception { + List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); + List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); + CustomAudience customAudience1 = + createCustomAudience(BUYER_1, CUSTOM_AUDIENCE_SHOES, bidsForBuyer1); + + // TODO(b/244530379) Make compatible with multiple buyers + CustomAudience customAudience2 = + createCustomAudience(BUYER_1, CUSTOM_AUDIENCE_SHIRT, bidsForBuyer2); + List<CustomAudience> customAudienceList = Arrays.asList(customAudience1, customAudience2); + + String memory_intense_logic_js = + "function scoreAd(ad, bid, auction_config, seller_signals," + + " trusted_scoring_signals, contextual_signal, user_signal," + + " custom_audience_signal) { \n" + + MEMORY_INTENSE_JS + + " return {'status': 0, 'score': bid };\n" + + "}\n" + + "function reportResult(ad_selection_config, render_uri, bid," + + " contextual_signals) { \n" + + " return {'status': 0, 'results': {'signals_for_buyer':" + + " '{\"signals_for_buyer\":1}', 'reporting_uri': '" + + SELLER_REPORTING_PATH + + "' } };\n" + + "}"; + + Dispatcher dispatcher = + new Dispatcher() { + @Override + public MockResponse dispatch(RecordedRequest request) { + if (DECISION_LOGIC_PATH.equals(request.getPath())) { + return new MockResponse().setBody(memory_intense_logic_js); + } else if (BUYER_BIDDING_LOGIC_URI_PATH.equals(request.getPath())) { + return new MockResponse().setBody(DEFAULT_BIDDING_LOGIC_JS); + } else if (BUYER_REPORTING_PATH.equals(request.getPath()) + || SELLER_REPORTING_PATH.equals(request.getPath())) { + return new MockResponse().setBody(""); + } else if (request.getPath().startsWith(TRUSTED_SCORING_SIGNAL_PATH)) { + return new MockResponse().setBody(TRUSTED_SCORING_SIGNALS.toString()); + } else if (request.getPath().startsWith(VALID_TRUSTED_BIDDING_URI_PATH)) { + return new MockResponse().setBody(TRUSTED_BIDDING_SIGNALS.toString()); + } + return new MockResponse().setResponseCode(404); + } + }; + + mMockWebServerRule.startMockWebServer(dispatcher); + + for (CustomAudience ca : customAudienceList) { + addDelayToAvoidThrottle(); + mCustomAudienceClient + .joinCustomAudience(ca) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + + // Running ad selection and asserting that the outcome is returned in < 10 seconds + addDelayToAvoidThrottle(); + AdSelectionOutcome outcome = + mAdSelectionClient + .selectAds(createAdSelectionConfig()) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + // The winning ad should be ad3 from CA shirt + Assert.assertEquals( + "Ad selection outcome is not expected", + createExpectedWinningUri(BUYER_1, CUSTOM_AUDIENCE_SHIRT, 3), + outcome.getRenderUri()); + + ReportImpressionRequest reportImpressionRequest = + new ReportImpressionRequest(outcome.getAdSelectionId(), createAdSelectionConfig()); + + // Performing reporting, and asserting that no exception is thrown + addDelayToAvoidThrottle(); + mAdSelectionClient + .reportImpression(reportImpressionRequest) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + // Cleanup + for (CustomAudience ca : customAudienceList) { + addDelayToAvoidThrottle(DELAY_TO_AVOID_THROTTLE_MS); + mCustomAudienceClient + .leaveCustomAudience(ca.getBuyer(), ca.getName()) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + } + + @Test + public void testAdSelectionAndReporting_multipleCustomAudienceList_success() throws Exception { + List<Double> bidsForBuyer1 = ImmutableList.of(1.1, 2.2); + List<Double> bidsForBuyer2 = ImmutableList.of(4.5, 6.7, 10.0); + CustomAudience customAudience1 = + createCustomAudience(BUYER_1, CUSTOM_AUDIENCE_SHOES, bidsForBuyer1); + + // TODO(b/244530379) Make compatible with multiple buyers + CustomAudience customAudience2 = + createCustomAudience(BUYER_1, CUSTOM_AUDIENCE_SHIRT, bidsForBuyer2); + List<CustomAudience> customAudienceList = new ArrayList<>(); + customAudienceList.add(customAudience1); + customAudienceList.add(customAudience2); + + // Create multiple generic custom audience entries + for (int i = 1; i <= 48; i++) { + CustomAudience customAudience = + createCustomAudience(BUYER_1, "GENERIC_CA_" + i, bidsForBuyer1); + customAudienceList.add(customAudience); + } + mMockWebServerRule.startMockWebServer(mDefaultDispatcher); + + for (CustomAudience ca : customAudienceList) { + addDelayToAvoidThrottle(); + mCustomAudienceClient + .joinCustomAudience(ca) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + + // Running ad selection and asserting that the outcome is returned in < 10 seconds + addDelayToAvoidThrottle(); + AdSelectionOutcome outcome = + mAdSelectionClient + .selectAds(createAdSelectionConfig()) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + // The winning ad should be ad3 from CA shirt + Assert.assertEquals( + "Ad selection outcome is not expected", + createExpectedWinningUri(BUYER_1, CUSTOM_AUDIENCE_SHIRT, 3), + outcome.getRenderUri()); + + ReportImpressionRequest reportImpressionRequest = + new ReportImpressionRequest(outcome.getAdSelectionId(), createAdSelectionConfig()); + + // Performing reporting, and asserting that no exception is thrown + addDelayToAvoidThrottle(); + mAdSelectionClient + .reportImpression(reportImpressionRequest) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + + // Cleanup + for (CustomAudience ca : customAudienceList) { + addDelayToAvoidThrottle(DELAY_TO_AVOID_THROTTLE_MS); + mCustomAudienceClient + .leaveCustomAudience(ca.getBuyer(), ca.getName()) + .get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS); + } + } + + private void addDelayToAvoidThrottle() throws InterruptedException { + addDelayToAvoidThrottle(DELAY_TO_AVOID_THROTTLE_MS); + } + + private void addDelayToAvoidThrottle(int delayValueMs) throws InterruptedException { + if (delayValueMs > 0) { + Thread.sleep(delayValueMs); + } + } + + private CustomAudience createCustomAudience( + final AdTechIdentifier buyer, + String name, + List<Double> bids, + Instant activationTime, + Instant expirationTime) { + // Generate ads for with bids provided + List<AdData> ads = new ArrayList<>(); + + // Create ads with the custom audience name and bid number as the ad URI + // Add the bid value to the metadata + for (int i = 0; i < bids.size(); i++) { + ads.add( + new AdData.Builder() + .setRenderUri( + getUri( + buyer.toString(), + AD_URI_PREFIX + name + "/ad" + (i + 1))) + .setMetadata("{\"result\":" + bids.get(i) + "}") + .build()); + } + + return new CustomAudience.Builder() + .setBuyer(buyer) + .setName(name) + .setActivationTime(activationTime) + .setExpirationTime(expirationTime) + .setDailyUpdateUri(getValidDailyUpdateUriByBuyer(buyer)) + .setUserBiddingSignals(VALID_USER_BIDDING_SIGNALS) + .setTrustedBiddingData(getValidTrustedBiddingDataByBuyer(buyer)) + .setBiddingLogicUri(mMockWebServerRule.uriForPath(BUYER_BIDDING_LOGIC_URI_PATH)) + .setAds(ads) + .build(); + } + + private CustomAudience createCustomAudience( + final AdTechIdentifier buyer, String name, List<Double> bids) { + return createCustomAudience( + buyer, name, bids, VALID_ACTIVATION_TIME, VALID_EXPIRATION_TIME); + } + + private AdSelectionConfig createAdSelectionConfig() { + return new AdSelectionConfig.Builder() + .setSeller(SELLER) + .setDecisionLogicUri(mMockWebServerRule.uriForPath(DECISION_LOGIC_PATH)) + .setCustomAudienceBuyers(CUSTOM_AUDIENCE_BUYERS) + .setAdSelectionSignals(AD_SELECTION_SIGNALS) + .setSellerSignals(SELLER_SIGNALS) + .setPerBuyerSignals(PER_BUYER_SIGNALS) + .setTrustedScoringSignalsUri( + mMockWebServerRule.uriForPath(TRUSTED_SCORING_SIGNAL_PATH)) + // TODO(b/244530379) Make compatible with multiple buyers + .setCustomAudienceBuyers(Collections.singletonList(BUYER_1)) + .build(); + } + + private Uri createExpectedWinningUri( + AdTechIdentifier buyer, String customAudienceName, int adNumber) { + return getUri(buyer.toString(), AD_URI_PREFIX + customAudienceName + "/ad" + adNumber); + } + + // TODO(b/244530379) Make compatible with multiple buyers + public Uri getValidDailyUpdateUriByBuyer(AdTechIdentifier buyer) { + return mMockWebServerRule.uriForPath("/update"); + } + + public TrustedBiddingData getValidTrustedBiddingDataByBuyer(AdTechIdentifier buyer) { + return new TrustedBiddingData.Builder() + .setTrustedBiddingKeys(VALID_TRUSTED_BIDDING_KEYS) + .setTrustedBiddingUri(getValidTrustedBiddingUriByBuyer(buyer)) + .build(); + } + + // TODO(b/244530379) Make compatible with multiple buyers + public Uri getValidTrustedBiddingUriByBuyer(AdTechIdentifier buyer) { + return mMockWebServerRule.uriForPath(VALID_TRUSTED_BIDDING_URI_PATH); + } +} |