// Copyright © 2011-2013, Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0
package fi.jumi.core.results;
import fi.jumi.api.drivers.TestId;
import fi.jumi.core.api.*;
import javax.annotation.concurrent.NotThreadSafe;
import java.util.*;
@NotThreadSafe
public class SuiteProgressMeter extends NullSuiteListener {
private final Map<TestFile, FileState> files = new HashMap<>();
private final Map<RunId, RunState> runs = new HashMap<>();
private Status status = Status.INDETERMINATE;
/**
* @return value between {@code 0.0} and {@code 1.0}
*/
public double getProgress() {
if (status == Status.INDETERMINATE) {
return 0;
}
double sum = 0;
for (FileState file : files.values()) {
sum += file.getProgress();
}
return files.size() == 0 ? 1 :
sum / files.size();
}
public Status getStatus() {
return status;
}
// suite events
@Override
public void onTestFileFound(TestFile testFile) {
files.put(testFile, new FileState());
}
@Override
public void onTestFileFinished(TestFile testFile) {
files.get(testFile).onTestFileFinished();
}
@Override
public void onTestFound(TestFile testFile, TestId testId, String name) {
files.get(testFile).onTestFound(testId);
}
@Override
public void onRunStarted(RunId runId, TestFile testFile) {
runs.put(runId, new RunState(files.get(testFile)));
}
@Override
public void onTestStarted(RunId runId, TestId testId) {
runs.get(runId).onTestStarted(testId);
}
@Override
public void onTestFinished(RunId runId) {
runs.get(runId).onTestFinished();
}
@Override
public void onRunFinished(RunId runId) {
runs.remove(runId); // let memory be garbage collected
}
@Override
public void onAllTestFilesFound() {
assert status == Status.INDETERMINATE : "status was " + status;
status = Status.IN_PROGRESS;
}
@Override
public void onSuiteFinished() {
// The status can be INDETERMINATE if the JVM terminated unexpectedly,
// so we should NOT assert here that the status is IN_PROGRESS
status = Status.COMPLETE;
}
@NotThreadSafe
public enum Status {
INDETERMINATE,
IN_PROGRESS,
COMPLETE
}
@NotThreadSafe
private static class RunState {
private final Deque<TestId> activeTests = new ArrayDeque<>();
private final FileState file;
public RunState(FileState file) {
this.file = file;
}
public void onTestStarted(TestId testId) {
activeTests.push(testId);
}
public void onTestFinished() {
TestId finishedTest = activeTests.pop();
file.onTestFinished(finishedTest);
}
}
@NotThreadSafe
private static class FileState {
private Set<TestId> allTests = new HashSet<>();
private Set<TestId> finishedTests = new HashSet<>();
private boolean fileFinished = false;
public double getProgress() {
if (fileFinished) {
return 1;
}
return allTests.size() == 0 ? 0 :
(double) finishedTests.size() / (double) allTests.size();
}
public void onTestFound(TestId testId) {
allTests.add(testId);
}
private void onTestFinished(TestId testId) {
finishedTests.add(testId);
}
public void onTestFileFinished() {
fileFinished = true;
// let memory be garbage collected
allTests = null;
finishedTests = null;
}
}
}