/* * Copyright 2016 Realm 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 io.realm; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; import java.util.Arrays; import java.util.Collections; import java.util.Date; import java.util.List; import java.util.concurrent.Callable; import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.Future; import io.realm.entities.AllJavaTypes; import io.realm.entities.NonLatinFieldNames; import io.realm.entities.NullTypes; import io.realm.rule.TestRealmConfigurationFactory; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; /** * Test class for all methods part of the the {@link RealmCollection} interface. * This class only tests collections that are managed by Realm. See {@link UnManagedRealmCollectionTests} for * all tests targeting unmanaged collections. * * Methods tested in this class: * * # RealmCollection * * + RealmQuery<E> where(); * + Number min(String fieldName); * + Number max(String fieldName); * + Number sum(String fieldName); * + double average(String fieldName); * + Date maxDate(String fieldName); * + Date minDate(String fieldName); * + void deleteAllFromRealm(); * + boolean isLoaded(); * + boolean load(); * + boolean isValid(); * + BaseRealm getRealm(); * * # Collection * * - public boolean add(E object); * - public boolean addAll(Collection<? extends E> collection); * - public void deleteAll(); * - public boolean contains(Object object); * - public boolean containsAll(Collection<?> collection); * - public boolean equals(Object object); * - public int hashCode(); * - public boolean isEmpty(); * - public Iterator<E> iterator(); * - public boolean remove(Object object); * - public boolean removeAll(Collection<?> collection); * - public boolean retainAll(Collection<?> collection); * - public int size(); * - public Object[] toArray(); * - public <T> T[] toArray(T[] array); */ @RunWith(Parameterized.class) public class ManagedRealmCollectionTests extends CollectionTests { private static final int TEST_SIZE = 10; @Rule public final TestRealmConfigurationFactory configFactory = new TestRealmConfigurationFactory(); @Rule public final ExpectedException thrown = ExpectedException.none(); private final ManagedCollection collectionClass; private Realm realm; // Collections used for testing private RealmCollection<AllJavaTypes> collection; @Parameterized.Parameters(name = "{0}") public static List<ManagedCollection> data() { return Arrays.asList(ManagedCollection.values()); } public ManagedRealmCollectionTests(ManagedCollection collectionType) { this.collectionClass = collectionType; } @Before public void setup() { realm = Realm.getInstance(configFactory.createConfiguration()); populateRealm(realm, TEST_SIZE); collection = createCollection(collectionClass); } @After public void tearDown() { realm.close(); } private OrderedRealmCollection<AllJavaTypes> createCollection(ManagedCollection collectionClass) { OrderedRealmCollection<AllJavaTypes> orderedCollection; switch (collectionClass) { case REALMRESULTS_SNAPSHOT_LIST_BASE: case MANAGED_REALMLIST: orderedCollection = realm.where(AllJavaTypes.class) .equalTo(AllJavaTypes.FIELD_LONG, 0) .findFirst() .getFieldList(); break; case REALMRESULTS_SNAPSHOT_RESULTS_BASE: case REALMRESULTS: orderedCollection = realm.where(AllJavaTypes.class) .findAllSorted(AllJavaTypes.FIELD_LONG, Sort.ASCENDING); break; default: throw new AssertionError("Unsupported class: " + collectionClass); } if (isSnapshot(collectionClass)) { orderedCollection = orderedCollection.createSnapshot(); } return orderedCollection; } private OrderedRealmCollection<NullTypes> createEmptyCollection(Realm realm, ManagedCollection collectionClass) { OrderedRealmCollection<NullTypes> orderedCollection; switch (collectionClass) { case REALMRESULTS_SNAPSHOT_LIST_BASE: case MANAGED_REALMLIST: realm.beginTransaction(); NullTypes obj = realm.createObject(NullTypes.class, 0); realm.commitTransaction(); orderedCollection = obj.getFieldListNull(); break; case REALMRESULTS_SNAPSHOT_RESULTS_BASE: case REALMRESULTS: orderedCollection = realm.where(NullTypes.class).findAll(); break; default: throw new AssertionError("Unknown collection: " + collectionClass); } if (isSnapshot(collectionClass)) { orderedCollection = orderedCollection.createSnapshot(); } return orderedCollection; } private OrderedRealmCollection<NullTypes> createAllNullRowsForNumericTesting(Realm realm, ManagedCollection collectionClass) { TestHelper.populateAllNullRowsForNumericTesting(realm); OrderedRealmCollection<NullTypes> orderedCollection; switch (collectionClass) { case REALMRESULTS_SNAPSHOT_LIST_BASE: case MANAGED_REALMLIST: RealmResults<NullTypes> results = realm.where(NullTypes.class).findAll(); RealmList<NullTypes> list = results.get(0).getFieldListNull(); realm.beginTransaction(); for (int i = 0; i < results.size(); i++) { list.add(results.get(i)); } realm.commitTransaction(); orderedCollection = list; break; case REALMRESULTS_SNAPSHOT_RESULTS_BASE: case REALMRESULTS: orderedCollection = realm.where(NullTypes.class).findAll(); break; default: throw new AssertionError("Unknown collection: " + collectionClass); } if (isSnapshot(collectionClass)) { orderedCollection = orderedCollection.createSnapshot(); } return orderedCollection; } private OrderedRealmCollection<NullTypes> createPartialNullRowsForNumericTesting(Realm realm, ManagedCollection collectionClass) { populatePartialNullRowsForNumericTesting(realm); OrderedRealmCollection<NullTypes> orderedCollection; switch (collectionClass) { case REALMRESULTS_SNAPSHOT_LIST_BASE: case MANAGED_REALMLIST: RealmResults<NullTypes> results = realm.where(NullTypes.class).findAll(); RealmList<NullTypes> list = results.get(0).getFieldListNull(); realm.beginTransaction(); int size = results.size(); for (int i = 0; i < size; i++) { list.add(results.get(i)); } realm.commitTransaction(); orderedCollection = list; break; case REALMRESULTS_SNAPSHOT_RESULTS_BASE: case REALMRESULTS: orderedCollection = realm.where(NullTypes.class).findAll(); break; default: throw new AssertionError("Unknown collection: " + collectionClass); } if (isSnapshot(collectionClass)) { orderedCollection = orderedCollection.createSnapshot(); } return orderedCollection; } // PRE-CONDITION: populateRealm() was called as part of setUp() private OrderedRealmCollection<NonLatinFieldNames> createNonLatinCollection(Realm realm, ManagedCollection collectionClass) { OrderedRealmCollection<NonLatinFieldNames> orderedCollection; switch (collectionClass) { case REALMRESULTS_SNAPSHOT_LIST_BASE: case MANAGED_REALMLIST: realm.beginTransaction(); RealmResults<NonLatinFieldNames> results = realm.where(NonLatinFieldNames.class).findAll(); RealmList<NonLatinFieldNames> list = results.get(0).getChildren(); for (int i = 0; i < results.size(); i++) { list.add(results.get(i)); } realm.commitTransaction(); orderedCollection = list; break; case REALMRESULTS_SNAPSHOT_RESULTS_BASE: case REALMRESULTS: orderedCollection = realm.where(NonLatinFieldNames.class).findAll(); break; default: throw new AssertionError("Unknown collection: " + collectionClass); } if (isSnapshot(collectionClass)) { orderedCollection = orderedCollection.createSnapshot(); } return orderedCollection; } @Test public void where() { if (isSnapshot(collectionClass)) { thrown.expect(UnsupportedOperationException.class); } RealmResults<AllJavaTypes> results = collection.where().findAll(); assertEquals(TEST_SIZE, results.size()); } @Test public void where_contains() { RealmQuery<AllJavaTypes> query = realm.where(AllJavaTypes.class).findAll().where(); AllJavaTypes item = query.findFirst(); assertTrue("Item should exist in results.", query.findAll().contains(item)); } @Test public void where_contains_null() { RealmQuery<AllJavaTypes> query = realm.where(AllJavaTypes.class).findAll().where(); assertFalse("Should not contain a null item.", query.findAll().contains(null)); } @Test public void where_shouldNotContainRemovedItem() { RealmQuery<AllJavaTypes> query = realm.where(AllJavaTypes.class).findAll().where(); AllJavaTypes item = realm.where(AllJavaTypes.class).findFirst(); realm.beginTransaction(); item.deleteFromRealm(); realm.commitTransaction(); assertFalse("Should not contain a removed item.", query.findAll().contains(item)); } /** * Tests to see if a particular item that does exist in the same Realm does not * exist in the result set of another query. */ @Test public void where_lessThanGreaterThan() { RealmResults<AllJavaTypes> items = realm.where(AllJavaTypes.class).lessThan(AllJavaTypes.FIELD_LONG, 1000).findAll(); AllJavaTypes anotherType = realm.where(AllJavaTypes.class).greaterThan(AllJavaTypes.FIELD_LONG, 1000).findFirst(); assertFalse("Should not be able to find item in another result list.", items.contains(anotherType)); } @Test public void where_equalTo_manyConditions() { RealmQuery<AllJavaTypes> query = realm.where(AllJavaTypes.class); query.equalTo(AllJavaTypes.FIELD_LONG, 0); for (int i = 1; i < TEST_SIZE; i++) { query.or().equalTo(AllJavaTypes.FIELD_LONG, i); } RealmResults<AllJavaTypes> allTypesRealmResults = query.findAll(); assertEquals(TEST_SIZE, allTypesRealmResults.size()); } @Test public void where_findAll_size() { RealmResults<AllJavaTypes> results = realm.where(AllJavaTypes.class).findAll(); assertEquals(TEST_SIZE, results.size()); // Querying a RealmResults should find objects that fulfill the condition. RealmResults<AllJavaTypes> onedigits = results.where().lessThan(AllJavaTypes.FIELD_LONG, 10).findAll(); assertEquals(Math.min(10, TEST_SIZE), onedigits.size()); // If no objects fulfill conditions, the result has zero objects. RealmResults<AllJavaTypes> none = results.where().greaterThan(AllJavaTypes.FIELD_LONG, TEST_SIZE).findAll(); assertEquals(0, none.size()); // Querying a result with zero objects must give zero objects. RealmResults<AllJavaTypes> stillNone = none.where().greaterThan(AllJavaTypes.FIELD_LONG, TEST_SIZE).findAll(); assertEquals(0, stillNone.size()); } @Test public void where_findAllSorted() { RealmResults<AllJavaTypes> results = realm.where(AllJavaTypes.class).findAllSorted(AllJavaTypes.FIELD_LONG, Sort.ASCENDING); assertEquals(TEST_SIZE, results.size()); //noinspection ConstantConditions assertEquals(0, results.first().getFieldLong()); //noinspection ConstantConditions assertEquals(TEST_SIZE - 1, results.last().getFieldLong()); RealmResults<AllJavaTypes> reverseList = realm.where(AllJavaTypes.class).findAllSorted(AllJavaTypes.FIELD_LONG, Sort.DESCENDING); assertEquals(TEST_SIZE, reverseList.size()); //noinspection ConstantConditions assertEquals(0, reverseList.last().getFieldLong()); //noinspection ConstantConditions assertEquals(TEST_SIZE - 1, reverseList.first().getFieldLong()); try { realm.where(AllJavaTypes.class).findAllSorted("invalid", Sort.DESCENDING); fail(); } catch (IllegalArgumentException ignored) { } } @Test public void where_queryDateField() { RealmQuery<AllJavaTypes> query = realm.where(AllJavaTypes.class).equalTo(AllJavaTypes.FIELD_DATE, new Date(YEAR_MILLIS * 20)); RealmResults<AllJavaTypes> all = query.findAll(); assertEquals(1, query.count()); assertEquals(1, all.size()); // before 1901 query = realm.where(AllJavaTypes.class).equalTo(AllJavaTypes.FIELD_DATE, new Date(YEAR_MILLIS * -100)); all = query.findAll(); assertEquals(1, query.count()); assertEquals(1, all.size()); // after 2038 query = realm.where(AllJavaTypes.class).equalTo(AllJavaTypes.FIELD_DATE, new Date(YEAR_MILLIS * 80)); all = query.findAll(); assertEquals(1, query.count()); assertEquals(1, all.size()); } @Test public void min() { Number minimum = collection.min(AllJavaTypes.FIELD_LONG); assertEquals(0, minimum.intValue()); } // Tests min on empty columns. @Test public void min_emptyNonNullFields() { OrderedRealmCollection<NullTypes> results = createEmptyCollection(realm, collectionClass); assertNull(results.min(NullTypes.FIELD_INTEGER_NOT_NULL)); assertNull(results.min(NullTypes.FIELD_FLOAT_NOT_NULL)); assertNull(results.min(NullTypes.FIELD_DOUBLE_NOT_NULL)); assertNull(results.minDate(NullTypes.FIELD_DATE_NOT_NULL)); } // Tests min on nullable rows with all null values. @Test public void min_emptyNullFields() { OrderedRealmCollection<NullTypes> results = createAllNullRowsForNumericTesting(realm, collectionClass); assertNull(results.max(NullTypes.FIELD_INTEGER_NULL)); assertNull(results.max(NullTypes.FIELD_FLOAT_NULL)); assertNull(results.max(NullTypes.FIELD_DOUBLE_NULL)); assertNull(results.maxDate(NullTypes.FIELD_DATE_NULL)); } // Tests min on nullable rows with partial null values. @Test public void min_partialNullRows() { OrderedRealmCollection<NullTypes> results = createPartialNullRowsForNumericTesting(realm, collectionClass); assertEquals(0, results.min(NullTypes.FIELD_INTEGER_NULL).intValue()); assertEquals(0f, results.min(NullTypes.FIELD_FLOAT_NULL).floatValue(), 0f); assertEquals(0d, results.min(NullTypes.FIELD_DOUBLE_NULL).doubleValue(), 0d); } @Test public void max() { Number maximum = collection.max(AllJavaTypes.FIELD_LONG); assertEquals(TEST_SIZE - 1, maximum.intValue()); } // Tests max on empty columns. @Test public void max_emptyNonNullFields() { OrderedRealmCollection<NullTypes> results = createEmptyCollection(realm, collectionClass); assertNull(results.max(NullTypes.FIELD_INTEGER_NOT_NULL)); assertNull(results.max(NullTypes.FIELD_FLOAT_NOT_NULL)); assertNull(results.max(NullTypes.FIELD_DOUBLE_NOT_NULL)); assertNull(results.maxDate(NullTypes.FIELD_DATE_NOT_NULL)); } // Tests max on nullable rows with all null values. @Test public void max_emptyNullFields() { OrderedRealmCollection<NullTypes> results = createAllNullRowsForNumericTesting(realm, collectionClass); assertNull(results.max(NullTypes.FIELD_INTEGER_NULL)); assertNull(results.max(NullTypes.FIELD_FLOAT_NULL)); assertNull(results.max(NullTypes.FIELD_DOUBLE_NULL)); assertNull(results.maxDate(NullTypes.FIELD_DATE_NULL)); } // Tests max on nullable rows with partial null values. @Test public void max_partialNullRows() { OrderedRealmCollection<NullTypes> results = createPartialNullRowsForNumericTesting(realm, collectionClass); assertEquals(1, results.max(NullTypes.FIELD_INTEGER_NULL).intValue()); assertEquals(2f, results.max(NullTypes.FIELD_FLOAT_NULL).floatValue(), 0f); assertEquals(3d, results.max(NullTypes.FIELD_DOUBLE_NULL).doubleValue(), 0d); } @Test public void sum() { Number sum = collection.sum(AllJavaTypes.FIELD_LONG); // Sum of numbers 0 to M-1: (M-1)*M/2 assertEquals((TEST_SIZE - 1) * TEST_SIZE / 2, sum.intValue()); } // Tests sum on nullable rows with all null values. @Test public void sum_nullRows() { OrderedRealmCollection<NullTypes> resultList = createAllNullRowsForNumericTesting(realm, collectionClass); assertEquals(0, resultList.sum(NullTypes.FIELD_INTEGER_NULL).intValue()); assertEquals(0f, resultList.sum(NullTypes.FIELD_FLOAT_NULL).floatValue(), 0f); assertEquals(0d, resultList.sum(NullTypes.FIELD_DOUBLE_NULL).doubleValue(), 0d); } // Tests sum on nullable rows with partial null values. @Test public void sum_partialNullRows() { OrderedRealmCollection<NullTypes> resultList = createPartialNullRowsForNumericTesting(realm, collectionClass); assertEquals(1, resultList.sum(NullTypes.FIELD_INTEGER_NULL).intValue()); assertEquals(2f, resultList.sum(NullTypes.FIELD_FLOAT_NULL).floatValue(), 0f); assertEquals(3d, resultList.sum(NullTypes.FIELD_DOUBLE_NULL).doubleValue(), 0d); } @Test public void sum_nonLatinColumnNames() { OrderedRealmCollection<NonLatinFieldNames> resultList = createNonLatinCollection(realm, collectionClass); Number sum = resultList.sum(NonLatinFieldNames.FIELD_LONG_KOREAN_CHAR); // Sum of numbers 0 to M-1: (M-1)*M/2 assertEquals((TEST_SIZE - 1) * TEST_SIZE / 2, sum.intValue()); sum = resultList.sum(NonLatinFieldNames.FIELD_LONG_GREEK_CHAR); // Sum of numbers 0 to M-1: (M-1)*M/2 assertEquals((TEST_SIZE - 1) * TEST_SIZE / 2, sum.intValue()); } @Test public void avg() { double N = (double) TEST_SIZE; // Sum of numbers 1 to M: M*(M+1)/2 // See setUp() for values of fields. // N = TEST_DATA_SIZE // Type: double; a = Math.PI // a, a+1, ..., a+i, ..., a+N-1 // sum = Math.PI*N + N*(N-1)/2 // average = sum/N = Math.PI+(N-1)/2 double average = Math.PI + (N - 1.0) * 0.5; assertEquals(average, collection.average(AllJavaTypes.FIELD_DOUBLE), 0.0001); // Type: long // 0, 1, ..., N-1 // sum = N*(N-1)/2 // average = sum/N = (N-1)/2 assertEquals(0.5 * (N - 1), collection.average(AllJavaTypes.FIELD_LONG), 0.0001); // Type: float; b = 1.234567 // b, b+1, ..., b+i, ..., b+N-1 // sum = b*N + N*(N-1)/2 // average = sum/N = b + (N-1)/2 assertEquals(1.234567 + 0.5 * (N - 1.0), collection.average(AllJavaTypes.FIELD_FLOAT), 0.0001); } // Tests average on empty columns. @Test public void avg_emptyNonNullFields() { OrderedRealmCollection<NullTypes> resultList = createEmptyCollection(realm, collectionClass); assertEquals(0d, resultList.average(NullTypes.FIELD_INTEGER_NOT_NULL), 0d); assertEquals(0d, resultList.average(NullTypes.FIELD_FLOAT_NOT_NULL), 0d); assertEquals(0d, resultList.average(NullTypes.FIELD_DOUBLE_NOT_NULL), 0d); } // Tests average on nullable rows with all null values. @Test public void avg_emptyNullFields() { OrderedRealmCollection<NullTypes> resultList = createEmptyCollection(realm, collectionClass); assertEquals(0d, resultList.average(NullTypes.FIELD_INTEGER_NULL), 0d); assertEquals(0d, resultList.average(NullTypes.FIELD_FLOAT_NULL), 0d); assertEquals(0d, resultList.average(NullTypes.FIELD_DOUBLE_NULL), 0d); } // Tests average on nullable rows with partial null values. @Test public void avg_partialNullRows() { OrderedRealmCollection<NullTypes> resultList = createPartialNullRowsForNumericTesting(realm, collectionClass); assertEquals(0.5d, resultList.average(NullTypes.FIELD_INTEGER_NULL), 0d); assertEquals(1.0d, resultList.average(NullTypes.FIELD_FLOAT_NULL), 0d); assertEquals(1.5d, resultList.average(NullTypes.FIELD_DOUBLE_NULL), 0d); } @Test public void maxDate() { assertEquals(TEST_SIZE, collection.size()); assertEquals(new Date(YEAR_MILLIS * 20 * (TEST_SIZE / 2 - 1)), collection.maxDate(AllJavaTypes.FIELD_DATE)); } @Test public void minDate() { assertEquals(TEST_SIZE, collection.size()); assertEquals(new Date(-YEAR_MILLIS * 20 * TEST_SIZE / 2), collection.minDate(AllJavaTypes.FIELD_DATE)); } // Deletes the last row in the collection then tests the aggregates methods. // Since deletion will turn the corresponding object into invalid for collection snapshot, this tests if the // aggregates methods ignore the invalid rows and return the correct result. @Test public void aggregates_deleteLastRow() { assertTrue(TEST_SIZE > 3); assertEquals(TEST_SIZE, collection.size()); realm.beginTransaction(); realm.where(AllJavaTypes.class).equalTo(AllJavaTypes.FIELD_LONG, TEST_SIZE - 1).findFirst().deleteFromRealm(); realm.commitTransaction(); int sizeAfterRemove = TEST_SIZE - 1; assertEquals(0, collection.min(AllJavaTypes.FIELD_LONG).intValue()); assertEquals(sizeAfterRemove - 1, collection.max(AllJavaTypes.FIELD_LONG).intValue()); // Sum of numbers 0 to M-1: (M-1)*M/2 assertEquals((sizeAfterRemove - 1) * sizeAfterRemove / 2, collection.sum(AllJavaTypes.FIELD_LONG).intValue()); double average = Math.PI + (sizeAfterRemove - 1.0) * 0.5; assertEquals(average, collection.average(AllJavaTypes.FIELD_DOUBLE), 0.0001); assertEquals(new Date(YEAR_MILLIS * 20 * (sizeAfterRemove / 2 - 1)), collection.maxDate(AllJavaTypes.FIELD_DATE)); assertEquals(new Date(-YEAR_MILLIS * 20 * TEST_SIZE / 2), collection.minDate(AllJavaTypes.FIELD_DATE)); } @Test public void realmMethods_invalidFieldNames() { String[] fieldNames = new String[] { null, "", "foo", AllJavaTypes.FIELD_STRING + ".foo", TestHelper.getRandomString(65) }; for (RealmCollectionMethod realmMethod : RealmCollectionMethod.values()) { for (String fieldName : fieldNames) { try { switch (realmMethod) { case MIN: collection.min(fieldName); break; case MAX: collection.max(fieldName); break; case SUM: collection.sum(fieldName); break; case AVERAGE: collection.average(fieldName); break; case MIN_DATE: collection.minDate(fieldName); break; case MAX_DATE: collection.maxDate(fieldName); break; // These methods doesn't take any arguments. // Just skip them. case WHERE: case DELETE_ALL_FROM_REALM: case IS_VALID: case IS_MANAGED: continue; default: fail("Unknown method: " + realmMethod); } fail(realmMethod + " did not throw an exception for input: " + fieldName); } catch (IllegalArgumentException ignored) { } } } } @Test public void realmMethods_invalidFieldType() { String fieldName = AllJavaTypes.FIELD_STRING; for (RealmCollectionMethod realmMethod : RealmCollectionMethod.values()) { try { switch (realmMethod) { case MIN: collection.min(fieldName); break; case MAX: collection.max(fieldName); break; case SUM: collection.sum(fieldName); break; case AVERAGE: collection.average(fieldName); break; case MIN_DATE: collection.minDate(fieldName); break; case MAX_DATE: collection.maxDate(fieldName); break; // These methods doesn't take any arguments. // Just skip them. case WHERE: case DELETE_ALL_FROM_REALM: case IS_VALID: case IS_MANAGED: continue; default: fail("Unknown method: " + realmMethod); } fail(realmMethod + " did not throw an exception for input: " + fieldName); } catch (IllegalArgumentException ignored) { } } } @Test public void deleteAllFromRealm() { // If we have a self-referencing collection, removing all objects will crash // any following method. To avoid that scenario we make sure to use a collection // without cycles. int size = TEST_SIZE; if (collectionClass == ManagedCollection.MANAGED_REALMLIST) { RealmList list = (RealmList) collection; realm.beginTransaction(); list.remove(0); // Breaks the cycle. realm.commitTransaction(); size = TEST_SIZE - 1; } assertEquals(size, collection.size()); realm.beginTransaction(); assertTrue(collection.deleteAllFromRealm()); realm.commitTransaction(); if (isSnapshot(collectionClass)) { assertEquals(TEST_SIZE, collection.size()); } else { assertEquals(0, collection.size()); } } @Test(expected = IllegalStateException.class) public void deleteAllFromRealm_outsideTransaction() { collection.deleteAllFromRealm(); } @Test public void deleteAllFromRealm_emptyList() { OrderedRealmCollection<NullTypes> collection = createEmptyCollection(realm, collectionClass); realm.beginTransaction(); assertFalse(collection.deleteAllFromRealm()); realm.commitTransaction(); assertEquals(0, collection.size()); } @Test public void deleteAllFromRealm_invalidList() { realm.close(); try { collection.deleteAllFromRealm(); fail(); } catch (IllegalStateException ignored) { } } @Test public void isLoaded() { // RealmCollections are currently always loaded. Only exception is RealmResults. // See RealmResultsTests for extended tests on this. assertTrue(collection.isLoaded()); } @Test public void load() { // RealmCollections are currently always loaded, so this just returns true. Only exception is RealmResults. // See RealmResultsTests for extended tests on this. assertTrue(collection.load()); } @Test public void isValid() { assertTrue(collection.isValid()); } @Test public void isValid_realmClosed() { realm.close(); assertFalse(collection.isValid()); } @Test public void isManaged() { assertTrue(collection.isManaged()); } @Test public void contains_deletedRealmObject() { AllJavaTypes obj = collection.iterator().next(); realm.beginTransaction(); obj.deleteFromRealm(); realm.commitTransaction(); assertFalse(collection.contains(obj)); } @Test public void equals_sameRealmObjectsDifferentCollection() { assertTrue(collection.equals(createCollection(collectionClass))); } // Tests all methods that mutate data throw correctly if not inside an transaction. // Due to implementation details both UnsupportedOperation and IllegalState is accepted at this level. @Test public void mutableMethodsOutsideTransactions() { for (CollectionMutatorMethod method : CollectionMutatorMethod.values()) { // Defines expected exception. Class<? extends Throwable> expected = IllegalStateException.class; if (collectionClass == ManagedCollection.REALMRESULTS || isSnapshot(collectionClass)) { switch (method) { case ADD_OBJECT: case ADD_ALL_OBJECTS: case CLEAR: case REMOVE_OBJECT: case REMOVE_ALL: case RETAIN_ALL: expected = UnsupportedOperationException.class; break; default: // use default exception } } try { switch (method) { case DELETE_ALL: collection.deleteAllFromRealm(); break; case ADD_OBJECT: collection.add(new AllJavaTypes()); break; case ADD_ALL_OBJECTS: collection.addAll(Collections.singletonList(new AllJavaTypes())); break; case CLEAR: collection.clear(); break; case REMOVE_OBJECT: collection.remove(new AllJavaTypes()); break; case REMOVE_ALL: collection.removeAll(Collections.singletonList(new AllJavaTypes())); break; case RETAIN_ALL: collection.retainAll(Collections.singletonList(new AllJavaTypes())); break; } fail("Unknown method or it failed to throw: " + method); } catch (Throwable t) { if (!t.getClass().equals(expected)) { fail(method + " didn't throw the expected exception. Was: " + t + ", expected: " + expected); } } } } @Test public void methodsThrowOnWrongThread() throws ExecutionException, InterruptedException { realm.beginTransaction(); AllJavaTypes allJavaTypes = realm.createObject(AllJavaTypes.class, 42); realm.commitTransaction(); for (RealmCollectionMethod method : RealmCollectionMethod.values()) { assertTrue(method + " failed", runMethodOnWrongThread(method)); } for (CollectionMethod method : CollectionMethod.values()) { assertTrue(method + " failed", runMethodOnWrongThread(method, allJavaTypes)); } } private boolean runMethodOnWrongThread(final RealmCollectionMethod method) throws ExecutionException, InterruptedException { realm.beginTransaction(); ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<Boolean> future = executorService.submit(new Callable<Boolean>() { @Override public Boolean call() throws Exception { try { switch (method) { case WHERE: collection.where(); break; case MIN: collection.min(AllJavaTypes.FIELD_LONG); break; case MAX: collection.min(AllJavaTypes.FIELD_LONG); break; case SUM: collection.sum(AllJavaTypes.FIELD_LONG); break; case AVERAGE: collection.average(AllJavaTypes.FIELD_LONG); break; case MIN_DATE: collection.minDate(AllJavaTypes.FIELD_DATE); break; case MAX_DATE: collection.maxDate(AllJavaTypes.FIELD_DATE); break; case DELETE_ALL_FROM_REALM: collection.deleteAllFromRealm(); break; case IS_VALID: collection.isValid(); break; case IS_MANAGED: collection.isManaged(); return true; } return false; } catch (IllegalStateException ignored) { return true; } catch (UnsupportedOperationException ignored) { return (method == RealmCollectionMethod.WHERE && isSnapshot(collectionClass)); } } }); Boolean result = future.get(); realm.cancelTransaction(); return result; } private boolean runMethodOnWrongThread(final CollectionMethod method, final AllJavaTypes tempObject) throws ExecutionException, InterruptedException { realm.beginTransaction(); ExecutorService executorService = Executors.newSingleThreadExecutor(); Future<Boolean> future = executorService.submit(new Callable<Boolean>() { @Override public Boolean call() throws Exception { // Defines expected exception. Class<? extends Throwable> expected = IllegalStateException.class; if (collectionClass == ManagedCollection.REALMRESULTS || isSnapshot(collectionClass)) { switch (method) { case ADD_OBJECT: case ADD_ALL_OBJECTS: case CLEAR: case REMOVE_OBJECT: case REMOVE_ALL: case RETAIN_ALL: expected = UnsupportedOperationException.class; break; default: // use default exception } } try { switch (method) { case ADD_OBJECT: collection.add(new AllJavaTypes()); break; case ADD_ALL_OBJECTS: collection.addAll(Collections.singletonList(new AllJavaTypes())); break; case CLEAR: collection.clear(); break; case CONTAINS: collection.contains(tempObject); break; case CONTAINS_ALL: collection.containsAll(Collections.singletonList(tempObject)); break; case EQUALS: //noinspection ResultOfMethodCallIgnored collection.equals(createCollection(collectionClass)); break; case HASHCODE: //noinspection ResultOfMethodCallIgnored collection.hashCode(); break; case IS_EMPTY: collection.isEmpty(); break; case ITERATOR: return true; // Creating an iterator should be safe. Accessing it will fail, but tested elsewhere. case REMOVE_OBJECT: collection.remove(new AllJavaTypes()); break; case REMOVE_ALL: collection.removeAll(Collections.singletonList(new AllJavaTypes())); break; case RETAIN_ALL: collection.retainAll(Collections.singletonList(new AllJavaTypes())); break; case SIZE: collection.size(); break; case TO_ARRAY: collection.toArray(); break; case TO_ARRAY_INPUT: collection.toArray(new Object[collection.size()]); break; } return false; } catch (Throwable t) { if (!t.getClass().equals(expected)) { return false; } } return true; } }); Boolean result = future.get(); realm.cancelTransaction(); return result; } }