Skip to content

Commit

Permalink
Make StdIn report available bytes (#725 / #728)
Browse files Browse the repository at this point in the history
Before reading from `System.in`, it is necessary to check
`System.in.available() != 0` to prevent blocking on empty input, but
`StdIn` subclasses `InputStream`, where `available()` always returns 0.

This change makes `StdIn` override `available()`, so it returns the
number of bytes from the input string that weren't yet read.

Closes: #725
PR: #728
  • Loading branch information
nipafx committed Apr 15, 2023
1 parent 1f07395 commit cdfa678
Show file tree
Hide file tree
Showing 2 changed files with 48 additions and 1 deletion.
15 changes: 14 additions & 1 deletion src/main/java/org/junitpioneer/jupiter/StdIn.java
Expand Up @@ -27,14 +27,27 @@ public class StdIn extends InputStream {
private final StringReader reader;
private final StringWriter writer = new StringWriter();

/**
* Keeps track of number of bytes that are still available to {@link InputStream#read() read()}.
*/
private int availableBytes;

public StdIn(String[] values) {
reader = new StringReader(String.join(StdIoExtension.SEPARATOR, values));
var mockedInput = String.join(StdIoExtension.SEPARATOR, values);
reader = new StringReader(mockedInput);
availableBytes = mockedInput.getBytes().length;
}

@Override
public int available() throws IOException {
return availableBytes;
}

@Override
public int read() throws IOException {
int reading = reader.read();
if (reading != -1) {
availableBytes--;
writer.write(reading);
}
return reading;
Expand Down
34 changes: 34 additions & 0 deletions src/test/java/org/junitpioneer/jupiter/StdIoExtensionTests.java
Expand Up @@ -106,6 +106,34 @@ void catchesNothing(StdIn in) {
assertThat(in.capturedLines()).containsExactly("");
}

@Test
@StdIo({ "Doth homage to his new-appearing sight", "Serving with looks his sacred majesty;" })
@DisplayName("for non-empty input, available() reports input's number of bytes")
void everythingAvailableBeforeRead(StdIn in) throws IOException {
int inputLength = ("Doth homage to his new-appearing sight" + System.getProperty("line.separator")
+ "Serving with looks his sacred majesty;").getBytes().length;

assertThat(in.available()).isEqualTo(inputLength);
}

@Test
@StdIo({ "Doth homage to his new-appearing sight" })
@DisplayName("for non-empty input after partial read, available() reports correctly reduced number of bytes")
void somethingAvailableAfterRead(StdIn in) throws IOException {
int bytesToRead = 16;
app.read(bytesToRead);
int remainingLength = "Doth homage to his new-appearing sight".getBytes().length - bytesToRead;

assertThat(in.available()).isEqualTo(remainingLength);
}

@Test
@StdIo("")
@DisplayName("for empty input, available() returns 0 available bytes")
void nothingAvailableWhenEmpty(StdIn in) throws IOException {
assertThat(in.available()).isEqualTo(0);
}

@Test
@StdIo({ "And having climbed the steep-up heavenly hill,", "Resembling strong youth in his middle age," })
@DisplayName("catches the input from the standard in and the output on the standard out")
Expand Down Expand Up @@ -357,6 +385,12 @@ public void read() throws IOException {
lines.add(reader.readLine());
}

public void read(int byteCount) throws IOException {
for (int i = 0; i < byteCount; i++) {
System.in.read();
}
}

public void readAndWrite() throws IOException {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
lines.add(reader.readLine());
Expand Down

0 comments on commit cdfa678

Please sign in to comment.