/*
* Copyright 2017 Google Inc.
*
* 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.google.firebase.database.integration;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import com.google.common.collect.ImmutableList;
import com.google.firebase.FirebaseApp;
import com.google.firebase.database.ChildEventListener;
import com.google.firebase.database.DataSnapshot;
import com.google.firebase.database.DatabaseError;
import com.google.firebase.database.DatabaseException;
import com.google.firebase.database.DatabaseReference;
import com.google.firebase.database.EventRecord;
import com.google.firebase.database.MapBuilder;
import com.google.firebase.database.Query;
import com.google.firebase.database.TestFailure;
import com.google.firebase.database.TestHelpers;
import com.google.firebase.database.ValueEventListener;
import com.google.firebase.database.ValueExpectationHelper;
import com.google.firebase.database.future.ReadFuture;
import com.google.firebase.database.future.WriteFuture;
import com.google.firebase.testing.IntegrationTestUtils;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeoutException;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import org.junit.After;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
public class QueryTestIT {
private static FirebaseApp masterApp;
@BeforeClass
public static void setUpClass() throws IOException {
masterApp = IntegrationTestUtils.ensureDefaultApp();
}
@Before
public void prepareApp() {
TestHelpers.wrapForErrorHandling(masterApp);
}
@After
public void checkAndCleanupApp() {
TestHelpers.assertAndUnwrapErrorHandlers(masterApp);
}
@Test
public void testCreateBasicQueries() {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
// Just make sure they don't throw anything
ref.limitToLast(10);
ref.startAt("199").limitToLast(10);
ref.startAt("199", "test").limitToLast(10);
ref.endAt(199).limitToLast(1);
ref.startAt(50, "test").endAt(100, "tree");
ref.startAt(4).endAt(10);
ref.startAt(null).endAt(10);
ref.orderByChild("child");
ref.orderByChild("child/deep/path");
ref.orderByValue();
ref.orderByPriority();
}
@Test
public void testInvalidPathsToOrderBy() {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
List<String> badKeys = ImmutableList.<String>builder()
.add("$child/foo", "$childfoo", "$/foo", "$child/foo/bar", "$child/.foo", ".priority",
"$priority", "$key", ".key", "$child/.priority")
.build();
for (String key : badKeys) {
try {
ref.orderByChild(key);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
}
}
@Test
public void testInvalidQueries() {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
try {
ref.orderByKey().orderByPriority();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByKey().orderByValue();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByKey().orderByChild("foo");
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByValue().orderByPriority();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByValue().orderByKey();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByValue().orderByValue();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByValue().orderByChild("foo");
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByChild("foo").orderByPriority();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByChild("foo").orderByKey();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByChild("foo").orderByValue();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByKey().startAt(1);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByKey().startAt(null);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByKey().endAt(null);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByKey().equalTo(null);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByKey().startAt("test", "test");
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByKey().endAt(1);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByKey().endAt("test", "test");
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByKey().orderByPriority();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByPriority().orderByKey();
fail("Should throw");
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByPriority().orderByValue();
fail("Should throw");
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByPriority().orderByPriority();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.limitToLast(1).limitToLast(1);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.limitToFirst(1).limitToLast(1);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.limitToLast(1).limitToFirst(1);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.equalTo(true).endAt("test", "test");
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.equalTo(true).startAt("test", "test");
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.equalTo(true).equalTo("test", "test");
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.equalTo("test").equalTo(true);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByChild("foo").orderByKey();
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.limitToFirst(5).limitToLast(10);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.startAt(5).equalTo(10);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByPriority().startAt(false);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByPriority().endAt(true);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
try {
ref.orderByPriority().equalTo(true);
fail("Should throw");
} catch (DatabaseException | IllegalArgumentException e) { // ignore
}
}
@Test
public void testInvalidKeysInStartAtOrEndAtQueries() {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
List<String> badKeys = ImmutableList.of(".test", "test.", "fo$o", "[what", "ever]", "ha#sh",
"/thing", "th/ing", "thing/");
for (String key : badKeys) {
try {
ref.startAt(null, key);
fail("Should throw");
} catch (DatabaseException e) { // ignore
}
try {
ref.endAt(null, key);
fail("Should throw");
} catch (DatabaseException e) { // ignore
}
}
}
@Test
public void testRemoveListener()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final Semaphore semaphore = new Semaphore(0);
ValueEventListener listener = ref.limitToLast(5)
.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
semaphore.release();
}
@Override
public void onCancelled(DatabaseError error) {
}
});
ref.setValue(MapBuilder.of("a", 5, "b", 6));
TestHelpers.waitFor(semaphore, 1);
ref.limitToLast(5).removeEventListener(listener);
new WriteFuture(ref, MapBuilder.of("a", 6, "b", 5)).timedGet();
TestHelpers.waitForQueue(ref);
assertEquals(0, semaphore.availablePermits());
}
@Test
public void testLimitQuery()
throws TestFailure, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ref.setValue(new MapBuilder().put("a", 1).put("b", 2).put("c", 3).put("d", 4)
.put("e", 5).put("f", 6).build());
DataSnapshot snap = new ReadFuture(ref.limitToLast(5)).timedGet().get(0).getSnapshot();
assertEquals(5, snap.getChildrenCount());
assertFalse(snap.hasChild("a"));
assertTrue(snap.hasChild("b"));
assertTrue(snap.hasChild("f"));
}
@Test
public void testLimitQueryServerResponse()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
for (int i = 0; i < 9; ++i) {
ref.push().setValue(i);
}
new WriteFuture(ref.push(), 9).timedGet();
DataSnapshot snap = TestHelpers.getSnap(ref.limitToLast(5));
long i = 5;
for (DataSnapshot child : snap.getChildren()) {
assertEquals(i, child.getValue());
i++;
}
assertEquals(10L, i);
}
@Test
public void testMultipleLimitQueries() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ValueExpectationHelper expectations = new ValueExpectationHelper();
expectations.add(ref.limitToLast(1), MapBuilder.of("c", 3L));
expectations.add(ref.endAt(null).limitToLast(1), MapBuilder.of("c", 3L));
expectations.add(ref.limitToLast(2), MapBuilder.of("b", 2L, "c", 3L));
expectations.add(ref.limitToLast(3),
MapBuilder.of("a", 1L, "b", 2L, "c", 3L));
expectations.add(ref.limitToLast(4),
MapBuilder.of("a", 1L, "b", 2L, "c", 3L));
ref.setValue(MapBuilder.of("a", 1L, "b", 2L, "c", 3L));
expectations.waitForEvents();
}
@Test
public void testMultipleLimitQueriesWithStartAt() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ValueExpectationHelper expectations = new ValueExpectationHelper();
expectations.add(ref.startAt(null).limitToFirst(1),
MapBuilder.of("a", 1L));
expectations.add(ref.startAt(null, "c").limitToFirst(1),
MapBuilder.of("c", 3L));
expectations.add(ref.startAt(null, "b").limitToFirst(1),
MapBuilder.of("b", 2L));
expectations.add(ref.startAt(null, "b").limitToFirst(2),
MapBuilder.of("b", 2L, "c", 3L));
expectations.add(ref.startAt(null, "b").limitToFirst(3),
MapBuilder.of("b", 2L, "c", 3L));
ref.setValue(MapBuilder.of("a", 1, "b", 2, "c", 3));
expectations.waitForEvents();
}
@Test
public void testMultipleLimitQueriesWithStartAtUsingServerData()
throws InterruptedException, TestFailure, ExecutionException, TimeoutException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
// TODO: this test has race conditions. The listens are added sequentially, so we get a
// lot of partial data back from the server. This all correct, and we
// end up in the correct state, but it's still kinda weird. Consider having
// ValueExpectationHelper deal with initial state.
new WriteFuture(ref, MapBuilder.of("a", 1L, "b", 2L, "c", 3L)).timedGet();
ValueExpectationHelper expectations = new ValueExpectationHelper();
expectations.add(ref.startAt(null).limitToFirst(1), MapBuilder.of("a", 1L));
expectations.add(ref.startAt(null, "c").limitToFirst(1),
MapBuilder.of("c", 3L));
expectations.add(ref.startAt(null, "b").limitToFirst(1),
MapBuilder.of("b", 2L));
expectations.add(ref.startAt(null, "b").limitToFirst(2),
MapBuilder.of("b", 2L, "c", 3L));
expectations.add(ref.startAt(null, "b").limitToFirst(3),
MapBuilder.of("b", 2L, "c", 3L));
expectations.waitForEvents();
}
@Test
public void testLimitQueryChildEvents()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<String> added = new ArrayList<>();
final List<String> removed = new ArrayList<>();
ref.limitToLast(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
added.add(snapshot.getKey());
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removed.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
new WriteFuture(ref, MapBuilder.of("a", 1, "b", 2,"c", 3)).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("b", "c"), added);
assertTrue(removed.isEmpty());
added.clear();
new WriteFuture(ref.child("d"), 4).timedGet();
assertEquals(1, added.size());
assertEquals("d", added.get(0));
assertEquals(1, removed.size());
assertEquals("b", removed.get(0));
}
@Test
public void testLimitQueryChildEventsUsingServerData()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<String> added = new ArrayList<>();
final List<String> removed = new ArrayList<>();
new WriteFuture(ref, MapBuilder.of("a", 1,"b", 2,"c", 3)).timedGet();
final Semaphore semaphore = new Semaphore(0);
ref.limitToLast(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
added.add(snapshot.getKey());
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removed.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore, 2);
TestHelpers.assertDeepEquals(ImmutableList.of("b", "c"), added);
assertTrue(removed.isEmpty());
added.clear();
new WriteFuture(ref.child("d"), 4).timedGet();
assertEquals(1, added.size());
assertEquals("d", added.get(0));
assertEquals(1, removed.size());
assertEquals("b", removed.get(0));
}
@Test
public void testLimitQueryChildEventsWithStartAt()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<String> added = new ArrayList<>();
final List<String> removed = new ArrayList<>();
ref.startAt(null, "a").limitToFirst(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
added.add(snapshot.getKey());
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removed.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
new WriteFuture(ref, MapBuilder.of("a", 1,"b", 2,"c", 3)).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("a", "b"), added);
assertTrue(removed.isEmpty());
added.clear();
new WriteFuture(ref.child("aa"), 4).timedGet();
assertEquals(1, added.size());
assertEquals("aa", added.get(0));
assertEquals(1, removed.size());
assertEquals("b", removed.get(0));
}
@Test
public void testLimitQueryChildEventsWithStartAtUsingServerData()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<String> added = new ArrayList<>();
final List<String> removed = new ArrayList<>();
new WriteFuture(ref, MapBuilder.of("a", 1,"b", 2,"c", 3)).timedGet();
final Semaphore semaphore = new Semaphore(0);
ref.startAt(null, "a").limitToFirst(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
added.add(snapshot.getKey());
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removed.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore, 2);
TestHelpers.assertDeepEquals(ImmutableList.of("a", "b"), added);
assertTrue(removed.isEmpty());
added.clear();
new WriteFuture(ref.child("aa"), 4).timedGet();
assertEquals(1, added.size());
assertEquals("aa", added.get(0));
assertEquals(1, removed.size());
assertEquals("b", removed.get(0));
}
@Test
public void testLimitQueryUnderLimitChildEventsWithStartAt()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<String> added = new ArrayList<>();
final List<String> removed = new ArrayList<>();
ref.startAt(null, "a").limitToFirst(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
added.add(snapshot.getKey());
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removed.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
new WriteFuture(ref, MapBuilder.of("c", 3)).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("c"), added);
assertTrue(removed.isEmpty());
added.clear();
new WriteFuture(ref.child("b"), 4).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("b"), added);
assertTrue(removed.isEmpty());
}
@Test
public void testLimitQueryUnderLimitChildEventsWithStartAtUsingServerData()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
new WriteFuture(ref, MapBuilder.of("c", 3)).timedGet();
final List<String> added = new ArrayList<>();
final List<String> removed = new ArrayList<>();
final Semaphore semaphore = new Semaphore(0);
ref.startAt(null, "a").limitToFirst(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
added.add(snapshot.getKey());
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removed.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore);
TestHelpers.assertDeepEquals(ImmutableList.of("c"), added);
assertTrue(removed.isEmpty());
added.clear();
new WriteFuture(ref.child("b"), 4).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("b"), added);
assertTrue(removed.isEmpty());
}
@Test
public void testSetLimitChildAddedAndChildRemovedWhenAnElementIsRemoved()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<String> added = new ArrayList<>();
final List<String> removed = new ArrayList<>();
ref.limitToLast(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
added.add(snapshot.getKey());
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removed.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
new WriteFuture(ref, MapBuilder.of("a", 1,"b", 2,"c", 3)).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("b", "c"), added);
assertTrue(removed.isEmpty());
added.clear();
new WriteFuture(ref.child("b"), null).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("a"), added);
TestHelpers.assertDeepEquals(ImmutableList.of("b"), removed);
}
@Test
public void testLimitQueryChildEventsWithNodeDeleteUsingServerData()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
new WriteFuture(ref, MapBuilder.of("a", 1,"b", 2,"c", 3)).timedGet();
final List<String> added = new ArrayList<>();
final List<String> removed = new ArrayList<>();
final Semaphore semaphore = new Semaphore(0);
ref.limitToLast(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
added.add(snapshot.getKey());
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removed.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore, 2);
TestHelpers.assertDeepEquals(ImmutableList.of("b", "c"), added);
assertTrue(removed.isEmpty());
added.clear();
new WriteFuture(ref.child("b"), null).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("a"), added);
TestHelpers.assertDeepEquals(ImmutableList.of("b"), removed);
}
@Test
public void testSetLimitChildRemovedWhenAllElementsRemoved()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<String> added = new ArrayList<>();
final List<String> removed = new ArrayList<>();
ref.limitToLast(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
added.add(snapshot.getKey());
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removed.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
new WriteFuture(ref, MapBuilder.of("b", 2,"c", 3)).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("b", "c"), added);
assertTrue(removed.isEmpty());
added.clear();
new WriteFuture(ref.child("b"), null).timedGet();
assertTrue(added.isEmpty());
TestHelpers.assertDeepEquals(ImmutableList.of("b"), removed);
new WriteFuture(ref.child("c"), null).timedGet();
assertTrue(added.isEmpty());
TestHelpers.assertDeepEquals(ImmutableList.of("b", "c"), removed);
}
@Test
public void testSetLimitChildRemovedWhenAllElementsRemovedUsingServerData()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
new WriteFuture(ref, MapBuilder.of("b", 2,"c", 3)).timedGet();
final List<String> added = new ArrayList<>();
final List<String> removed = new ArrayList<>();
final Semaphore semaphore = new Semaphore(0);
final ChildEventListener listener = ref.limitToLast(2)
.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
added.add(snapshot.getKey());
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removed.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// no-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore, 2);
TestHelpers.assertDeepEquals(ImmutableList.of("b", "c"), added);
assertTrue(removed.isEmpty());
added.clear();
new WriteFuture(ref.child("b"), null).timedGet();
assertTrue(added.isEmpty());
TestHelpers.assertDeepEquals(ImmutableList.of("b"), removed);
new WriteFuture(ref.child("c"), null).timedGet();
assertTrue(added.isEmpty());
TestHelpers.assertDeepEquals(ImmutableList.of("b", "c"), removed);
ref.limitToLast(2).removeEventListener(listener);
}
@Test
public void testStartAtEndAtWithPriority() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ValueExpectationHelper helper = new ValueExpectationHelper();
helper.add(ref.startAt("w").endAt("y"),
MapBuilder.of("b", 2L, "c", 3L, "d", 4L));
helper.add(ref.startAt("w").endAt("w"), MapBuilder.of("d", 4L));
helper.add(ref.startAt("a").endAt("c"), null);
ref.setValue(
new MapBuilder()
.put("a", MapBuilder.of(".value", 1, ".priority", "z"))
.put("b", MapBuilder.of(".value", 2, ".priority", "y"))
.put("c", MapBuilder.of(".value", 3, ".priority", "x"))
.put("d", MapBuilder.of(".value", 4, ".priority", "w")).build());
helper.waitForEvents();
}
@Test
public void testStartAtEndAtWithPriorityUsingServerData() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ref.setValue(
new MapBuilder()
.put("a", MapBuilder.of(".value", 1, ".priority", "z"))
.put("b", MapBuilder.of(".value", 2, ".priority", "y"))
.put("c", MapBuilder.of(".value", 3, ".priority", "x"))
.put("d", MapBuilder.of(".value", 4, ".priority", "w")).build());
ValueExpectationHelper helper = new ValueExpectationHelper();
helper.add(ref.startAt("w").endAt("y"),
MapBuilder.of("b", 2L, "c", 3L, "d", 4L));
helper.add(ref.startAt("w").endAt("w"), MapBuilder.of("d", 4L));
helper.add(ref.startAt("a").endAt("c"), null);
helper.waitForEvents();
}
@Test
public void testStartAtEndAtWithPriorityAndName() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ValueExpectationHelper helper = new ValueExpectationHelper();
helper.add(ref.startAt(1, "a").endAt(2, "d"),
new MapBuilder().put("a", 1L).put("b", 2L).put("c", 3L).put("d", 4L).build());
helper.add(ref.startAt(1, "b").endAt(2, "c"),
MapBuilder.of("b", 2L, "c", 3L));
helper.add(ref.startAt(1, "c").endAt(2),
MapBuilder.of("c", 3L, "d", 4L));
ref.setValue(
new MapBuilder()
.put("a", MapBuilder.of(".value", 1, ".priority", 1))
.put("b", MapBuilder.of(".value", 2, ".priority", 1))
.put("c", MapBuilder.of(".value", 3, ".priority", 2))
.put("d", MapBuilder.of(".value", 4, ".priority", 2)).build());
helper.waitForEvents();
}
@Test
public void testStartAtEndAtWithPriorityAndName2() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ValueExpectationHelper helper = new ValueExpectationHelper();
helper.add(ref.startAt(1, "c").endAt(2, "b"),
new MapBuilder().put("a", 1L).put("b", 2L).put("c", 3L).put("d", 4L).build());
helper.add(ref.startAt(1, "d").endAt(2, "a"),
MapBuilder.of("d", 4L, "a", 1L));
helper.add(ref.startAt(1, "e").endAt(2),
MapBuilder.of("a", 1L, "b", 2L));
ref.setValue(
new MapBuilder()
.put("c", MapBuilder.of(".value", 3, ".priority", 1))
.put("d", MapBuilder.of(".value", 4, ".priority", 1))
.put("a", MapBuilder.of(".value", 1, ".priority", 2))
.put("b", MapBuilder.of(".value", 2, ".priority", 2)).build());
helper.waitForEvents();
}
@Test
public void testStartAtEndAtWithPriorityAndNameUsingServerData() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ref.setValue(
new MapBuilder()
.put("a", MapBuilder.of(".value", 1, ".priority", 1))
.put("b", MapBuilder.of(".value", 2, ".priority", 1))
.put("c", MapBuilder.of(".value", 3, ".priority", 2))
.put("d", MapBuilder.of(".value", 4, ".priority", 2)).build());
ValueExpectationHelper helper = new ValueExpectationHelper();
helper.add(ref.startAt(1, "a").endAt(2, "d"),
new MapBuilder().put("a", 1L).put("b", 2L).put("c", 3L).put("d", 4L).build());
helper.add(ref.startAt(1, "b").endAt(2, "c"),
MapBuilder.of("b", 2L, "c", 3L));
helper.add(ref.startAt(1, "c").endAt(2),
MapBuilder.of("c", 3L, "d", 4L));
helper.waitForEvents();
}
@Test
public void testStartAtEndAtWithPriorityAndNameUsingServerData2() throws
InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ref.setValue(
new MapBuilder()
.put("c", MapBuilder.of(".value", 3, ".priority", 1))
.put("d", MapBuilder.of(".value", 4, ".priority", 1))
.put("a", MapBuilder.of(".value", 1, ".priority", 2))
.put("b", MapBuilder.of(".value", 2, ".priority", 2)).build());
ValueExpectationHelper helper = new ValueExpectationHelper();
helper.add(ref.startAt(1, "c").endAt(2, "b"),
new MapBuilder().put("a", 1L).put("b", 2L).put("c", 3L).put("d", 4L).build());
helper.add(ref.startAt(1, "d").endAt(2, "a"),
MapBuilder.of("d", 4L, "a", 1L));
helper.add(ref.startAt(1, "e").endAt(2),
MapBuilder.of("a", 1L, "b", 2L));
helper.waitForEvents();
}
@Test
public void testPrevNameWithLimit()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<String> names = new ArrayList<>();
ref.limitToLast(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
names.add(snapshot.getKey() + " " + previousChildName);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
ref.child("a").setValue(1);
ref.child("c").setValue(3);
ref.child("b").setValue(2);
new WriteFuture(ref.child("d"), 4).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("a null", "c a", "b null", "d c"), names);
}
// NOTE: skipping server data test here, it really doesn't test anything
// extra
@Test
public void testSetLimitWithMoveNodes()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<String> names = new ArrayList<>();
ref.limitToLast(2).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
names.add(snapshot.getKey() + " " + previousChildName);
}
@Override
public void onCancelled(DatabaseError error) {
}
});
ref.child("a").setValue("a", 10);
ref.child("b").setValue("b", 20);
ref.child("c").setValue("c", 30);
ref.child("d").setValue("d", 40);
// Start moving things
ref.child("c").setPriority(50);
ref.child("c").setPriority(35);
new WriteFuture(ref.child("b"), "b", 33).timedGet();
TestHelpers.assertDeepEquals(ImmutableList.of("c d", "c null"), names);
}
// NOTE: skipping server data version of the above test, it doesn't really
// test anything new
// NOTE: skipping numeric priority test, the same functionality is tested
// above
// NOTE: skipping local add / remove test w/ limits. Tested above
@Test
public void testSetLimitWithAddNodesRemotely()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
final List<String> events = new ArrayList<>();
final Semaphore semaphore = new Semaphore(0);
ReadFuture future = new ReadFuture(reader);
final ChildEventListener listener = reader.limitToLast(2)
.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
events.add(snapshot.getValue().toString() + " added");
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
events.add(snapshot.getValue().toString() + " removed");
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
fail("Should not be cancelled");
}
});
// Wait for initial load
future.timedGet();
for (int i = 0; i < 4; ++i) {
writer.push().setValue(i);
}
new WriteFuture(writer.push(), 4).timedGet();
List<String> expected = ImmutableList.<String>builder().add(
"0 added", "1 added", "0 removed", "2 added", "1 removed",
"3 added", "2 removed", "4 added").build();
// Make sure we wait for all the events
TestHelpers.waitFor(semaphore, 5);
TestHelpers.assertDeepEquals(expected, events);
reader.limitToLast(2).removeEventListener(listener);
}
@Test
public void testAttachingListener() {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ValueEventListener listener = ref.limitToLast(1)
.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
assertNotNull(listener);
}
@Test
public void testLimitOnUnsyncedNode()
throws TestFailure, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
// This will timeout if value never fires
assertEquals(1, new ReadFuture(ref.limitToLast(1)).timedGet().size());
}
@Test
public void testFilteringOnlyNullPriorities() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ref.setValue(
new MapBuilder().put("a", new MapBuilder().put(".priority", null).put(".value", 0).build())
.put("b", new MapBuilder().put(".priority", null).put(".value", 1).build())
.put("c", new MapBuilder().put(".priority", "2").put(".value", 2).build())
.put("d", new MapBuilder().put(".priority", 3).put(".value", 3).build())
.put("e", new MapBuilder().put(".priority", "hi").put(".value", 4).build()).build());
DataSnapshot snap = TestHelpers.getSnap(ref.endAt(null));
Map<String, Object> expected = MapBuilder.of("a", 0L,"b", 1L);
Object result = snap.getValue();
TestHelpers.assertDeepEquals(expected, result);
}
@Test
public void testNullPrioritiesIncludedInEndAt() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ref.setValue(
new MapBuilder()
.put("a", MapBuilder.of(".priority", null, ".value", 0))
.put("b", MapBuilder.of(".priority", null, ".value", 1))
.put("c", MapBuilder.of(".priority", 2, ".value", 2))
.put("d", MapBuilder.of(".priority", 3, ".value", 3))
.put("e", MapBuilder.of(".priority", "hi", ".value", 4)).build());
DataSnapshot snap = TestHelpers.getSnap(ref.endAt(2));
Map<String, Object> expected = MapBuilder.of("a", 0L, "b", 1L, "c", 2L);
Object result = snap.getValue();
TestHelpers.assertDeepEquals(expected, result);
}
@Test
public void testNullPrioritiesIncludedInStartAt() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ref.setValue(
new MapBuilder()
.put("a", new MapBuilder().put(".priority", null).put(".value", 0).build())
.put("b", new MapBuilder().put(".priority", null).put(".value", 1).build())
.put("c", new MapBuilder().put(".priority", 2).put(".value", 2).build())
.put("d", new MapBuilder().put(".priority", 3).put(".value", 3).build())
.put("e", new MapBuilder().put(".priority", "hi").put(".value", 4).build()).build());
DataSnapshot snap = TestHelpers.getSnap(ref.startAt(2));
Object result = snap.getValue();
Map<String, Object> expected = MapBuilder.of("c", 2L, "d", 3L, "e", 4L);
TestHelpers.assertDeepEquals(expected, result);
}
@Test
public void testLimitWithMixOfNullAndNonNullPrioritiesUsingServerData() throws
InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
Map<String, Object> toSet = new MapBuilder()
.put("Vikrum",
new MapBuilder().put(".priority", 1000.0).put("score", 1000L).put("name", "Vikrum")
.build())
.put("Mike",
new MapBuilder().put(".priority", 500.0).put("score", 500L).put("name", "Mike").build())
.put("Andrew",
new MapBuilder().put(".priority", 50.0).put("score", 50L).put("name", "Andrew").build())
.put("James",
new MapBuilder().put(".priority", 7.0).put("score", 7L).put("name", "James").build())
.put("Sally",
new MapBuilder().put(".priority", -7.0).put("score", -7L).put("name", "Sally").build())
.put("Fred", new MapBuilder().put("score", 0.0).put("name", "Fred").build()).build();
ref.setValue(toSet);
final Semaphore semaphore = new Semaphore(0);
final List<String> names = new ArrayList<>();
ref.limitToLast(5).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
names.add(snapshot.getKey());
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore, 5);
TestHelpers.assertDeepEquals(ImmutableList.of("Sally", "James", "Andrew", "Mike", "Vikrum"),
names);
}
@Test
public void testLimitOnNodeWithPriority() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final Semaphore semaphore = new Semaphore(0);
final Map<String, Object> data = MapBuilder.of("a", "blah", ".priority", "priority");
ref.setValue(data, new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError error, DatabaseReference ref) {
ref.limitToLast(2).addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
Map<String, Object> expected = MapBuilder.of("a", "blah");
TestHelpers.assertDeepEquals(expected, snapshot.getValue(true));
semaphore.release();
}
@Override
public void onCancelled(DatabaseError error) {
}
});
}
});
TestHelpers.waitFor(semaphore);
}
@Test
public void testLimitWithMixOfNullAndNonNullPriorities() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
Map<String, Object> toSet = new MapBuilder()
.put("Vikrum",
new MapBuilder().put(".priority", 1000.0).put("score", 1000L).put("name", "Vikrum")
.build())
.put("Mike",
new MapBuilder().put(".priority", 500.0).put("score", 500L).put("name", "Mike").build())
.put("Andrew",
new MapBuilder().put(".priority", 50.0).put("score", 50L).put("name", "Andrew").build())
.put("James",
new MapBuilder().put(".priority", 7.0).put("score", 7L).put("name", "James").build())
.put("Sally",
new MapBuilder().put(".priority", -7.0).put("score", -7L).put("name", "Sally").build())
.put("Fred", new MapBuilder().put("score", 0.0).put("name", "Fred").build()).build();
final Semaphore semaphore = new Semaphore(0);
final List<String> names = new ArrayList<>();
final ChildEventListener listener = ref.limitToLast(5)
.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
names.add(snapshot.getKey());
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
fail("Should not be cancelled");
}
});
ref.setValue(toSet);
TestHelpers.waitFor(semaphore, 5);
TestHelpers.assertDeepEquals(ImmutableList.of("Sally", "James", "Andrew", "Mike", "Vikrum"),
names);
ref.limitToLast(5).removeEventListener(listener);
}
// NOTE: skipping tests for js context argument
@Test
public void testDeletingEntireQueryWindow()
throws InterruptedException, TestFailure, TimeoutException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final ReadFuture readFuture = ReadFuture.untilCount(ref.limitToLast(2), 3);
// wait for null event
TestHelpers.waitForRoundtrip(ref);
ref.setValue(
MapBuilder.of(
"a", MapBuilder.of(".value", 1, ".priority", 1),
"b", MapBuilder.of(".value", 2, ".priority", 2),
"c", MapBuilder.of(".value", 3, ".priority", 3)));
ref.updateChildren(new MapBuilder().put("b", null).put("c", null).build());
List<EventRecord> events = readFuture.timedGet();
DataSnapshot snap = events.get(1).getSnapshot();
Map<String, Object> expected = MapBuilder.of("b", 2L, "c", 3L);
Object result = snap.getValue();
TestHelpers.assertDeepEquals(expected, result);
// The original set is still outstanding (synchronous API), so we have a
// full cache to re-window against
snap = events.get(2).getSnapshot();
result = snap.getValue();
TestHelpers.assertDeepEquals(MapBuilder.of("a", 1L), result);
}
@Test
public void testOutOfViewQueryOnAChild()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final ReadFuture parentFuture = ReadFuture.untilCountAfterNull(ref.limitToLast(1), 2);
final List<DataSnapshot> childSnaps = new ArrayList<>();
ref.child("a").addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
childSnaps.add(snapshot);
}
@Override
public void onCancelled(DatabaseError error) {
}
});
new WriteFuture(ref, MapBuilder.of("a", 1, "b", 2)).timedGet();
assertEquals(1L, childSnaps.get(0).getValue());
ref.updateChildren(MapBuilder.of("c", 3));
List<EventRecord> events = parentFuture.timedGet();
DataSnapshot snap = events.get(0).getSnapshot();
Object result = snap.getValue();
Map<String, Object> expected = MapBuilder.of("b", 2L);
TestHelpers.assertDeepEquals(expected, result);
snap = events.get(1).getSnapshot();
result = snap.getValue();
expected = MapBuilder.of("c", 3L);
TestHelpers.assertDeepEquals(expected, result);
assertEquals(1, childSnaps.size());
assertEquals(1L, childSnaps.get(0).getValue());
}
@Test
public void testChildQueryGoingOutOfViewOfTheParent()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final ReadFuture parentFuture = ReadFuture.untilCountAfterNull(ref.limitToLast(1), 3);
final List<DataSnapshot> childSnaps = new ArrayList<>();
final ValueEventListener listener = ref.child("a").addValueEventListener(
new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
childSnaps.add(snapshot);
}
@Override
public void onCancelled(DatabaseError error) {
fail("Should not be cancelled");
}
});
new WriteFuture(ref, MapBuilder.of("a", 1)).timedGet();
assertEquals(1L, childSnaps.get(0).getValue());
new WriteFuture(ref.child("b"), 2).timedGet();
assertEquals(1, childSnaps.size());
new WriteFuture(ref.child("b"), null).timedGet();
List<EventRecord> events = parentFuture.timedGet();
assertEquals(1, childSnaps.size());
Object result;
Map<String, Object> expected;
result = events.get(0).getSnapshot().getValue();
expected = MapBuilder.of("a", 1L);
TestHelpers.assertDeepEquals(expected, result);
result = events.get(1).getSnapshot().getValue();
expected = MapBuilder.of("b", 2L);
TestHelpers.assertDeepEquals(expected, result);
result = events.get(0).getSnapshot().getValue();
expected = MapBuilder.of("a", 1L);
TestHelpers.assertDeepEquals(expected, result);
ref.child("a").removeEventListener(listener);
}
@Test
public void testDivergingViews()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<DataSnapshot> cSnaps = new ArrayList<>();
final List<DataSnapshot> dSnaps = new ArrayList<>();
ref.endAt(null, "c").limitToLast(1).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.getValue() != null) {
cSnaps.add(snapshot);
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
ref.endAt(null, "d").limitToLast(1).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.getValue() != null) {
dSnaps.add(snapshot);
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
new WriteFuture(ref, MapBuilder.of("a", 1, "b", 2, "c", 3)).timedGet();
assertEquals(1, cSnaps.size());
final Map<String, Object> cExpected = MapBuilder.of("c", 3L);
TestHelpers.assertDeepEquals(cExpected, cSnaps.get(0).getValue());
new WriteFuture(ref.child("d"), 4).timedGet();
assertEquals(1, cSnaps.size());
assertEquals(2, dSnaps.size());
TestHelpers.assertDeepEquals(cExpected, dSnaps.get(0).getValue());
TestHelpers.assertDeepEquals(MapBuilder.of("d", 4L), dSnaps.get(1).getValue());
}
@Test
public void testRemovingQueriedElement()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<Long> values = new ArrayList<>();
final Semaphore semaphore = new Semaphore(0);
ref.limitToLast(1).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
Long val = (Long) snapshot.getValue();
values.add(val);
if (val == 1L) {
semaphore.release(1);
}
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
ref.setValue(MapBuilder.of("a", 1, "b", 2));
ref.child("b").removeValue();
TestHelpers.waitFor(semaphore);
TestHelpers.assertDeepEquals(ImmutableList.of(2L, 1L), values);
}
@Test
public void testStartAtLimit() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ref.setValue(MapBuilder.of("a", 1, "b", 2));
DataSnapshot snap = TestHelpers.getSnap(ref.limitToFirst(1));
assertEquals(1L, snap.child("a").getValue());
}
@Test
public void testStartAtLimitWhenChildIsRemoved() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ref.setValue(MapBuilder.of("a", 1, "b", 2));
final List<Long> values = new ArrayList<>();
final Semaphore semaphore = new Semaphore(0);
ref.limitToFirst(1).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
Long val = (Long) snapshot.getValue();
values.add(val);
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
// Wait for first value
TestHelpers.waitFor(semaphore);
assertEquals((Long) 1L, values.get(0));
ref.child("a").removeValue();
TestHelpers.waitFor(semaphore);
assertEquals((Long) 2L, values.get(1));
}
@Test
public void testStartAtWithTwoArguments()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
new WriteFuture(ref,
MapBuilder.of(
"Walker", MapBuilder.of("name", "Walker", "score", 20, ".priority", 20),
"Michael", MapBuilder.of("name", "Michael", "score", 100, ".priority", 100)))
.timedGet();
DataSnapshot snap = TestHelpers.getSnap(ref.startAt(20, "Walker").limitToFirst(2));
List<String> expected = ImmutableList.of("Walker", "Michael");
int i = 0;
for (DataSnapshot child : snap.getChildren()) {
assertEquals(expected.get(i), child.getKey());
i++;
}
assertEquals(2, i);
}
@Test
public void testMultipleQueriesOnTheSameNode()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
new WriteFuture(ref, new MapBuilder().put("a", 1).put("b", 2).put("c", 3).put("d", 4)
.put("e", 5).put("f", 6).build()).timedGet();
final AtomicInteger limit2Called = new AtomicInteger(0);
final Semaphore semaphore = new Semaphore(0);
ref.limitToLast(2).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
// Should only be called once
if (limit2Called.incrementAndGet() == 1) {
semaphore.release(1);
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
// Skipping nested calls, no re-entrant APIs in Java
TestHelpers.waitFor(semaphore);
assertEquals(1, limit2Called.get());
DataSnapshot snap = TestHelpers.getSnap(ref.limitToLast(1));
TestHelpers.assertDeepEquals(MapBuilder.of("f", 6L), snap.getValue());
}
@Test
public void testNodeWithDefaultListener()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
new WriteFuture(ref, new MapBuilder().put("a", 1).put("b", 2).put("c", 3).put("d", 4)
.put("e", 5).put("f", 6).build()).timedGet();
final AtomicInteger onCalled = new AtomicInteger(0);
final Semaphore semaphore = new Semaphore(0);
ref.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
// Should only be called once
if (onCalled.incrementAndGet() == 1) {
semaphore.release(1);
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore);
assertEquals(1, onCalled.get());
DataSnapshot snap = TestHelpers.getSnap(ref.limitToLast(1));
TestHelpers.assertDeepEquals(MapBuilder.of("f", 6L), snap.getValue());
}
@Test
public void testNodeWithDefaultListenerAndNonCompleteLimit()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
new WriteFuture(ref, MapBuilder.of("a", 1, "b", 2, "c", 3)).timedGet();
final AtomicInteger onCalled = new AtomicInteger(0);
final Semaphore semaphore = new Semaphore(0);
ref.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
// Should only be called once
if (onCalled.incrementAndGet() == 1) {
semaphore.release(1);
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore);
assertEquals(1, onCalled.get());
DataSnapshot snap = TestHelpers.getSnap(ref.limitToLast(5));
TestHelpers.assertDeepEquals(MapBuilder.of("a", 1L, "b", 2L, "c", 3L), snap.getValue());
}
@Test
public void testRemoteRemoveEvent()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
final DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
Map<String, Object> expected = new MapBuilder()
.put("a", "a").put("b", "b").put("c", "c").put("d", "d").put("e", "e").build();
new WriteFuture(writer, expected).timedGet();
List<EventRecord> events = new ReadFuture(reader.limitToLast(5),
new ReadFuture.CompletionCondition() {
@Override
public boolean isComplete(List<EventRecord> events) {
if (events.size() == 1) {
writer.child("c").removeValue();
}
return events.size() == 2;
}
}).timedGet();
TestHelpers.assertDeepEquals(expected, events.get(0).getSnapshot().getValue());
TestHelpers.assertDeepEquals(
new MapBuilder().put("a", "a").put("b", "b").put("d", "d").put("e", "e").build(),
events.get(1).getSnapshot().getValue());
}
@Test
public void testEndAtWithTwoArgumentsAndLimit()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
Map<String, Object> toSet = new MapBuilder().put("a", "a")
.put("b", "b").put("c", "c").put("d", "d").put("e", "e").put("f", "f")
.put("g", "g").put("h", "h").build();
new WriteFuture(ref, toSet).timedGet();
DataSnapshot snap = TestHelpers.getSnap(ref.endAt(null, "f").limitToLast(5));
Map<String, Object> expected = new MapBuilder().put("b", "b")
.put("c", "c").put("d", "d").put("e", "e").put("f", "f").build();
TestHelpers.assertDeepEquals(expected, snap.getValue());
}
@Test
public void testComplexUpdateQueryRoot()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
Map<String, Object> toSet = new MapBuilder().put("a", 1).put("b", 2).put("c", 3).put("d", 4)
.put("e", 5).build();
new WriteFuture(writer, toSet).timedGet();
final Semaphore semaphore = new Semaphore(0);
ReadFuture future = new ReadFuture(reader.limitToFirst(4),
new ReadFuture.CompletionCondition() {
@Override
public boolean isComplete(List<EventRecord> events) {
if (events.size() == 1) {
semaphore.release(1);
}
return events.size() == 2;
}
});
TestHelpers.waitFor(semaphore);
Map<String, Object> update = new MapBuilder().put("b", null).put("c", "a").put("cc", "new")
.put("cd", "new2").put("d", "gone").build();
writer.updateChildren(update);
List<EventRecord> events = future.timedGet();
Map<String, Object> expected = new MapBuilder().put("a", 1L).put("b", 2L).put("c", 3L)
.put("d", 4L).build();
Object result = events.get(0).getSnapshot().getValue();
TestHelpers.assertDeepEquals(expected, result);
expected = new MapBuilder().put("a", 1L).put("c", "a").put("cc", "new")
.put("cd", "new2").build();
result = events.get(1).getSnapshot().getValue();
TestHelpers.assertDeepEquals(expected, result);
}
@Test
public void testUpdateQueryRoot()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
Map<String, Object> toSet = MapBuilder.of("bar", "a", "baz", "b", "bam", "c");
new WriteFuture(writer, toSet).timedGet();
final Semaphore semaphore = new Semaphore(0);
ReadFuture future = new ReadFuture(reader.limitToLast(10),
new ReadFuture.CompletionCondition() {
@Override
public boolean isComplete(List<EventRecord> events) {
if (events.size() == 1) {
semaphore.release(1);
}
return events.size() == 2;
}
});
TestHelpers.waitFor(semaphore);
Map<String, Object> update = new MapBuilder().put("bar", "d").put("bam", null).put("bat", "e")
.build();
writer.updateChildren(update);
List<EventRecord> events = future.timedGet();
Map<String, Object> expected = MapBuilder.of("bar", "a", "baz", "b", "bam",
"c");
Object result = events.get(0).getSnapshot().getValue();
TestHelpers.assertDeepEquals(expected, result);
expected = MapBuilder.of("bar", "d", "baz", "b", "bat", "e");
result = events.get(1).getSnapshot().getValue();
TestHelpers.assertDeepEquals(expected, result);
}
@Test
public void testChildAddedWithLimit()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
writer.child("a").setValue(1);
writer.child("b").setValue("b");
final Map<String, Object> deepObject = MapBuilder.of(
"deep", "path",
"of", MapBuilder.of("stuff", true));
new WriteFuture(writer.child("c"), deepObject).timedGet();
final Semaphore semaphore = new Semaphore(0);
reader.limitToLast(3).addChildEventListener(new ChildEventListener() {
int count = 0;
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
if (count == 0) {
assertEquals("a", snapshot.getKey());
assertEquals(1L, snapshot.getValue());
} else if (count == 1) {
assertEquals("b", snapshot.getKey());
assertEquals("b", snapshot.getValue());
} else if (count == 2) {
assertEquals("c", snapshot.getKey());
TestHelpers.assertDeepEquals(deepObject, snapshot.getValue());
} else {
fail("Too many events");
}
count++;
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore, 3);
}
@Test
public void testChildChangedWithLimit()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
new WriteFuture(writer, MapBuilder.of("a", "something", "b", "we'll", "c", "overwrite"))
.timedGet();
final Map<String, Object> deepObject = MapBuilder.of(
"deep", "path",
"of", MapBuilder.of("stuff", true));
final Semaphore semaphore = new Semaphore(0);
final AtomicBoolean loaded = new AtomicBoolean(false);
reader.limitToLast(3).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
if (loaded.compareAndSet(false, true)) {
semaphore.release(1);
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
// Wait for the read to be initialized
TestHelpers.waitFor(semaphore);
reader.limitToLast(3).addChildEventListener(new ChildEventListener() {
int count = 0;
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
if (count == 0) {
assertEquals("a", snapshot.getKey());
assertEquals(1L, snapshot.getValue());
} else if (count == 1) {
assertEquals("b", snapshot.getKey());
assertEquals("b", snapshot.getValue());
} else if (count == 2) {
assertEquals("c", snapshot.getKey());
TestHelpers.assertDeepEquals(deepObject, snapshot.getValue());
} else {
fail("Too many events");
}
count++;
semaphore.release(1);
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
writer.child("a").setValue(1);
writer.child("b").setValue("b");
writer.child("c").setValue(deepObject);
TestHelpers.waitFor(semaphore, 3);
}
@Test
public void testChildRemovedWithLimit()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
writer.child("a").setValue(1);
writer.child("b").setValue("b");
final Map<String, Object> deepObject = MapBuilder.of(
"deep", "path",
"of", MapBuilder.of("stuff", true));
new WriteFuture(writer.child("c"), deepObject).timedGet();
final Semaphore semaphore = new Semaphore(0);
final AtomicBoolean loaded = new AtomicBoolean(false);
reader.limitToLast(3).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
if (loaded.compareAndSet(false, true)) {
semaphore.release(1);
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
// Wait for the read to be initialized
TestHelpers.waitFor(semaphore);
reader.limitToLast(3).addChildEventListener(new ChildEventListener() {
int count = 0;
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
if (count == 0) {
assertEquals("a", snapshot.getKey());
} else if (count == 1) {
assertEquals("b", snapshot.getKey());
} else if (count == 2) {
assertEquals("c", snapshot.getKey());
} else {
fail("Too many events");
}
count++;
semaphore.release(1);
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
writer.child("a").removeValue();
writer.child("b").removeValue();
writer.child("c").removeValue();
TestHelpers.waitFor(semaphore, 3);
}
@Test
public void testChildRemovedWhenParentRemoved()
throws InterruptedException, TestFailure, ExecutionException, TimeoutException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
writer.child("a").setValue(1);
writer.child("b").setValue("b");
final Map<String, Object> deepObject = MapBuilder.of(
"deep", "path",
"of", MapBuilder.of("stuff", true));
new WriteFuture(writer.child("c"), deepObject).timedGet();
final Semaphore semaphore = new Semaphore(0);
final AtomicBoolean loaded = new AtomicBoolean(false);
reader.limitToLast(3).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
if (loaded.compareAndSet(false, true)) {
semaphore.release(1);
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
// Wait for the read to be initialized
TestHelpers.waitFor(semaphore);
reader.limitToLast(3).addChildEventListener(new ChildEventListener() {
int count = 0;
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
if (count == 0) {
assertEquals("a", snapshot.getKey());
} else if (count == 1) {
assertEquals("b", snapshot.getKey());
} else if (count == 2) {
assertEquals("c", snapshot.getKey());
} else {
fail("Too many events");
}
count++;
semaphore.release(1);
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
writer.removeValue();
TestHelpers.waitFor(semaphore, 3);
}
@Test
public void testChildRemovedWhenParentSetToScalar()
throws InterruptedException, TestFailure, ExecutionException, TimeoutException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
writer.child("a").setValue(1);
writer.child("b").setValue("b");
final Map<String, Object> deepObject = MapBuilder.of(
"deep", "path",
"of", MapBuilder.of("stuff", true));
new WriteFuture(writer.child("c"), deepObject).timedGet();
final Semaphore semaphore = new Semaphore(0);
final AtomicBoolean loaded = new AtomicBoolean(false);
reader.limitToLast(3).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
if (loaded.compareAndSet(false, true)) {
semaphore.release(1);
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
// Wait for the read to be initialized
TestHelpers.waitFor(semaphore);
reader.limitToLast(3).addChildEventListener(new ChildEventListener() {
int count = 0;
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
if (count == 0) {
assertEquals("a", snapshot.getKey());
} else if (count == 1) {
assertEquals("b", snapshot.getKey());
} else if (count == 2) {
assertEquals("c", snapshot.getKey());
} else {
fail("Too many events");
}
count++;
semaphore.release(1);
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
writer.setValue("scalar");
TestHelpers.waitFor(semaphore, 3);
}
@Test
public void testMultipleQueries()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
Map<String, Object> toSet = new MapBuilder().put("a", 1).put("b", 2).put("c", 3)
.put("d", 4).build();
new WriteFuture(writer, toSet).timedGet();
TestHelpers.getSnap(reader);
final Semaphore semaphore = new Semaphore(0);
final AtomicInteger queryAddedCount = new AtomicInteger(0);
reader.startAt(null, "d").addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
queryAddedCount.incrementAndGet();
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
final AtomicInteger defaultAddedCount = new AtomicInteger(0);
reader.addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
defaultAddedCount.incrementAndGet();
semaphore.release(1);
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore, 5);
assertEquals(1, queryAddedCount.get());
assertEquals(4, defaultAddedCount.get());
}
@Test
public void testStartAtEndAtQueriesWithPriorityChanges()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final List<String> addedFirst = new ArrayList<>();
final List<String> removedFirst = new ArrayList<>();
final List<String> addedSecond = new ArrayList<>();
final List<String> removedSecond = new ArrayList<>();
ref.startAt(0).endAt(10).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
addedFirst.add(snapshot.getKey());
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
removedFirst.add(snapshot.getKey());
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onCancelled(DatabaseError error) {
}
});
ref.startAt(10).endAt(20).addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
addedSecond.add(snapshot.getKey());
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
// No-op
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
// No-op
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
removedSecond.add(snapshot.getKey());
}
@Override
public void onCancelled(DatabaseError error) {
}
});
ref.child("a").setValue("a", 5);
ref.child("a").setValue("a", 15);
ref.child("a").setValue("a", 10);
new WriteFuture(ref.child("a"), "a", 5).timedGet();
assertEquals(2, addedFirst.size());
assertEquals("a", addedFirst.get(0));
assertEquals("a", addedFirst.get(1));
assertEquals(1, removedFirst.size());
assertEquals("a", removedFirst.get(0));
assertEquals(1, addedSecond.size());
assertEquals("a", addedSecond.get(0));
assertEquals(1, removedSecond.size());
assertEquals("a", removedSecond.get(0));
}
@Test
public void testDivergingQueries()
throws TestFailure, ExecutionException, TimeoutException, InterruptedException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
final DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
final Map<String, Object> toSet = MapBuilder.of(
"a", MapBuilder.of("b", 1L, "c", 2L),
"e", 3L);
new WriteFuture(writer, toSet).timedGet();
final AtomicBoolean childCalled = new AtomicBoolean(false);
final AtomicLong childValue = new AtomicLong(0);
reader.child("a/b").addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
if (snapshot.getValue() != null) {
childCalled.compareAndSet(false, true);
childValue.compareAndSet(0L, snapshot.getValue(Long.class));
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
new ReadFuture(reader.limitToLast(2), new ReadFuture.CompletionCondition() {
@Override
public boolean isComplete(List<EventRecord> events) {
if (events.size() == 1) {
TestHelpers.assertDeepEquals(toSet, events.get(0).getSnapshot().getValue());
try {
writer.child("d").setValue(4);
} catch (DatabaseException e) { // ignore
fail("Should not fail");
}
return false;
} else {
Map<String, Object> expected = MapBuilder.of("d", 4L, "e", 3L);
TestHelpers.assertDeepEquals(expected, events.get(1).getSnapshot().getValue());
return true;
}
}
}).timedGet();
assertTrue(childCalled.get());
assertEquals(1L, childValue.longValue());
}
@Test
public void testCacheInvalidation()
throws InterruptedException, TestFailure, TimeoutException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference reader = refs.get(0);
DatabaseReference writer = refs.get(1);
final AtomicBoolean startChecking = new AtomicBoolean(false);
final Semaphore ready = new Semaphore(0);
final ReadFuture future = new ReadFuture(reader.limitToLast(2),
new ReadFuture.CompletionCondition() {
@Override
public boolean isComplete(List<EventRecord> events) {
DataSnapshot snap = events.get(events.size() - 1).getSnapshot();
Object result = snap.getValue();
if (startChecking.compareAndSet(false, true) && result == null) {
ready.release(1);
return false;
}
// We already initialized the location, and now the remove has
// happened
// so that
// we have no more data
return startChecking.get() && result == null;
}
});
TestHelpers.waitFor(ready);
for (int i = 0; i < 4; ++i) {
writer.child("k" + i).setValue(i);
}
writer.removeValue();
future.timedGet();
}
@Test
public void testIntegerKeys1()
throws InterruptedException, TestFailure, TimeoutException {
final DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final Semaphore done = new Semaphore(0);
ref.setValue(
new MapBuilder().put("1", true).put("50", true).put("550", true).put("6", true)
.put("600", true).put("70", true).put("8", true).put("80", true).build(),
new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError error, DatabaseReference ref) {
ref.startAt(null, "80").addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
Map<String, Object> expected = MapBuilder.of(
"80", true, "550", true, "600", true);
TestHelpers.assertDeepEquals(expected, snapshot.getValue());
done.release();
}
@Override
public void onCancelled(DatabaseError error) {
}
});
}
});
TestHelpers.waitFor(done);
}
@Test
public void testIntegerKeys2()
throws InterruptedException, TestFailure, TimeoutException {
final DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final Semaphore done = new Semaphore(0);
ref.setValue(
new MapBuilder().put("1", true).put("50", true).put("550", true).put("6", true)
.put("600", true).put("70", true).put("8", true).put("80", true).build(),
new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError error, DatabaseReference ref) {
ref.endAt(null, "50").addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
Map<String, Object> expected = new MapBuilder().put("1", true)
.put("6", true).put("8", true).put("50", true).build();
TestHelpers.assertDeepEquals(expected, snapshot.getValue());
done.release();
}
@Override
public void onCancelled(DatabaseError error) {
}
});
}
});
TestHelpers.waitFor(done);
}
@Test
public void testIntegerKeys3()
throws InterruptedException, TestFailure, TimeoutException {
final DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final Semaphore done = new Semaphore(0);
ref.setValue(
new MapBuilder().put("1", true).put("50", true).put("550", true).put("6", true)
.put("600", true).put("70", true).put("8", true).put("80", true).build(),
new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError error, DatabaseReference ref) {
ref.startAt(null, "50").endAt(null, "80")
.addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
Map<String, Object> expected = MapBuilder.of(
"50", true, "70", true, "80", true);
TestHelpers.assertDeepEquals(expected, snapshot.getValue());
done.release();
}
@Override
public void onCancelled(DatabaseError error) {
}
});
}
});
TestHelpers.waitFor(done);
}
@Test
public void testMoveOutsideOfWindowIntoWindow()
throws InterruptedException, ExecutionException, TimeoutException, TestFailure {
final DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
Map<String, Object> initialValue = MapBuilder.of(
"a", MapBuilder.of(".priority", 1L, ".value", "a"),
"b", MapBuilder.of(".priority", 2L, ".value", "b"),
"c", MapBuilder.of(".priority", 3L, ".value", "c"));
new WriteFuture(ref, initialValue).timedGet();
final Query query = ref.limitToLast(2);
final Semaphore ready = new Semaphore(0);
final AtomicBoolean loaded = new AtomicBoolean(false);
query.addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
if (loaded.compareAndSet(false, true)) {
assertEquals(2, snapshot.getChildrenCount());
assertTrue(snapshot.hasChild("b"));
assertTrue(snapshot.hasChild("c"));
ready.release(1);
}
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(ready);
ref.child("a").setPriority(4L, new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError error, DatabaseReference ref) {
ready.release(1);
}
});
TestHelpers.waitFor(ready);
}
@Test
public void testEmptyLimitWithBadHash() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ref.getRepo().setHijackHash(true);
DataSnapshot snap = TestHelpers.getSnap(ref.limitToLast(1));
assertNull(snap.getValue());
ref.getRepo().setHijackHash(false);
}
@Test
public void testAddingQueries()
throws InterruptedException, ExecutionException, TimeoutException, TestFailure {
final DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
new WriteFuture(ref.child("0"), "test1").timedGet();
ChildEventListener childEventListener = new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildChanged(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onChildRemoved(DataSnapshot dataSnapshot) {
}
@Override
public void onChildMoved(DataSnapshot dataSnapshot, String s) {
}
@Override
public void onCancelled(DatabaseError firebaseError) {
}
};
ref.startAt("a").endAt("b").addChildEventListener(childEventListener);
DataSnapshot snapshot = new ReadFuture(ref).timedGet().get(0).getSnapshot();
assertEquals(ImmutableList.of("test1"), snapshot.getValue());
}
@Test
public void testEqualToQuery() throws InterruptedException {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
ValueExpectationHelper expectations = new ValueExpectationHelper();
expectations.add(ref.equalTo(1), MapBuilder.of("a", "vala"));
expectations.add(ref.equalTo(2), MapBuilder.of("b", "valb"));
expectations.add(ref.equalTo("abc"), MapBuilder.of("z", "valz"));
expectations.add(ref.equalTo(2, "no_key"), null);
expectations.add(ref.equalTo(2, "b"), MapBuilder.of("b", "valb"));
ref.child("a").setValue("vala", 1);
ref.child("b").setValue("valb", 2);
ref.child("c").setValue("valc", 3);
ref.child("z").setValue("valz", "abc");
expectations.waitForEvents();
}
@Test
public void testRemoveListenerOnDefaultQuery()
throws InterruptedException, ExecutionException, TimeoutException, TestFailure {
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
new WriteFuture(ref.child("a"), "foo", 100).timedGet();
final Semaphore semaphore = new Semaphore(0);
final DataSnapshot[] snapshotHolder = new DataSnapshot[1];
ValueEventListener listener = ref.startAt(99).addValueEventListener(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
snapshotHolder[0] = snapshot;
semaphore.release();
}
@Override
public void onCancelled(DatabaseError error) {
fail("Unexpected error: " + error);
}
});
TestHelpers.waitFor(semaphore);
Map<String, Object> expected = MapBuilder.of("a", "foo");
TestHelpers.assertDeepEquals(expected, snapshotHolder[0].getValue());
ref.removeEventListener(listener);
new WriteFuture(ref.child("a"), "bar", 100).timedGet();
// the listener is removed the value should have not changed
TestHelpers.assertDeepEquals(expected, snapshotHolder[0].getValue());
}
@Test
public void testFallbackForOrderBy() throws InterruptedException {
final DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final Semaphore done = new Semaphore(0);
Map<String, Object> initial = MapBuilder.of(
"a", MapBuilder.of("foo", 3),
"b", MapBuilder.of("foo", 1),
"c", MapBuilder.of("foo", 2));
ref.setValue(initial, new DatabaseReference.CompletionListener() {
@Override
public void onComplete(DatabaseError error, DatabaseReference ref) {
done.release();
}
});
TestHelpers.waitFor(done);
final List<String> children = new ArrayList<>();
ref.orderByChild("foo").addChildEventListener(new ChildEventListener() {
@Override
public void onChildAdded(DataSnapshot snapshot, String previousChildName) {
children.add(snapshot.getKey());
if (children.size() == 3) {
done.release();
}
}
@Override
public void onChildChanged(DataSnapshot snapshot, String previousChildName) {
}
@Override
public void onChildRemoved(DataSnapshot snapshot) {
}
@Override
public void onChildMoved(DataSnapshot snapshot, String previousChildName) {
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(done);
TestHelpers.assertDeepEquals(ImmutableList.of("b", "c", "a"), children);
}
@Test
public void testSnapshotChildrenOrdering()
throws ExecutionException, TimeoutException, TestFailure, InterruptedException {
List<DatabaseReference> refs = IntegrationTestUtils.getRandomNode(masterApp, 2);
DatabaseReference writer = refs.get(0);
DatabaseReference reader = refs.get(1);
final Semaphore semaphore = new Semaphore(0);
final Map<String, Object> list = MapBuilder.of(
"a", MapBuilder.of(
"thisvaluefirst", MapBuilder.of(".value", true, ".priority", 1),
"name", MapBuilder.of(".value", "Michael", ".priority", 2),
"thisvaluelast", MapBuilder.of(".value", true, ".priority", 3)),
"b", MapBuilder.of(
"thisvaluefirst", new MapBuilder().put(".value", true).put(".priority", null).build(),
"name", MapBuilder.of(".value", "Rob", ".priority", 2),
"thisvaluelast", MapBuilder.of(".value", true, ".priority", 3)),
"c", MapBuilder.of(
"thisvaluefirst", MapBuilder.of(".value", true, ".priority", 1),
"name", MapBuilder.of(".value", "Jonny", ".priority", 2),
"thisvaluelast", MapBuilder.of(".value", true, ".priority", "somestring")));
new WriteFuture(writer, list).timedGet();
reader.orderByChild("name").addListenerForSingleValueEvent(new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
List<String> expectedKeys = ImmutableList.of("thisvaluefirst", "name", "thisvaluelast");
// Validate that snap.child() resets order to default for child
// snaps
List<String> orderedKeys = new ArrayList<>();
for (DataSnapshot childSnap : snapshot.child("b").getChildren()) {
orderedKeys.add(childSnap.getKey());
}
assertEquals(expectedKeys, orderedKeys);
// Validate that snap.forEach() resets ordering to default for
// child snaps
List<Object> orderedNames = new ArrayList<>();
for (DataSnapshot childSnap : snapshot.getChildren()) {
orderedNames.add(childSnap.child("name").getValue());
orderedKeys.clear();
for (DataSnapshot grandchildSnap : childSnap.getChildren()) {
orderedKeys.add(grandchildSnap.getKey());
}
assertEquals(expectedKeys, orderedKeys);
}
assertEquals(ImmutableList.of("Jonny", "Michael", "Rob"), orderedNames);
semaphore.release();
}
@Override
public void onCancelled(DatabaseError error) {
}
});
TestHelpers.waitFor(semaphore);
}
@Test
public void testAddingListensForTheSamePathDoesNotCheckFail() throws Throwable {
// This bug manifests itself if there's a hierarchy of query listener,
// default listener and
// one-time listener underneath. During one-time listener registration,
// sync-tree traversal
// stopped as soon as it found a complete server cache (this is the case
// for not indexed query
// view). The problem is that the same traversal was looking for a
// ancestor default view, and
// the early exit prevented from finding the default listener above the
// one-time listener. Event
// removal code path wasn't removing the listener because it stopped as
// soon as it found the
// default view. This left the zombie one-time listener and check failed
// on the second attempt
// to create a listener for the same path (asana#61028598952586).
DatabaseReference ref = IntegrationTestUtils.getRandomNode(masterApp);
final Semaphore semaphore = new Semaphore(0);
ValueEventListener dummyListen = new ValueEventListener() {
@Override
public void onDataChange(DataSnapshot snapshot) {
semaphore.release();
}
@Override
public void onCancelled(DatabaseError error) {
}
};
ref.child("child").setValue(TestHelpers.fromJsonString("{\"name\": \"John\"}"));
ref.orderByChild("name").equalTo("John").addValueEventListener(dummyListen);
ref.child("child").addValueEventListener(dummyListen);
TestHelpers.waitFor(semaphore, 2);
ref.child("child").child("favoriteToy").addListenerForSingleValueEvent(dummyListen);
TestHelpers.waitFor(semaphore, 1);
ref.child("child").child("favoriteToy").addListenerForSingleValueEvent(dummyListen);
TestHelpers.waitFor(semaphore, 1);
ref.removeEventListener(dummyListen);
ref.child("child").removeEventListener(dummyListen);
}
}