/********************************************************************************
* CruiseControl, a Continuous Integration Toolkit
* Copyright (c) 2006, ThoughtWorks, Inc.
* 200 E. Randolph, 25th Floor
* Chicago, IL 60601 USA
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* + Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* + Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
*
* + Neither the name of ThoughtWorks, Inc., CruiseControl, nor the
* names of its contributors may be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
********************************************************************************/
package net.sourceforge.cruisecontrol.util;
import junit.framework.TestCase;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
public class ProcessesTest extends TestCase {
public void testShouldReturnProcessWhenCommandProvided() throws IOException {
Processes.setRuntime(new MockExecutor());
Commandline c = new Commandline() {
};
assertNotNull(Processes.execute(c));
}
public void testShouldStartStreamPumperForErrorStream() throws Exception {
Processes.setRuntime(new MockExecutor());
Commandline c = new Commandline();
c.setExecutable("UnitTestDummyExcectuable");
// try to ensure pending thread cleanups occur, so thread counts include only threads
// created by this unit test
System.gc();
System.gc();
Runtime.getRuntime().runFinalization();
System.gc();
System.gc();
Thread.sleep(500);
int preCount = Thread.activeCount();
assertNotNull(Processes.execute(c));
// allow some time for thread to spin up. can be longer in java 5
int postCount = Thread.activeCount();
int waitCount = 0;
while ((postCount <= preCount) && (waitCount < 40)) {
waitCount++;
Thread.sleep(10);
postCount = Thread.activeCount();
}
final String msg = "A StreamPumper Thread wasn't started. postCount: " + postCount
+ "; preCount: " + preCount + "; waitCount: " + waitCount
+ (postCount < preCount
? "\n\tWARNING: Thread counts might include threads from prior tests."
: "");
assertTrue(msg, postCount > preCount);
}
public void testShouldCloseStreamsWhenExecutingFully() throws IOException, InterruptedException {
MockExecutor executor = new MockExecutor();
Processes.setRuntime(executor);
Commandline c = new Commandline() {
};
Processes.executeFully(c);
assertTrue(executor.streamsClosed());
}
private static class CloseableProcess extends MockProcess {
private CloseAwareInputStream error = new CloseAwareInputStream(4 * 1000);
private CloseAwareInputStream input = new CloseAwareInputStream(4 * 1000);
private CloseAwareOutputStream output = new CloseAwareOutputStream();
public CloseableProcess() {
super();
setErrorStream(error);
setInputStream(input);
setOutputStream(output);
}
public boolean streamsClosed() {
return error.isClosed() && input.isClosed() && output.isClosed();
}
}
private static final class CloseAwareInputStream extends InputStream {
private final int millisTillEndOfStream;
private long starttime;
private boolean closed;
private CloseAwareInputStream(final int millisTillEndOfStream) {
this.millisTillEndOfStream = millisTillEndOfStream;
}
public void close() throws IOException {
closed = true;
}
public boolean isClosed() {
return closed;
}
public int read() throws IOException {
if (starttime == 0) {
starttime = System.currentTimeMillis();
}
if ((System.currentTimeMillis() - starttime) < millisTillEndOfStream) {
Thread.yield();
//return 0;
// return a character value that allows Readers to read a new line,
// otherwise they buffer all reads until the final -1.
return '\n';
}
return -1;
}
}
static class CloseAwareOutputStream extends OutputStream {
private boolean closed;
public void close() throws IOException {
closed = true;
}
public boolean isClosed() {
return closed;
}
public void write(int i) throws IOException {
}
}
private static class MockExecutor implements Executor {
private CloseableProcess mockProcess;
public Process exec(Commandline c) throws IOException {
mockProcess = new CloseableProcess();
return mockProcess;
}
public boolean streamsClosed() {
return mockProcess.streamsClosed();
}
}
}