blob: ae38208c5412a7186df837850b5a4d12bda36c4c [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 com.android.adservices.service.adselection;
import static android.adservices.adselection.CustomAudienceBiddingInfoFixture.DATA_VERSION_1;
import static android.adservices.adselection.CustomAudienceBiddingInfoFixture.DATA_VERSION_2;
import static android.adservices.common.AdServicesStatusUtils.ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE;
import static android.adservices.common.AdServicesStatusUtils.RATE_LIMIT_REACHED_ERROR_MESSAGE;
import static android.adservices.common.AdServicesStatusUtils.STATUS_BACKGROUND_CALLER;
import static android.adservices.common.AdServicesStatusUtils.STATUS_INTERNAL_ERROR;
import static android.adservices.common.AdServicesStatusUtils.STATUS_RATE_LIMIT_REACHED;
import static android.adservices.common.AdServicesStatusUtils.STATUS_SUCCESS;
import static android.adservices.common.AdServicesStatusUtils.STATUS_TIMEOUT;
import static android.adservices.common.AdServicesStatusUtils.STATUS_UNSET;
import static android.adservices.common.AdServicesStatusUtils.STATUS_USER_CONSENT_REVOKED;
import static com.android.adservices.data.adselection.AdSelectionDatabase.DATABASE_NAME;
import static com.android.adservices.service.PhFlagsFixture.EXTENDED_FLEDGE_AD_SELECTION_BIDDING_TIMEOUT_PER_CA_MS;
import static com.android.adservices.service.PhFlagsFixture.EXTENDED_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS;
import static com.android.adservices.service.PhFlagsFixture.EXTENDED_FLEDGE_AD_SELECTION_OVERALL_TIMEOUT_MS;
import static com.android.adservices.service.PhFlagsFixture.EXTENDED_FLEDGE_AD_SELECTION_SCORING_TIMEOUT_MS;
import static com.android.adservices.service.PhFlagsFixture.EXTENDED_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS;
import static com.android.adservices.service.PhFlagsFixture.EXTENDED_FLEDGE_REPORT_IMPRESSION_OVERALL_TIMEOUT_MS;
import static com.android.adservices.service.adselection.AdSelectionRunner.AD_SELECTION_ERROR_PATTERN;
import static com.android.adservices.service.adselection.AdSelectionRunner.AD_SELECTION_TIMED_OUT;
import static com.android.adservices.service.adselection.AdSelectionRunner.ERROR_AD_SELECTION_FAILURE;
import static com.android.adservices.service.adselection.AdSelectionRunner.ERROR_NO_BUYERS_OR_CONTEXTUAL_ADS_AVAILABLE;
import static com.android.adservices.service.adselection.AdSelectionRunner.ERROR_NO_CA_AND_CONTEXTUAL_ADS_AVAILABLE;
import static com.android.adservices.service.adselection.AdSelectionRunner.ERROR_NO_VALID_BIDS_OR_CONTEXTUAL_ADS_FOR_SCORING;
import static com.android.adservices.service.adselection.AdSelectionRunner.ERROR_NO_WINNING_AD_FOUND;
import static com.android.adservices.service.adselection.AdSelectionRunner.ON_DEVICE_AUCTION_KILL_SWITCH_ENABLED;
import static com.android.adservices.service.adselection.AdSelectionScriptEngine.NUM_BITS_STOCHASTIC_ROUNDING;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.BIDDING_STAGE_END_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.BIDDING_STAGE_START_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.DB_AD_SELECTION_FILE_SIZE;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_BUYERS_CUSTOM_AUDIENCE_END_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_BUYERS_CUSTOM_AUDIENCE_LATENCY_MS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.IS_RMKT_ADS_WON;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.IS_RMKT_ADS_WON_UNSET;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.PERSIST_AD_SELECTION_END_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.PERSIST_AD_SELECTION_START_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.RUN_AD_BIDDING_END_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.RUN_AD_BIDDING_LATENCY_MS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.RUN_AD_BIDDING_START_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.RUN_AD_SELECTION_INTERNAL_FINAL_LATENCY_MS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.RUN_AD_SELECTION_OVERALL_LATENCY_MS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.START_ELAPSED_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.STOP_ELAPSED_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.TOTAL_BIDDING_STAGE_LATENCY_IN_MS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.sCallerMetadata;
import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.any;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.anyLong;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.never;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verifyZeroInteractions;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.when;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.anyList;
import android.adservices.adselection.AdBiddingOutcomeFixture;
import android.adservices.adselection.AdSelectionCallback;
import android.adservices.adselection.AdSelectionConfig;
import android.adservices.adselection.AdSelectionConfigFixture;
import android.adservices.adselection.AdSelectionInput;
import android.adservices.adselection.AdSelectionResponse;
import android.adservices.adselection.SignedContextualAds;
import android.adservices.adselection.SignedContextualAdsFixture;
import android.adservices.common.AdDataFixture;
import android.adservices.common.AdFilters;
import android.adservices.common.AdSelectionSignals;
import android.adservices.common.AdServicesStatusUtils;
import android.adservices.common.AdTechIdentifier;
import android.adservices.common.AppInstallFilters;
import android.adservices.common.CommonFixture;
import android.adservices.common.FledgeErrorResponse;
import android.adservices.customaudience.CustomAudienceFixture;
import android.adservices.exceptions.AdServicesException;
import android.content.Context;
import android.net.Uri;
import android.os.LimitExceededException;
import android.os.Process;
import android.os.RemoteException;
import android.webkit.WebView;
import androidx.room.Room;
import androidx.test.core.app.ApplicationProvider;
import com.android.adservices.common.DBAdDataFixture;
import com.android.adservices.concurrency.AdServicesExecutors;
import com.android.adservices.customaudience.DBCustomAudienceFixture;
import com.android.adservices.data.adselection.AdSelectionDatabase;
import com.android.adservices.data.adselection.AdSelectionEntryDao;
import com.android.adservices.data.adselection.DBAdSelection;
import com.android.adservices.data.adselection.DBAdSelectionEntry;
import com.android.adservices.data.adselection.DBAdSelectionHistogramInfo;
import com.android.adservices.data.adselection.DBReportingComputationInfo;
import com.android.adservices.data.adselection.datahandlers.AdSelectionInitialization;
import com.android.adservices.data.adselection.datahandlers.WinningCustomAudience;
import com.android.adservices.data.common.DBAdData;
import com.android.adservices.data.customaudience.CustomAudienceDao;
import com.android.adservices.data.customaudience.CustomAudienceDatabase;
import com.android.adservices.data.customaudience.DBCustomAudience;
import com.android.adservices.service.Flags;
import com.android.adservices.service.FlagsFactory;
import com.android.adservices.service.common.AdSelectionServiceFilter;
import com.android.adservices.service.common.AppImportanceFilter;
import com.android.adservices.service.common.FrequencyCapAdDataValidator;
import com.android.adservices.service.common.FrequencyCapAdDataValidatorNoOpImpl;
import com.android.adservices.service.common.Throttler;
import com.android.adservices.service.common.cache.CacheProviderFactory;
import com.android.adservices.service.common.httpclient.AdServicesHttpClientRequest;
import com.android.adservices.service.common.httpclient.AdServicesHttpClientResponse;
import com.android.adservices.service.common.httpclient.AdServicesHttpsClient;
import com.android.adservices.service.consent.ConsentManager;
import com.android.adservices.service.devapi.DevContext;
import com.android.adservices.service.exception.FilterException;
import com.android.adservices.service.js.JSScriptEngine;
import com.android.adservices.service.stats.AdSelectionExecutionLogger;
import com.android.adservices.service.stats.AdServicesLogger;
import com.android.adservices.service.stats.AdServicesLoggerImpl;
import com.android.adservices.service.stats.AdServicesStatsLog;
import com.android.adservices.service.stats.RunAdBiddingProcessReportedStats;
import com.android.adservices.service.stats.RunAdSelectionProcessReportedStats;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import com.google.common.collect.ImmutableList;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.Futures;
import org.junit.After;
import org.junit.Assert;
import org.junit.Assume;
import org.junit.Before;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoSession;
import org.mockito.Spy;
import org.mockito.internal.stubbing.answers.AnswersWithDelay;
import org.mockito.internal.stubbing.answers.Returns;
import org.mockito.quality.Strictness;
import java.io.File;
import java.time.Clock;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
import java.util.stream.Collectors;
/**
* This test covers strictly the unit of {@link AdSelectionRunner} The dependencies in this test are
* mocked and provide expected mock responses when invoked with desired input
*/
public class OnDeviceAdSelectionRunnerTest {
private static final String TAG = OnDeviceAdSelectionRunnerTest.class.getName();
private static final AdTechIdentifier BUYER_1 = AdSelectionConfigFixture.BUYER_1;
private static final AdTechIdentifier BUYER_2 = AdSelectionConfigFixture.BUYER_2;
private static final Long AD_SELECTION_ID = 1234L;
private static final String ERROR_INVALID_JSON = "Invalid Json Exception";
private static final int CALLER_UID = Process.myUid();
private static final String MY_APP_PACKAGE_NAME = CommonFixture.TEST_PACKAGE_NAME;
private static final AdTechIdentifier SELLER_VALID =
AdTechIdentifier.fromString("developer.android.com");
private static final String BUYER_BIDDING_LOGIC_URI_PATH = "/buyer/bidding/logic/";
private static final String DECISION_LOGIC_PATH = "/test/decisions_logic_uris";
private static final Uri DECISION_LOGIC_URI =
CommonFixture.getUri(SELLER_VALID, DECISION_LOGIC_PATH);
private static final String TRUSTED_SIGNALS_PATH = "/test/trusted_signals_uri";
private static final Uri TRUSTED_SIGNALS_URI =
CommonFixture.getUri(SELLER_VALID, TRUSTED_SIGNALS_PATH);
private static final int PERSIST_AD_SELECTION_LATENCY_MS =
(int) (PERSIST_AD_SELECTION_END_TIMESTAMP - PERSIST_AD_SELECTION_START_TIMESTAMP);
private static final String BUYER_DECISION_LOGIC_JS =
"function reportWin(ad_selection_signals, per_buyer_signals, signals_for_buyer,"
+ " contextual_signals, custom_audience_signals) { \n"
+ " return {'status': 0, 'results': {'reporting_uri': '"
+ " buyerReportingUri "
+ "' } };\n"
+ "}";
private MockitoSession mStaticMockSession = null;
@Mock private AdsScoreGenerator mMockAdsScoreGenerator;
@Mock private AdSelectionIdGenerator mMockAdSelectionIdGenerator;
@Spy private Clock mClockSpy = Clock.systemUTC();
@Mock private com.android.adservices.service.stats.Clock mAdSelectionExecutionLoggerClockMock;
@Mock private File mMockDBAdSelectionFile;
@Mock private AdFilterer mMockAdFilterer;
@Mock private AdServicesHttpsClient mMockHttpClient;
@Mock private AdCounterKeyCopier mAdCounterKeyCopierMock;
@Mock private FrequencyCapAdDataValidator mFrequencyCapAdDataValidatorMock;
@Mock private AdCounterHistogramUpdater mAdCounterHistogramUpdaterMock;
@Mock private DebugReporting mDebugReportingMock;
@Mock private DebugReportSenderStrategy mDebugReportSenderMock;
@Captor
ArgumentCaptor<RunAdSelectionProcessReportedStats>
mRunAdSelectionProcessReportedStatsArgumentCaptor;
@Captor
ArgumentCaptor<RunAdBiddingProcessReportedStats>
mRunAdBiddingProcessReportedStatsArgumentCaptor;
@Captor ArgumentCaptor<AdSelectionConfig> mAdSelectionConfigArgumentCaptor;
@Mock private PerBuyerBiddingRunner mPerBuyerBiddingRunnerMock;
private Flags mFlags = new OnDeviceAdSelectionRunnerTestFlags();
@Spy private Context mContextSpy = ApplicationProvider.getApplicationContext();
private AdServicesHttpsClient mAdServicesHttpsClient;
private ExecutorService mLightweightExecutorService;
private ExecutorService mBackgroundExecutorService;
private ScheduledThreadPoolExecutor mScheduledExecutor;
private CustomAudienceDao mCustomAudienceDao;
@Spy private AdSelectionEntryDao mAdSelectionEntryDaoSpy;
private AdServicesLogger mAdServicesLoggerMock =
ExtendedMockito.mock(AdServicesLoggerImpl.class);
private List<AdTechIdentifier> mCustomAudienceBuyers;
private AdSelectionConfig.Builder mAdSelectionConfigBuilder;
private DBCustomAudience mDBCustomAudienceForBuyer1;
private DBCustomAudience mDBCustomAudienceForBuyer2;
private List<DBCustomAudience> mBuyerCustomAudienceList;
private AdBiddingOutcome mAdBiddingOutcomeForBuyer1;
private AdBiddingOutcome mAdBiddingOutcomeForBuyer2;
private List<AdBiddingOutcome> mAdBiddingOutcomeList;
private AdScoringOutcome mAdScoringOutcomeForBuyer1;
private AdScoringOutcome mAdScoringOutcomeForBuyer2;
private List<AdScoringOutcome> mAdScoringOutcomeList;
private AdSelectionRunner mAdSelectionRunner;
private AdSelectionExecutionLogger mAdSelectionExecutionLogger;
// Use no-op implementations and test specific cases with the mocked objects
private final AdFilterer mAdFilterer = new AdFiltererNoOpImpl();
private final AdCounterKeyCopier mAdCounterKeyCopier = new AdCounterKeyCopierNoOpImpl();
private final FrequencyCapAdDataValidator mFrequencyCapAdDataValidator =
new FrequencyCapAdDataValidatorNoOpImpl();
private final AdCounterHistogramUpdater mAdCounterHistogramUpdater =
new AdCounterHistogramUpdaterNoOpImpl();
@Mock AdSelectionServiceFilter mAdSelectionServiceFilterMock;
@Before
public void setUp() {
// Every test in this class requires that the JS Sandbox be available. The JS Sandbox
// availability depends on an external component (the system webview) being higher than a
// certain minimum version. Marking that as an assumption that the test is making.
Assume.assumeTrue(JSScriptEngine.AvailabilityChecker.isJSSandboxAvailable());
// Initializing up here so object is spied
mAdSelectionEntryDaoSpy =
Room.inMemoryDatabaseBuilder(mContextSpy, AdSelectionDatabase.class)
.build()
.adSelectionEntryDao();
// Test applications don't have the required permissions to read config P/H flags, and
// injecting mocked flags everywhere is annoying and non-trivial for static methods
mStaticMockSession =
ExtendedMockito.mockitoSession()
.spyStatic(FlagsFactory.class)
.spyStatic(WebView.class)
.strictness(Strictness.LENIENT)
.initMocks(this)
.startMocking();
mLightweightExecutorService = AdServicesExecutors.getLightWeightExecutor();
mBackgroundExecutorService = AdServicesExecutors.getBackgroundExecutor();
mScheduledExecutor = AdServicesExecutors.getScheduler();
mAdServicesHttpsClient =
new AdServicesHttpsClient(
AdServicesExecutors.getBlockingExecutor(),
CacheProviderFactory.createNoOpCache());
mCustomAudienceDao =
Room.inMemoryDatabaseBuilder(mContextSpy, CustomAudienceDatabase.class)
.addTypeConverter(new DBCustomAudience.Converters(true, true))
.build()
.customAudienceDao();
mCustomAudienceBuyers = Arrays.asList(BUYER_1, BUYER_2);
mAdSelectionConfigBuilder =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setSeller(SELLER_VALID)
.setCustomAudienceBuyers(mCustomAudienceBuyers)
.setDecisionLogicUri(DECISION_LOGIC_URI)
.setTrustedScoringSignalsUri(TRUSTED_SIGNALS_URI);
mDBCustomAudienceForBuyer1 = createDBCustomAudience(BUYER_1);
mDBCustomAudienceForBuyer2 = createDBCustomAudience(BUYER_2);
mBuyerCustomAudienceList =
Arrays.asList(mDBCustomAudienceForBuyer1, mDBCustomAudienceForBuyer2);
mAdBiddingOutcomeForBuyer1 =
AdBiddingOutcomeFixture.anAdBiddingOutcomeBuilder(BUYER_1, 1.0).build();
mAdBiddingOutcomeForBuyer2 =
AdBiddingOutcomeFixture.anAdBiddingOutcomeBuilder(BUYER_2, 2.0).build();
mAdBiddingOutcomeList =
Arrays.asList(mAdBiddingOutcomeForBuyer1, mAdBiddingOutcomeForBuyer2);
mAdScoringOutcomeForBuyer1 =
AdScoringOutcomeFixture.anAdScoringBuilder(BUYER_1, 2.0).build();
mAdScoringOutcomeForBuyer2 =
AdScoringOutcomeFixture.anAdScoringBuilder(BUYER_2, 3.0).build();
mAdScoringOutcomeList =
Arrays.asList(mAdScoringOutcomeForBuyer1, mAdScoringOutcomeForBuyer2);
when(mContextSpy.getDatabasePath(DATABASE_NAME)).thenReturn(mMockDBAdSelectionFile);
when(mMockDBAdSelectionFile.length()).thenReturn(DB_AD_SELECTION_FILE_SIZE);
when(mDebugReportingMock.isEnabled()).thenReturn(false);
when(mDebugReportingMock.getSenderStrategy()).thenReturn(mDebugReportSenderMock);
doNothing()
.when(mAdSelectionServiceFilterMock)
.filterRequest(
SELLER_VALID,
MY_APP_PACKAGE_NAME,
true,
true,
CALLER_UID,
AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS,
Throttler.ApiKey.FLEDGE_API_SELECT_ADS,
DevContext.createForDevOptionsDisabled());
}
private DBCustomAudience createDBCustomAudience(final AdTechIdentifier buyer) {
return DBCustomAudienceFixture.getValidBuilderByBuyer(buyer)
.setOwner(buyer.toString() + CustomAudienceFixture.VALID_OWNER)
.setName(buyer + CustomAudienceFixture.VALID_NAME)
.setCreationTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI)
.setLastAdsAndBiddingDataUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI)
.build();
}
private DBCustomAudience createDBCustomAudienceNoFilters(final AdTechIdentifier buyer) {
return DBCustomAudienceFixture.getValidBuilderByBuyerNoFilters(buyer)
.setOwner(buyer.toString() + CustomAudienceFixture.VALID_OWNER)
.setName(buyer + CustomAudienceFixture.VALID_NAME)
.setCreationTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI)
.setLastAdsAndBiddingDataUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI)
.build();
}
@Test
public void testRunAdSelectionSuccess() throws AdServicesException {
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
Instant adSelectionCreationTs = Clock.systemUTC().instant().truncatedTo(ChronoUnit.MILLIS);
when(mClockSpy.instant()).thenReturn(adSelectionCreationTs);
DBAdSelectionEntry expectedAdSelectionResult =
new DBAdSelectionEntry.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setBiddingLogicUri(mDBCustomAudienceForBuyer2.getBiddingLogicUri())
.setWinningAdBid(
mAdScoringOutcomeForBuyer2.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer2.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer2
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBuyerDecisionLogicJs(
mAdBiddingOutcomeForBuyer1
.getCustomAudienceBiddingInfo()
.getBuyerDecisionLogicJs())
// TODO(b/230569187) add contextual signals once supported in the main logic
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.setCreationTimestamp(adSelectionCreationTs)
.build();
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(mAdBiddingOutcomeList, adSelectionConfig);
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedAdSelectionResult.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedAdSelectionResult.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
assertEquals(
expectedAdSelectionResult,
mAdSelectionEntryDaoSpy.getAdSelectionEntityById(AD_SELECTION_ID));
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForSuccessfulAdSelectionProcess();
verifyDebugReportingWasNotCalled();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionSuccessWithShouldUseUnifiedTablesFlag()
throws AdServicesException {
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
// Init runner with unified tables boolean true
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
true);
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
Instant adSelectionCreationTs = Clock.systemUTC().instant().truncatedTo(ChronoUnit.MILLIS);
when(mClockSpy.instant()).thenReturn(adSelectionCreationTs);
DBReportingComputationInfo expectedDBComputationInfo =
DBReportingComputationInfo.builder()
.setAdSelectionId(AD_SELECTION_ID)
.setBiddingLogicUri(mDBCustomAudienceForBuyer2.getBiddingLogicUri())
.setWinningAdBid(
mAdScoringOutcomeForBuyer2.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer2.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer2
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBuyerDecisionLogicJs(
mAdBiddingOutcomeForBuyer1
.getCustomAudienceBiddingInfo()
.getBuyerDecisionLogicJs())
// TODO(b/230569187) add contextual signals once supported in the main logic
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.build();
AdSelectionInitialization expectedAdSelectionInitialization =
AdSelectionInitialization.builder()
.setCreationInstant(adSelectionCreationTs)
.setCallerPackageName(MY_APP_PACKAGE_NAME)
.setSeller(SELLER_VALID)
.build();
WinningCustomAudience expectedWinningCustomAudience =
WinningCustomAudience.builder()
.setName(mAdScoringOutcomeForBuyer2.getCustomAudienceSignals().getName())
.setOwner(mAdScoringOutcomeForBuyer2.getCustomAudienceSignals().getOwner())
.build();
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(mAdBiddingOutcomeList, adSelectionConfig);
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedDBComputationInfo.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedDBComputationInfo.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesReportingComputationInfoExist(AD_SELECTION_ID));
verify(mAdSelectionEntryDaoSpy).insertDBReportingComputationInfo(expectedDBComputationInfo);
verify(mAdSelectionEntryDaoSpy)
.persistAdSelectionInitialization(
AD_SELECTION_ID, expectedAdSelectionInitialization);
verify(mAdSelectionEntryDaoSpy)
.persistAdSelectionResultForCustomAudience(anyLong(), any(), any(), any());
assertEquals(
expectedDBComputationInfo,
mAdSelectionEntryDaoSpy.getReportingComputationInfoById((AD_SELECTION_ID)));
assertEquals(
expectedAdSelectionInitialization,
mAdSelectionEntryDaoSpy.getAdSelectionInitializationForId(AD_SELECTION_ID));
assertEquals(
expectedWinningCustomAudience,
mAdSelectionEntryDaoSpy.getWinningCustomAudienceDataForId(AD_SELECTION_ID));
// Verify old dao methods were not called
verify(mAdSelectionEntryDaoSpy, never()).persistAdSelection(any());
verify(mAdSelectionEntryDaoSpy, never()).persistBuyerDecisionLogic(any());
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
assertNull(mAdSelectionEntryDaoSpy.getAdSelectionEntityById(AD_SELECTION_ID));
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForSuccessfulAdSelectionProcess();
verifyDebugReportingWasNotCalled();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionSuccessWithBuyerContextualSignals() throws AdServicesException {
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
AdCost adCost1 = new AdCost(1.5, NUM_BITS_STOCHASTIC_ROUNDING);
BuyerContextualSignals buyerContextualSignals1 =
BuyerContextualSignals.builder()
.setAdCost(adCost1)
.setDataVersion(DATA_VERSION_1)
.build();
AdCost adCost2 = new AdCost(3.0, NUM_BITS_STOCHASTIC_ROUNDING);
BuyerContextualSignals buyerContextualSignals2 =
BuyerContextualSignals.builder()
.setAdCost(adCost2)
.setDataVersion(DATA_VERSION_2)
.build();
mAdBiddingOutcomeForBuyer1 =
AdBiddingOutcomeFixture.anAdBiddingOutcomeBuilderWithBuyerContextualSignals(
BUYER_1, 1.0, buyerContextualSignals1)
.build();
mAdBiddingOutcomeForBuyer2 =
AdBiddingOutcomeFixture.anAdBiddingOutcomeBuilderWithBuyerContextualSignals(
BUYER_2, 2.0, buyerContextualSignals2)
.build();
mAdBiddingOutcomeList =
Arrays.asList(mAdBiddingOutcomeForBuyer1, mAdBiddingOutcomeForBuyer2);
mAdScoringOutcomeForBuyer1 =
AdScoringOutcomeFixture.anAdScoringBuilderWithBuyerContextualSignals(
BUYER_1, 2.0, buyerContextualSignals1)
.build();
mAdScoringOutcomeForBuyer2 =
AdScoringOutcomeFixture.anAdScoringBuilderWithBuyerContextualSignals(
BUYER_2, 3.0, buyerContextualSignals2)
.build();
mAdScoringOutcomeList =
Arrays.asList(mAdScoringOutcomeForBuyer1, mAdScoringOutcomeForBuyer2);
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
Instant adSelectionCreationTs = Clock.systemUTC().instant().truncatedTo(ChronoUnit.MILLIS);
when(mClockSpy.instant()).thenReturn(adSelectionCreationTs);
DBAdSelectionEntry expectedAdSelectionResult =
new DBAdSelectionEntry.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setBiddingLogicUri(mDBCustomAudienceForBuyer2.getBiddingLogicUri())
.setWinningAdBid(
mAdScoringOutcomeForBuyer2.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer2.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer2
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBuyerDecisionLogicJs(
mAdBiddingOutcomeForBuyer1
.getCustomAudienceBiddingInfo()
.getBuyerDecisionLogicJs())
// TODO(b/230569187) add contextual signals once supported in the main logic
.setBuyerContextualSignals(buyerContextualSignals2.toString())
.setSellerContextualSignals("{}")
.setCreationTimestamp(adSelectionCreationTs)
.build();
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(mAdBiddingOutcomeList, adSelectionConfig);
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedAdSelectionResult.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedAdSelectionResult.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
assertEquals(
expectedAdSelectionResult,
mAdSelectionEntryDaoSpy.getAdSelectionEntityById(AD_SELECTION_ID));
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForSuccessfulAdSelectionProcess();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionSuccessWithSellerDataVersionHeader() throws AdServicesException {
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
mAdBiddingOutcomeForBuyer1 =
AdBiddingOutcomeFixture.anAdBiddingOutcomeBuilder(BUYER_1, 1.0).build();
mAdBiddingOutcomeForBuyer2 =
AdBiddingOutcomeFixture.anAdBiddingOutcomeBuilder(BUYER_2, 2.0).build();
mAdBiddingOutcomeList =
Arrays.asList(mAdBiddingOutcomeForBuyer1, mAdBiddingOutcomeForBuyer2);
SellerContextualSignals sellerContextualSignals =
SellerContextualSignals.builder().setDataVersion(DATA_VERSION_1).build();
mAdScoringOutcomeForBuyer1 =
AdScoringOutcomeFixture.anAdScoringBuilderWithSellerContextualSignals(
BUYER_1, 2.0, sellerContextualSignals)
.build();
mAdScoringOutcomeForBuyer2 =
AdScoringOutcomeFixture.anAdScoringBuilderWithSellerContextualSignals(
BUYER_2, 3.0, sellerContextualSignals)
.build();
mAdScoringOutcomeList =
Arrays.asList(mAdScoringOutcomeForBuyer1, mAdScoringOutcomeForBuyer2);
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
Instant adSelectionCreationTs = Clock.systemUTC().instant().truncatedTo(ChronoUnit.MILLIS);
when(mClockSpy.instant()).thenReturn(adSelectionCreationTs);
DBAdSelectionEntry expectedAdSelectionResult =
new DBAdSelectionEntry.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setBiddingLogicUri(mDBCustomAudienceForBuyer2.getBiddingLogicUri())
.setWinningAdBid(
mAdScoringOutcomeForBuyer2.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer2.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer2
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBuyerDecisionLogicJs(
mAdBiddingOutcomeForBuyer1
.getCustomAudienceBiddingInfo()
.getBuyerDecisionLogicJs())
// TODO(b/230569187) add contextual signals once supported in the main logic
.setBuyerContextualSignals("{}")
.setSellerContextualSignals(sellerContextualSignals.toString())
.setCreationTimestamp(adSelectionCreationTs)
.build();
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(mAdBiddingOutcomeList, adSelectionConfig);
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedAdSelectionResult.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedAdSelectionResult.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
assertEquals(
expectedAdSelectionResult,
mAdSelectionEntryDaoSpy.getAdSelectionEntityById(AD_SELECTION_ID));
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForSuccessfulAdSelectionProcess();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionSuccessFilteringDisabled() throws AdServicesException {
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
Flags flagsWithFilteringDisabled =
new OnDeviceAdSelectionRunnerTestFlags() {
@Override
public long getAdSelectionOverallTimeoutMs() {
return 300;
}
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return true;
}
@Override
public boolean getFledgeAdSelectionFilteringEnabled() {
return false;
}
};
doReturn(flagsWithFilteringDisabled).when(FlagsFactory::getFlags);
DBCustomAudience dbCustomAudienceNoFilterBuyer1 = createDBCustomAudienceNoFilters(BUYER_1);
DBCustomAudience dbCustomAudienceNoFilterBuyer2 = createDBCustomAudienceNoFilters(BUYER_2);
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer1)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
Collections.singletonList(dbCustomAudienceNoFilterBuyer1),
flagsWithFilteringDisabled.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer2)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
Collections.singletonList(dbCustomAudienceNoFilterBuyer2),
flagsWithFilteringDisabled.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
when(mMockAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, adSelectionConfig))
.thenReturn((FluentFuture.from(Futures.immediateFuture(mAdScoringOutcomeList))));
mockAdSelectionExecutionLoggerSpyWithSuccessAdSelection();
when(mMockAdSelectionIdGenerator.generateId()).thenReturn(AD_SELECTION_ID);
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
flagsWithFilteringDisabled,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
new AdFiltererNoOpImpl(),
new AdCounterKeyCopierNoOpImpl(),
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
dbCustomAudienceNoFilterBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
dbCustomAudienceNoFilterBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
Instant adSelectionCreationTs = Clock.systemUTC().instant().truncatedTo(ChronoUnit.MILLIS);
when(mClockSpy.instant()).thenReturn(adSelectionCreationTs);
DBAdSelectionEntry expectedAdSelectionResult =
new DBAdSelectionEntry.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setBiddingLogicUri(dbCustomAudienceNoFilterBuyer2.getBiddingLogicUri())
.setWinningAdBid(
mAdScoringOutcomeForBuyer2.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer2.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer2
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBuyerDecisionLogicJs(
mAdBiddingOutcomeForBuyer1
.getCustomAudienceBiddingInfo()
.getBuyerDecisionLogicJs())
// TODO(b/230569187) add contextual signals once supported in the main logic
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.setCreationTimestamp(adSelectionCreationTs)
.build();
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
Collections.singletonList(dbCustomAudienceNoFilterBuyer1),
flagsWithFilteringDisabled.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
Collections.singletonList(dbCustomAudienceNoFilterBuyer2),
flagsWithFilteringDisabled.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(mAdBiddingOutcomeList, adSelectionConfig);
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedAdSelectionResult.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedAdSelectionResult.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
assertEquals(
expectedAdSelectionResult,
mAdSelectionEntryDaoSpy.getAdSelectionEntityById(AD_SELECTION_ID));
DBAdSelectionHistogramInfo histogramInfo =
mAdSelectionEntryDaoSpy.getAdSelectionHistogramInfoInOnDeviceTable(
resultsCallback.mAdSelectionResponse.getAdSelectionId(),
MY_APP_PACKAGE_NAME);
assertThat(histogramInfo).isNotNull();
assertThat(histogramInfo.getAdCounterKeys()).isNull();
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForSuccessfulAdSelectionProcess();
verifyDebugReportingWasNotCalled();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionRetriesAdSelectionIdGenerationAfterCollision()
throws AdServicesException {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
long existingAdSelectionId = 2345L;
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer1)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer2)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
when(mMockAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, adSelectionConfig))
.thenReturn((FluentFuture.from(Futures.immediateFuture(mAdScoringOutcomeList))));
Instant adSelectionCreationTs = Clock.systemUTC().instant().truncatedTo(ChronoUnit.MILLIS);
when(mClockSpy.instant()).thenReturn(adSelectionCreationTs);
DBAdSelectionEntry expectedAdSelectionResult =
new DBAdSelectionEntry.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setBiddingLogicUri(mDBCustomAudienceForBuyer2.getBiddingLogicUri())
.setWinningAdBid(
mAdScoringOutcomeForBuyer2.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer2.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer2
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBuyerDecisionLogicJs(
mAdBiddingOutcomeForBuyer1
.getCustomAudienceBiddingInfo()
.getBuyerDecisionLogicJs())
// TODO(b/230569187) add contextual signals once supported in the main logic
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.setCreationTimestamp(adSelectionCreationTs)
.build();
DBAdSelection existingAdSelection =
new DBAdSelection.Builder()
.setAdSelectionId(existingAdSelectionId)
.setWinningAdBid(
mAdScoringOutcomeForBuyer2.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer2.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer2
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
// TODO(b/230569187) add contextual signals once supported in the main logic
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.setCreationTimestamp(adSelectionCreationTs)
.setCallerPackageName(MY_APP_PACKAGE_NAME)
.setBiddingLogicUri(mAdScoringOutcomeForBuyer2.getBiddingLogicUri())
.build();
// Persist existing ad selection entry with existingAdSelectionId
mAdSelectionEntryDaoSpy.persistAdSelection(existingAdSelection);
mockAdSelectionExecutionLoggerSpyWithSuccessAdSelection();
// Mock generator to return a collision on the first generation
when(mMockAdSelectionIdGenerator.generateId())
.thenReturn(existingAdSelectionId, existingAdSelectionId, AD_SELECTION_ID);
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(existingAdSelectionId));
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(mAdBiddingOutcomeList, adSelectionConfig);
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedAdSelectionResult.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedAdSelectionResult.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(existingAdSelectionId));
assertEquals(
expectedAdSelectionResult,
mAdSelectionEntryDaoSpy.getAdSelectionEntityById(AD_SELECTION_ID));
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForSuccessfulAdSelectionProcess();
verifyDebugReportingWasNotCalled();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionWithRevokedUserConsentSuccess() throws AdServicesException {
doReturn(mFlags).when(FlagsFactory::getFlags);
doThrow(new FilterException(new ConsentManager.RevokedConsentException()))
.when(mAdSelectionServiceFilterMock)
.filterRequest(
SELLER_VALID,
MY_APP_PACKAGE_NAME,
true,
true,
CALLER_UID,
AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS,
Throttler.ApiKey.FLEDGE_API_SELECT_ADS,
DevContext.createForDevOptionsDisabled());
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
when(mMockAdSelectionIdGenerator.generateId()).thenReturn(AD_SELECTION_ID);
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByValidateRequest();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock, never()).runBidding(any(), any(), anyLong(), any());
verify(mMockAdsScoreGenerator, never()).runAdScoring(any(), any());
assertTrue(resultsCallback.mIsSuccess);
assertFalse(
mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(
resultsCallback.mAdSelectionResponse.getAdSelectionId()));
assertEquals(Uri.EMPTY, resultsCallback.mAdSelectionResponse.getRenderUri());
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
verifyLogForFailurePriorPersistAdSelection(STATUS_USER_CONSENT_REVOKED);
// Confirm a duplicate log entry does not exist.
// AdSelectionServiceFilter ensures the failing assertion is logged internally.
verify(mAdServicesLoggerMock, never())
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_USER_CONSENT_REVOKED),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionMissingBuyerSignals() throws AdServicesException {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
// Creating ad selection config with missing Buyer signals to test the fallback
AdSelectionConfig adSelectionConfig =
mAdSelectionConfigBuilder.setPerBuyerSignals(Collections.EMPTY_MAP).build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer1)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer2)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
when(mMockAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, adSelectionConfig))
.thenReturn((FluentFuture.from(Futures.immediateFuture(mAdScoringOutcomeList))));
DBAdSelection expectedDBAdSelectionResult =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCreationTimestamp(Calendar.getInstance().toInstant())
.setWinningAdBid(
mAdScoringOutcomeForBuyer2.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer2.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer2
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBiddingLogicUri(mAdScoringOutcomeForBuyer2.getBiddingLogicUri())
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.setCallerPackageName(MY_APP_PACKAGE_NAME)
.build();
when(mMockAdSelectionIdGenerator.generateId()).thenReturn(AD_SELECTION_ID);
mockAdSelectionExecutionLoggerSpyWithSuccessAdSelection();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(mAdBiddingOutcomeList, adSelectionConfig);
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedDBAdSelectionResult.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedDBAdSelectionResult.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForSuccessfulAdSelectionProcess();
verifyDebugReportingWasNotCalled();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionNoCAs() {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
// Do not populate CustomAudience DAO
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByNoCAs();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
assertFalse(resultsCallback.mIsSuccess);
verifyErrorMessageIsCorrect(
resultsCallback.mFledgeErrorResponse.getErrorMessage(),
ERROR_NO_CA_AND_CONTEXTUAL_ADS_AVAILABLE);
verifyLogForFailedBiddingStageDuringFetchBuyersCustomAudience(STATUS_INTERNAL_ERROR);
verifyLogForFailurePriorPersistAdSelection(STATUS_INTERNAL_ERROR);
// If there are no corresponding CAs we should not even attempt bidding
verifyZeroInteractions(mPerBuyerBiddingRunnerMock);
// If there was no bidding then we should not even attempt to run scoring
verifyZeroInteractions(mMockAdsScoreGenerator);
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_INTERNAL_ERROR),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionCallerNotInForeground_fails() {
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
doThrow(
new FilterException(
new AppImportanceFilter.WrongCallingApplicationStateException()))
.when(mAdSelectionServiceFilterMock)
.filterRequest(
SELLER_VALID,
MY_APP_PACKAGE_NAME,
true,
true,
CALLER_UID,
AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS,
Throttler.ApiKey.FLEDGE_API_SELECT_ADS,
DevContext.createForDevOptionsDisabled());
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByValidateRequest();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
assertFalse(resultsCallback.mIsSuccess);
verifyErrorMessageIsCorrect(
resultsCallback.mFledgeErrorResponse.getErrorMessage(),
ILLEGAL_STATE_BACKGROUND_CALLER_ERROR_MESSAGE);
verifyLogForFailurePriorPersistAdSelection(STATUS_BACKGROUND_CALLER);
// Confirm a duplicate log entry does not exist.
// AdSelectionServiceFilter ensures the failing assertion is logged internally.
verify(mAdServicesLoggerMock, never())
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_BACKGROUND_CALLER),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionCallerNotInForegroundFlagDisabled_doesNotFailValidation() {
Flags flags =
new OnDeviceAdSelectionRunnerTestFlags() {
@Override
public boolean getEnforceForegroundStatusForFledgeRunAdSelection() {
return false;
}
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return true;
}
};
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByNoCAs();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
flags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
// This ad selection fails because there are no CAs but the foreground status validation
// is not blocking the rest of the process
assertFalse(resultsCallback.mIsSuccess);
verifyErrorMessageIsCorrect(
resultsCallback.mFledgeErrorResponse.getErrorMessage(),
ERROR_NO_CA_AND_CONTEXTUAL_ADS_AVAILABLE);
verifyLogForFailedBiddingStageDuringFetchBuyersCustomAudience(STATUS_INTERNAL_ERROR);
verifyLogForFailurePriorPersistAdSelection(STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_INTERNAL_ERROR),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionWithValidSubdomainsSuccess() throws AdServicesException {
AdSelectionConfig adSelectionConfigWithValidSubdomains =
mAdSelectionConfigBuilder
.setDecisionLogicUri(
CommonFixture.getUriWithValidSubdomain(
SELLER_VALID.toString(), DECISION_LOGIC_PATH))
.setTrustedScoringSignalsUri(
CommonFixture.getUriWithValidSubdomain(
SELLER_VALID.toString(), TRUSTED_SIGNALS_PATH))
.build();
verifyAndSetupCommonSuccessScenario(adSelectionConfigWithValidSubdomains);
AdSelectionRunner runner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
Instant adSelectionCreationTs = Clock.systemUTC().instant().truncatedTo(ChronoUnit.MILLIS);
when(mClockSpy.instant()).thenReturn(adSelectionCreationTs);
DBAdSelectionEntry expectedAdSelectionResult =
new DBAdSelectionEntry.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setBiddingLogicUri(mDBCustomAudienceForBuyer2.getBiddingLogicUri())
.setWinningAdBid(
mAdScoringOutcomeForBuyer2.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer2.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer2
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBuyerDecisionLogicJs(
mAdBiddingOutcomeForBuyer1
.getCustomAudienceBiddingInfo()
.getBuyerDecisionLogicJs())
// TODO(b/230569187) add contextual signals once supported in the main logic
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.setCreationTimestamp(adSelectionCreationTs)
.build();
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(
runner, adSelectionConfigWithValidSubdomains, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfigWithValidSubdomains);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfigWithValidSubdomains);
verify(mMockAdsScoreGenerator)
.runAdScoring(mAdBiddingOutcomeList, adSelectionConfigWithValidSubdomains);
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedAdSelectionResult.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedAdSelectionResult.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
assertEquals(
expectedAdSelectionResult,
mAdSelectionEntryDaoSpy.getAdSelectionEntityById(AD_SELECTION_ID));
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForSuccessfulAdSelectionProcess();
verifyDebugReportingWasNotCalled();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionPartialBidding() throws AdServicesException {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
// In this case assuming bidding fails for one of ads and return partial result
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer1)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(null)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
// In this case we are only expected to get score for the first bidding,
// as second one is null
List<AdBiddingOutcome> partialBiddingOutcome = Arrays.asList(mAdBiddingOutcomeForBuyer1);
when(mMockAdsScoreGenerator.runAdScoring(partialBiddingOutcome, adSelectionConfig))
.thenReturn(
(FluentFuture.from(
Futures.immediateFuture(mAdScoringOutcomeList.subList(0, 1)))));
when(mMockAdSelectionIdGenerator.generateId()).thenReturn(AD_SELECTION_ID);
mockAdSelectionExecutionLoggerSpyWithSuccessAdSelection();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
DBAdSelection expectedDBAdSelectionResult =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCreationTimestamp(Calendar.getInstance().toInstant())
.setWinningAdBid(
mAdScoringOutcomeForBuyer1.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer1.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer1
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBiddingLogicUri(mAdScoringOutcomeForBuyer1.getBiddingLogicUri())
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.setCallerPackageName(MY_APP_PACKAGE_NAME)
.build();
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(partialBiddingOutcome, adSelectionConfig);
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedDBAdSelectionResult.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedDBAdSelectionResult.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
verifyLogForSuccessfulBiddingProcess(partialBiddingOutcome);
verifyLogForSuccessfulAdSelectionProcess();
verifyDebugReportingWasNotCalled();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionBiddingFailure() {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
// In this case assuming bidding fails and returns null
doReturn(ImmutableList.of(Futures.immediateFuture(null)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(null)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByNoBiddingOutcomes();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
// If the result of bidding is empty, then we should not even attempt to run scoring
verifyZeroInteractions(mMockAdsScoreGenerator);
assertFalse(resultsCallback.mIsSuccess);
verifyErrorMessageIsCorrect(
resultsCallback.mFledgeErrorResponse.getErrorMessage(),
ERROR_NO_VALID_BIDS_OR_CONTEXTUAL_ADS_FOR_SCORING);
verifyLogForSuccessfulBiddingProcess(Arrays.asList(null, null));
verifyLogForFailurePriorPersistAdSelection(STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_INTERNAL_ERROR),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionScoringFailure() throws AdServicesException {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer1)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer2)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
// In this case assuming we get an empty result
when(mMockAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, adSelectionConfig))
.thenReturn((FluentFuture.from(Futures.immediateFuture(Collections.EMPTY_LIST))));
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionDuringScoring();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(mAdBiddingOutcomeList, adSelectionConfig);
assertFalse(resultsCallback.mIsSuccess);
verifyErrorMessageIsCorrect(
resultsCallback.mFledgeErrorResponse.getErrorMessage(), ERROR_NO_WINNING_AD_FOUND);
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForFailurePriorPersistAdSelection(STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_INTERNAL_ERROR),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionNegativeScoring() throws AdServicesException {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer1)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer2)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
AdScoringOutcome adScoringNegativeOutcomeForBuyer1 =
AdScoringOutcomeFixture.anAdScoringBuilder(BUYER_1, -2.0).build();
AdScoringOutcome adScoringNegativeOutcomeForBuyer2 =
AdScoringOutcomeFixture.anAdScoringBuilder(BUYER_2, -3.0).build();
List<AdScoringOutcome> negativeScoreOutcome =
Arrays.asList(adScoringNegativeOutcomeForBuyer1, adScoringNegativeOutcomeForBuyer2);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
// In this case assuming we get a result with negative scores
when(mMockAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, adSelectionConfig))
.thenReturn((FluentFuture.from(Futures.immediateFuture(negativeScoreOutcome))));
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionDuringScoring();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(mAdBiddingOutcomeList, adSelectionConfig);
assertFalse(resultsCallback.mIsSuccess);
verifyErrorMessageIsCorrect(
resultsCallback.mFledgeErrorResponse.getErrorMessage(), ERROR_NO_WINNING_AD_FOUND);
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForFailurePriorPersistAdSelection(STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_INTERNAL_ERROR),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionPartialNegativeScoring() throws AdServicesException {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer1)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer2)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
AdScoringOutcome adScoringNegativeOutcomeForBuyer1 =
AdScoringOutcomeFixture.anAdScoringBuilder(BUYER_1, 2.0).build();
AdScoringOutcome adScoringNegativeOutcomeForBuyer2 =
AdScoringOutcomeFixture.anAdScoringBuilder(BUYER_2, -3.0).build();
List<AdScoringOutcome> negativeScoreOutcome =
Arrays.asList(adScoringNegativeOutcomeForBuyer1, adScoringNegativeOutcomeForBuyer2);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
// In this case assuming we get a result with partially negative scores
when(mMockAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, adSelectionConfig))
.thenReturn((FluentFuture.from(Futures.immediateFuture(negativeScoreOutcome))));
when(mMockAdSelectionIdGenerator.generateId()).thenReturn(AD_SELECTION_ID);
mockAdSelectionExecutionLoggerSpyWithSuccessAdSelection();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(mAdBiddingOutcomeList, adSelectionConfig);
DBAdSelection expectedDBAdSelectionResult =
new DBAdSelection.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setCreationTimestamp(Calendar.getInstance().toInstant())
.setWinningAdBid(
mAdScoringOutcomeForBuyer1.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer1.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer1
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBiddingLogicUri(mAdScoringOutcomeForBuyer1.getBiddingLogicUri())
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.setCallerPackageName(MY_APP_PACKAGE_NAME)
.build();
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedDBAdSelectionResult.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedDBAdSelectionResult.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForSuccessfulAdSelectionProcess();
verifyDebugReportingWasNotCalled();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionScoringException() throws AdServicesException {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig =
mAdSelectionConfigBuilder
.setAdSelectionSignals(AdSelectionSignals.fromString("{/}"))
.build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer1)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer2)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
// In this case we expect a JSON validation exception
when(mMockAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, adSelectionConfig))
.thenThrow(new AdServicesException(ERROR_INVALID_JSON));
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionDuringScoring();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
assertFalse(resultsCallback.mIsSuccess);
verifyErrorMessageIsCorrect(
resultsCallback.mFledgeErrorResponse.getErrorMessage(), ERROR_INVALID_JSON);
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForFailurePriorPersistAdSelection(STATUS_INTERNAL_ERROR);
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_INTERNAL_ERROR),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelection_withOnDeviceAuctionDisabled_throwsIllegalStateException() {
Flags flagsWithOnDeviceAuctionDisabled =
new OnDeviceAdSelectionRunnerTestFlags() {
@Override
public boolean getFledgeOnDeviceAuctionKillSwitch() {
return true;
}
};
mAdSelectionExecutionLogger =
new AdSelectionExecutionLogger(
sCallerMetadata,
mAdSelectionExecutionLoggerClockMock,
mContextSpy,
mAdServicesLoggerMock);
AdSelectionRunner runner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
flagsWithOnDeviceAuctionDisabled,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
AdSelectionTestCallback callback =
invokeRunAdSelection(runner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verifyErrorMessageIsCorrect(
callback.mFledgeErrorResponse.getErrorMessage(),
ON_DEVICE_AUCTION_KILL_SWITCH_ENABLED);
}
@Test
public void testmMockDBAdSeleciton() throws AdServicesException {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
Flags flagsWithSmallerLimits =
new OnDeviceAdSelectionRunnerTestFlags() {
@Override
public long getAdSelectionOverallTimeoutMs() {
return 1000;
}
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return true;
}
};
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer1)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
flagsWithSmallerLimits.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer2)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
flagsWithSmallerLimits.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
when(mMockAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, adSelectionConfig))
.thenReturn((FluentFuture.from(Futures.immediateFuture(mAdScoringOutcomeList))));
Instant adSelectionCreationTs = Clock.systemUTC().instant().truncatedTo(ChronoUnit.MILLIS);
when(mClockSpy.instant()).thenReturn(adSelectionCreationTs);
when(mMockAdSelectionIdGenerator.generateId())
.thenAnswer(
new AnswersWithDelay(
2 * mFlags.getAdSelectionOverallTimeoutMs(),
new Returns(AD_SELECTION_ID)));
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionBeforePersistAdSelection();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
flagsWithSmallerLimits,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
assertFalse(resultsCallback.mIsSuccess);
FledgeErrorResponse response = resultsCallback.mFledgeErrorResponse;
verifyErrorMessageIsCorrect(response.getErrorMessage(), AD_SELECTION_TIMED_OUT);
Assert.assertEquals(
"Error response code mismatch",
AdServicesStatusUtils.STATUS_TIMEOUT,
response.getStatusCode());
verifyLogForSuccessfulBiddingProcess(mAdBiddingOutcomeList);
verifyLogForFailureByRunAdSelectionOrchestrationTimesOut(STATUS_TIMEOUT);
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_TIMEOUT),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionPerBuyerTimeout() throws AdServicesException {
Flags flagsWithSmallPerBuyerTimeout =
new OnDeviceAdSelectionRunnerTestFlags() {
@Override
public long getAdSelectionOverallTimeoutMs() {
return 5000;
}
@Override
public long getAdSelectionBiddingTimeoutPerBuyerMs() {
return 100;
}
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return true;
}
};
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(flagsWithSmallPerBuyerTimeout).when(FlagsFactory::getFlags);
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
Callable<AdBiddingOutcome> delayedBiddingOutcomeForBuyer1 =
() -> {
TimeUnit.MILLISECONDS.sleep(
10
* flagsWithSmallPerBuyerTimeout
.getAdSelectionBiddingTimeoutPerBuyerMs());
return mAdBiddingOutcomeForBuyer1;
};
doReturn(ImmutableList.of())
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
flagsWithSmallPerBuyerTimeout.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer2)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
flagsWithSmallPerBuyerTimeout.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
// bidding Outcome List should only have one bidding outcome
List<AdBiddingOutcome> adBiddingOutcomeList = ImmutableList.of(mAdBiddingOutcomeForBuyer2);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
when(mMockAdsScoreGenerator.runAdScoring(adBiddingOutcomeList, adSelectionConfig))
.thenReturn(
(FluentFuture.from(
Futures.immediateFuture(
ImmutableList.of(mAdScoringOutcomeForBuyer2)))));
Instant adSelectionCreationTs = Clock.systemUTC().instant().truncatedTo(ChronoUnit.MILLIS);
when(mClockSpy.instant()).thenReturn(adSelectionCreationTs);
DBAdSelectionEntry expectedAdSelectionResult =
new DBAdSelectionEntry.Builder()
.setAdSelectionId(AD_SELECTION_ID)
.setBiddingLogicUri(mDBCustomAudienceForBuyer2.getBiddingLogicUri())
.setWinningAdBid(
mAdScoringOutcomeForBuyer2.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer2.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer2
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBuyerDecisionLogicJs(
mAdBiddingOutcomeForBuyer1
.getCustomAudienceBiddingInfo()
.getBuyerDecisionLogicJs())
// TODO(b/230569187) add contextual signals once supported in the main logic
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.setCreationTimestamp(adSelectionCreationTs)
.build();
when(mMockAdSelectionIdGenerator.generateId()).thenReturn(AD_SELECTION_ID);
mockAdSelectionExecutionLoggerSpyWithSuccessAdSelection();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
flagsWithSmallPerBuyerTimeout,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
flagsWithSmallPerBuyerTimeout.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
flagsWithSmallPerBuyerTimeout.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdsScoreGenerator).runAdScoring(adBiddingOutcomeList, adSelectionConfig);
assertTrue(resultsCallback.mIsSuccess);
assertEquals(
expectedAdSelectionResult.getAdSelectionId(),
resultsCallback.mAdSelectionResponse.getAdSelectionId());
assertEquals(
expectedAdSelectionResult.getWinningAdRenderUri(),
resultsCallback.mAdSelectionResponse.getRenderUri());
assertTrue(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
assertEquals(
expectedAdSelectionResult,
mAdSelectionEntryDaoSpy.getAdSelectionEntityById(AD_SELECTION_ID));
verifyLogForSuccessfulBiddingProcess(adBiddingOutcomeList);
verifyLogForSuccessfulAdSelectionProcess();
verifyDebugReportingWasNotCalled();
verify(mAdServicesLoggerMock)
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_SUCCESS),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testRunAdSelectionThrottledFailure() throws AdServicesException {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
// Creating ad selection config for happy case with all the buyers in place
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
// Throttle Ad Selection request
doThrow(new FilterException(new LimitExceededException(RATE_LIMIT_REACHED_ERROR_MESSAGE)))
.when(mAdSelectionServiceFilterMock)
.filterRequest(
SELLER_VALID,
MY_APP_PACKAGE_NAME,
true,
true,
CALLER_UID,
AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS,
Throttler.ApiKey.FLEDGE_API_SELECT_ADS,
DevContext.createForDevOptionsDisabled());
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByValidateRequest();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AdSelectionTestCallback resultsCallback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
assertFalse(resultsCallback.mIsSuccess);
FledgeErrorResponse response = resultsCallback.mFledgeErrorResponse;
verifyErrorMessageIsCorrect(response.getErrorMessage(), RATE_LIMIT_REACHED_ERROR_MESSAGE);
Assert.assertEquals(
"Error response code mismatch",
STATUS_RATE_LIMIT_REACHED,
response.getStatusCode());
verifyLogForFailurePriorPersistAdSelection(STATUS_RATE_LIMIT_REACHED);
// Confirm a duplicate log entry does not exist.
// AdSelectionServiceFilter ensures the failing assertion is logged internally.
verify(mAdServicesLoggerMock, never())
.logFledgeApiCallStats(
eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS),
eq(STATUS_RATE_LIMIT_REACHED),
eq(RUN_AD_SELECTION_OVERALL_LATENCY_MS));
}
@Test
public void testFilterOneAd() throws AdServicesException {
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mMockAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
List<DBAdData> adsToNotFilter =
DBAdDataFixture.getValidDbAdDataListByBuyer(mDBCustomAudienceForBuyer1.getBuyer());
AppInstallFilters appFilters =
new AppInstallFilters.Builder()
.setPackageNames(Collections.singleton(CommonFixture.TEST_PACKAGE_NAME_1))
.build();
DBAdData adToFilter =
DBAdDataFixture.getValidDbAdDataBuilder()
.setAdFilters(
new AdFilters.Builder().setAppInstallFilters(appFilters).build())
.build();
List<DBAdData> ads = new ArrayList<>(adsToNotFilter);
ads.add(adToFilter);
DBCustomAudience caWithFilterAd =
new DBCustomAudience.Builder(mDBCustomAudienceForBuyer1).setAds(ads).build();
DBCustomAudience caWithoutFilterAd =
new DBCustomAudience.Builder(mDBCustomAudienceForBuyer1)
.setAds(adsToNotFilter)
.build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
caWithFilterAd, CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
when(mMockAdFilterer.filterCustomAudiences(
Arrays.asList(caWithFilterAd, mDBCustomAudienceForBuyer2)))
.thenReturn((Arrays.asList(caWithoutFilterAd, mDBCustomAudienceForBuyer2)));
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(caWithoutFilterAd),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mMockAdFilterer, times(1))
.filterCustomAudiences(Arrays.asList(caWithFilterAd, mDBCustomAudienceForBuyer2));
}
@Test
public void testFilterWholeCa() throws AdServicesException {
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mMockAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AppInstallFilters appFilters =
new AppInstallFilters.Builder()
.setPackageNames(Collections.singleton(CommonFixture.TEST_PACKAGE_NAME_1))
.build();
DBAdData adToFilter =
DBAdDataFixture.getValidDbAdDataBuilder()
.setAdFilters(
new AdFilters.Builder().setAppInstallFilters(appFilters).build())
.build();
List<DBAdData> ads = Collections.singletonList(adToFilter);
DBCustomAudience caWithFilterAd =
new DBCustomAudience.Builder(mDBCustomAudienceForBuyer1).setAds(ads).build();
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
caWithFilterAd, CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
when(mMockAdFilterer.filterCustomAudiences(
Arrays.asList(caWithFilterAd, mDBCustomAudienceForBuyer2)))
.thenReturn((Arrays.asList(mDBCustomAudienceForBuyer2)));
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mPerBuyerBiddingRunnerMock, times(1))
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mPerBuyerBiddingRunnerMock, times(0))
.runBidding(eq(BUYER_1), any(), anyLong(), any());
verify(mMockAdFilterer, times(1))
.filterCustomAudiences(Arrays.asList(caWithFilterAd, mDBCustomAudienceForBuyer2));
}
@Test
public void testGetDecisionLogic_PreDownloaded()
throws ExecutionException, InterruptedException, TimeoutException, AdServicesException {
mAdScoringOutcomeForBuyer1 =
AdScoringOutcomeFixture.anAdScoringBuilder(BUYER_1, 1.0)
.setBiddingLogicJsDownloaded(true)
.build();
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
OnDeviceAdSelectionRunner adSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
assertEquals(
mAdScoringOutcomeForBuyer1.getBiddingLogicJs(),
adSelectionRunner
.getWinnerBiddingLogicJs(mAdScoringOutcomeForBuyer1)
.get(500, TimeUnit.MILLISECONDS));
}
@Test
public void testGetDecisionLogic_NotPreDownloaded()
throws ExecutionException, InterruptedException, TimeoutException, AdServicesException {
mAdScoringOutcomeForBuyer1 =
AdScoringOutcomeFixture.anAdScoringBuilder(BUYER_1, 1.0)
.setBiddingLogicUri(DECISION_LOGIC_URI)
.setBiddingLogicJsDownloaded(false)
.build();
when(mMockHttpClient.fetchPayload(
AdServicesHttpClientRequest.builder()
.setUri(DECISION_LOGIC_URI)
.setUseCache(mFlags.getFledgeHttpJsCachingEnabled())
.setDevContext(DevContext.createForDevOptionsDisabled())
.build()))
.thenReturn(
Futures.immediateFuture(
AdServicesHttpClientResponse.builder()
.setResponseBody(BUYER_DECISION_LOGIC_JS)
.build()));
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
OnDeviceAdSelectionRunner adSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mMockHttpClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
String downloadingDecisionLogic =
adSelectionRunner
.getWinnerBiddingLogicJs(mAdScoringOutcomeForBuyer1)
.get(500, TimeUnit.MILLISECONDS);
assertEquals("", downloadingDecisionLogic);
verify(mMockHttpClient, times(0))
.fetchPayload(
AdServicesHttpClientRequest.builder()
.setUri(DECISION_LOGIC_URI)
.setUseCache(true)
.setDevContext(DevContext.createForDevOptionsDisabled())
.build());
}
@Test
public void testCreateAdSelectionResult_Contextual_Enabled() throws AdServicesException {
Map<AdTechIdentifier, SignedContextualAds> signedContextualAdsMap = createContextualAds();
AdSelectionConfig adSelectionConfig =
mAdSelectionConfigBuilder
.build()
.cloneToBuilder()
.setCustomAudienceBuyers(Collections.EMPTY_LIST)
.setBuyerSignedContextualAds(signedContextualAdsMap)
.build();
final Flags flags =
new OnDeviceAdSelectionRunnerTestFlags() {
@Override
public long getAdSelectionOverallTimeoutMs() {
return 300;
}
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return true;
}
@Override
public boolean getFledgeAdSelectionFilteringEnabled() {
return true;
}
@Override
public boolean getFledgeAdSelectionContextualAdsEnabled() {
return true;
}
};
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByNoBiddingOutcomes();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
flags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mMockAdsScoreGenerator)
.runAdScoring(
eq(Collections.EMPTY_LIST), mAdSelectionConfigArgumentCaptor.capture());
assertEquals(
"The contextual ads should have reached scoring as is",
signedContextualAdsMap,
mAdSelectionConfigArgumentCaptor.getValue().getBuyerSignedContextualAds());
}
@Test
public void testCreateAdSelectionResult_Contextual_DisabledAndSkipped() {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
AdSelectionConfig adSelectionConfig =
mAdSelectionConfigBuilder
.build()
.cloneToBuilder()
.setCustomAudienceBuyers(Collections.EMPTY_LIST)
// Despite populating Contextual Ads, they will be removed
.setBuyerSignedContextualAds(createContextualAds())
.build();
final Flags flags =
new OnDeviceAdSelectionRunnerTestFlags() {
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return true;
}
@Override
public boolean getFledgeAdSelectionFilteringEnabled() {
return true;
}
@Override
public boolean getFledgeAdSelectionContextualAdsEnabled() {
return false;
}
};
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByNoCAs();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
flags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
AdSelectionTestCallback result =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
assertFalse(result.mIsSuccess);
assertEquals(
"Contextual Ads should have been flushed and Ad Selection resulted in error",
result.mFledgeErrorResponse.getErrorMessage(),
String.format(
AD_SELECTION_ERROR_PATTERN,
ERROR_AD_SELECTION_FAILURE,
ERROR_NO_BUYERS_OR_CONTEXTUAL_ADS_AVAILABLE));
}
@Test
public void testCreateAdSelectionResult_Contextual_AppInstallFiltered()
throws AdServicesException {
Map<AdTechIdentifier, SignedContextualAds> contextualAdsMap = createContextualAds();
AdSelectionConfig adSelectionConfig =
mAdSelectionConfigBuilder
.build()
.cloneToBuilder()
.setCustomAudienceBuyers(Collections.EMPTY_LIST)
.setBuyerSignedContextualAds(contextualAdsMap)
.build();
final Flags flags =
new OnDeviceAdSelectionRunnerTestFlags() {
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return true;
}
@Override
public boolean getFledgeAdSelectionFilteringEnabled() {
return true;
}
@Override
public boolean getFledgeAdSelectionContextualAdsEnabled() {
return true;
}
};
mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByNoCAs();
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
flags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mMockAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
when(mMockAdFilterer.filterContextualAds(contextualAdsMap.get(CommonFixture.VALID_BUYER_1)))
.thenReturn(contextualAdsMap.get(CommonFixture.VALID_BUYER_1));
when(mMockAdFilterer.filterContextualAds(contextualAdsMap.get(CommonFixture.VALID_BUYER_2)))
.thenReturn(
SignedContextualAdsFixture.aSignedContextualAdBuilder()
.setBuyer(CommonFixture.VALID_BUYER_2)
.setDecisionLogicUri(
contextualAdsMap
.get(CommonFixture.VALID_BUYER_2)
.getDecisionLogicUri())
.setAdsWithBid(Collections.EMPTY_LIST)
.build());
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
verify(mMockAdsScoreGenerator)
.runAdScoring(
eq(Collections.EMPTY_LIST), mAdSelectionConfigArgumentCaptor.capture());
assertEquals(
"The contextual ads should have remained same for Buyer 1",
contextualAdsMap.get(CommonFixture.VALID_BUYER_1).getAdsWithBid(),
mAdSelectionConfigArgumentCaptor
.getValue()
.getBuyerSignedContextualAds()
.get(CommonFixture.VALID_BUYER_1)
.getAdsWithBid());
assertEquals(
"The contextual ads should have been filtered for Buyer 2",
Collections.EMPTY_LIST,
mAdSelectionConfigArgumentCaptor
.getValue()
.getBuyerSignedContextualAds()
.get(CommonFixture.VALID_BUYER_2)
.getAdsWithBid());
}
@Test
public void testCopiedAdCounterKeysArePersisted() throws AdServicesException {
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopierMock,
mAdCounterHistogramUpdater,
mFrequencyCapAdDataValidatorMock,
mDebugReportingMock,
false);
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
DBAdSelection.Builder dbAdSelectionBuilder =
new DBAdSelection.Builder()
.setWinningAdBid(
mAdScoringOutcomeForBuyer1.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer1.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer1
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBiddingLogicUri(mAdScoringOutcomeForBuyer1.getBiddingLogicUri())
.setBuyerContextualSignals("{}")
.setSellerContextualSignals("{}")
.setAdCounterIntKeys(AdDataFixture.getAdCounterKeys());
doReturn(dbAdSelectionBuilder)
.when(mAdCounterKeyCopierMock)
.copyAdCounterKeys(any(DBAdSelection.Builder.class), any(AdScoringOutcome.class));
AdSelectionTestCallback callback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
assertThat(callback.mIsSuccess).isTrue();
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mAdCounterKeyCopierMock)
.copyAdCounterKeys(any(DBAdSelection.Builder.class), any(AdScoringOutcome.class));
assertTrue(
mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(
callback.mAdSelectionResponse.getAdSelectionId()));
DBAdSelectionHistogramInfo histogramInfo =
mAdSelectionEntryDaoSpy.getAdSelectionHistogramInfoInOnDeviceTable(
callback.mAdSelectionResponse.getAdSelectionId(), MY_APP_PACKAGE_NAME);
assertThat(histogramInfo).isNotNull();
assertThat(histogramInfo.getBuyer()).isEqualTo(BUYER_1);
assertThat(histogramInfo.getAdCounterKeys()).isNotNull();
assertThat(histogramInfo.getAdCounterKeys())
.containsExactlyElementsIn(AdDataFixture.getAdCounterKeys());
}
@Test
public void testAdCounterHistogramIsUpdatedWithWinEvent() throws Exception {
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopierMock,
mAdCounterHistogramUpdaterMock,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
DBAdSelection.Builder dbAdSelectionBuilder =
new DBAdSelection.Builder()
.setWinningAdBid(
mAdScoringOutcomeForBuyer1.getAdWithScore().getAdWithBid().getBid())
.setCustomAudienceSignals(
mAdScoringOutcomeForBuyer1.getCustomAudienceSignals())
.setWinningAdRenderUri(
mAdScoringOutcomeForBuyer1
.getAdWithScore()
.getAdWithBid()
.getAdData()
.getRenderUri())
.setBiddingLogicUri(mAdScoringOutcomeForBuyer1.getBiddingLogicUri())
.setBuyerContextualSignals("{}")
.setAdCounterIntKeys(AdDataFixture.getAdCounterKeys());
// Note that regardless of the input, this copier stubs the actual output of the auction
doReturn(dbAdSelectionBuilder)
.when(mAdCounterKeyCopierMock)
.copyAdCounterKeys(any(DBAdSelection.Builder.class), any(AdScoringOutcome.class));
AdSelectionTestCallback callback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
assertThat(callback.mIsSuccess).isTrue();
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mAdCounterKeyCopierMock)
.copyAdCounterKeys(any(DBAdSelection.Builder.class), any(AdScoringOutcome.class));
verify(mAdCounterHistogramUpdaterMock).updateWinHistogram(eq(dbAdSelectionBuilder.build()));
assertTrue(
mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(
callback.mAdSelectionResponse.getAdSelectionId()));
}
@Test
public void testFailedAdCounterHistogramWinUpdateDoesNotStopAdSelection() throws Exception {
AdSelectionConfig adSelectionConfig = mAdSelectionConfigBuilder.build();
verifyAndSetupCommonSuccessScenario(adSelectionConfig);
mAdSelectionRunner =
new OnDeviceAdSelectionRunner(
mContextSpy,
mCustomAudienceDao,
mAdSelectionEntryDaoSpy,
mAdServicesHttpsClient,
mLightweightExecutorService,
mBackgroundExecutorService,
mScheduledExecutor,
mMockAdsScoreGenerator,
mMockAdSelectionIdGenerator,
mClockSpy,
mAdServicesLoggerMock,
mFlags,
CALLER_UID,
mAdSelectionServiceFilterMock,
mAdSelectionExecutionLogger,
mPerBuyerBiddingRunnerMock,
mAdFilterer,
mAdCounterKeyCopier,
mAdCounterHistogramUpdaterMock,
mFrequencyCapAdDataValidator,
mDebugReportingMock,
false);
// Populating the Custom Audience DB
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer1,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_1));
mCustomAudienceDao.insertOrOverwriteCustomAudience(
mDBCustomAudienceForBuyer2,
CustomAudienceFixture.getValidDailyUpdateUriByBuyer(BUYER_2));
doThrow(new RuntimeException("Failing ad counter histogram update for test"))
.when(mAdCounterHistogramUpdaterMock)
.updateWinHistogram(any());
AdSelectionTestCallback callback =
invokeRunAdSelection(mAdSelectionRunner, adSelectionConfig, MY_APP_PACKAGE_NAME);
assertThat(callback.mIsSuccess).isTrue();
verify(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
verify(mAdCounterHistogramUpdaterMock).updateWinHistogram(any());
assertTrue(
mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(
callback.mAdSelectionResponse.getAdSelectionId()));
}
private void verifyErrorMessageIsCorrect(
final String actualErrorMassage, final String expectedErrorReason) {
Assert.assertTrue(
String.format(
"Actual error [%s] does not begin with [%s]",
actualErrorMassage, ERROR_AD_SELECTION_FAILURE),
actualErrorMassage.startsWith(ERROR_AD_SELECTION_FAILURE));
Assert.assertTrue(
String.format(
"Actual error [%s] does not contain expected message: [%s]",
actualErrorMassage, expectedErrorReason),
actualErrorMassage.contains(expectedErrorReason));
}
private AdSelectionTestCallback invokeRunAdSelection(
AdSelectionRunner adSelectionRunner,
AdSelectionConfig adSelectionConfig,
String callerPackageName) {
// Counted down in 1) callback and 2) logApiCall
CountDownLatch countDownLatch = new CountDownLatch(1);
AdSelectionTestCallback adSelectionTestCallback =
new AdSelectionTestCallback(countDownLatch);
AdSelectionInput input =
new AdSelectionInput.Builder()
.setAdSelectionConfig(adSelectionConfig)
.setCallerPackageName(callerPackageName)
.build();
adSelectionRunner.runAdSelection(
input, adSelectionTestCallback, DevContext.createForDevOptionsDisabled(), null);
try {
adSelectionTestCallback.mCountDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
return adSelectionTestCallback;
}
@After
public void tearDown() {
if (mAdSelectionEntryDaoSpy != null) {
mAdSelectionEntryDaoSpy.removeAdSelectionEntriesByIds(Arrays.asList(AD_SELECTION_ID));
}
if (mStaticMockSession != null) {
mStaticMockSession.finishMocking();
}
}
static class AdSelectionTestCallback extends AdSelectionCallback.Stub {
final CountDownLatch mCountDownLatch;
boolean mIsSuccess = false;
AdSelectionResponse mAdSelectionResponse;
FledgeErrorResponse mFledgeErrorResponse;
AdSelectionTestCallback(CountDownLatch countDownLatch) {
mCountDownLatch = countDownLatch;
mAdSelectionResponse = null;
mFledgeErrorResponse = null;
}
@Override
public void onSuccess(AdSelectionResponse adSelectionResponse) throws RemoteException {
mIsSuccess = true;
mAdSelectionResponse = adSelectionResponse;
mCountDownLatch.countDown();
}
@Override
public void onFailure(FledgeErrorResponse fledgeErrorResponse) throws RemoteException {
mIsSuccess = false;
mFledgeErrorResponse = fledgeErrorResponse;
mCountDownLatch.countDown();
}
}
private void mockAdSelectionExecutionLoggerSpyWithSuccessAdSelection() {
when(mAdSelectionExecutionLoggerClockMock.elapsedRealtime())
.thenReturn(
START_ELAPSED_TIMESTAMP,
BIDDING_STAGE_START_TIMESTAMP,
GET_BUYERS_CUSTOM_AUDIENCE_END_TIMESTAMP,
RUN_AD_BIDDING_START_TIMESTAMP,
RUN_AD_BIDDING_END_TIMESTAMP,
PERSIST_AD_SELECTION_START_TIMESTAMP,
PERSIST_AD_SELECTION_END_TIMESTAMP,
STOP_ELAPSED_TIMESTAMP);
mAdSelectionExecutionLogger =
new AdSelectionExecutionLogger(
sCallerMetadata,
mAdSelectionExecutionLoggerClockMock,
mContextSpy,
mAdServicesLoggerMock);
}
private void mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByValidateRequest() {
when(mAdSelectionExecutionLoggerClockMock.elapsedRealtime())
.thenReturn(START_ELAPSED_TIMESTAMP, STOP_ELAPSED_TIMESTAMP);
mAdSelectionExecutionLogger =
new AdSelectionExecutionLogger(
sCallerMetadata,
mAdSelectionExecutionLoggerClockMock,
mContextSpy,
mAdServicesLoggerMock);
}
private void mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByNoCAs() {
when(mAdSelectionExecutionLoggerClockMock.elapsedRealtime())
.thenReturn(
START_ELAPSED_TIMESTAMP,
BIDDING_STAGE_START_TIMESTAMP,
BIDDING_STAGE_END_TIMESTAMP,
STOP_ELAPSED_TIMESTAMP);
mAdSelectionExecutionLogger =
new AdSelectionExecutionLogger(
sCallerMetadata,
mAdSelectionExecutionLoggerClockMock,
mContextSpy,
mAdServicesLoggerMock);
}
private void mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionByNoBiddingOutcomes() {
when(mAdSelectionExecutionLoggerClockMock.elapsedRealtime())
.thenReturn(
START_ELAPSED_TIMESTAMP,
BIDDING_STAGE_START_TIMESTAMP,
GET_BUYERS_CUSTOM_AUDIENCE_END_TIMESTAMP,
RUN_AD_BIDDING_START_TIMESTAMP,
RUN_AD_BIDDING_END_TIMESTAMP,
STOP_ELAPSED_TIMESTAMP);
mAdSelectionExecutionLogger =
new AdSelectionExecutionLogger(
sCallerMetadata,
mAdSelectionExecutionLoggerClockMock,
mContextSpy,
mAdServicesLoggerMock);
}
// TODO(b/221861861): add SCORING TIMESTAMP.
private void mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionDuringScoring() {
when(mAdSelectionExecutionLoggerClockMock.elapsedRealtime())
.thenReturn(
START_ELAPSED_TIMESTAMP,
BIDDING_STAGE_START_TIMESTAMP,
GET_BUYERS_CUSTOM_AUDIENCE_END_TIMESTAMP,
RUN_AD_BIDDING_START_TIMESTAMP,
RUN_AD_BIDDING_END_TIMESTAMP,
STOP_ELAPSED_TIMESTAMP);
mAdSelectionExecutionLogger =
new AdSelectionExecutionLogger(
sCallerMetadata,
mAdSelectionExecutionLoggerClockMock,
mContextSpy,
mAdServicesLoggerMock);
}
private void mockAdSelectionExecutionLoggerSpyWithFailedAdSelectionBeforePersistAdSelection() {
when(mAdSelectionExecutionLoggerClockMock.elapsedRealtime())
.thenReturn(
START_ELAPSED_TIMESTAMP,
BIDDING_STAGE_START_TIMESTAMP,
GET_BUYERS_CUSTOM_AUDIENCE_END_TIMESTAMP,
RUN_AD_BIDDING_START_TIMESTAMP,
RUN_AD_BIDDING_END_TIMESTAMP,
STOP_ELAPSED_TIMESTAMP);
mAdSelectionExecutionLogger =
new AdSelectionExecutionLogger(
sCallerMetadata,
mAdSelectionExecutionLoggerClockMock,
mContextSpy,
mAdServicesLoggerMock);
}
// Verify bidding process.
private void verifyLogForSuccessfulBiddingProcess(List<AdBiddingOutcome> adBiddingOutcomeList) {
verify(mAdServicesLoggerMock)
.logRunAdBiddingProcessReportedStats(
mRunAdBiddingProcessReportedStatsArgumentCaptor.capture());
RunAdBiddingProcessReportedStats runAdBiddingProcessReportedStats =
mRunAdBiddingProcessReportedStatsArgumentCaptor.getValue();
assertThat(runAdBiddingProcessReportedStats.getGetBuyersCustomAudienceLatencyInMills())
.isEqualTo(GET_BUYERS_CUSTOM_AUDIENCE_LATENCY_MS);
assertThat(runAdBiddingProcessReportedStats.getGetBuyersCustomAudienceResultCode())
.isEqualTo(STATUS_SUCCESS);
assertThat(runAdBiddingProcessReportedStats.getNumBuyersRequested())
.isEqualTo(mCustomAudienceBuyers.size());
assertThat(runAdBiddingProcessReportedStats.getNumBuyersFetched())
.isEqualTo(
mBuyerCustomAudienceList.stream()
.filter(a -> !Objects.isNull(a))
.map(a -> a.getBuyer())
.collect(Collectors.toSet())
.size());
assertThat(runAdBiddingProcessReportedStats.getNumOfAdsEnteringBidding())
.isEqualTo(
mBuyerCustomAudienceList.stream()
.filter(a -> !Objects.isNull(a))
.map(a -> a.getAds().size())
.reduce(0, (a, b) -> (a + b)));
int numOfCAsEnteringBidding = mBuyerCustomAudienceList.size();
assertThat(runAdBiddingProcessReportedStats.getNumOfCasEnteringBidding())
.isEqualTo(numOfCAsEnteringBidding);
int numOfCAsPostBidding =
adBiddingOutcomeList.stream()
.filter(a -> !Objects.isNull(a))
.map(
a ->
a.getCustomAudienceBiddingInfo()
.getCustomAudienceSignals()
.hashCode())
.collect(Collectors.toSet())
.size();
assertThat(runAdBiddingProcessReportedStats.getNumOfCasPostBidding())
.isEqualTo(numOfCAsPostBidding);
assertThat(runAdBiddingProcessReportedStats.getRatioOfCasSelectingRmktAds())
.isEqualTo(((float) numOfCAsPostBidding) / numOfCAsEnteringBidding);
assertThat(runAdBiddingProcessReportedStats.getRunAdBiddingLatencyInMillis())
.isEqualTo(RUN_AD_BIDDING_LATENCY_MS);
assertThat(runAdBiddingProcessReportedStats.getRunAdBiddingResultCode())
.isEqualTo(STATUS_SUCCESS);
assertThat(runAdBiddingProcessReportedStats.getTotalAdBiddingStageLatencyInMillis())
.isEqualTo(TOTAL_BIDDING_STAGE_LATENCY_IN_MS);
}
private void verifyLogForFailedBiddingStageDuringFetchBuyersCustomAudience(int resultCode) {
verify(mAdServicesLoggerMock)
.logRunAdBiddingProcessReportedStats(
mRunAdBiddingProcessReportedStatsArgumentCaptor.capture());
RunAdBiddingProcessReportedStats runAdBiddingProcessReportedStats =
mRunAdBiddingProcessReportedStatsArgumentCaptor.getValue();
assertThat(runAdBiddingProcessReportedStats.getGetBuyersCustomAudienceLatencyInMills())
.isEqualTo(TOTAL_BIDDING_STAGE_LATENCY_IN_MS);
assertThat(runAdBiddingProcessReportedStats.getGetBuyersCustomAudienceResultCode())
.isEqualTo(resultCode);
assertThat(runAdBiddingProcessReportedStats.getNumBuyersRequested())
.isEqualTo(mCustomAudienceBuyers.size());
assertThat(runAdBiddingProcessReportedStats.getNumBuyersFetched()).isEqualTo(STATUS_UNSET);
assertThat(runAdBiddingProcessReportedStats.getNumOfAdsEnteringBidding())
.isEqualTo(STATUS_UNSET);
assertThat(runAdBiddingProcessReportedStats.getNumOfCasEnteringBidding())
.isEqualTo(STATUS_UNSET);
assertThat(runAdBiddingProcessReportedStats.getNumOfCasPostBidding())
.isEqualTo(STATUS_UNSET);
assertThat(runAdBiddingProcessReportedStats.getRatioOfCasSelectingRmktAds())
.isEqualTo(-1.0f);
assertThat(runAdBiddingProcessReportedStats.getRunAdBiddingLatencyInMillis())
.isEqualTo(STATUS_UNSET);
assertThat(runAdBiddingProcessReportedStats.getRunAdBiddingResultCode())
.isEqualTo(STATUS_UNSET);
assertThat(runAdBiddingProcessReportedStats.getTotalAdBiddingStageLatencyInMillis())
.isEqualTo(TOTAL_BIDDING_STAGE_LATENCY_IN_MS);
}
// Verify Ad selection process.
private void verifyLogForSuccessfulAdSelectionProcess() {
verify(mAdServicesLoggerMock)
.logRunAdSelectionProcessReportedStats(
mRunAdSelectionProcessReportedStatsArgumentCaptor.capture());
RunAdSelectionProcessReportedStats runAdSelectionProcessReportedStats =
mRunAdSelectionProcessReportedStatsArgumentCaptor.getValue();
assertThat(runAdSelectionProcessReportedStats.getIsRemarketingAdsWon())
.isEqualTo(IS_RMKT_ADS_WON);
assertThat(runAdSelectionProcessReportedStats.getDBAdSelectionSizeInBytes())
.isEqualTo((int) DB_AD_SELECTION_FILE_SIZE);
assertThat(runAdSelectionProcessReportedStats.getPersistAdSelectionLatencyInMillis())
.isEqualTo(PERSIST_AD_SELECTION_LATENCY_MS);
assertThat(runAdSelectionProcessReportedStats.getPersistAdSelectionResultCode())
.isEqualTo(STATUS_SUCCESS);
assertThat(runAdSelectionProcessReportedStats.getRunAdSelectionLatencyInMillis())
.isEqualTo(RUN_AD_SELECTION_INTERNAL_FINAL_LATENCY_MS);
assertThat(runAdSelectionProcessReportedStats.getRunAdSelectionResultCode())
.isEqualTo(STATUS_SUCCESS);
}
private void verifyLogForFailurePriorPersistAdSelection(int resultCode) {
verify(mAdServicesLoggerMock)
.logRunAdSelectionProcessReportedStats(
mRunAdSelectionProcessReportedStatsArgumentCaptor.capture());
RunAdSelectionProcessReportedStats runAdSelectionProcessReportedStats =
mRunAdSelectionProcessReportedStatsArgumentCaptor.getValue();
assertThat(runAdSelectionProcessReportedStats.getIsRemarketingAdsWon())
.isEqualTo(IS_RMKT_ADS_WON_UNSET);
assertThat(runAdSelectionProcessReportedStats.getDBAdSelectionSizeInBytes())
.isEqualTo(STATUS_UNSET);
assertThat(runAdSelectionProcessReportedStats.getPersistAdSelectionLatencyInMillis())
.isEqualTo(STATUS_UNSET);
assertThat(runAdSelectionProcessReportedStats.getPersistAdSelectionResultCode())
.isEqualTo(STATUS_UNSET);
assertThat(runAdSelectionProcessReportedStats.getRunAdSelectionLatencyInMillis())
.isEqualTo(RUN_AD_SELECTION_INTERNAL_FINAL_LATENCY_MS);
assertThat(runAdSelectionProcessReportedStats.getRunAdSelectionResultCode())
.isEqualTo(resultCode);
}
private void verifyLogForFailureByRunAdSelectionOrchestrationTimesOut(int resultCode) {
verify(mAdServicesLoggerMock)
.logRunAdSelectionProcessReportedStats(
mRunAdSelectionProcessReportedStatsArgumentCaptor.capture());
RunAdSelectionProcessReportedStats runAdSelectionProcessReportedStats =
mRunAdSelectionProcessReportedStatsArgumentCaptor.getValue();
assertThat(runAdSelectionProcessReportedStats.getRunAdSelectionLatencyInMillis())
.isEqualTo(RUN_AD_SELECTION_INTERNAL_FINAL_LATENCY_MS);
assertThat(runAdSelectionProcessReportedStats.getRunAdSelectionResultCode())
.isEqualTo(resultCode);
}
private void verifyAndSetupCommonSuccessScenario(AdSelectionConfig adSelectionConfig)
throws AdServicesException {
when(mClockSpy.instant()).thenReturn(Clock.systemUTC().instant());
doReturn(mFlags).when(FlagsFactory::getFlags);
// Getting BiddingOutcome-forBuyerX corresponding to each CA-forBuyerX
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer1)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_1,
ImmutableList.of(mDBCustomAudienceForBuyer1),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
doReturn(ImmutableList.of(Futures.immediateFuture(mAdBiddingOutcomeForBuyer2)))
.when(mPerBuyerBiddingRunnerMock)
.runBidding(
BUYER_2,
ImmutableList.of(mDBCustomAudienceForBuyer2),
mFlags.getAdSelectionBiddingTimeoutPerBuyerMs(),
adSelectionConfig);
// Getting ScoringOutcome-ForBuyerX corresponding to each BiddingOutcome-forBuyerX
when(mMockAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, adSelectionConfig))
.thenReturn((FluentFuture.from(Futures.immediateFuture(mAdScoringOutcomeList))));
mockAdSelectionExecutionLoggerSpyWithSuccessAdSelection();
when(mMockAdSelectionIdGenerator.generateId()).thenReturn(AD_SELECTION_ID);
assertFalse(mAdSelectionEntryDaoSpy.doesAdSelectionIdExist(AD_SELECTION_ID));
}
private void verifyDebugReportingWasNotCalled() {
assertThat(mFlags.getFledgeEventLevelDebugReportingEnabled()).isFalse();
verify(mDebugReportingMock, times(0)).getSenderStrategy();
verify(mDebugReportSenderMock, times(0)).batchEnqueue(anyList());
verify(mDebugReportSenderMock, times(0)).flush();
}
private static class OnDeviceAdSelectionRunnerTestFlags implements Flags {
@Override
public boolean getDisableFledgeEnrollmentCheck() {
return true;
}
@Override
public boolean getFledgeAdSelectionFilteringEnabled() {
return true;
}
@Override
public long getAdSelectionBiddingTimeoutPerCaMs() {
return EXTENDED_FLEDGE_AD_SELECTION_BIDDING_TIMEOUT_PER_CA_MS;
}
@Override
public long getAdSelectionScoringTimeoutMs() {
return EXTENDED_FLEDGE_AD_SELECTION_SCORING_TIMEOUT_MS;
}
@Override
public long getAdSelectionSelectingOutcomeTimeoutMs() {
return EXTENDED_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS;
}
@Override
public long getAdSelectionOverallTimeoutMs() {
return EXTENDED_FLEDGE_AD_SELECTION_OVERALL_TIMEOUT_MS;
}
@Override
public long getAdSelectionFromOutcomesOverallTimeoutMs() {
return EXTENDED_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS;
}
@Override
public long getReportImpressionOverallTimeoutMs() {
return EXTENDED_FLEDGE_REPORT_IMPRESSION_OVERALL_TIMEOUT_MS;
}
@Override
public boolean getFledgeOnDeviceAuctionKillSwitch() {
return false;
}
}
private Map<AdTechIdentifier, SignedContextualAds> createContextualAds() {
Map<AdTechIdentifier, SignedContextualAds> buyerContextualAds = new HashMap<>();
AdTechIdentifier buyer1 = CommonFixture.VALID_BUYER_1;
SignedContextualAds contextualAds1 =
SignedContextualAdsFixture.generateSignedContextualAds(
buyer1, ImmutableList.of(100.0, 200.0, 300.0))
.setDecisionLogicUri(
CommonFixture.getUri(BUYER_1, BUYER_BIDDING_LOGIC_URI_PATH))
.build();
AdTechIdentifier buyer2 = CommonFixture.VALID_BUYER_2;
SignedContextualAds contextualAds2 =
SignedContextualAdsFixture.generateSignedContextualAds(
buyer2, ImmutableList.of(400.0, 500.0))
.setDecisionLogicUri(
CommonFixture.getUri(BUYER_2, BUYER_BIDDING_LOGIC_URI_PATH))
.build();
buyerContextualAds.put(buyer1, contextualAds1);
buyerContextualAds.put(buyer2, contextualAds2);
return buyerContextualAds;
}
}