blob: 7a4088a3761d0e071eb0b631f9c7088a783a2d6d [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.common.AdServicesStatusUtils.STATUS_SUCCESS;
import static android.adservices.common.AdServicesStatusUtils.STATUS_UNSET;
import static com.android.adservices.data.adselection.CustomAudienceSignals.CONTEXTUAL_CA_NAME;
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.adselection.DataVersionFetcher.DATA_VERSION_HEADER_SCORING_KEY;
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.assertFalse;
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.SignedContextualAds;
import android.adservices.adselection.SignedContextualAdsFixture;
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.CustomAudienceSignals;
import com.android.adservices.data.adselection.DBAdSelectionOverride;
import com.android.adservices.data.adselection.DBBuyerDecisionOverride;
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/";
private final String mTrustedScoringSignalsPathWithDataVersionHeader =
"/getTrustedScoringSignalsWithDataVersionHeader/";
@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;
@Mock private DebugReporting mDebugReporting;
@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());
} else if (mTrustedScoringSignalsPathWithDataVersionHeader
.concat(mTrustedScoringParams)
.equals(request.getPath()))
return new MockResponse()
.setBody(mTrustedScoringSignals.toString())
.addHeader(DATA_VERSION_HEADER_SCORING_KEY, DATA_VERSION_1);
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);
when(mDebugReporting.isEnabled()).thenReturn(false);
mAdsScoreGenerator =
new AdsScoreGeneratorImpl(
mMockAdSelectionScriptEngine,
mLightweightExecutorService,
mBackgroundExecutorService,
mSchedulingExecutor,
mWebClient,
mDevContext,
mAdSelectionEntryDao,
mFlags,
mAdSelectionExecutionLogger,
mDebugReporting,
false);
}
@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<ScoreAdResult>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(
scores.stream()
.map(
score ->
ScoreAdResult.builder()
.setAdScore(score)
.build())
.collect(Collectors.toList()));
};
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 testRunAdScoringSuccessWithDataVersionHeaderEnabled() throws Exception {
// Re init generator
mAdsScoreGenerator =
new AdsScoreGeneratorImpl(
mMockAdSelectionScriptEngine,
mLightweightExecutorService,
mBackgroundExecutorService,
mSchedulingExecutor,
mWebClient,
mDevContext,
mAdSelectionEntryDao,
mFlags,
mAdSelectionExecutionLogger,
mDebugReporting,
true);
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);
// Set trusted scoring uri to return data version header
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(
mTrustedScoringSignalsPathWithDataVersionHeader))
.build();
AdSelectionSignals expectedSellerContextualSignals =
SellerContextualSignals.builder()
.setDataVersion(DATA_VERSION_1)
.build()
.toAdSelectionSignals();
Answer<ListenableFuture<List<ScoreAdResult>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(
scores.stream()
.map(score -> ScoreAdResult.builder().setAdScore(score).build())
.collect(Collectors.toList()));
};
Mockito.when(
mMockAdSelectionScriptEngine.scoreAds(
mSellerDecisionLogicJs,
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList()),
mAdSelectionConfig,
mAdSelectionConfig.getSellerSignals(),
mTrustedScoringSignals,
expectedSellerContextualSignals,
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,
expectedSellerContextualSignals,
mAdBiddingOutcomeList.stream()
.map(
a ->
a.getCustomAudienceBiddingInfo()
.getCustomAudienceSignals())
.collect(Collectors.toList()),
mAdSelectionExecutionLogger);
mMockWebServerRule.verifyMockServerRequests(
server,
2,
ImmutableList.of(
mFetchJavaScriptPath,
mTrustedScoringSignalsPathWithDataVersionHeader + mTrustedScoringParams),
mRequestMatcherExactMatch);
runAdScoringProcessLoggerLatch.await();
assertEquals(1L, scoringOutcome.get(0).getAdWithScore().getScore().longValue());
assertEquals(2L, scoringOutcome.get(1).getAdWithScore().getScore().longValue());
// Assert seller contextual signals were propagated through
assertEquals(
expectedSellerContextualSignals,
scoringOutcome.get(0).getSellerContextualSignals().toAdSelectionSignals());
assertEquals(
expectedSellerContextualSignals,
scoringOutcome.get(1).getSellerContextualSignals().toAdSelectionSignals());
verifySuccessAdScoringLogging(
mSellerDecisionLogicJs, mTrustedScoringSignals, mAdBiddingOutcomeList);
}
@Test
public void testRunAdScoringSuccessWithDataVersionHeaderDisabled() throws Exception {
// Re init generator
mAdsScoreGenerator =
new AdsScoreGeneratorImpl(
mMockAdSelectionScriptEngine,
mLightweightExecutorService,
mBackgroundExecutorService,
mSchedulingExecutor,
mWebClient,
mDevContext,
mAdSelectionEntryDao,
mFlags,
mAdSelectionExecutionLogger,
mDebugReporting,
false);
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);
// Set trusted scoring uri to return data version header
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(
mTrustedScoringSignalsPathWithDataVersionHeader))
.build();
Answer<ListenableFuture<List<ScoreAdResult>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(
scores.stream()
.map(score -> ScoreAdResult.builder().setAdScore(score).build())
.collect(Collectors.toList()));
};
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,
mTrustedScoringSignalsPathWithDataVersionHeader + mTrustedScoringParams),
mRequestMatcherExactMatch);
runAdScoringProcessLoggerLatch.await();
assertEquals(1L, scoringOutcome.get(0).getAdWithScore().getScore().longValue());
assertEquals(2L, scoringOutcome.get(1).getAdWithScore().getScore().longValue());
// Assert seller contextual signals were propagated through
assertEquals(
AdSelectionSignals.EMPTY,
scoringOutcome.get(0).getSellerContextualSignals().toAdSelectionSignals());
assertEquals(
AdSelectionSignals.EMPTY,
scoringOutcome.get(1).getSellerContextualSignals().toAdSelectionSignals());
verifySuccessAdScoringLogging(
mSellerDecisionLogicJs, mTrustedScoringSignals, mAdBiddingOutcomeList);
}
@Test
public void testRunAdScoringSuccess_withDebugReportingEnabled() throws Exception {
Uri winUri = Uri.parse("http://example.com/reportWin");
Uri lossUri = Uri.parse("http://example.com/reportLoss");
String sellerRejectReason = "invalid-bid";
when(mDebugReporting.isEnabled()).thenReturn(true);
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<ScoreAdResult>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(
scores.stream()
.map(
score ->
ScoreAdResult.builder()
.setAdScore(score)
.setWinDebugReportUri(winUri)
.setLossDebugReportUri(lossUri)
.setSellerRejectReason(
sellerRejectReason)
.build())
.collect(Collectors.toList()));
};
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);
AdsScoreGeneratorImpl adsScoreGenerator =
new AdsScoreGeneratorImpl(
mMockAdSelectionScriptEngine,
mLightweightExecutorService,
mBackgroundExecutorService,
mSchedulingExecutor,
mWebClient,
mDevContext,
mAdSelectionEntryDao,
mFlags,
mAdSelectionExecutionLogger,
mDebugReporting,
false);
FluentFuture<List<AdScoringOutcome>> scoringResultFuture =
adsScoreGenerator.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(winUri, scoringOutcome.get(0).getDebugReport().getWinDebugReportUri());
assertEquals(lossUri, scoringOutcome.get(0).getDebugReport().getLossDebugReportUri());
assertEquals(
sellerRejectReason, scoringOutcome.get(0).getDebugReport().getSellerRejectReason());
assertEquals(winUri, scoringOutcome.get(1).getDebugReport().getWinDebugReportUri());
assertEquals(lossUri, scoringOutcome.get(1).getDebugReport().getLossDebugReportUri());
assertEquals(
sellerRejectReason, scoringOutcome.get(1).getDebugReport().getSellerRejectReason());
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, SignedContextualAds> contextualAdsMap = createContextualAds();
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.setBuyerSignedContextualAds(contextualAdsMap)
.build();
List<AdWithBid> adsWithBid =
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList());
List<SignedContextualAds> signedContextualAds =
mAdSelectionConfig.getBuyerSignedContextualAds().values().stream()
.collect(Collectors.toList());
List<AdWithBid> contextualBidAds = new ArrayList<>();
for (SignedContextualAds ctx : signedContextualAds) {
contextualBidAds.addAll(ctx.getAdsWithBid());
}
adsWithBid.addAll(contextualBidAds);
Answer<ListenableFuture<List<ScoreAdResult>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(
scores.stream()
.map(
score ->
ScoreAdResult.builder()
.setAdScore(score)
.build())
.collect(Collectors.toList()));
};
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 testRunAdScoringContextual_withDebugReportingEnabled_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());
when(mDebugReporting.isEnabled()).thenReturn(true);
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, SignedContextualAds> contextualAdsMap = createContextualAds();
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.setBuyerSignedContextualAds(contextualAdsMap)
.build();
List<AdWithBid> adsWithBid =
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList());
List<SignedContextualAds> signedContextualAds =
mAdSelectionConfig.getBuyerSignedContextualAds().values().stream()
.collect(Collectors.toList());
List<AdWithBid> contextualBidAds = new ArrayList<>();
for (SignedContextualAds ctx : signedContextualAds) {
contextualBidAds.addAll(ctx.getAdsWithBid());
}
adsWithBid.addAll(contextualBidAds);
Answer<ListenableFuture<List<ScoreAdResult>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(
scores.stream()
.map(
score ->
ScoreAdResult.builder()
.setAdScore(score)
.setWinDebugReportUri(
Uri.parse(
"http://example.com/1"))
.setLossDebugReportUri(
Uri.parse(
"http://example.com/2"))
.setSellerRejectReason("invalid-bid")
.build())
.collect(Collectors.toList()));
};
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);
AdsScoreGeneratorImpl adsScoreGenerator =
new AdsScoreGeneratorImpl(
mMockAdSelectionScriptEngine,
mLightweightExecutorService,
mBackgroundExecutorService,
mSchedulingExecutor,
mWebClient,
mDevContext,
mAdSelectionEntryDao,
mFlags,
mAdSelectionExecutionLogger,
mDebugReporting,
false);
FluentFuture<List<AdScoringOutcome>> scoringResultFuture =
adsScoreGenerator.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(
Uri.parse("http://example.com/1"),
scoringOutcome.get(0).getDebugReport().getWinDebugReportUri());
assertEquals(
Uri.parse("http://example.com/2"),
scoringOutcome.get(0).getDebugReport().getLossDebugReportUri());
assertEquals("invalid-bid", scoringOutcome.get(0).getDebugReport().getSellerRejectReason());
assertEquals(
Uri.parse("http://example.com/1"),
scoringOutcome.get(1).getDebugReport().getWinDebugReportUri());
assertEquals(
Uri.parse("http://example.com/2"),
scoringOutcome.get(1).getDebugReport().getLossDebugReportUri());
assertEquals("invalid-bid", scoringOutcome.get(1).getDebugReport().getSellerRejectReason());
verifySuccessAdScoringLogging(
mSellerDecisionLogicJs, mTrustedScoringSignals, mAdBiddingOutcomeList);
}
@Test
public void testRunAdScoringContextual_UseOverride_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, SignedContextualAds> contextualAdsMap = createContextualAds();
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.setBuyerSignedContextualAds(contextualAdsMap)
.build();
List<AdWithBid> adsWithBid =
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList());
List<SignedContextualAds> signedContextualAds =
mAdSelectionConfig.getBuyerSignedContextualAds().values().stream()
.collect(Collectors.toList());
List<AdWithBid> contextualBidAds = new ArrayList<>();
for (SignedContextualAds ctx : signedContextualAds) {
contextualBidAds.addAll(ctx.getAdsWithBid());
}
final String fakeDecisionLogicForBuyer = "\"reportWin() { completely fake }\"";
// Create an override for buyers decision logic only for Buyer 2
String myAppPackageName = "com.google.ppapi.test";
DBAdSelectionOverride adSelectionOverride =
DBAdSelectionOverride.builder()
.setAdSelectionConfigId(
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(
mAdSelectionConfig))
.setAppPackageName(myAppPackageName)
.setDecisionLogicJS(mSellerDecisionLogicJs)
.setTrustedScoringSignals(mTrustedScoringSignals.toString())
.build();
mAdSelectionEntryDao.persistAdSelectionOverride(adSelectionOverride);
DBBuyerDecisionOverride buyerDecisionOverride =
DBBuyerDecisionOverride.builder()
.setAdSelectionConfigId(
AdSelectionDevOverridesHelper.calculateAdSelectionConfigId(
mAdSelectionConfig))
.setAppPackageName(myAppPackageName)
.setDecisionLogic(fakeDecisionLogicForBuyer)
.setBuyer(BUYER_2)
.build();
mAdSelectionEntryDao.persistBuyersDecisionLogicOverride(
ImmutableList.of(buyerDecisionOverride));
mDevContext =
DevContext.builder()
.setDevOptionsEnabled(true)
.setCallingAppPackageName(myAppPackageName)
.build();
adsWithBid.addAll(contextualBidAds);
Answer<ListenableFuture<List<ScoreAdResult>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(
scores.stream()
.map(
score ->
ScoreAdResult.builder()
.setAdScore(score)
.build())
.collect(Collectors.toList()));
};
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);
mAdsScoreGenerator =
new AdsScoreGeneratorImpl(
mMockAdSelectionScriptEngine,
mLightweightExecutorService,
mBackgroundExecutorService,
mSchedulingExecutor,
mWebClient,
mDevContext,
mAdSelectionEntryDao,
mFlags,
mAdSelectionExecutionLogger,
mDebugReporting,
false);
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);
// No calls should have been made to the server, as overrides are set
mMockWebServerRule.verifyMockServerRequests(
server, 0, ImmutableList.of(), 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);
validateCustomAudienceSignals(scoringOutcome.get(6).getCustomAudienceSignals(), BUYER_2);
// Only buyer2 decision logic should have been populated from overrides
assertFalse(
"Buyer 1 should not have gotten decision logic",
scoringOutcome.get(4).isBiddingLogicJsDownloaded());
assertTrue(
"Buyer 2 ctx ads should have gotten decision logic from overrides",
scoringOutcome.get(5).isBiddingLogicJsDownloaded()
&& scoringOutcome.get(6).isBiddingLogicJsDownloaded());
assertEquals(fakeDecisionLogicForBuyer, scoringOutcome.get(6).getBiddingLogicJs());
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, SignedContextualAds> contextualAdsMap = createContextualAds();
mAdSelectionConfig =
AdSelectionConfigFixture.anAdSelectionConfigWithContextualAdsBuilder()
.setDecisionLogicUri(decisionLogicUri)
.setTrustedScoringSignalsUri(
mMockWebServerRule.uriForPath(mTrustedScoringSignalsPath))
.setBuyerSignedContextualAds(contextualAdsMap)
.build();
List<AdWithBid> adsWithBid =
mAdBiddingOutcomeList.stream()
.map(a -> a.getAdWithBid())
.collect(Collectors.toList());
List<SignedContextualAds> signedContextualAds =
mAdSelectionConfig.getBuyerSignedContextualAds().values().stream()
.collect(Collectors.toList());
List<AdWithBid> contextualBidAds = new ArrayList<>();
for (SignedContextualAds ctx : signedContextualAds) {
contextualBidAds.addAll(ctx.getAdsWithBid());
}
adsWithBid.addAll(contextualBidAds);
Answer<ListenableFuture<List<ScoreAdResult>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(
scores.stream()
.map(
score ->
ScoreAdResult.builder()
.setAdScore(score)
.build())
.collect(Collectors.toList()));
};
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,
mDebugReporting,
false);
Answer<ListenableFuture<List<ScoreAdResult>>> loggerAnswer =
unused -> {
mAdSelectionExecutionLogger.startScoreAds();
mAdSelectionExecutionLogger.endScoreAds();
return Futures.immediateFuture(
scores.stream()
.map(
score ->
ScoreAdResult.builder()
.setAdScore(score)
.build())
.collect(Collectors.toList()));
};
Mockito.when(
mMockAdSelectionScriptEngine.scoreAds(
differentSellerDecisionLogicJs,
mAdBiddingOutcomeList.stream()
.map(AdBiddingOutcome::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,
mDebugReporting,
false);
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 void validateCustomAudienceSignals(
CustomAudienceSignals signals, AdTechIdentifier buyer) {
assertEquals(CONTEXTUAL_CA_NAME, signals.getName());
assertEquals(buyer.toString(), signals.getOwner());
assertEquals(buyer, signals.getBuyer());
assertEquals(AdSelectionSignals.EMPTY, signals.getUserBiddingSignals());
}
private Map<AdTechIdentifier, SignedContextualAds> createContextualAds() {
Map<AdTechIdentifier, SignedContextualAds> buyerContextualAds = new HashMap<>();
AdTechIdentifier buyer1 = BUYER_1;
SignedContextualAds contextualAds1 =
SignedContextualAdsFixture.generateSignedContextualAds(
buyer1, ImmutableList.of(100.0, 200.0, 300.0))
.build();
AdTechIdentifier buyer2 = CommonFixture.VALID_BUYER_2;
SignedContextualAds contextualAds2 =
SignedContextualAdsFixture.generateSignedContextualAds(
buyer2, 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 false;
}
@Override
public long getAdSelectionScoringTimeoutMs() {
return EXTENDED_FLEDGE_AD_SELECTION_SCORING_TIMEOUT_MS;
}
@Override
public long getAdSelectionOverallTimeoutMs() {
return EXTENDED_FLEDGE_AD_SELECTION_OVERALL_TIMEOUT_MS;
}
}
}