blob: 3fcd7adb9797a23ce0ca0bc4155d576f61b23207 [file] [log] [blame]
/*
* 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.debuggablects;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertThrows;
import android.Manifest;
import android.adservices.adselection.AdSelectionConfig;
import android.adservices.adselection.AdSelectionConfigFixture;
import android.adservices.adselection.AdSelectionOutcome;
import android.adservices.adselection.AddAdSelectionOverrideRequest;
import android.adservices.adselection.ReportImpressionRequest;
import android.adservices.clients.adselection.AdSelectionClient;
import android.adservices.clients.adselection.TestAdSelectionClient;
import android.adservices.clients.customaudience.AdvertisingCustomAudienceClient;
import android.adservices.clients.customaudience.TestAdvertisingCustomAudienceClient;
import android.adservices.common.AdData;
import android.adservices.common.AdSelectionSignals;
import android.adservices.common.AdTechIdentifier;
import android.adservices.common.CommonFixture;
import android.adservices.customaudience.AddCustomAudienceOverrideRequest;
import android.adservices.customaudience.CustomAudience;
import android.adservices.customaudience.CustomAudienceFixture;
import android.adservices.customaudience.TrustedBiddingDataFixture;
import android.net.Uri;
import android.os.Process;
import android.util.Log;
import androidx.test.platform.app.InstrumentationRegistry;
import com.android.adservices.service.PhFlagsFixture;
import com.android.adservices.service.devapi.DevContext;
import com.android.adservices.service.devapi.DevContextFilter;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.MoreExecutors;
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import java.time.Instant;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
public class FledgeCtsDebuggableTest extends ForegroundDebuggableCtsTest {
public static final String TAG = "adservices";
// Time allowed by current test setup for APIs to respond
private static final int API_RESPONSE_TIMEOUT_SECONDS = 5;
// This is used to check actual API timeout conditions; note that the default overall timeout
// for ad selection is 10 seconds
private static final int API_RESPONSE_LONGER_TIMEOUT_SECONDS = 12;
private static final Executor CALLBACK_EXECUTOR = Executors.newCachedThreadPool();
private static final AdTechIdentifier BUYER_1 = AdSelectionConfigFixture.BUYER_1;
private static final AdTechIdentifier BUYER_2 = AdSelectionConfigFixture.BUYER_2;
private static final String AD_URI_PREFIX = "/adverts/123/";
private static final String SELLER_DECISION_LOGIC_URI_PATH = "/ssp/decision/logic/";
private static final String BUYER_BIDDING_LOGIC_URI_PATH = "/buyer/bidding/logic/";
private static final String SELLER_TRUSTED_SIGNAL_URI_PATH = "/kv/seller/signals/";
private static final String SELLER_REPORTING_PATH = "/reporting/seller";
private static final String BUYER_REPORTING_PATH = "/reporting/buyer";
private static final String SELLER_REPORTING_URI =
String.format("https://%s%s", AdSelectionConfigFixture.SELLER, SELLER_REPORTING_PATH);
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_URI
+ "' } };\n"
+ "}";
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 APP_NOT_DEBUGGABLE = "App is not debuggable!";
private static final String DEVELOPER_OPTIONS_OFF = "Developer options are off!";
private static final AdSelectionConfig AD_SELECTION_CONFIG =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setCustomAudienceBuyers(Arrays.asList(BUYER_1, BUYER_2))
.setDecisionLogicUri(
Uri.parse(
String.format(
"https://%s%s",
AdSelectionConfigFixture.SELLER,
SELLER_DECISION_LOGIC_URI_PATH)))
.setTrustedScoringSignalsUri(
Uri.parse(
String.format(
"https://%s%s",
AdSelectionConfigFixture.SELLER,
SELLER_TRUSTED_SIGNAL_URI_PATH)))
.build();
private static final String BUYER_2_REPORTING_URI =
String.format("https://%s%s", AdSelectionConfigFixture.BUYER_2, BUYER_REPORTING_PATH);
private static final String BUYER_2_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_2_REPORTING_URI
+ "' } };\n"
+ "}";
private static final String BUYER_2_BIDDING_LOGIC_JS_V3 =
"function generateBid(customAudience, auction_signals, per_buyer_signals,\n"
+ " trusted_bidding_signals, contextual_signals) {\n"
+ " const ads = customAudience.ads;\n"
+ " let result = null;\n"
+ " for (const ad of ads) {\n"
+ " if (!result || ad.metadata.result > result.metadata.result) {\n"
+ " result = ad;\n"
+ " }\n"
+ " }\n"
+ " return { 'status': 0, 'ad': result, 'bid': result.metadata.result, "
+ "'render': result.render_uri };\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_2_REPORTING_URI
+ "' } };\n"
+ "}";
private static final String BUYER_1_REPORTING_URI =
String.format("https://%s%s", AdSelectionConfigFixture.BUYER_1, BUYER_REPORTING_PATH);
private static final String BUYER_1_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_1_REPORTING_URI
+ "' } };\n"
+ "}";
private static final String BUYER_1_BIDDING_LOGIC_JS_V3 =
"function generateBid(customAudience, auction_signals, per_buyer_signals,\n"
+ " trusted_bidding_signals, contextual_signals) {\n"
+ " const ads = customAudience.ads;\n"
+ " let result = null;\n"
+ " for (const ad of ads) {\n"
+ " if (!result || ad.metadata.result > result.metadata.result) {\n"
+ " result = ad;\n"
+ " }\n"
+ " }\n"
+ " return { 'status': 0, 'ad': result, 'bid': result.metadata.result, "
+ "'render': result.render_uri };\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_1_REPORTING_URI
+ "' } };\n"
+ "}";
private AdSelectionClient mAdSelectionClient;
private TestAdSelectionClient mTestAdSelectionClient;
private AdvertisingCustomAudienceClient mCustomAudienceClient;
private TestAdvertisingCustomAudienceClient mTestCustomAudienceClient;
private DevContext mDevContext;
private boolean mHasAccessToDevOverrides;
private String mAccessStatus;
@Before
public void setup() throws InterruptedException {
assertForegroundActivityStarted();
mAdSelectionClient =
new AdSelectionClient.Builder()
.setContext(sContext)
.setExecutor(CALLBACK_EXECUTOR)
.build();
mTestAdSelectionClient =
new TestAdSelectionClient.Builder()
.setContext(sContext)
.setExecutor(CALLBACK_EXECUTOR)
.build();
mCustomAudienceClient =
new AdvertisingCustomAudienceClient.Builder()
.setContext(sContext)
.setExecutor(MoreExecutors.directExecutor())
.build();
mTestCustomAudienceClient =
new TestAdvertisingCustomAudienceClient.Builder()
.setContext(sContext)
.setExecutor(MoreExecutors.directExecutor())
.build();
DevContextFilter devContextFilter = DevContextFilter.create(sContext);
mDevContext = DevContextFilter.create(sContext).createDevContext(Process.myUid());
boolean isDebuggable =
devContextFilter.isDebuggable(mDevContext.getCallingAppPackageName());
boolean isDeveloperMode = devContextFilter.isDeveloperMode();
mHasAccessToDevOverrides = mDevContext.getDevOptionsEnabled();
mAccessStatus =
String.format("Debuggable: %b\n", isDebuggable)
+ String.format("Developer options on: %b", isDeveloperMode);
InstrumentationRegistry.getInstrumentation()
.getUiAutomation()
.adoptShellPermissionIdentity(Manifest.permission.WRITE_DEVICE_CONFIG);
// Enable CTS to be run with versions of WebView < M105
PhFlagsFixture.overrideEnforceIsolateMaxHeapSize(false);
PhFlagsFixture.overrideIsolateMaxHeapSizeBytes(0);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
}
@After
public void tearDown() throws Exception {
mTestAdSelectionClient.resetAllAdSelectionConfigRemoteOverrides();
mTestCustomAudienceClient.resetAllCustomAudienceOverrides();
}
@Test
public void testFledgeAuctionSelectionFlow_overall_Success() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
CustomAudience customAudience2 = createCustomAudience(BUYER_2, bidsForBuyer2);
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Adding AdSelection override, no result to do assertion on. Failures will generate an
// exception."
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
AD_SELECTION_CONFIG, DEFAULT_DECISION_LOGIC_JS, TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest2 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience2.getBuyer())
.setName(customAudience2.getName())
.setBiddingLogicJs(BUYER_2_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
Log.i(
TAG,
"Running ad selection with logic URI " + AD_SELECTION_CONFIG.getDecisionLogicUri());
Log.i(
TAG,
"Decision logic URI domain is "
+ AD_SELECTION_CONFIG.getDecisionLogicUri().getHost());
// Running ad selection and asserting that the outcome is returned in < 10 seconds
AdSelectionOutcome outcome =
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Assert that the ad3 from buyer 2 is rendered, since it had the highest bid and score
Assert.assertEquals(
CommonFixture.getUri(BUYER_2, AD_URI_PREFIX + "/ad3"), outcome.getRenderUri());
ReportImpressionRequest reportImpressionRequest =
new ReportImpressionRequest(outcome.getAdSelectionId(), AD_SELECTION_CONFIG);
// Performing reporting, and asserting that no exception is thrown
mAdSelectionClient
.reportImpression(reportImpressionRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void testFledgeFlow_manuallyUpdateCustomAudience_Success() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
List<Double> bidsForBuyer = ImmutableList.of(1.1, 2.2);
List<Double> updatedBidsForBuyer = ImmutableList.of(4.5, 6.7, 10.0);
CustomAudience customAudience = createCustomAudience(BUYER_1, bidsForBuyer);
CustomAudience customAudienceUpdate = createCustomAudience(BUYER_1, updatedBidsForBuyer);
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception.
mCustomAudienceClient
.joinCustomAudience(customAudience)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudienceUpdate)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Adding AdSelection override, no result to do assertion on. Failures will generate an
// exception.
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
AD_SELECTION_CONFIG, DEFAULT_DECISION_LOGIC_JS, TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience.getBuyer())
.setName(customAudience.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception.
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
Log.i(
TAG,
"Running ad selection with logic URI " + AD_SELECTION_CONFIG.getDecisionLogicUri());
Log.i(
TAG,
"Decision logic URI domain is "
+ AD_SELECTION_CONFIG.getDecisionLogicUri().getHost());
// Running ad selection and asserting that the outcome is returned in < 10 seconds
AdSelectionOutcome outcome =
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Assert that the ad3 from buyer 1 is rendered, since it had the highest bid and score
// This verifies that the custom audience was updated, since it originally only had two ads
Assert.assertEquals(
CommonFixture.getUri(BUYER_1, AD_URI_PREFIX + "/ad3"), outcome.getRenderUri());
ReportImpressionRequest reportImpressionRequest =
new ReportImpressionRequest(outcome.getAdSelectionId(), AD_SELECTION_CONFIG);
// Performing reporting, and asserting that no exception is thrown
mAdSelectionClient
.reportImpression(reportImpressionRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void testAdSelection_etldViolation_failure() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
CustomAudience customAudience2 = createCustomAudience(BUYER_2, bidsForBuyer2);
AdSelectionConfig adSelectionConfigWithEtldViolations =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setCustomAudienceBuyers(Arrays.asList(BUYER_1, BUYER_2))
.setDecisionLogicUri(
Uri.parse(
String.format(
"https://%s%s",
AdSelectionConfigFixture.SELLER + "etld_noise",
SELLER_DECISION_LOGIC_URI_PATH)))
.setTrustedScoringSignalsUri(
Uri.parse(
String.format(
"https://%s%s",
AdSelectionConfigFixture.SELLER + "etld_noise",
SELLER_TRUSTED_SIGNAL_URI_PATH)))
.build();
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Adding AdSelection override, no result to do assertion on. Failures will generate an
// exception."
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
adSelectionConfigWithEtldViolations,
DEFAULT_DECISION_LOGIC_JS,
TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest2 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience2.getBuyer())
.setName(customAudience2.getName())
.setBiddingLogicJs(BUYER_2_BIDDING_LOGIC_JS)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Running ad selection and asserting that exception is thrown when decision and signals
// URIs are not etld+1 compliant
Exception selectAdsException =
assertThrows(
ExecutionException.class,
() ->
mAdSelectionClient
.selectAds(adSelectionConfigWithEtldViolations)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS));
assertThat(selectAdsException.getCause()).isInstanceOf(IllegalArgumentException.class);
}
@Test
public void testReportImpression_etldViolation_failure() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
CustomAudience customAudience2 = createCustomAudience(BUYER_2, bidsForBuyer2);
AdSelectionConfig adSelectionConfigWithEtldViolations =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setCustomAudienceBuyers(Arrays.asList(BUYER_1, BUYER_2))
.setDecisionLogicUri(
Uri.parse(
String.format(
"https://%s%s",
AdSelectionConfigFixture.SELLER + "etld_noise",
SELLER_DECISION_LOGIC_URI_PATH)))
.setTrustedScoringSignalsUri(
Uri.parse(
String.format(
"https://%s%s",
AdSelectionConfigFixture.SELLER + "etld_noise",
SELLER_TRUSTED_SIGNAL_URI_PATH)))
.build();
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Adding AdSelection override, no result to do assertion on. Failures will generate an
// exception."
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
AD_SELECTION_CONFIG, DEFAULT_DECISION_LOGIC_JS, TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest2 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience2.getBuyer())
.setName(customAudience2.getName())
.setBiddingLogicJs(BUYER_2_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Running ad selection and asserting that the outcome is returned in < 10 seconds
AdSelectionOutcome outcome =
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Assert that the ad3 from buyer 2 is rendered, since it had the highest bid and score
Assert.assertEquals(
CommonFixture.getUri(BUYER_2, AD_URI_PREFIX + "/ad3"), outcome.getRenderUri());
ReportImpressionRequest reportImpressionRequest =
new ReportImpressionRequest(
outcome.getAdSelectionId(), adSelectionConfigWithEtldViolations);
// Running report Impression and asserting that exception is thrown when decision and
// signals URIs are not etld+1 compliant
Exception selectAdsException =
assertThrows(
ExecutionException.class,
() ->
mAdSelectionClient
.reportImpression(reportImpressionRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS));
assertThat(selectAdsException.getCause()).isInstanceOf(IllegalArgumentException.class);
}
@Test
public void testAdSelection_skipAdsMalformedBiddingLogic_success() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
CustomAudience customAudience2 = createCustomAudience(BUYER_2, bidsForBuyer2);
String malformedBiddingLogic = " This is an invalid javascript";
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Adding AdSelection override, no result to do assertion on. Failures will generate an
// exception."
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
AD_SELECTION_CONFIG, DEFAULT_DECISION_LOGIC_JS, TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest2 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience2.getBuyer())
.setName(customAudience2.getName())
.setBiddingLogicJs(malformedBiddingLogic)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Running ad selection and asserting that the outcome is returned in < 10 seconds
AdSelectionOutcome outcome =
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Assert that the ad3 from buyer 2 is skipped despite having the highest bid, since it has
// malformed bidding logic
// The winner should come from buyer1 with the highest bid i.e. ad2
Assert.assertEquals(
CommonFixture.getUri(BUYER_1, AD_URI_PREFIX + "/ad2"), outcome.getRenderUri());
ReportImpressionRequest reportImpressionRequest =
new ReportImpressionRequest(outcome.getAdSelectionId(), AD_SELECTION_CONFIG);
// Performing reporting, and asserting that no exception is thrown
mAdSelectionClient
.reportImpression(reportImpressionRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void testAdSelection_malformedScoringLogic_failure() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
CustomAudience customAudience2 = createCustomAudience(BUYER_2, bidsForBuyer2);
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
String malformedScoringLogic = " This is an invalid javascript";
// Adding malformed scoring logic AdSelection override, no result to do assertion on.
// Failures will generate an
// exception."
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
AD_SELECTION_CONFIG, malformedScoringLogic, TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest2 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience2.getBuyer())
.setName(customAudience2.getName())
.setBiddingLogicJs(BUYER_2_BIDDING_LOGIC_JS)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Ad Selection will fail due to scoring logic malformed
Exception selectAdsException =
assertThrows(
ExecutionException.class,
() ->
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS));
assertThat(selectAdsException.getCause()).isInstanceOf(IllegalStateException.class);
}
@Test
public void testAdSelection_skipAdsFailedGettingBiddingLogic_success() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
CustomAudience customAudience2 = createCustomAudience(BUYER_2, bidsForBuyer2);
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Adding AdSelection override, no result to do assertion on. Failures will generate an
// exception."
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
AD_SELECTION_CONFIG, DEFAULT_DECISION_LOGIC_JS, TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// We do not provide override for CA 2, that should lead to failure to get biddingLogic
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Running ad selection and asserting that the outcome is returned in < 10 seconds
AdSelectionOutcome outcome =
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Assert that the ad3 from buyer 2 is skipped despite having the highest bid, since it has
// missing bidding logic
// The winner should come from buyer1 with the highest bid i.e. ad2
Assert.assertEquals(
CommonFixture.getUri(BUYER_1, AD_URI_PREFIX + "/ad2"), outcome.getRenderUri());
ReportImpressionRequest reportImpressionRequest =
new ReportImpressionRequest(outcome.getAdSelectionId(), AD_SELECTION_CONFIG);
// Performing reporting, and asserting that no exception is thrown
mAdSelectionClient
.reportImpression(reportImpressionRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void testAdSelection_errorGettingScoringLogic_failure() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
CustomAudience customAudience2 = createCustomAudience(BUYER_2, bidsForBuyer2);
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Skip adding AdSelection override, no result to do assertion on. Failures will generate an
// exception."
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest2 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience2.getBuyer())
.setName(customAudience2.getName())
.setBiddingLogicJs(BUYER_2_BIDDING_LOGIC_JS)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Ad Selection will fail due to scoring logic not found, because the URI that is used to
// fetch scoring logic does not exist
Exception selectAdsException =
assertThrows(
ExecutionException.class,
() ->
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(
API_RESPONSE_LONGER_TIMEOUT_SECONDS,
TimeUnit.SECONDS));
// Sometimes a 400 status code is returned (ISE) instead of the network fetch timing out
assertThat(
selectAdsException.getCause() instanceof TimeoutException
|| selectAdsException.getCause() instanceof IllegalStateException)
.isTrue();
}
@Test
public void testAdSelectionFlow_skipNonActivatedCA_Success() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
// CA 2 activated long in the future
CustomAudience customAudience2 =
createCustomAudience(
BUYER_2,
bidsForBuyer2,
CustomAudienceFixture.VALID_DELAYED_ACTIVATION_TIME,
CustomAudienceFixture.VALID_DELAYED_EXPIRATION_TIME);
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Adding AdSelection override, no result to do assertion on. Failures will generate an
// exception."
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
AD_SELECTION_CONFIG, DEFAULT_DECISION_LOGIC_JS, TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest2 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience2.getBuyer())
.setName(customAudience2.getName())
.setBiddingLogicJs(BUYER_2_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Running ad selection and asserting that the outcome is returned in < 10 seconds
AdSelectionOutcome outcome =
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Assert that the ad3 from buyer 2 is skipped despite having the highest bid, since it is
// not activated yet
// The winner should come from buyer1 with the highest bid i.e. ad2
Assert.assertEquals(
CommonFixture.getUri(BUYER_1, AD_URI_PREFIX + "/ad2"), outcome.getRenderUri());
ReportImpressionRequest reportImpressionRequest =
new ReportImpressionRequest(outcome.getAdSelectionId(), AD_SELECTION_CONFIG);
// Performing reporting, and asserting that no exception is thrown
mAdSelectionClient
.reportImpression(reportImpressionRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void testAdSelectionFlow_skipExpiredCA_Success() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
int caTimeToExpireSeconds = 2;
// Since we cannot create CA which is already expired, we create one which expires in few
// seconds
// We will then wait till this CA expires before we run Ad Selection
CustomAudience customAudience2 =
createCustomAudience(
BUYER_2,
bidsForBuyer2,
CustomAudienceFixture.VALID_ACTIVATION_TIME,
Instant.now().plusSeconds(caTimeToExpireSeconds));
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Adding AdSelection override, no result to do assertion on. Failures will generate an
// exception."
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
AD_SELECTION_CONFIG, DEFAULT_DECISION_LOGIC_JS, TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest2 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience2.getBuyer())
.setName(customAudience2.getName())
.setBiddingLogicJs(BUYER_2_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Wait to ensure that CA2 gets expired
Thread.sleep(caTimeToExpireSeconds * 2 * 1000);
// Running ad selection and asserting that the outcome is returned in < 10 seconds
AdSelectionOutcome outcome =
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Assert that the ad3 from buyer 2 is skipped despite having the highest bid, since it is
// expired
// The winner should come from buyer1 with the highest bid i.e. ad2
Assert.assertEquals(
CommonFixture.getUri(BUYER_1, AD_URI_PREFIX + "/ad2"), outcome.getRenderUri());
ReportImpressionRequest reportImpressionRequest =
new ReportImpressionRequest(outcome.getAdSelectionId(), AD_SELECTION_CONFIG);
// Performing reporting, and asserting that no exception is thrown
mAdSelectionClient
.reportImpression(reportImpressionRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void testAdSelectionFlow_skipCAsThatTimeoutDuringBidding_Success() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
CustomAudience customAudience2 = this.createCustomAudience(BUYER_2, bidsForBuyer2);
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
String jsWaitMoreThanAllowedForBiddingPerCa = insertJsWait(5000);
String readBidFromAdMetadataWithDelayJs =
"function generateBid(customAudience, auction_signals, per_buyer_signals,\n"
+ " trusted_bidding_signals, contextual_signals) {\n"
+ " const ads = customAudience.ads;\n"
+ " let result = null;\n"
+ " for (const ad of ads) {\n"
+ " if (!result || ad.metadata.result > result.metadata.result) {\n"
+ " result = ad;\n"
+ " }\n"
+ " }\n"
+ jsWaitMoreThanAllowedForBiddingPerCa
+ " return { 'status': 0, 'ad': result, 'bid': result.metadata.result, "
+ "'render': result.render_uri };\n"
+ "}\n";
// Adding AdSelection override, no result to do assertion on. Failures will generate an
// exception."
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
AD_SELECTION_CONFIG, DEFAULT_DECISION_LOGIC_JS, TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(BUYER_1_BIDDING_LOGIC_JS_V3)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest2 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience2.getBuyer())
.setName(customAudience2.getName())
.setBiddingLogicJs(readBidFromAdMetadataWithDelayJs)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Running ad selection and asserting that the outcome is returned in < 10 seconds
AdSelectionOutcome outcome =
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(API_RESPONSE_LONGER_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Assert that the ad3 from buyer 2 is skipped despite having the highest bid, since it
// timed out
// The winner should come from buyer1 with the highest bid i.e. ad2
Assert.assertEquals(
CommonFixture.getUri(BUYER_1, AD_URI_PREFIX + "/ad2"), outcome.getRenderUri());
ReportImpressionRequest reportImpressionRequest =
new ReportImpressionRequest(outcome.getAdSelectionId(), AD_SELECTION_CONFIG);
// Performing reporting, and asserting that no exception is thrown
mAdSelectionClient
.reportImpression(reportImpressionRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
@Test
public void testAdSelection_overallTimeout_Failure() throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
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, bidsForBuyer1);
CustomAudience customAudience2 = createCustomAudience(BUYER_2, bidsForBuyer2);
// Joining custom audiences, no result to do assertion on. Failures will generate an
// exception."
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// TODO(b/266725238): Remove/modify once the API rate limit has been adjusted for FLEDGE
Thread.sleep(PhFlagsFixture.DEFAULT_API_RATE_LIMIT_SLEEP_MS);
mCustomAudienceClient
.joinCustomAudience(customAudience2)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
String jsWaitMoreThanAllowedForScoring = insertJsWait(10000);
String useBidAsScoringWithDelayJs =
"function scoreAd(ad, bid, auction_config, seller_signals, "
+ "trusted_scoring_signals, contextual_signal, user_signal, "
+ "custom_audience_signal) { \n"
+ jsWaitMoreThanAllowedForScoring
+ " return {'status': 0, 'score': bid };\n"
+ "}";
// Adding AdSelection override, no result to do assertion on. Failures will generate an
// exception."
AddAdSelectionOverrideRequest addAdSelectionOverrideRequest =
new AddAdSelectionOverrideRequest(
AD_SELECTION_CONFIG, useBidAsScoringWithDelayJs, TRUSTED_SCORING_SIGNALS);
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(addAdSelectionOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience2.getBuyer())
.setName(customAudience2.getName())
.setBiddingLogicJs(BUYER_2_BIDDING_LOGIC_JS)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override, no result to do assertion on. Failures will generate an
// exception."
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
Log.i(
TAG,
"Running ad selection with logic URI " + AD_SELECTION_CONFIG.getDecisionLogicUri());
Log.i(
TAG,
"Decision logic URI domain is "
+ AD_SELECTION_CONFIG.getDecisionLogicUri().getHost());
// Running ad selection and asserting that the outcome is returned in < 10 seconds
Exception selectAdsException =
assertThrows(
ExecutionException.class,
() ->
mAdSelectionClient
.selectAds(AD_SELECTION_CONFIG)
.get(
API_RESPONSE_LONGER_TIMEOUT_SECONDS,
TimeUnit.SECONDS));
assertThat(selectAdsException.getCause()).isInstanceOf(TimeoutException.class);
}
private String insertJsWait(long waitTime) {
return " const wait = (ms) => {\n"
+ " var start = new Date().getTime();\n"
+ " var end = start;\n"
+ " while(end < start + ms) {\n"
+ " end = new Date().getTime();\n"
+ " }\n"
+ " }\n"
+ String.format(" wait(\"%d\");\n", waitTime);
}
/**
* @param buyer The name of the buyer for this Custom Audience
* @param bids these bids, are added to its metadata. Our JS logic then picks this value and
* creates ad with the provided value as bid
* @return a real Custom Audience object that can be persisted and used in bidding and scoring
*/
private CustomAudience createCustomAudience(final AdTechIdentifier buyer, List<Double> bids) {
return createCustomAudience(
buyer,
bids,
CustomAudienceFixture.VALID_ACTIVATION_TIME,
CustomAudienceFixture.VALID_EXPIRATION_TIME);
}
private CustomAudience createCustomAudience(
final AdTechIdentifier buyer,
List<Double> bids,
Instant activationTime,
Instant expirationTime) {
// Generate ads for with bids provided
List<AdData> ads = new ArrayList<>();
// Create ads with the buyer 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(
CommonFixture.getUri(buyer, AD_URI_PREFIX + "/ad" + (i + 1)))
.setMetadata("{\"result\":" + bids.get(i) + "}")
.build());
}
return new CustomAudience.Builder()
.setBuyer(buyer)
.setName(buyer + CustomAudienceFixture.VALID_NAME)
.setActivationTime(activationTime)
.setExpirationTime(expirationTime)
.setDailyUpdateUri(CustomAudienceFixture.getValidDailyUpdateUriByBuyer(buyer))
.setUserBiddingSignals(CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS)
.setTrustedBiddingData(
TrustedBiddingDataFixture.getValidTrustedBiddingDataByBuyer(buyer))
.setBiddingLogicUri(CommonFixture.getUri(buyer, BUYER_BIDDING_LOGIC_URI_PATH))
.setAds(ads)
.build();
}
private AdSelectionOutcome runAdSelectionAsPreSteps(
double bid, AdTechIdentifier buyer, String buyerDecisionLogic) throws Exception {
Assume.assumeTrue(mAccessStatus, mHasAccessToDevOverrides);
List<Double> bidsForBuyer = ImmutableList.of(bid);
CustomAudience customAudience1 = createCustomAudience(buyer, bidsForBuyer);
// Joining custom audiences
mCustomAudienceClient
.joinCustomAudience(customAudience1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AddCustomAudienceOverrideRequest addCustomAudienceOverrideRequest1 =
new AddCustomAudienceOverrideRequest.Builder()
.setBuyer(customAudience1.getBuyer())
.setName(customAudience1.getName())
.setBiddingLogicJs(buyerDecisionLogic)
.setTrustedBiddingSignals(TRUSTED_BIDDING_SIGNALS)
.build();
// Adding Custom audience override
mTestCustomAudienceClient
.overrideCustomAudienceRemoteInfo(addCustomAudienceOverrideRequest1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
AdSelectionConfig adSelectionConfigBuyer1 =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setDecisionLogicUri(
Uri.parse(
String.format(
"https://%s%s",
AdSelectionConfigFixture.SELLER,
SELLER_DECISION_LOGIC_URI_PATH)))
.setTrustedScoringSignalsUri(
Uri.parse(
String.format(
"https://%s%s",
AdSelectionConfigFixture.SELLER,
SELLER_TRUSTED_SIGNAL_URI_PATH)))
.setCustomAudienceBuyers(Collections.singletonList(buyer))
.build();
// Adding AdSelection override
mTestAdSelectionClient
.overrideAdSelectionConfigRemoteInfo(
new AddAdSelectionOverrideRequest(
adSelectionConfigBuyer1,
DEFAULT_DECISION_LOGIC_JS,
TRUSTED_SCORING_SIGNALS))
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
// Running ad selection and asserting that the outcome is returned in < 10 seconds
return mAdSelectionClient
.selectAds(adSelectionConfigBuyer1)
.get(API_RESPONSE_TIMEOUT_SECONDS, TimeUnit.SECONDS);
}
}