From 6f44f679233ad701f4a1ed64f315223306af1a79 Mon Sep 17 00:00:00 2001 From: Mikhail Yakushin Date: Tue, 13 Feb 2018 19:08:01 -0500 Subject: [PATCH 1/6] #585 - Completed. --- .../cactoos/iterator/SyncIteratorTest.java | 174 +++++++++++++++++- 1 file changed, 171 insertions(+), 3 deletions(-) diff --git a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java index 0678ca6004..e28407749d 100644 --- a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java +++ b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java @@ -23,16 +23,21 @@ */ package org.cactoos.iterator; +import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; +import junit.framework.TestCase; import org.cactoos.list.ListOf; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; -// @todo #482:30min add multi-threaded tests which test that the lock syncs the -// access to the next method against next and hasNext calls and calls to the -// hasNext method against next calls. /** * Test for {@link SyncIterator}. * @@ -73,4 +78,167 @@ public void syncIteratorReturnsCorrectValuesWithInternalLock() { ); } + @Test + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + public void correctValuesForConcurrentNextNext() + throws InterruptedException { + for (int iter = 0; iter < 5000; iter += 1) { + final List list = Arrays.asList("a", "b"); + final SyncIterator iterator = new SyncIterator<>( + list.iterator() + ); + final List sync = + Collections.synchronizedList( + new ArrayList<>(list.size()) + ); + final Runnable first = () -> { + sync.add(iterator.next()); + }; + final Runnable second = () -> { + sync.add(iterator.next()); + }; + final List test = new ArrayList<>(list.size()); + test.add(first); + test.add(second); + concurrent(test); + MatcherAssert.assertThat( + "Missing the list items(s) (next()).", + sync, + Matchers.containsInAnyOrder("a", "b") + ); + } + } + + @Test + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + public void correctValuesForConcurrentNextHasNext() + throws InterruptedException { + for (int iter = 0; iter < 5000; iter += 1) { + final List list = Arrays.asList("a", "b"); + final SyncIterator iterator = new SyncIterator<>( + list.iterator() + ); + final List sync = + Collections.synchronizedList( + new ArrayList<>(list.size()) + ); + final Runnable first = () -> { + sync.add(iterator.next()); + }; + final Runnable second = () -> { + sync.add(iterator.next()); + }; + final Runnable third = () -> { + sync.add(iterator.hasNext()); + }; + final List test = new ArrayList<>(list.size() + 1); + test.add(first); + test.add(second); + test.add(third); + concurrent(test); + MatcherAssert.assertThat( + "Missing the list items(s) (next()).", + sync, + Matchers.allOf( + Matchers.hasItem("a"), + Matchers.hasItem("b") + ) + ); + MatcherAssert.assertThat( + "Missing hasNext() value.", + sync, + Matchers.anyOf( + Matchers.hasItem(true), + Matchers.hasItem(false) + ) + ); + } + } + + @Test + @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") + public void correctValuesForConcurrentHasNextHasNext() + throws InterruptedException { + for (int iter = 0; iter < 5000; iter += 1) { + final List list = Arrays.asList("a", "b"); + final SyncIterator iterator = new SyncIterator<>( + list.iterator() + ); + final List sync = + Collections.synchronizedList( + new ArrayList<>(list.size()) + ); + final Runnable first = () -> { + sync.add(iterator.hasNext()); + }; + final Runnable second = () -> { + sync.add(iterator.hasNext()); + }; + final List test = new ArrayList<>(list.size()); + test.add(first); + test.add(second); + concurrent(test); + MatcherAssert.assertThat( + "Missing hasNext() value(s).", + sync, + Matchers.contains(true, true) + ); + } + } + + //@checkstyle IllegalCatchCheck (100 lines) + @SuppressWarnings({"PMD.ProhibitPlainJunitAssertionsRule", + "PMD.AvoidCatchingThrowable"}) + private static void concurrent( + final List runnables + ) throws InterruptedException { + final int threads = runnables.size(); + final List exceptions = + Collections.synchronizedList( + new ArrayList(runnables.size()) + ); + final ExecutorService pool = Executors.newFixedThreadPool(threads); + try { + final CountDownLatch ready = new CountDownLatch(threads); + final CountDownLatch init = new CountDownLatch(1); + final CountDownLatch done = new CountDownLatch(threads); + for (final Runnable runnable : runnables) { + pool.submit( + () -> { + ready.countDown(); + try { + init.await(); + runnable.run(); + } catch (final Throwable ex) { + exceptions.add(ex); + } finally { + done.countDown(); + } + }); + } + TestCase.assertTrue( + "Timeout initializing threads! Perform longer thread init.", + ready.await(runnables.size() * 20, TimeUnit.MILLISECONDS) + ); + init.countDown(); + TestCase.assertTrue( + String.format( + "Timeout! More than %d seconds", + 10 + ), + done.await(100, TimeUnit.SECONDS) + ); + } finally { + pool.shutdownNow(); + } + TestCase.assertTrue( + String.format( + "%s failed with exception(s) %s", + "Error", + exceptions.toString() + ), + exceptions.isEmpty() + ); + } + } From 9d6c61bdac700cb267172798546d57c9354b5148 Mon Sep 17 00:00:00 2001 From: Mikhail Yakushin Date: Tue, 13 Feb 2018 19:39:26 -0500 Subject: [PATCH 2/6] #585 - Refactored method to a class. --- .../cactoos/iterator/SyncIteratorTest.java | 140 ++++++++++++------ 1 file changed, 91 insertions(+), 49 deletions(-) diff --git a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java index e28407749d..debd7f2d71 100644 --- a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java +++ b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java @@ -100,7 +100,7 @@ public void correctValuesForConcurrentNextNext() final List test = new ArrayList<>(list.size()); test.add(first); test.add(second); - concurrent(test); + new Concurrent(test).launch(); MatcherAssert.assertThat( "Missing the list items(s) (next()).", sync, @@ -135,7 +135,7 @@ public void correctValuesForConcurrentNextHasNext() test.add(first); test.add(second); test.add(third); - concurrent(test); + new Concurrent(test).launch(); MatcherAssert.assertThat( "Missing the list items(s) (next()).", sync, @@ -177,7 +177,7 @@ public void correctValuesForConcurrentHasNextHasNext() final List test = new ArrayList<>(list.size()); test.add(first); test.add(second); - concurrent(test); + new Concurrent(test).launch(); MatcherAssert.assertThat( "Missing hasNext() value(s).", sync, @@ -186,59 +186,101 @@ public void correctValuesForConcurrentHasNextHasNext() } } - //@checkstyle IllegalCatchCheck (100 lines) - @SuppressWarnings({"PMD.ProhibitPlainJunitAssertionsRule", - "PMD.AvoidCatchingThrowable"}) - private static void concurrent( - final List runnables - ) throws InterruptedException { - final int threads = runnables.size(); - final List exceptions = - Collections.synchronizedList( - new ArrayList(runnables.size()) + /** + * Tests runnables for concurrency issues. + */ + private final class Concurrent { + + /** + * Runnables to run in different threads. + */ + private final List runnables; + + /** + * Collected exceptions. + */ + private final List exceptions; + + /** + * Thread pool. + */ + private final ExecutorService pool; + + /** + * All executor threads are ready. + */ + private final CountDownLatch ready; + + /** + * Start countdown with first thread. + */ + private final CountDownLatch init; + + /** + * All threads ready. + */ + private final CountDownLatch done; + + Concurrent(final List runnables) { + this.runnables = runnables; + this.exceptions = Collections.synchronizedList( + new ArrayList( + runnables.size() + ) ); - final ExecutorService pool = Executors.newFixedThreadPool(threads); - try { - final CountDownLatch ready = new CountDownLatch(threads); - final CountDownLatch init = new CountDownLatch(1); - final CountDownLatch done = new CountDownLatch(threads); - for (final Runnable runnable : runnables) { - pool.submit( - () -> { - ready.countDown(); - try { - init.await(); - runnable.run(); - } catch (final Throwable ex) { - exceptions.add(ex); - } finally { - done.countDown(); - } - }); + this.pool = Executors.newFixedThreadPool(runnables.size()); + this.ready = new CountDownLatch(runnables.size()); + this.init = new CountDownLatch(1); + this.done = new CountDownLatch(runnables.size()); + } + + //@checkstyle IllegalCatchCheck (100 lines) + @SuppressWarnings({"PMD.ProhibitPlainJunitAssertionsRule", + "PMD.AvoidCatchingThrowable"}) + public void launch() throws InterruptedException { + try { + for (final Runnable runnable : this.runnables) { + this.pool.submit( + () -> { + this.ready.countDown(); + try { + this.init.await(); + runnable.run(); + } catch (final Throwable ex) { + this.exceptions.add(ex); + } finally { + this.done.countDown(); + } + }); + } + TestCase.assertTrue( + "Timeout initializing threads! Perform longer thread init.", + this.ready.await( + this.runnables.size() * 20, + TimeUnit.MILLISECONDS + ) + ); + this.init.countDown(); + TestCase.assertTrue( + String.format( + "Timeout! More than %d seconds", + 10 + ), + this.done.await(100, TimeUnit.SECONDS) + ); + } finally { + this.pool.shutdownNow(); } - TestCase.assertTrue( - "Timeout initializing threads! Perform longer thread init.", - ready.await(runnables.size() * 20, TimeUnit.MILLISECONDS) - ); - init.countDown(); TestCase.assertTrue( String.format( - "Timeout! More than %d seconds", - 10 + "%s failed with exception(s) %s", + "Error", + this.exceptions.toString() ), - done.await(100, TimeUnit.SECONDS) + this.exceptions.isEmpty() ); - } finally { - pool.shutdownNow(); } - TestCase.assertTrue( - String.format( - "%s failed with exception(s) %s", - "Error", - exceptions.toString() - ), - exceptions.isEmpty() - ); + } } From 6746546e0ade897ee809296f8c49837477ec3526 Mon Sep 17 00:00:00 2001 From: Mikhail Yakushin Date: Tue, 13 Feb 2018 19:48:19 -0500 Subject: [PATCH 3/6] #585 - Fix. --- src/test/java/org/cactoos/iterator/SyncIteratorTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java index debd7f2d71..e2991f4d31 100644 --- a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java +++ b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java @@ -256,7 +256,7 @@ public void launch() throws InterruptedException { TestCase.assertTrue( "Timeout initializing threads! Perform longer thread init.", this.ready.await( - this.runnables.size() * 20, + this.runnables.size() * 50, TimeUnit.MILLISECONDS ) ); From d3ba2fdd6720557b47c61dc9c9c3478d33712cad Mon Sep 17 00:00:00 2001 From: Mikhail Yakushin Date: Fri, 16 Feb 2018 12:15:44 -0500 Subject: [PATCH 4/6] #585 - Refactored list to varargs in ctor. --- .../cactoos/iterator/SyncIteratorTest.java | 33 ++++++++----------- 1 file changed, 13 insertions(+), 20 deletions(-) diff --git a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java index e2991f4d31..55b4c1d8bf 100644 --- a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java +++ b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java @@ -34,6 +34,7 @@ import java.util.concurrent.locks.ReentrantReadWriteLock; import junit.framework.TestCase; import org.cactoos.list.ListOf; +import org.cactoos.list.StickyList; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; @@ -97,10 +98,7 @@ public void correctValuesForConcurrentNextNext() final Runnable second = () -> { sync.add(iterator.next()); }; - final List test = new ArrayList<>(list.size()); - test.add(first); - test.add(second); - new Concurrent(test).launch(); + new Concurrent(first, second).launch(); MatcherAssert.assertThat( "Missing the list items(s) (next()).", sync, @@ -131,11 +129,7 @@ public void correctValuesForConcurrentNextHasNext() final Runnable third = () -> { sync.add(iterator.hasNext()); }; - final List test = new ArrayList<>(list.size() + 1); - test.add(first); - test.add(second); - test.add(third); - new Concurrent(test).launch(); + new Concurrent(first, second, third).launch(); MatcherAssert.assertThat( "Missing the list items(s) (next()).", sync, @@ -174,10 +168,7 @@ public void correctValuesForConcurrentHasNextHasNext() final Runnable second = () -> { sync.add(iterator.hasNext()); }; - final List test = new ArrayList<>(list.size()); - test.add(first); - test.add(second); - new Concurrent(test).launch(); + new Concurrent(first, second).launch(); MatcherAssert.assertThat( "Missing hasNext() value(s).", sync, @@ -194,7 +185,7 @@ private final class Concurrent { /** * Runnables to run in different threads. */ - private final List runnables; + private final List runnables; /** * Collected exceptions. @@ -221,17 +212,19 @@ private final class Concurrent { */ private final CountDownLatch done; - Concurrent(final List runnables) { - this.runnables = runnables; + Concurrent(final Runnable... runnables) { + this.runnables = new StickyList( + new ListOf(runnables) + ); this.exceptions = Collections.synchronizedList( new ArrayList( - runnables.size() + runnables.length ) ); - this.pool = Executors.newFixedThreadPool(runnables.size()); - this.ready = new CountDownLatch(runnables.size()); + this.pool = Executors.newFixedThreadPool(runnables.length); + this.ready = new CountDownLatch(runnables.length); this.init = new CountDownLatch(1); - this.done = new CountDownLatch(runnables.size()); + this.done = new CountDownLatch(runnables.length); } //@checkstyle IllegalCatchCheck (100 lines) From 2bc525e538ba1e98a7ba44755874065a844b3f75 Mon Sep 17 00:00:00 2001 From: Mikhail Yakushin Date: Mon, 19 Feb 2018 14:16:17 -0500 Subject: [PATCH 5/6] #585 - Refactored TestCase to MatcherAssert --- src/test/java/org/cactoos/iterator/SyncIteratorTest.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java index 55b4c1d8bf..46aeef70aa 100644 --- a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java +++ b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java @@ -32,7 +32,6 @@ import java.util.concurrent.Executors; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; -import junit.framework.TestCase; import org.cactoos.list.ListOf; import org.cactoos.list.StickyList; import org.hamcrest.MatcherAssert; @@ -246,7 +245,7 @@ public void launch() throws InterruptedException { } }); } - TestCase.assertTrue( + MatcherAssert.assertThat( "Timeout initializing threads! Perform longer thread init.", this.ready.await( this.runnables.size() * 50, @@ -254,7 +253,7 @@ public void launch() throws InterruptedException { ) ); this.init.countDown(); - TestCase.assertTrue( + MatcherAssert.assertThat( String.format( "Timeout! More than %d seconds", 10 @@ -264,7 +263,7 @@ public void launch() throws InterruptedException { } finally { this.pool.shutdownNow(); } - TestCase.assertTrue( + MatcherAssert.assertThat( String.format( "%s failed with exception(s) %s", "Error", From 41da93f4e69fed7ef3eb3fac1e1886e1c69d83c5 Mon Sep 17 00:00:00 2001 From: Mikhail Yakushin Date: Mon, 19 Feb 2018 15:13:25 -0500 Subject: [PATCH 6/6] #585 - Refactored class to RunsInThreads. --- .../cactoos/iterator/SyncIteratorTest.java | 236 ++++-------------- 1 file changed, 49 insertions(+), 187 deletions(-) diff --git a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java index 46aeef70aa..915ac4dd8d 100644 --- a/src/test/java/org/cactoos/iterator/SyncIteratorTest.java +++ b/src/test/java/org/cactoos/iterator/SyncIteratorTest.java @@ -23,17 +23,10 @@ */ package org.cactoos.iterator; -import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; -import java.util.List; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantReadWriteLock; import org.cactoos.list.ListOf; -import org.cactoos.list.StickyList; +import org.cactoos.matchers.RunsInThreads; import org.hamcrest.MatcherAssert; import org.hamcrest.Matchers; import org.junit.Test; @@ -80,69 +73,25 @@ public void syncIteratorReturnsCorrectValuesWithInternalLock() { @Test @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - public void correctValuesForConcurrentNextNext() - throws InterruptedException { + public void correctValuesForConcurrentNextNext() { for (int iter = 0; iter < 5000; iter += 1) { - final List list = Arrays.asList("a", "b"); - final SyncIterator iterator = new SyncIterator<>( - list.iterator() - ); - final List sync = - Collections.synchronizedList( - new ArrayList<>(list.size()) - ); - final Runnable first = () -> { - sync.add(iterator.next()); - }; - final Runnable second = () -> { - sync.add(iterator.next()); - }; - new Concurrent(first, second).launch(); MatcherAssert.assertThat( - "Missing the list items(s) (next()).", - sync, - Matchers.containsInAnyOrder("a", "b") - ); - } - } - - @Test - @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - public void correctValuesForConcurrentNextHasNext() - throws InterruptedException { - for (int iter = 0; iter < 5000; iter += 1) { - final List list = Arrays.asList("a", "b"); - final SyncIterator iterator = new SyncIterator<>( - list.iterator() - ); - final List sync = - Collections.synchronizedList( - new ArrayList<>(list.size()) - ); - final Runnable first = () -> { - sync.add(iterator.next()); - }; - final Runnable second = () -> { - sync.add(iterator.next()); - }; - final Runnable third = () -> { - sync.add(iterator.hasNext()); - }; - new Concurrent(first, second, third).launch(); - MatcherAssert.assertThat( - "Missing the list items(s) (next()).", - sync, - Matchers.allOf( - Matchers.hasItem("a"), - Matchers.hasItem("b") - ) - ); - MatcherAssert.assertThat( - "Missing hasNext() value.", - sync, - Matchers.anyOf( - Matchers.hasItem(true), - Matchers.hasItem(false) + "", + map -> { + MatcherAssert.assertThat( + map.next(), + Matchers.anyOf( + Matchers.equalTo("a"), + Matchers.equalTo("b") + ) + ); + return true; + }, + new RunsInThreads<>( + new SyncIterator<>( + Arrays.asList("a", "b").iterator() + ), + 2 ) ); } @@ -150,129 +99,42 @@ public void correctValuesForConcurrentNextHasNext() @Test @SuppressWarnings("PMD.AvoidInstantiatingObjectsInLoops") - public void correctValuesForConcurrentHasNextHasNext() - throws InterruptedException { + public void correctValuesForConcurrentNextHasNext() { for (int iter = 0; iter < 5000; iter += 1) { - final List list = Arrays.asList("a", "b"); - final SyncIterator iterator = new SyncIterator<>( - list.iterator() - ); - final List sync = - Collections.synchronizedList( - new ArrayList<>(list.size()) - ); - final Runnable first = () -> { - sync.add(iterator.hasNext()); - }; - final Runnable second = () -> { - sync.add(iterator.hasNext()); - }; - new Concurrent(first, second).launch(); MatcherAssert.assertThat( - "Missing hasNext() value(s).", - sync, - Matchers.contains(true, true) - ); - } - } - - /** - * Tests runnables for concurrency issues. - */ - private final class Concurrent { - - /** - * Runnables to run in different threads. - */ - private final List runnables; - - /** - * Collected exceptions. - */ - private final List exceptions; - - /** - * Thread pool. - */ - private final ExecutorService pool; - - /** - * All executor threads are ready. - */ - private final CountDownLatch ready; - - /** - * Start countdown with first thread. - */ - private final CountDownLatch init; - - /** - * All threads ready. - */ - private final CountDownLatch done; - - Concurrent(final Runnable... runnables) { - this.runnables = new StickyList( - new ListOf(runnables) - ); - this.exceptions = Collections.synchronizedList( - new ArrayList( - runnables.length - ) - ); - this.pool = Executors.newFixedThreadPool(runnables.length); - this.ready = new CountDownLatch(runnables.length); - this.init = new CountDownLatch(1); - this.done = new CountDownLatch(runnables.length); - } - - //@checkstyle IllegalCatchCheck (100 lines) - @SuppressWarnings({"PMD.ProhibitPlainJunitAssertionsRule", - "PMD.AvoidCatchingThrowable"}) - public void launch() throws InterruptedException { - try { - for (final Runnable runnable : this.runnables) { - this.pool.submit( - () -> { - this.ready.countDown(); - try { - this.init.await(); - runnable.run(); - } catch (final Throwable ex) { - this.exceptions.add(ex); - } finally { - this.done.countDown(); - } - }); - } - MatcherAssert.assertThat( - "Timeout initializing threads! Perform longer thread init.", - this.ready.await( - this.runnables.size() * 50, - TimeUnit.MILLISECONDS - ) - ); - this.init.countDown(); - MatcherAssert.assertThat( - String.format( - "Timeout! More than %d seconds", - 10 + "", + map -> { + MatcherAssert.assertThat( + map.hasNext(), + Matchers.anyOf( + Matchers.equalTo(true), + Matchers.equalTo(true) + ) + ); + MatcherAssert.assertThat( + map.next(), + Matchers.anyOf( + Matchers.equalTo("a"), + Matchers.equalTo("b") + ) + ); + MatcherAssert.assertThat( + map.hasNext(), + Matchers.anyOf( + Matchers.equalTo(true), + Matchers.equalTo(false) + ) + ); + return true; + }, + new RunsInThreads<>( + new SyncIterator<>( + Arrays.asList("a", "b").iterator() ), - this.done.await(100, TimeUnit.SECONDS) - ); - } finally { - this.pool.shutdownNow(); - } - MatcherAssert.assertThat( - String.format( - "%s failed with exception(s) %s", - "Error", - this.exceptions.toString() - ), - this.exceptions.isEmpty() + 2 + ) ); } - } }