summaryrefslogtreecommitdiff
path: root/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/PerBuyerBiddingRunnerTest.java
diff options
context:
space:
mode:
Diffstat (limited to 'adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/PerBuyerBiddingRunnerTest.java')
-rw-r--r--adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/PerBuyerBiddingRunnerTest.java232
1 files changed, 232 insertions, 0 deletions
diff --git a/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/PerBuyerBiddingRunnerTest.java b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/PerBuyerBiddingRunnerTest.java
new file mode 100644
index 0000000000..4d7ecc316c
--- /dev/null
+++ b/adservices/tests/unittest/service-core/src/com/android/adservices/service/adselection/PerBuyerBiddingRunnerTest.java
@@ -0,0 +1,232 @@
+/*
+ * 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 android.adservices.adselection.AdSelectionConfig;
+import android.adservices.adselection.AdSelectionConfigFixture;
+import android.adservices.common.AdSelectionSignals;
+import android.adservices.common.AdTechIdentifier;
+import android.adservices.common.CommonFixture;
+import android.adservices.customaudience.CustomAudienceFixture;
+import android.adservices.customaudience.TrustedBiddingDataFixture;
+import android.net.Uri;
+
+import com.android.adservices.concurrency.AdServicesExecutors;
+import com.android.adservices.data.common.DBAdData;
+import com.android.adservices.data.customaudience.DBCustomAudience;
+import com.android.adservices.data.customaudience.DBTrustedBiddingData;
+import com.android.dx.mockito.inline.extended.ExtendedMockito;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.util.concurrent.ListenableFuture;
+import com.google.common.util.concurrent.ListeningExecutorService;
+import com.google.common.util.concurrent.MoreExecutors;
+
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.mockito.ArgumentMatcher;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+
+public class PerBuyerBiddingRunnerTest {
+
+ private static final String TEST_BUYER = "buyer";
+ private static final String AD_URI_PREFIX = "http://www.domain.com/adverts/123/";
+ private static final String FAST_SUFFIX = "FAST";
+ private static final String SLOW_SUFFIX = "SLOW";
+ private static final long SHORT_SLEEP_MS = 10L;
+ private static final long LONG_SLEEP_MS = 10000L;
+ private static final long PER_BUYER_TIMEOUT_MS = 1000L;
+ private static final AdSelectionConfig AD_SELECTION_CONFIG =
+ AdSelectionConfigFixture.anAdSelectionConfigBuilder().build();
+
+ AdTechIdentifier mBuyer;
+ List<DBCustomAudience> mDBCustomAudienceList;
+
+ @Mock AdBidGenerator mAdBidGeneratorMock;
+ @Mock AdBiddingOutcome mAdBiddingOutcome;
+
+ private PerBuyerBiddingRunner mPerBuyerBiddingRunner;
+ private ScheduledThreadPoolExecutor mScheduledExecutor = AdServicesExecutors.getScheduler();
+ private ListeningExecutorService mBackgroundExecutorService =
+ AdServicesExecutors.getBackgroundExecutor();
+ private ListeningExecutorService mExecutor =
+ MoreExecutors.listeningDecorator((AdServicesExecutors.getLightWeightExecutor()));
+
+ private ResponseMatcher mSlowResponseMatcher = new ResponseMatcher(SLOW_SUFFIX);
+ private ResponseMatcher mFastResponseMatcher = new ResponseMatcher(FAST_SUFFIX);
+
+ @Before
+ public void setup() {
+ MockitoAnnotations.initMocks(this);
+ mBuyer = AdTechIdentifier.fromString(TEST_BUYER);
+ mDBCustomAudienceList = new ArrayList<>();
+ mPerBuyerBiddingRunner =
+ new PerBuyerBiddingRunner(
+ mAdBidGeneratorMock, mScheduledExecutor, mBackgroundExecutorService);
+
+ ExtendedMockito.doReturn(createDelayedBiddingOutcome(SHORT_SLEEP_MS))
+ .when(mAdBidGeneratorMock)
+ .runAdBiddingPerCA(
+ ExtendedMockito.argThat(mFastResponseMatcher),
+ ExtendedMockito.any(AdSelectionSignals.class),
+ ExtendedMockito.any(AdSelectionSignals.class),
+ ExtendedMockito.any(AdSelectionSignals.class));
+
+ ExtendedMockito.doReturn(createDelayedBiddingOutcome(LONG_SLEEP_MS))
+ .when(mAdBidGeneratorMock)
+ .runAdBiddingPerCA(
+ ExtendedMockito.argThat(mSlowResponseMatcher),
+ ExtendedMockito.any(AdSelectionSignals.class),
+ ExtendedMockito.any(AdSelectionSignals.class),
+ ExtendedMockito.any(AdSelectionSignals.class));
+ }
+
+ @Test
+ public void testPerBuyerBidding_AllCASucceed() throws InterruptedException {
+ int numSlowCustomAudiences = 0;
+ int numFastCustomAudiences = 20;
+ runAndValidatePerBuyerBidding(numSlowCustomAudiences, numFastCustomAudiences);
+ }
+
+ @Test
+ public void testPerBuyerBidding_PartialCATimeout() throws InterruptedException {
+ int numSlowCustomAudiences = 10;
+ int numFastCustomAudiences = 10;
+ runAndValidatePerBuyerBidding(numSlowCustomAudiences, numFastCustomAudiences);
+ }
+
+ @Test
+ public void testPerBuyerBidding_AllCATimeout() throws InterruptedException {
+ int numSlowCustomAudiences = 20;
+ int numFastCustomAudiences = 0;
+ runAndValidatePerBuyerBidding(numSlowCustomAudiences, numFastCustomAudiences);
+ }
+
+ private void runAndValidatePerBuyerBidding(
+ int numSlowCustomAudiences, int numFastCustomAudiences) throws InterruptedException {
+ List<DBCustomAudience> slowCustomAudiences =
+ createCustomAudienceList(numSlowCustomAudiences, SLOW_SUFFIX);
+ List<DBCustomAudience> fastCustomAudiences =
+ createCustomAudienceList(numFastCustomAudiences, FAST_SUFFIX);
+
+ List<DBCustomAudience> customAudienceList = new ArrayList<>();
+ customAudienceList.addAll(fastCustomAudiences);
+ customAudienceList.addAll(slowCustomAudiences);
+
+ List<ListenableFuture<AdBiddingOutcome>> biddingOutcomes =
+ mPerBuyerBiddingRunner.runBidding(
+ mBuyer, customAudienceList, PER_BUYER_TIMEOUT_MS, AD_SELECTION_CONFIG);
+
+ Thread.sleep(2 * PER_BUYER_TIMEOUT_MS);
+
+ int completedBids = 0;
+ int cancelledIncompleteBids = 0;
+
+ for (ListenableFuture<AdBiddingOutcome> bidOutcome : biddingOutcomes) {
+ if (bidOutcome.isCancelled()) {
+ cancelledIncompleteBids++;
+ } else {
+ completedBids++;
+ }
+ }
+
+ Assert.assertEquals(
+ "Number of timed out bids, does not match cancelled bids",
+ numSlowCustomAudiences,
+ cancelledIncompleteBids);
+ Assert.assertEquals(
+ "Number of completed bids, does not match successful bids",
+ numFastCustomAudiences,
+ completedBids);
+ }
+
+ private ListenableFuture<AdBiddingOutcome> createDelayedBiddingOutcome(long delayTime) {
+ return mExecutor.submit(
+ () -> {
+ Thread.sleep(delayTime);
+ return mAdBiddingOutcome;
+ });
+ }
+
+ private List<DBCustomAudience> createCustomAudienceList(int count, String suffix) {
+ List<DBCustomAudience> customAudienceList = new ArrayList<>();
+ for (int i = 1; i <= count; i++) {
+ customAudienceList.add(createDBCustomAudience(mBuyer, String.valueOf(i), suffix));
+ }
+ return customAudienceList;
+ }
+
+ /**
+ * @return a real Custom Audience object that can be persisted and used in bidding and scoring
+ */
+ private DBCustomAudience createDBCustomAudience(
+ final AdTechIdentifier buyer, final String namePrefix, final String nameSuffix) {
+
+ // Generate ads for with bids provided
+ List<DBAdData> ads = new ArrayList<>();
+ List<Double> bids = ImmutableList.of(1.0, 2.0);
+ // Create ads with the buyer name and bid number as the ad URI
+ // Add the bid value to the metadata
+ for (int i = 0; i < bids.size(); i++) {
+ ads.add(
+ new DBAdData(
+ Uri.parse(AD_URI_PREFIX + buyer + "/ad" + (i + 1)),
+ "{\"result\":" + bids.get(i) + "}"));
+ }
+
+ return new DBCustomAudience.Builder()
+ .setOwner(buyer + CustomAudienceFixture.VALID_OWNER)
+ .setBuyer(buyer)
+ .setName(
+ buyer.toString()
+ + namePrefix
+ + CustomAudienceFixture.VALID_NAME
+ + nameSuffix)
+ .setActivationTime(CustomAudienceFixture.VALID_ACTIVATION_TIME)
+ .setExpirationTime(CustomAudienceFixture.VALID_EXPIRATION_TIME)
+ .setCreationTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI)
+ .setLastAdsAndBiddingDataUpdatedTime(CommonFixture.FIXED_NOW_TRUNCATED_TO_MILLI)
+ .setUserBiddingSignals(CustomAudienceFixture.VALID_USER_BIDDING_SIGNALS)
+ .setTrustedBiddingData(
+ new DBTrustedBiddingData.Builder()
+ .setUri(Uri.parse("https://www.example.com/trusted"))
+ .setKeys(TrustedBiddingDataFixture.getValidTrustedBiddingKeys())
+ .build())
+ .setBiddingLogicUri(Uri.parse("https://www.example.com/logic"))
+ .setAds(ads)
+ .build();
+ }
+
+ private static class ResponseMatcher implements ArgumentMatcher<DBCustomAudience> {
+ private String mSuffix;
+
+ ResponseMatcher(String suffix) {
+ this.mSuffix = suffix;
+ }
+
+ @Override
+ public boolean matches(DBCustomAudience right) {
+ return right.getName().endsWith(mSuffix);
+ }
+ }
+}