blob: 2a271234bd8deb60de95bbb0405d57dfbed237b5 [file] [log] [blame]
// Copyright 2020 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.runtime;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.mock;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterables;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import org.junit.Ignore;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.JUnit4;
/**
* Tests for {@link SynchronizedOutputStream}.
*
* <p>Note that, at the time of writing, these tests serve to document the actual behavior of the
* class, not necessarily the desired behavior.
*/
@RunWith(JUnit4.class)
public class SynchronizedOutputStreamTest {
@Test
public void testReadAndResetReturnsChunkedWritesSinceLastCall() throws IOException {
SynchronizedOutputStream underTest =
new SynchronizedOutputStream(/*maxBufferedLength=*/ 5, /*maxChunkSize=*/ 5);
underTest.write(new byte[] {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'});
assertThat(underTest.readAndReset()).containsExactly("abcde", "fgh").inOrder();
assertThat(underTest.readAndReset()).isEmpty();
underTest.write(new byte[] {'i', 'j', 'k'});
assertThat(underTest.readAndReset()).containsExactly("ijk");
}
@Test
public void testWriteFlushesStreamerWhenMaxBufferedLengthReached() throws IOException {
SynchronizedOutputStream underTest =
new SynchronizedOutputStream(/*maxBufferedLength=*/ 3, /*maxChunkSize=*/ 3);
List<List<String>> writes = new ArrayList<>();
BuildEventStreamer mockStreamer = mock(BuildEventStreamer.class);
doAnswer(
inv -> {
writes.add(ImmutableList.copyOf(underTest.readAndReset()));
return null;
})
.when(mockStreamer)
.flush();
underTest.registerStreamer(mockStreamer);
underTest.write(new byte[] {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h'});
underTest.write(new byte[] {'i', 'j'});
underTest.write(new byte[] {'k', 'l'});
assertThat(writes)
.containsExactly(
ImmutableList.of("abc", "def", "gh"),
// The write of {'k', 'j'} would have put the buffer over size, so {'i', 'j'} was
// flushed.
ImmutableList.of("ij"))
.inOrder();
}
@Test
public void testUsesMaxOfMaxBufferedSizeAndMaxChunkSizeForChunking() throws IOException {
SynchronizedOutputStream underTest =
new SynchronizedOutputStream(/*maxBufferedLength=*/ 2, /*maxChunkSize=*/ 1);
underTest.write(new byte[] {'a', 'b', 'c', 'd'});
assertThat(underTest.readAndReset()).containsExactly("ab", "cd").inOrder();
}
// TODO(b/154242266): Make BES handle binary data correctly.
@Ignore("b/154242266 - BES doesn't handle binary stdout/err correctly")
@Test
public void testHandlesArbitraryBinaryDataCorrectly() throws IOException {
SynchronizedOutputStream underTest =
new SynchronizedOutputStream(/*maxBufferedLength=*/ 1, /*maxChunkSize=*/ 1);
byte[] input = new byte[] {(byte) 0xff};
underTest.write(input);
String result = Iterables.getOnlyElement(underTest.readAndReset());
// In the real code the result eventually winds up in a protobuf, which treats strings as utf8.
assertThat(result.getBytes(StandardCharsets.UTF_8)).isEqualTo(input);
}
}