Skip to content
This repository was archived by the owner on Jan 31, 2022. It is now read-only.

Commit 2ec8906

Browse files
author
Clément Le Provost
committed
[offline][test] Add test cases for MirroredIndex
1 parent c8afcea commit 2ec8906

File tree

1 file changed

+320
-0
lines changed

1 file changed

+320
-0
lines changed
Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
/*
2+
* Copyright (c) 2012-2016 Algolia
3+
* http://www.algolia.com/
4+
*
5+
* Permission is hereby granted, free of charge, to any person obtaining a copy
6+
* of this software and associated documentation files (the "Software"), to deal
7+
* in the Software without restriction, including without limitation the rights
8+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
* copies of the Software, and to permit persons to whom the Software is
10+
* furnished to do so, subject to the following conditions:
11+
*
12+
* The above copyright notice and this permission notice shall be included in
13+
* all copies or substantial portions of the Software.
14+
*
15+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21+
* THE SOFTWARE.
22+
*/
23+
24+
package com.algolia.search.saas;
25+
26+
import android.support.annotation.NonNull;
27+
import android.support.annotation.Nullable;
28+
import android.util.Log;
29+
30+
import org.json.JSONArray;
31+
import org.json.JSONException;
32+
import org.json.JSONObject;
33+
import org.junit.Test;
34+
35+
import java.util.HashMap;
36+
import java.util.Map;
37+
import java.util.concurrent.CountDownLatch;
38+
import java.util.concurrent.TimeUnit;
39+
40+
import static org.junit.Assert.assertEquals;
41+
import static org.junit.Assert.assertFalse;
42+
import static org.junit.Assert.assertNotEquals;
43+
import static org.junit.Assert.assertNull;
44+
import static org.junit.Assert.assertTrue;
45+
import static org.junit.Assert.fail;
46+
47+
48+
/**
49+
* Unit tests for the `MirroredIndex` class.
50+
*/
51+
public class MirroredIndexTest extends OfflineTestBase {
52+
53+
/** Higher timeout for online queries. */
54+
protected static long onlineTimeout = 100;
55+
56+
/** The current sync listener. */
57+
private SyncListener listener;
58+
59+
protected static Map<String, JSONObject> moreObjects = new HashMap<>();
60+
static {
61+
try {
62+
moreObjects.put("snoopy", new JSONObject()
63+
.put("objectID", "1")
64+
.put("name", "Snoopy")
65+
.put("kind", new JSONArray().put("dog").put("animal"))
66+
.put("born", 1967)
67+
.put("series", "Peanuts")
68+
);
69+
moreObjects.put("woodstock", new JSONObject()
70+
.put("objectID", "2")
71+
.put("name", "Woodstock")
72+
.put("kind", new JSONArray().put("bird").put("animal"))
73+
.put("born", 1970)
74+
.put("series", "Peanuts")
75+
);
76+
moreObjects.put("charlie", new JSONObject()
77+
.put("objectID", "3")
78+
.put("name", "Charlie Brown")
79+
.put("kind", new JSONArray().put("human"))
80+
.put("born", 1950)
81+
.put("series", "Peanuts")
82+
);
83+
moreObjects.put("hobbes", new JSONObject()
84+
.put("objectID", "4")
85+
.put("name", "Hobbes")
86+
.put("kind", new JSONArray().put("tiger").put("animal").put("teddy"))
87+
.put("born", 1985)
88+
.put("series", "Calvin & Hobbes")
89+
);
90+
moreObjects.put("calvin", new JSONObject()
91+
.put("objectID", "5")
92+
.put("name", "Calvin")
93+
.put("kind", new JSONArray().put("human"))
94+
.put("born", 1985)
95+
.put("series", "Calvin & Hobbes")
96+
);
97+
}
98+
catch (JSONException e) {
99+
throw new RuntimeException(e); // should never happen
100+
}
101+
}
102+
103+
interface SyncCompletionHandler {
104+
void syncCompleted(@Nullable Throwable error);
105+
}
106+
107+
private void sync(final @NonNull MirroredIndex index, final @NonNull SyncCompletionHandler completionHandler) {
108+
// Delete the index.
109+
client.deleteIndexAsync(index.getIndexName(), new CompletionHandler() {
110+
@Override
111+
public void requestCompleted(JSONObject content, AlgoliaException error) {
112+
assertNull(error);
113+
int taskID = content.optInt("taskID", -1);
114+
assertNotEquals(-1, taskID);
115+
index.waitTaskAsync(Integer.toString(taskID), new CompletionHandler() {
116+
@Override
117+
public void requestCompleted(JSONObject content, AlgoliaException error) {
118+
assertNull(error);
119+
// Populate the online index.
120+
index.addObjectsAsync(new JSONArray(moreObjects.values()), new CompletionHandler() {
121+
@Override
122+
public void requestCompleted(JSONObject content, AlgoliaException error) {
123+
assertNull(error);
124+
int taskID = content.optInt("taskID", -1);
125+
assertNotEquals(-1, taskID);
126+
index.waitTaskAsync(Integer.toString(taskID), new CompletionHandler() {
127+
@Override
128+
public void requestCompleted(JSONObject content, AlgoliaException error) {
129+
assertNull(error);
130+
131+
// Sync the offline mirror.
132+
index.setMirrored(true);
133+
Query query = new Query();
134+
query.setNumericFilters(new JSONArray().put("born < 1980"));
135+
index.setDataSelectionQueries(
136+
new MirroredIndex.DataSelectionQuery(query, 10)
137+
);
138+
139+
listener = new SyncListener() {
140+
@Override
141+
public void syncDidStart(MirroredIndex index) {
142+
// Nothing to do.
143+
}
144+
145+
@Override
146+
public void syncDidFinish(MirroredIndex index, Throwable error, MirroredIndex.SyncStats stats) {
147+
Log.d(MirroredIndexTest.class.getSimpleName(), "Sync finished");
148+
index.removeSyncListener(listener);
149+
completionHandler.syncCompleted(error);
150+
}
151+
};
152+
index.addSyncListener(listener);
153+
index.sync();
154+
}
155+
});
156+
}
157+
});
158+
}
159+
});
160+
}
161+
});
162+
}
163+
164+
@Test
165+
public void testSync() throws Exception {
166+
final CountDownLatch signal = new CountDownLatch(1);
167+
final long waitTimeout = 5;
168+
169+
// Populate the online index & sync the offline mirror.
170+
final MirroredIndex index = client.getIndex(Helpers.safeIndexName(Helpers.getMethodName()));
171+
sync(index, new SyncCompletionHandler() {
172+
@Override
173+
public void syncCompleted(@Nullable Throwable error) {
174+
try {
175+
assertNull(error);
176+
177+
// Check that a call to `syncIfNeeded()` does *not* trigger a new sync.
178+
listener = new SyncListener() {
179+
@Override
180+
public void syncDidStart(MirroredIndex index) {
181+
fail("The sync should not have been started again");
182+
}
183+
184+
@Override
185+
public void syncDidFinish(MirroredIndex index, Throwable error, MirroredIndex.SyncStats stats) {
186+
// Nothing to do.
187+
}
188+
};
189+
final CountDownLatch signal2 = new CountDownLatch(1);
190+
index.addSyncListener(listener);
191+
index.syncIfNeeded();
192+
assertFalse(signal2.await(waitTimeout, TimeUnit.SECONDS));
193+
index.removeSyncListener(listener);
194+
195+
// Check that changing the data selection queries makes a new sync needed.
196+
index.setDataSelectionQueries(new MirroredIndex.DataSelectionQuery(new Query(), 6));
197+
listener = new SyncListener() {
198+
@Override
199+
public void syncDidStart(MirroredIndex index) {
200+
// Nothing to do.
201+
}
202+
203+
@Override
204+
public void syncDidFinish(MirroredIndex index, Throwable error, MirroredIndex.SyncStats stats) {
205+
index.removeSyncListener(listener);
206+
assertNull(error);
207+
signal.countDown();
208+
}
209+
};
210+
index.addSyncListener(listener);
211+
index.syncIfNeeded();
212+
} catch (InterruptedException e) {
213+
fail(e.getMessage());
214+
}
215+
}
216+
});
217+
assertTrue("No callback was called", signal.await(onlineTimeout * 2 + waitTimeout, TimeUnit.SECONDS));
218+
}
219+
220+
@Test
221+
public void testSearch() throws Exception {
222+
final CountDownLatch signal = new CountDownLatch(2);
223+
224+
// Populate the online index & sync the offline mirror.
225+
final MirroredIndex index = client.getIndex(Helpers.safeIndexName(Helpers.getMethodName()));
226+
sync(index, new SyncCompletionHandler() {
227+
@Override
228+
public void syncCompleted(@Nullable Throwable error) {
229+
assertNull(error);
230+
231+
// Query the online index explicitly.
232+
index.searchOnlineAsync(new Query(), new CompletionHandler() {
233+
@Override
234+
public void requestCompleted(JSONObject content, AlgoliaException error) {
235+
assertNull(error);
236+
assertEquals(5, content.optInt("nbHits"));
237+
assertEquals("remote", content.optString("origin"));
238+
signal.countDown();
239+
}
240+
});
241+
242+
// Query the offline index explicitly.
243+
index.searchOfflineAsync(new Query(), new CompletionHandler() {
244+
@Override
245+
public void requestCompleted(JSONObject content, AlgoliaException error) {
246+
assertNull(error);
247+
assertEquals(3, content.optInt("nbHits"));
248+
assertEquals("local", content.optString("origin"));
249+
signal.countDown();
250+
}
251+
});
252+
}
253+
});
254+
assertTrue("No callback was called", signal.await(onlineTimeout, TimeUnit.SECONDS));
255+
}
256+
257+
@Test
258+
public void testSearchFallback() throws Exception {
259+
final CountDownLatch signal = new CountDownLatch(1);
260+
261+
// Populate the online index & sync the offline mirror.
262+
final MirroredIndex index = client.getIndex(Helpers.safeIndexName(Helpers.getMethodName()));
263+
sync(index, new SyncCompletionHandler() {
264+
@Override
265+
public void syncCompleted(@Nullable Throwable error) {
266+
assertNull(error);
267+
268+
// Test offline fallback.
269+
client.setReadHosts("unknown.algolia.com");
270+
index.setRequestStrategy(MirroredIndex.Strategy.FALLBACK_ON_FAILURE);
271+
index.searchAsync(new Query(), new CompletionHandler() {
272+
@Override
273+
public void requestCompleted(JSONObject content, AlgoliaException error) {
274+
assertNull(error);
275+
assertEquals(3, content.optInt("nbHits"));
276+
assertEquals("local", content.optString("origin"));
277+
signal.countDown();
278+
}
279+
});
280+
}
281+
});
282+
assertTrue("No callback was called", signal.await(onlineTimeout, TimeUnit.SECONDS));
283+
}
284+
285+
@Test
286+
public void testBrowse() throws Exception {
287+
final CountDownLatch signal = new CountDownLatch(2);
288+
289+
// Populate the online index & sync the offline mirror.
290+
final MirroredIndex index = client.getIndex(Helpers.safeIndexName(Helpers.getMethodName()));
291+
sync(index, new SyncCompletionHandler() {
292+
@Override
293+
public void syncCompleted(@Nullable Throwable error) {
294+
assertNull(error);
295+
296+
// Query the online index explicitly.
297+
index.browseAsync(new Query(), new CompletionHandler() {
298+
@Override
299+
public void requestCompleted(JSONObject content, AlgoliaException error) {
300+
assertNull(error);
301+
assertEquals(5, content.optInt("nbHits"));
302+
signal.countDown();
303+
}
304+
});
305+
306+
// Query the offline index explicitly.
307+
index.browseMirrorAsync(new Query(), new CompletionHandler() {
308+
@Override
309+
public void requestCompleted(JSONObject content, AlgoliaException error) {
310+
assertNull(error);
311+
assertEquals(3, content.optInt("nbHits"));
312+
signal.countDown();
313+
}
314+
});
315+
}
316+
});
317+
assertTrue("No callback was called", signal.await(onlineTimeout, TimeUnit.SECONDS));
318+
}
319+
320+
}

0 commit comments

Comments
 (0)