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