Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion android-sdk/build.gradle
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2016-2020, Optimizely, Inc. and contributors *
* Copyright 2016-2020, 2023, Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand Down Expand Up @@ -55,6 +55,7 @@ dependencies {
api project(':datafile-handler')
api project(':event-handler')
api project(':user-profile')
api project(':odp')
api ("com.optimizely.ab:core-api:$java_core_ver") {
exclude group: 'com.google.code.findbugs'
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
/****************************************************************************
* Copyright 2023, Optimizely, Inc. and contributors *
* *
* 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.optimizely.ab.android.sdk;

import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.Build;

import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.filters.SdkSuppress;
import androidx.test.platform.app.InstrumentationRegistry;

import com.optimizely.ab.Optimizely;
import com.optimizely.ab.android.datafile_handler.DatafileHandler;
import com.optimizely.ab.android.datafile_handler.DatafileLoadedListener;
import com.optimizely.ab.android.datafile_handler.DefaultDatafileHandler;
import com.optimizely.ab.android.event_handler.DefaultEventHandler;
import com.optimizely.ab.android.shared.DatafileConfig;
import com.optimizely.ab.android.user_profile.DefaultUserProfileService;
import com.optimizely.ab.bucketing.UserProfileService;
import com.optimizely.ab.config.DatafileProjectConfig;
import com.optimizely.ab.config.ProjectConfig;
import com.optimizely.ab.config.Variation;
import com.optimizely.ab.config.parser.ConfigParseException;
import com.optimizely.ab.event.EventHandler;
import com.optimizely.ab.event.EventProcessor;
import com.optimizely.ab.event.internal.UserEvent;
import com.optimizely.ab.notification.NotificationCenter;
import com.optimizely.ab.notification.UpdateConfigNotification;
import com.optimizely.ab.odp.ODPEventManager;
import com.optimizely.ab.odp.ODPManager;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.slf4j.Logger;

import java.util.Collections;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNull;
import static junit.framework.Assert.assertTrue;
import static junit.framework.Assert.fail;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.anyString;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.doAnswer;
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;

/**
* Tests for Optimizely ODP Integration
*/
@RunWith(AndroidJUnit4.class)
public class ODPIntegrationTest {

private OptimizelyManager optimizelyManager;
private ODPManager odpManager;
private DefaultDatafileHandler datafileHandler;
private NotificationCenter notificationCenter;
private Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();
private String testSdkKey = "12345";

private String emptyV4Core =
"\"version\": \"4\"," +
"\"rollouts\": []," +
"\"anonymizeIP\": true," +
"\"projectId\": \"10431130345\"," +
"\"variables\": []," +
"\"featureFlags\": []," +
"\"experiments\": []," +
"\"audiences\": []," +
"\"groups\": []," +
"\"attributes\": []," +
"\"accountId\": \"10367498574\"," +
"\"events\": []," +
"\"revision\": \"100\",";

String integration1 = "\"integrations\":[{\"key\":\"odp\",\"host\":\"h-1\",\"publicKey\":\"p-1\"}]";
String integration2 = "\"integrations\":[{\"key\":\"odp\",\"host\":\"h-2\",\"publicKey\":\"p-2\"}]";
String odpDatafile1 = "{" + emptyV4Core + integration1 + "}";
String odpDatafile2 = "{" + emptyV4Core + integration2 + "}";

@Before
public void setup() throws Exception {
odpManager = mock(ODPManager.class);
when(odpManager.getEventManager()).thenReturn(mock(ODPEventManager.class));

datafileHandler = new DefaultDatafileHandler();
notificationCenter = new NotificationCenter();

optimizelyManager = new OptimizelyManager(
null,
testSdkKey,
null,
mock(Logger.class),
3600L,
datafileHandler,
null,
3600L,
mock(DefaultEventHandler.class),
mock(EventProcessor.class),
null,
notificationCenter,
null,
odpManager,
null);
}

@Test
public void initializeSynchronous_updateODPConfig() {
// NOTE: odpConfig is updated when Optimizely.java (java-sdk core) is initialized.
// Same for async-initialization, so need to repeat the same test (hard to test for async-init).

optimizelyManager.initialize(context, odpDatafile1);
verify(odpManager, times(1)).updateSettings(
eq("h-1"),
eq("p-1"),
eq(Collections.emptySet()));

// validate no other calls

verify(odpManager, times(1)).updateSettings(
anyString(),
anyString(),
any(Set.class));
}

@Test
public void updateODPConfigWhenDatafileUpdatedByBackgroundPolling() throws InterruptedException {
// NOTE: same logic for async-initialization, so no need to repeat for async

boolean updateConfigOnBackgroundDatafile = true;
optimizelyManager.initialize(context, odpDatafile1, true, updateConfigOnBackgroundDatafile);

// datafile will be saved when a new datafile is downloaded by background polling
datafileHandler.saveDatafile(context, new DatafileConfig(null, testSdkKey, null), odpDatafile2);
Thread.sleep(1000); // need a delay for file-observer (update notification)

// odpConfig updated on initialization

verify(odpManager, times(1)).updateSettings(
eq("h-1"),
eq("p-1"),
eq(Collections.emptySet()));

// odpConfig updated on background polling

verify(odpManager, times(1)).updateSettings(
eq("h-2"),
eq("p-2"),
eq(Collections.emptySet()));

// no other calls

verify(odpManager, times(2)).updateSettings(
anyString(),
anyString(),
any(Set.class));
}

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2017-2021, Optimizely, Inc. and contributors *
* Copyright 2017-2021, 2023 Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand Down Expand Up @@ -2197,6 +2197,40 @@ public void testCreateUserContext_withAttributes() {
assertEquals(userContext.getAttributes(), attributes);
}

@Test
public void testCreateUserContext_withVuid() {
String vuid = "test-vuid";
OptimizelyClient optimizelyClient = new OptimizelyClient(optimizely, logger, vuid);
OptimizelyUserContext userContext = optimizelyClient.createUserContext();
assertEquals(userContext.getUserId(), "test-vuid");
assert(userContext.getAttributes().isEmpty());
}

@Test
public void testCreateUserContext_withVuid_withAttributes() {
String vuid = "test-vuid";
Map<String, Object> attributes = Collections.singletonMap("house", "Gryffindor");

OptimizelyClient optimizelyClient = new OptimizelyClient(optimizely, logger, vuid);
OptimizelyUserContext userContext = optimizelyClient.createUserContext(attributes);
assertEquals(userContext.getUserId(), "test-vuid");
assertEquals(userContext.getAttributes(), attributes);
}

@Test
public void testVuidRegister() {
Optimizely mockOptimizely = mock(Optimizely.class);
when(mockOptimizely.isValid()).thenReturn(true);

OptimizelyClient optimizelyClient = new OptimizelyClient(mockOptimizely, logger, "any-vuid");

verify(mockOptimizely).sendODPEvent(
null,
"client_initialized",
null,
null);
}

@Test
// this should be enough to validate connection to the core java-sdk
public void testDecide() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2017, Optimizely, Inc. and contributors *
* Copyright 2017, 2023 Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand Down Expand Up @@ -63,4 +63,18 @@ public void buildDefaultAttributesMap() throws Exception {
assertEquals(defaultAttributes.size(), 4);
}

}
@Test
public void buildODPCommonData() throws Exception {
Context context = mock(Context.class);
Map<String, Object> commonData = OptimizelyDefaultAttributes.buildODPCommonData(context, logger);

assertEquals(commonData.size(), 4);

assertEquals(commonData.get("os"), "Android");
assertEquals(commonData.get("device_type"), "Phone");
assertTrue(commonData.get("os_version").toString().length() >= 1);
assertTrue(commonData.get("model").toString().length() > 2);
}

}

Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2017, Optimizely, Inc. and contributors *
* Copyright 2017, 2023 Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/****************************************************************************
* Copyright 2017-2021, Optimizely, Inc. and contributors *
* Copyright 2017-2021, 2023 Optimizely, Inc. and contributors *
* *
* Licensed under the Apache License, Version 2.0 (the "License"); *
* you may not use this file except in compliance with the License. *
Expand Down Expand Up @@ -163,7 +163,7 @@ public void initializeSyncWithEnvironment() {
EventHandler eventHandler = mock(DefaultEventHandler.class);
EventProcessor eventProcessor = mock(EventProcessor.class);
OptimizelyManager optimizelyManager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, 3600L, datafileHandler, null, 3600L,
eventHandler, eventProcessor, null, null, null);
eventHandler, eventProcessor, null, null, null, null, null);
/*
* Scenario#1: when datafile is not Empty
* Scenario#2: when datafile is Empty
Expand Down Expand Up @@ -222,7 +222,7 @@ public void initializeAsyncWithEnvironment() {
EventHandler eventHandler = mock(DefaultEventHandler.class);
EventProcessor eventProcessor = mock(EventProcessor.class);
final OptimizelyManager optimizelyManager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, 3600L, datafileHandler, null, 3600L,
eventHandler, eventProcessor, null, null, null);
eventHandler, eventProcessor, null, null, null, null, null);

/*
* Scenario#1: when datafile is not Empty
Expand Down Expand Up @@ -494,7 +494,7 @@ public void initializeSyncWithUpdateOnNewDatafileDisabled() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();

OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0,
null, null, null, null, null);
null, null, null, null, null, null, null);

doAnswer(
new Answer<Object>() {
Expand Down Expand Up @@ -527,7 +527,7 @@ public void initializeSyncWithUpdateOnNewDatafileEnabled() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();

OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0,
null, null, null, null, null);
null, null, null, null, null, null, null);

doAnswer(
new Answer<Object>() {
Expand Down Expand Up @@ -560,7 +560,7 @@ public void initializeSyncWithDownloadToCacheDisabled() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();

OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0,
null, null, null, null, null);
null, null, null, null, null, null, null);

doAnswer(
new Answer<Object>() {
Expand Down Expand Up @@ -593,7 +593,7 @@ public void initializeSyncWithUpdateOnNewDatafileDisabledWithPeriodicPollingEnab
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();

OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0,
null, null, null, null, null);
null, null, null, null, null, null, null);

doAnswer(
(Answer<Object>) invocation -> {
Expand Down Expand Up @@ -625,7 +625,7 @@ public void initializeSyncWithUpdateOnNewDatafileEnabledWithPeriodicPollingEnabl
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();

OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0,
null, null, null, null, null);
null, null, null, null, null, null, null);

doAnswer(
new Answer<Object>() {
Expand Down Expand Up @@ -658,7 +658,7 @@ public void initializeSyncWithUpdateOnNewDatafileDisabledWithPeriodicPollingDisa
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();

OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0,
null, null, null, null, null);
null, null, null, null, null, null, null);

doAnswer(
new Answer<Object>() {
Expand Down Expand Up @@ -692,7 +692,7 @@ public void initializeSyncWithUpdateOnNewDatafileEnabledWithPeriodicPollingDisab
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();

OptimizelyManager manager = new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0,
null, null, null, null, null);
null, null, null, null, null, null, null);

doAnswer(
new Answer<Object>() {
Expand Down Expand Up @@ -725,7 +725,7 @@ public void initializeSyncWithResourceDatafileNoCache() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();

OptimizelyManager manager = spy(new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0,
null, null, null, null, null));
null, null, null, null, null, null, null));

datafileHandler.removeSavedDatafile(context, manager.getDatafileConfig());
OptimizelyClient client = manager.initialize(context, R.raw.datafile, downloadToCache, updateConfigOnNewDatafile);
Expand All @@ -742,7 +742,7 @@ public void initializeSyncWithResourceDatafileNoCacheWithDefaultParams() {
Context context = InstrumentationRegistry.getInstrumentation().getTargetContext();

OptimizelyManager manager = spy(new OptimizelyManager(testProjectId, testSdkKey, null, logger, pollingInterval, datafileHandler, null, 0,
null, null, null, null, null));
null, null, null, null, null, null, null));

datafileHandler.removeSavedDatafile(context, manager.getDatafileConfig());
OptimizelyClient client = manager.initialize(context, R.raw.datafile);
Expand Down
Loading