From 9b7d272fa4c784f133f75350ce574e9bcab94722 Mon Sep 17 00:00:00 2001 From: Durim Kryeziu Date: Sat, 25 Apr 2026 15:02:41 +0200 Subject: [PATCH] Extract line IO abstractions into shared library LineReader and LineWriter implementations were duplicated across area-rectangular-room and pizza-party exercises. Created a new line-io module containing reusable abstractions and test fixtures (CapturingLineWriter, StubLineReader). Both exercises now depend on this shared module, eliminating duplication and establishing consistent patterns for input/output handling. --- .gitignore | 4 + area-rectangular-room/build.gradle | 2 + .../AreaRectangularRoomCalculator.java | 3 + .../rectangular/InputStreamLineReader.java | 25 ------- .../delivercraft/rectangular/LineReader.java | 6 -- .../delivercraft/rectangular/LineWriter.java | 8 -- .../dev/delivercraft/rectangular/Main.java | 5 ++ .../rectangular/OutputStreamLineWriter.java | 24 ------ .../AreaRectangularRoomCalculatorTest.java | 56 +++----------- line-io/build.gradle | 10 +++ .../io/InputStreamLineReader.java | 45 +++++++++++ .../java/dev/delivercraft/io/LineReader.java | 14 ++++ .../java/dev/delivercraft/io/LineWriter.java | 21 ++++++ .../io/OutputStreamLineWriter.java | 48 ++++++++++++ .../io/CapturingLineWriterTest.java | 48 ++++++++++++ .../io}/InputStreamLineReaderTest.java | 15 +++- .../io}/OutputStreamLineWriterTest.java | 10 ++- .../delivercraft/io/StubLineReaderTest.java | 55 ++++++++++++++ .../delivercraft/io/CapturingLineWriter.java | 45 +++++++++++ .../dev/delivercraft/io/StubLineReader.java | 42 +++++++++++ pizza-party/build.gradle | 2 + .../pizza/InputStreamLineReader.java | 25 ------- .../dev/delivercraft/pizza/LineReader.java | 6 -- .../dev/delivercraft/pizza/LineWriter.java | 8 -- .../java/dev/delivercraft/pizza/Main.java | 5 ++ .../pizza/OutputStreamLineWriter.java | 24 ------ .../dev/delivercraft/pizza/PizzaParty.java | 3 + .../pizza/PizzaPurchasePlanner.java | 3 + .../pizza/PizzaPurchasePlannerMain.java | 5 ++ .../pizza/InputStreamLineReaderTest.java | 47 ------------ .../pizza/OutputStreamLineWriterTest.java | 32 -------- .../delivercraft/pizza/PizzaPartyTest.java | 75 +++++-------------- .../pizza/PizzaPurchasePlannerTest.java | 72 +++++------------- settings.gradle | 1 + 34 files changed, 430 insertions(+), 364 deletions(-) delete mode 100644 area-rectangular-room/src/main/java/dev/delivercraft/rectangular/InputStreamLineReader.java delete mode 100644 area-rectangular-room/src/main/java/dev/delivercraft/rectangular/LineReader.java delete mode 100644 area-rectangular-room/src/main/java/dev/delivercraft/rectangular/LineWriter.java delete mode 100644 area-rectangular-room/src/main/java/dev/delivercraft/rectangular/OutputStreamLineWriter.java create mode 100644 line-io/build.gradle create mode 100644 line-io/src/main/java/dev/delivercraft/io/InputStreamLineReader.java create mode 100644 line-io/src/main/java/dev/delivercraft/io/LineReader.java create mode 100644 line-io/src/main/java/dev/delivercraft/io/LineWriter.java create mode 100644 line-io/src/main/java/dev/delivercraft/io/OutputStreamLineWriter.java create mode 100644 line-io/src/test/java/dev/delivercraft/io/CapturingLineWriterTest.java rename {area-rectangular-room/src/test/java/dev/delivercraft/rectangular => line-io/src/test/java/dev/delivercraft/io}/InputStreamLineReaderTest.java (70%) rename {area-rectangular-room/src/test/java/dev/delivercraft/rectangular => line-io/src/test/java/dev/delivercraft/io}/OutputStreamLineWriterTest.java (76%) create mode 100644 line-io/src/test/java/dev/delivercraft/io/StubLineReaderTest.java create mode 100644 line-io/src/testFixtures/java/dev/delivercraft/io/CapturingLineWriter.java create mode 100644 line-io/src/testFixtures/java/dev/delivercraft/io/StubLineReader.java delete mode 100644 pizza-party/src/main/java/dev/delivercraft/pizza/InputStreamLineReader.java delete mode 100644 pizza-party/src/main/java/dev/delivercraft/pizza/LineReader.java delete mode 100644 pizza-party/src/main/java/dev/delivercraft/pizza/LineWriter.java delete mode 100644 pizza-party/src/main/java/dev/delivercraft/pizza/OutputStreamLineWriter.java delete mode 100644 pizza-party/src/test/java/dev/delivercraft/pizza/InputStreamLineReaderTest.java delete mode 100644 pizza-party/src/test/java/dev/delivercraft/pizza/OutputStreamLineWriterTest.java diff --git a/.gitignore b/.gitignore index ba44bcc..c5352a1 100644 --- a/.gitignore +++ b/.gitignore @@ -12,4 +12,8 @@ out/ !**/src/main/**/out/ !**/src/test/**/out/ +# macOS +**/.DS_Store .DS_Store + +.worktrees/ diff --git a/area-rectangular-room/build.gradle b/area-rectangular-room/build.gradle index 0675bd8..a5548fb 100644 --- a/area-rectangular-room/build.gradle +++ b/area-rectangular-room/build.gradle @@ -3,6 +3,8 @@ plugins { } dependencies { + implementation(project(':line-io')) + testImplementation(testFixtures(project(':line-io'))) testImplementation(libs.assertj.core) testImplementation(libs.junit.jupiter) } diff --git a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/AreaRectangularRoomCalculator.java b/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/AreaRectangularRoomCalculator.java index 64a4502..305dcff 100644 --- a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/AreaRectangularRoomCalculator.java +++ b/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/AreaRectangularRoomCalculator.java @@ -1,5 +1,8 @@ package dev.delivercraft.rectangular; +import dev.delivercraft.io.LineReader; +import dev.delivercraft.io.LineWriter; + class AreaRectangularRoomCalculator { private static final double CONVERSION_FACTOR = 0.092_903_04; diff --git a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/InputStreamLineReader.java b/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/InputStreamLineReader.java deleted file mode 100644 index 746cc10..0000000 --- a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/InputStreamLineReader.java +++ /dev/null @@ -1,25 +0,0 @@ -package dev.delivercraft.rectangular; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; - -final class InputStreamLineReader implements LineReader { - - private final BufferedReader bufferedReader; - - InputStreamLineReader(InputStream inputStream) { - this.bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); - } - - @Override - public String readLine() { - try { - return this.bufferedReader.readLine(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } -} diff --git a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/LineReader.java b/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/LineReader.java deleted file mode 100644 index cb693e3..0000000 --- a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/LineReader.java +++ /dev/null @@ -1,6 +0,0 @@ -package dev.delivercraft.rectangular; - -public interface LineReader { - - String readLine(); -} diff --git a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/LineWriter.java b/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/LineWriter.java deleted file mode 100644 index 2d46b8c..0000000 --- a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/LineWriter.java +++ /dev/null @@ -1,8 +0,0 @@ -package dev.delivercraft.rectangular; - -public interface LineWriter { - - void write(String text); - - void writeLine(String line); -} diff --git a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/Main.java b/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/Main.java index 0220519..ea5b732 100644 --- a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/Main.java +++ b/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/Main.java @@ -1,5 +1,10 @@ package dev.delivercraft.rectangular; +import dev.delivercraft.io.InputStreamLineReader; +import dev.delivercraft.io.LineReader; +import dev.delivercraft.io.LineWriter; +import dev.delivercraft.io.OutputStreamLineWriter; + public final class Main { void main() { diff --git a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/OutputStreamLineWriter.java b/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/OutputStreamLineWriter.java deleted file mode 100644 index d57fc1d..0000000 --- a/area-rectangular-room/src/main/java/dev/delivercraft/rectangular/OutputStreamLineWriter.java +++ /dev/null @@ -1,24 +0,0 @@ -package dev.delivercraft.rectangular; - -import java.io.OutputStream; -import java.io.PrintWriter; - -final class OutputStreamLineWriter implements LineWriter { - - private final PrintWriter printWriter; - - OutputStreamLineWriter(OutputStream outputStream) { - this.printWriter = new PrintWriter(outputStream, true); - } - - @Override - public void write(String text) { - this.printWriter.print(text); - this.printWriter.flush(); - } - - @Override - public void writeLine(String line) { - this.printWriter.println(line); - } -} diff --git a/area-rectangular-room/src/test/java/dev/delivercraft/rectangular/AreaRectangularRoomCalculatorTest.java b/area-rectangular-room/src/test/java/dev/delivercraft/rectangular/AreaRectangularRoomCalculatorTest.java index eb0b043..d2d2b48 100644 --- a/area-rectangular-room/src/test/java/dev/delivercraft/rectangular/AreaRectangularRoomCalculatorTest.java +++ b/area-rectangular-room/src/test/java/dev/delivercraft/rectangular/AreaRectangularRoomCalculatorTest.java @@ -1,11 +1,13 @@ package dev.delivercraft.rectangular; +import dev.delivercraft.io.CapturingLineWriter; +import dev.delivercraft.io.LineReader; +import dev.delivercraft.io.LineWriter; +import dev.delivercraft.io.StubLineReader; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.NullAndEmptySource; -import java.util.StringJoiner; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException; @@ -16,7 +18,7 @@ class AreaRectangularRoomCalculatorTest { void calculateArea_GivenNoInput_ShouldThrowException(String input) { LineReader lineReader = () -> input; AreaRectangularRoomCalculator calculator = new AreaRectangularRoomCalculator(lineReader, - new InMemoryLineWriter()); + new CapturingLineWriter()); assertThatIllegalArgumentException() .isThrownBy(calculator::calculateArea) @@ -26,7 +28,7 @@ void calculateArea_GivenNoInput_ShouldThrowException(String input) { @Test void calculateArea_ShouldAskForLength() { LineReader lineReader = () -> "123"; - LineWriter lineWriter = new InMemoryLineWriter(); + LineWriter lineWriter = new CapturingLineWriter(); AreaRectangularRoomCalculator calculator = new AreaRectangularRoomCalculator(lineReader, lineWriter); calculator.calculateArea(); @@ -38,7 +40,7 @@ void calculateArea_ShouldAskForLength() { @Test void calculateArea_ShouldAskForWidth() { LineReader lineReader = () -> "123"; - LineWriter lineWriter = new InMemoryLineWriter(); + LineWriter lineWriter = new CapturingLineWriter(); AreaRectangularRoomCalculator calculator = new AreaRectangularRoomCalculator(lineReader, lineWriter); calculator.calculateArea(); @@ -50,7 +52,7 @@ void calculateArea_ShouldAskForWidth() { @Test void calculateArea_GivenLengthAndWidth_ShouldDisplayDimensions() { LineReader lineReader = new StubLineReader("15", "20"); - LineWriter lineWriter = new InMemoryLineWriter(); + LineWriter lineWriter = new CapturingLineWriter(); AreaRectangularRoomCalculator calculator = new AreaRectangularRoomCalculator(lineReader, lineWriter); calculator.calculateArea(); @@ -62,7 +64,7 @@ void calculateArea_GivenLengthAndWidth_ShouldDisplayDimensions() { @Test void calculateArea_GivenValidInput_ShouldDisplayTheAreaInSquareFeet() { LineReader lineReader = new StubLineReader("15", "20"); - LineWriter lineWriter = new InMemoryLineWriter(); + LineWriter lineWriter = new CapturingLineWriter(); AreaRectangularRoomCalculator calculator = new AreaRectangularRoomCalculator(lineReader, lineWriter); calculator.calculateArea(); @@ -75,7 +77,7 @@ void calculateArea_GivenValidInput_ShouldDisplayTheAreaInSquareFeet() { @Test void calculateArea_GivenValidInput_ShouldDisplayTheAreaInSquareMeters() { LineReader lineReader = new StubLineReader("15", "20"); - LineWriter lineWriter = new InMemoryLineWriter(); + LineWriter lineWriter = new CapturingLineWriter(); AreaRectangularRoomCalculator calculator = new AreaRectangularRoomCalculator(lineReader, lineWriter); calculator.calculateArea(); @@ -87,47 +89,11 @@ void calculateArea_GivenValidInput_ShouldDisplayTheAreaInSquareMeters() { @Test void calculateArea_GivenNonNumericInput_ShouldThrowException() { LineReader lineReader = new StubLineReader("asdf"); - LineWriter lineWriter = new InMemoryLineWriter(); + LineWriter lineWriter = new CapturingLineWriter(); AreaRectangularRoomCalculator calculator = new AreaRectangularRoomCalculator(lineReader, lineWriter); assertThatIllegalArgumentException() .isThrownBy(calculator::calculateArea) .withMessage("Please enter a numeric value! Input: asdf"); } - - private static final class StubLineReader implements LineReader { - - private final String[] inputs; - - private int index; - - private StubLineReader(String... inputs) { - this.inputs = inputs; - } - - @Override - public String readLine() { - return this.inputs[this.index++]; - } - } - - private static final class InMemoryLineWriter implements LineWriter { - - private final StringJoiner stringJoiner = new StringJoiner(""); - - @Override - public void write(String text) { - this.stringJoiner.add(text); - } - - @Override - public void writeLine(String line) { - this.stringJoiner.add(line).add(System.lineSeparator()); - } - - @Override - public String toString() { - return this.stringJoiner.toString(); - } - } } diff --git a/line-io/build.gradle b/line-io/build.gradle new file mode 100644 index 0000000..f7ed360 --- /dev/null +++ b/line-io/build.gradle @@ -0,0 +1,10 @@ +plugins { + id 'dev.delivercraft.common-conventions' + id 'java-library' + id 'java-test-fixtures' +} + +dependencies { + testImplementation(libs.assertj.core) + testImplementation(libs.junit.jupiter) +} diff --git a/line-io/src/main/java/dev/delivercraft/io/InputStreamLineReader.java b/line-io/src/main/java/dev/delivercraft/io/InputStreamLineReader.java new file mode 100644 index 0000000..c01884d --- /dev/null +++ b/line-io/src/main/java/dev/delivercraft/io/InputStreamLineReader.java @@ -0,0 +1,45 @@ +package dev.delivercraft.io; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.UncheckedIOException; +import java.util.Objects; + +/** + * Reads lines from an {@link InputStream}. + * + *

This implementation uses the platform default charset, returns {@code null} when the stream is exhausted, and + * wraps checked {@link IOException} failures in {@link UncheckedIOException}. + */ +public final class InputStreamLineReader implements LineReader { + + private final BufferedReader bufferedReader; + + /** + * Creates a line reader backed by the supplied input stream. + * + * @param inputStream the stream to read from + * @throws NullPointerException when {@code inputStream} is {@code null} + */ + public InputStreamLineReader(InputStream inputStream) { + Objects.requireNonNull(inputStream, "inputStream must not be null"); + this.bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); + } + + /** + * Reads the next line from the stream. + * + * @return the next line without a line separator, or {@code null} when the stream is exhausted + * @throws UncheckedIOException when the underlying stream cannot be read + */ + @Override + public String readLine() { + try { + return this.bufferedReader.readLine(); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } +} diff --git a/line-io/src/main/java/dev/delivercraft/io/LineReader.java b/line-io/src/main/java/dev/delivercraft/io/LineReader.java new file mode 100644 index 0000000..c89aeb1 --- /dev/null +++ b/line-io/src/main/java/dev/delivercraft/io/LineReader.java @@ -0,0 +1,14 @@ +package dev.delivercraft.io; + +/** + * Reads one line of text. + */ +public interface LineReader { + + /** + * Reads one line of text. + * + * @return the line without a line separator, or {@code null} when no input remains + */ + String readLine(); +} diff --git a/line-io/src/main/java/dev/delivercraft/io/LineWriter.java b/line-io/src/main/java/dev/delivercraft/io/LineWriter.java new file mode 100644 index 0000000..6176708 --- /dev/null +++ b/line-io/src/main/java/dev/delivercraft/io/LineWriter.java @@ -0,0 +1,21 @@ +package dev.delivercraft.io; + +/** + * Writes text and lines of text. + */ +public interface LineWriter { + + /** + * Writes text without appending a line separator. + * + * @param text the text to write + */ + void write(String text); + + /** + * Writes text followed by the platform line separator. + * + * @param line the line to write + */ + void writeLine(String line); +} diff --git a/line-io/src/main/java/dev/delivercraft/io/OutputStreamLineWriter.java b/line-io/src/main/java/dev/delivercraft/io/OutputStreamLineWriter.java new file mode 100644 index 0000000..c5e23be --- /dev/null +++ b/line-io/src/main/java/dev/delivercraft/io/OutputStreamLineWriter.java @@ -0,0 +1,48 @@ +package dev.delivercraft.io; + +import java.io.OutputStream; +import java.io.PrintWriter; +import java.util.Objects; + +/** + * Writes text and lines to an {@link OutputStream}. + * + *

This implementation writes text without validation or formatting, flushes prompt text written through + * {@link #write(String)}, and uses {@link PrintWriter#println(String)} for line output. + */ +public final class OutputStreamLineWriter implements LineWriter { + + private final PrintWriter printWriter; + + /** + * Creates a line writer backed by the supplied output stream. + * + * @param outputStream the stream to write to + * @throws NullPointerException when {@code outputStream} is {@code null} + */ + public OutputStreamLineWriter(OutputStream outputStream) { + Objects.requireNonNull(outputStream, "outputStream must not be null"); + this.printWriter = new PrintWriter(outputStream, true); + } + + /** + * Writes text without appending a line separator and flushes the underlying writer. + * + * @param text the text to write + */ + @Override + public void write(String text) { + this.printWriter.print(text); + this.printWriter.flush(); + } + + /** + * Writes text followed by the platform line separator. + * + * @param line the line to write + */ + @Override + public void writeLine(String line) { + this.printWriter.println(line); + } +} diff --git a/line-io/src/test/java/dev/delivercraft/io/CapturingLineWriterTest.java b/line-io/src/test/java/dev/delivercraft/io/CapturingLineWriterTest.java new file mode 100644 index 0000000..9641aff --- /dev/null +++ b/line-io/src/test/java/dev/delivercraft/io/CapturingLineWriterTest.java @@ -0,0 +1,48 @@ +package dev.delivercraft.io; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; + +class CapturingLineWriterTest { + + private static final String GREETING = "Hello, World!"; + + @Test + void write_GivenText_ShouldCaptureTextWithoutLineSeparator() { + LineWriter lineWriter = new CapturingLineWriter(); + + lineWriter.write(GREETING); + + assertThat(lineWriter).hasToString(GREETING); + } + + @Test + void writeLine_GivenText_ShouldCaptureTextWithLineSeparator() { + LineWriter lineWriter = new CapturingLineWriter(); + + lineWriter.writeLine(GREETING); + + assertThat(lineWriter).hasToString(GREETING + System.lineSeparator()); + } + + @Test + void writeAndWriteLine_GivenMixedCalls_ShouldPreserveOrder() { + LineWriter lineWriter = new CapturingLineWriter(); + + lineWriter.write("prompt "); + lineWriter.writeLine("answer"); + + assertThat(lineWriter).hasToString("prompt answer" + System.lineSeparator()); + } + + @Test + void writeAndWriteLine_GivenNullValues_ShouldCaptureNullAsString() { + LineWriter lineWriter = new CapturingLineWriter(); + + lineWriter.write(null); + lineWriter.writeLine(null); + + assertThat(lineWriter).hasToString("nullnull" + System.lineSeparator()); + } +} diff --git a/area-rectangular-room/src/test/java/dev/delivercraft/rectangular/InputStreamLineReaderTest.java b/line-io/src/test/java/dev/delivercraft/io/InputStreamLineReaderTest.java similarity index 70% rename from area-rectangular-room/src/test/java/dev/delivercraft/rectangular/InputStreamLineReaderTest.java rename to line-io/src/test/java/dev/delivercraft/io/InputStreamLineReaderTest.java index ec619f8..13fde17 100644 --- a/area-rectangular-room/src/test/java/dev/delivercraft/rectangular/InputStreamLineReaderTest.java +++ b/line-io/src/test/java/dev/delivercraft/io/InputStreamLineReaderTest.java @@ -1,4 +1,4 @@ -package dev.delivercraft.rectangular; +package dev.delivercraft.io; import org.junit.jupiter.api.Test; @@ -9,6 +9,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; class InputStreamLineReaderTest { @@ -34,7 +35,7 @@ void readLine_GivenInput_ShouldReturnInputLine() { } @Test - void readLine_GivenNullInputStream_ShouldThrowException() throws IOException { + void readLine_GivenFailingInputStream_ShouldThrowUncheckedIOException() throws IOException { @SuppressWarnings("PMD.CloseResource") // This is to simulate a closed stream for testing InputStream inputStream = InputStream.nullInputStream(); inputStream.close(); @@ -42,6 +43,14 @@ void readLine_GivenNullInputStream_ShouldThrowException() throws IOException { LineReader lineReader = new InputStreamLineReader(inputStream); assertThatExceptionOfType(UncheckedIOException.class) - .isThrownBy(lineReader::readLine); + .isThrownBy(lineReader::readLine) + .withCauseInstanceOf(IOException.class); + } + + @Test + void constructor_GivenNullInputStream_ShouldThrowException() { + assertThatNullPointerException() + .isThrownBy(() -> new InputStreamLineReader(null)) + .withMessage("inputStream must not be null"); } } diff --git a/area-rectangular-room/src/test/java/dev/delivercraft/rectangular/OutputStreamLineWriterTest.java b/line-io/src/test/java/dev/delivercraft/io/OutputStreamLineWriterTest.java similarity index 76% rename from area-rectangular-room/src/test/java/dev/delivercraft/rectangular/OutputStreamLineWriterTest.java rename to line-io/src/test/java/dev/delivercraft/io/OutputStreamLineWriterTest.java index 3cbe3c2..d47ede9 100644 --- a/area-rectangular-room/src/test/java/dev/delivercraft/rectangular/OutputStreamLineWriterTest.java +++ b/line-io/src/test/java/dev/delivercraft/io/OutputStreamLineWriterTest.java @@ -1,10 +1,11 @@ -package dev.delivercraft.rectangular; +package dev.delivercraft.io; import org.junit.jupiter.api.Test; import java.io.ByteArrayOutputStream; import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; class OutputStreamLineWriterTest { @@ -39,4 +40,11 @@ void writeLine_GivenText_ShouldPrintTextWithNewLine() { assertThat(outputStream).hasToString(line + System.lineSeparator()); } + + @Test + void constructor_GivenNullOutputStream_ShouldThrowException() { + assertThatNullPointerException() + .isThrownBy(() -> new OutputStreamLineWriter(null)) + .withMessage("outputStream must not be null"); + } } diff --git a/line-io/src/test/java/dev/delivercraft/io/StubLineReaderTest.java b/line-io/src/test/java/dev/delivercraft/io/StubLineReaderTest.java new file mode 100644 index 0000000..b0e92ec --- /dev/null +++ b/line-io/src/test/java/dev/delivercraft/io/StubLineReaderTest.java @@ -0,0 +1,55 @@ +package dev.delivercraft.io; + +import org.junit.jupiter.api.Test; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatNullPointerException; + +class StubLineReaderTest { + + @Test + void readLine_GivenInputs_ShouldReturnInputsInOrder() { + LineReader lineReader = new StubLineReader("first", "second"); + + String first = lineReader.readLine(); + String second = lineReader.readLine(); + + assertThat(first).isEqualTo("first"); + assertThat(second).isEqualTo("second"); + } + + @Test + void readLine_GivenNoInputs_ShouldReturnNull() { + LineReader lineReader = new StubLineReader(); + + String result = lineReader.readLine(); + + assertThat(result).isNull(); + } + + @Test + void readLine_GivenExhaustedInputs_ShouldReturnNull() { + LineReader lineReader = new StubLineReader("only"); + + lineReader.readLine(); + String result = lineReader.readLine(); + + assertThat(result).isNull(); + } + + @Test + void readLine_GivenNullInputValue_ShouldReturnNull() { + LineReader lineReader = new StubLineReader((String) null); + + String result = lineReader.readLine(); + + assertThat(result).isNull(); + } + + @Test + void constructor_GivenNullInputsArray_ShouldThrowException() { + assertThatNullPointerException() + .isThrownBy(() -> new StubLineReader((String[]) null)) + .withMessage("inputs must not be null"); + } +} diff --git a/line-io/src/testFixtures/java/dev/delivercraft/io/CapturingLineWriter.java b/line-io/src/testFixtures/java/dev/delivercraft/io/CapturingLineWriter.java new file mode 100644 index 0000000..182feaf --- /dev/null +++ b/line-io/src/testFixtures/java/dev/delivercraft/io/CapturingLineWriter.java @@ -0,0 +1,45 @@ +package dev.delivercraft.io; + +import java.io.StringWriter; + +/** + * Captures line writer output for state-based test assertions. + * + *

Writes are captured in call order. {@link #write(String)} appends text without a line separator, and + * {@link #writeLine(String)} appends text followed by {@link System#lineSeparator()}. + */ +public final class CapturingLineWriter implements LineWriter { + + private final StringWriter output = new StringWriter(); + + /** + * Captures text without appending a line separator. + * + * @param text the text to capture + */ + @Override + public void write(String text) { + this.output.write(text); + } + + /** + * Captures text followed by the platform line separator. + * + * @param line the line to capture + */ + @Override + public void writeLine(String line) { + this.output.write(line); + this.output.write(System.lineSeparator()); + } + + /** + * Returns the captured output. + * + * @return the captured output + */ + @Override + public String toString() { + return this.output.toString(); + } +} diff --git a/line-io/src/testFixtures/java/dev/delivercraft/io/StubLineReader.java b/line-io/src/testFixtures/java/dev/delivercraft/io/StubLineReader.java new file mode 100644 index 0000000..7c03727 --- /dev/null +++ b/line-io/src/testFixtures/java/dev/delivercraft/io/StubLineReader.java @@ -0,0 +1,42 @@ +package dev.delivercraft.io; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Supplies canned line input to tests. + * + *

Inputs are returned in constructor order. When all inputs are exhausted, {@link #readLine()} returns + * {@code null} to match end-of-input behavior. Multi-line strings are not split; each input line should be supplied as + * a separate constructor argument. + */ +public final class StubLineReader implements LineReader { + + private final String[] inputs; + + private int index; + + /** + * Creates a reader with canned input values. + * + * @param inputs the values returned by subsequent calls to {@link #readLine()} + * @throws NullPointerException when {@code inputs} is {@code null} + */ + public StubLineReader(String... inputs) { + Objects.requireNonNull(inputs, "inputs must not be null"); + this.inputs = Arrays.copyOf(inputs, inputs.length); + } + + /** + * Returns the next canned input value. + * + * @return the next canned input, or {@code null} when no inputs remain + */ + @Override + public String readLine() { + if (this.index >= this.inputs.length) { + return null; + } + return this.inputs[this.index++]; + } +} diff --git a/pizza-party/build.gradle b/pizza-party/build.gradle index 0675bd8..a5548fb 100644 --- a/pizza-party/build.gradle +++ b/pizza-party/build.gradle @@ -3,6 +3,8 @@ plugins { } dependencies { + implementation(project(':line-io')) + testImplementation(testFixtures(project(':line-io'))) testImplementation(libs.assertj.core) testImplementation(libs.junit.jupiter) } diff --git a/pizza-party/src/main/java/dev/delivercraft/pizza/InputStreamLineReader.java b/pizza-party/src/main/java/dev/delivercraft/pizza/InputStreamLineReader.java deleted file mode 100644 index acbe45f..0000000 --- a/pizza-party/src/main/java/dev/delivercraft/pizza/InputStreamLineReader.java +++ /dev/null @@ -1,25 +0,0 @@ -package dev.delivercraft.pizza; - -import java.io.BufferedReader; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.UncheckedIOException; - -final class InputStreamLineReader implements LineReader { - - private final BufferedReader bufferedReader; - - InputStreamLineReader(InputStream inputStream) { - this.bufferedReader = new BufferedReader(new InputStreamReader(inputStream)); - } - - @Override - public String readLine() { - try { - return this.bufferedReader.readLine(); - } catch (IOException e) { - throw new UncheckedIOException(e); - } - } -} diff --git a/pizza-party/src/main/java/dev/delivercraft/pizza/LineReader.java b/pizza-party/src/main/java/dev/delivercraft/pizza/LineReader.java deleted file mode 100644 index 4781ad7..0000000 --- a/pizza-party/src/main/java/dev/delivercraft/pizza/LineReader.java +++ /dev/null @@ -1,6 +0,0 @@ -package dev.delivercraft.pizza; - -public interface LineReader { - - String readLine(); -} diff --git a/pizza-party/src/main/java/dev/delivercraft/pizza/LineWriter.java b/pizza-party/src/main/java/dev/delivercraft/pizza/LineWriter.java deleted file mode 100644 index fe31b14..0000000 --- a/pizza-party/src/main/java/dev/delivercraft/pizza/LineWriter.java +++ /dev/null @@ -1,8 +0,0 @@ -package dev.delivercraft.pizza; - -public interface LineWriter { - - void write(String text); - - void writeLine(String line); -} diff --git a/pizza-party/src/main/java/dev/delivercraft/pizza/Main.java b/pizza-party/src/main/java/dev/delivercraft/pizza/Main.java index 75bdb3b..0ed253e 100644 --- a/pizza-party/src/main/java/dev/delivercraft/pizza/Main.java +++ b/pizza-party/src/main/java/dev/delivercraft/pizza/Main.java @@ -1,5 +1,10 @@ package dev.delivercraft.pizza; +import dev.delivercraft.io.InputStreamLineReader; +import dev.delivercraft.io.LineReader; +import dev.delivercraft.io.LineWriter; +import dev.delivercraft.io.OutputStreamLineWriter; + public final class Main { void main() { diff --git a/pizza-party/src/main/java/dev/delivercraft/pizza/OutputStreamLineWriter.java b/pizza-party/src/main/java/dev/delivercraft/pizza/OutputStreamLineWriter.java deleted file mode 100644 index 52ae228..0000000 --- a/pizza-party/src/main/java/dev/delivercraft/pizza/OutputStreamLineWriter.java +++ /dev/null @@ -1,24 +0,0 @@ -package dev.delivercraft.pizza; - -import java.io.OutputStream; -import java.io.PrintWriter; - -final class OutputStreamLineWriter implements LineWriter { - - private final PrintWriter printWriter; - - OutputStreamLineWriter(OutputStream outputStream) { - this.printWriter = new PrintWriter(outputStream, true); - } - - @Override - public void write(String text) { - this.printWriter.print(text); - this.printWriter.flush(); - } - - @Override - public void writeLine(String line) { - this.printWriter.println(line); - } -} diff --git a/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaParty.java b/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaParty.java index 7566004..4999904 100644 --- a/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaParty.java +++ b/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaParty.java @@ -1,5 +1,8 @@ package dev.delivercraft.pizza; +import dev.delivercraft.io.LineReader; +import dev.delivercraft.io.LineWriter; + import java.util.Objects; final class PizzaParty { diff --git a/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaPurchasePlanner.java b/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaPurchasePlanner.java index d6f3f43..8192f13 100644 --- a/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaPurchasePlanner.java +++ b/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaPurchasePlanner.java @@ -1,5 +1,8 @@ package dev.delivercraft.pizza; +import dev.delivercraft.io.LineReader; +import dev.delivercraft.io.LineWriter; + import java.util.Objects; final class PizzaPurchasePlanner { diff --git a/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaPurchasePlannerMain.java b/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaPurchasePlannerMain.java index 3f54f5c..45fc22b 100644 --- a/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaPurchasePlannerMain.java +++ b/pizza-party/src/main/java/dev/delivercraft/pizza/PizzaPurchasePlannerMain.java @@ -1,5 +1,10 @@ package dev.delivercraft.pizza; +import dev.delivercraft.io.InputStreamLineReader; +import dev.delivercraft.io.LineReader; +import dev.delivercraft.io.LineWriter; +import dev.delivercraft.io.OutputStreamLineWriter; + public final class PizzaPurchasePlannerMain { void main() { diff --git a/pizza-party/src/test/java/dev/delivercraft/pizza/InputStreamLineReaderTest.java b/pizza-party/src/test/java/dev/delivercraft/pizza/InputStreamLineReaderTest.java deleted file mode 100644 index 1a12539..0000000 --- a/pizza-party/src/test/java/dev/delivercraft/pizza/InputStreamLineReaderTest.java +++ /dev/null @@ -1,47 +0,0 @@ -package dev.delivercraft.pizza; - -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.UncheckedIOException; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; - -class InputStreamLineReaderTest { - - @Test - void readLine_GivenNoInput_ShouldReturnNull() { - ByteArrayInputStream inputStream = new ByteArrayInputStream(new byte[]{}); - LineReader lineReader = new InputStreamLineReader(inputStream); - - String result = lineReader.readLine(); - - assertThat(result).isNull(); - } - - @Test - void readLine_GivenInput_ShouldReturnInputLine() { - String input = "Hello, World!\n"; - ByteArrayInputStream inputStream = new ByteArrayInputStream(input.getBytes()); - LineReader lineReader = new InputStreamLineReader(inputStream); - - String result = lineReader.readLine(); - - assertThat(result).isEqualTo("Hello, World!"); - } - - @Test - void readLine_GivenNullInputStream_ShouldThrowException() throws IOException { - @SuppressWarnings("PMD.CloseResource") - InputStream inputStream = InputStream.nullInputStream(); - inputStream.close(); - - LineReader lineReader = new InputStreamLineReader(inputStream); - - assertThatExceptionOfType(UncheckedIOException.class) - .isThrownBy(lineReader::readLine); - } -} diff --git a/pizza-party/src/test/java/dev/delivercraft/pizza/OutputStreamLineWriterTest.java b/pizza-party/src/test/java/dev/delivercraft/pizza/OutputStreamLineWriterTest.java deleted file mode 100644 index cc4e09f..0000000 --- a/pizza-party/src/test/java/dev/delivercraft/pizza/OutputStreamLineWriterTest.java +++ /dev/null @@ -1,32 +0,0 @@ -package dev.delivercraft.pizza; - -import org.junit.jupiter.api.Test; - -import java.io.ByteArrayOutputStream; - -import static org.assertj.core.api.Assertions.assertThat; - -class OutputStreamLineWriterTest { - - @Test - void write_GivenText_ShouldPrintText() { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - LineWriter lineWriter = new OutputStreamLineWriter(outputStream); - String text = "Hello, World!"; - - lineWriter.write(text); - - assertThat(outputStream).hasToString(text); - } - - @Test - void writeLine_GivenText_ShouldPrintTextWithNewLine() { - ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); - LineWriter lineWriter = new OutputStreamLineWriter(outputStream); - String line = "Hello, World!"; - - lineWriter.writeLine(line); - - assertThat(outputStream).hasToString(line + System.lineSeparator()); - } -} diff --git a/pizza-party/src/test/java/dev/delivercraft/pizza/PizzaPartyTest.java b/pizza-party/src/test/java/dev/delivercraft/pizza/PizzaPartyTest.java index 9f9ec64..cb34c37 100644 --- a/pizza-party/src/test/java/dev/delivercraft/pizza/PizzaPartyTest.java +++ b/pizza-party/src/test/java/dev/delivercraft/pizza/PizzaPartyTest.java @@ -1,11 +1,14 @@ package dev.delivercraft.pizza; +import dev.delivercraft.io.CapturingLineWriter; +import dev.delivercraft.io.LineReader; +import dev.delivercraft.io.LineWriter; +import dev.delivercraft.io.StubLineReader; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.util.StringJoiner; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -13,8 +16,6 @@ class PizzaPartyTest { - private static final int SINGLE_INPUT = 1; - private static final String FIRST_INPUT = Integer.toString(8); private static final String SECOND_NUMBER = Integer.toString(2); @@ -32,7 +33,7 @@ class PizzaPartyTest { @MethodSource("promptTexts") void asksForPrompts(String prompt) { LineReader lineReader = new StubLineReader(FIRST_INPUT, SECOND_NUMBER, THIRD_NUMBER); - LineWriter lineWriter = new InMemoryLineWriter(); + LineWriter lineWriter = new CapturingLineWriter(); PizzaParty pizzaParty = new PizzaParty(lineReader, lineWriter); pizzaParty.displayPizzaParty(); @@ -51,7 +52,7 @@ private static Stream promptTexts() { @MethodSource("pizzaOutputs") void displaysPizzaOutput(String people, String pizzas, String slicesPerPizza, String expectedOutput) { LineReader lineReader = new StubLineReader(people, pizzas, slicesPerPizza); - LineWriter lineWriter = new InMemoryLineWriter(); + LineWriter lineWriter = new CapturingLineWriter(); PizzaParty pizzaParty = new PizzaParty(lineReader, lineWriter); pizzaParty.displayPizzaParty(); @@ -85,27 +86,28 @@ private static Stream pizzaOutputs() { @ParameterizedTest @MethodSource("missingInputs") - void inputIsRequired(String input) { - PizzaParty pizzaParty = new PizzaParty(new StubLineReader(input), new InMemoryLineWriter()); + void inputIsRequired(String people, String pizzas, String slicesPerPizza) { + PizzaParty pizzaParty = new PizzaParty(new StubLineReader(people, pizzas, slicesPerPizza), + new CapturingLineWriter()); assertThatIllegalArgumentException() .isThrownBy(pizzaParty::displayPizzaParty) .withMessage("Input must not be empty!"); } - private static Stream missingInputs() { + private static Stream missingInputs() { return Stream.of( - "", - " ", - System.lineSeparator() + SECOND_NUMBER + System.lineSeparator() + THIRD_NUMBER, - FIRST_INPUT + System.lineSeparator() + System.lineSeparator() + THIRD_NUMBER, - FIRST_INPUT + System.lineSeparator() + SECOND_NUMBER + System.lineSeparator()); + Arguments.of("", SECOND_NUMBER, THIRD_NUMBER), + Arguments.of(" ", SECOND_NUMBER, THIRD_NUMBER), + Arguments.of("", SECOND_NUMBER, THIRD_NUMBER), + Arguments.of(FIRST_INPUT, "", THIRD_NUMBER), + Arguments.of(FIRST_INPUT, SECOND_NUMBER, "")); } @Test void inputMustBeANumber() { PizzaParty pizzaParty = new PizzaParty( - new StubLineReader("abc", SECOND_NUMBER, THIRD_NUMBER), new InMemoryLineWriter()); + new StubLineReader("abc", SECOND_NUMBER, THIRD_NUMBER), new CapturingLineWriter()); assertThatIllegalArgumentException() .isThrownBy(pizzaParty::displayPizzaParty) @@ -115,7 +117,7 @@ void inputMustBeANumber() { @Test void inputMustBePositive() { PizzaParty pizzaParty = new PizzaParty( - new StubLineReader("-1", SECOND_NUMBER, THIRD_NUMBER), new InMemoryLineWriter()); + new StubLineReader("-1", SECOND_NUMBER, THIRD_NUMBER), new CapturingLineWriter()); assertThatIllegalArgumentException() .isThrownBy(pizzaParty::displayPizzaParty) @@ -125,51 +127,10 @@ void inputMustBePositive() { @Test void peopleMustBeGreaterThanZero() { PizzaParty pizzaParty = new PizzaParty(new StubLineReader("0", SECOND_NUMBER, THIRD_NUMBER), - new InMemoryLineWriter()); + new CapturingLineWriter()); assertThatIllegalArgumentException() .isThrownBy(pizzaParty::displayPizzaParty) .withMessage("Number of people must be greater than zero!"); } - - private static final class StubLineReader implements LineReader { - - private final String[] inputs; - - private int index; - - private StubLineReader(String... inputs) { - if (inputs.length == SINGLE_INPUT) { - this.inputs = inputs[0].split("\\n", -1); - } else { - this.inputs = inputs; - } - } - - @Override - public String readLine() { - return this.inputs[this.index++]; - } - } - - private static final class InMemoryLineWriter implements LineWriter { - - private final StringJoiner stringJoiner = new StringJoiner(""); - - @Override - public void write(String text) { - this.stringJoiner.add(text); - } - - @Override - public void writeLine(String line) { - this.stringJoiner.add(line).add(System.lineSeparator()); - } - - @Override - public String toString() { - return this.stringJoiner.toString(); - } - } - } diff --git a/pizza-party/src/test/java/dev/delivercraft/pizza/PizzaPurchasePlannerTest.java b/pizza-party/src/test/java/dev/delivercraft/pizza/PizzaPurchasePlannerTest.java index 7956b85..95c1cf8 100644 --- a/pizza-party/src/test/java/dev/delivercraft/pizza/PizzaPurchasePlannerTest.java +++ b/pizza-party/src/test/java/dev/delivercraft/pizza/PizzaPurchasePlannerTest.java @@ -1,11 +1,14 @@ package dev.delivercraft.pizza; +import dev.delivercraft.io.CapturingLineWriter; +import dev.delivercraft.io.LineReader; +import dev.delivercraft.io.LineWriter; +import dev.delivercraft.io.StubLineReader; import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import java.util.StringJoiner; import java.util.stream.Stream; import static org.assertj.core.api.Assertions.assertThat; @@ -13,8 +16,6 @@ class PizzaPurchasePlannerTest { - private static final int SINGLE_INPUT = 1; - private static final String EIGHT = "8"; private static final String FIVE = "5"; @@ -36,7 +37,7 @@ class PizzaPurchasePlannerTest { @MethodSource("promptTexts") void asksForPrompts(String prompt) { LineReader lineReader = new StubLineReader("8", "2"); - LineWriter lineWriter = new InMemoryLineWriter(); + LineWriter lineWriter = new CapturingLineWriter(); PizzaPurchasePlanner planner = new PizzaPurchasePlanner(lineReader, lineWriter); planner.displayPizzaPurchasePlan(); @@ -52,7 +53,7 @@ private static Stream promptTexts() { @MethodSource("plannerOutputs") void displaysRequiredPizzaCount(String people, String piecesPerPerson, String expectedOutput) { LineReader lineReader = new StubLineReader(people, piecesPerPerson); - LineWriter lineWriter = new InMemoryLineWriter(); + LineWriter lineWriter = new CapturingLineWriter(); PizzaPurchasePlanner planner = new PizzaPurchasePlanner(lineReader, lineWriter); planner.displayPizzaPurchasePlan(); @@ -74,21 +75,26 @@ private static Stream plannerOutputs() { @ParameterizedTest @MethodSource("missingInputs") - void inputIsRequired(String input) { - PizzaPurchasePlanner planner = new PizzaPurchasePlanner(new StubLineReader(input), new InMemoryLineWriter()); + void inputIsRequired(String people, String piecesPerPerson) { + PizzaPurchasePlanner planner = new PizzaPurchasePlanner(new StubLineReader(people, piecesPerPerson), + new CapturingLineWriter()); assertThatIllegalArgumentException().isThrownBy(planner::displayPizzaPurchasePlan).withMessage("Input must " + "not be empty!"); } - private static Stream missingInputs() { - return Stream.of("", " ", System.lineSeparator() + TWO + System.lineSeparator(), - EIGHT + System.lineSeparator() + System.lineSeparator()); + private static Stream missingInputs() { + return Stream.of( + Arguments.of("", TWO), + Arguments.of(" ", TWO), + Arguments.of("", TWO), + Arguments.of(EIGHT, "")); } @Test void inputMustBeANumber() { - PizzaPurchasePlanner planner = new PizzaPurchasePlanner(new StubLineReader(ABC, TWO), new InMemoryLineWriter()); + CapturingLineWriter writer = new CapturingLineWriter(); + PizzaPurchasePlanner planner = new PizzaPurchasePlanner(new StubLineReader(ABC, TWO), writer); assertThatIllegalArgumentException().isThrownBy(planner::displayPizzaPurchasePlan).withMessage("Please enter " + "a valid number! Input: abc"); @@ -97,7 +103,7 @@ void inputMustBeANumber() { @Test void inputMustBePositive() { PizzaPurchasePlanner planner = new PizzaPurchasePlanner(new StubLineReader(EIGHT, "-1"), - new InMemoryLineWriter()); + new CapturingLineWriter()); assertThatIllegalArgumentException().isThrownBy(planner::displayPizzaPurchasePlan).withMessage("Please enter " + "a positive number! Input: -1"); @@ -106,49 +112,9 @@ void inputMustBePositive() { @Test void peopleMustBeGreaterThanZero() { PizzaPurchasePlanner planner = new PizzaPurchasePlanner(new StubLineReader(ZERO, TWO), - new InMemoryLineWriter()); + new CapturingLineWriter()); assertThatIllegalArgumentException().isThrownBy(planner::displayPizzaPurchasePlan).withMessage("Number of " + "people must be greater than zero!"); } - - private static final class StubLineReader implements LineReader { - - private final String[] inputs; - - private int index; - - private StubLineReader(String... inputs) { - if (inputs.length == SINGLE_INPUT) { - this.inputs = inputs[0].split("\\n", -1); - } else { - this.inputs = inputs; - } - } - - @Override - public String readLine() { - return this.inputs[this.index++]; - } - } - - private static final class InMemoryLineWriter implements LineWriter { - - private final StringJoiner stringJoiner = new StringJoiner(""); - - @Override - public void write(String text) { - this.stringJoiner.add(text); - } - - @Override - public void writeLine(String line) { - this.stringJoiner.add(line).add(System.lineSeparator()); - } - - @Override - public String toString() { - return this.stringJoiner.toString(); - } - } } diff --git a/settings.gradle b/settings.gradle index 5d974e0..8e7cee0 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1,5 +1,6 @@ rootProject.name = 'exercises-for-programmers-java' include( + 'line-io', 'saying-hello', 'characters-count', 'printing-quotes',