-
Notifications
You must be signed in to change notification settings - Fork 331
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1680 from jplag/feature/idleBars
Added idle bars
- Loading branch information
Showing
8 changed files
with
231 additions
and
31 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
26 changes: 26 additions & 0 deletions
26
cli/src/main/java/de/jplag/cli/logger/CliProgressBarProvider.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
package de.jplag.cli.logger; | ||
|
||
import de.jplag.logging.ProgressBar; | ||
import de.jplag.logging.ProgressBarProvider; | ||
import de.jplag.logging.ProgressBarType; | ||
|
||
import me.tongfei.progressbar.ProgressBarBuilder; | ||
import me.tongfei.progressbar.ProgressBarStyle; | ||
|
||
/** | ||
* A ProgressBar provider, that used the tongfei progress bar library underneath, to show progress bars on the cli. | ||
*/ | ||
public class CliProgressBarProvider implements ProgressBarProvider { | ||
@Override | ||
public ProgressBar initProgressBar(ProgressBarType type, int totalSteps) { | ||
if (type.isIdleBar()) { | ||
IdleBar idleBar = new IdleBar(type.getDefaultText()); | ||
idleBar.start(); | ||
return idleBar; | ||
} else { | ||
me.tongfei.progressbar.ProgressBar progressBar = new ProgressBarBuilder().setTaskName(type.getDefaultText()).setInitialMax(totalSteps) | ||
.setStyle(ProgressBarStyle.ASCII).build(); | ||
return new TongfeiProgressBar(progressBar); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,103 @@ | ||
package de.jplag.cli.logger; | ||
|
||
import java.io.IOException; | ||
import java.io.PrintStream; | ||
|
||
import org.apache.commons.lang3.time.DurationFormatUtils; | ||
import org.jline.terminal.Terminal; | ||
import org.jline.terminal.TerminalBuilder; | ||
|
||
import de.jplag.logging.ProgressBar; | ||
|
||
/** | ||
* Prints an idle progress bar, that does not count upwards. | ||
*/ | ||
public class IdleBar implements ProgressBar { | ||
private final PrintStream output; | ||
|
||
private final Thread runner; | ||
|
||
private long startTime; | ||
private final String text; | ||
private int length; | ||
|
||
private int currentPos; | ||
private int currentDirection; | ||
|
||
private boolean running = false; | ||
|
||
public IdleBar(String text) { | ||
this.output = System.out; | ||
this.runner = new Thread(this::run); | ||
this.length = 50; | ||
this.currentDirection = -1; | ||
this.currentPos = 0; | ||
this.text = text; | ||
try { | ||
Terminal terminal = TerminalBuilder.terminal(); | ||
this.length = Math.min(terminal.getWidth() / 2, terminal.getWidth() - 50); | ||
terminal.close(); | ||
} catch (IOException ignore) { | ||
// ignore exceptions here. If we cannot access the terminal, we guess a width | ||
} | ||
if (this.length < 10) { | ||
this.length = 10; | ||
} | ||
} | ||
|
||
public void start() { | ||
this.startTime = System.currentTimeMillis(); | ||
this.running = true; | ||
this.runner.start(); | ||
} | ||
|
||
@Override | ||
public void dispose() { | ||
this.running = false; | ||
try { | ||
this.runner.join(); | ||
} catch (InterruptedException ignored) { | ||
Thread.currentThread().interrupt(); | ||
} | ||
this.output.println(); | ||
} | ||
|
||
private void run() { | ||
while (running) { | ||
this.output.print('\r'); | ||
this.output.print(printLine()); | ||
if (currentPos == 0 || currentPos == length - 1) { | ||
currentDirection *= -1; | ||
} | ||
try { | ||
Thread.sleep(200); | ||
} catch (InterruptedException ignore) { | ||
Thread.currentThread().interrupt(); | ||
} | ||
currentPos += currentDirection; | ||
} | ||
} | ||
|
||
private String printLine() { | ||
StringBuilder line = new StringBuilder(); | ||
line.append(this.text).append(' '); | ||
|
||
line.append('<'); | ||
line.append(" ".repeat(Math.max(0, currentPos))); | ||
line.append("<+>"); | ||
line.append(" ".repeat(Math.max(0, length - currentPos - 1))); | ||
line.append('>'); | ||
|
||
long timeRunning = System.currentTimeMillis() - this.startTime; | ||
line.append(' '); | ||
String duration = DurationFormatUtils.formatDuration(timeRunning, "H:mm:ss"); | ||
line.append(duration); | ||
|
||
return line.toString(); | ||
} | ||
|
||
@Override | ||
public void step(int number) { | ||
// does nothing, because the idle bar has no steps | ||
} | ||
} |
20 changes: 0 additions & 20 deletions
20
cli/src/main/java/de/jplag/cli/logger/TongfeiProgressBarProvider.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
package de.jplag.cli.logger; | ||
|
||
import java.io.ByteArrayOutputStream; | ||
import java.io.PrintStream; | ||
|
||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.Test; | ||
|
||
class IdleBarTest { | ||
private static final String TEST_BAR_TEXT = "Test"; | ||
private static final long IDLE_BAR_ANIMATION_DELAY = 200; | ||
|
||
private static final int TARGET_FRAME_NUMBER = 5; | ||
|
||
/** | ||
* Tests if the output of the idle bar looks plausible | ||
*/ | ||
@Test | ||
void testIdleBarPlausible() { | ||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); | ||
PrintStream oldSystemOut = System.out; | ||
System.setOut(new PrintStream(outputStream)); | ||
|
||
IdleBar idleBar = new IdleBar(TEST_BAR_TEXT); | ||
idleBar.start(); | ||
while (outputStream.toString().split("\\r").length <= TARGET_FRAME_NUMBER) { | ||
Thread.yield(); | ||
} | ||
|
||
idleBar.dispose(); | ||
System.setOut(oldSystemOut); | ||
|
||
String result = outputStream.toString(); | ||
String[] animationFrames = result.substring(1).split("\\r"); | ||
|
||
String firstFrame = animationFrames[0]; | ||
int numberOfSpaces = firstFrame.lastIndexOf('>') - firstFrame.indexOf('<') - 3 - 1; | ||
for (int i = 0; i < TARGET_FRAME_NUMBER; i++) { | ||
checkIdleBarOutput(animationFrames[i], i, numberOfSpaces); | ||
} | ||
} | ||
|
||
/** | ||
* Checks that the given string matches the expected content of an animation frame | ||
* @param output The animation frame | ||
* @param frameIndex The index of the frame | ||
* @param numberOfSpaces The number of spaces within the bar | ||
*/ | ||
private void checkIdleBarOutput(String output, int frameIndex, int numberOfSpaces) { | ||
int pass = frameIndex / numberOfSpaces; | ||
int offset = frameIndex % numberOfSpaces; | ||
if (pass % 2 == 1) { | ||
offset = numberOfSpaces - offset; | ||
} | ||
|
||
String expectedOutput = TEST_BAR_TEXT + ' ' + '<' + " ".repeat(offset) + "<+>" + " ".repeat(numberOfSpaces - offset) + '>'; | ||
|
||
int endOfPredictableOutput = output.lastIndexOf(' '); | ||
String predictableOutput = output.substring(0, endOfPredictableOutput); | ||
String time = output.substring(endOfPredictableOutput + 1).trim(); | ||
|
||
Assertions.assertEquals(expectedOutput, predictableOutput); | ||
Assertions.assertTrue(time.matches("[0-9]:[0-9]{2}:[0-9]{2}"), "Invalid format for time"); | ||
|
||
String[] timeParts = time.split(":"); | ||
int seconds = Integer.parseInt(timeParts[0]) * 60 * 60 + Integer.parseInt(timeParts[1]) * 60 + Integer.parseInt(timeParts[2]); | ||
int expectedTime = (int) ((IDLE_BAR_ANIMATION_DELAY * frameIndex) / 1000); | ||
Assertions.assertTrue(Math.abs(seconds - expectedTime) < 1, "Frame time of by more than one second"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters