diff --git a/database/build.gradle b/database/build.gradle index 833e943aa..9c6b07aa9 100644 --- a/database/build.gradle +++ b/database/build.gradle @@ -10,6 +10,7 @@ android { targetSdkVersion targetSdk versionCode 1 versionName "1.0" + testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/database/src/androidTest/java/com/firebase/ui/database/utils/Bean.java b/database/src/androidTest/java/com/firebase/ui/database/Bean.java similarity index 93% rename from database/src/androidTest/java/com/firebase/ui/database/utils/Bean.java rename to database/src/androidTest/java/com/firebase/ui/database/Bean.java index ef926a547..03ff6a22a 100644 --- a/database/src/androidTest/java/com/firebase/ui/database/utils/Bean.java +++ b/database/src/androidTest/java/com/firebase/ui/database/Bean.java @@ -1,4 +1,4 @@ -package com.firebase.ui.database.utils; +package com.firebase.ui.database; public class Bean { private int number; diff --git a/database/src/androidTest/java/com/firebase/ui/database/FirebaseArrayOfObjectsTest.java b/database/src/androidTest/java/com/firebase/ui/database/FirebaseArrayOfObjectsTest.java index f7420eee7..f371fd648 100644 --- a/database/src/androidTest/java/com/firebase/ui/database/FirebaseArrayOfObjectsTest.java +++ b/database/src/androidTest/java/com/firebase/ui/database/FirebaseArrayOfObjectsTest.java @@ -14,11 +14,9 @@ package com.firebase.ui.database; +import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import com.firebase.ui.database.utils.Bean; import com.google.firebase.FirebaseApp; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; @@ -33,34 +31,35 @@ import static com.firebase.ui.database.TestUtils.getAppInstance; import static com.firebase.ui.database.TestUtils.getBean; import static com.firebase.ui.database.TestUtils.runAndWaitUntil; +import static org.junit.Assert.assertEquals; @RunWith(AndroidJUnit4.class) -@SmallTest -public class FirebaseArrayOfObjectsTest extends InstrumentationTestCase { +public class FirebaseArrayOfObjectsTest { + private static final int INITIAL_SIZE = 3; + private DatabaseReference mRef; private FirebaseArray mArray; @Before public void setUp() throws Exception { - FirebaseApp app = getAppInstance(getInstrumentation().getContext()); + FirebaseApp app = getAppInstance(InstrumentationRegistry.getContext()); mRef = FirebaseDatabase.getInstance(app).getReference() .child("firebasearray").child("objects"); mArray = new FirebaseArray(mRef); mRef.removeValue(); runAndWaitUntil(mArray, new Runnable() { - @Override - public void run() { - for (int i = 1; i <= 3; i++) { - mRef.push().setValue(new Bean(i, "Text " + i, i % 2 == 0), i); - } - } - }, new Callable() { - @Override - public Boolean call() throws Exception { - return mArray.getCount() == 3; - } - } - ); + @Override + public void run() { + for (int i = 1; i <= INITIAL_SIZE; i++) { + mRef.push().setValue(new Bean(i, "Text " + i, i % 2 == 0), i); + } + } + }, new Callable() { + @Override + public Boolean call() throws Exception { + return mArray.getCount() == INITIAL_SIZE; + } + }); } @After @@ -74,14 +73,8 @@ public void tearDown() throws Exception { } } - @Test - public void testSize() throws Exception { - assertEquals(3, mArray.getCount()); - } - @Test public void testPushIncreasesSize() throws Exception { - assertEquals(3, mArray.getCount()); runAndWaitUntil(mArray, new Runnable() { public void run() { mRef.push().setValue(new Bean(4)); diff --git a/database/src/androidTest/java/com/firebase/ui/database/FirebaseArrayTest.java b/database/src/androidTest/java/com/firebase/ui/database/FirebaseArrayTest.java index b79a4fcf3..b9ec7a7c8 100644 --- a/database/src/androidTest/java/com/firebase/ui/database/FirebaseArrayTest.java +++ b/database/src/androidTest/java/com/firebase/ui/database/FirebaseArrayTest.java @@ -14,9 +14,8 @@ package com.firebase.ui.database; +import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; import com.google.firebase.FirebaseApp; import com.google.firebase.database.DatabaseReference; @@ -32,28 +31,29 @@ import static com.firebase.ui.database.TestUtils.getAppInstance; import static com.firebase.ui.database.TestUtils.isValuesEqual; import static com.firebase.ui.database.TestUtils.runAndWaitUntil; +import static org.junit.Assert.assertEquals; @RunWith(AndroidJUnit4.class) -@SmallTest -public class FirebaseArrayTest extends InstrumentationTestCase { +public class FirebaseArrayTest { + private static final int INITIAL_SIZE = 3; private DatabaseReference mRef; private FirebaseArray mArray; @Before public void setUp() throws Exception { - FirebaseApp app = getAppInstance(getInstrumentation().getContext()); + FirebaseApp app = getAppInstance(InstrumentationRegistry.getContext()); mRef = FirebaseDatabase.getInstance(app).getReference().child("firebasearray"); mArray = new FirebaseArray(mRef); mRef.removeValue(); runAndWaitUntil(mArray, new Runnable() { public void run() { - for (int i = 1; i <= 3; i++) { + for (int i = 1; i <= INITIAL_SIZE; i++) { mRef.push().setValue(i, i); } } }, new Callable() { public Boolean call() throws Exception { - return mArray.getCount() == 3; + return mArray.getCount() == INITIAL_SIZE; } }); } @@ -69,14 +69,8 @@ public void tearDown() throws Exception { } } - @Test - public void testSize() throws Exception { - assertEquals(3, mArray.getCount()); - } - @Test public void testPushIncreasesSize() throws Exception { - assertEquals(3, mArray.getCount()); runAndWaitUntil(mArray, new Runnable() { public void run() { mRef.push().setValue(4); @@ -118,7 +112,7 @@ public Boolean call() throws Exception { } @Test - public void testChangePriorities() throws Exception { + public void testChangePriorityBackToFront() throws Exception { runAndWaitUntil(mArray, new Runnable() { public void run() { mArray.getItem(2).getRef().setPriority(0.5); @@ -129,4 +123,17 @@ public Boolean call() throws Exception { } }); } + + @Test + public void testChangePriorityFrontToBack() throws Exception { + runAndWaitUntil(mArray, new Runnable() { + public void run() { + mArray.getItem(0).getRef().setPriority(4); + } + }, new Callable() { + public Boolean call() throws Exception { + return isValuesEqual(mArray, new int[]{2, 3, 1}); + } + }); + } } diff --git a/database/src/androidTest/java/com/firebase/ui/database/FirebaseIndexArrayOfObjectsTest.java b/database/src/androidTest/java/com/firebase/ui/database/FirebaseIndexArrayOfObjectsTest.java index ce833f74b..095bf9db8 100644 --- a/database/src/androidTest/java/com/firebase/ui/database/FirebaseIndexArrayOfObjectsTest.java +++ b/database/src/androidTest/java/com/firebase/ui/database/FirebaseIndexArrayOfObjectsTest.java @@ -14,11 +14,9 @@ package com.firebase.ui.database; +import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; -import com.firebase.ui.database.utils.Bean; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; @@ -32,10 +30,12 @@ import static com.firebase.ui.database.TestUtils.getAppInstance; import static com.firebase.ui.database.TestUtils.getBean; import static com.firebase.ui.database.TestUtils.runAndWaitUntil; +import static org.junit.Assert.assertEquals; @RunWith(AndroidJUnit4.class) -@SmallTest -public class FirebaseIndexArrayOfObjectsTest extends InstrumentationTestCase { +public class FirebaseIndexArrayOfObjectsTest { + private static final int INITIAL_SIZE = 3; + private DatabaseReference mRef; private DatabaseReference mKeyRef; private FirebaseArray mArray; @@ -43,7 +43,7 @@ public class FirebaseIndexArrayOfObjectsTest extends InstrumentationTestCase { @Before public void setUp() throws Exception { FirebaseDatabase databaseInstance = - FirebaseDatabase.getInstance(getAppInstance(getInstrumentation().getContext())); + FirebaseDatabase.getInstance(getAppInstance(InstrumentationRegistry.getContext())); mRef = databaseInstance.getReference().child("firebasearray").child("objects"); mKeyRef = databaseInstance.getReference().child("firebaseindexarray").child("objects"); @@ -52,19 +52,18 @@ public void setUp() throws Exception { mKeyRef.removeValue(); runAndWaitUntil(mArray, new Runnable() { - @Override - public void run() { - for (int i = 1; i <= 3; i++) { - setValue(new Bean(i, "Text " + i, i % 2 == 0), i); - } - } - }, new Callable() { - @Override - public Boolean call() throws Exception { - return mArray.getCount() == 3; - } - } - ); + @Override + public void run() { + for (int i = 1; i <= INITIAL_SIZE; i++) { + pushValue(new Bean(i, "Text " + i, i % 2 == 0), i); + } + } + }, new Callable() { + @Override + public Boolean call() throws Exception { + return mArray.getCount() == INITIAL_SIZE; + } + }); } @After @@ -78,17 +77,11 @@ public void tearDown() throws Exception { } } - @Test - public void testSize() throws Exception { - assertEquals(3, mArray.getCount()); - } - @Test public void testPushIncreasesSize() throws Exception { - assertEquals(3, mArray.getCount()); runAndWaitUntil(mArray, new Runnable() { public void run() { - setValue(new Bean(4), null); + pushValue(new Bean(4), null); } }, new Callable() { @Override @@ -102,7 +95,7 @@ public Boolean call() throws Exception { public void testPushAppends() throws Exception { runAndWaitUntil(mArray, new Runnable() { public void run() { - setValue(new Bean(4), 4); + pushValue(new Bean(4), 4); } }, new Callable() { @Override @@ -116,7 +109,7 @@ public Boolean call() throws Exception { public void testAddValueWithPriority() throws Exception { runAndWaitUntil(mArray, new Runnable() { public void run() { - setValue(new Bean(4), 0.5); + pushValue(new Bean(4), 0.5); } }, new Callable() { public Boolean call() throws Exception { @@ -143,7 +136,7 @@ && getBean(mArray, 1).getNumber() == 1 }); } - private void setValue(Object value, Object priority) { + private void pushValue(Object value, Object priority) { String key = mKeyRef.push().getKey(); if (priority != null) { diff --git a/database/src/androidTest/java/com/firebase/ui/database/FirebaseIndexArrayTest.java b/database/src/androidTest/java/com/firebase/ui/database/FirebaseIndexArrayTest.java index 71d62f96e..91782ce88 100644 --- a/database/src/androidTest/java/com/firebase/ui/database/FirebaseIndexArrayTest.java +++ b/database/src/androidTest/java/com/firebase/ui/database/FirebaseIndexArrayTest.java @@ -14,9 +14,8 @@ package com.firebase.ui.database; +import android.support.test.InstrumentationRegistry; import android.support.test.runner.AndroidJUnit4; -import android.test.InstrumentationTestCase; -import android.test.suitebuilder.annotation.SmallTest; import com.google.firebase.database.DatabaseReference; import com.google.firebase.database.FirebaseDatabase; @@ -33,8 +32,9 @@ import static com.firebase.ui.database.TestUtils.runAndWaitUntil; @RunWith(AndroidJUnit4.class) -@SmallTest -public class FirebaseIndexArrayTest extends InstrumentationTestCase { +public class FirebaseIndexArrayTest { + private static final int INITIAL_SIZE = 3; + private DatabaseReference mRef; private DatabaseReference mKeyRef; private FirebaseIndexArray mArray; @@ -42,7 +42,7 @@ public class FirebaseIndexArrayTest extends InstrumentationTestCase { @Before public void setUp() throws Exception { FirebaseDatabase databaseInstance = - FirebaseDatabase.getInstance(getAppInstance(getInstrumentation().getContext())); + FirebaseDatabase.getInstance(getAppInstance(InstrumentationRegistry.getContext())); mRef = databaseInstance.getReference().child("firebasearray"); mKeyRef = databaseInstance.getReference().child("firebaseindexarray"); @@ -53,14 +53,14 @@ public void setUp() throws Exception { runAndWaitUntil(mArray, new Runnable() { @Override public void run() { - for (int i = 1; i <= 3; i++) { - setValue(i, i); + for (int i = 1; i <= INITIAL_SIZE; i++) { + pushValue(i, i); } } }, new Callable() { @Override public Boolean call() throws Exception { - return mArray.getCount() == 3; + return mArray.getCount() == INITIAL_SIZE; } }); } @@ -76,17 +76,11 @@ public void tearDown() throws Exception { } } - @Test - public void testSize() throws Exception { - assertEquals(3, mArray.getCount()); - } - @Test public void testPushIncreasesSize() throws Exception { - assertEquals(3, mArray.getCount()); runAndWaitUntil(mArray, new Runnable() { public void run() { - setValue(4, null); + pushValue(4, null); } }, new Callable() { @Override @@ -100,7 +94,7 @@ public Boolean call() throws Exception { public void testPushAppends() throws Exception { runAndWaitUntil(mArray, new Runnable() { public void run() { - setValue(4, 4); + pushValue(4, 4); } }, new Callable() { @Override @@ -114,7 +108,7 @@ public Boolean call() throws Exception { public void testAddValueWithPriority() throws Exception { runAndWaitUntil(mArray, new Runnable() { public void run() { - setValue(4, 0.5); + pushValue(4, 0.5); } }, new Callable() { public Boolean call() throws Exception { @@ -137,7 +131,7 @@ public Boolean call() throws Exception { }); } - private void setValue(Object value, Object priority) { + private void pushValue(Object value, Object priority) { String key = mKeyRef.push().getKey(); if (priority != null) { diff --git a/database/src/androidTest/java/com/firebase/ui/database/TestUtils.java b/database/src/androidTest/java/com/firebase/ui/database/TestUtils.java index b990fd609..c32ea54da 100644 --- a/database/src/androidTest/java/com/firebase/ui/database/TestUtils.java +++ b/database/src/androidTest/java/com/firebase/ui/database/TestUtils.java @@ -2,17 +2,16 @@ import android.content.Context; -import com.firebase.ui.database.utils.Bean; import com.google.firebase.FirebaseApp; import com.google.firebase.FirebaseOptions; import com.google.firebase.database.DatabaseError; -import junit.framework.AssertionFailedError; - import java.util.concurrent.Callable; import java.util.concurrent.Semaphore; import java.util.concurrent.TimeUnit; +import static org.junit.Assert.assertTrue; + public class TestUtils { private static final String APP_NAME = "firebaseui-tests"; private static final int TIMEOUT = 10000; @@ -64,9 +63,7 @@ public void onCancelled(DatabaseError databaseError) { // and we're not done } } - if (!isDone) { - throw new AssertionFailedError(); - } + assertTrue("Timed out waiting for expected results on FirebaseArray", isDone); array.setOnChangedListener(null); } diff --git a/database/src/main/java/com/firebase/ui/database/FirebaseArray.java b/database/src/main/java/com/firebase/ui/database/FirebaseArray.java index 859f4b8ce..1f2a575ba 100644 --- a/database/src/main/java/com/firebase/ui/database/FirebaseArray.java +++ b/database/src/main/java/com/firebase/ui/database/FirebaseArray.java @@ -21,7 +21,8 @@ import com.google.firebase.database.ValueEventListener; import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.ListIterator; /** * This class implements an array-like collection on top of a Firebase location. @@ -38,11 +39,12 @@ enum EventType {ADDED, CHANGED, REMOVED, MOVED} } - private Query mQuery; + private final Query mQuery; + private final ArrayList mSnapshots = new ArrayList<>(); + private final HashMap mSnapshotMap = new HashMap<>(); private OnChangedListener mListener; - private List mSnapshots = new ArrayList<>(); - public FirebaseArray(Query ref) { + FirebaseArray(Query ref) { mQuery = ref; mQuery.addChildEventListener(this); mQuery.addValueEventListener(this); @@ -61,13 +63,20 @@ public DataSnapshot getItem(int index) { return mSnapshots.get(index); } - private int getIndexForKey(String key) { - int index = 0; - for (DataSnapshot snapshot : mSnapshots) { - if (snapshot.getKey().equals(key)) { - return index; - } else { - index++; + public int getIndexForKey(String key) { + final Integer index = mSnapshotMap.get(key); + if (index != null) { + return index; + } else { + throw new IllegalArgumentException("Key not found"); + } + } + + private int scanArrayForKey(String key) { + final ListIterator it = mSnapshots.listIterator(); + while (it.hasNext()) { + if (it.next().getKey().equals(key)) { + return it.previousIndex(); } } throw new IllegalArgumentException("Key not found"); @@ -80,6 +89,7 @@ public void onChildAdded(DataSnapshot snapshot, String previousChildKey) { index = getIndexForKey(previousChildKey) + 1; } mSnapshots.add(index, snapshot); + mSnapshotMap.put(snapshot.getKey(), index); notifyChangedListeners(OnChangedListener.EventType.ADDED, index); } @@ -94,6 +104,7 @@ public void onChildChanged(DataSnapshot snapshot, String previousChildKey) { public void onChildRemoved(DataSnapshot snapshot) { int index = getIndexForKey(snapshot.getKey()); mSnapshots.remove(index); + mSnapshotMap.remove(snapshot.getKey()); notifyChangedListeners(OnChangedListener.EventType.REMOVED, index); } @@ -101,8 +112,15 @@ public void onChildRemoved(DataSnapshot snapshot) { public void onChildMoved(DataSnapshot snapshot, String previousChildKey) { int oldIndex = getIndexForKey(snapshot.getKey()); mSnapshots.remove(oldIndex); - int newIndex = previousChildKey == null ? 0 : (getIndexForKey(previousChildKey) + 1); + int newIndex = previousChildKey == null ? 0 : (scanArrayForKey(previousChildKey) + 1); mSnapshots.add(newIndex, snapshot); + + // Rebuild the map of indices + mSnapshotMap.clear(); + for (int i = 0; i < mSnapshots.size(); i++) { + mSnapshotMap.put(mSnapshots.get(i).getKey(), i); + } + notifyChangedListeners(OnChangedListener.EventType.MOVED, newIndex, oldIndex); } @@ -120,17 +138,17 @@ public void setOnChangedListener(OnChangedListener listener) { mListener = listener; } - protected void notifyChangedListeners(OnChangedListener.EventType type, int index) { + void notifyChangedListeners(OnChangedListener.EventType type, int index) { notifyChangedListeners(type, index, -1); } - protected void notifyChangedListeners(OnChangedListener.EventType type, int index, int oldIndex) { + void notifyChangedListeners(OnChangedListener.EventType type, int index, int oldIndex) { if (mListener != null) { mListener.onChildChanged(type, index, oldIndex); } } - protected void notifyCancelledListeners(DatabaseError databaseError) { + void notifyCancelledListeners(DatabaseError databaseError) { if (mListener != null) { mListener.onCancelled(databaseError); } diff --git a/database/src/main/java/com/firebase/ui/database/FirebaseIndexArray.java b/database/src/main/java/com/firebase/ui/database/FirebaseIndexArray.java index 6044426a0..c0baab1e9 100644 --- a/database/src/main/java/com/firebase/ui/database/FirebaseIndexArray.java +++ b/database/src/main/java/com/firebase/ui/database/FirebaseIndexArray.java @@ -23,18 +23,16 @@ import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; -import java.util.List; +import java.util.Iterator; import java.util.Map; -import java.util.Set; class FirebaseIndexArray extends FirebaseArray { private static final String TAG = FirebaseIndexArray.class.getSimpleName(); - private Query mQuery; + private final Query mQuery; + private final HashMap mRefs = new HashMap<>(); + private final ArrayList mDataSnapshots = new ArrayList<>(); private OnChangedListener mListener; - private Map mRefs = new HashMap<>(); - private List mDataSnapshots = new ArrayList<>(); public FirebaseIndexArray(Query keyRef, Query dataRef) { super(keyRef); @@ -44,9 +42,11 @@ public FirebaseIndexArray(Query keyRef, Query dataRef) { @Override public void cleanup() { super.cleanup(); - Set refs = new HashSet<>(mRefs.keySet()); - for (Query ref : refs) { - ref.removeEventListener(mRefs.remove(ref)); + final Iterator> it = mRefs.entrySet().iterator(); + while (it.hasNext()) { + final Map.Entry entry = it.next(); + entry.getKey().removeEventListener(entry.getValue()); + it.remove(); } } @@ -60,21 +60,7 @@ public DataSnapshot getItem(int index) { return mDataSnapshots.get(index); } - private int getIndexForKey(String key) { - int dataCount = getCount(); - int index = 0; - for (int keyIndex = 0; index < dataCount; keyIndex++) { - String superKey = super.getItem(keyIndex).getKey(); - if (key.equals(superKey)) { - break; - } else if (mDataSnapshots.get(index).getKey().equals(superKey)) { - index++; - } - } - return index; - } - - private boolean isMatch(int index, String key) { + private boolean isKeyAtIndex(int index, String key) { return index >= 0 && index < getCount() && mDataSnapshots.get(index).getKey().equals(key); } @@ -105,7 +91,7 @@ public void onChildRemoved(DataSnapshot keySnapshot) { super.onChildRemoved(keySnapshot); super.setOnChangedListener(mListener); - if (isMatch(index, key)) { + if (isKeyAtIndex(index, key)) { mDataSnapshots.remove(index); notifyChangedListeners(OnChangedListener.EventType.REMOVED, index); } @@ -120,7 +106,7 @@ public void onChildMoved(DataSnapshot keySnapshot, String previousChildKey) { super.onChildMoved(keySnapshot, previousChildKey); super.setOnChangedListener(mListener); - if (isMatch(oldIndex, key)) { + if (isKeyAtIndex(oldIndex, key)) { DataSnapshot snapshot = mDataSnapshots.remove(oldIndex); int newIndex = getIndexForKey(key); mDataSnapshots.add(newIndex, snapshot); @@ -147,7 +133,7 @@ public void onDataChange(DataSnapshot snapshot) { int index = getIndexForKey(key); if (snapshot.getValue() != null) { - if (!isMatch(index, key)) { + if (!isKeyAtIndex(index, key)) { mDataSnapshots.add(index, snapshot); notifyChangedListeners(OnChangedListener.EventType.ADDED, index); } else { @@ -155,7 +141,7 @@ public void onDataChange(DataSnapshot snapshot) { notifyChangedListeners(OnChangedListener.EventType.CHANGED, index); } } else { - if (isMatch(index, key)) { + if (isKeyAtIndex(index, key)) { mDataSnapshots.remove(index); notifyChangedListeners(OnChangedListener.EventType.REMOVED, index); } else {