/*
* This file is part of LibrePlan
*
* Copyright (C) 2009-2010 Fundación para o Fomento da Calidade Industrial e
* Desenvolvemento Tecnolóxico de Galicia
* Copyright (C) 2010-2011 Igalia, S.L.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.zkoss.ganttz.util;
import static org.hamcrest.CoreMatchers.equalTo;
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.assertThat;
import static org.junit.Assert.assertTrue;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.junit.Test;
import org.zkoss.ganttz.util.MutableTreeModel.IChildrenExtractor;
import org.zkoss.zul.TreeModel;
import org.zkoss.zul.event.TreeDataEvent;
/**
* @author Óscar González Fernández <ogonzalez@igalia.com>
*/
public class MutableTreeModelTest {
private static class Prueba {}
@Test
public void aMutableTreeModelIsAZkTreeModel() {
assertTrue(TreeModel.class.isAssignableFrom(MutableTreeModel.class));
}
@Test
public void aMutableTreeModelCanBeCreatedPassingType() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
assertNotNull(model);
assertNull(model.getRoot());
}
@Test
public void aMutableTreeModelCanBeCreatedPassingTypeAndRootObject() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
assertNotNull(model);
assertThat(model.getRoot(), equalTo(root));
}
@Test
public void childrenCanBeAdded() {
Prueba prueba = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, prueba);
Prueba other = new Prueba();
model.add(model.getRoot(), other);
Prueba otherChild = new Prueba();
model.addToRoot(otherChild);
assertThat(model.getChildCount(model.getRoot()), equalTo(2));
assertThat(model.getChild(model.getRoot(), 0), equalTo(other));
assertThat(model.getChild(model.getRoot(), 1), equalTo(otherChild));
}
@Test
public void testLeaf() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
Prueba other = new Prueba();
model.add(model.getRoot(), other);
assertTrue(model.isLeaf(other));
assertFalse(model.isLeaf(root));
}
@Test
public void childAddedCanBeFoundUsingGetPath() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
Prueba child = new Prueba();
model.add(root, child);
int[] path = model.getPath(model.getRoot(), child);
assertThat(path.length, equalTo(1));
assertThat(path[0], equalTo(0));
}
@Test
public void getPathReturnsEmptyArrayWhenParentNotFound() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
Prueba child = new Prueba();
model.add(root, child);
assertThat(model.getPath(null, child), equalTo(new int[0]));
}
@Test
public void getPathReturnsEmptyArrayWhenChildNotFound() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
Prueba child = new Prueba();
model.add(root, child);
assertThat(model.getPath(root, new Prueba()), equalTo(new int[0]));
}
@Test
public void ifThereisNotPathReturnEmptyArray() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
Prueba child = new Prueba();
model.add(root, child);
assertThat(model.getPath(child, root), equalTo(new int[0]));
}
@Test
public void hasMethodGetParentToMakeNavigationEasier() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
Prueba child = new Prueba();
model.add(root, child);
Prueba grandChild = new Prueba();
model.add(child, grandChild);
assertThat(model.getParent(grandChild), equalTo(child));
assertThat(model.getParent(child), equalTo(root));
}
@Test
public void hasMethodGetAllParentsUntilRoot() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
Prueba child = new Prueba();
model.add(root, child);
Prueba grandChild = new Prueba();
model.add(child, grandChild);
List<Prueba> parents = model.getParents(grandChild);
assertThat(parents.size(), equalTo(2));
assertThat(parents, equalTo(Arrays.asList(child, root)));
}
@Test(expected = IllegalArgumentException.class)
public void findObjectAtInvalidPath() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
model.findObjectAt(0, 1);
}
@Test(expected = IllegalArgumentException.class)
public void findObjectAtInvalidPathInTheMiddle() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
Prueba p1 = new Prueba();
model.addToRoot(p1);
model.addToRoot(new Prueba());
model.add(p1, new Prueba());
model.findObjectAt(1, 1);
}
@Test
public void findAtPathCanReceiveGetPathResult() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
assertTrue(canBeRetrievedWithGetPath(model, model.getRoot()));
model.addToRoot(new Prueba());
Prueba p2 = new Prueba();
model.addToRoot(p2);
assertTrue(canBeRetrievedWithGetPath(model, p2));
Prueba grandChild = new Prueba();
model.add(p2, grandChild);
assertTrue(canBeRetrievedWithGetPath(model, grandChild));
}
private static <T> boolean canBeRetrievedWithGetPath(final MutableTreeModel<T> tree, T object) {
int[] path = tree.getPath(object);
return tree.findObjectAt(path).equals(object);
}
@Test(expected = IllegalArgumentException.class)
public void getParentOfRootThrowsException() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
model.getParent(root);
}
@Test
public void addingTriggersEvent() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
final ArrayList<TreeDataEvent> eventsFired = new ArrayList<>();
model.addTreeDataListener(event -> eventsFired.add(event));
Prueba child1 = new Prueba();
Prueba child2 = new Prueba();
Prueba granChildren1 = new Prueba();
model.add(model.getRoot(), child1);
checkIsValid(getLast(eventsFired), TreeDataEvent.INTERVAL_ADDED, model.getRoot(), 0);
model.add(model.getRoot(), child2);
checkIsValid(getLast(eventsFired), TreeDataEvent.INTERVAL_ADDED, model.getRoot(), 1);
model.add(child1, granChildren1);
checkIsValid(getLast(eventsFired), TreeDataEvent.INTERVAL_ADDED, model.getParent(child1), 0);
assertThat(eventsFired.size(), equalTo(3));
}
@Test
public void canAddSeveral() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
final ArrayList<TreeDataEvent> eventsFired = new ArrayList<>();
model.addTreeDataListener(event -> eventsFired.add(event));
Prueba child1 = new Prueba();
Prueba child2 = new Prueba();
model.add(model.getRoot(), Arrays.asList(child1, child2));
assertThat(eventsFired.size(), equalTo(1));
TreeDataEvent event = getLast(eventsFired);
checkIsValid(event, TreeDataEvent.INTERVAL_ADDED, model.getRoot(), 0, 1);
}
@Test
public void canAddSeveralAtPosition() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba p1 = new Prueba();
model.add(model.getRoot(), p1);
Prueba p2 = new Prueba();
Prueba p3 = new Prueba();
model.add(model.getRoot(), 0, Arrays.asList(p2, p3));
assertThat(model.getChild(model.getRoot(), 0), equalTo(p2));
assertThat(model.getChild(model.getRoot(), 1), equalTo(p3));
assertThat(model.getChild(model.getRoot(), 2), equalTo(p1));
}
@Test
public void canAddSeveralAtPositionSendEventsWithCorrectValue() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba p1 = new Prueba();
model.add(model.getRoot(), p1);
final ArrayList<TreeDataEvent> eventsFired = new ArrayList<>();
model.addTreeDataListener(event -> eventsFired.add(event));
model.add(model.getRoot(), 0, Arrays.asList(new Prueba(), new Prueba()));
TreeDataEvent event = getLast(eventsFired);
checkIsValid(event, TreeDataEvent.INTERVAL_ADDED, model.getRoot(), 0, 1);
}
@Test
public void addingSeveralGroupsEvents() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba child1 = new Prueba();
Prueba child2 = new Prueba();
model.add(model.getRoot(), Arrays.asList(child1, child2));
assertThat(model.getChildCount(model.getRoot()), equalTo(2));
}
@Test
public void addingAnEmptyListOfElementsDontDoNothing() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
final List<TreeDataEvent> events = new ArrayList<>();
model.addTreeDataListener(event -> events.add(event));
model.add(model.getRoot(), new ArrayList<>());
assertThat(events.size(), equalTo(0));
}
@Test
public void childrenCanBeAddedAutomaticallyIfAChildrenExtractorIsProvided() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
final Prueba newlyAdded = new Prueba();
final Prueba child1 = new Prueba();
final Prueba child2 = new Prueba();
model.add(model.getRoot(), Collections.singletonList(newlyAdded), childrenFor(newlyAdded, child1, child2));
assertThat(model.getChild(model.getRoot(), 0), equalTo(newlyAdded));
assertThat(model.getChildCount(newlyAdded), equalTo(2));
}
private IChildrenExtractor<Prueba> childrenFor(final Prueba parent, final Prueba... children) {
return p -> parent == p ? Arrays.asList(children) : Collections.emptyList();
}
@Test
public void whenChildrenAreAutomaticallyAddedOnlySendsEventForTopInsertion() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
final Prueba newlyAdded = new Prueba();
final Prueba child1 = new Prueba();
final Prueba child2 = new Prueba();
final List<TreeDataEvent> eventsFired = new ArrayList<>();
model.addTreeDataListener(event -> eventsFired.add(event));
model.add(model.getRoot(), Collections.singletonList(newlyAdded), childrenFor(newlyAdded, child1, child2));
assertThat(eventsFired.size(), equalTo(1));
}
@Test
public void aNodeCanBeRemoved() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba prueba1 = new Prueba();
model.add(model.getRoot(), prueba1);
model.remove(prueba1);
assertThat(model.getChildCount(model.getRoot()), equalTo(0));
}
@Test(expected = IllegalArgumentException.class)
public void theRootNodeCannotBeRemoved() {
Prueba root = new Prueba();
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class, root);
model.remove(root);
}
@Test
public void removingANodeWithChildrenRemovesTheChildren() {
MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba parent = new Prueba();
model.add(model.getRoot(), parent);
Prueba grandson = new Prueba();
model.add(parent, grandson);
model.remove(parent);
assertThat(model.getPath(parent, grandson).length, equalTo(0));
}
@Test
public void removingANodeTriggersEvent() {
final MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
final List<TreeDataEvent> removeEventsFired = new ArrayList<>();
model.addTreeDataListener(event -> {
if ( event.getType() == TreeDataEvent.INTERVAL_REMOVED ) {
removeEventsFired.add(event);
}
});
Prueba prueba1 = new Prueba();
Prueba prueba2 = new Prueba();
Prueba grandChild = new Prueba();
model.add(model.getRoot(), prueba1);
model.add(model.getRoot(), prueba2);
model.add(prueba1, grandChild);
model.remove(grandChild);
assertThat(removeEventsFired.size(), equalTo(1));
checkIsValid(getLast(removeEventsFired), TreeDataEvent.INTERVAL_REMOVED, model.getParent(prueba1), 0);
model.remove(prueba2);
assertThat(getLast(removeEventsFired).getModel().getRoot(), equalTo((Object) model.getRoot()));
checkIsValid(getLast(removeEventsFired), TreeDataEvent.INTERVAL_REMOVED, model.getRoot(), 1);
model.remove(prueba1);
assertThat(removeEventsFired.size(), equalTo(3));
checkIsValid(getLast(removeEventsFired), TreeDataEvent.INTERVAL_REMOVED, model.getRoot(), 0);
}
@Test
public void aNodeCanBeReplacedByOther() {
final MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba toRemove = new Prueba();
Prueba prueba2 = new Prueba();
Prueba grandChild = new Prueba();
model.add(model.getRoot(), toRemove);
model.add(model.getRoot(), prueba2);
model.add(toRemove, grandChild);
Prueba substitution = new Prueba();
model.replace(toRemove, substitution);
assertThat(model.getChildCount(substitution), equalTo(0));
assertThat(model.getChild(model.getRoot(), 0), equalTo(substitution));
}
@Test
public void aNodeCanBeMovedDown() {
final MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba prueba1 = new Prueba();
model.addToRoot(prueba1);
Prueba prueba2 = new Prueba();
model.addToRoot(prueba2);
Prueba prueba3 = new Prueba();
model.addToRoot(prueba3);
model.down(prueba1);
assertThat(model.getChild(model.getRoot(), 0), equalTo(prueba2));
assertThat(model.getChild(model.getRoot(), 1), equalTo(prueba1));
assertThat(model.getChild(model.getRoot(), 2), equalTo(prueba3));
}
@Test
public void aNodeCanBeMovedUp() {
final MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba prueba1 = new Prueba();
model.addToRoot(prueba1);
Prueba prueba2 = new Prueba();
model.addToRoot(prueba2);
Prueba prueba3 = new Prueba();
model.addToRoot(prueba3);
model.up(prueba2);
assertThat(model.getChild(model.getRoot(), 0), equalTo(prueba2));
assertThat(model.getChild(model.getRoot(), 1), equalTo(prueba1));
assertThat(model.getChild(model.getRoot(), 2), equalTo(prueba3));
}
@Test
public void IfItIsAtTheTopUpDoesNothing() {
final MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba prueba1 = new Prueba();
model.addToRoot(prueba1);
Prueba prueba2 = new Prueba();
model.addToRoot(prueba2);
Prueba prueba3 = new Prueba();
model.addToRoot(prueba3);
model.up(prueba1);
assertThat(model.getChild(model.getRoot(), 0), equalTo(prueba1));
assertThat(model.getChild(model.getRoot(), 1), equalTo(prueba2));
assertThat(model.getChild(model.getRoot(), 2), equalTo(prueba3));
}
@Test
public void movingUpAndDownSendsRemovalAndAddingEventsSoZKReloadsCorrectlyTheData() {
final MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba prueba1 = new Prueba();
model.addToRoot(prueba1);
Prueba prueba2 = new Prueba();
model.addToRoot(prueba2);
Prueba prueba3 = new Prueba();
model.addToRoot(prueba3);
final ArrayList<TreeDataEvent> eventsFired = new ArrayList<>();
model.addTreeDataListener(event -> eventsFired.add(event));
model.up(prueba2);
checkIsValid(getPreviousToLast(eventsFired), TreeDataEvent.INTERVAL_REMOVED, model.getRoot(), 0, 1);
checkIsValid(getLast(eventsFired), TreeDataEvent.INTERVAL_ADDED, model.getRoot(), 0, 1);
model.down(prueba1);
checkIsValid(getPreviousToLast(eventsFired), TreeDataEvent.INTERVAL_REMOVED, model.getRoot(), 1, 2);
checkIsValid(getLast(eventsFired), TreeDataEvent.INTERVAL_ADDED, model.getRoot(), 1, 2);
}
@Test
public void ifItIsAtTheBottomDownDoesNothing() {
final MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba prueba1 = new Prueba();
model.addToRoot(prueba1);
Prueba prueba2 = new Prueba();
model.addToRoot(prueba2);
model.down(prueba2);
assertThat(model.getChild(model.getRoot(), 1), equalTo(prueba2));
}
@Test
public void canBeKnownIfAnEntityIsOnTheTree() {
final MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba prueba1 = new Prueba();
model.addToRoot(prueba1);
assertTrue(model.contains(prueba1));
assertTrue(model.contains(model.getRoot()));
assertFalse(model.contains(new Prueba()));
}
@Test
public void treeParentContainsChild() {
final MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba parent = new Prueba();
model.addToRoot(parent);
Prueba child = new Prueba();
model.add(parent, child);
assertTrue(model.contains(parent, child));
}
@Test
public void treeParentDoesNotContainChild() {
final MutableTreeModel<Prueba> model = MutableTreeModel.create(Prueba.class);
Prueba parent = new Prueba();
model.addToRoot(parent);
Prueba child = new Prueba();
assertFalse(model.contains(parent, child));
}
private void checkIsValid(TreeDataEvent event, int type, Prueba expectedParent, int expectedPosition) {
checkIsValid(event, type, expectedParent, expectedPosition, expectedPosition);
}
private void checkIsValid(TreeDataEvent event, int type, int[] expectedPath, int expectedPosition) {
checkIsValid(event, type, expectedPath, expectedPosition, expectedPosition);
}
private void checkIsValid(TreeDataEvent event,
int type,
Prueba expectedParent,
int expectedFromPosition,
int expectedToPosition) {
assertEquals(event.getModel().getRoot(), expectedParent);
assertThat(event.getIndexFrom(), equalTo(expectedFromPosition));
assertThat(event.getIndexTo(), equalTo(expectedToPosition));
assertThat(event.getType(), equalTo(type));
}
private void checkIsValid(TreeDataEvent event,
int type,
int[] expectedPath,
int expectedFromPosition,
int expectedToPosition) {
assertEquals(event.getPath(), expectedPath);
assertThat(event.getIndexFrom(), equalTo(expectedFromPosition));
assertThat(event.getIndexTo(), equalTo(expectedToPosition));
assertThat(event.getType(), equalTo(type));
}
private TreeDataEvent getPreviousToLast(List<TreeDataEvent> list) {
return list.get(list.size() - 2);
}
private TreeDataEvent getLast(List<TreeDataEvent> list) {
if ( list.isEmpty() ) {
throw new RuntimeException("no events");
}
return list.get(list.size() - 1);
}
}