// Copyright 2014 The Bazel Authors. All rights reserved. // // Licensed 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. package com.google.devtools.build.lib.util.io; import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertEquals; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; import java.io.ByteArrayOutputStream; import java.io.OutputStream; import java.io.UnsupportedEncodingException; import java.nio.charset.Charset; import java.util.Random; /** * Tests {@link StreamDemultiplexer}. */ @RunWith(JUnit4.class) public class StreamDemultiplexerTest { private ByteArrayOutputStream out = new ByteArrayOutputStream(); private ByteArrayOutputStream err = new ByteArrayOutputStream(); private ByteArrayOutputStream ctl = new ByteArrayOutputStream(); private String toAnsi(ByteArrayOutputStream stream) { try { return new String(stream.toByteArray(), "ISO-8859-1"); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); } } private byte[] inAnsi(String string) { try { return string.getBytes("ISO-8859-1"); } catch (UnsupportedEncodingException e) { throw new AssertionError(e); } } @Test public void testHelloWorldOnStandardOut() throws Exception { byte[] multiplexed = chunk(1, "Hello, world."); try (final StreamDemultiplexer demux = new StreamDemultiplexer((byte) 1, out)) { demux.write(multiplexed); } assertEquals("Hello, world.", out.toString("ISO-8859-1")); } @Test public void testOutErrCtl() throws Exception { byte[] multiplexed = concat(chunk(1, "out"), chunk(2, "err"), chunk(3, "ctl")); try (final StreamDemultiplexer demux = new StreamDemultiplexer((byte) 1, out, err, ctl)) { demux.write(multiplexed); } assertEquals("out", toAnsi(out)); assertEquals("err", toAnsi(err)); assertEquals("ctl", toAnsi(ctl)); } @Test public void testWithoutLineBreaks() throws Exception { byte[] multiplexed = concat(chunk(1, "just "), chunk(1, "one "), chunk(1, "line")); try (final StreamDemultiplexer demux = new StreamDemultiplexer((byte) 1, out)) { demux.write(multiplexed); } assertEquals("just one line", out.toString("ISO-8859-1")); } @Test public void testMultiplexAndBackWithHelloWorld() throws Exception { StreamDemultiplexer demux = new StreamDemultiplexer((byte) 1, out); StreamMultiplexer mux = new StreamMultiplexer(demux); OutputStream out = mux.createStdout(); out.write(inAnsi("Hello, world.")); out.flush(); assertEquals("Hello, world.", toAnsi(this.out)); } @Test public void testMultiplexDemultiplexBinaryStress() throws Exception { StreamDemultiplexer demux = new StreamDemultiplexer((byte) 1, out, err, ctl); StreamMultiplexer mux = new StreamMultiplexer(demux); OutputStream[] muxOuts = {mux.createStdout(), mux.createStderr(), mux.createControl()}; ByteArrayOutputStream[] expectedOuts = {new ByteArrayOutputStream(), new ByteArrayOutputStream(), new ByteArrayOutputStream()}; Random random = new Random(0xdeadbeef); for (int round = 0; round < 100; round++) { byte[] buffer = new byte[random.nextInt(100)]; random.nextBytes(buffer); int streamId = random.nextInt(3); expectedOuts[streamId].write(buffer); expectedOuts[streamId].flush(); muxOuts[streamId].write(buffer); muxOuts[streamId].flush(); } assertArrayEquals(expectedOuts[0].toByteArray(), out.toByteArray()); assertArrayEquals(expectedOuts[1].toByteArray(), err.toByteArray()); assertArrayEquals(expectedOuts[2].toByteArray(), ctl.toByteArray()); } private static byte[] chunk(int stream, String payload) { byte[] payloadBytes = payload.getBytes(Charset.defaultCharset()); byte[] result = new byte[payloadBytes.length + 5]; System.arraycopy(payloadBytes, 0, result, 5, payloadBytes.length); result[0] = (byte) stream; result[1] = (byte) (payloadBytes.length >> 24); result[2] = (byte) ((payloadBytes.length >> 16) & 0xff); result[3] = (byte) ((payloadBytes.length >> 8) & 0xff); result[4] = (byte) (payloadBytes.length & 0xff); return result; } private static byte[] concat(byte[]... chunks) { int length = 0; for (byte[] chunk : chunks) { length += chunk.length; } byte[] result = new byte[length]; int previousChunks = 0; for (byte[] chunk : chunks) { System.arraycopy(chunk, 0, result, previousChunks, chunk.length); previousChunks += chunk.length; } return result; } }