You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by ti...@apache.org on 2015/11/28 08:43:23 UTC
[3/3] maven-surefire git commit: [SUREFIRE-1193] reworked
MasterProcessReader
[SUREFIRE-1193] reworked MasterProcessReader
Project: http://git-wip-us.apache.org/repos/asf/maven-surefire/repo
Commit: http://git-wip-us.apache.org/repos/asf/maven-surefire/commit/0693bd90
Tree: http://git-wip-us.apache.org/repos/asf/maven-surefire/tree/0693bd90
Diff: http://git-wip-us.apache.org/repos/asf/maven-surefire/diff/0693bd90
Branch: refs/heads/master
Commit: 0693bd900cdb38d3aa0bd2d79cbf0c2f786783c5
Parents: 47bf4b8
Author: Tibor17 <ti...@lycos.com>
Authored: Sat Nov 28 08:01:28 2015 +0100
Committer: Tibor17 <ti...@lycos.com>
Committed: Sat Nov 28 08:01:28 2015 +0100
----------------------------------------------------------------------
.../maven/surefire/booter/BiProperty.java | 50 ++
.../maven/surefire/booter/CommandListener.java | 28 +
.../maven/surefire/booter/CommandReader.java | 433 +++++++++++++++
.../surefire/booter/MasterProcessListener.java | 28 -
.../surefire/booter/MasterProcessReader.java | 544 -------------------
.../surefire/booter/TwoPropertiesWrapper.java | 50 --
.../maven/surefire/booter/ForkedBooter.java | 20 +-
.../maven/surefire/booter/LazyTestsToRun.java | 14 +-
.../surefire/booter/CommandReaderTest.java | 199 +++++++
.../maven/surefire/booter/JUnit4SuiteTest.java | 2 +-
.../booter/MasterProcessReaderTest.java | 254 ---------
.../maven/surefire/junit4/JUnit4Provider.java | 14 +-
.../surefire/junitcore/JUnitCoreProvider.java | 14 +-
.../maven/surefire/testng/TestNGProvider.java | 14 +-
14 files changed, 752 insertions(+), 912 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-api/src/main/java/org/apache/maven/surefire/booter/BiProperty.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/BiProperty.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BiProperty.java
new file mode 100644
index 0000000..b49e12b
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/BiProperty.java
@@ -0,0 +1,50 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Internal generic wrapper.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ * @param <P1> first property
+ * @param <P2> second property
+ */
+final class BiProperty<P1, P2>
+{
+ private final P1 p1;
+ private final P2 p2;
+
+ BiProperty( P1 p1, P2 p2 )
+ {
+ this.p1 = p1;
+ this.p2 = p2;
+ }
+
+ P1 getP1()
+ {
+ return p1;
+ }
+
+ P2 getP2()
+ {
+ return p2;
+ }
+}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandListener.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandListener.java
new file mode 100644
index 0000000..523ca76
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandListener.java
@@ -0,0 +1,28 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+/**
+ * Command listener interface.
+ */
+public interface CommandListener
+{
+ void update( Command command );
+}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
new file mode 100644
index 0000000..45e8a24
--- /dev/null
+++ b/surefire-api/src/main/java/org/apache/maven/surefire/booter/CommandReader.java
@@ -0,0 +1,433 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.testset.TestSetFailedException;
+
+import java.io.DataInputStream;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.atomic.AtomicReference;
+
+import static java.lang.Thread.State.NEW;
+import static java.lang.Thread.State.RUNNABLE;
+import static java.lang.Thread.State.TERMINATED;
+import static org.apache.maven.surefire.booter.Command.toShutdown;
+import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
+import static org.apache.maven.surefire.booter.MasterProcessCommand.decode;
+import static org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
+import static org.apache.maven.surefire.util.internal.StringUtils.isNotBlank;
+import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
+import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThread;
+
+/**
+ * Reader of commands coming from plugin(master) process.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+public final class CommandReader
+{
+ private static final String LAST_TEST_SYMBOL = "";
+
+ private static final CommandReader READER = new CommandReader();
+
+ private final Queue<BiProperty<MasterProcessCommand, CommandListener>> listeners
+ = new ConcurrentLinkedQueue<BiProperty<MasterProcessCommand, CommandListener>>();
+
+ private final Thread commandThread = newDaemonThread( new CommandRunnable(), "surefire-forkedjvm-command-thread" );
+
+ private final AtomicReference<Thread.State> state = new AtomicReference<Thread.State>( NEW );
+
+ private final CountDownLatch startMonitor = new CountDownLatch( 1 );
+
+ private final Semaphore nextCommandNotifier = new Semaphore( 0 );
+
+ private final CopyOnWriteArrayList<String> testClasses = new CopyOnWriteArrayList<String>();
+
+ private volatile Shutdown shutdown;
+
+ public static CommandReader getReader()
+ {
+ final CommandReader reader = READER;
+ if ( reader.state.compareAndSet( NEW, RUNNABLE ) )
+ {
+ reader.commandThread.start();
+ }
+ return reader;
+ }
+
+ public CommandReader setShutdown( Shutdown shutdown )
+ {
+ this.shutdown = shutdown;
+ return this;
+ }
+
+ public boolean awaitStarted()
+ throws TestSetFailedException
+ {
+ if ( state.get() == RUNNABLE )
+ {
+ try
+ {
+ startMonitor.await();
+ return true;
+ }
+ catch ( InterruptedException e )
+ {
+ throw new TestSetFailedException( e.getLocalizedMessage() );
+ }
+ }
+ else
+ {
+ return false;
+ }
+ }
+
+ /**
+ * @param listener listener called with <em>Any</em> {@link MasterProcessCommand command type}
+ */
+ public void addListener( CommandListener listener )
+ {
+ listeners.add( new BiProperty<MasterProcessCommand, CommandListener>( null, listener ) );
+ }
+
+ public void addTestListener( CommandListener listener )
+ {
+ addListener( RUN_CLASS, listener );
+ }
+
+ public void addTestsFinishedListener( CommandListener listener )
+ {
+ addListener( TEST_SET_FINISHED, listener );
+ }
+
+ public void addSkipNextListener( CommandListener listener )
+ {
+ addListener( SKIP_SINCE_NEXT_TEST, listener );
+ }
+
+ public void addShutdownListener( CommandListener listener )
+ {
+ addListener( SHUTDOWN, listener );
+ }
+
+ public void addNoopListener( CommandListener listener )
+ {
+ addListener( NOOP, listener );
+ }
+
+ private void addListener( MasterProcessCommand cmd, CommandListener listener )
+ {
+ listeners.add( new BiProperty<MasterProcessCommand, CommandListener>( cmd, listener ) );
+ }
+
+ public void removeListener( CommandListener listener )
+ {
+ for ( Iterator<BiProperty<MasterProcessCommand, CommandListener>> it = listeners.iterator(); it.hasNext(); )
+ {
+ BiProperty<MasterProcessCommand, CommandListener> listenerWrapper = it.next();
+ if ( listener == listenerWrapper.getP2() )
+ {
+ it.remove();
+ }
+ }
+ }
+
+ /**
+ * The iterator can be used only in one Thread.
+ *
+ * @param originalOutStream original stream in current JVM process
+ * @return Iterator with test classes lazily loaded as commands from the main process
+ */
+ Iterable<String> getIterableClasses( PrintStream originalOutStream )
+ {
+ return new ClassesIterable( originalOutStream );
+ }
+
+ public void stop()
+ {
+ if ( state.compareAndSet( NEW, TERMINATED ) || state.compareAndSet( RUNNABLE, TERMINATED ) )
+ {
+ makeQueueFull();
+ listeners.clear();
+ commandThread.interrupt();
+ }
+ }
+
+ private boolean isStopped()
+ {
+ return state.get() == TERMINATED;
+ }
+
+ private boolean isQueueFull()
+ {
+ return testClasses.contains( LAST_TEST_SYMBOL );
+ }
+
+ public void makeQueueFull()
+ {
+ testClasses.addIfAbsent( LAST_TEST_SYMBOL );
+ }
+
+ public boolean insertToQueue( String test )
+ {
+ if ( isNotBlank( test ) && !isQueueFull() )
+ {
+ testClasses.add( test );
+ return true;
+ }
+ return false;
+ }
+
+ private final class ClassesIterable
+ implements Iterable<String>
+ {
+ private final PrintStream originalOutStream;
+
+ ClassesIterable( PrintStream originalOutStream )
+ {
+ this.originalOutStream = originalOutStream;
+ }
+
+ public Iterator<String> iterator()
+ {
+ return new ClassesIterator( originalOutStream );
+ }
+ }
+
+ private final class ClassesIterator
+ implements Iterator<String>
+ {
+ private final PrintStream originalOutStream;
+
+ private String clazz;
+
+ private int nextQueueIndex = 0;
+
+ private ClassesIterator( PrintStream originalOutStream )
+ {
+ this.originalOutStream = originalOutStream;
+ }
+
+ public boolean hasNext()
+ {
+ popUnread();
+ return isNotBlank( clazz );
+ }
+
+ public String next()
+ {
+ popUnread();
+ try
+ {
+ if ( isBlank( clazz ) )
+ {
+ throw new NoSuchElementException( CommandReader.this.isStopped() ? "stream was stopped" : "" );
+ }
+ else
+ {
+ return clazz;
+ }
+ }
+ finally
+ {
+ clazz = null;
+ }
+ }
+
+ public void remove()
+ {
+ throw new UnsupportedOperationException();
+ }
+
+ private void popUnread()
+ {
+ if ( CommandReader.this.isStopped() || CommandReader.this.isQueueFull() )
+ {
+ clazz = null;
+ return;
+ }
+
+ if ( isBlank( clazz ) )
+ {
+ requestNextTest();
+ CommandReader.this.awaitNextTest();
+ if ( CommandReader.this.isStopped() || CommandReader.this.isQueueFull() )
+ {
+ clazz = null;
+ return;
+ }
+ clazz = CommandReader.this.testClasses.get( nextQueueIndex++ );
+ }
+
+ if ( CommandReader.this.isStopped() )
+ {
+ clazz = null;
+ }
+ }
+
+ private void requestNextTest()
+ {
+ byte[] encoded = encodeStringForForkCommunication( ( (char) BOOTERCODE_NEXT_TEST ) + ",0,want more!\n" );
+ originalOutStream.write( encoded, 0, encoded.length );
+ }
+ }
+
+ private void awaitNextTest()
+ {
+ nextCommandNotifier.acquireUninterruptibly();
+ }
+
+ private void wakeupIterator()
+ {
+ nextCommandNotifier.release();
+ }
+
+ private final class CommandRunnable
+ implements Runnable
+ {
+ public void run()
+ {
+ CommandReader.this.startMonitor.countDown();
+ DataInputStream stdIn = new DataInputStream( System.in );
+ boolean isTestSetFinished = false;
+ try
+ {
+ while ( CommandReader.this.state.get() == RUNNABLE )
+ {
+ Command command = decode( stdIn );
+ if ( command == null )
+ {
+ System.err.println( "[SUREFIRE] std/in stream corrupted: first sequence not recognized" );
+ break;
+ }
+ else
+ {
+ switch ( command.getCommandType() )
+ {
+ case RUN_CLASS:
+ String test = command.getData();
+ boolean inserted = CommandReader.this.insertToQueue( test );
+ CommandReader.this.wakeupIterator();
+ if ( inserted )
+ {
+ insertToListeners( command );
+ }
+ break;
+ case TEST_SET_FINISHED:
+ CommandReader.this.makeQueueFull();
+ isTestSetFinished = true;
+ CommandReader.this.wakeupIterator();
+ insertToListeners( command );
+ break;
+ case SHUTDOWN:
+ CommandReader.this.makeQueueFull();
+ CommandReader.this.wakeupIterator();
+ insertToListeners( command );
+ break;
+ default:
+ insertToListeners( command );
+ break;
+ }
+ }
+ }
+ }
+ catch ( EOFException e )
+ {
+ CommandReader.this.state.set( TERMINATED );
+ if ( !isTestSetFinished )
+ {
+ exitByConfiguration();
+ // does not go to finally
+ }
+ }
+ catch ( IOException e )
+ {
+ CommandReader.this.state.set( TERMINATED );
+ // If #stop() method is called, reader thread is interrupted and cause is InterruptedException.
+ if ( !( e.getCause() instanceof InterruptedException ) )
+ {
+ System.err.println( "[SUREFIRE] std/in stream corrupted" );
+ e.printStackTrace();
+ }
+ }
+ finally
+ {
+ // ensure fail-safe iterator as well as safe to finish in for-each loop using ClassesIterator
+ if ( !isTestSetFinished )
+ {
+ CommandReader.this.makeQueueFull();
+ }
+ CommandReader.this.wakeupIterator();
+ }
+ }
+
+ private void insertToListeners( Command cmd )
+ {
+ MasterProcessCommand expectedCommandType = cmd.getCommandType();
+ for ( BiProperty<MasterProcessCommand, CommandListener> listenerWrapper : CommandReader.this.listeners )
+ {
+ MasterProcessCommand commandType = listenerWrapper.getP1();
+ CommandListener listener = listenerWrapper.getP2();
+ if ( commandType == null || commandType == expectedCommandType )
+ {
+ listener.update( cmd );
+ }
+ }
+ }
+
+ private void exitByConfiguration()
+ {
+ Shutdown shutdown = CommandReader.this.shutdown; // won't read inconsistent changes through the stack
+ if ( shutdown != null )
+ {
+ CommandReader.this.makeQueueFull();
+ CommandReader.this.wakeupIterator();
+ insertToListeners( toShutdown( shutdown ) );
+ switch ( shutdown )
+ {
+ case EXIT:
+ System.exit( 1 );
+ case KILL:
+ Runtime.getRuntime().halt( 1 );
+ case DEFAULT:
+ default:
+ // should not happen; otherwise you missed enum case
+ break;
+ }
+ }
+ }
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java
deleted file mode 100644
index 89a4f89..0000000
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessListener.java
+++ /dev/null
@@ -1,28 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * listener interface
- */
-public interface MasterProcessListener
-{
- void update( Command command );
-}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
deleted file mode 100644
index dabe80a..0000000
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/MasterProcessReader.java
+++ /dev/null
@@ -1,544 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.apache.maven.surefire.testset.TestSetFailedException;
-
-import java.io.DataInputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.io.PrintStream;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-import java.util.Queue;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.atomic.AtomicReference;
-
-import static java.lang.Thread.State.NEW;
-import static java.lang.Thread.State.RUNNABLE;
-import static java.lang.Thread.State.TERMINATED;
-import static org.apache.maven.surefire.booter.Command.toShutdown;
-import static org.apache.maven.surefire.booter.ForkingRunListener.BOOTERCODE_NEXT_TEST;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.NOOP;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.RUN_CLASS;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.SHUTDOWN;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.SKIP_SINCE_NEXT_TEST;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.TEST_SET_FINISHED;
-import static org.apache.maven.surefire.booter.MasterProcessCommand.decode;
-import static org.apache.maven.surefire.util.internal.StringUtils.encodeStringForForkCommunication;
-import static org.apache.maven.surefire.util.internal.StringUtils.isNotBlank;
-import static org.apache.maven.surefire.util.internal.StringUtils.isBlank;
-import static org.apache.maven.surefire.util.internal.DaemonThreadFactory.newDaemonThread;
-
-/**
- * Reader of commands coming from plugin(master) process.
- *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
- */
-public final class MasterProcessReader
-{
- private static final MasterProcessReader READER = new MasterProcessReader();
-
- private final Queue<TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener>> listeners
- = new ConcurrentLinkedQueue<TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener>>();
-
- private final Thread commandThread = newDaemonThread( new CommandRunnable(), "surefire-forkedjvm-command-thread" );
-
- private final AtomicReference<Thread.State> state = new AtomicReference<Thread.State>( NEW );
-
- private final CountDownLatch startMonitor = new CountDownLatch( 1 );
-
- private final Node headTestClassQueue = new Node();
-
- private final Semaphore newCommandNotifier = new Semaphore( 0 );
-
- private volatile Node tailTestClassQueue = headTestClassQueue;
-
- private volatile Shutdown shutdown;
-
- private static class Node
- {
- final AtomicReference<Node> successor = new AtomicReference<Node>();
- volatile String item;
- }
-
- public static MasterProcessReader getReader()
- {
- final MasterProcessReader reader = READER;
- if ( reader.state.compareAndSet( NEW, RUNNABLE ) )
- {
- reader.commandThread.start();
- }
- return reader;
- }
-
- public MasterProcessReader setShutdown( Shutdown shutdown )
- {
- this.shutdown = shutdown;
- return this;
- }
-
- public boolean awaitStarted()
- throws TestSetFailedException
- {
- if ( state.get() == RUNNABLE )
- {
- try
- {
- startMonitor.await();
- return true;
- }
- catch ( InterruptedException e )
- {
- throw new TestSetFailedException( e.getLocalizedMessage() );
- }
- }
- else
- {
- return false;
- }
- }
-
- /**
- * @param listener listener called with <em>Any</em> {@link MasterProcessCommand command type}
- */
- public void addListener( MasterProcessListener listener )
- {
- listeners.add( new TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener>( null, listener ) );
- }
-
- public void addTestListener( MasterProcessListener listener )
- {
- addListener( RUN_CLASS, listener );
- }
-
- public void addTestsFinishedListener( MasterProcessListener listener )
- {
- addListener( TEST_SET_FINISHED, listener );
- }
-
- public void addSkipNextListener( MasterProcessListener listener )
- {
- addListener( SKIP_SINCE_NEXT_TEST, listener );
- }
-
- public void addShutdownListener( MasterProcessListener listener )
- {
- addListener( SHUTDOWN, listener );
- }
-
- public void addNoopListener( MasterProcessListener listener )
- {
- addListener( NOOP, listener );
- }
-
- private void addListener( MasterProcessCommand cmd, MasterProcessListener listener )
- {
- listeners.add( new TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener>( cmd, listener ) );
- }
-
- public void removeListener( MasterProcessListener listener )
- {
- for ( Iterator<TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener>> it = listeners.iterator();
- it.hasNext(); )
- {
- TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener> listenerWrapper = it.next();
- if ( listener == listenerWrapper.getP2() )
- {
- it.remove();
- }
- }
- }
-
- Iterable<String> getIterableClasses( PrintStream originalOutStream )
- {
- return new ClassesIterable( headTestClassQueue, originalOutStream );
- }
-
- public void stop()
- {
- if ( state.compareAndSet( NEW, TERMINATED ) || state.compareAndSet( RUNNABLE, TERMINATED ) )
- {
- makeQueueFull();
- listeners.clear();
- commandThread.interrupt();
- }
- }
-
- private boolean isStopped()
- {
- return state.get() == TERMINATED;
- }
-
- private static boolean isLastNode( Node current )
- {
- return current.successor.get() == current;
- }
-
- private boolean isQueueFull()
- {
- return isLastNode( tailTestClassQueue );
- }
-
- /**
- * thread-safety: Must be called from single thread like here the reader thread only.
- */
- private boolean addTestClassToQueue( String item )
- {
- if ( tailTestClassQueue.item == null )
- {
- tailTestClassQueue.item = item;
- Node newNode = new Node();
- tailTestClassQueue.successor.set( newNode );
- tailTestClassQueue = newNode;
- return true;
- }
- else
- {
- return false;
- }
- }
-
- /**
- * After this method returns the queue is closed, new item cannot be added and method
- * {@link #isQueueFull()} returns true.
- */
- @SuppressWarnings( { "all", "checkstyle:needbraces", "checkstyle:emptystatement" } )
- public void makeQueueFull()
- {
- // order between (#compareAndSet, and #get) matters in multithreading
- for ( Node tail = this.tailTestClassQueue;
- !tail.successor.compareAndSet( null, tail ) && tail.successor.get() != tail;
- tail = tail.successor.get() );
- }
-
- /**
- * thread-safety: Must be called from single thread like here the reader thread only.
- */
- private void insertToQueue( Command cmd )
- {
- MasterProcessCommand expectedCommandType = cmd.getCommandType();
- switch ( expectedCommandType )
- {
- case RUN_CLASS:
- addTestClassToQueue( cmd.getData() );
- break;
- case TEST_SET_FINISHED:
- makeQueueFull();
- break;
- default:
- // checkstyle noop
- break;
- }
- }
-
- private void insertToListeners( Command cmd )
- {
- MasterProcessCommand expectedCommandType = cmd.getCommandType();
- for ( TwoPropertiesWrapper<MasterProcessCommand, MasterProcessListener> listenerWrapper
- : MasterProcessReader.this.listeners )
- {
- MasterProcessCommand commandType = listenerWrapper.getP1();
- MasterProcessListener listener = listenerWrapper.getP2();
- if ( commandType == null || commandType == expectedCommandType )
- {
- listener.update( cmd );
- }
- }
- }
-
- /**
- * thread-safety: Must be called from single thread like here the reader thread only.
- */
- private void insert( Command cmd )
- {
- insertToQueue( cmd );
- insertToListeners( cmd );
- }
-
- private final class ClassesIterable
- implements Iterable<String>
- {
- private final Node head;
- private final PrintStream originalOutStream;
-
- ClassesIterable( Node head, PrintStream originalOutStream )
- {
- this.head = head;
- this.originalOutStream = originalOutStream;
- }
-
- public Iterator<String> iterator()
- {
- return new ClassesIterator( head, originalOutStream );
- }
- }
-
- private final class ClassesIterator
- implements Iterator<String>
- {
- private final PrintStream originalOutStream;
-
- private Node current;
-
- private String clazz;
-
- private ClassesIterator( Node current, PrintStream originalOutStream )
- {
- this.current = current;
- this.originalOutStream = originalOutStream;
- }
-
- public boolean hasNext()
- {
- popUnread();
- return isNotBlank( clazz );
- }
-
- public String next()
- {
- popUnread();
- try
- {
- if ( isBlank( clazz ) )
- {
- throw new NoSuchElementException();
- }
- else
- {
- return clazz;
- }
- }
- finally
- {
- clazz = null;
- }
- }
-
- public void remove()
- {
- throw new UnsupportedOperationException();
- }
-
- private void popUnread()
- {
- if ( isStopped() )
- {
- clazz = null;
- return;
- }
-
- if ( isBlank( clazz ) )
- {
- do
- {
- requestNextTest();
- /**
- * this branching should be refactored to
- * waitNextTest();
- * if ( isStopped() )
- * {
- * clazz = null;
- * return;
- * }
- * clazz = current.item;
- * if ( !isLastNode( current ) )
- * {
- * current = current.successor.get();
- * }
- */
- if ( isLastNode( current ) )
- {
- clazz = null;
- }
- else if ( current.item == null )
- {
- do
- {
- awaitNextTest();
- if ( isStopped() )
- {
- clazz = null;
- return;
- }
- } while ( current.item == null && !isLastNode( current ) );
- clazz = current.item;
- current = current.successor.get();
- }
- else
- {
- clazz = current.item;
- current = current.successor.get();
- }
- }
- while ( tryNullWhiteClass() );
- }
-
- if ( isStopped() )
- {
- clazz = null;
- }
- }
-
- private boolean tryNullWhiteClass()
- {
- if ( clazz != null && isBlank( clazz ) )
- {
- clazz = null;
- return true;
- }
- else
- {
- return false;
- }
- }
-
- private void requestNextTest()
- {
- byte[] encoded = encodeStringForForkCommunication( ( (char) BOOTERCODE_NEXT_TEST ) + ",0,want more!\n" );
- originalOutStream.write( encoded, 0, encoded.length );
- }
- }
-
- /**
- * thread-safety: Must be called from single thread like here the reader thread only.
- */
- private Command read( DataInputStream stdIn )
- throws IOException
- {
- Command command = decode( stdIn );
- if ( command != null )
- {
- insertToQueue( command );
- }
- return command;
- }
-
- private void awaitNextTest()
- {
- newCommandNotifier.acquireUninterruptibly();
- }
-
- private void wakeupIterator()
- {
- newCommandNotifier.release();
- }
-
- private final class CommandRunnable
- implements Runnable
- {
- public void run()
- {
- MasterProcessReader.this.startMonitor.countDown();
- DataInputStream stdIn = new DataInputStream( System.in );
- boolean isTestSetFinished = false;
- try
- {
- while ( MasterProcessReader.this.state.get() == RUNNABLE )
- {
- Command command = read( stdIn );
- if ( command == null )
- {
- System.err.println( "[SUREFIRE] std/in stream corrupted: first sequence not recognized" );
- break;
- }
- else
- {
- switch ( command.getCommandType() )
- {
- case TEST_SET_FINISHED:
- isTestSetFinished = true;
- wakeupIterator();
- break;
- case RUN_CLASS:
- wakeupIterator();
- break;
- case SHUTDOWN:
- insertToQueue( Command.TEST_SET_FINISHED );
- wakeupIterator();
- break;
- default:
- // checkstyle do nothing
- break;
- }
-
- insertToListeners( command );
- }
- }
- }
- catch ( EOFException e )
- {
- MasterProcessReader.this.state.set( TERMINATED );
- if ( !isTestSetFinished )
- {
- exitByConfiguration();
- // does not go to finally
- }
- }
- catch ( IOException e )
- {
- MasterProcessReader.this.state.set( TERMINATED );
- // If #stop() method is called, reader thread is interrupted and cause is InterruptedException.
- if ( !( e.getCause() instanceof InterruptedException ) )
- {
- System.err.println( "[SUREFIRE] std/in stream corrupted" );
- e.printStackTrace();
- }
- }
- finally
- {
- // ensure fail-safe iterator as well as safe to finish in for-each loop using ClassesIterator
- if ( !isTestSetFinished )
- {
- insert( Command.TEST_SET_FINISHED );
- }
- wakeupIterator();
- }
- }
-
- /**
- * thread-safety: Must be called from single thread like here the reader thread only.
- */
- private void exitByConfiguration()
- {
- Shutdown shutdown = MasterProcessReader.this.shutdown; // won't read inconsistent changes through the stack
- if ( shutdown != null )
- {
- insert( Command.TEST_SET_FINISHED ); // lazily
- wakeupIterator();
- insertToListeners( toShutdown( shutdown ) );
- switch ( shutdown )
- {
- case EXIT:
- System.exit( 1 );
- case KILL:
- Runtime.getRuntime().halt( 1 );
- case DEFAULT:
- default:
- // should not happen; otherwise you missed enum case
- break;
- }
- }
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-api/src/main/java/org/apache/maven/surefire/booter/TwoPropertiesWrapper.java
----------------------------------------------------------------------
diff --git a/surefire-api/src/main/java/org/apache/maven/surefire/booter/TwoPropertiesWrapper.java b/surefire-api/src/main/java/org/apache/maven/surefire/booter/TwoPropertiesWrapper.java
deleted file mode 100644
index 5c6e690..0000000
--- a/surefire-api/src/main/java/org/apache/maven/surefire/booter/TwoPropertiesWrapper.java
+++ /dev/null
@@ -1,50 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-/**
- * Internal generic wrapper.
- *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
- * @param <P1> first property
- * @param <P2> second property
- */
-final class TwoPropertiesWrapper<P1, P2>
-{
- private final P1 p1;
- private final P2 p2;
-
- TwoPropertiesWrapper( P1 p1, P2 p2 )
- {
- this.p1 = p1;
- this.p2 = p2;
- }
-
- P1 getP1()
- {
- return p1;
- }
-
- P2 getP2()
- {
- return p2;
- }
-}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
index a0421cc..ba1c9b7 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/ForkedBooter.java
@@ -73,7 +73,7 @@ public final class ForkedBooter
*/
public static void main( String... args )
{
- final MasterProcessReader reader = startupMasterProcessReader();
+ final CommandReader reader = startupMasterProcessReader();
final ScheduledFuture<?> pingScheduler = listenToShutdownCommands( reader );
final PrintStream originalOut = System.out;
try
@@ -155,12 +155,12 @@ public final class ForkedBooter
}
}
- private static MasterProcessReader startupMasterProcessReader()
+ private static CommandReader startupMasterProcessReader()
{
- return MasterProcessReader.getReader();
+ return CommandReader.getReader();
}
- private static ScheduledFuture<?> listenToShutdownCommands( MasterProcessReader reader )
+ private static ScheduledFuture<?> listenToShutdownCommands( CommandReader reader )
{
reader.addShutdownListener( createExitHandler( reader ) );
AtomicBoolean pingDone = new AtomicBoolean( true );
@@ -169,9 +169,9 @@ public final class ForkedBooter
0, PING_TIMEOUT_IN_SECONDS, SECONDS );
}
- private static MasterProcessListener createPingHandler( final AtomicBoolean pingDone )
+ private static CommandListener createPingHandler( final AtomicBoolean pingDone )
{
- return new MasterProcessListener()
+ return new CommandListener()
{
public void update( Command command )
{
@@ -180,9 +180,9 @@ public final class ForkedBooter
};
}
- private static MasterProcessListener createExitHandler( final MasterProcessReader reader )
+ private static CommandListener createExitHandler( final CommandReader reader )
{
- return new MasterProcessListener()
+ return new CommandListener()
{
public void update( Command command )
{
@@ -191,7 +191,7 @@ public final class ForkedBooter
};
}
- private static Runnable createPingJob( final AtomicBoolean pingDone, final MasterProcessReader reader )
+ private static Runnable createPingJob( final AtomicBoolean pingDone, final CommandReader reader )
{
return new Runnable()
{
@@ -212,7 +212,7 @@ public final class ForkedBooter
out.write( encodeBytes, 0, encodeBytes.length );
}
- private static void exit( int returnCode, Shutdown shutdownType, MasterProcessReader reader )
+ private static void exit( int returnCode, Shutdown shutdownType, CommandReader reader )
{
switch ( shutdownType )
{
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
index d3e770c..c94f032 100644
--- a/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
+++ b/surefire-booter/src/main/java/org/apache/maven/surefire/booter/LazyTestsToRun.java
@@ -32,6 +32,8 @@ import org.apache.maven.surefire.util.TestsToRun;
* The method {@link #iterator()} returns an Iterator that blocks on calls to
* {@link Iterator#hasNext()} or {@link Iterator#next()} until new classes are available, or no more
* classes will be available or the internal stream is closed.
+ * The iterator can be used only in one Thread and it is the thread which executes
+ * {@link org.apache.maven.surefire.providerapi.SurefireProvider provider implementation}.
*
* @author Andreas Gudian
* @author Tibor Digana
@@ -57,7 +59,7 @@ final class LazyTestsToRun
implements Iterator<Class<?>>
{
private final Iterator<String> it =
- MasterProcessReader.getReader().getIterableClasses( originalOutStream ).iterator();
+ CommandReader.getReader().getIterableClasses( originalOutStream ).iterator();
public boolean hasNext()
{
@@ -77,15 +79,18 @@ final class LazyTestsToRun
}
- /* (non-Javadoc)
- * @see org.apache.maven.surefire.util.TestsToRun#iterator()
- */
+ /**
+ * The iterator can be used only in one Thread.
+ * {@inheritDoc}
+ * @see org.apache.maven.surefire.util.TestsToRun#iterator()
+ * */
public Iterator<Class<?>> iterator()
{
return new BlockingIterator();
}
/* (non-Javadoc)
+ * {@inheritDoc}
* @see org.apache.maven.surefire.util.TestsToRun#toString()
*/
public String toString()
@@ -94,6 +99,7 @@ final class LazyTestsToRun
}
/* (non-Javadoc)
+ * {@inheritDoc}
* @see org.apache.maven.surefire.util.TestsToRun#allowEagerReading()
*/
public boolean allowEagerReading()
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
new file mode 100644
index 0000000..5284974
--- /dev/null
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/CommandReaderTest.java
@@ -0,0 +1,199 @@
+package org.apache.maven.surefire.booter;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.surefire.testset.TestSetFailedException;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.PrintStream;
+import java.io.UnsupportedEncodingException;
+import java.nio.ByteBuffer;
+import java.util.Iterator;
+import java.util.NoSuchElementException;
+import java.util.concurrent.BlockingQueue;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.FutureTask;
+import java.util.concurrent.LinkedBlockingQueue;
+
+import static org.apache.maven.surefire.util.internal.StringUtils.FORK_STREAM_CHARSET_NAME;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.is;
+
+/**
+ * Testing singleton {@code MasterProcessReader} in multiple class loaders.
+ *
+ * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
+ * @since 2.19
+ */
+@RunWith( NewClassLoaderRunner.class )
+public class CommandReaderTest
+{
+ private final BlockingQueue<Byte> blockingStream = new LinkedBlockingQueue<Byte>();
+ private InputStream realInputStream;
+ private CommandReader reader;
+
+ @Before
+ public void init()
+ throws UnsupportedEncodingException
+ {
+ Thread.interrupted();
+ realInputStream = System.in;
+ addTestToPipeline( getClass().getName() );
+ System.setIn( new SystemInputStream() );
+ reader = CommandReader.getReader();
+ }
+
+ @After
+ public void deinit()
+ {
+ reader.stop();
+ System.setIn( realInputStream );
+ }
+
+ @Test
+ public void readJustOneClass() throws Exception
+ {
+ Iterator<String> it = reader.getIterableClasses( new PrintStream( new ByteArrayOutputStream() ) )
+ .iterator();
+ assertTrue( it.hasNext() );
+ assertThat( it.next(), is( getClass().getName() ) );
+ reader.stop();
+ assertFalse( it.hasNext() );
+ try
+ {
+ it.next();
+ fail();
+ }
+ catch ( NoSuchElementException e )
+ {
+ // expected
+ }
+ }
+
+ @Test( expected = NoSuchElementException.class )
+ public void stopBeforeReadInThread()
+ throws Throwable
+ {
+ Runnable runnable = new Runnable()
+ {
+ public void run()
+ {
+ Iterator<String> it = reader.getIterableClasses( new PrintStream( new ByteArrayOutputStream() ) )
+ .iterator();
+ assertThat( it.next(), is( CommandReaderTest.class.getName() ) );
+ }
+ };
+ FutureTask<Object> futureTask = new FutureTask<Object>( runnable, null );
+ Thread t = new Thread( futureTask );
+ reader.stop();
+ t.start();
+ try
+ {
+ futureTask.get();
+ }
+ catch ( ExecutionException e )
+ {
+ throw e.getCause();
+ }
+ }
+
+ @Test
+ public void readTwoClassesInThread()
+ throws Throwable
+ {
+ final CountDownLatch counter = new CountDownLatch( 1 );
+ Runnable runnable = new Runnable()
+ {
+ public void run()
+ {
+ Iterator<String> it = reader.getIterableClasses( new PrintStream( new ByteArrayOutputStream() ) )
+ .iterator();
+ assertThat( it.next(), is( CommandReaderTest.class.getName() ) );
+ counter.countDown();
+ assertThat( it.next(), is( PropertiesWrapperTest.class.getName() ) );
+ }
+ };
+ FutureTask<Object> futureTask = new FutureTask<Object>( runnable, null );
+ Thread t = new Thread( futureTask );
+ t.start();
+ counter.await();
+ addTestToPipeline( PropertiesWrapperTest.class.getName() );
+ try
+ {
+ futureTask.get();
+ }
+ catch ( ExecutionException e )
+ {
+ throw e.getCause();
+ }
+ }
+
+ @Test( timeout = 15000 )
+ public void shouldAwaitReaderUp()
+ throws TestSetFailedException
+ {
+ assertTrue( reader.awaitStarted() );
+ reader.stop();
+ assertFalse( reader.awaitStarted() );
+ }
+
+ private class SystemInputStream
+ extends InputStream
+ {
+ @Override
+ public int read()
+ throws IOException
+ {
+ try
+ {
+ return CommandReaderTest.this.blockingStream.take();
+ }
+ catch ( InterruptedException e )
+ {
+ throw new IOException( e );
+ }
+ }
+ }
+
+ private void addTestToPipeline( String cls )
+ throws UnsupportedEncodingException
+ {
+ byte[] clazz = cls.getBytes( FORK_STREAM_CHARSET_NAME );
+ ByteBuffer buffer = ByteBuffer.allocate( 8 + clazz.length )
+ .putInt( MasterProcessCommand.RUN_CLASS.getId() )
+ .putInt( clazz.length )
+ .put( clazz );
+ buffer.rewind();
+ for ( ; buffer.hasRemaining(); )
+ {
+ blockingStream.add( buffer.get() );
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
index 5e84775..2bdcf21 100644
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
+++ b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/JUnit4SuiteTest.java
@@ -32,7 +32,7 @@ import org.junit.runners.Suite;
*/
@Suite.SuiteClasses( {
ClasspathTest.class,
- MasterProcessReaderTest.class,
+ CommandReaderTest.class,
PropertiesWrapperTest.class,
SurefireReflectorTest.class
} )
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-booter/src/test/java/org/apache/maven/surefire/booter/MasterProcessReaderTest.java
----------------------------------------------------------------------
diff --git a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/MasterProcessReaderTest.java b/surefire-booter/src/test/java/org/apache/maven/surefire/booter/MasterProcessReaderTest.java
deleted file mode 100644
index fc56421..0000000
--- a/surefire-booter/src/test/java/org/apache/maven/surefire/booter/MasterProcessReaderTest.java
+++ /dev/null
@@ -1,254 +0,0 @@
-package org.apache.maven.surefire.booter;
-
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements. See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership. The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License. You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied. See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import org.apache.maven.surefire.testset.TestSetFailedException;
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.PrintStream;
-import java.io.UnsupportedEncodingException;
-import java.nio.ByteBuffer;
-import java.util.Arrays;
-import java.util.Iterator;
-import java.util.NoSuchElementException;
-import java.util.Random;
-import java.util.concurrent.BlockingQueue;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
-import java.util.concurrent.FutureTask;
-import java.util.concurrent.LinkedBlockingQueue;
-import java.util.concurrent.TimeUnit;
-
-import static org.apache.maven.surefire.util.internal.StringUtils.FORK_STREAM_CHARSET_NAME;
-import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertTrue;
-import static org.junit.Assert.fail;
-import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.*;
-
-/**
- * Testing singleton {@code MasterProcessReader} in multiple class loaders.
- *
- * @author <a href="mailto:tibordigana@apache.org">Tibor Digana (tibor17)</a>
- * @since 2.19
- */
-@RunWith( NewClassLoaderRunner.class )
-public class MasterProcessReaderTest
-{
- private final BlockingQueue<Byte> blockingStream = new LinkedBlockingQueue<Byte>();
- private InputStream realInputStream;
- private MasterProcessReader reader;
-
- @Before
- public void init()
- throws UnsupportedEncodingException
- {
- Thread.interrupted();
- realInputStream = System.in;
- addThisTestToPipeline( getClass().getName() );
- System.setIn( new SystemInputStream() );
- reader = MasterProcessReader.getReader();
- }
-
- @After
- public void deinit()
- {
- reader.stop();
- System.setIn( realInputStream );
- }
-
- @Test
- public void readJustOneClass() throws Exception
- {
- Iterator<String> it = reader.getIterableClasses( new PrintStream( new ByteArrayOutputStream() ) )
- .iterator();
- assertTrue( it.hasNext() );
- assertThat( it.next(), is( getClass().getName() ) );
- reader.stop();
- assertFalse( it.hasNext() );
- try
- {
- it.next();
- fail();
- }
- catch ( NoSuchElementException e )
- {
- // expected
- }
- }
-
- @Test( expected = NoSuchElementException.class )
- public void stopBeforeReadInThread()
- throws Throwable
- {
- Runnable runnable = new Runnable()
- {
- public void run()
- {
- Iterator<String> it = reader.getIterableClasses( new PrintStream( new ByteArrayOutputStream() ) )
- .iterator();
- assertThat( it.next(), is( MasterProcessReaderTest.class.getName() ) );
- }
- };
- FutureTask<Object> futureTask = new FutureTask<Object>( runnable, null );
- Thread t = new Thread( futureTask );
- reader.stop();
- t.start();
- try
- {
- futureTask.get();
- }
- catch ( ExecutionException e )
- {
- throw e.getCause();
- }
- }
-
- @Test
- public void readTwoClassesInThread()
- throws Throwable
- {
- final CountDownLatch counter = new CountDownLatch( 1 );
- Runnable runnable = new Runnable()
- {
- public void run()
- {
- Iterator<String> it = reader.getIterableClasses( new PrintStream( new ByteArrayOutputStream() ) )
- .iterator();
- assertThat( it.next(), is( MasterProcessReaderTest.class.getName() ) );
- counter.countDown();
- assertThat( it.next(), is( PropertiesWrapperTest.class.getName() ) );
- }
- };
- FutureTask<Object> futureTask = new FutureTask<Object>( runnable, null );
- Thread t = new Thread( futureTask );
- t.start();
- counter.await();
- addThisTestToPipeline( PropertiesWrapperTest.class.getName() );
- try
- {
- futureTask.get();
- }
- catch ( ExecutionException e )
- {
- throw e.getCause();
- }
- }
-
- // @Test
- public void readTwoClassesInTwoThreads()
- throws Throwable
- {
- final Iterable<String> lazyTestSet =
- reader.getIterableClasses( new PrintStream( new ByteArrayOutputStream() ) );
-
- class Provider implements Runnable
- {
- public void run()
- {
- Iterator<String> it = lazyTestSet.iterator();
- assertThat( it.next(), is( MasterProcessReaderTest.class.getName() ) );
- assertThat( it.next(), is( PropertiesWrapperTest.class.getName() ) );
- assertFalse( it.hasNext() );
- }
- }
-
- ExecutorService es = Executors.newFixedThreadPool( 2 );
- Future<?> result1 = es.submit( new Provider() );
- Random rnd = new Random();
- TimeUnit.MILLISECONDS.sleep( rnd.nextInt( 50 ) );
- Future<?> result2 = es.submit( new Provider() );
- TimeUnit.MILLISECONDS.sleep( rnd.nextInt( 50 ) );
- addThisTestToPipeline( PropertiesWrapperTest.class.getName() );
- TimeUnit.MILLISECONDS.sleep( rnd.nextInt( 50 ) );
- addTestSetFinishedToPipeline();
- for ( Future<?> result : Arrays.asList( result1, result2 ) )
- {
- try
- {
- result.get();
- }
- catch ( ExecutionException e )
- {
- throw e.getCause();
- }
- }
- }
-
- @Test( timeout = 15000 )
- public void shouldAwaitReaderUp()
- throws TestSetFailedException
- {
- assertTrue( reader.awaitStarted() );
- reader.stop();
- assertFalse( reader.awaitStarted() );
- }
-
- private class SystemInputStream
- extends InputStream
- {
- @Override
- public int read()
- throws IOException
- {
- try
- {
- return MasterProcessReaderTest.this.blockingStream.take();
- }
- catch ( InterruptedException e )
- {
- throw new IOException( e );
- }
- }
- }
-
- private void addThisTestToPipeline( String cls )
- throws UnsupportedEncodingException
- {
- byte[] clazz = cls.getBytes( FORK_STREAM_CHARSET_NAME );
- ByteBuffer buffer = ByteBuffer.allocate( 8 + clazz.length )
- .putInt( MasterProcessCommand.RUN_CLASS.getId() )
- .putInt( clazz.length )
- .put( clazz );
- buffer.rewind();
- for ( ; buffer.hasRemaining(); )
- {
- blockingStream.add( buffer.get() );
- }
- }
-
- private void addTestSetFinishedToPipeline()
- throws UnsupportedEncodingException
- {
- for ( byte b : MasterProcessCommand.TEST_SET_FINISHED.encode() )
- {
- blockingStream.add( b );
- }
- }
-}
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
index 948d514..85b2a98 100644
--- a/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
+++ b/surefire-providers/surefire-junit4/src/main/java/org/apache/maven/surefire/junit4/JUnit4Provider.java
@@ -20,8 +20,8 @@ package org.apache.maven.surefire.junit4;
*/
import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.booter.MasterProcessListener;
-import org.apache.maven.surefire.booter.MasterProcessReader;
+import org.apache.maven.surefire.booter.CommandListener;
+import org.apache.maven.surefire.booter.CommandReader;
import org.apache.maven.surefire.common.junit4.ClassMethod;
import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
import org.apache.maven.surefire.common.junit4.JUnit4TestChecker;
@@ -92,14 +92,14 @@ public class JUnit4Provider
private final int rerunFailingTestsCount;
- private final MasterProcessReader commandsReader;
+ private final CommandReader commandsReader;
private TestsToRun testsToRun;
public JUnit4Provider( ProviderParameters booterParameters )
{
// don't start a thread in MasterProcessReader while we are in in-plugin process
- commandsReader = booterParameters.isInsideFork() ? MasterProcessReader.getReader().setShutdown(
+ commandsReader = booterParameters.isInsideFork() ? CommandReader.getReader().setShutdown(
booterParameters.getShutdown() ) : null;
providerParameters = booterParameters;
testClassLoader = booterParameters.getTestClassLoader();
@@ -170,7 +170,7 @@ public class JUnit4Provider
if ( commandsReader != null )
{
- commandsReader.addShutdownListener( new MasterProcessListener()
+ commandsReader.addShutdownListener( new CommandListener()
{
public void update( Command command )
{
@@ -224,9 +224,9 @@ public class JUnit4Provider
}
}
- private MasterProcessListener registerPleaseStopJunitListener( final Notifier notifier )
+ private CommandListener registerPleaseStopJunitListener( final Notifier notifier )
{
- MasterProcessListener listener = new MasterProcessListener()
+ CommandListener listener = new CommandListener()
{
public void update( Command command )
{
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
index f2fa49f..5e03bff 100644
--- a/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
+++ b/surefire-providers/surefire-junit47/src/main/java/org/apache/maven/surefire/junitcore/JUnitCoreProvider.java
@@ -20,8 +20,8 @@ package org.apache.maven.surefire.junitcore;
*/
import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.booter.MasterProcessListener;
-import org.apache.maven.surefire.booter.MasterProcessReader;
+import org.apache.maven.surefire.booter.CommandListener;
+import org.apache.maven.surefire.booter.CommandReader;
import org.apache.maven.surefire.common.junit4.JUnit4RunListener;
import org.apache.maven.surefire.common.junit4.JUnitTestFailureListener;
import org.apache.maven.surefire.common.junit4.Notifier;
@@ -83,14 +83,14 @@ public class JUnitCoreProvider
private final TestListResolver testResolver;
- private final MasterProcessReader commandsReader;
+ private final CommandReader commandsReader;
private TestsToRun testsToRun;
public JUnitCoreProvider( ProviderParameters providerParameters )
{
commandsReader = providerParameters.isInsideFork()
- ? MasterProcessReader.getReader().setShutdown( providerParameters.getShutdown() )
+ ? CommandReader.getReader().setShutdown( providerParameters.getShutdown() )
: null;
this.providerParameters = providerParameters;
testClassLoader = providerParameters.getTestClassLoader();
@@ -167,7 +167,7 @@ public class JUnitCoreProvider
if ( commandsReader != null )
{
- commandsReader.addShutdownListener( new MasterProcessListener()
+ commandsReader.addShutdownListener( new CommandListener()
{
public void update( Command command )
{
@@ -225,9 +225,9 @@ public class JUnitCoreProvider
}
}
- private MasterProcessListener registerPleaseStopJunitListener( final Notifier stoppable )
+ private CommandListener registerPleaseStopJunitListener( final Notifier stoppable )
{
- MasterProcessListener listener = new MasterProcessListener()
+ CommandListener listener = new CommandListener()
{
public void update( Command command )
{
http://git-wip-us.apache.org/repos/asf/maven-surefire/blob/0693bd90/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
----------------------------------------------------------------------
diff --git a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
index 75d8463..5444fde 100644
--- a/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
+++ b/surefire-providers/surefire-testng/src/main/java/org/apache/maven/surefire/testng/TestNGProvider.java
@@ -20,8 +20,8 @@ package org.apache.maven.surefire.testng;
*/
import org.apache.maven.surefire.booter.Command;
-import org.apache.maven.surefire.booter.MasterProcessListener;
-import org.apache.maven.surefire.booter.MasterProcessReader;
+import org.apache.maven.surefire.booter.CommandListener;
+import org.apache.maven.surefire.booter.CommandReader;
import org.apache.maven.surefire.cli.CommandLineOption;
import org.apache.maven.surefire.providerapi.AbstractProvider;
import org.apache.maven.surefire.providerapi.ProviderParameters;
@@ -66,7 +66,7 @@ public class TestNGProvider
private final List<CommandLineOption> mainCliOptions;
- private final MasterProcessReader commandsReader;
+ private final CommandReader commandsReader;
private TestsToRun testsToRun;
@@ -74,7 +74,7 @@ public class TestNGProvider
{
// don't start a thread in MasterProcessReader while we are in in-plugin process
commandsReader = booterParameters.isInsideFork()
- ? MasterProcessReader.getReader().setShutdown( booterParameters.getShutdown() )
+ ? CommandReader.getReader().setShutdown( booterParameters.getShutdown() )
: null;
providerParameters = booterParameters;
testClassLoader = booterParameters.getTestClassLoader();
@@ -140,7 +140,7 @@ public class TestNGProvider
if ( commandsReader != null )
{
- commandsReader.addShutdownListener( new MasterProcessListener()
+ commandsReader.addShutdownListener( new CommandListener()
{
public void update( Command command )
{
@@ -189,9 +189,9 @@ public class TestNGProvider
}
}
- private MasterProcessListener registerPleaseStopListener()
+ private CommandListener registerPleaseStopListener()
{
- MasterProcessListener listener = new MasterProcessListener()
+ CommandListener listener = new CommandListener()
{
public void update( Command command )
{