Repository: xlate/staedi
Pull request #429 (2023-12-31 12:01:03)

Fix write binary data from ByteBuffer, use underlying stream directly

sonarcloud[bot]: ## [![Quality Gate Failed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-failed-20px.png 'Quality Gate Failed')](https://sonarcloud.io/dashboard?id=xlate_staedi&pullRequest=429) **Quality Gate failed**  
Failed conditions

[83.3% Coverage on New Code](https://sonarcloud.io/component_measures?id=xlate_staedi&pullRequest=429&metric=new_coverage&view=list) (required ≥ 90%)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=xlate_staedi&pullRequest=429)


sonarcloud[bot]: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=xlate_staedi&pullRequest=429) **Quality Gate passed**  
Kudos, no new issues were introduced!

[0 New issues](https://sonarcloud.io/project/issues?id=xlate_staedi&pullRequest=429&resolved=false&inNewCodePeriod=true)  
[0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=xlate_staedi&pullRequest=429&resolved=false&inNewCodePeriod=true)  
[100.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=xlate_staedi&pullRequest=429&metric=new_coverage&view=list)  
[0.0% Duplication on New Code](https://sonarcloud.io/component_measures?id=xlate_staedi&pullRequest=429&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarCloud](https://sonarcloud.io/dashboard?id=xlate_staedi&pullRequest=429)



Patch:
diff --git a/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamWriter.java b/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamWriter.java
index fc88d0b..8f9db34 100644
--- a/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamWriter.java
+++ b/src/main/java/io/xlate/edi/internal/stream/StaEDIStreamWriter.java
@@ -814,12 +814,10 @@ public class StaEDIStreamWriter implements EDIStreamWriter, ElementDataHandler,
             flush(); // Write `Writer` buffers to stream before writing binary
 
             while ((output = binaryStream.read()) != -1) {
-                location.incrementOffset(output);
-                stream.write(output);
-                elementLength++;
+                writeBinary(output);
             }
         } catch (IOException e) {
-            throw new EDIStreamException("Exception writing binary element data", location, e);
+            throw new EDIStreamException("Exception reading binary data source for output", location, e);
         }
 
         return this;
@@ -831,17 +829,10 @@ public class StaEDIStreamWriter implements EDIStreamWriter, ElementDataHandler,
         ensureState(State.ELEMENT_DATA_BINARY);
         ensureArgs(binary.length, start, end);
         writeRequiredSeparators(end - start);
+        flush(); // Write `Writer` buffers to stream before writing binary
 
-        try {
-            flush(); // Write `Writer` buffers to stream before writing binary
-
-            for (int i = start; i < end; i++) {
-                location.incrementOffset(binary[i]);
-                stream.write(binary[i]);
-                elementLength++;
-            }
-        } catch (IOException e) {
-            throw new EDIStreamException("Exception writing binary element data", location, e);
+        for (int i = start; i < end; i++) {
+            writeBinary(binary[i]);
         }
 
         return this;
@@ -852,15 +843,27 @@ public class StaEDIStreamWriter implements EDIStreamWriter, ElementDataHandler,
         ensureLevel(LEVEL_ELEMENT);
         ensureState(State.ELEMENT_DATA_BINARY);
         writeRequiredSeparators(binary.remaining());
+        flush(); // Write `Writer` buffers to stream before writing binary
 
         while (binary.hasRemaining()) {
-            write(binary.get());
-            elementLength++;
+            writeBinary(binary.get());
         }
 
         return this;
     }
 
+    private void writeBinary(int output) throws EDIStreamException {
+        location.incrementOffset(output);
+
+        try {
+            stream.write(output);
+        } catch (IOException e) {
+            throw new EDIStreamException("Exception writing binary element data", location, e);
+        }
+
+        elementLength++;
+    }
+
     @Override
     public boolean binaryData(InputStream binary) {
         // No operation


Test patch:
diff --git a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamWriterTest.java b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamWriterTest.java
index 0b76778..3e1f903 100644
--- a/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamWriterTest.java
+++ b/src/test/java/io/xlate/edi/internal/stream/StaEDIStreamWriterTest.java
@@ -40,6 +40,7 @@ import java.util.Arrays;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.stream.Stream;
 
 import org.junit.jupiter.api.Test;
@@ -657,7 +658,7 @@ class StaEDIStreamWriterTest {
     }
 
     @Test
-    void testWriteBinaryDataInputStreamIOException() throws Exception {
+    void testWriteBinaryDataInputStreamReadIOException() throws Exception {
         EDIOutputFactory factory = EDIOutputFactory.newFactory();
         ByteArrayOutputStream stream = new ByteArrayOutputStream(4096);
         EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
@@ -672,6 +673,41 @@ class StaEDIStreamWriterTest {
         writer.writeElementData("4");
         writer.endElement();
         writer.writeStartElementBinary();
+        EDIStreamException thrown = assertThrows(EDIStreamException.class,
+                                                 () -> writer.writeBinaryData(binaryStream));
+        assertEquals("Exception reading binary data source for output in segment BIN at position 2, element 2",
+                     thrown.getMessage());
+        assertSame(ioException, thrown.getCause());
+    }
+
+    @Test
+    void testWriteBinaryDataInputStreamWriteIOException() throws Exception {
+        EDIOutputFactory factory = EDIOutputFactory.newFactory();
+        ByteArrayOutputStream stream = Mockito.spy(new ByteArrayOutputStream(4096));
+        EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
+
+        byte[] binary = { '\n', 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, '\t' };
+        InputStream binaryStream = new ByteArrayInputStream(binary);
+
+        AtomicBoolean binaryElementStarted = new AtomicBoolean(false);
+        IOException ioException = new IOException();
+        Mockito.doAnswer(args -> {
+            if (binaryElementStarted.get()) {
+                throw ioException;
+            }
+            args.callRealMethod();
+            return null;
+        }).when(stream).write(0x00); // fail on second byte of binary stream
+
+        writer.startInterchange();
+        writeHeader(writer);
+        stream.reset();
+        writer.writeStartSegment("BIN");
+        writer.writeStartElement();
+        writer.writeElementData("4");
+        writer.endElement();
+        writer.writeStartElementBinary();
+        binaryElementStarted.set(true);
         EDIStreamException thrown = assertThrows(EDIStreamException.class,
                                                  () -> writer.writeBinaryData(binaryStream));
         assertEquals("Exception writing binary element data in segment BIN at position 2, element 2",
@@ -684,21 +720,21 @@ class StaEDIStreamWriterTest {
         EDIOutputFactory factory = EDIOutputFactory.newFactory();
         ByteArrayOutputStream stream = new ByteArrayOutputStream(4096);
         EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
-        byte[] binary = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A' };
+        byte[] binary = { '\n', 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, '\t' };
         writer.startInterchange();
         writeHeader(writer);
         writer.flush();
         stream.reset();
         writer.writeStartSegment("BIN");
         writer.writeStartElement();
-        writer.writeElementData("11");
+        writer.writeElementData("8");
         writer.endElement();
         writer.writeStartElementBinary();
         writer.writeBinaryData(binary, 0, binary.length);
         writer.endElement();
         writer.writeEndSegment();
         writer.flush();
-        assertEquals("BIN*11*0123456789A~", stream.toString());
+        assertEquals("BIN*8*\n\u0000\u0001\u0002\u0003\u0004\u0005\t~", stream.toString());
     }
 
     @Test
@@ -706,7 +742,7 @@ class StaEDIStreamWriterTest {
         EDIOutputFactory factory = EDIOutputFactory.newFactory();
         ByteArrayOutputStream stream = new ByteArrayOutputStream(4096);
         EDIStreamWriter writer = factory.createEDIStreamWriter(stream);
-        byte[] binary = { 'B', 'U', 'S', 'T', 'M', 'Y', 'B', 'U', 'F', 'F', 'E', 'R', 'S', '\n' };
+        byte[] binary = { '\n', 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, '\t' };
         ByteBuffer buffer = ByteBuffer.wrap(binary);
         writer.startInterchange();
         writeHeader(writer);
@@ -714,14 +750,14 @@ class StaEDIStreamWriterTest {
         stream.reset();
         writer.writeStartSegment("BIN");
         writer.writeStartElement();
-        writer.writeElementData("14");
+        writer.writeElementData("8");
         writer.endElement();
         writer.writeStartElementBinary();
         writer.writeBinaryData(buffer);
         writer.endElement();
         writer.writeEndSegment();
         writer.flush();
-        assertEquals("BIN*14*BUSTMYBUFFERS\n~", stream.toString());
+        assertEquals("BIN*8*\n\u0000\u0001\u0002\u0003\u0004\u0005\t~", stream.toString());
     }
 
     @ParameterizedTest