blob: f66de7ec1550874a6b9c23a8b6383f26a5d44ccb [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 Ovidio Mallo and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* Ovidio Mallo - initial API and implementation (bug 305367)
******************************************************************************/
package org.eclipse.core.tests.internal.databinding.observable.masterdetail;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import org.eclipse.core.databinding.beans.BeansObservables;
import org.eclipse.core.databinding.observable.IObservable;
import org.eclipse.core.databinding.observable.IObservableCollection;
import org.eclipse.core.databinding.observable.Realm;
import org.eclipse.core.databinding.observable.list.IObservableList;
import org.eclipse.core.databinding.observable.list.ListDiff;
import org.eclipse.core.databinding.observable.list.ListDiffEntry;
import org.eclipse.core.databinding.observable.list.WritableList;
import org.eclipse.core.databinding.observable.masterdetail.IObservableFactory;
import org.eclipse.core.databinding.observable.value.WritableValue;
import org.eclipse.core.internal.databinding.observable.masterdetail.ListDetailValueObservableList;
import org.eclipse.jface.databinding.conformance.ObservableListContractTest;
import org.eclipse.jface.databinding.conformance.delegate.AbstractObservableCollectionContractDelegate;
import org.eclipse.jface.databinding.conformance.util.ListChangeEventTracker;
import org.eclipse.jface.examples.databinding.model.SimplePerson;
import org.eclipse.jface.tests.databinding.AbstractDefaultRealmTestCase;
import org.junit.Test;
import junit.framework.TestSuite;
/**
* @since 1.3
*/
public class ListDetailValueObservableListTest extends
AbstractDefaultRealmTestCase {
public static void addConformanceTest(TestSuite suite) {
suite.addTest(ObservableListContractTest.suite(new Delegate()));
}
@Test
public void testUnmodifiability() {
WritableList masterObservableList = new WritableList();
masterObservableList.add(new SimplePerson());
masterObservableList.add(new SimplePerson());
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterObservableList, BeansObservables.valueFactory("name"),
null);
try {
ldol.add("name");
fail("ListDetailValueObservableList must not be modifiable.");
} catch (UnsupportedOperationException e) {
// expected exception
}
try {
ldol.remove(masterObservableList.get(0));
fail("ListDetailValueObservableList must not be modifiable.");
} catch (UnsupportedOperationException e) {
// expected exception
}
try {
ldol.removeAll(Collections.singleton(masterObservableList.get(0)));
fail("ListDetailValueObservableList must not be modifiable.");
} catch (UnsupportedOperationException e) {
// expected exception
}
try {
ldol.retainAll(Collections.EMPTY_LIST);
fail("ListDetailValueObservableList must not be modifiable.");
} catch (UnsupportedOperationException e) {
// expected exception
}
try {
ldol.move(0, 1);
fail("ListDetailValueObservableList must not be modifiable.");
} catch (UnsupportedOperationException e) {
// expected exception
}
}
@Test
public void testGetElementType() {
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
new WritableList(), BeansObservables.valueFactory("name"),
String.class);
assertSame(String.class, ldol.getElementType());
}
@Test
public void testGetObserved() {
WritableList masterList = new WritableList();
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterList, BeansObservables.valueFactory("name"), String.class);
// The observed object is the master list.
assertSame(masterList, ldol.getObserved());
}
@Test
public void testMasterListInitiallyNotEmpty() {
WritableList masterList = new WritableList();
SimplePerson person = new SimplePerson();
person.setName("name");
masterList.add(person);
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterList, BeansObservables.valueFactory("name"), String.class);
// Make sure that a non-empty master list is initialized correctly.
assertEquals(masterList.size(), ldol.size());
assertEquals(person.getName(), ldol.get(0));
}
@Test
public void testAddRemove() {
WritableList masterList = new WritableList();
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterList, BeansObservables.valueFactory("name"), String.class);
// Initially, the detail list is empty.
assertTrue(ldol.isEmpty());
// Add a first person and check that its name is in the detail list.
SimplePerson p1 = new SimplePerson();
p1.setName("name1");
masterList.add(p1);
assertEquals(masterList.size(), ldol.size());
assertEquals(p1.getName(), ldol.get(0));
// Add a second person and check that it's name is in the detail list.
SimplePerson p2 = new SimplePerson();
p2.setName("name2");
masterList.add(p2);
assertEquals(masterList.size(), ldol.size());
assertEquals(p2.getName(), ldol.get(1));
// Remove the first person from the master list and check that we still
// have the name of the second person in the detail list.
masterList.remove(0);
assertEquals(masterList.size(), ldol.size());
assertEquals(p2.getName(), ldol.get(0));
// Remove the second person as well.
masterList.remove(0);
assertTrue(ldol.isEmpty());
}
@Test
public void testChangeDetail() {
WritableList masterList = new WritableList();
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterList, BeansObservables.valueFactory("name"), String.class);
// Change the detail attribute explicitly.
SimplePerson p1 = new SimplePerson();
p1.setName("name1");
masterList.add(p1);
assertEquals(p1.getName(), ldol.get(0));
p1.setName("name2");
assertEquals(p1.getName(), ldol.get(0));
// Change the detail attribute by changing the master.
SimplePerson p2 = new SimplePerson();
p2.setName("name3");
masterList.set(0, p2);
assertEquals(p2.getName(), ldol.get(0));
}
@Test
public void testSet() {
WritableList masterList = new WritableList();
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterList, BeansObservables.valueFactory("name"), String.class);
// Change the detail attribute explicitly.
SimplePerson person = new SimplePerson();
person.setName("name1");
masterList.add(person);
assertEquals(person.getName(), ldol.get(0));
// Set a new name on the detail list.
ldol.set(0, "name2");
// Check that the name has been propagated to the master.
assertEquals("name2", person.getName());
assertEquals(person.getName(), ldol.get(0));
}
@Test
public void testDuplicateMasterElements() {
WritableList masterList = new WritableList();
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterList, BeansObservables.valueFactory("name"), String.class);
SimplePerson master = new SimplePerson();
master.setName("name1");
// Add the same master twice.
masterList.add(master);
masterList.add(master);
// Attach the change listener to the detail list.
ListChangeEventTracker changeTracker = ListChangeEventTracker
.observe(ldol);
// Setting the name on master should trigger an event on both
// occurrences of in the master list.
master.setName("name2");
// We should have 2 replace diffs, i.e. 4 diff entries.
assertEquals(1, changeTracker.count);
assertEquals(4, changeTracker.event.diff.getDifferences().length);
assertReplaceDiffAt(changeTracker.event.diff, 0, 0, "name1", "name2");
assertReplaceDiffAt(changeTracker.event.diff, 2, 0, "name1", "name2");
// Remove one instance of the master (one will remain).
masterList.remove(master);
// It should still be possible to work on the remaining master instance.
ldol.set(0, "name3");
assertEquals("name3", master.getName());
}
@Test
public void testDetailObservableChangeEvent() {
WritableList masterList = new WritableList();
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterList, BeansObservables.valueFactory("name"), String.class);
ListChangeEventTracker changeTracker = ListChangeEventTracker
.observe(ldol);
SimplePerson person = new SimplePerson();
person.setName("old name");
// Initially, we should not have received any event.
assertEquals(0, changeTracker.count);
// Add the person and check that we receive an addition event on the
// correct index and with the correct value.
masterList.add(person);
assertEquals(1, changeTracker.count);
assertEquals(1, changeTracker.event.diff.getDifferences().length);
assertTrue(changeTracker.event.diff.getDifferences()[0].isAddition());
assertEquals(0,
changeTracker.event.diff.getDifferences()[0].getPosition());
assertEquals(person.getName(),
changeTracker.event.diff.getDifferences()[0].getElement());
// Change the detail property and check that we receive a replace event.
person.setName("new name");
assertEquals(2, changeTracker.count);
assertIsSingleReplaceDiff(changeTracker.event.diff, 0, "old name",
"new name");
}
private void assertIsSingleReplaceDiff(ListDiff diff, int index,
Object oldElement, Object newElement) {
// We should have 2 diff entries.
assertEquals(2, diff.getDifferences().length);
// Check that it indeed is a replace diff.
assertReplaceDiffAt(diff, 0, index, oldElement, newElement);
}
private void assertReplaceDiffAt(ListDiff diff, int diffOffset, int index,
Object oldElement, Object newElement) {
ListDiffEntry entry1 = diff.getDifferences()[0];
ListDiffEntry entry2 = diff.getDifferences()[1];
// One diff entry must be an addition, the other a removal.
assertTrue(entry1.isAddition() != entry2.isAddition());
// Check for the index on the diff entries.
assertEquals(index, entry1.getPosition());
assertEquals(index, entry2.getPosition());
// Check for the old/new element values on both diff entries.
if (entry1.isAddition()) {
assertEquals(oldElement, entry2.getElement());
assertEquals(newElement, entry1.getElement());
} else {
assertEquals(oldElement, entry1.getElement());
assertEquals(newElement, entry2.getElement());
}
}
@Test
public void testMasterNull() {
WritableList masterObservableList = new WritableList();
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterObservableList, BeansObservables.valueFactory("name"),
String.class);
// Make sure null values are handled gracefully.
masterObservableList.add(null);
assertEquals(1, ldol.size());
assertNull(ldol.get(0));
}
@Test
public void testDetailObservableValuesAreDisposed() {
final List detailObservables = new ArrayList();
IObservableFactory detailValueFactory = new IObservableFactory() {
@Override
public IObservable createObservable(Object target) {
WritableValue detailObservable = new WritableValue();
// Remember the created observables.
detailObservables.add(detailObservable);
return detailObservable;
}
};
WritableList masterList = new WritableList();
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterList, detailValueFactory, null);
masterList.add(new Object());
masterList.add(new Object());
assertEquals(ldol.size(), detailObservables.size());
// No detail observables should be disposed yet.
assertFalse(((WritableValue) detailObservables.get(0)).isDisposed());
assertFalse(((WritableValue) detailObservables.get(1)).isDisposed());
// Only the detail observable for the removed master should be disposed.
masterList.remove(1);
assertFalse(((WritableValue) detailObservables.get(0)).isDisposed());
assertTrue(((WritableValue) detailObservables.get(1)).isDisposed());
// After disposing the detail list, all detail observables should be
// disposed.
ldol.dispose();
assertTrue(((WritableValue) detailObservables.get(0)).isDisposed());
assertTrue(((WritableValue) detailObservables.get(1)).isDisposed());
}
@Test
public void testDisposeOnMasterDisposed() {
WritableList masterList = new WritableList();
ListDetailValueObservableList ldol = new ListDetailValueObservableList(
masterList, BeansObservables.valueFactory("name"), String.class);
// Initially, nothing should be disposed.
assertFalse(masterList.isDisposed());
assertFalse(ldol.isDisposed());
// Upon disposing the master list, the detail list should be disposed as
// well.
masterList.dispose();
assertTrue(masterList.isDisposed());
assertTrue(ldol.isDisposed());
}
private static class Delegate extends
AbstractObservableCollectionContractDelegate {
@Override
public IObservableCollection createObservableCollection(Realm realm,
int elementCount) {
WritableList masterList = new WritableList(realm);
for (int i = 0; i < elementCount; i++) {
masterList.add(new SimplePerson());
}
return new TestListDetailValueObservableList(masterList,
BeansObservables.valueFactory(realm, "name"), String.class);
}
@Override
public void change(IObservable observable) {
TestListDetailValueObservableList ldol = (TestListDetailValueObservableList) observable;
ldol.masterList.add(new SimplePerson());
}
@Override
public Object getElementType(IObservableCollection collection) {
return String.class;
}
}
private static class TestListDetailValueObservableList extends
ListDetailValueObservableList {
final IObservableList masterList;
public TestListDetailValueObservableList(IObservableList masterList,
IObservableFactory detailValueFactory, Object detailType) {
super(masterList, detailValueFactory, detailType);
this.masterList = masterList;
}
}
}