1 /* 2 * Copyright (C) 2023 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.measurement.attribution; 18 19 import static com.android.adservices.service.Flags.MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS; 20 import static com.android.adservices.service.Flags.MEASUREMENT_MIN_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS; 21 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 22 23 import static org.junit.Assert.assertEquals; 24 import static org.mockito.Mockito.when; 25 26 import android.util.Pair; 27 28 import com.android.adservices.LogUtil; 29 import com.android.adservices.common.AdServicesMockitoTestCase; 30 import com.android.adservices.service.measurement.AttributionConfig; 31 import com.android.adservices.service.measurement.FilterMap; 32 import com.android.adservices.service.measurement.Source; 33 import com.android.adservices.service.measurement.SourceFixture; 34 import com.android.adservices.service.measurement.Trigger; 35 import com.android.adservices.service.measurement.TriggerFixture; 36 import com.android.adservices.service.measurement.util.Filter; 37 import com.android.adservices.service.measurement.util.UnsignedLong; 38 39 import org.json.JSONArray; 40 import org.json.JSONException; 41 import org.json.JSONObject; 42 import org.junit.Before; 43 import org.junit.Test; 44 45 import java.util.Arrays; 46 import java.util.Collections; 47 import java.util.Comparator; 48 import java.util.List; 49 import java.util.UUID; 50 import java.util.concurrent.TimeUnit; 51 52 public final class XnaSourceCreatorTest extends AdServicesMockitoTestCase { 53 private static final UnsignedLong SHARED_DEBUG_KEY_1 = new UnsignedLong(1786463L); 54 private static final String AD_ID = "abc-def-ghi"; 55 private static final String JOIN_KEY = "join-abc-def-ghi"; 56 private static final long LOOKBACK_WINDOW_VALUE = 10L; 57 58 private Filter mFilter; 59 60 @Before setup()61 public void setup() { 62 doReturn(true).when(mMockFlags).getMeasurementEnableSharedSourceDebugKey(); 63 doReturn(true).when(mMockFlags).getMeasurementEnableSharedFilterDataKeysXNA(); 64 when(mMockFlags.getMeasurementMinReportingRegisterSourceExpirationInSeconds()) 65 .thenReturn(MEASUREMENT_MIN_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS); 66 when(mMockFlags.getMeasurementMaxReportingRegisterSourceExpirationInSeconds()) 67 .thenReturn(MEASUREMENT_MAX_REPORTING_REGISTER_SOURCE_EXPIRATION_IN_SECONDS); 68 mFilter = new Filter(mMockFlags); 69 } 70 71 @Test generateDerivedSources_withVarietyOfSources_filtersAndGeneratesSources()72 public void generateDerivedSources_withVarietyOfSources_filtersAndGeneratesSources() 73 throws JSONException { 74 // Setup 75 String enrollment1 = "enrollment1"; 76 JSONArray filters = 77 new JSONArray( 78 Collections.singletonList(new JSONObject(buildMatchingFilterData(false)))); 79 AttributionConfig attributionConfig1 = 80 new AttributionConfig.Builder() 81 .setSourceAdtech(enrollment1) 82 .setSourceFilters(mFilter.deserializeFilterSet(filters)) 83 .setSourceNotFilters(null) 84 .setFilterData( 85 new FilterMap.Builder() 86 .buildFilterData( 87 new JSONObject(buildMatchingFilterData(false))) 88 .build()) 89 .setExpiry(50L) 90 .setPriority(50L) 91 .setPostInstallExclusivityWindow(5L) 92 .setSourcePriorityRange(new Pair<>(1L, 100L)) 93 .build(); 94 String enrollment2 = "enrollment2"; 95 AttributionConfig attributionConfig2 = 96 new AttributionConfig.Builder() 97 .setSourceAdtech(enrollment2) 98 .setSourceFilters(null) 99 .setSourceNotFilters( 100 mFilter.deserializeFilterSet( 101 new JSONArray( 102 Collections.singletonList( 103 new JSONObject( 104 buildNonMatchingFilterData()))))) 105 .setSourcePriorityRange(new Pair<>(101L, 200L)) 106 .setSourceExpiryOverride(TimeUnit.DAYS.toSeconds(10L)) 107 .build(); 108 109 AttributionConfig attributionConfig1_copy = 110 new AttributionConfig.Builder() 111 .setSourceAdtech(enrollment1) 112 .setSourceFilters(mFilter.deserializeFilterSet(filters)) 113 .setSourceNotFilters(null) 114 .setFilterData( 115 new FilterMap.Builder() 116 .buildFilterData( 117 new JSONObject(buildMatchingFilterData(false))) 118 .build()) 119 .setExpiry(60L) 120 .setPriority(70L) 121 .setPostInstallExclusivityWindow(50L) 122 .setSourcePriorityRange(new Pair<>(1L, 100L)) 123 .build(); 124 125 String attributionConfigsArray = 126 new JSONArray( 127 Arrays.asList( 128 attributionConfig1.serializeAsJson(mMockFlags), 129 attributionConfig2.serializeAsJson(mMockFlags), 130 // This ensures that already consumed sources aren't reused 131 attributionConfig1_copy.serializeAsJson(mMockFlags))) 132 .toString(); 133 Trigger trigger = 134 TriggerFixture.getValidTriggerBuilder() 135 .setAttributionConfig(attributionConfigsArray) 136 .build(); 137 138 // Aggregate source 139 JSONObject aggregatableSource = new JSONObject(); 140 aggregatableSource.put("key1", "0x159"); 141 aggregatableSource.put("key2", "0x1"); 142 aggregatableSource.put("key3", "0x2"); 143 144 // enrollment1 sources 145 Source source1Matches = 146 createValidSourceBuilder() 147 .setId(UUID.randomUUID().toString()) 148 .setEnrollmentId(enrollment1) 149 .setPriority(1L) 150 .setAggregateSource(aggregatableSource.toString()) 151 .setSharedAggregationKeys( 152 new JSONArray(Arrays.asList("key2", "key3")).toString()) 153 .setSharedDebugKey(SHARED_DEBUG_KEY_1) 154 .setDebugAdId(AD_ID) 155 .setDebugJoinKey(JOIN_KEY) 156 .build(); 157 158 JSONObject derivedAggregatableSource1 = new JSONObject(); 159 derivedAggregatableSource1.put("key2", "0x1"); 160 derivedAggregatableSource1.put("key3", "0x2"); 161 Source expectedDerivedSource1 = 162 createValidSourceBuilder() 163 .setId(UUID.randomUUID().toString()) 164 .setEnrollmentId(enrollment1) 165 .setPriority(attributionConfig1.getPriority()) 166 .setFilterDataString( 167 attributionConfig1 168 .getFilterData() 169 .serializeAsJson(mMockFlags) 170 .toString()) 171 .setExpiryTime(source1Matches.getExpiryTime()) 172 .setInstallCooldownWindow( 173 attributionConfig1.getPostInstallExclusivityWindow()) 174 .setParentId(source1Matches.getId()) 175 .setSharedAggregationKeys( 176 new JSONArray(Arrays.asList("key2", "key3")).toString()) 177 .setAggregateSource(derivedAggregatableSource1.toString()) 178 // shared_debug_key is shared as debug_key on the derived source 179 .setDebugKey(SHARED_DEBUG_KEY_1) 180 // adId isn't shared 181 .setDebugAdId(null) 182 // join key isn't shared 183 .setDebugJoinKey(null) 184 .build(); 185 Source source2Matches = 186 createValidSourceBuilder() 187 .setId(UUID.randomUUID().toString()) 188 .setEnrollmentId(enrollment1) 189 .setPriority(100L) 190 .setFilterDataString(buildMatchingFilterData(false)) 191 .setAggregateSource(aggregatableSource.toString()) 192 .setSharedAggregationKeys( 193 new JSONArray(Collections.singletonList("key1")).toString()) 194 .build(); 195 196 JSONObject derivedAggregatableSource2 = new JSONObject(); 197 derivedAggregatableSource2.put("key1", "0x159"); 198 Source expectedDerivedSource2; 199 expectedDerivedSource2 = 200 createValidSourceBuilder() 201 .setId(UUID.randomUUID().toString()) 202 .setEnrollmentId(enrollment1) 203 .setPriority(attributionConfig1.getPriority()) 204 .setFilterDataString( 205 attributionConfig1 206 .getFilterData() 207 .serializeAsJson(mMockFlags) 208 .toString()) 209 .setExpiryTime(source2Matches.getExpiryTime()) 210 .setInstallCooldownWindow( 211 attributionConfig1.getPostInstallExclusivityWindow()) 212 .setParentId(source2Matches.getId()) 213 .setAggregateSource(derivedAggregatableSource2.toString()) 214 .setSharedAggregationKeys( 215 new JSONArray(Collections.singletonList("key1")).toString()) 216 .build(); 217 Source source3OutOfPriorityRange = 218 createValidSourceBuilder() 219 .setId(UUID.randomUUID().toString()) 220 .setEnrollmentId(enrollment1) 221 .setPriority(101L) 222 .setFilterDataString(buildMatchingFilterData(false)) 223 .build(); 224 Source source4NonMatchingFilter = 225 createValidSourceBuilder() 226 .setId(UUID.randomUUID().toString()) 227 .setEnrollmentId(enrollment1) 228 .setPriority(50L) 229 .setFilterDataString(buildNonMatchingFilterData()) 230 .build(); 231 232 // enrollment2 sources 233 Source source5Matches = 234 createValidSourceBuilder() 235 .setId(UUID.randomUUID().toString()) 236 .setEnrollmentId(enrollment2) 237 .setPriority(120L) 238 .setInstallCooldownWindow(0L) 239 .setSharedDebugKey(SHARED_DEBUG_KEY_1) 240 .build(); 241 Source expectedDerivedSource5 = 242 createValidSourceBuilder() 243 .setId(UUID.randomUUID().toString()) 244 .setEnrollmentId(enrollment2) 245 .setPriority(120L) 246 .setFilterDataString(source5Matches.getFilterDataString()) 247 .setExpiryTime(source5Matches.getExpiryTime()) 248 .setInstallCooldownWindow(0L) 249 .setParentId(source5Matches.getId()) 250 .setAggregateSource(new JSONObject().toString()) 251 .setDebugKey(SHARED_DEBUG_KEY_1) 252 .build(); 253 Source source6NoFiltersIssue = 254 createValidSourceBuilder() 255 .setId(UUID.randomUUID().toString()) 256 .setEnrollmentId(enrollment2) 257 .setPriority(130L) 258 .setFilterDataString(buildNonMatchingFilterData()) 259 .build(); 260 Source source7ExpiresBeforeTriggerTime = 261 createValidSourceBuilder() 262 .setId(UUID.randomUUID().toString()) 263 .setEnrollmentId(enrollment2) 264 .setPriority(120L) 265 .setEventTime( 266 trigger.getTriggerTime() 267 - TimeUnit.DAYS.toMillis(10L) 268 - 50L /* random value so that eventTime+override is before 269 trigger time*/) 270 .build(); 271 272 // enrollmentX source - is not considered since to attribution config is present for this 273 Source source8NoAttributionConfig = 274 createValidSourceBuilder() 275 .setId(UUID.randomUUID().toString()) 276 .setEnrollmentId("enrollmentX") 277 .setPriority(120L) 278 .setInstallCooldownWindow(0L) 279 .build(); 280 281 List<Source> expectedDerivedSources = 282 Arrays.asList( 283 expectedDerivedSource1, expectedDerivedSource2, expectedDerivedSource5); 284 Comparator<Source> sorter = Comparator.comparing(Source::getParentId); 285 expectedDerivedSources.sort(sorter); 286 287 // Execution 288 XnaSourceCreator xnaSourceCreator = new XnaSourceCreator(mMockFlags); 289 List<Source> parentSources = 290 Arrays.asList( 291 source1Matches, 292 source2Matches, 293 source3OutOfPriorityRange, 294 source4NonMatchingFilter, 295 source5Matches, 296 source6NoFiltersIssue, 297 source7ExpiresBeforeTriggerTime, 298 source8NoAttributionConfig); 299 List<Source> actualDerivedSources = 300 xnaSourceCreator.generateDerivedSources(trigger, parentSources); 301 actualDerivedSources.sort(sorter); 302 303 // Assertion 304 assertEquals(expectedDerivedSources, actualDerivedSources); 305 } 306 307 @Test generateDerivedSources_sharedDebugKeyDisabled_doesntAddDebugKeyToDerivedSource()308 public void generateDerivedSources_sharedDebugKeyDisabled_doesntAddDebugKeyToDerivedSource() 309 throws JSONException { 310 // Setup 311 doReturn(false).when(mMockFlags).getMeasurementEnableSharedSourceDebugKey(); 312 String enrollment1 = "enrollment1"; 313 JSONArray filters = 314 new JSONArray( 315 Collections.singletonList(new JSONObject(buildMatchingFilterData(false)))); 316 AttributionConfig attributionConfig1 = 317 new AttributionConfig.Builder() 318 .setSourceAdtech(enrollment1) 319 .setSourceFilters(mFilter.deserializeFilterSet(filters)) 320 .setSourceNotFilters(null) 321 .setFilterData( 322 new FilterMap.Builder() 323 .buildFilterData( 324 new JSONObject(buildMatchingFilterData(false))) 325 .build()) 326 .setExpiry(50L) 327 .setPriority(50L) 328 .setPostInstallExclusivityWindow(5L) 329 .setSourcePriorityRange(new Pair<>(1L, 100L)) 330 .build(); 331 String attributionConfigsArray = 332 new JSONArray( 333 Collections.singletonList( 334 attributionConfig1.serializeAsJson(mMockFlags))) 335 .toString(); 336 Trigger trigger = 337 TriggerFixture.getValidTriggerBuilder() 338 .setAttributionConfig(attributionConfigsArray) 339 .build(); 340 341 // Aggregate source 342 JSONObject aggregatableSource = new JSONObject(); 343 aggregatableSource.put("key1", "0x159"); 344 aggregatableSource.put("key2", "0x1"); 345 aggregatableSource.put("key3", "0x2"); 346 347 // enrollment1 sources 348 Source source1Matches = 349 createValidSourceBuilder() 350 .setId(UUID.randomUUID().toString()) 351 .setEnrollmentId(enrollment1) 352 .setPriority(1L) 353 .setAggregateSource(aggregatableSource.toString()) 354 .setSharedAggregationKeys( 355 new JSONArray(Arrays.asList("key2", "key3")).toString()) 356 .setSharedDebugKey(SHARED_DEBUG_KEY_1) 357 .setDebugAdId(AD_ID) 358 .setDebugJoinKey(JOIN_KEY) 359 .build(); 360 361 JSONObject derivedAggregatableSource1 = new JSONObject(); 362 derivedAggregatableSource1.put("key2", "0x1"); 363 derivedAggregatableSource1.put("key3", "0x2"); 364 Source expectedDerivedSource1 = 365 createValidSourceBuilder() 366 .setId(UUID.randomUUID().toString()) 367 .setEnrollmentId(enrollment1) 368 .setPriority(attributionConfig1.getPriority()) 369 .setFilterDataString( 370 attributionConfig1 371 .getFilterData() 372 .serializeAsJson(mMockFlags) 373 .toString()) 374 .setExpiryTime(source1Matches.getExpiryTime()) 375 .setInstallCooldownWindow( 376 attributionConfig1.getPostInstallExclusivityWindow()) 377 .setParentId(source1Matches.getId()) 378 .setSharedAggregationKeys( 379 new JSONArray(Arrays.asList("key2", "key3")).toString()) 380 .setAggregateSource(derivedAggregatableSource1.toString()) 381 // shared_debug_key is not shared when the feature is disabled 382 .setDebugKey(null) 383 // adId isn't shared 384 .setDebugAdId(null) 385 // join key isn't shared 386 .setDebugJoinKey(null) 387 .build(); 388 389 List<Source> expectedDerivedSources = Collections.singletonList(expectedDerivedSource1); 390 391 // Execution 392 XnaSourceCreator xnaSourceCreator = new XnaSourceCreator(mMockFlags); 393 List<Source> parentSources = Collections.singletonList(source1Matches); 394 List<Source> actualDerivedSources = 395 xnaSourceCreator.generateDerivedSources(trigger, parentSources); 396 397 // Assertion 398 assertEquals(expectedDerivedSources, actualDerivedSources); 399 } 400 401 @Test generateDerivedSources_withSharedFilterDataKeys_filtersAndGeneratesSources()402 public void generateDerivedSources_withSharedFilterDataKeys_filtersAndGeneratesSources() 403 throws JSONException { 404 // Setup 405 doReturn(false).when(mMockFlags).getMeasurementEnableSharedSourceDebugKey(); 406 String enrollment1 = "enrollment1"; 407 408 JSONArray filters = 409 new JSONArray( 410 Collections.singletonList(new JSONObject(buildMatchingSharedFilterData()))); 411 AttributionConfig attributionConfig1 = 412 new AttributionConfig.Builder() 413 .setSourceAdtech(enrollment1) 414 .setSourceFilters(mFilter.deserializeFilterSet(filters)) 415 .setSourceNotFilters(null) 416 .setFilterData( 417 new FilterMap.Builder() 418 .buildFilterData( 419 new JSONObject(buildMatchingSharedFilterData())) 420 .build()) 421 .setExpiry(50L) 422 .setPriority(50L) 423 .setPostInstallExclusivityWindow(5L) 424 .setSourcePriorityRange(new Pair<>(1L, 100L)) 425 .build(); 426 String attributionConfigsArray = 427 new JSONArray( 428 Collections.singletonList( 429 attributionConfig1.serializeAsJson(mMockFlags))) 430 .toString(); 431 Trigger trigger = 432 TriggerFixture.getValidTriggerBuilder() 433 .setAttributionConfig(attributionConfigsArray) 434 .build(); 435 436 // Aggregate source 437 JSONObject aggregatableSource = new JSONObject(); 438 aggregatableSource.put("key1", "0x159"); 439 aggregatableSource.put("key2", "0x1"); 440 aggregatableSource.put("key3", "0x2"); 441 442 // enrollment1 sources 443 Source source1Matches = 444 createValidSourceBuilder() 445 .setId(UUID.randomUUID().toString()) 446 .setEnrollmentId(enrollment1) 447 .setPriority(1L) 448 .setAggregateSource(aggregatableSource.toString()) 449 .setSharedAggregationKeys( 450 new JSONArray(Arrays.asList("key2", "key3")).toString()) 451 .setFilterDataString(buildMatchingSharedFilterData()) 452 .setSharedFilterDataKeys(new JSONArray(Arrays.asList("product")).toString()) 453 .setSharedDebugKey(SHARED_DEBUG_KEY_1) 454 .setDebugAdId(AD_ID) 455 .setDebugJoinKey(JOIN_KEY) 456 .build(); 457 458 JSONObject derivedAggregatableSource1 = new JSONObject(); 459 derivedAggregatableSource1.put("key2", "0x1"); 460 derivedAggregatableSource1.put("key3", "0x2"); 461 Source expectedDerivedSource1 = 462 createValidSourceBuilder() 463 .setId(UUID.randomUUID().toString()) 464 .setEnrollmentId(enrollment1) 465 .setPriority(attributionConfig1.getPriority()) 466 .setFilterDataString( 467 attributionConfig1 468 .getFilterData() 469 .serializeAsJson(mMockFlags) 470 .toString()) 471 .setExpiryTime(source1Matches.getExpiryTime()) 472 .setInstallCooldownWindow( 473 attributionConfig1.getPostInstallExclusivityWindow()) 474 .setParentId(source1Matches.getId()) 475 .setFilterDataString("{\"product\":[\"123\"]}") 476 .setSharedFilterDataKeys(null) 477 .setSharedAggregationKeys( 478 new JSONArray(Arrays.asList("key2", "key3")).toString()) 479 .setAggregateSource(derivedAggregatableSource1.toString()) 480 // shared_debug_key is not shared when the feature is disabled 481 .setDebugKey(null) 482 // adId isn't shared 483 .setDebugAdId(null) 484 // join key isn't shared 485 .setDebugJoinKey(null) 486 .build(); 487 488 List<Source> expectedDerivedSources = Collections.singletonList(expectedDerivedSource1); 489 490 // Execution 491 XnaSourceCreator xnaSourceCreator = new XnaSourceCreator(mMockFlags); 492 List<Source> parentSources = Collections.singletonList(source1Matches); 493 List<Source> actualDerivedSources = 494 xnaSourceCreator.generateDerivedSources(trigger, parentSources); 495 496 // Assertion 497 assertEquals(expectedDerivedSources, actualDerivedSources); 498 } 499 500 @Test generateDerivedSources_lookbackWindow_filtersAndGeneratesSources()501 public void generateDerivedSources_lookbackWindow_filtersAndGeneratesSources() 502 throws JSONException { 503 // Setup 504 doReturn(true).when(mMockFlags).getMeasurementEnableLookbackWindowFilter(); 505 String enrollment1 = "enrollment1"; 506 507 JSONArray filters = 508 new JSONArray( 509 Collections.singletonList(new JSONObject(buildMatchingFilterData(true)))); 510 JSONArray sourceFilters = 511 new JSONArray( 512 Collections.singletonList(new JSONObject(buildMatchingFilterData(false)))); 513 AttributionConfig attributionConfig1 = 514 new AttributionConfig.Builder() 515 .setSourceAdtech(enrollment1) 516 .setSourceFilters(mFilter.deserializeFilterSet(filters)) 517 .setSourceNotFilters(null) 518 .setFilterData( 519 new FilterMap.Builder() 520 .buildFilterDataV2( 521 new JSONObject(buildMatchingFilterData(false))) 522 .build()) 523 .setExpiry(50L) 524 .setPriority(50L) 525 .setPostInstallExclusivityWindow(5L) 526 .setSourcePriorityRange(new Pair<>(1L, 100L)) 527 .build(); 528 String attributionConfigsArray = 529 new JSONArray( 530 Collections.singletonList( 531 attributionConfig1.serializeAsJson(mMockFlags))) 532 .toString(); 533 Trigger trigger = 534 TriggerFixture.getValidTriggerBuilder() 535 .setAttributionConfig(attributionConfigsArray) 536 .setTriggerTime(20000L) 537 .build(); 538 539 // Aggregate source 540 JSONObject aggregatableSource = new JSONObject(); 541 aggregatableSource.put("key1", "0x159"); 542 aggregatableSource.put("key2", "0x1"); 543 aggregatableSource.put("key3", "0x2"); 544 545 // enrollment1 sources 546 Source sourceWithinLookbackWindow = 547 createValidSourceBuilder() 548 .setId(UUID.randomUUID().toString()) 549 .setEventTime( 550 trigger.getTriggerTime() 551 - TimeUnit.SECONDS.toMillis(LOOKBACK_WINDOW_VALUE - 1)) 552 .setEnrollmentId(enrollment1) 553 .setPriority(1L) 554 .setSharedAggregationKeys( 555 new JSONArray(Arrays.asList("key2", "key3")).toString()) 556 .setAggregateSource(aggregatableSource.toString()) 557 .setFilterDataString(buildMatchingFilterData(false)) 558 .setSharedDebugKey(SHARED_DEBUG_KEY_1) 559 .setDebugAdId(AD_ID) 560 .setDebugJoinKey(JOIN_KEY) 561 .build(); 562 563 Source sourceOutsideLookbackWindow = 564 createValidSourceBuilder() 565 .setId(UUID.randomUUID().toString()) 566 .setEventTime( 567 trigger.getTriggerTime() 568 - TimeUnit.SECONDS.toMillis(LOOKBACK_WINDOW_VALUE + 1)) 569 .setEnrollmentId(enrollment1) 570 .setPriority(1L) 571 .setSharedAggregationKeys( 572 new JSONArray(Arrays.asList("key2", "key3")).toString()) 573 .setAggregateSource(aggregatableSource.toString()) 574 .setFilterDataString(buildMatchingFilterData(false)) 575 .setSharedDebugKey(SHARED_DEBUG_KEY_1) 576 .setDebugAdId(AD_ID) 577 .setDebugJoinKey(JOIN_KEY) 578 .build(); 579 580 JSONObject derivedAggregatableSource1 = new JSONObject(); 581 derivedAggregatableSource1.put("key2", "0x1"); 582 derivedAggregatableSource1.put("key3", "0x2"); 583 Source expectedDerivedSource1 = 584 createValidSourceBuilder() 585 .setId(UUID.randomUUID().toString()) 586 .setEnrollmentId(enrollment1) 587 .setPriority(attributionConfig1.getPriority()) 588 .setEventTime(sourceWithinLookbackWindow.getEventTime()) 589 .setFilterDataString(buildMatchingFilterData(false)) 590 .setExpiryTime(86411000L) 591 .setInstallCooldownWindow( 592 attributionConfig1.getPostInstallExclusivityWindow()) 593 .setParentId(sourceWithinLookbackWindow.getId()) 594 .setSharedFilterDataKeys(null) 595 .setSharedAggregationKeys( 596 new JSONArray(Arrays.asList("key2", "key3")).toString()) 597 .setInstallAttributed(true) 598 .setAggregateSource(derivedAggregatableSource1.toString()) 599 // shared_debug_key is not shared when the feature is disabled 600 .setDebugKey(SHARED_DEBUG_KEY_1) 601 // adId isn't shared 602 .setDebugAdId(null) 603 // join key isn't shared 604 .setDebugJoinKey(null) 605 .build(); 606 607 List<Source> expectedDerivedSources = Collections.singletonList(expectedDerivedSource1); 608 609 // Execution 610 XnaSourceCreator xnaSourceCreator = new XnaSourceCreator(mMockFlags); 611 List<Source> parentSources = 612 Arrays.asList(sourceWithinLookbackWindow, sourceOutsideLookbackWindow); 613 List<Source> actualDerivedSources = 614 xnaSourceCreator.generateDerivedSources(trigger, parentSources); 615 616 // Assertion 617 assertEquals(expectedDerivedSources, actualDerivedSources); 618 } 619 createValidSourceBuilder()620 private Source.Builder createValidSourceBuilder() { 621 return new Source.Builder() 622 .setEventId(SourceFixture.ValidSourceParams.SOURCE_EVENT_ID) 623 .setPublisher(SourceFixture.ValidSourceParams.PUBLISHER) 624 .setAppDestinations(SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS) 625 .setWebDestinations(SourceFixture.ValidSourceParams.WEB_DESTINATIONS) 626 .setEnrollmentId(SourceFixture.ValidSourceParams.ENROLLMENT_ID) 627 .setRegistrant(SourceFixture.ValidSourceParams.REGISTRANT) 628 .setEventTime(SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME) 629 .setExpiryTime(SourceFixture.ValidSourceParams.EXPIRY_TIME) 630 .setPriority(SourceFixture.ValidSourceParams.PRIORITY) 631 .setSourceType(SourceFixture.ValidSourceParams.SOURCE_TYPE) 632 .setInstallAttributionWindow( 633 SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW) 634 .setInstallCooldownWindow(SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW) 635 .setAttributionMode(SourceFixture.ValidSourceParams.ATTRIBUTION_MODE) 636 .setAggregateSource(SourceFixture.ValidSourceParams.buildAggregateSource()) 637 .setFilterDataString(buildMatchingFilterData(false)) 638 .setIsDebugReporting(true) 639 .setRegistrationId(SourceFixture.ValidSourceParams.REGISTRATION_ID) 640 .setSharedAggregationKeys(SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS) 641 .setInstallTime(SourceFixture.ValidSourceParams.INSTALL_TIME) 642 .setRegistrationOrigin(SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN); 643 } 644 buildMatchingFilterData(boolean addLookbackWindowFilter)645 private String buildMatchingFilterData(boolean addLookbackWindowFilter) { 646 try { 647 JSONObject filterMap = new JSONObject(); 648 filterMap.put( 649 "conversion_subdomain", 650 new JSONArray(Collections.singletonList("electronics.megastore"))); 651 if (addLookbackWindowFilter) { 652 filterMap.put(FilterMap.LOOKBACK_WINDOW, LOOKBACK_WINDOW_VALUE); 653 } 654 return filterMap.toString(); 655 } catch (JSONException e) { 656 LogUtil.e("JSONException when building aggregate filter data."); 657 } 658 return null; 659 } 660 buildNonMatchingFilterData()661 private String buildNonMatchingFilterData() { 662 try { 663 JSONObject filterMap = new JSONObject(); 664 filterMap.put( 665 "conversion_subdomain", 666 new JSONArray(Collections.singletonList("non.matching"))); 667 return filterMap.toString(); 668 } catch (JSONException e) { 669 LogUtil.e("JSONException when building aggregate filter data."); 670 } 671 return null; 672 } 673 buildMatchingSharedFilterData()674 private String buildMatchingSharedFilterData() { 675 try { 676 JSONObject filterMap = new JSONObject(); 677 filterMap.put( 678 "conversion_subdomain", 679 new JSONArray(Collections.singletonList("electronics.megastore"))); 680 filterMap.put("product", new JSONArray(Collections.singletonList("123"))); 681 return filterMap.toString(); 682 } catch (JSONException e) { 683 LogUtil.e("JSONException when building aggregate filter data."); 684 } 685 return null; 686 } 687 } 688