/* * Copyright (c) 2009, SQL Power Group Inc. * * This file is part of SQL Power Library. * * SQL Power Library is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * SQL Power Library 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package ca.sqlpower.object; import java.beans.PropertyChangeEvent; import java.util.ArrayList; import java.util.Collections; import java.util.List; import junit.framework.TestCase; import ca.sqlpower.util.TransactionEvent; import com.google.common.collect.ImmutableList; public class AbstractPoolingSPListenerTest extends TestCase { /** * This listener tracks the order events were handled. */ private static class ExecutionOrderListener extends AbstractPoolingSPListener { /** * A list of events that were handled in the order they were handled by * this listener. Used to test that events are handled in the correct order * when in a transaction block. */ private final List<Object> eventsInOrder = new ArrayList<Object>(); @Override protected void transactionStartedImpl(TransactionEvent e) { eventsInOrder.add(e); } @Override protected void transactionEndedImpl(TransactionEvent e) { eventsInOrder.add(e); } @Override protected void transactionRollbackImpl(TransactionEvent e) { eventsInOrder.add(e); } @Override protected void propertyChangeImpl(PropertyChangeEvent evt) { eventsInOrder.add(evt); } @Override protected void childAddedImpl(SPChildEvent e) { eventsInOrder.add(e); } @Override protected void childRemovedImpl(SPChildEvent e) { eventsInOrder.add(e); } public List<Object> getEventsInOrder() { return Collections.unmodifiableList(eventsInOrder); } } public static class StubSPObject extends AbstractSPObject { public static final List<Class<? extends SPObject>> allowedChildTypes = new ImmutableList.Builder<Class<? extends SPObject>>() .add(StubSPObject.class) .build(); private final List<StubSPObject> children = new ArrayList<StubSPObject>(); @Override protected boolean removeChildImpl(SPObject child) { return children.remove(child); } @Override protected void addChildImpl(SPObject child, int index) { children.add(index, (StubSPObject) child); } public boolean allowsChildren() { return true; } public int childPositionOffset( Class<? extends SPObject> childType) { return 0; } public List<? extends SPObject> getChildren() { return children; } public List<SPObject> getDependencies() { return Collections.emptyList(); } public void removeDependency(SPObject dependency) { //do nothing } public List<Class<? extends SPObject>> getAllowedChildTypes() { return allowedChildTypes; } }; private final AbstractSPObject wo = new StubSPObject(); /** * This tests that events fired. */ public void testTransactionActsOnEvents() throws Exception { ExecutionOrderListener listener = new ExecutionOrderListener(); wo.addSPListener(listener); SPChildEvent event1 = wo.fireChildAdded(SPObject.class, new StubSPObject(), 0); SPChildEvent event2 = wo.fireChildRemoved(SPObject.class, new StubSPObject(), 0); PropertyChangeEvent event3 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); List<Object> eventsInOrder = listener.getEventsInOrder(); assertEquals(event1, eventsInOrder.get(0)); assertEquals(event2, eventsInOrder.get(1)); assertEquals(event3, eventsInOrder.get(2)); } /** * This tests that events are fired while in a transaction are acted * on when the transaction completes successfully. */ public void testTransactionEndActsOnEvents() throws Exception { ExecutionOrderListener listener = new ExecutionOrderListener(); wo.addSPListener(listener); TransactionEvent event1 = wo.fireTransactionStarted("Start"); PropertyChangeEvent event2 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); SPChildEvent event3 = wo.fireChildAdded(SPObject.class, new StubSPObject(), 0); SPChildEvent event4 = wo.fireChildRemoved(SPObject.class, new StubSPObject(), 0); PropertyChangeEvent event5 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); assertEquals(1, listener.getEventsInOrder().size()); TransactionEvent event6 = wo.fireTransactionEnded(); List<Object> eventsInOrder = listener.getEventsInOrder(); assertEquals(event1, eventsInOrder.get(0)); assertEquals(event2, eventsInOrder.get(1)); assertEquals(event3, eventsInOrder.get(2)); assertEquals(event4, eventsInOrder.get(3)); assertEquals(event5, eventsInOrder.get(4)); assertEquals(event6, eventsInOrder.get(5)); } /** * This tests that events are fired while in a transaction are acted * on when the parent transaction completes successfully. */ public void testParentTransactionEndActsOnEvents() throws Exception { ExecutionOrderListener listener = new ExecutionOrderListener(); AbstractSPObject parent = new StubSPObject(); parent.addChild(wo); wo.addSPListener(listener); parent.addSPListener(listener); assertTrue(parent.getChildren().contains(wo)); TransactionEvent event1 = parent.fireTransactionStarted("Start"); PropertyChangeEvent event2 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); SPChildEvent event3 = wo.fireChildAdded(SPObject.class, new StubSPObject(), 0); SPChildEvent event4 = wo.fireChildRemoved(SPObject.class, new StubSPObject(), 0); PropertyChangeEvent event5 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); assertEquals("Only the start event should be acted on at this point", 1, listener.getEventsInOrder().size()); TransactionEvent event6 = parent.fireTransactionEnded(); assertEquals("All events should be in the list once the transaction has ended", 6, listener.getEventsInOrder().size()); List<Object> eventsInOrder = listener.getEventsInOrder(); assertEquals(event1, eventsInOrder.get(0)); assertEquals(event2, eventsInOrder.get(1)); assertEquals(event3, eventsInOrder.get(2)); assertEquals(event4, eventsInOrder.get(3)); assertEquals(event5, eventsInOrder.get(4)); assertEquals(event6, eventsInOrder.get(5)); } /** * This tests that events are fired while in a transaction are acted on when * the parent transaction completes successfully. This also interleaves * parent and child events so we know the result is the correct order as * input. */ public void testParentInterleavedTransactionEndActsOnEvents() throws Exception { ExecutionOrderListener listener = new ExecutionOrderListener(); AbstractSPObject parent = new StubSPObject(); parent.addChild(wo); wo.addSPListener(listener); parent.addSPListener(listener); assertTrue(parent.getChildren().contains(wo)); TransactionEvent event1 = parent.fireTransactionStarted("Start"); PropertyChangeEvent event2 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); PropertyChangeEvent event3 = parent.firePropertyChange("dummyProperty", "oldValue", "newValue"); SPChildEvent event4 = wo.fireChildAdded(SPObject.class, new StubSPObject(), 0); SPChildEvent event5 = wo.fireChildRemoved(SPObject.class, new StubSPObject(), 0); PropertyChangeEvent event6 = parent.firePropertyChange("dummyProperty", "oldValue", "newValue"); PropertyChangeEvent event7 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); PropertyChangeEvent event8 = parent.firePropertyChange("dummyProperty", "oldValue", "newValue"); assertEquals("Only the start event should be acted on at this point", 1, listener.getEventsInOrder().size()); TransactionEvent event9 = parent.fireTransactionEnded(); assertEquals("All events should be in the list once the transaction has ended", 9, listener.getEventsInOrder().size()); List<Object> eventsInOrder = listener.getEventsInOrder(); assertEquals(event1, eventsInOrder.get(0)); assertEquals(event2, eventsInOrder.get(1)); assertEquals(event3, eventsInOrder.get(2)); assertEquals(event4, eventsInOrder.get(3)); assertEquals(event5, eventsInOrder.get(4)); assertEquals(event6, eventsInOrder.get(5)); assertEquals(event7, eventsInOrder.get(6)); assertEquals(event8, eventsInOrder.get(7)); assertEquals(event9, eventsInOrder.get(8)); } /** * This tests that events are fired while in a transaction are acted on when * the ancestor transactions completes successfully.<br> * This test has both the parent of the event object and a grand parent * causing transactions. For this case the parent transaction wraps the * grandparent transaction. */ public void testParentGrandParentTransactionEndActsOnEvents() throws Exception { ExecutionOrderListener listener = new ExecutionOrderListener(); AbstractSPObject parent = new StubSPObject(); parent.addChild(wo); assertTrue(parent.getChildren().contains(wo)); AbstractSPObject grandParent = new StubSPObject(); grandParent.addChild(parent); assertTrue(grandParent.getChildren().contains(parent)); wo.addSPListener(listener); parent.addSPListener(listener); grandParent.addSPListener(listener); TransactionEvent event1 = parent.fireTransactionStarted("Start"); TransactionEvent event2 = grandParent.fireTransactionStarted("Start"); PropertyChangeEvent event3 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); SPChildEvent event4 = wo.fireChildAdded(SPObject.class, new StubSPObject(), 0); SPChildEvent event5 = wo.fireChildRemoved(SPObject.class, new StubSPObject(), 0); PropertyChangeEvent event6 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); assertEquals("Only the start event should be acted on at this point", 2, listener.getEventsInOrder().size()); TransactionEvent event7 = grandParent.fireTransactionEnded(); //We allow the grandparent transaction end here and all grandparent changes. If we wanted the grandparent changes interleaved //with the other changes we should have the grandparent transaction start first. assertEquals("Only the start event should be acted on at this point", 3, listener.getEventsInOrder().size()); TransactionEvent event8 = parent.fireTransactionEnded(); assertEquals("All events should be in the list once the transaction has ended", 8, listener.getEventsInOrder().size()); List<Object> eventsInOrder = listener.getEventsInOrder(); assertEquals(event1, eventsInOrder.get(0)); assertEquals(event2, eventsInOrder.get(1)); assertEquals(event7, eventsInOrder.get(2)); assertEquals(event3, eventsInOrder.get(3)); assertEquals(event4, eventsInOrder.get(4)); assertEquals(event5, eventsInOrder.get(5)); assertEquals(event6, eventsInOrder.get(6)); assertEquals(event8, eventsInOrder.get(7)); } /** * This tests that events are fired while in a transaction are acted on when * the ancestor transactions completes successfully.<br> * This test has both the parent of the event object and a grand parent * causing transactions. For this case the grand parent transaction wraps the * parent transaction. */ public void testGrandParentParentTransactionEndActsOnEvents() throws Exception { ExecutionOrderListener listener = new ExecutionOrderListener(); AbstractSPObject parent = new StubSPObject(); parent.addChild(wo); assertTrue(parent.getChildren().contains(wo)); AbstractSPObject grandParent = new StubSPObject(); grandParent.addChild(parent); assertTrue(grandParent.getChildren().contains(parent)); wo.addSPListener(listener); parent.addSPListener(listener); grandParent.addSPListener(listener); TransactionEvent event1 = grandParent.fireTransactionStarted("Start"); TransactionEvent event2 = parent.fireTransactionStarted("Start"); PropertyChangeEvent event3 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); SPChildEvent event4 = wo.fireChildAdded(SPObject.class, new StubSPObject(), 0); SPChildEvent event5 = wo.fireChildRemoved(SPObject.class, new StubSPObject(), 0); PropertyChangeEvent event6 = wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); assertEquals("Only the start event should be acted on at this point", 2, listener.getEventsInOrder().size()); TransactionEvent event7 = parent.fireTransactionEnded(); assertEquals("Only the start event should be acted on at this point", 2, listener.getEventsInOrder().size()); TransactionEvent event8 = grandParent.fireTransactionEnded(); assertEquals("All events should be in the list once the transaction has ended", 8, listener.getEventsInOrder().size()); List<Object> eventsInOrder = listener.getEventsInOrder(); assertEquals(event1, eventsInOrder.get(0)); assertEquals(event2, eventsInOrder.get(1)); assertEquals(event3, eventsInOrder.get(2)); assertEquals(event4, eventsInOrder.get(3)); assertEquals(event5, eventsInOrder.get(4)); assertEquals(event6, eventsInOrder.get(5)); assertEquals(event7, eventsInOrder.get(6)); assertEquals(event8, eventsInOrder.get(7)); } public void testTransactionRollbackDoesNotActOnEvents() throws Exception { ExecutionOrderListener listener = new ExecutionOrderListener(); wo.addSPListener(listener); TransactionEvent event1 = wo.fireTransactionStarted("Start"); wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); wo.fireChildAdded(SPObject.class, new StubSPObject(), 0); wo.fireChildRemoved(SPObject.class, new StubSPObject(), 0); wo.firePropertyChange("dummyProperty", "oldValue", "newValue"); assertEquals(1, listener.getEventsInOrder().size()); TransactionEvent event2 = wo.fireTransactionRollback("Rollback"); assertEquals(2, listener.getEventsInOrder().size()); assertEquals(event1, listener.getEventsInOrder().get(0)); assertEquals(event2, listener.getEventsInOrder().get(1)); } }