| /******************************************************************************* |
| * Copyright (c) 2009, 2012 Oracle. 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: |
| * Oracle - initial API and implementation |
| ******************************************************************************/ |
| package org.eclipse.jpt.common.utility.tests.internal.command; |
| |
| import org.eclipse.jpt.common.utility.ExceptionHandler; |
| import org.eclipse.jpt.common.utility.command.Command; |
| import org.eclipse.jpt.common.utility.command.RepeatingCommand; |
| import org.eclipse.jpt.common.utility.internal.CollectingExceptionHandler; |
| import org.eclipse.jpt.common.utility.internal.CollectionTools; |
| import org.eclipse.jpt.common.utility.internal.ConsumerThreadCoordinator; |
| import org.eclipse.jpt.common.utility.internal.ReflectionTools; |
| import org.eclipse.jpt.common.utility.internal.command.AsynchronousRepeatingCommandWrapper; |
| import org.eclipse.jpt.common.utility.tests.internal.MultiThreadedTestCase; |
| import org.eclipse.jpt.common.utility.tests.internal.TestTools; |
| |
| @SuppressWarnings("nls") |
| public class AsynchronousRepeatingCommandWrapperTests |
| extends MultiThreadedTestCase |
| { |
| PrimaryModel1 primaryModel1; |
| SecondaryModel1 secondaryModel1; |
| Command command1; |
| RepeatingCommand repeatingCommand1; |
| |
| PrimaryModel2 primaryModel2; |
| SecondaryModel2 secondaryModel2; |
| Command command2; |
| RepeatingCommand repeatingCommand2; |
| |
| public AsynchronousRepeatingCommandWrapperTests(String name) { |
| super(name); |
| } |
| |
| @Override |
| protected void setUp() throws Exception { |
| super.setUp(); |
| this.primaryModel1 = new PrimaryModel1(); |
| this.secondaryModel1 = new SecondaryModel1(this.primaryModel1); |
| this.command1 = new SynchronizeSecondaryModelCommand1(this.secondaryModel1); |
| this.repeatingCommand1 = new AsynchronousRepeatingCommandWrapper(this.command1, this.buildThreadFactory(), null, ExceptionHandler.Runtime.instance()); |
| this.primaryModel1.setSynchronizer(this.repeatingCommand1); |
| |
| this.primaryModel2 = new PrimaryModel2(); |
| this.secondaryModel2 = new SecondaryModel2(this.primaryModel2); |
| this.command2 = new SynchronizeSecondaryModelCommand2(this.primaryModel2, this.secondaryModel2); |
| this.repeatingCommand2 = new AsynchronousRepeatingCommandWrapper(this.command2, this.buildThreadFactory(), null, ExceptionHandler.Runtime.instance()); |
| this.primaryModel2.setSynchronizer(this.repeatingCommand2); |
| } |
| |
| @Override |
| protected void tearDown() throws Exception { |
| this.repeatingCommand1.stop(); |
| this.repeatingCommand2.stop(); |
| super.tearDown(); |
| } |
| |
| public void testInitialization() { |
| assertEquals(4, this.secondaryModel1.getDoubleCount()); |
| } |
| |
| public void testToString() { |
| assertNotNull(this.repeatingCommand1.toString()); |
| } |
| |
| public void testChange() throws Exception { |
| assertEquals(4, this.secondaryModel1.getDoubleCount()); |
| this.primaryModel1.setCount(7); |
| |
| this.sleep(TICK); |
| this.repeatingCommand1.stop(); |
| this.sleep(TICK); |
| |
| assertEquals(14, this.secondaryModel1.getDoubleCount()); |
| |
| // re-start so tear-down works |
| this.repeatingCommand1.start(); |
| } |
| |
| public void testStart() throws Exception { |
| assertEquals(4, this.secondaryModel1.getDoubleCount()); |
| this.primaryModel1.setSynchronizer(RepeatingCommand.Null.instance()); |
| this.primaryModel1.setCount(7); |
| assertEquals(4, this.secondaryModel1.getDoubleCount()); |
| this.primaryModel1.setSynchronizer(this.repeatingCommand1); |
| // the async synchronizer does not synchronize at start-up |
| assertEquals(4, this.secondaryModel1.getDoubleCount()); |
| |
| this.primaryModel1.setCount(8); |
| |
| this.sleep(TICK); |
| this.repeatingCommand1.stop(); |
| this.sleep(TICK); |
| |
| assertEquals(16, this.secondaryModel1.getDoubleCount()); |
| |
| // re-start so tear-down works |
| this.repeatingCommand1.start(); |
| } |
| |
| public void testStop() throws Exception { |
| assertEquals(4, this.secondaryModel1.getDoubleCount()); |
| this.primaryModel1.dispose(); |
| this.primaryModel1.setCount(7); |
| assertEquals(4, this.secondaryModel1.getDoubleCount()); |
| |
| // re-start so tear-down works |
| this.repeatingCommand1.start(); |
| } |
| |
| public void testDoubleStart() throws Exception { |
| assertEquals(4, this.secondaryModel1.getDoubleCount()); |
| boolean exCaught = false; |
| try { |
| this.primaryModel1.startSynchronizer(); |
| fail(); |
| } catch (IllegalStateException ex) { |
| exCaught = true; |
| } |
| assertTrue(exCaught); |
| this.primaryModel1.setCount(7); |
| |
| this.sleep(TICK); |
| this.repeatingCommand1.stop(); |
| this.sleep(TICK); |
| |
| assertEquals(14, this.secondaryModel1.getDoubleCount()); |
| |
| // re-start so tear-down works |
| this.repeatingCommand1.start(); |
| } |
| |
| public void testDoubleStop() throws Exception { |
| assertEquals(4, this.secondaryModel1.getDoubleCount()); |
| this.primaryModel1.dispose(); |
| boolean exCaught = false; |
| try { |
| this.primaryModel1.dispose(); |
| fail(); |
| } catch (IllegalStateException ex) { |
| exCaught = true; |
| } |
| assertTrue(exCaught); |
| this.primaryModel1.setCount(7); |
| assertEquals(4, this.secondaryModel1.getDoubleCount()); |
| |
| // re-start so tear-down works |
| this.repeatingCommand1.start(); |
| } |
| |
| public void testRecursiveChange() throws Exception { |
| assertEquals(4, this.secondaryModel2.getDoubleCount()); |
| this.primaryModel2.setCount(7); |
| |
| this.sleep(TICK); |
| assertEquals(10, this.primaryModel2.getCountPlus3()); |
| assertEquals(14, this.secondaryModel2.getDoubleCount()); |
| |
| this.sleep(TICK); |
| assertEquals(20, this.secondaryModel2.getDoubleCountPlus3()); |
| } |
| |
| public void testNullCommand() { |
| boolean exCaught = false; |
| try { |
| RepeatingCommand s = new AsynchronousRepeatingCommandWrapper(null, this.buildThreadFactory(), null, ExceptionHandler.Runtime.instance()); |
| fail("bogus: " + s); |
| } catch (NullPointerException ex) { |
| exCaught = true; |
| } |
| assertTrue(exCaught); |
| } |
| |
| public void testThreadName() throws Exception { |
| RepeatingCommand s = new AsynchronousRepeatingCommandWrapper(this.command1, this.buildThreadFactory(), "sync", ExceptionHandler.Runtime.instance()); |
| s.start(); |
| ConsumerThreadCoordinator ctc = (ConsumerThreadCoordinator) ReflectionTools.getFieldValue(s, "consumerThreadCoordinator"); |
| Thread t = (Thread) ReflectionTools.getFieldValue(ctc, "thread"); |
| assertEquals("sync", t.getName()); |
| s.stop(); |
| } |
| |
| public void testExecuteCalledBeforeStart() throws Exception { |
| SimpleCommand command = new SimpleCommand(); |
| RepeatingCommand synchronizer = new AsynchronousRepeatingCommandWrapper(command, this.buildThreadFactory(), null, ExceptionHandler.Runtime.instance()); |
| |
| synchronizer.execute(); |
| synchronizer.start(); |
| this.sleep(TICK); |
| synchronizer.stop(); |
| assertEquals(1, command.count); |
| } |
| |
| public class SimpleCommand implements Command { |
| int count = 0; |
| public void execute() { |
| this.count++; |
| } |
| } |
| |
| public void testException() throws Exception { |
| BogusCommand command = new BogusCommand(); |
| CollectingExceptionHandler exHandler = new CollectingExceptionHandler(); |
| RepeatingCommand synchronizer = new AsynchronousRepeatingCommandWrapper(command, this.buildThreadFactory(), null, exHandler); |
| synchronizer.start(); |
| |
| synchronizer.execute(); |
| this.sleep(TICK); |
| |
| synchronizer.execute(); |
| this.sleep(TICK); |
| |
| synchronizer.stop(); |
| assertEquals(2, CollectionTools.size(exHandler.getExceptions())); |
| assertEquals(2, command.count); |
| } |
| |
| public class BogusCommand implements Command { |
| int count = 0; |
| public void execute() { |
| this.count++; |
| throw new NullPointerException(); |
| } |
| } |
| |
| /** |
| * Make sure the <code>DEBUG</code> constant is <code>false</code> |
| * before checking in the code. |
| */ |
| public void testDEBUG() { |
| TestTools.assertFalseDEBUG(AsynchronousRepeatingCommandWrapper.class); |
| } |
| |
| |
| // ********** synchronize commands ********** |
| |
| public static class SynchronizeSecondaryModelCommand1 |
| implements Command |
| { |
| private final SecondaryModel1 secondaryModel; |
| |
| public SynchronizeSecondaryModelCommand1(SecondaryModel1 secondaryModel) { |
| super(); |
| this.secondaryModel = secondaryModel; |
| } |
| |
| public void execute() { |
| this.secondaryModel.synchronize(); |
| } |
| } |
| |
| /** |
| * the primary model (subclass) has to synchronize with itself (superclass) |
| */ |
| public static class SynchronizeSecondaryModelCommand2 |
| extends SynchronizeSecondaryModelCommand1 |
| { |
| private final PrimaryModel2 primaryModel; |
| |
| public SynchronizeSecondaryModelCommand2(PrimaryModel2 primaryModel, SecondaryModel2 secondaryModel) { |
| super(secondaryModel); |
| this.primaryModel = primaryModel; |
| } |
| |
| @Override |
| public void execute() { |
| super.execute(); |
| this.primaryModel.synchronize(); |
| } |
| } |
| |
| |
| // ********** primary models ********** |
| |
| /** |
| * this object will call the synchronizer whenever its count changes, |
| * allowing interested parties to synchronize with the change |
| */ |
| public static class PrimaryModel1 { |
| protected RepeatingCommand synchronizer; |
| protected int count = 2; |
| |
| public PrimaryModel1() { |
| super(); |
| this.setSynchronizer_(RepeatingCommand.Null.instance()); |
| } |
| |
| public int getCount() { |
| return this.count; |
| } |
| public void setCount(int count) { |
| if (count != this.count) { |
| this.count = count; |
| this.stateChanged(); |
| } |
| } |
| |
| protected void stateChanged() { |
| this.synchronizer.execute(); |
| } |
| |
| public void setSynchronizer(RepeatingCommand synchronizer) throws InterruptedException { |
| if (synchronizer == null) { |
| throw new NullPointerException(); |
| } |
| this.synchronizer.stop(); |
| this.setSynchronizer_(synchronizer); |
| } |
| |
| protected void setSynchronizer_(RepeatingCommand synchronizer) { |
| this.synchronizer = synchronizer; |
| this.synchronizer.start(); |
| } |
| |
| public void startSynchronizer() { |
| this.synchronizer.start(); // this should cause an exception |
| } |
| public void dispose() throws InterruptedException { |
| this.synchronizer.stop(); |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(this.getClass().getSimpleName()); |
| sb.append('('); |
| this.toString(sb); |
| sb.append(')'); |
| return sb.toString(); |
| } |
| public void toString(StringBuilder sb) { |
| sb.append("count="); |
| sb.append(this.count); |
| } |
| } |
| |
| /** |
| * This model synchronizes with itself, triggering a recursive synchronization |
| * with the change. Whenever the [inherited] 'count' changes, 'countPlus3' |
| * is updated appropriately and another synchronize is initiated if appropriate. |
| */ |
| public static class PrimaryModel2 |
| extends PrimaryModel1 |
| { |
| private int countPlus3 = 0; |
| |
| public PrimaryModel2() { |
| super(); |
| this.countPlus3 = this.count + 3; |
| } |
| |
| public int getCountPlus3() { |
| return this.countPlus3; |
| } |
| protected void setCountPlus3(int countPlus3) { |
| if (countPlus3 != this.countPlus3) { |
| this.countPlus3 = countPlus3; |
| this.stateChanged(); |
| } |
| } |
| |
| // synchronize with itself, so to speak |
| public void synchronize() { |
| this.setCountPlus3(this.count + 3); |
| } |
| |
| @Override |
| public void toString(StringBuilder sb) { |
| super.toString(sb); |
| sb.append(", countPlus3="); |
| sb.append(this.countPlus3); |
| } |
| } |
| |
| |
| // ********** secondary models ********** |
| |
| /** |
| * This dependent object updates its 'doubleCount' whenever the |
| * PrimaryModel1's 'count' changes, via the 'synchronizer'. |
| */ |
| public static class SecondaryModel1 { |
| protected final PrimaryModel1 primaryModel; |
| protected int doubleCount = 0; |
| |
| public SecondaryModel1(PrimaryModel1 primaryModel) { |
| super(); |
| this.primaryModel = primaryModel; |
| this.synchronize(); |
| } |
| |
| public void synchronize() { |
| this.doubleCount = this.primaryModel.getCount() * 2; |
| } |
| |
| public int getDoubleCount() { |
| return this.doubleCount; |
| } |
| |
| @Override |
| public String toString() { |
| StringBuilder sb = new StringBuilder(); |
| sb.append(this.getClass().getSimpleName()); |
| sb.append('('); |
| this.toString(sb); |
| sb.append(')'); |
| return sb.toString(); |
| } |
| public void toString(StringBuilder sb) { |
| sb.append("doubleCount="); |
| sb.append(this.doubleCount); |
| } |
| } |
| |
| public static class SecondaryModel2 |
| extends SecondaryModel1 |
| { |
| private int doubleCountPlus3 = 0; |
| |
| public SecondaryModel2(PrimaryModel2 extendedPrimaryModel) { |
| super(extendedPrimaryModel); |
| } |
| |
| protected PrimaryModel2 getExtendedPrimaryModel() { |
| return (PrimaryModel2) this.primaryModel; |
| } |
| |
| @Override |
| public void synchronize() { |
| super.synchronize(); |
| int temp = this.getExtendedPrimaryModel().getCountPlus3() * 2; |
| if (this.doubleCountPlus3 != temp) { |
| this.doubleCountPlus3 = temp; |
| } |
| } |
| |
| public int getDoubleCountPlus3() { |
| return this.doubleCountPlus3; |
| } |
| |
| @Override |
| public void toString(StringBuilder sb) { |
| super.toString(sb); |
| sb.append(", doubleCountPlus3="); |
| sb.append(this.doubleCountPlus3); |
| } |
| } |
| } |