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 android.rubidium.js; 18 19 import static com.android.adservices.service.js.JSScriptArgument.arrayArg; 20 import static com.android.adservices.service.js.JSScriptArgument.jsonArg; 21 import static com.android.adservices.service.js.JSScriptArgument.numericArg; 22 import static com.android.adservices.service.js.JSScriptArgument.recordArg; 23 import static com.android.adservices.service.js.JSScriptArgument.stringArg; 24 import static com.android.adservices.service.js.JSScriptArgument.stringArrayArg; 25 26 import static com.google.common.truth.Truth.assertThat; 27 28 import static org.junit.Assume.assumeTrue; 29 30 import android.adservices.adselection.AdSelectionConfig; 31 import android.adservices.adselection.AdWithBid; 32 import android.adservices.common.AdData; 33 import android.adservices.common.AdSelectionSignals; 34 import android.adservices.common.AdTechIdentifier; 35 import android.annotation.SuppressLint; 36 import android.content.Context; 37 import android.net.Uri; 38 import android.perftests.utils.BenchmarkState; 39 import android.perftests.utils.PerfStatusReporter; 40 import android.util.Log; 41 42 import androidx.annotation.NonNull; 43 import androidx.test.core.app.ApplicationProvider; 44 import androidx.test.filters.MediumTest; 45 import androidx.test.runner.AndroidJUnit4; 46 47 import com.android.adservices.data.adselection.CustomAudienceSignals; 48 import com.android.adservices.service.adselection.AdCounterKeyCopier; 49 import com.android.adservices.service.adselection.AdCounterKeyCopierNoOpImpl; 50 import com.android.adservices.service.adselection.AdDataArgumentUtil; 51 import com.android.adservices.service.adselection.AdSelectionConfigArgumentUtil; 52 import com.android.adservices.service.adselection.AdWithBidArgumentUtil; 53 import com.android.adservices.service.adselection.CustomAudienceBiddingSignalsArgumentUtil; 54 import com.android.adservices.service.adselection.CustomAudienceScoringSignalsArgumentUtil; 55 import com.android.adservices.service.js.IsolateSettings; 56 import com.android.adservices.service.js.JSScriptArgument; 57 import com.android.adservices.service.js.JSScriptArrayArgument; 58 import com.android.adservices.service.js.JSScriptEngine; 59 import com.android.adservices.service.js.JSScriptRecordArgument; 60 import com.android.adservices.service.profiling.JSScriptEngineLogConstants; 61 import com.android.adservices.service.profiling.Profiler; 62 63 import com.google.common.collect.ImmutableList; 64 import com.google.common.collect.ImmutableMap; 65 import com.google.common.util.concurrent.ListenableFuture; 66 67 import org.json.JSONArray; 68 import org.json.JSONObject; 69 import org.junit.After; 70 import org.junit.Before; 71 import org.junit.Rule; 72 import org.junit.Test; 73 import org.junit.runner.RunWith; 74 75 import java.io.IOException; 76 import java.io.InputStream; 77 import java.nio.charset.StandardCharsets; 78 import java.time.Clock; 79 import java.time.Duration; 80 import java.time.Instant; 81 import java.time.ZoneOffset; 82 import java.util.ArrayList; 83 import java.util.Collections; 84 import java.util.List; 85 import java.util.Locale; 86 import java.util.Map; 87 import java.util.Objects; 88 import java.util.Random; 89 import java.util.concurrent.CountDownLatch; 90 import java.util.concurrent.ExecutorService; 91 import java.util.concurrent.Executors; 92 import java.util.concurrent.TimeUnit; 93 import java.util.stream.Collectors; 94 import java.util.stream.IntStream; 95 96 /** To run the unit tests for this class, run "atest RubidiumPerfTests:JSScriptEnginePerfTests" */ 97 @MediumTest 98 @RunWith(AndroidJUnit4.class) 99 public class JSScriptEnginePerfTests { 100 private static final String TAG = JSScriptEngine.TAG; 101 private static final Context sContext = ApplicationProvider.getApplicationContext(); 102 private static final ExecutorService sExecutorService = Executors.newFixedThreadPool(10); 103 104 private static final JSScriptEngine sJSScriptEngine = 105 JSScriptEngine.getInstanceForTesting( 106 sContext, Profiler.createInstance(JSScriptEngine.TAG)); 107 private static final Clock CLOCK = Clock.fixed(Instant.now(), ZoneOffset.UTC); 108 private static final Instant ACTIVATION_TIME = CLOCK.instant(); 109 private static final Instant EXPIRATION_TIME = CLOCK.instant().plus(Duration.ofDays(1)); 110 private static final AdSelectionSignals CONTEXTUAL_SIGNALS = AdSelectionSignals.EMPTY; 111 private static final AdCounterKeyCopier AD_COUNTER_KEY_COPIER_NO_OP = 112 new AdCounterKeyCopierNoOpImpl(); 113 114 private final AdDataArgumentUtil mAdDataArgumentUtil = 115 new AdDataArgumentUtil(AD_COUNTER_KEY_COPIER_NO_OP); 116 private final AdWithBidArgumentUtil mAdWithBidArgumentUtil = 117 new AdWithBidArgumentUtil(mAdDataArgumentUtil); 118 119 @Rule 120 public PerfStatusReporter mPerfStatusReporter = new PerfStatusReporter(); 121 122 @Before before()123 public void before() throws Exception { 124 // Warm up the sandbox env. 125 callJSEngine( 126 "function test() { return \"hello world\";" + " }", ImmutableList.of(), "test"); 127 } 128 129 @After after()130 public void after() { 131 sJSScriptEngine.shutdown(); 132 } 133 134 @Test evaluate_helloWorld()135 public void evaluate_helloWorld() throws Exception { 136 BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 137 while (state.keepRunning()) { 138 String res = 139 callJSEngine( 140 "function test() { return \"hello world\";" + " }", 141 ImmutableList.of(), 142 "test"); 143 assertThat(res).isEqualTo("\"hello world\""); 144 } 145 } 146 147 @Test evaluate_emptyGenerateBid()148 public void evaluate_emptyGenerateBid() throws Exception { 149 BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 150 state.pauseTiming(); 151 152 InputStream testJsInputStream = sContext.getAssets().open("empty_generate_bid.js"); 153 String jsTestFile = new String(testJsInputStream.readAllBytes(), StandardCharsets.UTF_8); 154 JSScriptArgument adDataArgument = 155 recordArg( 156 "ad", 157 stringArg("render_url", "http://google.com"), 158 recordArg("metadata", numericArg("input", 10))); 159 callJSEngine(jsTestFile, ImmutableList.of(adDataArgument), "generateBid"); 160 161 state.resumeTiming(); 162 while (state.keepRunning()) { 163 callJSEngine(jsTestFile, ImmutableList.of(adDataArgument), "generateBid"); 164 } 165 } 166 167 @Test evaluate_turtledoveSampleGenerateBid()168 public void evaluate_turtledoveSampleGenerateBid() throws Exception { 169 BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 170 state.pauseTiming(); 171 172 InputStream testJsInputStream = sContext.getAssets().open("turtledove_generate_bid.js"); 173 String jsTestFile = new String(testJsInputStream.readAllBytes(), StandardCharsets.UTF_8); 174 // Initialize the environment with one call. 175 callJSEngine(jsTestFile, ImmutableList.of(), "generateBid"); 176 177 state.resumeTiming(); 178 while (state.keepRunning()) { 179 callJSEngine(jsTestFile, ImmutableList.of(), "generateBid"); 180 } 181 } 182 183 @Test evaluate_turtledoveSampleGenerateBid_parametrized_10Ads()184 public void evaluate_turtledoveSampleGenerateBid_parametrized_10Ads() throws Exception { 185 runParametrizedTurtledoveScript(10); 186 } 187 188 @Test evaluate_turtledoveSampleGenerateBid_parametrized_25Ads()189 public void evaluate_turtledoveSampleGenerateBid_parametrized_25Ads() throws Exception { 190 runParametrizedTurtledoveScript(25); 191 } 192 193 @Test evaluate_turtledoveSampleGenerateBid_parametrized_50Ads()194 public void evaluate_turtledoveSampleGenerateBid_parametrized_50Ads() throws Exception { 195 runParametrizedTurtledoveScript(50); 196 } 197 198 @Test evaluate_turtledoveSampleGenerateBid_parametrized_75Ads()199 public void evaluate_turtledoveSampleGenerateBid_parametrized_75Ads() throws Exception { 200 runParametrizedTurtledoveScript(75); 201 } 202 203 @Test evaluate_rubidiumGenerateBid_parametrized_1Ad()204 public void evaluate_rubidiumGenerateBid_parametrized_1Ad() throws Exception { 205 runParameterizedRubidiumGenerateBid(1); 206 } 207 208 @Test evaluate_rubidiumGenerateBid_parametrized_10Ads()209 public void evaluate_rubidiumGenerateBid_parametrized_10Ads() throws Exception { 210 runParameterizedRubidiumGenerateBid(10); 211 } 212 213 @Test evaluate_rubidiumGenerateBid_parametrized_25Ads()214 public void evaluate_rubidiumGenerateBid_parametrized_25Ads() throws Exception { 215 runParameterizedRubidiumGenerateBid(25); 216 } 217 218 @Test evaluate_rubidiumGenerateBid_parametrized_50Ads()219 public void evaluate_rubidiumGenerateBid_parametrized_50Ads() throws Exception { 220 runParameterizedRubidiumGenerateBid(50); 221 } 222 223 @Test evaluate_rubidiumGenerateBid_parametrized_75Ads()224 public void evaluate_rubidiumGenerateBid_parametrized_75Ads() throws Exception { 225 runParameterizedRubidiumGenerateBid(75); 226 } 227 228 @Test evaluate_rubidiumScoreAd_parametrized_1Ad()229 public void evaluate_rubidiumScoreAd_parametrized_1Ad() throws Exception { 230 runParameterizedRubidiumScoreAd(1); 231 } 232 233 @Test evaluate_rubidiumScoreAd_parametrized_10Ads()234 public void evaluate_rubidiumScoreAd_parametrized_10Ads() throws Exception { 235 runParameterizedRubidiumScoreAd(10); 236 } 237 238 @Test evaluate_rubidiumScoreAd_parametrized_25Ads()239 public void evaluate_rubidiumScoreAd_parametrized_25Ads() throws Exception { 240 runParameterizedRubidiumScoreAd(25); 241 } 242 243 @Test evaluate_rubidiumScoreAd_parametrized_50Ads()244 public void evaluate_rubidiumScoreAd_parametrized_50Ads() throws Exception { 245 runParameterizedRubidiumScoreAd(50); 246 } 247 248 @Test evaluate_rubidiumScoreAd_parametrized_75Ads()249 public void evaluate_rubidiumScoreAd_parametrized_75Ads() throws Exception { 250 runParameterizedRubidiumScoreAd(75); 251 } 252 253 @SuppressLint("DefaultLocale") runParametrizedTurtledoveScript(int numAds)254 private void runParametrizedTurtledoveScript(int numAds) throws Exception { 255 BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 256 state.pauseTiming(); 257 InputStream testJsInputStream = 258 sContext.getAssets().open("turtledove_parametrized_generateBid.js"); 259 String jsTestFile = new String(testJsInputStream.readAllBytes(), StandardCharsets.UTF_8); 260 261 state.resumeTiming(); 262 while (state.keepRunning()) { 263 int numInterestGroups = 1; 264 String res = 265 callJSEngine( 266 sJSScriptEngine, 267 jsTestFile, 268 ImmutableList.of( 269 buildSampleInterestGroupArg(numInterestGroups, numAds)), 270 "generateBid"); 271 272 // I modified the Turtledove script to have the total execution time 273 // (across all IGs) as the last element in the response array. 274 JSONArray obj = new JSONArray(res); 275 long webviewExecTime = obj.getJSONObject(obj.length() - 1).getLong("generateBidTime"); 276 String webviewExecTimeLog = 277 String.format( 278 "(%s: %d)", 279 JSScriptEngineLogConstants.WEBVIEW_EXECUTION_TIME, webviewExecTime); 280 // The listener picks up logs from JSScriptEngine, so simulate logging from there. 281 Log.d(TAG, webviewExecTimeLog); 282 } 283 } 284 buildSampleInterestGroupArg( int numCustomAudiences, int numAds)285 private JSScriptArrayArgument<JSScriptRecordArgument> buildSampleInterestGroupArg( 286 int numCustomAudiences, int numAds) { 287 JSScriptRecordArgument ad = 288 recordArg( 289 "foo", 290 ImmutableList.of( 291 stringArg( 292 "renderUrl", 293 "https://googleads.g.doubleclick.net/ads/simple-ad" 294 + ".html?adg_id=52836427830&cr_id=310927197297" 295 + "&cv_id=4"), 296 stringArrayArg( 297 "metadata", 298 ImmutableList.of( 299 "52836427830", "310927197297", "4", "608936333")))); 300 301 JSScriptRecordArgument interestGroupArg = 302 recordArg( 303 "foo", 304 stringArg("owner", "https://googleads.g.doubleclick.net/"), 305 stringArg("name", "1j115753478"), 306 stringArg("biddingLogicUrl", "https://googleads.g.doubleclick.net/td/bjs"), 307 stringArg( 308 "dailyUpdateUrl", "https://googleads.g.doubleclick.net/td/update"), 309 stringArg( 310 "trustedBiddingSignalsUrl", 311 "https://googleads.g.doubleclick.net/td/sjs"), 312 stringArrayArg( 313 "trustedBiddingSignalsKeys", ImmutableList.of("1j115753478")), 314 stringArrayArg("userBiddingSignals", ImmutableList.of()), 315 new JSScriptArrayArgument("ads", Collections.nCopies(numAds, ad))); 316 317 return arrayArg("foo", Collections.nCopies(numCustomAudiences, interestGroupArg)); 318 } 319 320 @Test evaluate_turtledoveWasm()321 public void evaluate_turtledoveWasm() throws Exception { 322 assumeTrue(sJSScriptEngine.isWasmSupported().get(3, TimeUnit.SECONDS)); 323 324 BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 325 state.pauseTiming(); 326 327 String jsTestFile = readAsset("generate_bid_using_wasm.js"); 328 byte[] wasmTestFile = readBinaryAsset("generate_bid.wasm"); 329 JSScriptArgument[] inputBytes = new JSScriptArgument[200]; 330 Random rand = new Random(); 331 for (int i = 0; i < inputBytes.length; i++) { 332 byte value = (byte) (rand.nextInt(2 * Byte.MAX_VALUE) - Byte.MIN_VALUE); 333 inputBytes[i] = JSScriptArgument.numericArg("_", value); 334 } 335 JSScriptArgument adDataArgument = 336 recordArg( 337 "ad", 338 stringArg("render_url", "http://google.com"), 339 recordArg("metadata", JSScriptArgument.arrayArg("input", inputBytes))); 340 341 state.resumeTiming(); 342 while (state.keepRunning()) { 343 callJSEngine(jsTestFile, wasmTestFile, ImmutableList.of(adDataArgument), "generateBid"); 344 } 345 } 346 callJSEngine( @onNull String jsScript, @NonNull List<JSScriptArgument> args, @NonNull String functionName)347 private String callJSEngine( 348 @NonNull String jsScript, 349 @NonNull List<JSScriptArgument> args, 350 @NonNull String functionName) 351 throws Exception { 352 return callJSEngine(sJSScriptEngine, jsScript, args, functionName); 353 } 354 callJSEngine( @onNull String jsScript, @NonNull byte[] wasmScript, @NonNull List<JSScriptArgument> args, @NonNull String functionName)355 private String callJSEngine( 356 @NonNull String jsScript, 357 @NonNull byte[] wasmScript, 358 @NonNull List<JSScriptArgument> args, 359 @NonNull String functionName) 360 throws Exception { 361 return callJSEngine(sJSScriptEngine, jsScript, wasmScript, args, functionName); 362 } 363 callJSEngine( @onNull JSScriptEngine jsScriptEngine, @NonNull String jsScript, @NonNull List<JSScriptArgument> args, @NonNull String functionName)364 private static String callJSEngine( 365 @NonNull JSScriptEngine jsScriptEngine, 366 @NonNull String jsScript, 367 @NonNull List<JSScriptArgument> args, 368 @NonNull String functionName) 369 throws Exception { 370 CountDownLatch resultLatch = new CountDownLatch(1); 371 ListenableFuture<String> futureResult = 372 callJSEngineAsync(jsScriptEngine, jsScript, args, functionName, resultLatch); 373 resultLatch.await(); 374 return futureResult.get(); 375 } 376 callJSEngine( @onNull JSScriptEngine jsScriptEngine, @NonNull String jsScript, @NonNull byte[] wasmScript, @NonNull List<JSScriptArgument> args, @NonNull String functionName)377 private String callJSEngine( 378 @NonNull JSScriptEngine jsScriptEngine, 379 @NonNull String jsScript, 380 @NonNull byte[] wasmScript, 381 @NonNull List<JSScriptArgument> args, 382 @NonNull String functionName) 383 throws Exception { 384 CountDownLatch resultLatch = new CountDownLatch(1); 385 ListenableFuture<String> futureResult = 386 callJSEngineAsync( 387 jsScriptEngine, jsScript, wasmScript, args, functionName, resultLatch); 388 resultLatch.await(); 389 return futureResult.get(); 390 } 391 callJSEngineAsync( @onNull String jsScript, @NonNull List<JSScriptArgument> args, @NonNull String functionName, @NonNull CountDownLatch resultLatch)392 private static ListenableFuture<String> callJSEngineAsync( 393 @NonNull String jsScript, 394 @NonNull List<JSScriptArgument> args, 395 @NonNull String functionName, 396 @NonNull CountDownLatch resultLatch) { 397 return callJSEngineAsync(sJSScriptEngine, jsScript, args, functionName, resultLatch); 398 } 399 callJSEngineAsync( @onNull JSScriptEngine engine, @NonNull String jsScript, @NonNull List<JSScriptArgument> args, @NonNull String functionName, @NonNull CountDownLatch resultLatch)400 private static ListenableFuture<String> callJSEngineAsync( 401 @NonNull JSScriptEngine engine, 402 @NonNull String jsScript, 403 @NonNull List<JSScriptArgument> args, 404 @NonNull String functionName, 405 @NonNull CountDownLatch resultLatch) { 406 Objects.requireNonNull(engine); 407 Objects.requireNonNull(resultLatch); 408 ListenableFuture<String> result = engine.evaluate( 409 jsScript, 410 args, 411 functionName, 412 IsolateSettings.forMaxHeapSizeEnforcementDisabled()); 413 result.addListener(resultLatch::countDown, sExecutorService); 414 return result; 415 } 416 callJSEngineAsync( @onNull JSScriptEngine engine, @NonNull String jsScript, @NonNull byte[] wasmScript, @NonNull List<JSScriptArgument> args, @NonNull String functionName, @NonNull CountDownLatch resultLatch)417 private ListenableFuture<String> callJSEngineAsync( 418 @NonNull JSScriptEngine engine, 419 @NonNull String jsScript, 420 @NonNull byte[] wasmScript, 421 @NonNull List<JSScriptArgument> args, 422 @NonNull String functionName, 423 @NonNull CountDownLatch resultLatch) { 424 Objects.requireNonNull(engine); 425 Objects.requireNonNull(resultLatch); 426 ListenableFuture<String> result = engine.evaluate( 427 jsScript, 428 wasmScript, 429 args, 430 functionName, 431 IsolateSettings.forMaxHeapSizeEnforcementDisabled()); 432 result.addListener(resultLatch::countDown, sExecutorService); 433 return result; 434 } 435 readBinaryAsset(@onNull String assetName)436 private byte[] readBinaryAsset(@NonNull String assetName) throws IOException { 437 return sContext.getAssets().open(assetName).readAllBytes(); 438 } 439 readAsset(@onNull String assetName)440 private String readAsset(@NonNull String assetName) throws IOException { 441 return new String(readBinaryAsset(assetName), StandardCharsets.UTF_8); 442 } 443 runParameterizedRubidiumGenerateBid(int numOfAds)444 public void runParameterizedRubidiumGenerateBid(int numOfAds) throws Exception { 445 BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 446 state.pauseTiming(); 447 List<AdData> adDataList = getSampleAdDataList(numOfAds, "https://ads.example/"); 448 ImmutableList.Builder<JSScriptArgument> adDataListArgument = new ImmutableList.Builder<>(); 449 for (AdData adData : adDataList) { 450 adDataListArgument.add(mAdDataArgumentUtil.asScriptArgument("ignored", adData)); 451 } 452 AdSelectionSignals perBuyerSignals = generatePerBuyerSignals(numOfAds); 453 AdSelectionSignals auctionSignals = AdSelectionSignals.fromString("{\"auctionSignal1" 454 + "\":\"auctionValue1\",\"auctionSignal2\":\"auctionValue2\"}"); 455 456 AdTechIdentifier buyer = AdTechIdentifier.fromString("https://example-dsp.com"); 457 AdSelectionSignals trustedBiddingSignals = AdSelectionSignals.fromString("{\"key1" 458 + "\":\"tbs1\",\"key2\":{}}"); 459 CustomAudienceSignals customAudienceSignals = getSampleCustomAudienceSignals(buyer, 460 "shoes-running"); 461 462 ImmutableList<JSScriptArgument> args = ImmutableList.<JSScriptArgument>builder() 463 .add(arrayArg("ads", adDataListArgument.build())) 464 .add(jsonArg("auctionSignals", auctionSignals)) 465 .add(jsonArg("perBuyerSignals", perBuyerSignals)) 466 .add(jsonArg("trustedBiddingSignals", trustedBiddingSignals)) 467 .add(jsonArg("contextualSignals", CONTEXTUAL_SIGNALS)) 468 .add(CustomAudienceBiddingSignalsArgumentUtil.asScriptArgument( 469 "customAudienceBiddingSignal", customAudienceSignals)) 470 .build(); 471 InputStream testJsInputStream = sContext.getAssets().open( 472 "rubidium_bidding_logic_compiled.js"); 473 String jsTestFile = new String(testJsInputStream.readAllBytes(), StandardCharsets.UTF_8); 474 //logging time taken to call JS 475 state.resumeTiming(); 476 while (state.keepRunning()) { 477 String res = callJSEngine(jsTestFile, args, "generateBidIterative"); 478 JSONObject jsonObject = new JSONObject(res); 479 long webviewExecTime = jsonObject.getLong("duration"); 480 String webviewExecTimeLog = 481 String.format(Locale.ENGLISH, 482 "(%s: %d)", 483 JSScriptEngineLogConstants.WEBVIEW_EXECUTION_TIME, 484 webviewExecTime); 485 // The listener picks up logs from JSScriptEngine, so simulate logging from there. 486 Log.d(TAG, webviewExecTimeLog); 487 } 488 } 489 runParameterizedRubidiumScoreAd(int numOfAds)490 public void runParameterizedRubidiumScoreAd(int numOfAds) throws Exception { 491 BenchmarkState state = mPerfStatusReporter.getBenchmarkState(); 492 state.pauseTiming(); 493 String adRenderUrl = "https://rtb.example/creative"; 494 List<AdWithBid> adWithBidList = getSampleAdDataWithBidList(numOfAds, adRenderUrl); 495 ImmutableList.Builder<JSScriptArgument> adWithBidArrayArgument = 496 new ImmutableList.Builder<>(); 497 for (AdWithBid adWithBid : adWithBidList) { 498 adWithBidArrayArgument.add( 499 mAdWithBidArgumentUtil.asScriptArgument("adWithBid", adWithBid)); 500 } 501 AdTechIdentifier seller = AdTechIdentifier.fromString("www.example-ssp.com"); 502 AdSelectionSignals sellerSignals = AdSelectionSignals.fromString("{\"signals\":[]}"); 503 String trustedScoringSignalJson = String.format(Locale.ENGLISH, 504 "{\"renderUrl\":{\"%s\":[]}}", adRenderUrl); 505 AdSelectionSignals trustedScoringSignalsJson = AdSelectionSignals.fromString( 506 trustedScoringSignalJson); 507 508 AdTechIdentifier buyer1 = AdTechIdentifier.fromString("https://example-dsp.com"); 509 AdSelectionSignals buyer1Signals = AdSelectionSignals.fromString("{\"https://example-dsp" 510 + ".com:1\":\"value1\",\"https://example-dsp.com:2\":\"value2\"}"); 511 Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals = ImmutableMap.of(buyer1, 512 buyer1Signals); 513 514 AdSelectionConfig adSelectionConfig = getSampleAdSelectionConfig(seller, sellerSignals, 515 perBuyerSignals); 516 CustomAudienceSignals customAudienceSignals = getSampleCustomAudienceSignals(buyer1, 517 "shoes-running"); 518 519 ImmutableList<JSScriptArgument> args = ImmutableList.<JSScriptArgument>builder() 520 .add(arrayArg("adsWithBids", adWithBidArrayArgument.build())) 521 .add(AdSelectionConfigArgumentUtil.asScriptArgument(adSelectionConfig, 522 "adSelectionConfig")) 523 .add(jsonArg("sellerSignals", sellerSignals)) 524 .add(jsonArg("trustedScoringSignals", trustedScoringSignalsJson)) 525 .add(jsonArg("contextualSignals", CONTEXTUAL_SIGNALS)) 526 .add(CustomAudienceScoringSignalsArgumentUtil.asScriptArgument( 527 "customAudienceScoringSignal", customAudienceSignals)) 528 .build(); 529 InputStream testJsInputStream = sContext.getAssets().open( 530 "rubidium_scoring_logic_compiled.js"); 531 String jsTestFile = new String(testJsInputStream.readAllBytes(), StandardCharsets.UTF_8); 532 //logging time taken to call JS 533 state.resumeTiming(); 534 while (state.keepRunning()) { 535 String res = callJSEngine(jsTestFile, args, "scoreAdIterative"); 536 JSONObject jsonObject = new JSONObject(res); 537 long webviewExecTime = jsonObject.getLong("duration"); 538 String webviewExecTimeLog = 539 String.format(Locale.ENGLISH, 540 "(%s: %d)", 541 JSScriptEngineLogConstants.WEBVIEW_EXECUTION_TIME, 542 webviewExecTime); 543 // The listener picks up logs from JSScriptEngine, so simulate logging from there. 544 Log.d(TAG, webviewExecTimeLog); 545 } 546 } 547 getSampleAdDataWithBidList(int size, String baseUri)548 private List<AdWithBid> getSampleAdDataWithBidList(int size, String baseUri) { 549 double initialBid = 1.23; 550 return IntStream.rangeClosed(1, size).mapToObj(iterator -> { 551 Uri renderUri = Uri.parse(String.format(Locale.ENGLISH, "%s%d", baseUri, iterator)); 552 String metaDataJson = String.format(Locale.ENGLISH, "{\"metadata\":[\"%d\",\"123\"]}", 553 iterator); 554 AdData adData = new AdData.Builder().setRenderUri(renderUri).setMetadata( 555 metaDataJson).build(); 556 return new AdWithBid(adData, initialBid + iterator); 557 }).collect(Collectors.toCollection(ArrayList::new)); 558 } 559 getSampleAdDataList(int size, String baseUri)560 private List<AdData> getSampleAdDataList(int size, String baseUri) { 561 return IntStream.rangeClosed(1, size).mapToObj(iterator -> { 562 Uri renderUri = Uri.parse(String.format(Locale.ENGLISH, "%s%d", baseUri, iterator)); 563 String metaDataJson = String.format(Locale.ENGLISH, "{\"metadata\":[\"%d\",\"123\"]}", 564 iterator); 565 return new AdData.Builder().setRenderUri(renderUri).setMetadata( 566 metaDataJson).build(); 567 }).collect(Collectors.toCollection(ArrayList::new)); 568 } 569 570 private CustomAudienceSignals getSampleCustomAudienceSignals(AdTechIdentifier buyer, 571 String name) { 572 String owner = "www.example-dsp.com"; 573 AdSelectionSignals userBiddingSignals = AdSelectionSignals.fromString("{\"signals\":[]}"); 574 return new CustomAudienceSignals.Builder() 575 .setOwner(owner) 576 .setBuyer(buyer) 577 .setActivationTime(ACTIVATION_TIME) 578 .setExpirationTime(EXPIRATION_TIME) 579 .setUserBiddingSignals(userBiddingSignals) 580 .setName(name) 581 .build(); 582 } 583 584 private AdSelectionConfig getSampleAdSelectionConfig(AdTechIdentifier seller, 585 AdSelectionSignals sellerSignals, 586 Map<AdTechIdentifier, AdSelectionSignals> perBuyerSignals) { 587 Uri decisionLogicUri = Uri.parse("https://www.example-ssp.com/decide.js"); 588 Uri trustedScoringSignalsUri = Uri.parse("https://www.example-ssp.com/signals"); 589 List<AdTechIdentifier> buyers = ImmutableList.copyOf( 590 new ArrayList<>(perBuyerSignals.keySet())); 591 return new AdSelectionConfig.Builder() 592 .setSeller(seller) 593 .setDecisionLogicUri(decisionLogicUri) 594 .setCustomAudienceBuyers(buyers) 595 .setAdSelectionSignals(AdSelectionSignals.EMPTY) 596 .setSellerSignals(sellerSignals) 597 .setPerBuyerSignals(perBuyerSignals) 598 .setTrustedScoringSignalsUri(trustedScoringSignalsUri) 599 .build(); 600 } 601 602 private AdSelectionSignals generatePerBuyerSignals(int size) { 603 String signalArrayFormat = "[\"%d\",\"123\",%d]"; 604 String signalArray = IntStream.rangeClosed(1, size) 605 .mapToObj(i -> String.format(Locale.ENGLISH, signalArrayFormat, i, i)) 606 .collect(Collectors.joining(", ", "[", "]")); 607 return AdSelectionSignals.fromString( 608 String.format(Locale.ENGLISH, "{\"signals\":[null,%s,[null]]}", 609 signalArray)); 610 } 611 } 612