diff --git a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/FSHRunner.java b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/FSHRunner.java index 63c69803b..8bff7d2cc 100644 --- a/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/FSHRunner.java +++ b/org.hl7.fhir.publisher.core/src/main/java/org/hl7/fhir/igtools/publisher/FSHRunner.java @@ -20,6 +20,8 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Objects; +import java.util.function.Consumer; import java.util.StringJoiner; public class FSHRunner { @@ -63,7 +65,7 @@ protected void runFsh(File file, Publisher.IGBuildMode mode) throws IOException log("Run Sushi on "+file.getAbsolutePath()); DefaultExecutor exec = new DefaultExecutor(); exec.setExitValue(0); - MySushiHandler pumpHandler = new MySushiHandler(); + MySushiHandler pumpHandler = new MySushiHandler(this::log); PumpStreamHandler pump = new PumpStreamHandler(pumpHandler); exec.setStreamHandler(pump); exec.setWorkingDirectory(file); @@ -96,11 +98,12 @@ protected void runFsh(File file, Publisher.IGBuildMode mode) throws IOException log("Exception: "+ioex.getMessage()); throw ioex; } - if (pumpHandler.errorCount > 0) { + if (pumpHandler.getErrorCount() > 0) { throw new IOException("Sushi failed with errors. Complete output from running Sushi : " + pumpHandler.getBufferString()); } } + @Nonnull protected CommandLine getDefaultCommandLine(String fshVersion, Publisher.IGBuildMode mode) { final List sushiCommandList = getSushiCommandList(fshVersion, mode); @@ -147,40 +150,45 @@ protected String getSushiCommandString(String fshVersion, Publisher.IGBuildMode } return stringJoiner.toString(); } - public class MySushiHandler extends OutputStream { + + public static class MySushiHandler extends OutputStream { - private byte[] buffer; - private int length; + private final StringBuilder buffer; private int errorCount = -1; + private final Consumer outputConsumer; - public MySushiHandler() { - buffer = new byte[256]; + public MySushiHandler(final Consumer outputConsumer) { + buffer = new StringBuilder(256); + this.outputConsumer = Objects.requireNonNull(outputConsumer); } public String getBufferString() { - return new String(this.buffer, 0, length); + return this.buffer.toString(); } private boolean passSushiFilter(String s) { - if (Utilities.noString(s)) + if (Utilities.noString(s) || s.isBlank()) return false; return true; } @Override public void write(int b) throws IOException { - buffer[length] = (byte) b; - length++; + this.buffer.appendCodePoint(b); if (b == 10) { // eoln - String s = new String(buffer, 0, length); + final String s = this.getBufferString(); if (passSushiFilter(s)) { - log("Sushi: "+ StringUtils.stripEnd(s, null)); + this.outputConsumer.accept("Sushi: "+ StringUtils.stripEnd(s, null)); if (s.trim().startsWith("Errors:")) { errorCount = Integer.parseInt(s.substring(10).trim()); } } - length = 0; + this.buffer.setLength(0); } } + + protected int getErrorCount() { + return errorCount; + } } } diff --git a/org.hl7.fhir.publisher.core/src/test/java/org/hl7/fhir/igtools/publisher/FSHRunnerTest.java b/org.hl7.fhir.publisher.core/src/test/java/org/hl7/fhir/igtools/publisher/FSHRunnerTest.java new file mode 100644 index 000000000..e6fcf76e8 --- /dev/null +++ b/org.hl7.fhir.publisher.core/src/test/java/org/hl7/fhir/igtools/publisher/FSHRunnerTest.java @@ -0,0 +1,72 @@ +package org.hl7.fhir.igtools.publisher; + +import org.junit.jupiter.api.Test; + +import javax.annotation.Nonnull; +import java.io.IOException; +import java.util.function.Consumer; + +import static org.junit.jupiter.api.Assertions.*; + +/** + * Tests for the {@link FSHRunner} class. + * + * @author Quentin Ligier + **/ +class FSHRunnerTest { + + @Test + void testMySushiHandler() throws IOException { + + final StringBuilder stringBuilder = new StringBuilder(512); + final FSHRunner.MySushiHandler mySushiHandler = getMySushiHandler(stringBuilder); + for (final int codePoint : "1234".codePoints().toArray()) { + mySushiHandler.write(codePoint); + } + assertEquals("1234", mySushiHandler.getBufferString()); + assertEquals(0, stringBuilder.length()); + mySushiHandler.write(10); // EOL + assertEquals("", mySushiHandler.getBufferString()); + assertEquals("Sushi: 1234", stringBuilder.toString()); + } + + @Nonnull + private static FSHRunner.MySushiHandler getMySushiHandler(StringBuilder stringBuilder) { + final Consumer stringConsumer = stringBuilder::append; + return new FSHRunner.MySushiHandler(stringConsumer); + } + + @Test + public void testMySushiHandlerLongStrings() throws IOException { + final StringBuilder stringBuilder = new StringBuilder(512); + final FSHRunner.MySushiHandler mySushiHandler = getMySushiHandler(stringBuilder); + // Test long strings, larger than the initial buffer size + final int largeLength = 500; + for (int i = 0; i < largeLength; ++i) { + mySushiHandler.write('a'); + } + assertEquals(largeLength, mySushiHandler.getBufferString().length()); + assertEquals(0, stringBuilder.length()); + mySushiHandler.write(10); // EOL + assertEquals("", mySushiHandler.getBufferString()); + assertEquals(largeLength + 7, stringBuilder.toString().length()); + } + + @Test + void testMySushiHandlerErrors() throws IOException { + + final StringBuilder stringBuilder = new StringBuilder(512); + final FSHRunner.MySushiHandler mySushiHandler = getMySushiHandler(stringBuilder); + for (final int codePoint : "1234".codePoints().toArray()) { + mySushiHandler.write(codePoint); + } + mySushiHandler.write(10); // EOL + for (final int codePoint : " Errors: 13".codePoints().toArray()) { + mySushiHandler.write(codePoint); + } + mySushiHandler.write(10); // EOL + assertEquals("", mySushiHandler.getBufferString()); + assertEquals("Sushi: 1234Sushi: Errors: 13", stringBuilder.toString()); + assertEquals(13, mySushiHandler.getErrorCount()); + } +}