• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2022 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.adservices.service.adselection;
18 
19 import static android.adservices.adselection.AdSelectionFromOutcomesConfigFixture.SAMPLE_SELLER;
20 import static android.adservices.common.AdServicesStatusUtils.STATUS_INVALID_ARGUMENT;
21 import static android.adservices.common.AdServicesStatusUtils.STATUS_TIMEOUT;
22 import static android.adservices.common.AdServicesStatusUtils.STATUS_USER_CONSENT_REVOKED;
23 
24 import static com.android.adservices.common.CommonFlagsValues.EXTENDED_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS;
25 import static com.android.adservices.common.CommonFlagsValues.EXTENDED_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS;
26 import static com.android.adservices.service.FlagsConstants.KEY_DISABLE_FLEDGE_ENROLLMENT_CHECK;
27 import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS;
28 import static com.android.adservices.service.FlagsConstants.KEY_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS;
29 import static com.android.adservices.service.stats.AdServicesStatsLog.AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES;
30 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doAnswer;
31 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doNothing;
32 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doThrow;
33 import static com.android.dx.mockito.inline.extended.ExtendedMockito.eq;
34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
35 
36 import static com.google.common.truth.Truth.assertWithMessage;
37 
38 import static org.mockito.ArgumentMatchers.any;
39 import static org.mockito.ArgumentMatchers.anyInt;
40 import static org.mockito.ArgumentMatchers.argThat;
41 import static org.mockito.Mockito.never;
42 
43 import android.adservices.adselection.AdSelectionCallback;
44 import android.adservices.adselection.AdSelectionFromOutcomesConfig;
45 import android.adservices.adselection.AdSelectionFromOutcomesConfigFixture;
46 import android.adservices.adselection.AdSelectionFromOutcomesInput;
47 import android.adservices.adselection.AdSelectionResponse;
48 import android.adservices.adselection.CustomAudienceSignalsFixture;
49 import android.adservices.common.CommonFixture;
50 import android.adservices.common.FledgeErrorResponse;
51 import android.annotation.NonNull;
52 import android.net.Uri;
53 import android.os.Process;
54 import android.os.RemoteException;
55 
56 import androidx.room.Room;
57 import androidx.test.core.app.ApplicationProvider;
58 
59 import com.android.adservices.common.AdServicesExtendedMockitoTestCase;
60 import com.android.adservices.concurrency.AdServicesExecutors;
61 import com.android.adservices.data.adselection.AdSelectionDatabase;
62 import com.android.adservices.data.adselection.AdSelectionEntryDao;
63 import com.android.adservices.data.adselection.CustomAudienceSignals;
64 import com.android.adservices.data.adselection.DBAdSelection;
65 import com.android.adservices.data.adselection.datahandlers.AdSelectionResultBidAndUri;
66 import com.android.adservices.service.DebugFlags;
67 import com.android.adservices.service.Flags;
68 import com.android.adservices.service.FlagsFactory;
69 import com.android.adservices.service.common.AdSelectionServiceFilter;
70 import com.android.adservices.service.common.Throttler;
71 import com.android.adservices.service.consent.ConsentManager;
72 import com.android.adservices.service.devapi.DevContext;
73 import com.android.adservices.service.exception.FilterException;
74 import com.android.adservices.service.stats.AdServicesLogger;
75 import com.android.adservices.service.stats.AdServicesLoggerImpl;
76 import com.android.adservices.service.stats.SelectAdsFromOutcomesExecutionLogger;
77 import com.android.adservices.shared.testing.SkipLoggingUsageRule;
78 import com.android.adservices.shared.testing.annotations.SetFlagTrue;
79 import com.android.adservices.shared.testing.annotations.SetLongFlag;
80 import com.android.dx.mockito.inline.extended.ExtendedMockito;
81 import com.android.modules.utils.testing.ExtendedMockitoRule.SpyStatic;
82 
83 import com.google.common.util.concurrent.ListenableFuture;
84 import com.google.common.util.concurrent.ListeningExecutorService;
85 
86 import org.junit.Before;
87 import org.junit.Test;
88 import org.mockito.ArgumentMatcher;
89 import org.mockito.Mock;
90 
91 import java.time.Instant;
92 import java.util.HashSet;
93 import java.util.List;
94 import java.util.concurrent.CountDownLatch;
95 import java.util.stream.Collectors;
96 
97 @SpyStatic(FlagsFactory.class)
98 @SpyStatic(DebugFlags.class)
99 @SetLongFlag(
100         name = KEY_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS,
101         value = EXTENDED_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS)
102 @SetLongFlag(
103         name = KEY_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS,
104         value = EXTENDED_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS)
105 // TODO (b/384952360): refine CEL related verifications later
106 @SkipLoggingUsageRule(reason = "b/384952360")
107 public final class OutcomeSelectionRunnerTest extends AdServicesExtendedMockitoTestCase {
108     private static final int CALLER_UID = Process.myUid();
109     private static final String MY_APP_PACKAGE_NAME = CommonFixture.TEST_PACKAGE_NAME;
110     private static final String ANOTHER_CALLER_PACKAGE_NAME = "another.caller.package";
111     private static final Uri RENDER_URI_1 = Uri.parse("https://www.domain.com/advert1/");
112     private static final Uri RENDER_URI_2 = Uri.parse("https://www.domain.com/advert2/");
113     private static final Uri RENDER_URI_3 = Uri.parse("https://www.domain.com/advert3/");
114     private static final long AD_SELECTION_ID_1 = 1;
115     private static final long AD_SELECTION_ID_2 = 2;
116     private static final long AD_SELECTION_ID_3 = 3;
117     private static final double BID_1 = 10.0;
118     private static final double BID_2 = 20.0;
119     private static final double BID_3 = 30.0;
120     private static final AdSelectionResultBidAndUri AD_SELECTION_WITH_BID_1 =
121             AdSelectionResultBidAndUri.builder()
122                     .setAdSelectionId(AD_SELECTION_ID_1)
123                     .setWinningAdBid(BID_1)
124                     .setWinningAdRenderUri(RENDER_URI_1)
125                     .build();
126     private static final AdSelectionResultBidAndUri AD_SELECTION_WITH_BID_2 =
127             AdSelectionResultBidAndUri.builder()
128                     .setAdSelectionId(AD_SELECTION_ID_2)
129                     .setWinningAdBid(BID_2)
130                     .setWinningAdRenderUri(RENDER_URI_2)
131                     .build();
132     private static final AdSelectionResultBidAndUri AD_SELECTION_WITH_BID_3 =
133             AdSelectionResultBidAndUri.builder()
134                     .setAdSelectionId(AD_SELECTION_ID_3)
135                     .setWinningAdBid(BID_3)
136                     .setWinningAdRenderUri(RENDER_URI_3)
137                     .build();
138 
139     private AdSelectionEntryDao mAdSelectionEntryDao;
140     @Mock private AdOutcomeSelector mAdOutcomeSelectorMock;
141     private OutcomeSelectionRunner mOutcomeSelectionRunner;
142     private final AdServicesLogger mAdServicesLoggerMock =
143             ExtendedMockito.mock(AdServicesLoggerImpl.class);
144     private ListeningExecutorService mBlockingExecutorService;
145 
146     @Mock private AdSelectionServiceFilter mAdSelectionServiceFilter;
147     @Mock private SelectAdsFromOutcomesExecutionLogger mSelectAdsFromOutcomesExecutionLoggerMock;
148 
149     @Before
setup()150     public void setup() {
151         mBlockingExecutorService = AdServicesExecutors.getBlockingExecutor();
152 
153         mAdSelectionEntryDao =
154                 Room.inMemoryDatabaseBuilder(
155                                 ApplicationProvider.getApplicationContext(),
156                                 AdSelectionDatabase.class)
157                         .build()
158                         .adSelectionEntryDao();
159         mocker.mockGetDebugFlags(mFakeDebugFlags);
160         mOutcomeSelectionRunner =
161                 new OutcomeSelectionRunner(
162                         CALLER_UID,
163                         mAdOutcomeSelectorMock,
164                         mAdSelectionEntryDao,
165                         mBlockingExecutorService,
166                         AdServicesExecutors.getLightWeightExecutor(),
167                         AdServicesExecutors.getScheduler(),
168                         mAdServicesLoggerMock,
169                         mContext,
170                         mFakeFlags,
171                         mFakeDebugFlags,
172                         mAdSelectionServiceFilter,
173                         DevContext.createForDevOptionsDisabled(),
174                         false);
175         doNothing()
176                 .when(mAdSelectionServiceFilter)
177                 .filterRequest(
178                         SAMPLE_SELLER,
179                         MY_APP_PACKAGE_NAME,
180                         true,
181                         true,
182                         true,
183                         CALLER_UID,
184                         AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES,
185                         Throttler.ApiKey.FLEDGE_API_SELECT_ADS,
186                         DevContext.createForDevOptionsDisabled());
187     }
188 
189     @Test
testRunOutcomeSelectionInvalidAdSelectionConfigFromOutcomes()190     public void testRunOutcomeSelectionInvalidAdSelectionConfigFromOutcomes() {
191         List<AdSelectionResultBidAndUri> adSelectionIdWithBidAndRenderUris =
192                 List.of(AD_SELECTION_WITH_BID_1, AD_SELECTION_WITH_BID_2, AD_SELECTION_WITH_BID_3);
193         persistAdSelectionEntry(adSelectionIdWithBidAndRenderUris.get(0), MY_APP_PACKAGE_NAME);
194         // Not persisting index 1
195         // Persisting index 2 with a different package name
196         persistAdSelectionEntry(
197                 adSelectionIdWithBidAndRenderUris.get(2), ANOTHER_CALLER_PACKAGE_NAME);
198 
199         List<Long> adOutcomesConfigParam =
200                 adSelectionIdWithBidAndRenderUris.stream()
201                         .map(AdSelectionResultBidAndUri::getAdSelectionId)
202                         .collect(Collectors.toList());
203 
204         AdSelectionFromOutcomesConfig config =
205                 AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig(
206                         adOutcomesConfigParam);
207 
208         AdSelectionTestCallback resultsCallback =
209                 invokeRunAdSelectionFromOutcomes(
210                         mOutcomeSelectionRunner,
211                         config,
212                         MY_APP_PACKAGE_NAME,
213                         mSelectAdsFromOutcomesExecutionLoggerMock);
214 
215         var unused =
216                 verify(mAdOutcomeSelectorMock, never()).runAdOutcomeSelector(any(), any(), any());
217         expect.that(resultsCallback.mIsSuccess).isFalse();
218         expect.that(STATUS_INVALID_ARGUMENT)
219                 .isEqualTo(resultsCallback.mFledgeErrorResponse.getStatusCode());
220         verify(mAdServicesLoggerMock)
221                 .logFledgeApiCallStats(
222                         eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES),
223                         eq(MY_APP_PACKAGE_NAME),
224                         eq(STATUS_INVALID_ARGUMENT),
225                         anyInt());
226     }
227 
228     @Test
testRunOutcomeSelectionRevokedUserConsentEmptyResult()229     public void testRunOutcomeSelectionRevokedUserConsentEmptyResult() {
230         doThrow(new FilterException(new ConsentManager.RevokedConsentException()))
231                 .when(mAdSelectionServiceFilter)
232                 .filterRequest(
233                         SAMPLE_SELLER,
234                         MY_APP_PACKAGE_NAME,
235                         true,
236                         true,
237                         true,
238                         CALLER_UID,
239                         AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES,
240                         Throttler.ApiKey.FLEDGE_API_SELECT_ADS,
241                         DevContext.createForDevOptionsDisabled());
242 
243         List<AdSelectionResultBidAndUri> adSelectionIdWithBidAndRenderUris =
244                 List.of(AD_SELECTION_WITH_BID_1, AD_SELECTION_WITH_BID_2, AD_SELECTION_WITH_BID_3);
245         for (AdSelectionResultBidAndUri idWithBid : adSelectionIdWithBidAndRenderUris) {
246             persistAdSelectionEntry(idWithBid, MY_APP_PACKAGE_NAME);
247         }
248 
249         List<Long> adOutcomesConfigParam =
250                 adSelectionIdWithBidAndRenderUris.stream()
251                         .map(AdSelectionResultBidAndUri::getAdSelectionId)
252                         .collect(Collectors.toList());
253 
254         AdSelectionFromOutcomesConfig config =
255                 AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig(
256                         adOutcomesConfigParam);
257 
258         AdSelectionTestCallback resultsCallback =
259                 invokeRunAdSelectionFromOutcomes(
260                         mOutcomeSelectionRunner,
261                         config,
262                         MY_APP_PACKAGE_NAME,
263                         mSelectAdsFromOutcomesExecutionLoggerMock);
264 
265         var unused =
266                 verify(mAdOutcomeSelectorMock, never()).runAdOutcomeSelector(any(), any(), any());
267         expect.that(resultsCallback.mIsSuccess).isTrue();
268         expect.that(resultsCallback.mAdSelectionResponse).isNull();
269 
270         // Confirm a duplicate log entry does not exist.
271         // AdSelectionServiceFilter ensures the failing assertion is logged internally.
272         verify(mAdServicesLoggerMock, never())
273                 .logFledgeApiCallStats(
274                         eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES),
275                         eq(MY_APP_PACKAGE_NAME),
276                         eq(STATUS_USER_CONSENT_REVOKED),
277                         anyInt());
278     }
279 
280     @Test
testRunOutcomeSelectionRevokedUserConsentEmptyResult_UXNotificationNotEnforced()281     public void testRunOutcomeSelectionRevokedUserConsentEmptyResult_UXNotificationNotEnforced() {
282         mockGetConsentNotificationDebugMode(true);
283 
284         doThrow(new FilterException(new ConsentManager.RevokedConsentException()))
285                 .when(mAdSelectionServiceFilter)
286                 .filterRequest(
287                         SAMPLE_SELLER,
288                         MY_APP_PACKAGE_NAME,
289                         true,
290                         true,
291                         false,
292                         CALLER_UID,
293                         AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES,
294                         Throttler.ApiKey.FLEDGE_API_SELECT_ADS,
295                         DevContext.createForDevOptionsDisabled());
296 
297         List<AdSelectionResultBidAndUri> adSelectionIdWithBidAndRenderUris =
298                 List.of(AD_SELECTION_WITH_BID_1, AD_SELECTION_WITH_BID_2, AD_SELECTION_WITH_BID_3);
299         for (AdSelectionResultBidAndUri idWithBid : adSelectionIdWithBidAndRenderUris) {
300             persistAdSelectionEntry(idWithBid, MY_APP_PACKAGE_NAME);
301         }
302 
303         List<Long> adOutcomesConfigParam =
304                 adSelectionIdWithBidAndRenderUris.stream()
305                         .map(AdSelectionResultBidAndUri::getAdSelectionId)
306                         .collect(Collectors.toList());
307 
308         AdSelectionFromOutcomesConfig config =
309                 AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig(
310                         adOutcomesConfigParam);
311 
312         OutcomeSelectionRunner outcomeSelectionRunner =
313                 new OutcomeSelectionRunner(
314                         CALLER_UID,
315                         mAdOutcomeSelectorMock,
316                         mAdSelectionEntryDao,
317                         mBlockingExecutorService,
318                         AdServicesExecutors.getLightWeightExecutor(),
319                         AdServicesExecutors.getScheduler(),
320                         mAdServicesLoggerMock,
321                         mContext,
322                         mFakeFlags,
323                         mFakeDebugFlags,
324                         mAdSelectionServiceFilter,
325                         DevContext.createForDevOptionsDisabled(),
326                         false);
327 
328         AdSelectionTestCallback resultsCallback =
329                 invokeRunAdSelectionFromOutcomes(
330                         outcomeSelectionRunner,
331                         config,
332                         MY_APP_PACKAGE_NAME,
333                         mSelectAdsFromOutcomesExecutionLoggerMock);
334 
335         var unused =
336                 verify(mAdOutcomeSelectorMock, never()).runAdOutcomeSelector(any(), any(), any());
337         expect.that(resultsCallback.mIsSuccess).isTrue();
338         expect.that(resultsCallback.mAdSelectionResponse).isNull();
339 
340         // Confirm a duplicate log entry does not exist.
341         // AdSelectionServiceFilter ensures the failing assertion is logged internally.
342         verify(mAdServicesLoggerMock, never())
343                 .logFledgeApiCallStats(
344                         eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES),
345                         eq(MY_APP_PACKAGE_NAME),
346                         eq(STATUS_USER_CONSENT_REVOKED),
347                         anyInt());
348 
349         verify(mAdSelectionServiceFilter)
350                 .filterRequest(
351                         SAMPLE_SELLER,
352                         MY_APP_PACKAGE_NAME,
353                         true,
354                         true,
355                         false,
356                         CALLER_UID,
357                         AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES,
358                         Throttler.ApiKey.FLEDGE_API_SELECT_ADS,
359                         DevContext.createForDevOptionsDisabled());
360     }
361 
362     @Test
363     @SetLongFlag(name = KEY_FLEDGE_AD_SELECTION_SELECTING_OUTCOME_TIMEOUT_MS, value = 300)
364     @SetLongFlag(name = KEY_FLEDGE_AD_SELECTION_FROM_OUTCOMES_OVERALL_TIMEOUT_MS, value = 100)
365     @SetFlagTrue(KEY_DISABLE_FLEDGE_ENROLLMENT_CHECK)
testRunOutcomeSelectionOrchestrationTimeoutFailure()366     public void testRunOutcomeSelectionOrchestrationTimeoutFailure() {
367         List<AdSelectionResultBidAndUri> adSelectionIdWithBidAndRenderUris =
368                 List.of(AD_SELECTION_WITH_BID_1, AD_SELECTION_WITH_BID_2, AD_SELECTION_WITH_BID_3);
369         for (AdSelectionResultBidAndUri idWithBid : adSelectionIdWithBidAndRenderUris) {
370             persistAdSelectionEntry(idWithBid, MY_APP_PACKAGE_NAME);
371         }
372 
373         List<Long> adOutcomesConfigParam =
374                 adSelectionIdWithBidAndRenderUris.stream()
375                         .map(AdSelectionResultBidAndUri::getAdSelectionId)
376                         .collect(Collectors.toList());
377 
378         AdSelectionFromOutcomesConfig config =
379                 AdSelectionFromOutcomesConfigFixture.anAdSelectionFromOutcomesConfig(
380                         adOutcomesConfigParam);
381 
382         GenericListMatcher matcher = new GenericListMatcher(adSelectionIdWithBidAndRenderUris);
383         doAnswer((ignored) -> getSelectedOutcomeWithDelay(AD_SELECTION_ID_1, mFakeFlags))
384                 .when(mAdOutcomeSelectorMock)
385                 .runAdOutcomeSelector(
386                         argThat(matcher),
387                         eq(config),
388                         eq(mSelectAdsFromOutcomesExecutionLoggerMock));
389 
390         OutcomeSelectionRunner outcomeSelectionRunner =
391                 new OutcomeSelectionRunner(
392                         CALLER_UID,
393                         mAdOutcomeSelectorMock,
394                         mAdSelectionEntryDao,
395                         mBlockingExecutorService,
396                         AdServicesExecutors.getLightWeightExecutor(),
397                         AdServicesExecutors.getScheduler(),
398                         mAdServicesLoggerMock,
399                         mContext,
400                         mFakeFlags,
401                         mFakeDebugFlags,
402                         mAdSelectionServiceFilter,
403                         DevContext.createForDevOptionsDisabled(),
404                         false);
405 
406         AdSelectionTestCallback resultsCallback =
407                 invokeRunAdSelectionFromOutcomes(
408                         outcomeSelectionRunner,
409                         config,
410                         MY_APP_PACKAGE_NAME,
411                         mSelectAdsFromOutcomesExecutionLoggerMock);
412 
413         var unused = verify(mAdOutcomeSelectorMock).runAdOutcomeSelector(any(), any(), any());
414         expect.that(resultsCallback.mIsSuccess).isFalse();
415         assertWithMessage("mFledgeErrorResponse")
416                 .that(resultsCallback.mFledgeErrorResponse)
417                 .isNotNull();
418         expect.that(STATUS_TIMEOUT).isEqualTo(resultsCallback.mFledgeErrorResponse.getStatusCode());
419         verify(mAdServicesLoggerMock)
420                 .logFledgeApiCallStats(
421                         eq(AD_SERVICES_API_CALLED__API_NAME__SELECT_ADS_FROM_OUTCOMES),
422                         eq(MY_APP_PACKAGE_NAME),
423                         eq(STATUS_TIMEOUT),
424                         anyInt());
425     }
426 
persistAdSelectionEntry( AdSelectionResultBidAndUri idWithBidAndRenderUri, String callerPackageName)427     private void persistAdSelectionEntry(
428             AdSelectionResultBidAndUri idWithBidAndRenderUri, String callerPackageName) {
429         final Uri biddingLogicUri1 = Uri.parse("https://www.domain.com/logic/1");
430         final Instant activationTime = Instant.now();
431         final String contextualSignals = "contextual_signals";
432         final CustomAudienceSignals customAudienceSignals =
433                 CustomAudienceSignalsFixture.aCustomAudienceSignals();
434 
435         final DBAdSelection dbAdSelectionEntry =
436                 new DBAdSelection.Builder()
437                         .setAdSelectionId(idWithBidAndRenderUri.getAdSelectionId())
438                         .setCustomAudienceSignals(customAudienceSignals)
439                         .setBuyerContextualSignals(contextualSignals)
440                         .setBiddingLogicUri(biddingLogicUri1)
441                         .setWinningAdRenderUri(idWithBidAndRenderUri.getWinningAdRenderUri())
442                         .setWinningAdBid(idWithBidAndRenderUri.getWinningAdBid())
443                         .setCreationTimestamp(activationTime)
444                         .setCallerPackageName(callerPackageName)
445                         .build();
446         mAdSelectionEntryDao.persistAdSelection(dbAdSelectionEntry);
447     }
448 
invokeRunAdSelectionFromOutcomes( OutcomeSelectionRunner outcomeSelectionRunner, AdSelectionFromOutcomesConfig config, String callerPackageName, SelectAdsFromOutcomesExecutionLogger selectAdsFromOutcomesExecutionLogger)449     private OutcomeSelectionRunnerTest.AdSelectionTestCallback invokeRunAdSelectionFromOutcomes(
450             OutcomeSelectionRunner outcomeSelectionRunner,
451             AdSelectionFromOutcomesConfig config,
452             String callerPackageName,
453             SelectAdsFromOutcomesExecutionLogger selectAdsFromOutcomesExecutionLogger) {
454 
455         // Counted down in the callback
456         CountDownLatch countDownLatch = new CountDownLatch(1);
457         OutcomeSelectionRunnerTest.AdSelectionTestCallback adSelectionTestCallback =
458                 new OutcomeSelectionRunnerTest.AdSelectionTestCallback(countDownLatch);
459 
460         AdSelectionFromOutcomesInput input =
461                 new AdSelectionFromOutcomesInput.Builder()
462                         .setAdSelectionFromOutcomesConfig(config)
463                         .setCallerPackageName(callerPackageName)
464                         .build();
465 
466         outcomeSelectionRunner.runOutcomeSelection(
467                 input, adSelectionTestCallback, selectAdsFromOutcomesExecutionLogger);
468         try {
469             adSelectionTestCallback.mCountDownLatch.await();
470         } catch (InterruptedException e) {
471             e.printStackTrace();
472         }
473         return adSelectionTestCallback;
474     }
475 
getSelectedOutcomeWithDelay( Long outcomeId, @NonNull Flags flags)476     private ListenableFuture<Long> getSelectedOutcomeWithDelay(
477             Long outcomeId, @NonNull Flags flags) {
478         return mBlockingExecutorService.submit(
479                 () -> {
480                     Thread.sleep(2 * flags.getAdSelectionFromOutcomesOverallTimeoutMs());
481                     return outcomeId;
482                 });
483     }
484 
485     private static final class AdSelectionTestCallback extends AdSelectionCallback.Stub {
486 
487         final CountDownLatch mCountDownLatch;
488         boolean mIsSuccess = false;
489         AdSelectionResponse mAdSelectionResponse;
490         FledgeErrorResponse mFledgeErrorResponse;
491 
AdSelectionTestCallback(CountDownLatch countDownLatch)492         AdSelectionTestCallback(CountDownLatch countDownLatch) {
493             mCountDownLatch = countDownLatch;
494             mAdSelectionResponse = null;
495             mFledgeErrorResponse = null;
496         }
497 
498         @Override
onSuccess(AdSelectionResponse adSelectionResponse)499         public void onSuccess(AdSelectionResponse adSelectionResponse) throws RemoteException {
500             mIsSuccess = true;
501             mAdSelectionResponse = adSelectionResponse;
502             mCountDownLatch.countDown();
503         }
504 
505         @Override
onFailure(FledgeErrorResponse fledgeErrorResponse)506         public void onFailure(FledgeErrorResponse fledgeErrorResponse) throws RemoteException {
507             mIsSuccess = false;
508             mFledgeErrorResponse = fledgeErrorResponse;
509             mCountDownLatch.countDown();
510         }
511     }
512 
513     private static final class GenericListMatcher
514             implements ArgumentMatcher<List<AdSelectionResultBidAndUri>> {
515         private final List<AdSelectionResultBidAndUri> mTruth;
516 
GenericListMatcher(List<AdSelectionResultBidAndUri> truth)517         GenericListMatcher(List<AdSelectionResultBidAndUri> truth) {
518             this.mTruth = truth;
519         }
520 
521         @Override
matches(List<AdSelectionResultBidAndUri> argument)522         public boolean matches(List<AdSelectionResultBidAndUri> argument) {
523             return mTruth.size() == argument.size()
524                     && new HashSet<>(mTruth).equals(new HashSet<>(argument));
525         }
526     }
527 }
528