package edu.mayo.bior.pipeline;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.fail;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.util.NoSuchElementException;
import org.junit.Before;
import org.junit.Test;
import com.tinkerpop.pipes.AbstractPipe;
import com.tinkerpop.pipes.Pipe;
import edu.mayo.cli.InvalidDataException;
import edu.mayo.pipes.exceptions.InvalidPipeInputException;
import edu.mayo.pipes.history.History;
public class UnixStreamPipelineTest {
private UnixStreamPipeline mPipeline;
@Before
public void setUp() throws Exception {
mPipeline = new UnixStreamPipeline();
}
@Test
/**
* Creates a MOCK history, runs the UnixStreamPipeline that modifies the
* history, and then checks that the modified history is correct.
*
* @throws IOException
*/
public void testExecute() throws IOException, InvalidDataException {
final String history =
"##Directive\n" +
"#COL1\tCOL2\tCOL3\n" +
"A\tB\tC";
// create pipe that will modify history by appending a suffix to each item
Pipe<History, History> pipe = new AppendSuffixPipe("_MODIFIED");
String historyModified = run(history, pipe);
String[] lines = historyModified.split("\n");
assertEquals(3, lines.length);
assertEquals("##Directive", lines[0].trim());
assertEquals("#COL1\tCOL2\tCOL3", lines[1].trim());
assertEquals("A_MODIFIED\tB_MODIFIED\tC_MODIFIED", lines[2].trim());
}
/**
* Helper method that runs the UnixStreamPipeline with the given history and logic pipe.
* @param history
* @param pipe
* @return
* @throws IOException
* @throws InvalidDataException
*/
private String run(String history, Pipe pipe) throws IOException, InvalidDataException {
// create IN/OUT streams to be used by UnixStreamPipeline
InputStream inStream = new ByteArrayInputStream(history.getBytes());
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
// save default streams for STDIN and STDOUT
final InputStream DEFAULT_STDIN = System.in;
final PrintStream DEFAULT_STDOUT = System.out;
// override STDIN/STDOUT defaults for java
System.setIn(inStream);
System.setOut(new PrintStream(outStream, true));
// run UnixStreamPipeline
mPipeline.execute(pipe);
// flush stream and grab the modified history
outStream.flush();
String historyModified = outStream.toString().trim();
outStream.close();
// reset streams back to defaults
System.setIn(DEFAULT_STDIN);
System.setOut(DEFAULT_STDOUT);
return historyModified;
}
/** Test invalid lines - this should only show a warning, but not fail the pipeline (since pipeline runs to completion. */
@Test
public void testInvalidInput() throws IOException {
final String history =
"##Directive\n" +
"#COL1\tCOL2\tCOL3\n" +
"A\tB\tC\n" +
"1\t2\t3";
Pipe<History, History> pipe = new InvalidInputPipe();
final String expected =
"##Directive\n" +
"#COL1\tCOL2\tCOL3\n" +
"1\t2\t3";
try {
// run UnixStreamPipeline
String out = run(history, pipe);
System.out.println("\nOutput:\n" + out);
assertEquals(expected, out);
System.out.println("Test passed. InvalidDataException was caught and handled correctly as just a warning, not an error.");
} catch (InvalidDataException e) {
fail("FAILED: testInvalidInput - one or more invalid lines should not fail the pipeline.");
}
}
/** Test an unrecoverable error - this will happen on the first line and fail the pipeline,
* preventing subsequent lines from being read.
* @throws InvalidDataException */
@Test
public void testDeadlyError() throws IOException, InvalidDataException {
final String history =
"##Directive\n" +
"#COL1\tCOL2\tCOL3\n" +
"A\tB\tC" +
"1\t2\t3";
Pipe<History, History> pipe = new DeadlyErrorPipe();
try {
// run UnixStreamPipeline
String out = run(history, pipe);
System.out.println("output = " + out);
fail("FAILED: testDeadlyError() - a NullPointerException should kill the pipeline, but did not.");
} catch (NullPointerException e) {
System.out.println("PASSED: testDeadlyError() passed. NullPointerException was expected..");
}
}
/**
* Simple pipe that appends the given SUFFIX to the end of each item in the history.
*/
class AppendSuffixPipe extends AbstractPipe<History, History> {
private String mSuffix;
public AppendSuffixPipe(String suffix) {
this.mSuffix = suffix;
}
@Override
protected History processNextStart() throws NoSuchElementException {
if(this.starts.hasNext()){
History history = this.starts.next();
for (int i=0; i < history.size(); i++) {
String value = history.get(i) + mSuffix;
history.set(i, value);
}
return history;
} else {
throw new NoSuchElementException();
}
}
}
/**
* Pipe that immediately throws an InvalidPipeInputException for 1st history, but allows subsequent lines to be processed
*/
class InvalidInputPipe extends AbstractPipe<History, History> {
private boolean mIsFirst = true;
@Override
protected History processNextStart() throws NoSuchElementException, InvalidPipeInputException {
History h = this.starts.next();
if (mIsFirst) {
mIsFirst = false;
throw new InvalidPipeInputException("message", this);
}
return h;
}
}
/** Pipe that immediately throws a NullPointerException which will end the pipeline, and not allow further processing */
class DeadlyErrorPipe extends AbstractPipe<History, History> {
private boolean mIsFirst = true;
@Override
protected History processNextStart() throws NoSuchElementException, InvalidPipeInputException {
History h = this.starts.next();
if (mIsFirst) {
mIsFirst = false;
throw new NullPointerException("This should be an unrecoverable error, and end the pipeline");
}
return h;
}
}
}