blob: a1617f4567eb1f2427ae02a097b40c816047694e [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.common.AdServicesStatusUtils.STATUS_SUCCESS;
import static android.adservices.common.AdServicesStatusUtils.STATUS_UNSET;
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.adselection.AdsScoreGeneratorImpl.MISSING_TRUSTED_SCORING_SIGNALS;
import static com.android.adservices.service.adselection.AdsScoreGeneratorImpl.QUERY_PARAM_RENDER_URIS;
import static com.android.adservices.service.adselection.AdsScoreGeneratorImpl.SCORES_COUNT_LESS_THAN_EXPECTED;
import static com.android.adservices.service.adselection.AdsScoreGeneratorImpl.SCORING_TIMED_OUT;
import static com.android.adservices.service.stats.AdSelectionExecutionLogger.SCRIPT_JAVASCRIPT;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_AD_SCORES_END_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_AD_SCORES_LATENCY_MS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_AD_SCORES_START_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_AD_SELECTION_LOGIC_END_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_AD_SELECTION_LOGIC_LATENCY_MS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_AD_SELECTION_LOGIC_START_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_TRUSTED_SCORING_SIGNALS_END_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_TRUSTED_SCORING_SIGNALS_LATENCY_MS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.GET_TRUSTED_SCORING_SIGNALS_START_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.RUN_AD_SCORING_END_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.RUN_AD_SCORING_LATENCY_MS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.RUN_AD_SCORING_START_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.SCORE_ADS_END_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.SCORE_ADS_LATENCY_MS;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.SCORE_ADS_START_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.START_ELAPSED_TIMESTAMP;
import static com.android.adservices.service.stats.AdSelectionExecutionLoggerTest.sCallerMetadata;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.adservices.adselection.AdBiddingOutcomeFixture;
import android.adservices.adselection.AdSelectionConfig;
import android.adservices.adselection.AdSelectionConfigFixture;
import android.adservices.adselection.AdWithBid;
import android.adservices.adselection.ContextualAds;
import android.adservices.adselection.ContextualAdsFixture;
import android.adservices.common.AdSelectionSignals;
import android.adservices.common.AdTechIdentifier;
import android.adservices.common.CommonFixture;
import android.adservices.http.MockWebServerRule;
import android.annotation.NonNull;
import android.net.Uri;
import androidx.room.Room;
import androidx.test.core.app.ApplicationProvider;
import com.android.adservices.MockWebServerRuleFactory;
import com.android.adservices.concurrency.AdServicesExecutors;
import com.android.adservices.data.adselection.AdSelectionDatabase;
import com.android.adservices.data.adselection.AdSelectionEntryDao;
import com.android.adservices.data.adselection.DBAdSelectionOverride;
import com.android.adservices.service.Flags;
import com.android.adservices.service.common.cache.CacheProviderFactory;
import com.android.adservices.service.common.httpclient.AdServicesHttpsClient;
import com.android.adservices.service.devapi.AdSelectionDevOverridesHelper;
import com.android.adservices.service.devapi.DevContext;
import com.android.adservices.service.stats.AdSelectionExecutionLogger;
import com.android.adservices.service.stats.AdServicesLogger;
import com.android.adservices.service.stats.AdServicesLoggerUtil;
import com.android.adservices.service.stats.Clock;
import com.android.adservices.service.stats.RunAdScoringProcessReportedStats;
import com.google.common.collect.ImmutableList;
import com.google.common.truth.Truth;
import com.google.common.util.concurrent.FluentFuture;
import com.google.common.util.concurrent.Futures;
import com.google.common.util.concurrent.ListenableFuture;
import com.google.common.util.concurrent.ListeningExecutorService;
import com.google.mockwebserver.Dispatcher;
import com.google.mockwebserver.MockResponse;
import com.google.mockwebserver.MockWebServer;
import com.google.mockwebserver.RecordedRequest;
import org.json.JSONException;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.mockito.stubbing.Answer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.stream.Collectors;
public class AdsScoreGeneratorImplTest {
private static final AdTechIdentifier BUYER_1 = AdSelectionConfigFixture.BUYER_1;
private static final AdTechIdentifier BUYER_2 = AdSelectionConfigFixture.BUYER_2;
private final String mFetchJavaScriptPath = "/fetchJavascript/";
private final String mTrustedScoringSignalsPath = "/getTrustedScoringSignals/";
@Rule public MockWebServerRule mMockWebServerRule = MockWebServerRuleFactory.createForHttps();
AdSelectionConfig mAdSelectionConfig;
@Mock private AdSelectionScriptEngine mMockAdSelectionScriptEngine;
private ListeningExecutorService mLightweightExecutorService;
private ListeningExecutorService mBackgroundExecutorService;
private ListeningExecutorService mBlockingExecutorService;
private ScheduledThreadPoolExecutor mSchedulingExecutor;
private AdServicesHttpsClient mWebClient;
private String mSellerDecisionLogicJs;
private AdBiddingOutcome mAdBiddingOutcomeBuyer1;
private AdBiddingOutcome mAdBiddingOutcomeBuyer2;
private List<AdBiddingOutcome> mAdBiddingOutcomeList;
private AdsScoreGenerator mAdsScoreGenerator;
private DevContext mDevContext;
private Flags mFlags;
private AdSelectionEntryDao mAdSelectionEntryDao;
private AdSelectionSignals mTrustedScoringSignals;
private String mTrustedScoringParams;
private List<String> mTrustedScoringSignalsKeys;
private Dispatcher mDefaultDispatcher;
private MockWebServerRule.RequestMatcher<String> mRequestMatcherExactMatch;
private AdSelectionExecutionLogger mAdSelectionExecutionLogger;
@Mock Clock mAdSelectionExecutionLoggerClock;
@Mock private AdServicesLogger mAdServicesLoggerMock;
@Captor
ArgumentCaptor<RunAdScoringProcessReportedStats>
mRunAdScoringProcessReportedStatsArgumentCaptor;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mFlags = new AdsScoreGeneratorImplTestFlags();
mDevContext = DevContext.createForDevOptionsDisabled();
mLightweightExecutorService = AdServicesExecutors.getLightWeightExecutor();
mBackgroundExecutorService = AdServicesExecutors.getBackgroundExecutor();
mBlockingExecutorService = AdServicesExecutors.getBlockingExecutor();
mSchedulingExecutor = AdServicesExecutors.getScheduler();
mWebClient =
new AdServicesHttpsClient(
AdServicesExecutors.getBlockingExecutor(),
CacheProviderFactory.createNoOpCache());
mAdBiddingOutcomeBuyer1 =
AdBiddingOutcomeFixture.anAdBiddingOutcomeBuilder(BUYER_1, 1.0).build();
mAdBiddingOutcomeBuyer2 =
AdBiddingOutcomeFixture.anAdBiddingOutcomeBuilder(BUYER_2, 2.0).build();
mAdBiddingOutcomeList = ImmutableList.of(mAdBiddingOutcomeBuyer1, mAdBiddingOutcomeBuyer2);
mSellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_uri, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":1}', 'reporting_uri': '"
+ " /reporting/seller "
+ "' } };\n"
+ "}";
mAdSelectionEntryDao =
Room.inMemoryDatabaseBuilder(
ApplicationProvider.getApplicationContext(),
AdSelectionDatabase.class)
.build()
.adSelectionEntryDao();
mTrustedScoringSignalsKeys =
ImmutableList.of(
mAdBiddingOutcomeBuyer1
.getAdWithBid()
.getAdData()
.getRenderUri()
.getEncodedPath(),
mAdBiddingOutcomeBuyer2
.getAdWithBid()
.getAdData()
.getRenderUri()
.getEncodedPath());
mTrustedScoringParams =
String.format(
"?%s=%s",
QUERY_PARAM_RENDER_URIS,
Uri.encode(String.join(",", mTrustedScoringSignalsKeys)));
mTrustedScoringSignals =
AdSelectionSignals.fromString(
"{\n"
+ mAdBiddingOutcomeBuyer1
.getAdWithBid()
.getAdData()
.getRenderUri()
.getEncodedPath()
+ ": signalsForUri1,\n"
+ mAdBiddingOutcomeBuyer2
.getAdWithBid()
.getAdData()
.getRenderUri()
.getEncodedPath()
+ ": signalsForUri2,\n"
+ "}");
mDefaultDispatcher =
new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) {
if (mFetchJavaScriptPath.equals(request.getPath())) {
return new MockResponse().setBody(mSellerDecisionLogicJs);
} else if (mTrustedScoringSignalsPath
.concat(mTrustedScoringParams)
.equals(request.getPath())) {
return new MockResponse().setBody(mTrustedScoringSignals.toString());
}
return new MockResponse().setResponseCode(404);
}
};
mRequestMatcherExactMatch =
(actualRequest, expectedRequest) -> actualRequest.equals(expectedRequest);
when(mAdSelectionExecutionLoggerClock.elapsedRealtime())
.thenReturn(START_ELAPSED_TIMESTAMP);
mAdSelectionExecutionLogger =
new AdSelectionExecutionLogger(
sCallerMetadata,
mAdSelectionExecutionLoggerClock,
ApplicationProvider.getApplicationContext(),
mAdServicesLoggerMock);
mAdsScoreGenerator =
new AdsScoreGeneratorImpl(
mMockAdSelectionScriptEngine,
mLightweightExecutorService,
mBackgroundExecutorService,
mSchedulingExecutor,
mWebClient,
mDevContext,
mAdSelectionEntryDao,
mFlags,
mAdSelectionExecutionLogger);
}
@Test
public void testRunAdScoringSuccess() throws Exception {
when(mAdSelectionExecutionLoggerClock.elapsedRealtime())
.thenReturn(
RUN_AD_SCORING_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_END_TIMESTAMP,
GET_AD_SCORES_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_END_TIMESTAMP,
SCORE_ADS_START_TIMESTAMP,
SCORE_ADS_END_TIMESTAMP,
GET_AD_SCORES_END_TIMESTAMP,
RUN_AD_SCORING_END_TIMESTAMP);
// Logger calls come after the callback is returned
CountDownLatch runAdScoringProcessLoggerLatch = new CountDownLatch(1);
doAnswer(
unusedInvocation -> {
runAdScoringProcessLoggerLatch.countDown();
return null;
})
.when(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(any());
List<Double> scores = ImmutableList.of(1.0, 2.0);
MockWebServer server = mMockWebServerRule.startMockWebServer(mDefaultDispatcher);
Uri decisionLogicUri = mMockWebServerRule.uriForPath(mFetchJavaScriptPath);
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.build();
Answer<ListenableFuture<List<Double>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(scores);
};
Mockito.when(
mMockAdSelectionScriptEngine.scoreAds(
mSellerDecisionLogicJs,
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList()),
mAdSelectionConfig,
mAdSelectionConfig.getSellerSignals(),
mTrustedScoringSignals,
AdSelectionSignals.EMPTY,
mAdBiddingOutcomeList.stream()
.map(
a ->
a.getCustomAudienceBiddingInfo()
.getCustomAudienceSignals())
.collect(Collectors.toList()),
mAdSelectionExecutionLogger))
.thenAnswer(loggerAnswer);
FluentFuture<List<AdScoringOutcome>> scoringResultFuture =
mAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, mAdSelectionConfig);
List<AdScoringOutcome> scoringOutcome = waitForFuture(() -> scoringResultFuture);
Mockito.verify(mMockAdSelectionScriptEngine)
.scoreAds(
mSellerDecisionLogicJs,
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList()),
mAdSelectionConfig,
mAdSelectionConfig.getSellerSignals(),
mTrustedScoringSignals,
AdSelectionSignals.EMPTY,
mAdBiddingOutcomeList.stream()
.map(
a ->
a.getCustomAudienceBiddingInfo()
.getCustomAudienceSignals())
.collect(Collectors.toList()),
mAdSelectionExecutionLogger);
mMockWebServerRule.verifyMockServerRequests(
server,
2,
ImmutableList.of(
mFetchJavaScriptPath, mTrustedScoringSignalsPath + mTrustedScoringParams),
mRequestMatcherExactMatch);
runAdScoringProcessLoggerLatch.await();
assertEquals(1L, scoringOutcome.get(0).getAdWithScore().getScore().longValue());
assertEquals(2L, scoringOutcome.get(1).getAdWithScore().getScore().longValue());
verifySuccessAdScoringLogging(
mSellerDecisionLogicJs, mTrustedScoringSignals, mAdBiddingOutcomeList);
}
@Test
public void testRunAdScoringContextual_Success() throws Exception {
when(mAdSelectionExecutionLoggerClock.elapsedRealtime())
.thenReturn(
RUN_AD_SCORING_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_END_TIMESTAMP,
GET_AD_SCORES_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_END_TIMESTAMP,
SCORE_ADS_START_TIMESTAMP,
SCORE_ADS_END_TIMESTAMP,
GET_AD_SCORES_END_TIMESTAMP,
RUN_AD_SCORING_END_TIMESTAMP);
// Logger calls come after the callback is returned
CountDownLatch runAdScoringProcessLoggerLatch = new CountDownLatch(1);
doAnswer(
unusedInvocation -> {
runAdScoringProcessLoggerLatch.countDown();
return null;
})
.when(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(any());
List<Double> scores = ImmutableList.of(1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0);
MockWebServer server = mMockWebServerRule.startMockWebServer(mDefaultDispatcher);
Uri decisionLogicUri = mMockWebServerRule.uriForPath(mFetchJavaScriptPath);
Map<AdTechIdentifier, ContextualAds> contextualAdsMap = createContextualAds();
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.setBuyerContextualAds(contextualAdsMap)
.build();
List<AdWithBid> adsWithBid =
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList());
List<ContextualAds> contextualAds =
mAdSelectionConfig.getBuyerContextualAds().values().stream()
.collect(Collectors.toList());
List<AdWithBid> contextualBidAds = new ArrayList<>();
for (ContextualAds ctx : contextualAds) {
contextualBidAds.addAll(ctx.getAdsWithBid());
}
adsWithBid.addAll(contextualBidAds);
Answer<ListenableFuture<List<Double>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(scores);
};
Mockito.when(
mMockAdSelectionScriptEngine.scoreAds(
mSellerDecisionLogicJs,
adsWithBid,
mAdSelectionConfig,
mAdSelectionConfig.getSellerSignals(),
mTrustedScoringSignals,
AdSelectionSignals.EMPTY,
mAdBiddingOutcomeList.stream()
.map(
a ->
a.getCustomAudienceBiddingInfo()
.getCustomAudienceSignals())
.collect(Collectors.toList()),
mAdSelectionExecutionLogger))
.thenAnswer(loggerAnswer);
FluentFuture<List<AdScoringOutcome>> scoringResultFuture =
mAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, mAdSelectionConfig);
List<AdScoringOutcome> scoringOutcome = waitForFuture(() -> scoringResultFuture);
Mockito.verify(mMockAdSelectionScriptEngine)
.scoreAds(
mSellerDecisionLogicJs,
adsWithBid,
mAdSelectionConfig,
mAdSelectionConfig.getSellerSignals(),
mTrustedScoringSignals,
AdSelectionSignals.EMPTY,
mAdBiddingOutcomeList.stream()
.map(
a ->
a.getCustomAudienceBiddingInfo()
.getCustomAudienceSignals())
.collect(Collectors.toList()),
mAdSelectionExecutionLogger);
mMockWebServerRule.verifyMockServerRequests(
server,
2,
ImmutableList.of(
mFetchJavaScriptPath, mTrustedScoringSignalsPath + mTrustedScoringParams),
mRequestMatcherExactMatch);
runAdScoringProcessLoggerLatch.await();
assertEquals(1L, scoringOutcome.get(0).getAdWithScore().getScore().longValue());
assertEquals(2L, scoringOutcome.get(1).getAdWithScore().getScore().longValue());
assertEquals(5L, scoringOutcome.get(4).getAdWithScore().getScore().longValue());
assertEquals(300, scoringOutcome.get(4).getAdWithScore().getAdWithBid().getBid(), 0);
assertEquals(500, scoringOutcome.get(6).getAdWithScore().getAdWithBid().getBid(), 0);
verifySuccessAdScoringLogging(
mSellerDecisionLogicJs, mTrustedScoringSignals, mAdBiddingOutcomeList);
}
@Test
public void testRunAdScoringContextualScoresMismatch_Failure() throws Exception {
when(mAdSelectionExecutionLoggerClock.elapsedRealtime())
.thenReturn(
RUN_AD_SCORING_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_END_TIMESTAMP,
GET_AD_SCORES_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_END_TIMESTAMP,
SCORE_ADS_START_TIMESTAMP,
SCORE_ADS_END_TIMESTAMP,
GET_AD_SCORES_END_TIMESTAMP,
RUN_AD_SCORING_END_TIMESTAMP);
// Logger calls come after the callback is returned
CountDownLatch runAdScoringProcessLoggerLatch = new CountDownLatch(1);
doAnswer(
unusedInvocation -> {
runAdScoringProcessLoggerLatch.countDown();
return null;
})
.when(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(any());
// Ads expected to be scored are 7, but scoring is wired to return only 6 scores
List<Double> scores = ImmutableList.of(1.0, 2.0, 3.0, 4.0, 5.0, 6.0);
MockWebServer server = mMockWebServerRule.startMockWebServer(mDefaultDispatcher);
Uri decisionLogicUri = mMockWebServerRule.uriForPath(mFetchJavaScriptPath);
Map<AdTechIdentifier, ContextualAds> contextualAdsMap = createContextualAds();
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.setBuyerContextualAds(contextualAdsMap)
.build();
List<AdWithBid> adsWithBid =
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList());
List<ContextualAds> contextualAds =
mAdSelectionConfig.getBuyerContextualAds().values().stream()
.collect(Collectors.toList());
List<AdWithBid> contextualBidAds = new ArrayList<>();
for (ContextualAds ctx : contextualAds) {
contextualBidAds.addAll(ctx.getAdsWithBid());
}
adsWithBid.addAll(contextualBidAds);
Answer<ListenableFuture<List<Double>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(scores);
};
Mockito.when(
mMockAdSelectionScriptEngine.scoreAds(
mSellerDecisionLogicJs,
adsWithBid,
mAdSelectionConfig,
mAdSelectionConfig.getSellerSignals(),
mTrustedScoringSignals,
AdSelectionSignals.EMPTY,
mAdBiddingOutcomeList.stream()
.map(
a ->
a.getCustomAudienceBiddingInfo()
.getCustomAudienceSignals())
.collect(Collectors.toList()),
mAdSelectionExecutionLogger))
.thenAnswer(loggerAnswer);
FluentFuture<List<AdScoringOutcome>> scoringResultFuture =
mAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, mAdSelectionConfig);
IllegalStateException missingScoresException =
new IllegalStateException(SCORES_COUNT_LESS_THAN_EXPECTED);
ExecutionException outException =
assertThrows(ExecutionException.class, scoringResultFuture::get);
assertEquals(outException.getCause().getMessage(), missingScoresException.getMessage());
}
@Test
public void testMissingTrustedSignalsException() throws Exception {
when(mAdSelectionExecutionLoggerClock.elapsedRealtime())
.thenReturn(
RUN_AD_SCORING_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_END_TIMESTAMP,
GET_AD_SCORES_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_START_TIMESTAMP,
RUN_AD_SCORING_END_TIMESTAMP);
// Logger calls come after the callback is returned
CountDownLatch runAdScoringProcessLoggerLatch = new CountDownLatch(1);
doAnswer(
unusedInvocation -> {
runAdScoringProcessLoggerLatch.countDown();
return null;
})
.when(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(any());
// Missing server connection for trusted signals
Dispatcher missingSignalsDispatcher =
new Dispatcher() {
@Override
public MockResponse dispatch(RecordedRequest request) {
if (mFetchJavaScriptPath.equals(request.getPath())) {
return new MockResponse().setBody(mSellerDecisionLogicJs);
}
return new MockResponse().setResponseCode(404);
}
};
MockWebServer server = mMockWebServerRule.startMockWebServer(missingSignalsDispatcher);
Uri decisionLogicUri = mMockWebServerRule.uriForPath(mFetchJavaScriptPath);
IllegalStateException missingSignalsException =
new IllegalStateException(MISSING_TRUSTED_SCORING_SIGNALS);
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.build();
FluentFuture<List<AdScoringOutcome>> scoringResultFuture =
mAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, mAdSelectionConfig);
runAdScoringProcessLoggerLatch.await();
ExecutionException outException =
assertThrows(ExecutionException.class, scoringResultFuture::get);
assertEquals(outException.getCause().getMessage(), missingSignalsException.getMessage());
mMockWebServerRule.verifyMockServerRequests(
server,
2,
ImmutableList.of(
mFetchJavaScriptPath, mTrustedScoringSignalsPath + mTrustedScoringParams),
mRequestMatcherExactMatch);
verifyFailedAdScoringLoggingMissingTrustedScoringSignals(
mSellerDecisionLogicJs,
mAdBiddingOutcomeList,
AdServicesLoggerUtil.getResultCodeFromException(
missingSignalsException.getCause()));
}
@Test
public void testRunAdScoringUseDevOverrideForJS() throws Exception {
when(mAdSelectionExecutionLoggerClock.elapsedRealtime())
.thenReturn(
RUN_AD_SCORING_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_END_TIMESTAMP,
GET_AD_SCORES_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_END_TIMESTAMP,
SCORE_ADS_START_TIMESTAMP,
SCORE_ADS_END_TIMESTAMP,
GET_AD_SCORES_END_TIMESTAMP,
RUN_AD_SCORING_END_TIMESTAMP);
// Logger calls come after the callback is returned
CountDownLatch runAdScoringProcessLoggerLatch = new CountDownLatch(1);
doAnswer(
unusedInvocation -> {
runAdScoringProcessLoggerLatch.countDown();
return null;
})
.when(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(any());
List<Double> scores = ImmutableList.of(1.0, 2.0);
MockWebServer server = mMockWebServerRule.startMockWebServer(mDefaultDispatcher);
Uri decisionLogicUri = mMockWebServerRule.uriForPath(mFetchJavaScriptPath);
// Different seller decision logic JS to simulate different different override from server
String differentSellerDecisionLogicJs =
"function reportResult(ad_selection_config, render_uri, bid, contextual_signals) {"
+ " \n"
+ " return {'status': 0, 'results': {'signals_for_buyer':"
+ " '{\"signals_for_buyer\":2}', 'reporting_uri': '"
+ " /reporting/seller "
+ "' } };\n"
+ "}";
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.build();
// Set dev override for this AdSelection
String myAppPackageName = "com.google.ppapi.test";
DBAdSelectionOverride adSelectionOverride =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(
mAdSelectionConfig))
.setAppPackageName(myAppPackageName)
.setDecisionLogicJS(differentSellerDecisionLogicJs)
.setTrustedScoringSignals(mTrustedScoringSignals.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(adSelectionOverride);
// Resetting Generator to use new dev context
mDevContext =
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(myAppPackageName)
.build();
mAdsScoreGenerator =
new AdsScoreGeneratorImpl(
mMockAdSelectionScriptEngine,
mLightweightExecutorService,
mBackgroundExecutorService,
mSchedulingExecutor,
mWebClient,
mDevContext,
mAdSelectionEntryDao,
mFlags,
mAdSelectionExecutionLogger);
Answer<ListenableFuture<List<Double>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(scores);
};
Mockito.when(
mMockAdSelectionScriptEngine.scoreAds(
differentSellerDecisionLogicJs,
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList()),
mAdSelectionConfig,
mAdSelectionConfig.getSellerSignals(),
mTrustedScoringSignals,
AdSelectionSignals.EMPTY,
mAdBiddingOutcomeList.stream()
.map(
a ->
a.getCustomAudienceBiddingInfo()
.getCustomAudienceSignals())
.collect(Collectors.toList()),
mAdSelectionExecutionLogger))
.thenAnswer(loggerAnswer);
FluentFuture<List<AdScoringOutcome>> scoringResultFuture =
mAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, mAdSelectionConfig);
List<AdScoringOutcome> scoringOutcome = waitForFuture(() -> scoringResultFuture);
runAdScoringProcessLoggerLatch.await();
// The server will not be invoked as the web calls should be overridden
mMockWebServerRule.verifyMockServerRequests(
server, 0, Collections.emptyList(), mRequestMatcherExactMatch);
Assert.assertEquals(1L, scoringOutcome.get(0).getAdWithScore().getScore().longValue());
Assert.assertEquals(2L, scoringOutcome.get(1).getAdWithScore().getScore().longValue());
verifySuccessAdScoringLogging(
differentSellerDecisionLogicJs, mTrustedScoringSignals, mAdBiddingOutcomeList);
}
@Test
public void testRunAdScoringJsonException() throws Exception {
when(mAdSelectionExecutionLoggerClock.elapsedRealtime())
.thenReturn(
RUN_AD_SCORING_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_END_TIMESTAMP,
GET_AD_SCORES_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_END_TIMESTAMP,
RUN_AD_SCORING_END_TIMESTAMP);
// Logger calls come after the callback is returned
CountDownLatch runAdScoringProcessLoggerLatch = new CountDownLatch(1);
doAnswer(
unusedInvocation -> {
runAdScoringProcessLoggerLatch.countDown();
return null;
})
.when(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(any());
MockWebServer server = mMockWebServerRule.startMockWebServer(mDefaultDispatcher);
Uri decisionLogicUri = mMockWebServerRule.uriForPath(mFetchJavaScriptPath);
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.build();
Mockito.when(
mMockAdSelectionScriptEngine.scoreAds(
mSellerDecisionLogicJs,
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList()),
mAdSelectionConfig,
mAdSelectionConfig.getSellerSignals(),
mTrustedScoringSignals,
AdSelectionSignals.EMPTY,
mAdBiddingOutcomeList.stream()
.map(
a ->
a.getCustomAudienceBiddingInfo()
.getCustomAudienceSignals())
.collect(Collectors.toList()),
mAdSelectionExecutionLogger))
.thenThrow(new JSONException("Badly formatted JSON"));
FluentFuture<List<AdScoringOutcome>> scoringResultFuture =
mAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, mAdSelectionConfig);
runAdScoringProcessLoggerLatch.await();
ExecutionException adServicesException =
Assert.assertThrows(
ExecutionException.class,
() -> {
waitForFuture(() -> scoringResultFuture);
});
Truth.assertThat(adServicesException.getMessage()).contains("Badly formatted JSON");
mMockWebServerRule.verifyMockServerRequests(
server,
2,
ImmutableList.of(
mFetchJavaScriptPath, mTrustedScoringSignalsPath + mTrustedScoringParams),
mRequestMatcherExactMatch);
verifyFailedAdScoringLoggingJSONExceptionWithScoreAds(
mSellerDecisionLogicJs,
mTrustedScoringSignals,
mAdBiddingOutcomeList,
AdServicesLoggerUtil.getResultCodeFromException(adServicesException.getCause()));
}
@Test
public void testRunAdScoringTimesOut() throws Exception {
when(mAdSelectionExecutionLoggerClock.elapsedRealtime())
.thenReturn(
RUN_AD_SCORING_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_START_TIMESTAMP,
GET_AD_SELECTION_LOGIC_END_TIMESTAMP,
GET_AD_SCORES_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_START_TIMESTAMP,
GET_TRUSTED_SCORING_SIGNALS_END_TIMESTAMP,
RUN_AD_SCORING_END_TIMESTAMP);
// Logger calls come after the callback is returned
CountDownLatch runAdScoringProcessLoggerLatch = new CountDownLatch(1);
doAnswer(
unusedInvocation -> {
runAdScoringProcessLoggerLatch.countDown();
return null;
})
.when(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(any());
Flags flagsWithSmallerLimits =
new Flags() {
@Override
public long getAdSelectionScoringTimeoutMs() {
return 100;
}
};
mAdsScoreGenerator =
new AdsScoreGeneratorImpl(
mMockAdSelectionScriptEngine,
mLightweightExecutorService,
mBackgroundExecutorService,
mSchedulingExecutor,
mWebClient,
mDevContext,
mAdSelectionEntryDao,
flagsWithSmallerLimits,
mAdSelectionExecutionLogger);
List<Double> scores = Arrays.asList(1.0, 2.0);
mMockWebServerRule.startMockWebServer(mDefaultDispatcher);
Uri decisionLogicUri = mMockWebServerRule.uriForPath(mFetchJavaScriptPath);
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.build();
Mockito.when(
mMockAdSelectionScriptEngine.scoreAds(
mSellerDecisionLogicJs,
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList()),
mAdSelectionConfig,
mAdSelectionConfig.getSellerSignals(),
mTrustedScoringSignals,
AdSelectionSignals.EMPTY,
mAdBiddingOutcomeList.stream()
.map(
a ->
a.getCustomAudienceBiddingInfo()
.getCustomAudienceSignals())
.collect(Collectors.toList()),
mAdSelectionExecutionLogger))
.thenAnswer((invocation) -> getScoresWithDelay(scores, flagsWithSmallerLimits));
FluentFuture<List<AdScoringOutcome>> scoringResultFuture =
mAdsScoreGenerator.runAdScoring(mAdBiddingOutcomeList, mAdSelectionConfig);
runAdScoringProcessLoggerLatch.await();
ExecutionException thrown =
assertThrows(ExecutionException.class, scoringResultFuture::get);
assertTrue(thrown.getMessage().contains(SCORING_TIMED_OUT));
verifyFailedAdScoringLoggingTimeout(
mAdBiddingOutcomeList,
AdServicesLoggerUtil.getResultCodeFromException(thrown.getCause()));
}
private void verifyFailedAdScoringLoggingTimeout(
List<AdBiddingOutcome> adBiddingOutcomeList, int resultCode) {
int numOfCAsEnteringScoring =
adBiddingOutcomeList.stream()
.filter(Objects::nonNull)
.map(a -> a.getCustomAudienceBiddingInfo())
.filter(Objects::nonNull)
.map(a -> a.getCustomAudienceSignals().hashCode())
.collect(Collectors.toSet())
.size();
int numOfAdsEnteringScoring =
adBiddingOutcomeList.stream()
.filter(Objects::nonNull)
.map(AdBiddingOutcome::getAdWithBid)
.filter(Objects::nonNull)
.collect(Collectors.toSet())
.size();
verify(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(
mRunAdScoringProcessReportedStatsArgumentCaptor.capture());
RunAdScoringProcessReportedStats runAdScoringProcessReportedStats =
mRunAdScoringProcessReportedStatsArgumentCaptor.getValue();
// Timeout exception could be thrown at any stage of the RunAdScoring process, so we only
// verify partial logging of the start and the end stage of RunAdScoring.
assertThat(runAdScoringProcessReportedStats.getNumOfCasEnteringScoring())
.isEqualTo(numOfCAsEnteringScoring);
assertThat(runAdScoringProcessReportedStats.getNumOfRemarketingAdsEnteringScoring())
.isEqualTo(numOfAdsEnteringScoring);
assertThat(runAdScoringProcessReportedStats.getNumOfContextualAdsEnteringScoring())
.isEqualTo(STATUS_UNSET);
assertThat(runAdScoringProcessReportedStats.getRunAdScoringResultCode())
.isEqualTo(resultCode);
}
private void verifyFailedAdScoringLoggingJSONExceptionWithScoreAds(
String sellerDecisionLogicJs,
AdSelectionSignals trustedScoringSignals,
List<AdBiddingOutcome> adBiddingOutcomeList,
int resultCode) {
int fetchedAdSelectionLogicScriptSizeInBytes = sellerDecisionLogicJs.getBytes().length;
int fetchedTrustedScoringSignalsDataSizeInBytes =
trustedScoringSignals.toString().getBytes().length;
int numOfCAsEnteringScoring =
adBiddingOutcomeList.stream()
.filter(Objects::nonNull)
.map(a -> a.getCustomAudienceBiddingInfo())
.filter(Objects::nonNull)
.map(a -> a.getCustomAudienceSignals().hashCode())
.collect(Collectors.toSet())
.size();
int numOfAdsEnteringScoring =
adBiddingOutcomeList.stream()
.filter(Objects::nonNull)
.map(AdBiddingOutcome::getAdWithBid)
.filter(Objects::nonNull)
.collect(Collectors.toSet())
.size();
verify(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(
mRunAdScoringProcessReportedStatsArgumentCaptor.capture());
RunAdScoringProcessReportedStats runAdScoringProcessReportedStats =
mRunAdScoringProcessReportedStatsArgumentCaptor.getValue();
assertThat(runAdScoringProcessReportedStats.getGetAdSelectionLogicLatencyInMillis())
.isEqualTo(GET_AD_SELECTION_LOGIC_LATENCY_MS);
assertThat(runAdScoringProcessReportedStats.getGetAdSelectionLogicResultCode())
.isEqualTo(STATUS_SUCCESS);
assertThat(runAdScoringProcessReportedStats.getGetAdSelectionLogicScriptType())
.isEqualTo(SCRIPT_JAVASCRIPT);
assertThat(runAdScoringProcessReportedStats.getFetchedAdSelectionLogicScriptSizeInBytes())
.isEqualTo(fetchedAdSelectionLogicScriptSizeInBytes);
assertThat(runAdScoringProcessReportedStats.getGetTrustedScoringSignalsLatencyInMillis())
.isEqualTo(GET_TRUSTED_SCORING_SIGNALS_LATENCY_MS);
assertThat(runAdScoringProcessReportedStats.getGetTrustedScoringSignalsResultCode())
.isEqualTo(STATUS_SUCCESS);
assertThat(
runAdScoringProcessReportedStats
.getFetchedTrustedScoringSignalsDataSizeInBytes())
.isEqualTo(fetchedTrustedScoringSignalsDataSizeInBytes);
assertThat(runAdScoringProcessReportedStats.getScoreAdsLatencyInMillis())
.isEqualTo(STATUS_UNSET);
assertThat(runAdScoringProcessReportedStats.getGetAdScoresLatencyInMillis())
.isEqualTo((int) (RUN_AD_SCORING_END_TIMESTAMP - GET_AD_SCORES_START_TIMESTAMP));
assertThat(runAdScoringProcessReportedStats.getGetAdScoresResultCode())
.isEqualTo(resultCode);
assertThat(runAdScoringProcessReportedStats.getNumOfCasEnteringScoring())
.isEqualTo(numOfCAsEnteringScoring);
assertThat(runAdScoringProcessReportedStats.getNumOfRemarketingAdsEnteringScoring())
.isEqualTo(numOfAdsEnteringScoring);
assertThat(runAdScoringProcessReportedStats.getNumOfContextualAdsEnteringScoring())
.isEqualTo(STATUS_UNSET);
assertThat(runAdScoringProcessReportedStats.getRunAdScoringLatencyInMillis())
.isEqualTo(RUN_AD_SCORING_LATENCY_MS);
assertThat(runAdScoringProcessReportedStats.getRunAdScoringResultCode())
.isEqualTo(resultCode);
}
private void verifyFailedAdScoringLoggingMissingTrustedScoringSignals(
String sellerDecisionLogicJs,
List<AdBiddingOutcome> adBiddingOutcomeList,
int resultCode) {
int fetchedAdSelectionLogicScriptSizeInBytes = sellerDecisionLogicJs.getBytes().length;
int numOfCAsEnteringScoring =
adBiddingOutcomeList.stream()
.filter(Objects::nonNull)
.map(a -> a.getCustomAudienceBiddingInfo())
.filter(Objects::nonNull)
.map(a -> a.getCustomAudienceSignals().hashCode())
.collect(Collectors.toSet())
.size();
int numOfAdsEnteringScoring =
adBiddingOutcomeList.stream()
.filter(Objects::nonNull)
.map(AdBiddingOutcome::getAdWithBid)
.filter(Objects::nonNull)
.collect(Collectors.toSet())
.size();
verify(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(
mRunAdScoringProcessReportedStatsArgumentCaptor.capture());
RunAdScoringProcessReportedStats runAdScoringProcessReportedStats =
mRunAdScoringProcessReportedStatsArgumentCaptor.getValue();
assertThat(runAdScoringProcessReportedStats.getGetAdSelectionLogicLatencyInMillis())
.isEqualTo(GET_AD_SELECTION_LOGIC_LATENCY_MS);
assertThat(runAdScoringProcessReportedStats.getGetAdSelectionLogicResultCode())
.isEqualTo(STATUS_SUCCESS);
assertThat(runAdScoringProcessReportedStats.getGetAdSelectionLogicScriptType())
.isEqualTo(SCRIPT_JAVASCRIPT);
assertThat(runAdScoringProcessReportedStats.getFetchedAdSelectionLogicScriptSizeInBytes())
.isEqualTo(fetchedAdSelectionLogicScriptSizeInBytes);
assertThat(runAdScoringProcessReportedStats.getGetTrustedScoringSignalsLatencyInMillis())
.isEqualTo(
(int)
(RUN_AD_SCORING_END_TIMESTAMP
- GET_TRUSTED_SCORING_SIGNALS_START_TIMESTAMP));
assertThat(runAdScoringProcessReportedStats.getGetTrustedScoringSignalsResultCode())
.isEqualTo(resultCode);
assertThat(
runAdScoringProcessReportedStats
.getFetchedTrustedScoringSignalsDataSizeInBytes())
.isEqualTo(STATUS_UNSET);
assertThat(runAdScoringProcessReportedStats.getScoreAdsLatencyInMillis())
.isEqualTo(STATUS_UNSET);
assertThat(runAdScoringProcessReportedStats.getGetAdScoresLatencyInMillis())
.isEqualTo((int) (RUN_AD_SCORING_END_TIMESTAMP - GET_AD_SCORES_START_TIMESTAMP));
assertThat(runAdScoringProcessReportedStats.getGetAdScoresResultCode())
.isEqualTo(resultCode);
assertThat(runAdScoringProcessReportedStats.getNumOfCasEnteringScoring())
.isEqualTo(numOfCAsEnteringScoring);
assertThat(runAdScoringProcessReportedStats.getNumOfRemarketingAdsEnteringScoring())
.isEqualTo(numOfAdsEnteringScoring);
assertThat(runAdScoringProcessReportedStats.getNumOfContextualAdsEnteringScoring())
.isEqualTo(STATUS_UNSET);
assertThat(runAdScoringProcessReportedStats.getRunAdScoringLatencyInMillis())
.isEqualTo(RUN_AD_SCORING_LATENCY_MS);
assertThat(runAdScoringProcessReportedStats.getRunAdScoringResultCode())
.isEqualTo(resultCode);
}
private void verifySuccessAdScoringLogging(
String sellerDecisionLogicJs,
AdSelectionSignals trustedScoringSignals,
List<AdBiddingOutcome> adBiddingOutcomeList) {
int fetchedAdSelectionLogicScriptSizeInBytes = sellerDecisionLogicJs.getBytes().length;
int fetchedTrustedScoringSignalsDataSizeInBytes =
trustedScoringSignals.toString().getBytes().length;
int numOfCAsEnteringScoring =
adBiddingOutcomeList.stream()
.filter(Objects::nonNull)
.map(a -> a.getCustomAudienceBiddingInfo())
.filter(Objects::nonNull)
.map(a -> a.getCustomAudienceSignals().hashCode())
.collect(Collectors.toSet())
.size();
int numOfAdsEnteringScoring =
adBiddingOutcomeList.stream()
.filter(Objects::nonNull)
.map(AdBiddingOutcome::getAdWithBid)
.filter(Objects::nonNull)
.collect(Collectors.toSet())
.size();
verify(mAdServicesLoggerMock)
.logRunAdScoringProcessReportedStats(
mRunAdScoringProcessReportedStatsArgumentCaptor.capture());
RunAdScoringProcessReportedStats runAdScoringProcessReportedStats =
mRunAdScoringProcessReportedStatsArgumentCaptor.getValue();
assertThat(runAdScoringProcessReportedStats.getGetAdSelectionLogicLatencyInMillis())
.isEqualTo(GET_AD_SELECTION_LOGIC_LATENCY_MS);
assertThat(runAdScoringProcessReportedStats.getGetAdSelectionLogicResultCode())
.isEqualTo(STATUS_SUCCESS);
assertThat(runAdScoringProcessReportedStats.getGetAdSelectionLogicScriptType())
.isEqualTo(SCRIPT_JAVASCRIPT);
assertThat(runAdScoringProcessReportedStats.getFetchedAdSelectionLogicScriptSizeInBytes())
.isEqualTo(fetchedAdSelectionLogicScriptSizeInBytes);
assertThat(runAdScoringProcessReportedStats.getGetTrustedScoringSignalsLatencyInMillis())
.isEqualTo(GET_TRUSTED_SCORING_SIGNALS_LATENCY_MS);
assertThat(runAdScoringProcessReportedStats.getGetTrustedScoringSignalsResultCode())
.isEqualTo(STATUS_SUCCESS);
assertThat(
runAdScoringProcessReportedStats
.getFetchedTrustedScoringSignalsDataSizeInBytes())
.isEqualTo(fetchedTrustedScoringSignalsDataSizeInBytes);
assertThat(runAdScoringProcessReportedStats.getScoreAdsLatencyInMillis())
.isEqualTo(SCORE_ADS_LATENCY_MS);
assertThat(runAdScoringProcessReportedStats.getGetAdScoresLatencyInMillis())
.isEqualTo(GET_AD_SCORES_LATENCY_MS);
assertThat(runAdScoringProcessReportedStats.getGetAdScoresResultCode())
.isEqualTo(STATUS_SUCCESS);
assertThat(runAdScoringProcessReportedStats.getNumOfCasEnteringScoring())
.isEqualTo(numOfCAsEnteringScoring);
assertThat(runAdScoringProcessReportedStats.getNumOfRemarketingAdsEnteringScoring())
.isEqualTo(numOfAdsEnteringScoring);
assertThat(runAdScoringProcessReportedStats.getNumOfContextualAdsEnteringScoring())
.isEqualTo(STATUS_UNSET);
assertThat(runAdScoringProcessReportedStats.getRunAdScoringLatencyInMillis())
.isEqualTo(RUN_AD_SCORING_LATENCY_MS);
assertThat(runAdScoringProcessReportedStats.getRunAdScoringResultCode())
.isEqualTo(STATUS_SUCCESS);
}
private ListenableFuture<List<Double>> getScoresWithDelay(
List<Double> scores, @NonNull Flags flags) {
return mBlockingExecutorService.submit(
() -> {
Thread.sleep(2 * flags.getAdSelectionScoringTimeoutMs());
return scores;
});
}
private Map<AdTechIdentifier, ContextualAds> createContextualAds() {
Map<AdTechIdentifier, ContextualAds> buyerContextualAds = new HashMap<>();
AdTechIdentifier buyer1 = CommonFixture.VALID_BUYER_1;
ContextualAds contextualAds1 =
ContextualAdsFixture.generateContextualAds(
buyer1, ImmutableList.of(100.0, 200.0, 300.0))
.build();
AdTechIdentifier buyer2 = CommonFixture.VALID_BUYER_2;
ContextualAds contextualAds2 =
ContextualAdsFixture.generateContextualAds(buyer1, ImmutableList.of(400.0, 500.0))
.build();
buyerContextualAds.put(buyer1, contextualAds1);
buyerContextualAds.put(buyer2, contextualAds2);
return buyerContextualAds;
}
private <T> T waitForFuture(
AdsScoreGeneratorImplTest.ThrowingSupplier<ListenableFuture<T>> function)
throws Exception {
CountDownLatch resultLatch = new CountDownLatch(1);
ListenableFuture<T> futureResult = function.get();
futureResult.addListener(resultLatch::countDown, mLightweightExecutorService);
resultLatch.await();
return futureResult.get();
}
interface ThrowingSupplier<T> {
T get() throws Exception;
}
private static class AdsScoreGeneratorImplTestFlags implements Flags {
@Override
public boolean getFledgeAdSelectionContextualAdsEnabled() {
return true;
}
@Override
public long getAdSelectionScoringTimeoutMs() {
return EXTENDED_FLEDGE_AD_SELECTION_SCORING_TIMEOUT_MS;
}
@Override
public long getAdSelectionOverallTimeoutMs() {
return EXTENDED_FLEDGE_AD_SELECTION_OVERALL_TIMEOUT_MS;
}
}
}