Skip to content

Commit 484b6a1

Browse files
author
Josh Deffibaugh
committed
Merge branch 'devel'
2 parents 4894324 + d6b9c8f commit 484b6a1

File tree

16 files changed

+364
-376
lines changed

16 files changed

+364
-376
lines changed

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,9 @@
11
# Optimizely Android X SDK Changelog
2+
### 0.2.0
3+
October 28, 2016
4+
5+
*Breaking Changes*
6+
- Renames `AndroidOptimizely` to `OptimizelyClient`
27

38
### 0.1.3
49
October 27, 2016

android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/DataFileClientTest.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ public void setup() {
6262

6363
@Test
6464
public void request200() throws IOException {
65-
URL url = new URL(String.format(DataFileLoader.RequestDataFileFromClientTask.FORMAT_CDN_URL, "1"));
65+
URL url = new URL(String.format(DataFileLoader.FORMAT_CDN_URL, "1"));
6666
when(client.openConnection(url)).thenReturn(urlConnection);
6767
when(urlConnection.getResponseCode()).thenReturn(200);
6868
when(client.readStream(urlConnection)).thenReturn("{}");
@@ -87,7 +87,7 @@ public void request200() throws IOException {
8787

8888
@Test
8989
public void request201() throws IOException {
90-
URL url = new URL(String.format(DataFileLoader.RequestDataFileFromClientTask.FORMAT_CDN_URL, "1"));
90+
URL url = new URL(String.format(DataFileLoader.FORMAT_CDN_URL, "1"));
9191
when(client.openConnection(url)).thenReturn(urlConnection);
9292
when(urlConnection.getResponseCode()).thenReturn(201);
9393
when(client.readStream(urlConnection)).thenReturn("{}");
@@ -112,7 +112,7 @@ public void request201() throws IOException {
112112

113113
@Test
114114
public void request299() throws IOException {
115-
URL url = new URL(String.format(DataFileLoader.RequestDataFileFromClientTask.FORMAT_CDN_URL, "1"));
115+
URL url = new URL(String.format(DataFileLoader.FORMAT_CDN_URL, "1"));
116116
when(client.openConnection(url)).thenReturn(urlConnection);
117117
when(urlConnection.getResponseCode()).thenReturn(299);
118118
when(client.readStream(urlConnection)).thenReturn("{}");
@@ -137,7 +137,7 @@ public void request299() throws IOException {
137137

138138
@Test
139139
public void request300() throws IOException {
140-
URL url = new URL(String.format(DataFileLoader.RequestDataFileFromClientTask.FORMAT_CDN_URL, "1"));
140+
URL url = new URL(String.format(DataFileLoader.FORMAT_CDN_URL, "1"));
141141
when(client.openConnection(url)).thenReturn(urlConnection);
142142
when(urlConnection.getResponseCode()).thenReturn(300);
143143

@@ -157,7 +157,7 @@ public void request300() throws IOException {
157157

158158
@Test
159159
public void handlesIOException() throws IOException {
160-
URL url = new URL(String.format(DataFileLoader.RequestDataFileFromClientTask.FORMAT_CDN_URL, "1"));
160+
URL url = new URL(String.format(DataFileLoader.FORMAT_CDN_URL, "1"));
161161
when(client.openConnection(url)).thenReturn(urlConnection);
162162
when(urlConnection.getResponseCode()).thenReturn(200);
163163
doThrow(new IOException()).when(client).readStream(urlConnection);
@@ -179,15 +179,15 @@ public void handlesIOException() throws IOException {
179179

180180
@Test
181181
public void handlesNullResponse() throws MalformedURLException {
182-
URL url = new URL(String.format(DataFileLoader.RequestDataFileFromClientTask.FORMAT_CDN_URL, "1"));
182+
URL url = new URL(String.format(DataFileLoader.FORMAT_CDN_URL, "1"));
183183
when(client.execute(any(Client.Request.class), eq(2), eq(3))).thenReturn(null);
184184
assertNull(dataFileClient.request(url.toString()));
185185
}
186186

187187
@Test
188188
public void handlesEmptyStringResponse() throws MalformedURLException {
189-
URL url = new URL(String.format(DataFileLoader.RequestDataFileFromClientTask.FORMAT_CDN_URL, "1"));
189+
URL url = new URL(String.format(DataFileLoader.FORMAT_CDN_URL, "1"));
190190
when(client.execute(any(Client.Request.class), eq(2), eq(3))).thenReturn("");
191-
assertNull(dataFileClient.request(url.toString()));
191+
assertEquals("", dataFileClient.request(url.toString()));
192192
}
193193
}
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
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+
package com.optimizely.ab.android.sdk;
17+
18+
import android.content.Context;
19+
import android.os.Build;
20+
import android.support.annotation.RequiresApi;
21+
import android.support.test.InstrumentationRegistry;
22+
import android.support.test.espresso.core.deps.guava.util.concurrent.ListeningExecutorService;
23+
import android.support.test.espresso.core.deps.guava.util.concurrent.MoreExecutors;
24+
import android.support.test.runner.AndroidJUnit4;
25+
26+
import com.optimizely.ab.android.shared.Cache;
27+
import com.optimizely.ab.android.shared.Client;
28+
29+
import org.json.JSONException;
30+
import org.json.JSONObject;
31+
import org.junit.After;
32+
import org.junit.Before;
33+
import org.junit.Test;
34+
import org.junit.runner.RunWith;
35+
import org.slf4j.Logger;
36+
37+
import java.io.IOException;
38+
import java.net.MalformedURLException;
39+
import java.util.concurrent.TimeUnit;
40+
41+
import static junit.framework.Assert.assertEquals;
42+
import static junit.framework.Assert.assertNotNull;
43+
import static junit.framework.Assert.assertNull;
44+
import static junit.framework.Assert.fail;
45+
import static org.mockito.Matchers.any;
46+
import static org.mockito.Matchers.anyInt;
47+
import static org.mockito.Mockito.atMost;
48+
import static org.mockito.Mockito.mock;
49+
import static org.mockito.Mockito.verify;
50+
import static org.mockito.Mockito.when;
51+
52+
/**
53+
* Tests for {@link DataFileLoader}
54+
*/
55+
@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)
56+
@RunWith(AndroidJUnit4.class)
57+
public class DataFileLoaderTest {
58+
59+
private DataFileService datafileService;
60+
private DataFileCache dataFileCache;
61+
private DataFileClient dataFileClient;
62+
private Client client;
63+
private Logger logger;
64+
private DataFileLoadedListener dataFileLoadedListener;
65+
66+
@Before
67+
public void setup() {
68+
datafileService = mock(DataFileService.class);
69+
logger = mock(Logger.class);
70+
final Context targetContext = InstrumentationRegistry.getTargetContext();
71+
dataFileCache = new DataFileCache("1", new Cache(targetContext, logger), logger);
72+
client = mock(Client.class);
73+
dataFileClient = new DataFileClient(client, logger);
74+
dataFileLoadedListener = mock(DataFileLoadedListener.class);
75+
when(datafileService.getApplicationContext()).thenReturn(targetContext);
76+
when(datafileService.isBound()).thenReturn(true);
77+
}
78+
79+
@After
80+
public void tearDown() {
81+
dataFileCache.delete();
82+
}
83+
84+
@Test
85+
public void loadFromCDNWhenNoCachedFile() throws MalformedURLException, JSONException {
86+
final ListeningExecutorService executor = MoreExecutors.newDirectExecutorService();
87+
DataFileLoader dataFileLoader =
88+
new DataFileLoader(datafileService, dataFileClient, dataFileCache, executor, logger);
89+
90+
when(client.execute(any(Client.Request.class), anyInt(), anyInt())).thenReturn("{}");
91+
92+
dataFileLoader.getDataFile("1", dataFileLoadedListener);
93+
try {
94+
executor.awaitTermination(5, TimeUnit.SECONDS);
95+
} catch (InterruptedException e) {
96+
fail();
97+
}
98+
99+
final JSONObject cachedDataFile = dataFileCache.load();
100+
assertNotNull(cachedDataFile);
101+
assertEquals("{}", cachedDataFile.toString());
102+
verify(dataFileLoadedListener, atMost(1)).onDataFileLoaded("{}");
103+
}
104+
105+
@Test
106+
public void loadWhenCacheFileExistsAndCDNNotModified() {
107+
final ListeningExecutorService executor = MoreExecutors.newDirectExecutorService();
108+
DataFileLoader dataFileLoader =
109+
new DataFileLoader(datafileService, dataFileClient, dataFileCache, executor, logger);
110+
dataFileCache.save("{}");
111+
112+
when(client.execute(any(Client.Request.class), anyInt(), anyInt())).thenReturn("");
113+
114+
dataFileLoader.getDataFile("1", dataFileLoadedListener);
115+
try {
116+
executor.awaitTermination(5, TimeUnit.SECONDS);
117+
} catch (InterruptedException e) {
118+
fail();
119+
}
120+
121+
final JSONObject cachedDataFile = dataFileCache.load();
122+
assertNotNull(cachedDataFile);
123+
assertEquals("{}", cachedDataFile.toString());
124+
verify(dataFileLoadedListener, atMost(1)).onDataFileLoaded("{}");
125+
}
126+
127+
@Test
128+
public void noCacheAndLoadFromCDNFails() {
129+
final ListeningExecutorService executor = MoreExecutors.newDirectExecutorService();
130+
DataFileLoader dataFileLoader =
131+
new DataFileLoader(datafileService, dataFileClient, dataFileCache, executor, logger);
132+
133+
when(client.execute(any(Client.Request.class), anyInt(), anyInt())).thenReturn(null);
134+
135+
dataFileLoader.getDataFile("1", dataFileLoadedListener);
136+
try {
137+
executor.awaitTermination(5, TimeUnit.SECONDS);
138+
} catch (InterruptedException e) {
139+
fail();
140+
}
141+
142+
final JSONObject cachedDataFile = dataFileCache.load();
143+
assertNull(cachedDataFile);
144+
verify(dataFileLoadedListener, atMost(1)).onDataFileLoaded(null);
145+
}
146+
147+
@Test
148+
public void warningsAreLogged() throws IOException {
149+
final ListeningExecutorService executor = MoreExecutors.newDirectExecutorService();
150+
Cache cache = mock(Cache.class);
151+
dataFileCache = new DataFileCache("1", cache, logger);
152+
DataFileLoader dataFileLoader =
153+
new DataFileLoader(datafileService, dataFileClient, dataFileCache, executor, logger);
154+
155+
when(client.execute(any(Client.Request.class), anyInt(), anyInt())).thenReturn("{}");
156+
when(cache.exists(dataFileCache.getFileName())).thenReturn(true);
157+
when(cache.delete(dataFileCache.getFileName())).thenReturn(false);
158+
when(cache.save(dataFileCache.getFileName(), "{}")).thenReturn(false);
159+
160+
dataFileLoader.getDataFile("1", dataFileLoadedListener);
161+
try {
162+
executor.awaitTermination(5, TimeUnit.SECONDS);
163+
} catch (InterruptedException e) {
164+
fail();
165+
}
166+
167+
verify(logger).warn("Unable to delete old datafile");
168+
verify(logger).warn("Unable to save new datafile");
169+
verify(dataFileLoadedListener, atMost(1)).onDataFileLoaded("{}");
170+
}
171+
172+
}

android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/DatafileServiceTest.java

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,12 @@
2121
import android.os.IBinder;
2222
import android.support.annotation.RequiresApi;
2323
import android.support.test.InstrumentationRegistry;
24+
import android.support.test.espresso.core.deps.guava.util.concurrent.MoreExecutors;
2425
import android.support.test.rule.ServiceTestRule;
2526

27+
import com.optimizely.ab.android.shared.Cache;
28+
import com.optimizely.ab.android.shared.Client;
29+
2630
import org.junit.Ignore;
2731
import org.junit.Rule;
2832
import org.junit.Test;
@@ -33,6 +37,7 @@
3337
import static junit.framework.Assert.assertTrue;
3438
import static org.mockito.Mockito.mock;
3539
import static org.mockito.Mockito.verify;
40+
import static org.mockito.Mockito.when;
3641

3742
/**
3843
* Test for {@link DataFileService}
@@ -51,9 +56,16 @@ public void testBinding() throws TimeoutException {
5156
Context context = InstrumentationRegistry.getTargetContext();
5257
Intent intent = new Intent(context, DataFileService.class);
5358
IBinder binder = mServiceRule.bindService(intent);
54-
DataFileService dataFileService = ((DataFileService.LocalBinder) binder).getService();
55-
DataFileLoader dataFileLoader = new DataFileLoader(new DataFileLoader.TaskChain(dataFileService), mock(Logger.class));
59+
final Context targetContext = InstrumentationRegistry.getTargetContext();
60+
Logger logger = mock(Logger.class);
61+
DataFileCache dataFileCache = new DataFileCache("1", new Cache(targetContext, logger), logger);
62+
Client client = mock(Client.class);
63+
DataFileClient dataFileClient = new DataFileClient(client, logger);
5664
DataFileLoadedListener dataFileLoadedListener = mock(DataFileLoadedListener.class);
65+
66+
67+
DataFileService dataFileService = ((DataFileService.LocalBinder) binder).getService();
68+
DataFileLoader dataFileLoader = new DataFileLoader(dataFileService, dataFileClient, dataFileCache, MoreExecutors.newDirectExecutorService(), mock(Logger.class));
5769
dataFileService.getDataFile("1", dataFileLoader, dataFileLoadedListener);
5870

5971
assertTrue(dataFileService.isBound());

android-sdk/src/androidTest/java/com/optimizely/ab/android/sdk/OptimizelyManagerTest.java

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,5 @@ public void injectOptimizelyDoesNotDuplicateCallback() {
212212
} catch (InterruptedException e) {
213213
fail("Timed out");
214214
}
215-
216-
verify(logger).info("No listener to send Optimizely to");
217215
}
218216
}

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

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
package com.optimizely.ab.android.sdk;
1818

1919
import android.support.annotation.NonNull;
20+
import android.support.annotation.Nullable;
2021

2122
import com.optimizely.ab.android.shared.Client;
2223

@@ -27,7 +28,7 @@
2728
import java.net.URL;
2829

2930
/*
30-
* Makes requests to the Optly CDN to get the data file
31+
* Makes requests to the Optly CDN to get the datafile
3132
*/
3233
class DataFileClient {
3334

@@ -39,6 +40,20 @@ class DataFileClient {
3940
this.logger = logger;
4041
}
4142

43+
/*
44+
* If the datafile is modified on the CDN since the last time
45+
* our local datafile was modified the response body will be a valid
46+
* Optimizely datafile and the response code will be 200.
47+
*
48+
* If the datafile has not been modified since last time our local
49+
* datafile was modified there will be no response body and the
50+
* response code will be 304.
51+
*
52+
* @param urlString the CDN url of an Optimizely datafile
53+
*
54+
* @return a valid datafile, null, or an empty string (on 304 responses)
55+
*/
56+
@Nullable
4257
String request(final String urlString) {
4358
Client.Request<String> request = new Client.Request<String>() {
4459
@Override
@@ -76,13 +91,6 @@ public String execute() {
7691
}
7792
};
7893

79-
// If the response was a 304 Not Modified an empty string is returned
80-
// Consumers of this method expect either a valid datafile or null
81-
String response = client.execute(request, 2, 3);
82-
if (response != null && response.isEmpty()) {
83-
response = null;
84-
}
85-
86-
return response;
94+
return client.execute(request, 2, 3);
8795
}
8896
}

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

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,18 +16,23 @@
1616

1717
package com.optimizely.ab.android.sdk;
1818

19+
import android.support.annotation.Nullable;
20+
1921
/**
2022
* Listens for new Optimizely datafiles
2123
*
24+
* Datafiles can come from a local file or the CDN
25+
*
2226
* @hide
2327
*/
2428
interface DataFileLoadedListener {
2529

2630
/**
2731
* Called with new datafile
2832
*
29-
* @param dataFile the datafile json
33+
* @param dataFile the datafile json, can be null if datafile loading failed.
34+
*
3035
* @hide
3136
*/
32-
void onDataFileLoaded(String dataFile);
37+
void onDataFileLoaded(@Nullable String dataFile);
3338
}

0 commit comments

Comments
 (0)