diff --git a/twin/pom.xml b/twin/pom.xml index 95e942493..7f764f53d 100644 --- a/twin/pom.xml +++ b/twin/pom.xml @@ -14,5 +14,10 @@ junit test + + org.mockito + mockito-core + test + diff --git a/twin/src/main/java/com/iluwatar/twin/App.java b/twin/src/main/java/com/iluwatar/twin/App.java index eaa21a849..cb971c490 100644 --- a/twin/src/main/java/com/iluwatar/twin/App.java +++ b/twin/src/main/java/com/iluwatar/twin/App.java @@ -41,6 +41,6 @@ public class App { } private static void waiting() throws Exception { - Thread.sleep(2500); + Thread.sleep(750); } } diff --git a/twin/src/main/java/com/iluwatar/twin/BallThread.java b/twin/src/main/java/com/iluwatar/twin/BallThread.java index e165782a5..2d9e7c41a 100644 --- a/twin/src/main/java/com/iluwatar/twin/BallThread.java +++ b/twin/src/main/java/com/iluwatar/twin/BallThread.java @@ -25,15 +25,14 @@ public class BallThread extends Thread { public void run() { while (isRunning) { - while (!isSuspended) { + if (!isSuspended) { twin.draw(); twin.move(); - try { - Thread.sleep(1000); - } catch (InterruptedException e) { - throw new RuntimeException(e); - } - + } + try { + Thread.sleep(250); + } catch (InterruptedException e) { + throw new RuntimeException(e); } } } diff --git a/twin/src/test/java/com/iluwatar/twin/BallItemTest.java b/twin/src/test/java/com/iluwatar/twin/BallItemTest.java new file mode 100644 index 000000000..ca1da7ac8 --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/BallItemTest.java @@ -0,0 +1,62 @@ +package com.iluwatar.twin; + +import org.junit.Test; +import org.mockito.InOrder; + +import static org.mockito.Mockito.inOrder; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; + +/** + * Date: 12/30/15 - 18:44 PM + * + * @author Jeroen Meulemeester + */ +public class BallItemTest extends StdOutTest { + + @Test + public void testClick() { + final BallThread ballThread = mock(BallThread.class); + final BallItem ballItem = new BallItem(); + ballItem.setTwin(ballThread); + + final InOrder inOrder = inOrder(ballThread); + + for (int i = 0; i < 10; i++) { + ballItem.click(); + inOrder.verify(ballThread).suspendMe(); + + ballItem.click(); + inOrder.verify(ballThread).resumeMe(); + } + + inOrder.verifyNoMoreInteractions(); + } + + @Test + public void testDoDraw() { + final BallItem ballItem = new BallItem(); + final BallThread ballThread = mock(BallThread.class); + ballItem.setTwin(ballThread); + + ballItem.draw(); + verify(getStdOutMock()).println("draw"); + verify(getStdOutMock()).println("doDraw"); + + verifyNoMoreInteractions(ballThread, getStdOutMock()); + } + + @Test + public void testMove() { + final BallItem ballItem = new BallItem(); + final BallThread ballThread = mock(BallThread.class); + ballItem.setTwin(ballThread); + + ballItem.move(); + verify(getStdOutMock()).println("move"); + + verifyNoMoreInteractions(ballThread, getStdOutMock()); + } + +} \ No newline at end of file diff --git a/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java b/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java new file mode 100644 index 000000000..7e0bdc11e --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/BallThreadTest.java @@ -0,0 +1,89 @@ +package com.iluwatar.twin; + +import org.junit.Test; + +import static java.lang.Thread.UncaughtExceptionHandler; +import static java.lang.Thread.sleep; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.timeout; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.verifyNoMoreInteractions; +import static org.mockito.Mockito.verifyZeroInteractions; + +/** + * Date: 12/30/15 - 18:55 PM + * + * @author Jeroen Meulemeester + */ +public class BallThreadTest { + + /** + * Verify if the {@link BallThread} can be resumed + */ + @Test(timeout = 5000) + public void testSuspend() throws Exception { + final BallThread ballThread = new BallThread(); + + final BallItem ballItem = mock(BallItem.class); + ballThread.setTwin(ballItem); + + ballThread.start(); + + verify(ballItem, timeout(2000).atLeastOnce()).draw(); + verify(ballItem, timeout(2000).atLeastOnce()).move(); + ballThread.suspendMe(); + + sleep(1000); + + ballThread.stopMe(); + ballThread.join(); + + verifyNoMoreInteractions(ballItem); + } + + /** + * Verify if the {@link BallThread} can be resumed + */ + @Test(timeout = 5000) + public void testResume() throws Exception { + final BallThread ballThread = new BallThread(); + + final BallItem ballItem = mock(BallItem.class); + ballThread.setTwin(ballItem); + + ballThread.suspendMe(); + ballThread.start(); + + sleep(1000); + + verifyZeroInteractions(ballItem); + + ballThread.resumeMe(); + verify(ballItem, timeout(2000).atLeastOnce()).draw(); + verify(ballItem, timeout(2000).atLeastOnce()).move(); + + ballThread.stopMe(); + ballThread.join(); + + verifyNoMoreInteractions(ballItem); + } + + /** + * Verify if the {@link BallThread} is interruptible + */ + @Test(timeout = 5000) + public void testInterrupt() throws Exception { + final BallThread ballThread = new BallThread(); + final UncaughtExceptionHandler exceptionHandler = mock(UncaughtExceptionHandler.class); + ballThread.setUncaughtExceptionHandler(exceptionHandler); + ballThread.setTwin(mock(BallItem.class)); + ballThread.start(); + ballThread.interrupt(); + ballThread.join(); + + verify(exceptionHandler).uncaughtException(eq(ballThread), any(RuntimeException.class)); + verifyNoMoreInteractions(exceptionHandler); + } +} \ No newline at end of file diff --git a/twin/src/test/java/com/iluwatar/twin/StdOutTest.java b/twin/src/test/java/com/iluwatar/twin/StdOutTest.java new file mode 100644 index 000000000..f506886e1 --- /dev/null +++ b/twin/src/test/java/com/iluwatar/twin/StdOutTest.java @@ -0,0 +1,53 @@ +package com.iluwatar.twin; + +import org.junit.After; +import org.junit.Before; + +import java.io.PrintStream; + +import static org.mockito.Mockito.mock; + +/** + * Date: 12/10/15 - 8:37 PM + * + * @author Jeroen Meulemeester + */ +public abstract class StdOutTest { + + /** + * The mocked standard out {@link PrintStream}, required since some actions don't have any + * influence on accessible objects, except for writing to std-out using {@link System#out} + */ + private final PrintStream stdOutMock = mock(PrintStream.class); + + /** + * Keep the original std-out so it can be restored after the test + */ + private final PrintStream stdOutOrig = System.out; + + /** + * Inject the mocked std-out {@link PrintStream} into the {@link System} class before each test + */ + @Before + public void setUp() { + System.setOut(this.stdOutMock); + } + + /** + * Removed the mocked std-out {@link PrintStream} again from the {@link System} class + */ + @After + public void tearDown() { + System.setOut(this.stdOutOrig); + } + + /** + * Get the mocked stdOut {@link PrintStream} + * + * @return The stdOut print stream mock, renewed before each test + */ + final PrintStream getStdOutMock() { + return this.stdOutMock; + } + +}