/* * Copyright 2015-present Facebook, Inc. * * 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.facebook.buck.util; import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.empty; import static org.hamcrest.Matchers.equalTo; import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.not; import static org.junit.Assert.assertThat; import com.google.common.base.Predicate; import java.nio.ByteBuffer; import java.nio.CharBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; import java.util.List; import org.junit.Before; import org.junit.Test; public class LineIteratingTest { private TestLineHandler lineHandler; private TestByteLineHandler byteLineHandler; private static class TestLineHandler extends LineIterating.CharLineHandler { public List<String> lines = new ArrayList<>(); public Predicate<List<String>> shouldContinue = x -> true; public TestLineHandler() { super(/* initialBufferCapacity */ 20); } @Override public boolean handleLine(CharBuffer line) { lines.add(line.toString()); return shouldContinue.apply(lines); } } private static class TestByteLineHandler extends LineIterating.ByteLineHandler { public List<String> lines = new ArrayList<>(); public Predicate<List<String>> shouldContinue = x -> true; public TestByteLineHandler() { super(/* initialBufferCapacity */ 20); } @Override public boolean handleLine(ByteBuffer line) { String lineString = StandardCharsets.UTF_8.decode(line).toString(); lines.add(lineString); return shouldContinue.apply(lines); } } @Before public void setUp() { lineHandler = new TestLineHandler(); byteLineHandler = new TestByteLineHandler(); } @Test public void emptyStringIteratesNoLines() { LineIterating.iterateByLines("", lineHandler); assertThat(lineHandler.lines, empty()); lineHandler.close(); assertThat(lineHandler.lines, empty()); } @Test public void stringWithJustUnixEolIteratesEmptyStrings() { LineIterating.iterateByLines("\n\n\n", lineHandler); assertThat(lineHandler.lines, contains("", "", "")); lineHandler.close(); assertThat(lineHandler.lines, contains("", "", "")); } @Test public void stringWithJustMacEolIteratesEmptyStrings() { LineIterating.iterateByLines("\r\r\r", lineHandler); // We can't be sure the third newline is not a Windows EOL // until closed. assertThat(lineHandler.lines, contains("", "")); lineHandler.close(); assertThat(lineHandler.lines, contains("", "", "")); } @Test public void stringWithJustWindowsEolIteratesEmptyStrings() { LineIterating.iterateByLines("\r\n\r\n\r\n", lineHandler); assertThat(lineHandler.lines, contains("", "", "")); } @Test public void stringWithoutEolIteratesAfterFinish() { LineIterating.iterateByLines("foo", lineHandler); assertThat(lineHandler.lines, empty()); lineHandler.close(); assertThat(lineHandler.lines, contains("foo")); } @Test public void stringWithUnixEolIteratesBeforeFinish() { LineIterating.iterateByLines("foo\n", lineHandler); assertThat(lineHandler.lines, contains("foo")); lineHandler.close(); assertThat(lineHandler.lines, contains("foo")); } @Test public void stringWithMultipleUnixEolIterateOncePerLineBeforeFinish() { LineIterating.iterateByLines("foo\nbar\nbaz\n", lineHandler); assertThat(lineHandler.lines, contains("foo", "bar", "baz")); lineHandler.close(); assertThat(lineHandler.lines, contains("foo", "bar", "baz")); } @Test public void stringWithMultipleWindowsCarriageReturnEolIteratesOncePerLine() { LineIterating.iterateByLines("foo\r\nbar\r\nbaz\r\n", lineHandler); assertThat(lineHandler.lines, contains("foo", "bar", "baz")); lineHandler.close(); assertThat(lineHandler.lines, contains("foo", "bar", "baz")); } @Test public void stringWithMultipleMacCarriageReturnsIteratesOncePerLine() { LineIterating.iterateByLines("foo\rbar\rbaz\r", lineHandler); // We can't add baz before seeing if this is the last iteration, since there // might be a \n on the next iteration. assertThat(lineHandler.lines, contains("foo", "bar")); lineHandler.close(); // Now we should get baz. assertThat(lineHandler.lines, contains("foo", "bar", "baz")); } @Test public void stringWithMixedLineEndingsIteratesOncePerLine() { LineIterating.iterateByLines("foo\nbar\rbaz\r\n", lineHandler); assertThat(lineHandler.lines, contains("foo", "bar", "baz")); lineHandler.close(); assertThat(lineHandler.lines, contains("foo", "bar", "baz")); } @Test public void iteratingByLinesWithMultipleInputBuffersGathersInputUntilEolAppears() { LineIterating.iterateByLines("foo", lineHandler); assertThat(lineHandler.lines, empty()); LineIterating.iterateByLines("bar", lineHandler); assertThat(lineHandler.lines, empty()); LineIterating.iterateByLines("baz\n", lineHandler); assertThat(lineHandler.lines, contains("foobarbaz")); lineHandler.close(); assertThat(lineHandler.lines, contains("foobarbaz")); } @Test public void iteratingByLinesWithMultipleInputBuffersGathersInputUntilCloseCalled() { LineIterating.iterateByLines("foo", lineHandler); assertThat(lineHandler.lines, empty()); LineIterating.iterateByLines("bar", lineHandler); assertThat(lineHandler.lines, empty()); LineIterating.iterateByLines("baz", lineHandler); assertThat(lineHandler.lines, empty()); lineHandler.close(); assertThat(lineHandler.lines, contains("foobarbaz")); } @Test public void goingOverBufferCapacityGrowsBuffer() { // The buffer is only 20 bytes long, so write 25 bytes to make it grow. LineIterating.iterateByLines("0123456789012345678901234", lineHandler); assertThat(lineHandler.lines, empty()); LineIterating.iterateByLines("\n", lineHandler); assertThat(lineHandler.lines, contains("0123456789012345678901234")); lineHandler.close(); assertThat(lineHandler.lines, contains("0123456789012345678901234")); } @Test public void goingOverBufferCapacityGrowsBufferAndReturnsOnClose() { // The buffer is only 20 bytes long, so write 25 bytes to make it grow. LineIterating.iterateByLines("0123456789012345678901234", lineHandler); assertThat(lineHandler.lines, empty()); lineHandler.close(); assertThat(lineHandler.lines, contains("0123456789012345678901234")); } @Test public void stopIteratingAfterTwoEol() { lineHandler.shouldContinue = lines -> lines.size() < 2; LineIterating.iterateByLines("foo\nbar\nbaz\n", lineHandler); assertThat(lineHandler.lines, contains("foo", "bar")); // It's kind of an interesting question whether stopping early // should buffer the rest of the data and return the lines on // close. We'll go with "no". lineHandler.close(); assertThat(lineHandler.lines, contains("foo", "bar")); } @Test public void inputBufferPositionSetToLimitAfterDone() { CharBuffer buf = CharBuffer.wrap("foo\nbar\nbaz\n"); assertThat(buf.position(), equalTo(0)); assertThat(buf.limit(), equalTo(12)); LineIterating.iterateByLines(buf, lineHandler); assertThat(buf.position(), equalTo(12)); assertThat(buf.limit(), equalTo(12)); } @Test public void inputBufferReusedForCallbacksIfNothingLeft() { final CharBuffer buf = CharBuffer.wrap("foo\nbar\nbaz\n"); LineIterating.CharLineHandler reuseLineHandler = new LineIterating.CharLineHandler(20) { @Override public boolean handleLine(CharBuffer line) { assertThat(line, is(buf)); return true; } }; LineIterating.iterateByLines(buf, reuseLineHandler); } @Test public void inputBufferNotReusedForCallbacksIfDataLeftInLineHandler() { final CharBuffer buf = CharBuffer.wrap("bar\n"); LineIterating.CharLineHandler reuseLineHandler = new LineIterating.CharLineHandler(20) { @Override public boolean handleLine(CharBuffer line) { assertThat(line, not(is(buf))); return true; } }; LineIterating.iterateByLines("no-newline-yet", reuseLineHandler); LineIterating.iterateByLines(buf, reuseLineHandler); } @Test public void emptyByteBufferLineIteration() { LineIterating.iterateByLines(StandardCharsets.UTF_8.encode(""), byteLineHandler); assertThat(byteLineHandler.lines, empty()); lineHandler.close(); assertThat(byteLineHandler.lines, empty()); } @Test public void nonEmptyByteBufferLineIteration() { LineIterating.iterateByLines(StandardCharsets.UTF_8.encode("foo\nbar\nbaz"), byteLineHandler); assertThat(byteLineHandler.lines, contains("foo", "bar")); byteLineHandler.close(); assertThat(byteLineHandler.lines, contains("foo", "bar", "baz")); } }