Skip to content

Commit d605c5b

Browse files
author
Josh Deffibaugh
committed
Gets Espresso tests recording and asserting on events
1 parent 7a5903a commit d605c5b

File tree

12 files changed

+217
-63
lines changed

12 files changed

+217
-63
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ public void track(@NonNull String eventName,
113113
@NonNull Map<String, String> attributes) throws UnknownEventTypeException {
114114
if (optimizely != null) {
115115
optimizely.track(eventName, userId, attributes);
116+
116117
} else {
117118
logger.warn("Optimizely is not initialized, could not track event {} for user {}" +
118119
" with attributes", eventName, userId);

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

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,10 @@
3535
* @hide
3636
*/
3737
public class DataFileService extends Service {
38-
static final String EXTRA_PROJECT_ID = "com.optimizely.ab.android.EXTRA_PROJECT_ID";
38+
/**
39+
* Extra containing the project id this instance of Optimizely was built with
40+
*/
41+
public static final String EXTRA_PROJECT_ID = "com.optimizely.ab.android.EXTRA_PROJECT_ID";
3942
@NonNull private final IBinder binder = new LocalBinder();
4043
Logger logger = LoggerFactory.getLogger(getClass());
4144
private boolean isBound;

event-handler/src/main/java/com/optimizely/ab/android/event_handler/EventDispatcher.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import android.support.annotation.NonNull;
2323
import android.util.Pair;
2424

25+
import com.optimizely.ab.android.shared.CountingIdlingResourceManager;
2526
import com.optimizely.ab.android.shared.OptlyStorage;
2627
import com.optimizely.ab.android.shared.ServiceScheduler;
2728

@@ -136,6 +137,8 @@ private boolean dispatch(Event event) {
136137
boolean eventWasSent = eventClient.sendEvent(event);
137138

138139
if (eventWasSent) {
140+
CountingIdlingResourceManager.decrement();
141+
CountingIdlingResourceManager.recordEvent(new Pair<>(event.getURL().toString(), event.getRequestBody()));
139142
return true;
140143
} else {
141144
boolean eventWasStored = eventDAO.storeEvent(event);
@@ -148,6 +151,4 @@ private boolean dispatch(Event event) {
148151
}
149152
}
150153
}
151-
152-
153154
}

shared/build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ dependencies {
5454
compile "com.noveogroup.android:android-logger:$android_logger_ver"
5555
compile "com.optimizely.ab:core-api:$java_core_ver"
5656
compile "com.google.code.gson:gson:$gson_ver"
57+
compile "com.android.support.test.espresso:espresso-idling-resource:$espresso_ver"
5758
provided "com.android.support:support-annotations:$support_annotations_ver"
5859

5960
testCompile "junit:junit:$junit_ver"
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright 2016, Optimizely
3+
* <p/>
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
* <p/>
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
* <p/>
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.optimizely.ab.android.shared;
18+
19+
import android.support.annotation.NonNull;
20+
import android.support.annotation.Nullable;
21+
import android.support.annotation.VisibleForTesting;
22+
import android.support.test.espresso.idling.CountingIdlingResource;
23+
import android.util.Pair;
24+
25+
import java.util.LinkedList;
26+
import java.util.List;
27+
28+
/**
29+
* Manages an Espresso {@link CountingIdlingResource}
30+
*/
31+
@VisibleForTesting
32+
public class CountingIdlingResourceManager {
33+
34+
@Nullable private static CountingIdlingResource countingIdlingResource;
35+
@NonNull private static List<Pair<String, String>> eventList = new LinkedList<>();
36+
37+
@VisibleForTesting
38+
@Nullable
39+
public static CountingIdlingResource getIdlingResource() {
40+
if (countingIdlingResource == null) {
41+
countingIdlingResource = new CountingIdlingResource("optimizely", true);
42+
}
43+
return countingIdlingResource;
44+
}
45+
46+
@VisibleForTesting
47+
public static void setIdlingResource(@NonNull CountingIdlingResource countingIdlingResource) {
48+
CountingIdlingResourceManager.countingIdlingResource = countingIdlingResource;
49+
}
50+
51+
@VisibleForTesting
52+
public static void increment() {
53+
if (countingIdlingResource != null) {
54+
countingIdlingResource.increment();
55+
}
56+
}
57+
58+
@VisibleForTesting
59+
public static void decrement() {
60+
if (countingIdlingResource != null) {
61+
countingIdlingResource.decrement();
62+
}
63+
}
64+
65+
@VisibleForTesting
66+
public static void recordEvent(Pair<String, String> event) {
67+
if (countingIdlingResource != null) {
68+
eventList.add(event);
69+
}
70+
}
71+
72+
@VisibleForTesting
73+
public static void clearEvents() {
74+
eventList.clear();
75+
}
76+
77+
@VisibleForTesting
78+
public static List<Pair<String, String>> getEvents() {
79+
return eventList;
80+
}
81+
}

shared/src/main/java/com/optimizely/ab/android/shared/ServiceScheduler.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,16 @@ public void unschedule(Intent intent) {
8585
logger.info("Unscheduled {}", intent.getComponent().toShortString());
8686
}
8787

88+
/**
89+
* Is an {@link Intent} for a service scheduled
90+
* @param intent the intent in question
91+
* @return is it scheduled or not
92+
* @hide
93+
*/
94+
public boolean isScheduled(Intent intent) {
95+
return pendingIntentFactory.hasPendingIntent(intent);
96+
}
97+
8898
/**
8999
* Handles the complexities around PendingIntent flags
90100
*

test-app/build.gradle

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ dependencies {
3636
// compile 'com.optimizely.ab:android-sdk:0.1.2-SNAPSHOT'
3737
compile 'com.android.support:appcompat-v7:24.2.1'
3838
compile 'com.android.support:design:24.2.1'
39-
compile "com.android.support.test.espresso:espresso-idling-resource:$espresso_ver"
40-
4139

4240
testCompile "junit:junit:$junit_ver"
4341
testCompile "org.mockito:mockito-core:$mockito_ver"

test-app/src/androidTest/java/com/optimizely/ab/android/test_app/MainActivityEspressoTest.java

Lines changed: 100 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -15,72 +15,143 @@
1515
*/
1616
package com.optimizely.ab.android.test_app;
1717

18+
import android.app.AlarmManager;
1819
import android.content.Context;
20+
import android.content.Intent;
1921
import android.support.test.InstrumentationRegistry;
2022
import android.support.test.espresso.Espresso;
2123
import android.support.test.espresso.idling.CountingIdlingResource;
2224
import android.support.test.filters.LargeTest;
2325
import android.support.test.rule.ActivityTestRule;
2426
import android.support.test.runner.AndroidJUnit4;
27+
import android.util.Pair;
28+
29+
import com.optimizely.ab.android.event_handler.EventIntentService;
30+
import com.optimizely.ab.android.sdk.DataFileService;
31+
import com.optimizely.ab.android.shared.CountingIdlingResourceManager;
32+
import com.optimizely.ab.android.shared.ServiceScheduler;
2533

26-
import org.junit.After;
27-
import org.junit.Before;
2834
import org.junit.Rule;
2935
import org.junit.Test;
36+
import org.junit.rules.ExternalResource;
37+
import org.junit.rules.RuleChain;
38+
import org.junit.rules.TestRule;
3039
import org.junit.runner.RunWith;
40+
import org.slf4j.LoggerFactory;
41+
42+
import java.util.Iterator;
43+
import java.util.List;
3144

3245
import static android.support.test.espresso.Espresso.onView;
46+
import static android.support.test.espresso.action.ViewActions.click;
3347
import static android.support.test.espresso.assertion.ViewAssertions.matches;
3448
import static android.support.test.espresso.matcher.ViewMatchers.withId;
3549
import static android.support.test.espresso.matcher.ViewMatchers.withText;
50+
import static junit.framework.Assert.assertFalse;
51+
import static junit.framework.Assert.assertTrue;
3652

3753
@RunWith(AndroidJUnit4.class)
3854
@LargeTest
3955
public class MainActivityEspressoTest {
4056

41-
@Rule
42-
public ActivityTestRule<MainActivity> activityRule = new ActivityTestRule<>(MainActivity.class);
43-
private Context context;
57+
private Context context = InstrumentationRegistry.getTargetContext();
4458
private CountingIdlingResource countingIdlingResource;
59+
private ServiceScheduler serviceScheduler;
60+
private Intent dataFileServiceIntent, eventIntentService;
4561

46-
@Before
47-
public void setupStorage() {
48-
// Clear sticky bucketing
49-
context = InstrumentationRegistry.getTargetContext();
50-
context.deleteFile("optly-user-experiment-record-7664231436.json");
51-
context.deleteFile("optly-data-file-7664231436.json");
52-
// Set the user's id to the test user that is in the whitelist.
53-
context.getSharedPreferences("user", Context.MODE_PRIVATE).edit()
54-
.putString("userId", "test_user")
55-
.apply();
56-
}
62+
private ActivityTestRule<MainActivity> activityTestRule = new ActivityTestRule<>(MainActivity.class);
63+
@Rule public TestRule chain = RuleChain
64+
.outerRule(new ExternalResource() {
65+
@Override
66+
protected void before() throws Throwable {
67+
super.before();
68+
countingIdlingResource = CountingIdlingResourceManager.getIdlingResource();
69+
// To prove that the test fails, omit this call:
70+
Espresso.registerIdlingResources(countingIdlingResource);
71+
}
5772

73+
@Override
74+
protected void after() {
75+
super.after();
76+
Espresso.unregisterIdlingResources(countingIdlingResource);
77+
}
78+
})
79+
.around(new ExternalResource() {
80+
@Override
81+
protected void before() throws Throwable {
82+
super.before();
83+
// Set the user's id to the test user that is in the whitelist.
84+
context.getSharedPreferences("user", Context.MODE_PRIVATE).edit()
85+
.putString("userId", "test_user")
86+
.apply();
87+
}
5888

59-
@Before
60-
public void registerIdlingResource() {
61-
countingIdlingResource = activityRule.getActivity().getIdlingResource();
62-
// To prove that the test fails, omit this call:
63-
Espresso.registerIdlingResources(countingIdlingResource);
64-
}
89+
@Override
90+
protected void after() {
91+
super.after();
92+
context.getSharedPreferences("user", Context.MODE_PRIVATE).edit().apply();
93+
// Clear sticky bucketing
94+
context.deleteFile(String.format("optly-user-experiment-record-%s.json", MyApplication.PROJECT_ID));
95+
}
96+
})
97+
.around(new ExternalResource() {
98+
@Override
99+
protected void before() throws Throwable {
100+
super.before();
101+
102+
dataFileServiceIntent = new Intent(context, DataFileService.class);
103+
dataFileServiceIntent.putExtra(DataFileService.EXTRA_PROJECT_ID, MyApplication.PROJECT_ID);
104+
105+
eventIntentService = new Intent(context, EventIntentService.class);
106+
eventIntentService.putExtra(DataFileService.EXTRA_PROJECT_ID, MyApplication.PROJECT_ID);
107+
108+
Context applicationContext = context.getApplicationContext();
109+
ServiceScheduler.PendingIntentFactory pendingIntentFactory = new ServiceScheduler.PendingIntentFactory(applicationContext);
110+
AlarmManager alarmManager = (AlarmManager) applicationContext.getSystemService(Context.ALARM_SERVICE);
111+
serviceScheduler = new ServiceScheduler(alarmManager, pendingIntentFactory, LoggerFactory.getLogger(ServiceScheduler.class));
112+
}
113+
114+
@Override
115+
protected void after() {
116+
super.after();
117+
serviceScheduler.unschedule(dataFileServiceIntent);
118+
assertFalse(serviceScheduler.isScheduled(dataFileServiceIntent));
119+
assertFalse(serviceScheduler.isScheduled(eventIntentService));
120+
}
121+
})
122+
.around(activityTestRule);
65123

66124
@Test
67-
public void experimentActivationForWhitelistUser() {
125+
public void experimentActivationForWhitelistUser() throws InterruptedException {
68126
// Check that the text was changed.
69127
// These tests are pointed at a real project.
70128
// The user 'test_user` is in the whitelist for variation 1 for experiment 0 and experiment 1.
71129
onView(withId(R.id.button_1))
72130
.check(matches(withText(context.getString(R.string.button_1_text_var_1))));
73131

74-
75132
// Espresso will wait for Optimizely to start due to the registered idling resources
76133
onView(withId(R.id.text_view_1))
77134
.check(matches(withText(context.getString(R.string.text_view_1_var_1))));
78-
}
79135

80-
@After
81-
public void unregisterIdlingResource() {
82-
if (countingIdlingResource != null) {
83-
Espresso.unregisterIdlingResources(countingIdlingResource);
136+
assertTrue(serviceScheduler.isScheduled(dataFileServiceIntent));
137+
138+
onView(withId(R.id.button_1)) // withId(R.id.my_view) is a ViewMatcher
139+
.perform(click()); // click() is a ViewAction
140+
141+
List<Pair<String, String>> events = CountingIdlingResourceManager.getEvents();
142+
assertTrue(events.size() == 4);
143+
Iterator<Pair<String, String>> iterator = events.iterator();
144+
while (iterator.hasNext()) {
145+
Pair<String, String> event = iterator.next();
146+
final String url = event.first;
147+
final String payload = event.second;
148+
if (url.equals("https://p13nlog.dz.optimizely.com/log/decision") && payload.contains("7676481120") && payload.contains("7661891902")
149+
|| url.equals("https://p13nlog.dz.optimizely.com/log/decision") && payload.contains("7651112186") && payload.contains("7674261140")
150+
|| url.equals("https://p13nlog.dz.optimizely.com/log/event") && payload.contains("experiment_0")
151+
|| url.equals("https://p13nlog.dz.optimizely.com/log/event") && payload.contains("experiment_1")) {
152+
iterator.remove();
153+
}
84154
}
155+
assertTrue(events.isEmpty());
85156
}
86157
}

0 commit comments

Comments
 (0)