Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,5 +25,5 @@ before_script:
- adb shell input keyevent 82 &
script:
- ./gradlew cleanAllModules
- ./gradlew testAllModules
- ./gradlew testAllModulesTravis
- if [[ -n $TRAVIS_TAG ]]; then ./gradlew ship; fi
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,16 @@ public class OptimizelyManager {
this.logger = logger;
}

@NonNull
public Long getDataFileDownloadInterval() {
return dataFileDownloadInterval;
}

@NonNull
public TimeUnit getDataFileDownloadIntervalTimeUnit() {
return dataFileDownloadIntervalTimeUnit;
}

/**
* Returns the {@link OptimizelyManager} builder
*
Expand Down Expand Up @@ -606,6 +616,14 @@ public Builder withDataFileDownloadInterval(long interval, @NonNull TimeUnit tim
public OptimizelyManager build() {
final Logger logger = LoggerFactory.getLogger(OptimizelyManager.class);

// AlarmManager doesn't allow intervals less than 60 seconds
if (dataFileDownloadIntervalTimeUnit.toMillis(dataFileDownloadInterval) < (60 * 1000)) {
dataFileDownloadIntervalTimeUnit = TimeUnit.SECONDS;
dataFileDownloadInterval = 60L;
logger.warn("Minimum datafile polling interval is 60 seconds. " +
"Defaulting to 60 seconds.");
}

return new OptimizelyManager(projectId,
eventHandlerDispatchInterval,
eventHandlerDispatchIntervalTimeUnit,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/****************************************************************************
* Copyright 2017, 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 org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.runners.MockitoJUnitRunner;

import java.util.concurrent.TimeUnit;

import static junit.framework.Assert.assertEquals;

@RunWith(MockitoJUnitRunner.class)
public class OptimizelyManagerBuilderTest {

/**
* Verify that building the {@link OptimizelyManager} with a polling interval less than 60
* seconds defaults to 60 seconds.
*/
@Test
public void testBuildWithInvalidPollingInterval() {
OptimizelyManager manager = OptimizelyManager.builder("1")
.withDataFileDownloadInterval(5, TimeUnit.SECONDS)
.build();

assertEquals(60L, manager.getDataFileDownloadInterval().longValue());
assertEquals(TimeUnit.SECONDS, manager.getDataFileDownloadIntervalTimeUnit());
}

/**
* Verify that building the {@link OptimizelyManager} with a polling interval greater than 60
* seconds is properly registered.
*/
@Test
public void testBuildWithValidPollingInterval() {
OptimizelyManager manager = OptimizelyManager.builder("1")
.withDataFileDownloadInterval(61, TimeUnit.SECONDS)
.build();

assertEquals(61L, manager.getDataFileDownloadInterval().longValue());
assertEquals(TimeUnit.SECONDS, manager.getDataFileDownloadIntervalTimeUnit());
}
}
8 changes: 7 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,12 @@ task testAllModules << {
logger.info("Running android tests for all modules")
}

testAllModules.dependsOn(':android-sdk:connectedAndroidTest', ':android-sdk:test',
task testAllModulesTravis << {
logger.info("Running android tests for Travis")
}

testAllModulesTravis.dependsOn(':android-sdk:connectedAndroidTest', ':android-sdk:test',
':event-handler:connectedAndroidTest', ':event-handler:test',
':user-profile:connectedAndroidTest', ':shared:connectedAndroidTest')

testAllModules.dependsOn('testAllModulesTravis', ':test-app:connectedAndroidTest')
32 changes: 32 additions & 0 deletions test-app/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# Optimizely X Android SDK Demo App

This module houses the demo app used to demonstrate how to get started with the Android SDK. The app
is also used to run integration tests on using the Android Espresso framework.

## Experiments Run

The experiments run in this demo app are part of the mobile-test@optimizely.com account.

We run the following experiment:
- Background change in second activity, which is loaded after the splash screen.

## How it works

The SDK is implemented in the following way:
- The splash screen initializes the Optimizely manager asynchronously. This starts the datafile
fetch.
- Once the datafile is fetched and the Optimizely manager is started, we use grab the `optimizelyClient`
from the manager and use it to activate the `background_experiment`. This buckets the user and sends
an impression event.
- We then use the bucketed variation to determine which activity to show. `VariationAActivity` for
`variation_a` and `VariationBActivity` for `variation_b`.
- Each of those activities include a `Test Conversion` button.
- Clicking on that button will call `optimizelyClient.track()` and send a conversion event for the
event named `sample_conversion`.
- Then the application will navigate to the conversion page to confirm that a conversion event has
been sent.

## Running the test app

Run `./gradlew test-app:connectedAndroidTest` to run the Espresso tests.

Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
/*
* Copyright 2016, Optimizely
* <p/>
* 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
* <p/>
* http://www.apache.org/licenses/LICENSE-2.0
* <p/>
* 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.
*/
/****************************************************************************
* Copyright 2017, 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.test_app;

import android.app.AlarmManager;
Expand Down Expand Up @@ -47,6 +47,7 @@
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static junit.framework.Assert.assertFalse;
Expand All @@ -62,7 +63,7 @@ public class MainActivityEspressoTest {
private ServiceScheduler serviceScheduler;
private Intent dataFileServiceIntent, eventIntentService;

private ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class);
private ActivityTestRule<SplashScreenActivity> activityTestRule = new ActivityTestRule<>(SplashScreenActivity.class);
@Rule public TestRule chain = RuleChain
.outerRule(new ExternalResource() {
@Override
Expand Down Expand Up @@ -148,46 +149,32 @@ protected void after() {
public void experimentActivationForWhitelistUser() throws InterruptedException {
// Check that the text was changed.
// These tests are pointed at a real project.
// The user 'test_user` is in the whitelist for variation 1 for experiment 0 and experiment 1.
onView(withId(R.id.button_1))
.check(matches(withText(context.getString(R.string.main_act_button_1_text_var_1))));
// The user 'test_user` is in the whitelist for variation_a for experiment background_experiment
onView(withId(R.id.tv_variation_a_text_1))
.check(matches(isDisplayed()));

// Espresso will wait for Optimizely to start due to the registered idling resources
onView(withId(R.id.text_view_1))
.check(matches(withText(context.getString(R.string.main_act_text_view_1_var_1))));

assertTrue(serviceScheduler.isScheduled(dataFileServiceIntent));

onView(withId(R.id.button_1)) // withId(R.id.my_view) is a ViewMatcher
.perform(click()); // click() is a ViewAction

onView(withId(R.id.text_view_1))
.check(matches(withText(context.getString(R.string.secondary_frag_text_view_1_var_1))));

onView(withId(R.id.button_1)) // withId(R.id.my_view) is a ViewMatcher
.perform(click()); // click() is a ViewAction
onView(withId(R.id.btn_variation_conversion)) // withId(R.id.my_view) is a ViewMatcher
.perform(click()); // click() is a ViewAction

List<Pair<String, String>> events = CountingIdlingResourceManager.getEvents();
assertTrue(events.size() == 6);
assertTrue(events.size() == 2);
Iterator<Pair<String, String>> iterator = events.iterator();
while (iterator.hasNext()) {
Pair<String, String> event = iterator.next();
final String url = event.first;
final String payload = event.second;
if (url.equals("https://logx.optimizely.com/log/decision") && payload.contains("7676481120") && payload.contains("7661891902")
|| url.equals("https://logx.optimizely.com/log/decision") && payload.contains("7651112186") && payload.contains("7674261140")
|| url.equals("https://logx.optimizely.com/log/event") && payload.contains("experiment_0")
|| url.equals("https://logx.optimizely.com/log/event") && payload.contains("experiment_1")
|| url.equals("https://logx.optimizely.com/log/decision") && payload.contains("7680080715") && payload.contains("7685562539")
|| url.equals("https://logx.optimizely.com/log/event") && payload.contains("experiment_2")) {
if (url.equals("https://logx.optimizely.com/log/decision") && payload.contains("8126664113") && payload.contains("8146590584")
|| url.equals("https://logx.optimizely.com/log/event") && payload.contains("sample_conversion")) {
iterator.remove();
}
}
assertTrue(events.isEmpty());
MyApplication myApplication = (MyApplication) activityTestRule.getActivity().getApplication();
UserProfile userProfile = myApplication.getOptimizelyManager().getUserProfile();
// Being in the white list should override user profile
assertNull(userProfile.lookup("test_user", "experiment_0"));
assertNull(userProfile.lookup("test_user", "experiment_1"));
assertNull(userProfile.lookup("test_user", "background_experiment"));
}
}
50 changes: 34 additions & 16 deletions test-app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,9 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest package="com.optimizely.ab.android.test_app"
xmlns:android="http://schemas.android.com/apk/res/android">
<!--
/****************************************************************************
* Copyright 2017, 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. *
***************************************************************************/
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.optimizely.ab.android.test_app">

<uses-permission android:name="android.permission.DISABLE_KEYGUARD"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
<uses-permission android:name="android.permission.WAKE_LOCK" />

<application
android:name=".MyApplication"
Expand All @@ -12,22 +29,23 @@
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity">
<service
android:name=".NotificationService"
android:exported="false" />

<activity
android:name=".SplashScreenActivity"
android:theme="@style/SplashTheme">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".SecondaryActivity"
android:parentActivityName=".MainActivity">
</activity>

<service
android:name=".NotificationService"
android:exported="false">
</service>
<activity android:name=".VariationAActivity" />
<activity android:name=".EventConfirmationActivity" />
<activity android:name=".ActivationErrorActivity" />
<activity android:name=".VariationBActivity"></activity>
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/****************************************************************************
* Copyright 2017, 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.test_app;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class ActivationErrorActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_activation_error);
Button btnConversion = (Button)findViewById(R.id.btn_conversion_error_back);

btnConversion.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
onBackPressed();
}
});
}
}
Loading