8316017: Refactor timeout handler in PassFailJFrame
Reviewed-by: prr
This commit is contained in:
parent
202c0137b8
commit
3abd772672
@ -34,6 +34,8 @@ import java.awt.Rectangle;
|
|||||||
import java.awt.Robot;
|
import java.awt.Robot;
|
||||||
import java.awt.Toolkit;
|
import java.awt.Toolkit;
|
||||||
import java.awt.Window;
|
import java.awt.Window;
|
||||||
|
import java.awt.event.ActionEvent;
|
||||||
|
import java.awt.event.ActionListener;
|
||||||
import java.awt.event.WindowAdapter;
|
import java.awt.event.WindowAdapter;
|
||||||
import java.awt.event.WindowEvent;
|
import java.awt.event.WindowEvent;
|
||||||
import java.awt.event.WindowListener;
|
import java.awt.event.WindowListener;
|
||||||
@ -155,14 +157,26 @@ public class PassFailJFrame {
|
|||||||
* Prefix for the user-provided failure reason.
|
* Prefix for the user-provided failure reason.
|
||||||
*/
|
*/
|
||||||
private static final String FAILURE_REASON = "Failure Reason:\n";
|
private static final String FAILURE_REASON = "Failure Reason:\n";
|
||||||
|
/**
|
||||||
|
* The failure reason message when the user didn't provide one.
|
||||||
|
*/
|
||||||
|
private static final String EMPTY_REASON = "(no reason provided)";
|
||||||
|
|
||||||
private static final List<Window> windowList = new ArrayList<>();
|
private static final List<Window> windowList = new ArrayList<>();
|
||||||
private static final Timer timer = new Timer(0, null);
|
|
||||||
private static final CountDownLatch latch = new CountDownLatch(1);
|
private static final CountDownLatch latch = new CountDownLatch(1);
|
||||||
|
|
||||||
private static volatile boolean failed;
|
private static TimeoutHandler timeoutHandler;
|
||||||
private static volatile boolean timeout;
|
|
||||||
private static volatile String testFailedReason;
|
/**
|
||||||
|
* The description of why the test fails.
|
||||||
|
* <p>
|
||||||
|
* Note: <strong>do not use</strong> this field directly,
|
||||||
|
* use the {@link #setFailureReason(String) setFailureReason} and
|
||||||
|
* {@link #getFailureReason() getFailureReason} methods to modify and
|
||||||
|
* to read its value.
|
||||||
|
*/
|
||||||
|
private static String failureReason;
|
||||||
|
|
||||||
private static final AtomicInteger imgCounter = new AtomicInteger(0);
|
private static final AtomicInteger imgCounter = new AtomicInteger(0);
|
||||||
|
|
||||||
@ -319,42 +333,27 @@ public class PassFailJFrame {
|
|||||||
frame = new JFrame(title);
|
frame = new JFrame(title);
|
||||||
frame.setLayout(new BorderLayout());
|
frame.setLayout(new BorderLayout());
|
||||||
|
|
||||||
|
JLabel testTimeoutLabel = new JLabel("", JLabel.CENTER);
|
||||||
|
timeoutHandler = new TimeoutHandler(testTimeoutLabel, testTimeOut);
|
||||||
|
frame.add(testTimeoutLabel, BorderLayout.NORTH);
|
||||||
|
|
||||||
JTextComponent text = instructions.startsWith("<html>")
|
JTextComponent text = instructions.startsWith("<html>")
|
||||||
? configureHTML(instructions, rows, columns)
|
? configureHTML(instructions, rows, columns)
|
||||||
: configurePlainText(instructions, rows, columns);
|
: configurePlainText(instructions, rows, columns);
|
||||||
text.setEditable(false);
|
text.setEditable(false);
|
||||||
|
|
||||||
long tTimeout = TimeUnit.MINUTES.toMillis(testTimeOut);
|
|
||||||
|
|
||||||
final JLabel testTimeoutLabel = new JLabel(String.format("Test " +
|
|
||||||
"timeout: %s", convertMillisToTimeStr(tTimeout)), JLabel.CENTER);
|
|
||||||
final long startTime = System.currentTimeMillis();
|
|
||||||
timer.setDelay(1000);
|
|
||||||
timer.addActionListener((e) -> {
|
|
||||||
long leftTime = tTimeout - (System.currentTimeMillis() - startTime);
|
|
||||||
if ((leftTime < 0) || failed) {
|
|
||||||
timer.stop();
|
|
||||||
testFailedReason = FAILURE_REASON
|
|
||||||
+ "Timeout User did not perform testing.";
|
|
||||||
timeout = true;
|
|
||||||
latch.countDown();
|
|
||||||
}
|
|
||||||
testTimeoutLabel.setText(String.format("Test timeout: %s", convertMillisToTimeStr(leftTime)));
|
|
||||||
});
|
|
||||||
timer.start();
|
|
||||||
frame.add(testTimeoutLabel, BorderLayout.NORTH);
|
|
||||||
frame.add(new JScrollPane(text), BorderLayout.CENTER);
|
frame.add(new JScrollPane(text), BorderLayout.CENTER);
|
||||||
|
|
||||||
JButton btnPass = new JButton("Pass");
|
JButton btnPass = new JButton("Pass");
|
||||||
btnPass.addActionListener((e) -> {
|
btnPass.addActionListener((e) -> {
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
timer.stop();
|
timeoutHandler.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
JButton btnFail = new JButton("Fail");
|
JButton btnFail = new JButton("Fail");
|
||||||
btnFail.addActionListener((e) -> {
|
btnFail.addActionListener((e) -> {
|
||||||
getFailureReason();
|
requestFailureReason();
|
||||||
timer.stop();
|
timeoutHandler.stop();
|
||||||
});
|
});
|
||||||
|
|
||||||
JPanel buttonsPanel = new JPanel();
|
JPanel buttonsPanel = new JPanel();
|
||||||
@ -480,12 +479,58 @@ public class PassFailJFrame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static final class TimeoutHandler implements ActionListener {
|
||||||
|
private final long endTime;
|
||||||
|
|
||||||
|
private final Timer timer;
|
||||||
|
|
||||||
|
private final JLabel label;
|
||||||
|
|
||||||
|
public TimeoutHandler(final JLabel label, final long testTimeOut) {
|
||||||
|
endTime = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(testTimeOut);
|
||||||
|
|
||||||
|
this.label = label;
|
||||||
|
|
||||||
|
timer = new Timer(1000, this);
|
||||||
|
timer.start();
|
||||||
|
updateTime(testTimeOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void actionPerformed(ActionEvent e) {
|
||||||
|
long leftTime = endTime - System.currentTimeMillis();
|
||||||
|
if (leftTime < 0) {
|
||||||
|
timer.stop();
|
||||||
|
setFailureReason(FAILURE_REASON
|
||||||
|
+ "Timeout - User did not perform testing.");
|
||||||
|
latch.countDown();
|
||||||
|
}
|
||||||
|
updateTime(leftTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateTime(final long leftTime) {
|
||||||
|
if (leftTime < 0) {
|
||||||
|
label.setText("Test timeout: 00:00:00");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
long hours = leftTime / 3_600_000;
|
||||||
|
long minutes = (leftTime - hours * 3_600_000) / 60_000;
|
||||||
|
long seconds = (leftTime - hours * 3_600_000 - minutes * 60_000) / 1_000;
|
||||||
|
label.setText(String.format("Test timeout: %02d:%02d:%02d",
|
||||||
|
hours, minutes, seconds));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void stop() {
|
||||||
|
timer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static final class WindowClosingHandler extends WindowAdapter {
|
private static final class WindowClosingHandler extends WindowAdapter {
|
||||||
@Override
|
@Override
|
||||||
public void windowClosing(WindowEvent e) {
|
public void windowClosing(WindowEvent e) {
|
||||||
testFailedReason = FAILURE_REASON
|
setFailureReason(FAILURE_REASON
|
||||||
+ "User closed a window";
|
+ "User closed a window");
|
||||||
failed = true;
|
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -579,14 +624,28 @@ public class PassFailJFrame {
|
|||||||
JOptionPane.INFORMATION_MESSAGE);
|
JOptionPane.INFORMATION_MESSAGE);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String convertMillisToTimeStr(long millis) {
|
/**
|
||||||
if (millis < 0) {
|
* Sets the failure reason which describes why the test fails.
|
||||||
return "00:00:00";
|
* This method ensures the {@code failureReason} field does not change
|
||||||
|
* after it's set to a non-{@code null} value.
|
||||||
|
* @param reason the description of why the test fails
|
||||||
|
* @throws IllegalArgumentException if the {@code reason} parameter
|
||||||
|
* is {@code null}
|
||||||
|
*/
|
||||||
|
private static synchronized void setFailureReason(final String reason) {
|
||||||
|
if (reason == null) {
|
||||||
|
throw new IllegalArgumentException("The failure reason must not be null");
|
||||||
}
|
}
|
||||||
long hours = millis / 3_600_000;
|
if (failureReason == null) {
|
||||||
long minutes = (millis - hours * 3_600_000) / 60_000;
|
failureReason = reason;
|
||||||
long seconds = (millis - hours * 3_600_000 - minutes * 60_000) / 1_000;
|
}
|
||||||
return String.format("%02d:%02d:%02d", hours, minutes, seconds);
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@return the description of why the test fails}
|
||||||
|
*/
|
||||||
|
private static synchronized String getFailureReason() {
|
||||||
|
return failureReason;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -607,30 +666,18 @@ public class PassFailJFrame {
|
|||||||
latch.await();
|
latch.await();
|
||||||
invokeAndWait(PassFailJFrame::disposeWindows);
|
invokeAndWait(PassFailJFrame::disposeWindows);
|
||||||
|
|
||||||
if (timeout) {
|
String failure = getFailureReason();
|
||||||
throw new RuntimeException(testFailedReason);
|
if (failure != null) {
|
||||||
}
|
throw new RuntimeException(failure);
|
||||||
|
|
||||||
if (failed) {
|
|
||||||
throw new RuntimeException("Test failed! : " + testFailedReason);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
System.out.println("Test passed!");
|
System.out.println("Test passed!");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Disposes of all the windows. It disposes of the test instruction frame
|
* Requests the description of the test failure reason from the tester.
|
||||||
* and all other windows added via {@link #addTestWindow(Window)}.
|
|
||||||
*/
|
*/
|
||||||
private static synchronized void disposeWindows() {
|
private static void requestFailureReason() {
|
||||||
windowList.forEach(Window::dispose);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read the test failure reason and add the reason to the test result
|
|
||||||
* example in the jtreg .jtr file.
|
|
||||||
*/
|
|
||||||
private static void getFailureReason() {
|
|
||||||
final JDialog dialog = new JDialog(frame, "Test Failure ", true);
|
final JDialog dialog = new JDialog(frame, "Test Failure ", true);
|
||||||
dialog.setTitle("Failure reason");
|
dialog.setTitle("Failure reason");
|
||||||
JPanel jPanel = new JPanel(new BorderLayout());
|
JPanel jPanel = new JPanel(new BorderLayout());
|
||||||
@ -638,7 +685,9 @@ public class PassFailJFrame {
|
|||||||
|
|
||||||
JButton okButton = new JButton("OK");
|
JButton okButton = new JButton("OK");
|
||||||
okButton.addActionListener((ae) -> {
|
okButton.addActionListener((ae) -> {
|
||||||
testFailedReason = FAILURE_REASON + jTextArea.getText();
|
String text = jTextArea.getText();
|
||||||
|
setFailureReason(FAILURE_REASON
|
||||||
|
+ (!text.isEmpty() ? text : EMPTY_REASON));
|
||||||
dialog.setVisible(false);
|
dialog.setVisible(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -653,11 +702,22 @@ public class PassFailJFrame {
|
|||||||
dialog.pack();
|
dialog.pack();
|
||||||
dialog.setVisible(true);
|
dialog.setVisible(true);
|
||||||
|
|
||||||
failed = true;
|
// Ensure the test fails even if the dialog is closed
|
||||||
|
// without clicking the OK button
|
||||||
|
setFailureReason(FAILURE_REASON + EMPTY_REASON);
|
||||||
|
|
||||||
dialog.dispose();
|
dialog.dispose();
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disposes of all the windows. It disposes of the test instruction frame
|
||||||
|
* and all other windows added via {@link #addTestWindow(Window)}.
|
||||||
|
*/
|
||||||
|
private static synchronized void disposeWindows() {
|
||||||
|
windowList.forEach(Window::dispose);
|
||||||
|
}
|
||||||
|
|
||||||
private static void positionInstructionFrame(final Position position) {
|
private static void positionInstructionFrame(final Position position) {
|
||||||
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
|
||||||
|
|
||||||
@ -827,8 +887,7 @@ public class PassFailJFrame {
|
|||||||
* @param reason the reason why the test is failed
|
* @param reason the reason why the test is failed
|
||||||
*/
|
*/
|
||||||
public static void forceFail(String reason) {
|
public static void forceFail(String reason) {
|
||||||
failed = true;
|
setFailureReason(FAILURE_REASON + reason);
|
||||||
testFailedReason = FAILURE_REASON + reason;
|
|
||||||
latch.countDown();
|
latch.countDown();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user