blob: baa43e87ba4ace2e18b1427b8894fa266ee2b12e [file] [log] [blame]
/*
* 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.measurement;
import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThrows;
import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.net.Uri;
import android.util.Pair;
import androidx.annotation.Nullable;
import com.android.adservices.common.WebUtil;
import com.android.adservices.service.Flags;
import com.android.adservices.service.measurement.aggregation.AggregatableAttributionSource;
import com.android.adservices.service.measurement.util.UnsignedLong;
import com.android.dx.mockito.inline.extended.ExtendedMockito;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Random;
import java.util.TreeMap;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.LongStream;
@RunWith(MockitoJUnitRunner.class)
public class SourceTest {
private static final UnsignedLong DEBUG_KEY_1 = new UnsignedLong(81786463L);
private static final UnsignedLong DEBUG_KEY_2 = new UnsignedLong(23487834L);
private static final UnsignedLong SHARED_DEBUG_KEY_1 = new UnsignedLong(1786463L);
private static final UnsignedLong SHARED_DEBUG_KEY_2 = new UnsignedLong(3487834L);
private static final List<AttributedTrigger> ATTRIBUTED_TRIGGERS =
List.of(
new AttributedTrigger(
"triggerId",
/* long priority */ 5L,
/* UnsignedLong triggerData */ new UnsignedLong("89"),
/* long value */ 15L,
/* long triggerTime */ 1934567890L,
/* UnsignedLong dedupKey */ null,
/* UnsignedLong debugKey */ null,
/* boolean hasSourceDebugKey */ false));
@Mock private Flags mFlags;
@Before
public void setup() {
ExtendedMockito.doReturn(false).when(mFlags).getMeasurementEnableLookbackWindowFilter();
}
@Test
public void testDefaults() {
Source source = SourceFixture.getMinimalValidSourceBuilder().build();
assertEquals(0, source.getEventReportDedupKeys().size());
assertEquals(0, source.getAggregateReportDedupKeys().size());
assertEquals(Source.Status.ACTIVE, source.getStatus());
assertEquals(Source.SourceType.EVENT, source.getSourceType());
assertEquals(Source.AttributionMode.UNASSIGNED, source.getAttributionMode());
assertNull(source.getAttributedTriggers());
}
@Test
public void testEqualsPass() throws JSONException {
assertEquals(
SourceFixture.getMinimalValidSourceBuilder().build(),
SourceFixture.getMinimalValidSourceBuilder().build());
JSONObject aggregateSource = new JSONObject();
aggregateSource.put("campaignCounts", "0x159");
aggregateSource.put("geoValue", "0x5");
JSONObject filterMap = new JSONObject();
filterMap.put(
"conversion_subdomain", Collections.singletonList("electronics.megastore"));
filterMap.put("product", Arrays.asList("1234", "2345"));
String sharedAggregateKeys = "[\"campaignCounts\"]";
String sharedFilterDataKeys = "[\"product\"]";
String parentId = "parent-id";
String debugJoinKey = "SAMPLE_DEBUG_JOIN_KEY";
String debugAppAdId = "SAMPLE_DEBUG_APP_ADID";
String debugWebAdId = "SAMPLE_DEBUG_WEB_ADID";
TriggerSpecs triggerSpecs = SourceFixture.getValidTriggerSpecsValueSum();
assertEquals(
new Source.Builder()
.setEnrollmentId("enrollment-id")
.setAppDestinations(List.of(Uri.parse("android-app://example.test/aD1")))
.setWebDestinations(List.of(Uri.parse("https://example.test/aD2")))
.setPublisher(Uri.parse("https://example.test/aS"))
.setPublisherType(EventSurfaceType.WEB)
.setId("1")
.setEventId(new UnsignedLong(2L))
.setPriority(3L)
.setEventTime(5L)
.setExpiryTime(5L)
.setEventReportWindow(55L)
.setAggregatableReportWindow(555L)
.setIsDebugReporting(true)
.setEventReportDedupKeys(
LongStream.range(0, 2)
.boxed()
.map(UnsignedLong::new)
.collect(Collectors.toList()))
.setAggregateReportDedupKeys(
LongStream.range(0, 2)
.boxed()
.map(UnsignedLong::new)
.collect(Collectors.toList()))
.setStatus(Source.Status.ACTIVE)
.setSourceType(Source.SourceType.EVENT)
.setRegistrant(Uri.parse("android-app://com.example.abc"))
.setFilterData(filterMap.toString())
.setAggregateSource(aggregateSource.toString())
.setAggregateContributions(50001)
.setDebugKey(DEBUG_KEY_1)
.setRegistrationId("R1")
.setSharedAggregationKeys(sharedAggregateKeys)
.setSharedFilterDataKeys(sharedFilterDataKeys)
.setInstallTime(100L)
.setParentId(parentId)
.setDebugJoinKey(debugJoinKey)
.setPlatformAdId(debugAppAdId)
.setDebugAdId(debugWebAdId)
.setRegistrationOrigin(WebUtil.validUri("https://subdomain.example.test"))
.setCoarseEventReportDestinations(true)
.setSharedDebugKey(SHARED_DEBUG_KEY_1)
.setAttributedTriggers(ATTRIBUTED_TRIGGERS)
.setTriggerSpecs(triggerSpecs)
.setTriggerSpecsString(triggerSpecs.encodeToJson())
.setMaxEventLevelReports(triggerSpecs.getMaxReports())
.setEventAttributionStatus(null)
.setPrivacyParameters(triggerSpecs.encodePrivacyParametersToJSONString())
.setDropSourceIfInstalled(true)
.build(),
new Source.Builder()
.setEnrollmentId("enrollment-id")
.setAppDestinations(List.of(Uri.parse("android-app://example.test/aD1")))
.setWebDestinations(List.of(Uri.parse("https://example.test/aD2")))
.setPublisher(Uri.parse("https://example.test/aS"))
.setPublisherType(EventSurfaceType.WEB)
.setId("1")
.setEventId(new UnsignedLong(2L))
.setPriority(3L)
.setEventTime(5L)
.setExpiryTime(5L)
.setEventReportWindow(55L)
.setAggregatableReportWindow(555L)
.setIsDebugReporting(true)
.setEventReportDedupKeys(
LongStream.range(0, 2)
.boxed()
.map(UnsignedLong::new)
.collect(Collectors.toList()))
.setAggregateReportDedupKeys(
LongStream.range(0, 2)
.boxed()
.map(UnsignedLong::new)
.collect(Collectors.toList()))
.setStatus(Source.Status.ACTIVE)
.setSourceType(Source.SourceType.EVENT)
.setRegistrant(Uri.parse("android-app://com.example.abc"))
.setFilterData(filterMap.toString())
.setAggregateSource(aggregateSource.toString())
.setAggregateContributions(50001)
.setDebugKey(DEBUG_KEY_1)
.setRegistrationId("R1")
.setSharedAggregationKeys(sharedAggregateKeys)
.setSharedFilterDataKeys(sharedFilterDataKeys)
.setInstallTime(100L)
.setParentId(parentId)
.setDebugJoinKey(debugJoinKey)
.setPlatformAdId(debugAppAdId)
.setDebugAdId(debugWebAdId)
.setRegistrationOrigin(WebUtil.validUri("https://subdomain.example.test"))
.setCoarseEventReportDestinations(true)
.setSharedDebugKey(SHARED_DEBUG_KEY_1)
.setAttributedTriggers(ATTRIBUTED_TRIGGERS)
.setTriggerSpecs(triggerSpecs)
.setTriggerSpecsString(triggerSpecs.encodeToJson())
.setMaxEventLevelReports(triggerSpecs.getMaxReports())
.setEventAttributionStatus(null)
.setPrivacyParameters(triggerSpecs.encodePrivacyParametersToJSONString())
.setDropSourceIfInstalled(true)
.build());
}
@Test
public void testEqualsFail() throws JSONException {
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setEventId(new UnsignedLong(1L))
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setEventId(new UnsignedLong(2L))
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setAppDestinations(List.of(Uri.parse("android-app://1.test")))
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setAppDestinations(List.of(Uri.parse("android-app://2.test")))
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setWebDestinations(List.of(Uri.parse("https://1.test")))
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setWebDestinations(List.of(Uri.parse("https://2.test")))
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setEnrollmentId("enrollment-id-1")
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setEnrollmentId("enrollment-id-2")
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setPublisher(Uri.parse("https://1.test"))
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setPublisher(Uri.parse("https://2.test"))
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder().setParentId("parent-id-1").build(),
SourceFixture.getMinimalValidSourceBuilder().setParentId("parent-id-2").build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setPublisherType(EventSurfaceType.APP)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setPublisherType(EventSurfaceType.WEB)
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder().setPriority(1L).build(),
SourceFixture.getMinimalValidSourceBuilder().setPriority(2L).build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder().setEventTime(1L).build(),
SourceFixture.getMinimalValidSourceBuilder().setEventTime(2L).build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder().setExpiryTime(1L).build(),
SourceFixture.getMinimalValidSourceBuilder().setExpiryTime(2L).build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder().setEventReportWindow(1L).build(),
SourceFixture.getMinimalValidSourceBuilder().setEventReportWindow(2L).build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setAggregatableReportWindow(1L)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setAggregatableReportWindow(2L)
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.EVENT)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.NAVIGATION)
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setStatus(Source.Status.ACTIVE)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setStatus(Source.Status.IGNORED)
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setEventReportDedupKeys(
LongStream.range(0, 2)
.boxed()
.map(UnsignedLong::new)
.collect(Collectors.toList()))
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setEventReportDedupKeys(
LongStream.range(1, 3)
.boxed()
.map(UnsignedLong::new)
.collect(Collectors.toList()))
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setAggregateReportDedupKeys(
LongStream.range(0, 2)
.boxed()
.map(UnsignedLong::new)
.collect(Collectors.toList()))
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setAggregateReportDedupKeys(
LongStream.range(1, 3)
.boxed()
.map(UnsignedLong::new)
.collect(Collectors.toList()))
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setRegistrant(Uri.parse("android-app://com.example.abc"))
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setRegistrant(Uri.parse("android-app://com.example.xyz"))
.build());
assertNotEquals(
SourceFixture.ValidSourceParams.buildAggregatableAttributionSource(),
SourceFixture.getMinimalValidSourceBuilder()
.setAggregatableAttributionSource(
new AggregatableAttributionSource.Builder().build())
.build());
JSONObject aggregateSource1 = new JSONObject();
aggregateSource1.put("campaignCounts", "0x159");
JSONObject aggregateSource2 = new JSONObject();
aggregateSource2.put("geoValue", "0x5");
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setAggregateSource(aggregateSource1.toString())
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setAggregateSource(aggregateSource2.toString())
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setAggregateContributions(4000)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setAggregateContributions(4055)
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder().setDebugKey(DEBUG_KEY_1).build(),
SourceFixture.getMinimalValidSourceBuilder().setDebugKey(DEBUG_KEY_2).build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder().setRegistrationId("R1").build(),
SourceFixture.getMinimalValidSourceBuilder().setRegistrationId("R2").build());
String sharedAggregationKeys1 = "[\"key1\"]";
String sharedAggregationKeys2 = "[\"key2\"]";
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setSharedAggregationKeys(sharedAggregationKeys1)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setSharedAggregationKeys(sharedAggregationKeys2)
.build());
String sharedFilterDataKeys1 = "[\"key1\"]";
String sharedFilterDataKeys2 = "[\"key2\"]";
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setSharedFilterDataKeys(sharedFilterDataKeys1)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setSharedFilterDataKeys(sharedFilterDataKeys2)
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder().setInstallTime(100L).build(),
SourceFixture.getMinimalValidSourceBuilder().setInstallTime(101L).build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setDebugJoinKey("debugJoinKey1")
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setDebugJoinKey("debugJoinKey2")
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setPlatformAdId("debugAppAdId1")
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setPlatformAdId("debugAppAdId2")
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder().setDebugAdId("debugWebAdId1").build(),
SourceFixture.getMinimalValidSourceBuilder().setDebugAdId("debugWebAdId2").build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setRegistrationOrigin(WebUtil.validUri("https://subdomain1.example.test"))
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setRegistrationOrigin(WebUtil.validUri("https://subdomain2.example.test"))
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setCoarseEventReportDestinations(false)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setCoarseEventReportDestinations(true)
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setSharedDebugKey(SHARED_DEBUG_KEY_1)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setSharedDebugKey(SHARED_DEBUG_KEY_2)
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setAttributedTriggers(ATTRIBUTED_TRIGGERS)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setAttributedTriggers(new ArrayList<>())
.build());
TriggerSpecs triggerSpecsValueSumBased =
new TriggerSpecs(
SourceFixture.getTriggerSpecValueSumArrayValidBaseline(),
5,
null);
TriggerSpecs triggerSpecsCountBased = SourceFixture.getValidTriggerSpecsCountBased();
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setTriggerSpecsString(triggerSpecsValueSumBased.encodeToJson())
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setTriggerSpecsString(triggerSpecsCountBased.encodeToJson())
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setMaxEventLevelReports(3)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setMaxEventLevelReports(4)
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setEventAttributionStatus(null)
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setEventAttributionStatus(new JSONArray().toString())
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder()
.setPrivacyParameters(
triggerSpecsValueSumBased.encodePrivacyParametersToJSONString())
.build(),
SourceFixture.getMinimalValidSourceBuilder()
.setPrivacyParameters(
triggerSpecsCountBased.encodePrivacyParametersToJSONString())
.build());
assertNotEquals(
SourceFixture.getMinimalValidSourceBuilder().setDropSourceIfInstalled(true).build(),
SourceFixture.getMinimalValidSourceBuilder()
.setDropSourceIfInstalled(false)
.build());
}
@Test
public void testSourceBuilder_validateArgumentPublisher() {
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
null,
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS,
SourceFixture.ValidSourceParams.WEB_DESTINATIONS,
SourceFixture.ValidSourceParams.ENROLLMENT_ID,
SourceFixture.ValidSourceParams.REGISTRANT,
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
SourceFixture.ValidSourceParams.SOURCE_TYPE,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
Uri.parse("com.source"),
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS,
SourceFixture.ValidSourceParams.WEB_DESTINATIONS,
SourceFixture.ValidSourceParams.ENROLLMENT_ID,
SourceFixture.ValidSourceParams.REGISTRANT,
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
SourceFixture.ValidSourceParams.SOURCE_TYPE,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
}
@Test
public void testSourceBuilder_validateArgumentAttributionDestination() {
// Invalid app Uri
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
SourceFixture.ValidSourceParams.PUBLISHER,
List.of(Uri.parse("com.destination")),
SourceFixture.ValidSourceParams.WEB_DESTINATIONS,
SourceFixture.ValidSourceParams.ENROLLMENT_ID,
SourceFixture.ValidSourceParams.REGISTRANT,
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
SourceFixture.ValidSourceParams.SOURCE_TYPE,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
// Invalid web Uri
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
SourceFixture.ValidSourceParams.PUBLISHER,
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS,
List.of(Uri.parse("com.destination")),
SourceFixture.ValidSourceParams.ENROLLMENT_ID,
SourceFixture.ValidSourceParams.REGISTRANT,
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
SourceFixture.ValidSourceParams.SOURCE_TYPE,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
// Empty app destinations list
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
SourceFixture.ValidSourceParams.PUBLISHER,
new ArrayList<>(),
SourceFixture.ValidSourceParams.WEB_DESTINATIONS,
SourceFixture.ValidSourceParams.ENROLLMENT_ID,
SourceFixture.ValidSourceParams.REGISTRANT,
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
SourceFixture.ValidSourceParams.SOURCE_TYPE,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
// Empty web destinations list
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
SourceFixture.ValidSourceParams.PUBLISHER,
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS,
new ArrayList<>(),
SourceFixture.ValidSourceParams.ENROLLMENT_ID,
SourceFixture.ValidSourceParams.REGISTRANT,
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
SourceFixture.ValidSourceParams.SOURCE_TYPE,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
// Too many app destinations
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
SourceFixture.ValidSourceParams.PUBLISHER,
List.of(
Uri.parse("android-app://com.destination"),
Uri.parse("android-app://com.destination2")),
SourceFixture.ValidSourceParams.WEB_DESTINATIONS,
SourceFixture.ValidSourceParams.ENROLLMENT_ID,
SourceFixture.ValidSourceParams.REGISTRANT,
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
SourceFixture.ValidSourceParams.SOURCE_TYPE,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
}
@Test
public void testSourceBuilder_validateArgumentEnrollmentId() {
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
SourceFixture.ValidSourceParams.PUBLISHER,
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS,
SourceFixture.ValidSourceParams.WEB_DESTINATIONS,
null,
SourceFixture.ValidSourceParams.REGISTRANT,
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
SourceFixture.ValidSourceParams.SOURCE_TYPE,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
}
@Test
public void testSourceBuilder_validateArgumentRegistrant() {
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
SourceFixture.ValidSourceParams.PUBLISHER,
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS,
SourceFixture.ValidSourceParams.WEB_DESTINATIONS,
SourceFixture.ValidSourceParams.ENROLLMENT_ID,
null,
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
SourceFixture.ValidSourceParams.SOURCE_TYPE,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
SourceFixture.ValidSourceParams.PUBLISHER,
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS,
SourceFixture.ValidSourceParams.WEB_DESTINATIONS,
SourceFixture.ValidSourceParams.ENROLLMENT_ID,
Uri.parse("com.registrant"),
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
SourceFixture.ValidSourceParams.SOURCE_TYPE,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
}
@Test
public void testSourceBuilder_validateArgumentSourceType() {
assertInvalidSourceArguments(
SourceFixture.ValidSourceParams.SOURCE_EVENT_ID,
SourceFixture.ValidSourceParams.PUBLISHER,
SourceFixture.ValidSourceParams.ATTRIBUTION_DESTINATIONS,
SourceFixture.ValidSourceParams.WEB_DESTINATIONS,
SourceFixture.ValidSourceParams.ENROLLMENT_ID,
SourceFixture.ValidSourceParams.REGISTRANT,
SourceFixture.ValidSourceParams.SOURCE_EVENT_TIME,
SourceFixture.ValidSourceParams.EXPIRY_TIME,
SourceFixture.ValidSourceParams.PRIORITY,
null,
SourceFixture.ValidSourceParams.INSTALL_ATTRIBUTION_WINDOW,
SourceFixture.ValidSourceParams.INSTALL_COOLDOWN_WINDOW,
SourceFixture.ValidSourceParams.DEBUG_KEY,
SourceFixture.ValidSourceParams.ATTRIBUTION_MODE,
SourceFixture.ValidSourceParams.buildAggregateSource(),
SourceFixture.ValidSourceParams.buildFilterData(),
SourceFixture.ValidSourceParams.REGISTRATION_ID,
SourceFixture.ValidSourceParams.SHARED_AGGREGATE_KEYS,
SourceFixture.ValidSourceParams.SHARED_FILTER_DATA_KEYS,
SourceFixture.ValidSourceParams.INSTALL_TIME,
SourceFixture.ValidSourceParams.REGISTRATION_ORIGIN);
}
@Test
public void testAggregatableAttributionSource() throws Exception {
final TreeMap<String, BigInteger> aggregatableSource = new TreeMap<>();
aggregatableSource.put("2", new BigInteger("71"));
final Map<String, List<String>> filterMap = Map.of("x", List.of("1"));
final AggregatableAttributionSource attributionSource =
new AggregatableAttributionSource.Builder()
.setAggregatableSource(aggregatableSource)
.setFilterMap(
new FilterMap.Builder()
.setAttributionFilterMap(filterMap)
.build())
.build();
final Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setAggregatableAttributionSource(attributionSource)
.build();
Trigger trigger = TriggerFixture.getValidTrigger();
assertNotNull(source.getAggregatableAttributionSource(trigger, mFlags).orElse(null));
assertNotNull(
source.getAggregatableAttributionSource(trigger, mFlags)
.orElse(null)
.getAggregatableSource());
assertNotNull(
source.getAggregatableAttributionSource(trigger, mFlags)
.orElse(null)
.getFilterMap());
assertEquals(
aggregatableSource,
source.getAggregatableAttributionSource(trigger, mFlags)
.orElse(null)
.getAggregatableSource());
assertEquals(
filterMap,
source.getAggregatableAttributionSource(trigger, mFlags)
.orElse(null)
.getFilterMap()
.getAttributionFilterMap());
}
@Test
public void testAggregatableAttributionSourceWithTrigger_addsLookbackWindow() throws Exception {
when(mFlags.getMeasurementEnableLookbackWindowFilter()).thenReturn(true);
JSONObject aggregatableSource = new JSONObject();
aggregatableSource.put("campaignCounts", "0x159");
aggregatableSource.put("geoValue", "0x5");
JSONObject filterMapJson = new JSONObject();
filterMapJson.put("conversion", new JSONArray(Collections.singletonList("electronics")));
final Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setAggregateSource(aggregatableSource.toString())
.setFilterData(filterMapJson.toString())
.build();
Trigger trigger = TriggerFixture.getValidTrigger();
Optional<AggregatableAttributionSource> aggregatableAttributionSource =
source.getAggregatableAttributionSource(trigger, mFlags);
assertThat(aggregatableAttributionSource.isPresent()).isTrue();
assertThat(aggregatableAttributionSource.get().getAggregatableSource())
.containsExactly(
"campaignCounts",
new BigInteger("159", 16),
"geoValue",
new BigInteger("5", 16));
assertThat(
aggregatableAttributionSource
.get()
.getFilterMap()
.getAttributionFilterMapWithLongValue())
.containsExactly(
"conversion",
FilterValue.ofStringList(Collections.singletonList("electronics")),
"source_type",
FilterValue.ofStringList(Collections.singletonList("event")),
FilterMap.LOOKBACK_WINDOW,
FilterValue.ofLong(8640000L));
}
@Test
public void testTriggerDataCardinality() {
Source eventSource =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.EVENT)
.build();
assertEquals(
PrivacyParams.EVENT_TRIGGER_DATA_CARDINALITY,
eventSource.getTriggerDataCardinality());
Source navigationSource =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.NAVIGATION)
.build();
assertEquals(
PrivacyParams.getNavigationTriggerDataCardinality(),
navigationSource.getTriggerDataCardinality());
}
@Test
public void testGetAttributionDestinations() {
Source source = SourceFixture.getValidSource();
assertEquals(
source.getAttributionDestinations(EventSurfaceType.APP),
source.getAppDestinations());
assertEquals(
source.getAttributionDestinations(EventSurfaceType.WEB),
source.getWebDestinations());
}
@Test
public void testGetFilterData_nonEmpty() throws JSONException {
JSONObject filterMapJson = new JSONObject();
filterMapJson.put("conversion", new JSONArray(Collections.singletonList("electronics")));
filterMapJson.put("product", new JSONArray(Arrays.asList("1234", "2345")));
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.NAVIGATION)
.setFilterData(filterMapJson.toString())
.build();
Trigger trigger = TriggerFixture.getValidTrigger();
FilterMap filterMap = source.getFilterData(trigger, mFlags);
assertEquals(filterMap.getAttributionFilterMap().size(), 3);
assertEquals(Collections.singletonList("electronics"),
filterMap.getAttributionFilterMap().get("conversion"));
assertEquals(Arrays.asList("1234", "2345"),
filterMap.getAttributionFilterMap().get("product"));
assertEquals(
Collections.singletonList("navigation"),
filterMap.getAttributionFilterMap().get("source_type"));
}
@Test
public void testGetFilterData_withTrigger_addsLookbackWindow() throws JSONException {
when(mFlags.getMeasurementEnableLookbackWindowFilter()).thenReturn(true);
JSONObject filterMapJson = new JSONObject();
filterMapJson.put("conversion", new JSONArray(List.of("electronics")));
filterMapJson.put("product", new JSONArray(List.of("1234", "2345")));
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.NAVIGATION)
.setFilterData(filterMapJson.toString())
.build();
Trigger trigger = TriggerFixture.getValidTrigger();
FilterMap filterMap = source.getFilterData(trigger, mFlags);
assertThat(filterMap.getAttributionFilterMapWithLongValue())
.containsExactly(
"conversion",
FilterValue.ofStringList(List.of("electronics")),
"product",
FilterValue.ofStringList(List.of("1234", "2345")),
"source_type",
FilterValue.ofStringList(List.of("navigation")),
FilterMap.LOOKBACK_WINDOW,
FilterValue.ofLong(8640000L));
}
@Test
public void testGetFilterData_withTriggerAndEmptyData_addsLookbackWindow()
throws JSONException {
when(mFlags.getMeasurementEnableLookbackWindowFilter()).thenReturn(true);
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.NAVIGATION)
.build();
Trigger trigger = TriggerFixture.getValidTrigger();
FilterMap filterMap = source.getFilterData(trigger, mFlags);
assertThat(filterMap.getAttributionFilterMapWithLongValue())
.containsExactly(
"source_type",
FilterValue.ofStringList(List.of("navigation")),
FilterMap.LOOKBACK_WINDOW,
FilterValue.ofLong(8640000L));
}
@Test
public void testGetSharedFilterData_withLongValue_success() throws JSONException {
when(mFlags.getMeasurementEnableLookbackWindowFilter()).thenReturn(true);
JSONObject filterMapJson = new JSONObject();
filterMapJson.put("conversion", new JSONArray(List.of("electronics")));
filterMapJson.put("product", new JSONArray(List.of("1234", "2345")));
String sharedFilterDataKeys = "[\"product\"]";
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.NAVIGATION)
.setFilterData(filterMapJson.toString())
.setSharedFilterDataKeys(sharedFilterDataKeys)
.build();
Trigger trigger = TriggerFixture.getValidTrigger();
FilterMap filterMap = source.getSharedFilterData(trigger, mFlags);
assertThat(filterMap.getAttributionFilterMapWithLongValue())
.containsExactly("product", FilterValue.ofStringList(List.of("1234", "2345")));
}
@Test
public void testGetSharedFilterData_success() throws JSONException {
when(mFlags.getMeasurementEnableLookbackWindowFilter()).thenReturn(false);
JSONObject filterMapJson = new JSONObject();
filterMapJson.put("conversion", new JSONArray(List.of("electronics")));
filterMapJson.put("product", new JSONArray(List.of("1234", "2345")));
String sharedFilterDataKeys = "[\"product\"]";
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.NAVIGATION)
.setFilterData(filterMapJson.toString())
.setSharedFilterDataKeys(sharedFilterDataKeys)
.build();
Trigger trigger = TriggerFixture.getValidTrigger();
FilterMap filterMap = source.getSharedFilterData(trigger, mFlags);
assertThat(filterMap.getAttributionFilterMap())
.containsExactly("product", List.of("1234", "2345"));
}
@Test
public void testGetFilterData_nullFilterData() throws JSONException {
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.EVENT)
.build();
Trigger trigger = TriggerFixture.getValidTrigger();
FilterMap filterMap = source.getFilterData(trigger, mFlags);
assertEquals(filterMap.getAttributionFilterMap().size(), 1);
assertEquals(Collections.singletonList("event"),
filterMap.getAttributionFilterMap().get("source_type"));
}
@Test
public void testGetFilterData_emptyFilterData() throws JSONException {
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.EVENT)
.setFilterData("")
.build();
Trigger trigger = TriggerFixture.getValidTrigger();
FilterMap filterMap = source.getFilterData(trigger, mFlags);
assertEquals(filterMap.getAttributionFilterMap().size(), 1);
assertEquals(Collections.singletonList("event"),
filterMap.getAttributionFilterMap().get("source_type"));
}
@Test
public void testParseAggregateSource() throws JSONException {
JSONObject aggregatableSource = new JSONObject();
aggregatableSource.put("campaignCounts", "0x159");
aggregatableSource.put("geoValue", "0x5");
JSONObject filterMap = new JSONObject();
filterMap.put("conversion_subdomain",
new JSONArray(Collections.singletonList("electronics.megastore")));
filterMap.put("product", new JSONArray(Arrays.asList("1234", "2345")));
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.NAVIGATION)
.setAggregateSource(aggregatableSource.toString())
.setFilterData(filterMap.toString())
.build();
Trigger trigger = TriggerFixture.getValidTrigger();
Optional<AggregatableAttributionSource> aggregatableAttributionSource =
source.getAggregatableAttributionSource(trigger, mFlags);
assertTrue(aggregatableAttributionSource.isPresent());
AggregatableAttributionSource aggregateSource = aggregatableAttributionSource.get();
assertEquals(aggregateSource.getAggregatableSource().size(), 2);
assertEquals(
aggregateSource.getAggregatableSource().get("campaignCounts").longValue(), 345L);
assertEquals(aggregateSource.getAggregatableSource().get("geoValue").longValue(), 5L);
assertEquals(aggregateSource.getFilterMap().getAttributionFilterMap().size(), 3);
}
@Test
public void fromBuilder_equalsComparison_success() {
// Setup
Source fromSource = SourceFixture.getValidSource();
// Assertion
assertEquals(fromSource, Source.Builder.from(fromSource).build());
}
@Test
public void setSharedDebugKey_success() throws JSONException {
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setSharedDebugKey(SHARED_DEBUG_KEY_1)
.build();
assertEquals(SHARED_DEBUG_KEY_1, source.getSharedDebugKey());
}
@Test
public void attributedTriggersToJson_buildAttributedTriggers_encodesAndDecodes()
throws JSONException {
JSONArray existingAttributes = new JSONArray();
JSONObject triggerRecord1 = new JSONObject();
triggerRecord1.put("trigger_id", "100");
triggerRecord1.put("trigger_data", new UnsignedLong(123L).toString());
triggerRecord1.put("dedup_key", new UnsignedLong(456L).toString());
JSONObject triggerRecord2 = new JSONObject();
triggerRecord2.put("trigger_id", "200");
triggerRecord2.put("trigger_data", new UnsignedLong(12L).toString());
triggerRecord2.put("dedup_key", new UnsignedLong(45678L).toString());
existingAttributes.put(triggerRecord1);
existingAttributes.put(triggerRecord2);
Source sourceWithEventAttributionStatus =
SourceFixture.getValidSourceBuilder()
.setEventAttributionStatus(existingAttributes.toString())
.setAttributedTriggers(null)
.build();
sourceWithEventAttributionStatus.buildAttributedTriggers();
String encodedTriggerStatusJson =
sourceWithEventAttributionStatus.attributedTriggersToJson();
JSONArray encodedTriggerStatus = new JSONArray(encodedTriggerStatusJson);
Source sourceWithEventAttributionStatusAfterDecoding =
SourceFixture.getValidSourceBuilder()
.setEventAttributionStatus(encodedTriggerStatusJson)
.setAttributedTriggers(null)
.build();
sourceWithEventAttributionStatusAfterDecoding.buildAttributedTriggers();
// Assertion
HashSet<String> keys1 = new HashSet(triggerRecord1.keySet());
keys1.addAll(encodedTriggerStatus.getJSONObject(0).keySet());
for (String key : keys1) {
assertEquals(
triggerRecord1.get(key).toString(),
encodedTriggerStatus.getJSONObject(0).get(key).toString());
}
HashSet<String> keys2 = new HashSet(triggerRecord2.keySet());
keys2.addAll(encodedTriggerStatus.getJSONObject(1).keySet());
for (String key : keys2) {
assertEquals(
triggerRecord2.get(key).toString(),
encodedTriggerStatus.getJSONObject(1).get(key).toString());
}
assertEquals(
sourceWithEventAttributionStatus.getAttributedTriggers().size(),
sourceWithEventAttributionStatusAfterDecoding.getAttributedTriggers().size());
for (int i = 0; i < sourceWithEventAttributionStatus.getAttributedTriggers().size(); i++) {
assertEquals(
sourceWithEventAttributionStatus.getAttributedTriggers().get(i),
sourceWithEventAttributionStatusAfterDecoding.getAttributedTriggers().get(i));
}
}
@Test
public void attributedTriggersToJsonFlexApi_buildAttributedTriggers_encodesAndDecodes()
throws JSONException {
JSONArray existingAttributes = new JSONArray();
JSONObject triggerRecord1 = new JSONObject();
triggerRecord1.put("trigger_id", "100");
triggerRecord1.put("value", 2L);
triggerRecord1.put("priority", 1L);
triggerRecord1.put("trigger_time", 1689564817000L);
triggerRecord1.put("trigger_data", new UnsignedLong(1L).toString());
/* dedup_key not provided */
JSONObject triggerRecord2 = new JSONObject();
triggerRecord2.put("trigger_id", "200");
triggerRecord2.put("value", 3L);
triggerRecord2.put("priority", 4L);
triggerRecord2.put("trigger_time", 1689564817010L);
triggerRecord2.put("trigger_data", new UnsignedLong(1L).toString());
triggerRecord2.put("dedup_key", new UnsignedLong(45678L).toString());
existingAttributes.put(triggerRecord1);
existingAttributes.put(triggerRecord2);
Source sourceWithEventAttributionStatus =
SourceFixture.getValidSourceBuilder()
.setEventAttributionStatus(existingAttributes.toString())
.setAttributedTriggers(null)
.build();
sourceWithEventAttributionStatus.buildAttributedTriggers();
String encodedTriggerStatusJson =
sourceWithEventAttributionStatus.attributedTriggersToJsonFlexApi();
JSONArray encodedTriggerStatus = new JSONArray(encodedTriggerStatusJson);
Source sourceWithEventAttributionStatusAfterDecoding =
SourceFixture.getValidSourceBuilder()
.setEventAttributionStatus(encodedTriggerStatusJson)
.setAttributedTriggers(null)
.build();
sourceWithEventAttributionStatusAfterDecoding.buildAttributedTriggers();
// Assertion
HashSet<String> keys1 = new HashSet(triggerRecord1.keySet());
keys1.addAll(encodedTriggerStatus.getJSONObject(0).keySet());
for (String key : keys1) {
assertEquals(
triggerRecord1.get(key).toString(),
encodedTriggerStatus.getJSONObject(0).get(key).toString());
}
HashSet<String> keys2 = new HashSet(triggerRecord2.keySet());
keys2.addAll(encodedTriggerStatus.getJSONObject(1).keySet());
for (String key : keys2) {
assertEquals(
triggerRecord2.get(key).toString(),
encodedTriggerStatus.getJSONObject(1).get(key).toString());
}
assertEquals(
sourceWithEventAttributionStatus.getAttributedTriggers().size(),
sourceWithEventAttributionStatusAfterDecoding.getAttributedTriggers().size());
for (int i = 0; i < sourceWithEventAttributionStatus.getAttributedTriggers().size(); i++) {
assertEquals(
sourceWithEventAttributionStatus.getAttributedTriggers().get(i),
sourceWithEventAttributionStatusAfterDecoding.getAttributedTriggers().get(i));
}
}
@Test
public void buildAttributedTriggers_multipleCalls_doesNotParseAttributionStatus()
throws JSONException {
JSONArray existingAttributes = new JSONArray();
JSONObject triggerRecord = new JSONObject();
triggerRecord.put("trigger_id", "100");
triggerRecord.put("value", 2L);
triggerRecord.put("priority", 1L);
triggerRecord.put("trigger_time", 1689564817000L);
triggerRecord.put("trigger_data", new UnsignedLong(1L).toString());
triggerRecord.put("dedup_key", new UnsignedLong(34567L).toString());
existingAttributes.put(triggerRecord);
Source sourceWithEventAttributionStatus =
SourceFixture.getValidSourceBuilder()
.setEventAttributionStatus(existingAttributes.toString())
.setAttributedTriggers(null)
.build();
Source spySource = spy(sourceWithEventAttributionStatus);
// Multiple calls to `Source::buildAttributedTriggers`
spySource.buildAttributedTriggers();
spySource.buildAttributedTriggers();
spySource.buildAttributedTriggers();
verify(spySource, times(1)).parseEventAttributionStatus();
}
@Test
public void triggerSpecs_encodingDecoding_equal() throws JSONException {
// Setup
Source validSource = SourceFixture.getValidSourceWithFlexEventReport();
TriggerSpecs originalTriggerSpecs = validSource.getTriggerSpecs();
String encodedTriggerSpecs = originalTriggerSpecs.encodeToJson();
String encodeddMaxReports = Integer.toString(originalTriggerSpecs.getMaxReports());
String encodedPrivacyParameters =
originalTriggerSpecs.encodePrivacyParametersToJSONString();
TriggerSpecs triggerSpecs =
new TriggerSpecs(
encodedTriggerSpecs,
encodeddMaxReports,
validSource,
encodedPrivacyParameters);
// Assertion
assertEquals(originalTriggerSpecs.getMaxReports(), triggerSpecs.getMaxReports());
assertArrayEquals(originalTriggerSpecs.getTriggerSpecs(), triggerSpecs.getTriggerSpecs());
assertEquals(originalTriggerSpecs, triggerSpecs);
}
private void assertInvalidSourceArguments(
UnsignedLong sourceEventId,
Uri publisher,
List<Uri> appDestinations,
List<Uri> webDestinations,
String enrollmentId,
Uri registrant,
Long sourceEventTime,
Long expiryTime,
Long priority,
Source.SourceType sourceType,
Long installAttributionWindow,
Long installCooldownWindow,
@Nullable UnsignedLong debugKey,
@Source.AttributionMode int attributionMode,
@Nullable String aggregateSource,
@Nullable String filterData,
@Nullable String registrationId,
@Nullable String sharedAggregationKeys,
@Nullable String sharedFilterDataKeys,
@Nullable Long installTime,
Uri registrationOrigin) {
assertThrows(
IllegalArgumentException.class,
() ->
new Source.Builder()
.setEventId(sourceEventId)
.setPublisher(publisher)
.setAppDestinations(appDestinations)
.setWebDestinations(webDestinations)
.setEnrollmentId(enrollmentId)
.setRegistrant(registrant)
.setEventTime(sourceEventTime)
.setExpiryTime(expiryTime)
.setPriority(priority)
.setSourceType(sourceType)
.setInstallAttributionWindow(installAttributionWindow)
.setInstallCooldownWindow(installCooldownWindow)
.setAttributionMode(attributionMode)
.setAggregateSource(aggregateSource)
.setFilterData(filterData)
.setDebugKey(debugKey)
.setRegistrationId(registrationId)
.setSharedAggregationKeys(sharedAggregationKeys)
.setInstallTime(installTime)
.setRegistrationOrigin(registrationOrigin)
.setSharedFilterDataKeys(sharedFilterDataKeys)
.build());
}
@Test
public void getTriggerDataCardinality_flexEventApi_equals() {
Source eventSource =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.EVENT)
.build();
assertEquals(
PrivacyParams.EVENT_TRIGGER_DATA_CARDINALITY,
eventSource.getTriggerDataCardinality());
Source navigationSource =
SourceFixture.getMinimalValidSourceBuilder()
.setSourceType(Source.SourceType.NAVIGATION)
.build();
assertEquals(
PrivacyParams.getNavigationTriggerDataCardinality(),
navigationSource.getTriggerDataCardinality());
}
@Test
public void isFlexEventApiValueValid_eventSource_true() throws JSONException {
Flags flags = mock(Flags.class);
doReturn(2).when(flags).getMeasurementVtcConfigurableMaxEventReportsCount();
doReturn(true).when(flags).getMeasurementFlexibleEventReportingApiEnabled();
doReturn(Flags.MEASUREMENT_FLEX_API_MAX_INFO_GAIN_EVENT)
.when(flags)
.getMeasurementFlexApiMaxInformationGainEvent();
doReturn(Flags.MEASUREMENT_FLEX_API_MAX_INFO_GAIN_NAVIGATION)
.when(flags)
.getMeasurementFlexApiMaxInformationGainNavigation();
// setup
String triggerSpecsString =
"[{\"trigger_data\": [1, 2],"
+ "\"event_report_windows\": { "
+ "\"start_time\": \"0\", "
+ String.format("\"end_times\": [%s]}, ", TimeUnit.DAYS.toMillis(7))
+ "\"summary_window_operator\": \"count\", "
+ "\"summary_buckets\": [1]}]\n";
TriggerSpec[] triggerSpecsArray = TriggerSpecsUtil.triggerSpecArrayFrom(triggerSpecsString);
int maxEventLevelReports = 3;
Source testSource =
SourceFixture.getMinimalValidSourceBuilder()
.setEventId(new UnsignedLong(1L))
.setAppDestinations(List.of(Uri.parse("android-app://com.destination1")))
.setWebDestinations(
List.of(WebUtil.validUri("https://web-destination1.test")))
.setRegistrant(Uri.parse("android-app://com.example"))
.setEventTime(new Random().nextLong())
.setExpiryTime(8640000010L)
.setPriority(100L)
.setSourceType(Source.SourceType.NAVIGATION)
.setAttributionMode(Source.AttributionMode.TRUTHFULLY)
.setDebugKey(new UnsignedLong(47823478789L))
.setMaxEventLevelReports(maxEventLevelReports)
.setTriggerSpecs(new TriggerSpecs(
triggerSpecsArray, maxEventLevelReports, null))
.build();
assertTrue(testSource.isFlexEventApiValueValid(flags));
}
@Test
public void isFlexEventApiValueValid_eventSource_false() throws JSONException {
Flags flags = mock(Flags.class);
doReturn(2).when(flags).getMeasurementVtcConfigurableMaxEventReportsCount();
doReturn(true).when(flags).getMeasurementFlexibleEventReportingApiEnabled();
doReturn(Flags.MEASUREMENT_FLEX_API_MAX_INFO_GAIN_EVENT)
.when(flags)
.getMeasurementFlexApiMaxInformationGainEvent();
doReturn(Flags.MEASUREMENT_FLEX_API_MAX_INFO_GAIN_NAVIGATION)
.when(flags)
.getMeasurementFlexApiMaxInformationGainNavigation();
// setup
String triggerSpecsString =
"[{\"trigger_data\": [1, 2, 3, 4, 5, 6, 7, 8],"
+ "\"event_report_windows\": { "
+ "\"start_time\": \"0\", "
+ String.format(
"\"end_times\": [%s, %s, %s]}, ",
TimeUnit.DAYS.toMillis(2),
TimeUnit.DAYS.toMillis(7),
TimeUnit.DAYS.toMillis(30))
+ "\"summary_window_operator\": \"count\", "
+ "\"summary_buckets\": [1, 2, 3]}]\n";
TriggerSpec[] triggerSpecsArray = TriggerSpecsUtil.triggerSpecArrayFrom(triggerSpecsString);
int maxEventLevelReports = 3;
Source testSource =
SourceFixture.getMinimalValidSourceBuilder()
.setEventId(new UnsignedLong(1L))
.setAppDestinations(List.of(Uri.parse("android-app://com.destination1")))
.setWebDestinations(
List.of(WebUtil.validUri("https://web-destination1.test")))
.setRegistrant(Uri.parse("android-app://com.example"))
.setEventTime(new Random().nextLong())
.setExpiryTime(8640000010L)
.setPriority(100L)
.setSourceType(Source.SourceType.EVENT)
.setAttributionMode(Source.AttributionMode.TRUTHFULLY)
.setDebugKey(new UnsignedLong(47823478789L))
.setMaxEventLevelReports(3)
.setTriggerSpecs(new TriggerSpecs(
triggerSpecsArray, maxEventLevelReports, null))
.build();
assertFalse(testSource.isFlexEventApiValueValid(flags));
}
@Test
public void isFlexEventApiValueValid_navigationSource_true() throws JSONException {
Flags flags = mock(Flags.class);
doReturn(2).when(flags).getMeasurementVtcConfigurableMaxEventReportsCount();
doReturn(true).when(flags).getMeasurementFlexibleEventReportingApiEnabled();
doReturn(Flags.MEASUREMENT_FLEX_API_MAX_INFO_GAIN_EVENT)
.when(flags)
.getMeasurementFlexApiMaxInformationGainEvent();
doReturn(Flags.MEASUREMENT_FLEX_API_MAX_INFO_GAIN_NAVIGATION)
.when(flags)
.getMeasurementFlexApiMaxInformationGainNavigation();
// setup
String triggerSpecsString =
"[{\"trigger_data\": [1, 2, 3, 4, 5, 6, 7, 8],"
+ "\"event_report_windows\": { "
+ "\"start_time\": \"0\", "
+ String.format(
"\"end_times\": [%s, %s, %s]}, ",
TimeUnit.DAYS.toMillis(2),
TimeUnit.DAYS.toMillis(7),
TimeUnit.DAYS.toMillis(30))
+ "\"summary_window_operator\": \"count\", "
+ "\"summary_buckets\": [1, 2, 3]}]\n";
TriggerSpec[] triggerSpecsArray = TriggerSpecsUtil.triggerSpecArrayFrom(triggerSpecsString);
int maxEventLevelReports = 3;
Source testSource =
SourceFixture.getMinimalValidSourceBuilder()
.setEventId(new UnsignedLong(1L))
.setAppDestinations(List.of(Uri.parse("android-app://com.destination1")))
.setWebDestinations(
List.of(WebUtil.validUri("https://web-destination1.test")))
.setRegistrant(Uri.parse("android-app://com.example"))
.setEventTime(new Random().nextLong())
.setExpiryTime(8640000010L)
.setPriority(100L)
.setSourceType(Source.SourceType.NAVIGATION)
.setAttributionMode(Source.AttributionMode.TRUTHFULLY)
.setDebugKey(new UnsignedLong(47823478789L))
.setMaxEventLevelReports(maxEventLevelReports)
.setTriggerSpecs(new TriggerSpecs(
triggerSpecsArray, maxEventLevelReports, null))
.build();
assertTrue(testSource.isFlexEventApiValueValid(flags));
}
@Test
public void buildTriggerSpecs_validParams_pass() throws JSONException {
String triggerSpecsString =
"[{\"trigger_data\": [1, 2, 3],"
+ "\"event_report_windows\": { "
+ "\"start_time\": \"0\", "
+ String.format(
"\"end_times\": [%s, %s, %s]}, ",
TimeUnit.DAYS.toMillis(2),
TimeUnit.DAYS.toMillis(7),
TimeUnit.DAYS.toMillis(30))
+ "\"summary_window_operator\": \"count\", "
+ "\"summary_buckets\": [1, 2, 3, 4]}]";
Source source =
SourceFixture.getMinimalValidSourceBuilder()
.setTriggerSpecsString(triggerSpecsString)
.setMaxEventLevelReports(3)
.setPrivacyParameters("{\"flip_probability\" :0.0024}")
.build();
source.buildTriggerSpecs();
// Assertion
assertEquals(
source.getTriggerSpecs(),
new TriggerSpecs(triggerSpecsString, "3", source, "{\"flip_probability\":0.0024}"));
}
@Test
public void buildTriggerSpecs_invalidParamsSyntaxError_throws() throws JSONException {
String triggerSpecsString =
"[{\"trigger_data\": [1, 2, 3,"
+ "\"event_report_windows\": { "
+ "\"start_time\": \"0\", "
+ String.format(
"\"end_times\": [%s, %s, %s]}, ",
TimeUnit.DAYS.toMillis(2),
TimeUnit.DAYS.toMillis(7),
TimeUnit.DAYS.toMillis(30))
+ "\"summary_window_operator\": \"count\", "
+ "\"summary_buckets\": [1, 2, 3, 4]}]";
Source testSource =
SourceFixture.getMinimalValidSourceBuilder()
.setTriggerSpecsString(triggerSpecsString)
.setMaxEventLevelReports(3)
.setPrivacyParameters("{\"flip_probability\" :0.0024}")
.build();
// Assertion
assertThrows(JSONException.class, () -> testSource.buildTriggerSpecs());
}
@Test
public void parseEventReportWindows() {
// Invalid JSON
assertNull(Source.parseEventReportWindows("{'}"));
// No Start Time
List<Pair<Long, Long>> eventReportWindows =
Source.parseEventReportWindows("{'end_times': [3600, 86400]}");
assertNotNull(eventReportWindows);
assertEquals(2, eventReportWindows.size());
assertEquals(new Pair<>(0L, 3600L), eventReportWindows.get(0));
assertEquals(new Pair<>(3600L, 86400L), eventReportWindows.get(1));
// With Start Time
eventReportWindows =
Source.parseEventReportWindows(
"{'start_time': '2000', 'end_times': [3600, 86400, 172000]}");
assertNotNull(eventReportWindows);
assertEquals(eventReportWindows.size(), 3);
assertEquals(new Pair<>(2000L, 3600L), eventReportWindows.get(0));
assertEquals(new Pair<>(3600L, 86400L), eventReportWindows.get(1));
assertEquals(new Pair<>(86400L, 172000L), eventReportWindows.get(2));
}
@Test
public void getOrDefaultEventReportWindows() throws JSONException {
Flags flags = mock(Flags.class);
JSONObject windowsObj = new JSONObject("{'start_time': '2000000', 'end_times': "
+ "[3600000, 86400000, 172000000]}");
// Provided Windows
List<Pair<Long, Long>> eventReportWindows =
Source.getOrDefaultEventReportWindows(
windowsObj,
Source.SourceType.EVENT,
8640000,
flags);
assertNotNull(eventReportWindows);
assertEquals(eventReportWindows.size(), 3);
assertEquals(new Pair<>(2000000L, 3600000L), eventReportWindows.get(0));
assertEquals(new Pair<>(3600000L, 86400000L), eventReportWindows.get(1));
assertEquals(new Pair<>(86400000L, 172000000L), eventReportWindows.get(2));
// Default Windows - Event
when(flags.getMeasurementEventReportsVtcEarlyReportingWindows()).thenReturn("86400");
when(flags.getMeasurementEventReportsCtcEarlyReportingWindows())
.thenReturn("172800,604800");
eventReportWindows =
Source.getOrDefaultEventReportWindows(
null, Source.SourceType.EVENT, TimeUnit.DAYS.toMillis(15), flags);
assertNotNull(eventReportWindows);
assertEquals(2, eventReportWindows.size());
assertEquals(new Pair<>(0L, 86400000L), eventReportWindows.get(0));
assertEquals(new Pair<>(86400000L, 1296000000L), eventReportWindows.get(1));
// Default Windows - Navigation
eventReportWindows =
Source.getOrDefaultEventReportWindows(
null, Source.SourceType.NAVIGATION, TimeUnit.DAYS.toMillis(15), flags);
assertNotNull(eventReportWindows);
assertEquals(3, eventReportWindows.size());
assertEquals(new Pair<>(0L, 172800000L), eventReportWindows.get(0));
assertEquals(new Pair<>(172800000L, 604800000L), eventReportWindows.get(1));
assertEquals(new Pair<>(604800000L, 1296000000L), eventReportWindows.get(2));
}
@Test
public void parsedProcessedEventReportWindows() {
// Null event report window
Source source = SourceFixture.getValidSource();
assertNull(source.parsedProcessedEventReportWindows());
// Invalid event report windows string
Source sourceInvalidWindows =
SourceFixture.getMinimalValidSourceBuilder()
.setEventTime(10L)
.setEventReportWindows("{''}")
.build();
assertNull(sourceInvalidWindows.parsedProcessedEventReportWindows());
// Valid event report windows string
Source sourceValidWindows =
SourceFixture.getMinimalValidSourceBuilder()
.setEventTime(10L)
.setEventReportWindows(
"{'start_time': 1800000, 'end_times': [3600000, 864000000]}")
.build();
List<Pair<Long, Long>> windows = sourceValidWindows.parsedProcessedEventReportWindows();
assertNotNull(windows);
assertEquals(2, windows.size());
assertEquals(new Pair<>(1800010L, 3600010L), windows.get(0));
assertEquals(new Pair<>(3600010L, 864000010L), windows.get(1));
}
@Test
public void getOrDefaultMaxEventLevelReports() {
Flags flags = mock(Flags.class);
when(flags.getMeasurementVtcConfigurableMaxEventReportsCount()).thenReturn(2);
// null, Default for EVENT
assertEquals(
Integer.valueOf(2),
Source.getOrDefaultMaxEventLevelReports(Source.SourceType.EVENT, null, flags));
// null, Default for NAVIGATION
assertEquals(
Integer.valueOf(3),
Source.getOrDefaultMaxEventLevelReports(Source.SourceType.NAVIGATION, null, flags));
// Valid value provided
assertEquals(
Integer.valueOf(7),
Source.getOrDefaultMaxEventLevelReports(Source.SourceType.NAVIGATION, 7, flags));
}
@Test
public void getProcessedEventReportWindow() {
// null eventReportWindow
Source sourceNullEventReportWindow =
SourceFixture.getMinimalValidSourceBuilder()
.setEventTime(10)
.setEventReportWindow(null)
.build();
assertNull(sourceNullEventReportWindow.getProcessedEventReportWindow());
// eventReportWindow Value < eventTime
Source sourceNewEventReportWindow =
SourceFixture.getMinimalValidSourceBuilder()
.setEventTime(10)
.setEventReportWindow(4L)
.build();
assertEquals(Long.valueOf(14L), sourceNewEventReportWindow.getProcessedEventReportWindow());
// eventReportWindow Value > eventTime
Source sourceOldEventReportWindow =
SourceFixture.getMinimalValidSourceBuilder()
.setEventTime(10)
.setEventReportWindow(15L)
.build();
assertEquals(Long.valueOf(15L), sourceOldEventReportWindow.getProcessedEventReportWindow());
}
}