Skip to content

Commit 7bfd707

Browse files
authored
[FSSDK-10758] implement vuid configuration independent of odp (#497)
1 parent b5e8a64 commit 7bfd707

File tree

7 files changed

+194
-31
lines changed

7 files changed

+194
-31
lines changed

android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyClient.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ public class OptimizelyClient {
8080
So, we start with an empty map of default attributes until the manager is initialized.
8181
*/
8282

83-
if (isValid()) {
83+
if (isValid() && vuid != null) {
8484
// identifiers are empty here since vuid will be inserted by java-sdk core
8585
sendODPEvent(null, "client_initialized", null, null);
8686
}

android-sdk/src/main/java/com/optimizely/ab/android/sdk/OptimizelyManager.java

+15-7
Original file line numberDiff line numberDiff line change
@@ -48,11 +48,7 @@
4848
import com.optimizely.ab.event.BatchEventProcessor;
4949
import com.optimizely.ab.event.EventHandler;
5050
import com.optimizely.ab.event.EventProcessor;
51-
import com.optimizely.ab.event.internal.BuildVersionInfo;
52-
import com.optimizely.ab.event.internal.ClientEngineInfo;
53-
import com.optimizely.ab.event.internal.payload.EventBatch;
5451
import com.optimizely.ab.notification.NotificationCenter;
55-
import com.optimizely.ab.notification.UpdateConfigNotification;
5652
import com.optimizely.ab.odp.ODPApiManager;
5753
import com.optimizely.ab.odp.ODPEventManager;
5854
import com.optimizely.ab.odp.ODPManager;
@@ -65,7 +61,6 @@
6561
import java.io.IOException;
6662
import java.io.InputStream;
6763
import java.util.Collections;
68-
import java.util.HashMap;
6964
import java.util.List;
7065
import java.util.Map;
7166
import java.util.Set;
@@ -792,6 +787,7 @@ public static class Builder {
792787
private int timeoutForODPSegmentFetchInSecs = 10;
793788
private int timeoutForODPEventDispatchInSecs = 10;
794789
private boolean odpEnabled = true;
790+
private boolean vuidEnabled = false;
795791
private String vuid = null;
796792

797793
private String customSdkName = null;
@@ -1031,6 +1027,15 @@ public Builder withODPDisabled() {
10311027
return this;
10321028
}
10331029

1030+
/**
1031+
* Enable Vuid
1032+
* @return this {@link Builder} instance
1033+
*/
1034+
public Builder withVuidEnabled() {
1035+
this.vuidEnabled = true;
1036+
return this;
1037+
}
1038+
10341039
/**
10351040
* Override the default (SDK-generated and persistent) vuid.
10361041
* @param vuid a user-defined vuid value
@@ -1120,8 +1125,11 @@ public OptimizelyManager build(Context context) {
11201125

11211126
}
11221127

1123-
if (vuid == null) {
1124-
vuid = VuidManager.Companion.getShared(context).getVuid();
1128+
VuidManager vuidManager = VuidManager.Companion.getInstance();
1129+
vuidManager.configure(vuidEnabled, context);
1130+
1131+
if (vuid == null && vuidEnabled) {
1132+
vuid = vuidManager.getVuid();
11251133
}
11261134

11271135
ODPManager odpManager = null;

android-sdk/src/test/java/com/optimizely/ab/android/sdk/OptimizelyManagerBuilderTest.java

+73-7
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@
1717
package com.optimizely.ab.android.sdk;
1818

1919
import android.content.Context;
20-
import android.graphics.Path;
2120

22-
import com.optimizely.ab.Optimizely;
2321
import com.optimizely.ab.android.datafile_handler.DatafileHandler;
2422
import com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler;
2523
import com.optimizely.ab.android.event_handler.DefaultEventHandler;
@@ -28,30 +26,31 @@
2826
import com.optimizely.ab.android.odp.ODPSegmentClient;
2927
import com.optimizely.ab.android.odp.VuidManager;
3028
import com.optimizely.ab.android.shared.DatafileConfig;
31-
import com.optimizely.ab.android.shared.WorkerScheduler;
3229
import com.optimizely.ab.android.user_profile.DefaultUserProfileService;
3330
import com.optimizely.ab.bucketing.UserProfileService;
3431
import com.optimizely.ab.error.ErrorHandler;
3532
import com.optimizely.ab.event.BatchEventProcessor;
3633
import com.optimizely.ab.event.EventHandler;
3734
import com.optimizely.ab.event.EventProcessor;
3835
import com.optimizely.ab.notification.NotificationCenter;
39-
import com.optimizely.ab.odp.ODPApiManager;
4036
import com.optimizely.ab.odp.ODPEventManager;
4137
import com.optimizely.ab.odp.ODPManager;
4238
import com.optimizely.ab.odp.ODPSegmentManager;
4339

40+
import org.junit.After;
4441
import org.junit.Before;
4542
import org.junit.Test;
4643
import org.junit.runner.RunWith;
4744
import org.mockito.ArgumentCaptor;
48-
import org.mockito.runners.MockitoJUnitRunner;
45+
import org.powermock.api.mockito.PowerMockito;
4946
import org.powermock.core.classloader.annotations.PowerMockIgnore;
5047
import org.powermock.core.classloader.annotations.PrepareForTest;
5148
import org.powermock.modules.junit4.PowerMockRunner;
49+
import org.powermock.reflect.Whitebox;
5250
import org.slf4j.Logger;
5351

5452
import static junit.framework.Assert.assertEquals;
53+
import static org.junit.Assert.assertFalse;
5554
import static org.junit.Assert.assertNotNull;
5655
import static org.junit.Assert.assertNull;
5756
import static org.mockito.ArgumentMatchers.anyLong;
@@ -63,25 +62,27 @@
6362
import static org.mockito.Mockito.mock;
6463
import static org.mockito.Mockito.never;
6564
import static org.mockito.Mockito.spy;
65+
import static org.mockito.Mockito.times;
6666
import static org.mockito.Mockito.verify;
6767
import static org.mockito.Mockito.when;
6868
import static org.powermock.api.mockito.PowerMockito.mockStatic;
6969
import static org.powermock.api.mockito.PowerMockito.verifyNew;
7070
import static org.powermock.api.mockito.PowerMockito.whenNew;
7171

72-
import java.sql.Time;
7372
import java.util.Map;
7473
import java.util.concurrent.TimeUnit;
7574

7675
@RunWith(PowerMockRunner.class)
7776
@PowerMockIgnore("jdk.internal.reflect.*")
78-
@PrepareForTest({OptimizelyManager.class, BatchEventProcessor.class, DefaultEventHandler.class, ODPManager.class, ODPSegmentManager.class, ODPEventManager.class})
77+
@PrepareForTest({OptimizelyManager.class, BatchEventProcessor.class, DefaultEventHandler.class, ODPManager.class, ODPSegmentManager.class, ODPEventManager.class, VuidManager.class})
7978
public class OptimizelyManagerBuilderTest {
8079

8180
private String testProjectId = "7595190003";
8281
private String testSdkKey = "1234";
8382
private Logger logger;
8483

84+
private VuidManager mockVuidManager;
85+
8586
private String minDatafile = "{\n" +
8687
"experiments: [ ],\n" +
8788
"version: \"2\",\n" +
@@ -101,6 +102,15 @@ public class OptimizelyManagerBuilderTest {
101102
public void setup() throws Exception {
102103
mockContext = mock(Context.class);
103104
mockDatafileHandler = mock(DefaultDatafileHandler.class);
105+
106+
mockStatic(VuidManager.class);
107+
VuidManager.Companion mockCompanion = PowerMockito.mock(VuidManager.Companion.class);
108+
mockVuidManager = PowerMockito.mock(VuidManager.class);
109+
PowerMockito.doReturn(mockVuidManager).when(mockCompanion).getInstance();
110+
Whitebox.setInternalState(
111+
VuidManager.class, "Companion",
112+
mockCompanion
113+
);
104114
}
105115

106116
/**
@@ -400,4 +410,60 @@ public void testBuildWithODP_defaultCommonDataAndIdentifiers() throws Exception
400410
assertEquals(identifiers.size(), 1);
401411
}
402412

413+
ODPManager.Builder getMockODPManagerBuilder() {
414+
ODPManager.Builder mockBuilder = PowerMockito.mock(ODPManager.Builder.class);
415+
when(mockBuilder.withApiManager(any())).thenReturn(mockBuilder);
416+
when(mockBuilder.withSegmentCacheSize(any())).thenReturn(mockBuilder);
417+
when(mockBuilder.withSegmentCacheTimeout(any())).thenReturn(mockBuilder);
418+
when(mockBuilder.withSegmentManager(any())).thenReturn(mockBuilder);
419+
when(mockBuilder.withEventManager(any())).thenReturn(mockBuilder);
420+
when(mockBuilder.withUserCommonData(any())).thenReturn(mockBuilder);
421+
when(mockBuilder.withUserCommonIdentifiers(any())).thenReturn(mockBuilder);
422+
return mockBuilder;
423+
}
424+
425+
@Test
426+
public void testBuildWithVuidDisabled() throws Exception {
427+
mockStatic(ODPManager.class);
428+
ODPManager.Builder mockBuilder = getMockODPManagerBuilder();
429+
when(mockBuilder.build()).thenReturn(mock(ODPManager.class));
430+
when(ODPManager.builder()).thenReturn(mockBuilder);
431+
432+
OptimizelyManager manager = OptimizelyManager.builder()
433+
.withSDKKey(testSdkKey)
434+
.build(mockContext);
435+
436+
verify(mockVuidManager, times(1)).configure(eq(false), any(Context.class));
437+
438+
ArgumentCaptor<Map<String, String>> identifiersCaptor = ArgumentCaptor.forClass(Map.class);
439+
verify(mockBuilder).withUserCommonIdentifiers(identifiersCaptor.capture());
440+
Map<String, String> identifiers = identifiersCaptor.getValue();
441+
assertFalse(identifiers.containsKey("vuid"));
442+
443+
when(ODPManager.builder()).thenCallRealMethod();
444+
}
445+
446+
@Test
447+
public void testBuildWithVuidEnabled() throws Exception {
448+
mockStatic(ODPManager.class);
449+
ODPManager.Builder mockBuilder = getMockODPManagerBuilder();
450+
when(mockBuilder.build()).thenReturn(mock(ODPManager.class));
451+
when(ODPManager.builder()).thenReturn(mockBuilder);
452+
453+
when(mockVuidManager.getVuid()).thenReturn("vuid_test");
454+
455+
OptimizelyManager manager = OptimizelyManager.builder()
456+
.withSDKKey(testSdkKey)
457+
.withVuidEnabled()
458+
.build(mockContext);
459+
460+
verify(mockVuidManager, times(1)).configure(eq(true), any(Context.class));
461+
462+
ArgumentCaptor<Map<String, String>> identifiersCaptor = ArgumentCaptor.forClass(Map.class);
463+
verify(mockBuilder).withUserCommonIdentifiers(identifiersCaptor.capture());
464+
Map<String, String> identifiers = identifiersCaptor.getValue();
465+
assertEquals(identifiers.get("vuid"), "vuid_test");
466+
467+
when(ODPManager.builder()).thenCallRealMethod();
468+
}
403469
}

android-sdk/src/test/java/com/optimizely/ab/android/sdk/OptimizelyManagerIntervalTest.java

+15-4
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,14 @@
1818

1919
import static org.mockito.Matchers.any;
2020
import static org.mockito.Matchers.anyInt;
21-
import static org.mockito.Matchers.anyList;
2221
import static org.mockito.Matchers.anyLong;
2322
import static org.mockito.Matchers.anyString;
2423
import static org.mockito.Matchers.eq;
2524
import static org.mockito.Mockito.mock;
25+
import static org.mockito.Mockito.spy;
2626
import static org.mockito.Mockito.verify;
2727
import static org.mockito.Mockito.when;
28+
import static org.powermock.api.mockito.PowerMockito.doReturn;
2829
import static org.powermock.api.mockito.PowerMockito.mockStatic;
2930
import static org.powermock.api.mockito.PowerMockito.verifyNew;
3031
import static org.powermock.api.mockito.PowerMockito.whenNew;
@@ -33,9 +34,9 @@
3334

3435
import com.optimizely.ab.android.datafile_handler.DatafileHandler;
3536
import com.optimizely.ab.android.event_handler.DefaultEventHandler;
37+
import com.optimizely.ab.android.odp.VuidManager;
3638
import com.optimizely.ab.android.shared.DatafileConfig;
3739
import com.optimizely.ab.bucketing.UserProfileService;
38-
import com.optimizely.ab.error.ErrorHandler;
3940
import com.optimizely.ab.event.BatchEventProcessor;
4041
import com.optimizely.ab.event.EventHandler;
4142
import com.optimizely.ab.event.EventProcessor;
@@ -45,11 +46,12 @@
4546
import org.junit.Before;
4647
import org.junit.Test;
4748
import org.junit.runner.RunWith;
48-
import org.powermock.core.PowerMockUtils;
49+
import org.powermock.api.mockito.PowerMockito;
4950
import org.powermock.core.classloader.annotations.PowerMockIgnore;
5051
import org.powermock.core.classloader.annotations.PrepareForTest;
5152
import org.powermock.modules.junit4.PowerMockRunner;
5253

54+
import org.powermock.reflect.Whitebox;
5355
import org.slf4j.Logger;
5456

5557
import java.util.concurrent.BlockingQueue;
@@ -59,7 +61,7 @@
5961

6062
@RunWith(PowerMockRunner.class)
6163
@PowerMockIgnore("jdk.internal.reflect.*")
62-
@PrepareForTest({OptimizelyManager.class, BatchEventProcessor.class, DefaultEventHandler.class})
64+
@PrepareForTest({OptimizelyManager.class, BatchEventProcessor.class, DefaultEventHandler.class, VuidManager.class})
6365
public class OptimizelyManagerIntervalTest {
6466

6567
private Logger logger;
@@ -76,6 +78,15 @@ public void setup() throws Exception {
7678
mockEventHandler = mock(DefaultEventHandler.class);
7779
mockStatic(DefaultEventHandler.class);
7880
when(DefaultEventHandler.getInstance(any())).thenReturn(mockEventHandler);
81+
82+
mockStatic(VuidManager.class);
83+
VuidManager.Companion mockCompanion = PowerMockito.mock(VuidManager.Companion.class);
84+
VuidManager mockVuidManager = PowerMockito.mock(VuidManager.class);
85+
doReturn(mockVuidManager).when(mockCompanion).getInstance();
86+
Whitebox.setInternalState(
87+
VuidManager.class, "Companion",
88+
mockCompanion
89+
);
7990
}
8091

8192
// DatafileDownloadInterval

odp/src/androidTest/java/com/optimizely/ab/android/odp/VuidManagerTest.kt

+66-5
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ import org.junit.After
2222
import org.junit.Assert.assertEquals
2323
import org.junit.Assert.assertFalse
2424
import org.junit.Assert.assertNotEquals
25+
import org.junit.Assert.assertNotNull
26+
import org.junit.Assert.assertNull
2527
import org.junit.Assert.assertTrue
2628
import org.junit.Before
2729
import org.junit.Test
@@ -40,7 +42,8 @@ class VuidManagerTest {
4042
// remove a singleton instance
4143
VuidManager.removeSharedForTesting()
4244

43-
vuidManager = VuidManager.getShared(context)
45+
vuidManager = VuidManager.getInstance()
46+
vuidManager.configure(true, context)
4447
}
4548

4649
@After
@@ -51,6 +54,16 @@ class VuidManagerTest {
5154
editor.commit()
5255
}
5356

57+
fun saveInSharedPrefs(key: String, value: String) {
58+
val sharedPreferences = context.getSharedPreferences("optly", Context.MODE_PRIVATE).edit()
59+
sharedPreferences.putString(key, value)
60+
sharedPreferences.apply()
61+
}
62+
63+
fun getFromSharedPrefs(key: String): String? {
64+
return context.getSharedPreferences("optly", Context.MODE_PRIVATE).getString(key, null)
65+
}
66+
5467
@Test
5568
fun makeVuid() {
5669
val vuid = vuidManager.makeVuid()
@@ -90,25 +103,73 @@ class VuidManagerTest {
90103

91104
@Test
92105
fun autoLoaded() {
93-
val vuid1 = VuidManager.getShared(context).vuid
106+
val vuidManager1 = VuidManager.getInstance()
107+
vuidManager1.configure(true, context)
108+
val vuid1 = vuidManager1.vuid
94109
assertTrue("vuid should be auto loaded when constructed", vuid1.startsWith("vuid_"))
95110

96-
val vuid2 = VuidManager.getShared(context).vuid
111+
val vuidManager2 = VuidManager.getInstance()
112+
vuidManager2.configure(true, context)
113+
val vuid2 = vuidManager2.vuid
97114
assertEquals("the same vuid should be returned when getting a singleton", vuid1, vuid2)
98115

99116
// remove shared instance, so will be re-instantiated
100117
VuidManager.removeSharedForTesting()
101118

102-
val vuid3 = VuidManager.getShared(context).vuid
119+
val vuidManager3 = VuidManager.getInstance()
120+
vuidManager3.configure(true, context)
121+
val vuid3 = vuidManager3.vuid
103122
assertEquals("the saved vuid should be returned when instantiated again", vuid2, vuid3)
104123

105124
// remove saved vuid
106125
cleanSharedPrefs()
107126
// remove shared instance, so will be re-instantiated
108127
VuidManager.removeSharedForTesting()
109128

110-
val vuid4 = VuidManager.getShared(context).vuid
129+
val vuidManager4 = VuidManager.getInstance()
130+
vuidManager4.configure(true, context)
131+
val vuid4 = vuidManager4.vuid
111132
assertNotEquals("a new vuid should be returned when storage cleared and re-instantiated", vuid3, vuid4)
112133
assertTrue(vuid4.startsWith("vuid_"))
113134
}
135+
136+
@Test
137+
fun configureWithVuidDisabled() {
138+
cleanSharedPrefs()
139+
saveInSharedPrefs("vuid", "vuid_test")
140+
VuidManager.removeSharedForTesting()
141+
142+
vuidManager = VuidManager.getInstance()
143+
vuidManager.configure(false, context)
144+
145+
assertNull(getFromSharedPrefs("vuid"))
146+
assertEquals(vuidManager.vuid, "")
147+
}
148+
149+
@Test
150+
fun configureWithVuidEnabledWhenVuidAlreadyExists() {
151+
cleanSharedPrefs()
152+
saveInSharedPrefs("vuid", "vuid_test")
153+
VuidManager.removeSharedForTesting()
154+
155+
vuidManager = VuidManager.getInstance()
156+
vuidManager.configure(true, context)
157+
158+
assertEquals(vuidManager.vuid, "vuid_test")
159+
}
160+
161+
@Test
162+
fun configureWithVuidEnabledWhenVuidDoesNotExist() {
163+
cleanSharedPrefs()
164+
VuidManager.removeSharedForTesting()
165+
assertNull(getFromSharedPrefs("vuid"))
166+
167+
vuidManager = VuidManager.getInstance()
168+
vuidManager.configure(true, context)
169+
170+
assertTrue(vuidManager.vuid.startsWith("vuid_"))
171+
assertNotNull(getFromSharedPrefs("vuid"))
172+
getFromSharedPrefs("vuid")?.let { assertTrue(it.startsWith("vuid_")) }
173+
assertEquals(getFromSharedPrefs("vuid"), vuidManager.vuid)
174+
}
114175
}

0 commit comments

Comments
 (0)