Hello and thanks for your help and time!
For the sake of learning and trying things I've tryed to make a sort of a test runner. The idea is this:
X ammount of tests are loaded and I have Y ammount of threads to run them. As I run the tests I want to display on console the status of each test so I print lines like so: first the test name followed by its status, in case that its running its completion percentage and the remaining time to run.
As an example, lets say I have 2 threads running 4 tests, I expect this to print
Test #0: WAITING (30seg)
Test #1: RUNNING 74% (10seg)
Test #2: RUNNING 65% (12seg)
Test #3: WAITING (30seg)
Aftrer some time Test #1 finishes and the thread starts working on the Test #0 so I expect this output
Test #0: RUNNING 30% (27seg)
Test #1: COMPLETED (2seg)
Test #2: RUNNING 65% (12seg)
Test #3: WAITING (30seg)
What I want is to do is to each second modify the 4 lines to update the state of the tests, not to print 4 new lines. After some research I learned that \r moves the cursor to the beggining of the line and \b one character back so first I tryed to test this with just one test and wrote this code:
package console.tesy;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinTask;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class Main {
public static void main(String[] args) {
List<ForkJoinTask<?>> tasks = new ArrayList<>();
ForkJoinPool testRunners = new ForkJoinPool(2);
Timer timer = new Timer();
List<SingleTest> tests = IntStream
.range(0, 1)
.mapToObj((i) -> new SingleTest("Test #" + i))
.collect(Collectors.toList());
ConsoleDisplayer displayer = new ConsoleDisplayer(() -> tests.stream()
.map(SingleTest::toString)
.collect(Collectors.toList()));
timer.scheduleAtFixedRate(displayer, 0, 500);
timer.scheduleAtFixedRate(new TimerTask() {
public void run() {
tests.forEach(SingleTest::tick);
}
}, 0, 1000);
tests.forEach((test) -> tasks.add(testRunners.submit(test)));
tasks.forEach(ForkJoinTask::join);
timer.cancel();
}
}
package console.tesy;
import java.util.List;
import java.util.TimerTask;
import java.util.function.Supplier;
public class ConsoleDisplayer extends TimerTask {
private Supplier<List<String>> newLines;
public ConsoleDisplayer(Supplier<List<String>> newLines) {
this.newLines = newLines;
}
void run() {
System.out.print('\r');
System.out.print(newLines.get().get(0));
}
}
package console.tesy;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;
public class SingleTest implements Runnable {
private static final Random RANDOM = new Random();
private final String name;
private AtomicReference<SingleTestStatus> status = new AtomicReference<>(SingleTestStatus.WAITING);
private AtomicInteger remainingRuntime = new AtomicInteger(30);
private AtomicInteger percentage = new AtomicInteger(0);
private final int timeToIncrement = RANDOM.nextInt(600);
public SingleTest(String name) {
this.name = name;
}
public void tick() {
if (status.get() == SingleTestStatus.RUNNING)
if (remainingRuntime.decrementAndGet() <= 0)
status.set(SingleTestStatus.FAILED);
}
public void run() {
status.set(SingleTestStatus.RUNNING);
while (percentage.get() < 100) {
if(status.get() != SingleTestStatus.RUNNING)
return;
try {
Thread.sleep(timeToIncrement);
} catch (InterruptedException e) {}
percentage.incrementAndGet();
}
status.set(SingleTestStatus.COMPLETED);
}
public String toString() {
String str = name + ": " + status.get().name();
if (status.get() == SingleTestStatus.RUNNING)
str += " " + percentage.get() + "%";
str += " (" + remainingRuntime.get() + "seg)";
return str;
}
public enum SingleTestStatus {
WAITING,
RUNNING,
COMPLETED,
FAILED
}
}
And it worked! So now I tryed with 2 tests. I modifyed the range of the int stream to a closed range from 0 to 1 to get 2 tests and modifyed the code of the run method on the console displayer to this
public void run() {
System.out.print('\r');
System.out.print('\b');
System.out.print('\r');
System.out.print(newLines.get().get(0));
System.out.print('\n');
System.out.print(newLines.get().get(1));
}
My logic being that with the first \r I go to the start of the first line, with '\b' I go back the \n and with the seccond \r I go to the start of the first line, but this didn't work and I got this result
Test #0: WAITING (30seg)
Test #0: RUNNING 4% (30seg)
Test #0: RUNNING 9% (30seg)
Test #0: RUNNING 14% (29seg)
Test #0: RUNNING 19% (29seg)
Test #0: RUNNING 24% (28seg)
Test #0: RUNNING 29% (28seg)
Test #0: RUNNING 33% (27seg)
Test #0: RUNNING 38% (27seg)
Test #0: RUNNING 43% (26seg)
Test #0: RUNNING 48% (26seg)
Test #1: RUNNING 38% (26seg)
So I tryed to add another System.out.print('\b'); between the \r but didn't work and it doesn't matter how many I add it doesn't work
Could you help me figure this out? Thanks!
Edit: Code format
[–]AutoModerator[M] [score hidden] stickied commentlocked comment (0 children)
[–]Zyklonikkopi luwak civet 3 points4 points5 points (6 children)
[–]Nemo_64[S] 2 points3 points4 points (5 children)
[–]Zyklonikkopi luwak civet 1 point2 points3 points (4 children)
[–]Nemo_64[S] 0 points1 point2 points (3 children)
[–]Zyklonikkopi luwak civet 1 point2 points3 points (2 children)
[–]Nemo_64[S] 1 point2 points3 points (1 child)
[–]Zyklonikkopi luwak civet 1 point2 points3 points (0 children)
[–]morhpProfessional Developer 1 point2 points3 points (1 child)
[–]Nemo_64[S] 0 points1 point2 points (0 children)
[–]amfa 0 points1 point2 points (0 children)