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