package bluej.testmgr;

import bluej.BlueJTheme;
import bluej.Config;
import bluej.debugger.DebuggerTestResult;
import bluej.debugger.SourceLocation;
import bluej.pkgmgr.Package;
import bluej.pkgmgr.Project;
import bluej.utility.JavaNames;
import bluej.utility.javafx.JavaFXUtil;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Orientation;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.ProgressBar;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.SplitPane;
import javafx.scene.control.TextArea;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.stage.Window;
import threadchecker.OnThread;
import threadchecker.Tag;


| A JavaFX based user interface to run tests. | | @author Andrew Patterson | public @OnThread(Tag.FXPlatform) class TestDisplayFrame { @OnThread(Tag.FXPlatform) private static TestDisplayFrame singleton = null; @OnThread(Tag.FXPlatform) private static BooleanProperty frameShowing = new SimpleBooleanProperty(false); @OnThread(Tag.FXPlatform) public static TestDisplayFrame getTestDisplay() { if (singleton == null) { singleton = new TestDisplayFrame(); } return singleton; } public static BooleanProperty showingProperty() { return frameShowing; } static { JavaFXUtil.addChangeListenerPlatform(frameShowing, doShow -> { TestDisplayFrame testDisplayFrame = getTestDisplay(); if (doShow) { if (!testDisplayFrame.frame.isShowing()) { testDisplayFrame.frame.show(); } testDisplayFrame.frame.toFront(); } else { if (testDisplayFrame.frame.isShowing()) { testDisplayFrame.frame.hide(); } } }); } private final Image failureIcon = Config.getFixedImageAsFXImage("failure.gif"); private final Image errorIcon = Config.getFixedImageAsFXImage("error.gif"); private final Image okIcon = Config.getFixedImageAsFXImage("ok.gif");
| The actual window | private Stage frame;
| The list of test results which is displayed in testNames | private ObservableList<DebuggerTestResult> testEntries;
| The top list of test names | private ListView<DebuggerTestResult> testNames; private ProgressBar progressBar; private final SimpleIntegerProperty errorCount; private final SimpleIntegerProperty failureCount; private final SimpleIntegerProperty totalTimeMs; private final SimpleIntegerProperty testTotal;
| Keeps track of whether we are running a single test | or multiple tests | private boolean doingMultiple; private BooleanBinding hasErrors; private BooleanBinding hasFailures; private BooleanBinding hasFailuresOrErrors;
| The text field showing the exception message | private TextArea exceptionMessageField; private Button showSourceButton; private Project project; public TestDisplayFrame() { testTotal = new SimpleIntegerProperty(0); errorCount = new SimpleIntegerProperty(0); failureCount = new SimpleIntegerProperty(0); totalTimeMs = new SimpleIntegerProperty(0); doingMultiple = false; createUI(); }
| Show or hide the test display window. | public void showTestDisplay(boolean doShow) { frameShowing.set(doShow); if (doShow) { frame.toFront(); } }
| Create the user-interface for the error display dialog. | protected void createUI() { frame = new Stage(); frame.setTitle(Config.getString("testdisplay.title")); frame.setOnShown(e -> { if (!frameShowing.get()) frameShowing.set(true);
/*org.scenicview.ScenicView.show(frame.getScene()); }); frame.setOnHidden(e -> { if (frameShowing.get()) frameShowing.set(false); }); BlueJTheme.setWindowIconFX(frame); frame.setMinWidth(500.0); frame.setMinHeight(250.0); Config.loadAndTrackPositionAndSize(frame, "bluej.testdisplay"); SplitPane mainDivider = new SplitPane(); mainDivider.setOrientation(Orientation.VERTICAL); testEntries = FXCollections.observableArrayList(); testNames = new ListView(); testNames.setMinHeight(50.0); testNames.setPrefHeight(150.0); testNames.setEditable(false); testNames.getSelectionModel().setSelectionMode(SelectionMode.SINGLE); JavaFXUtil.addChangeListener(testNames.getSelectionModel().selectedItemProperty(), this::selected); testNames.setDisable(false); JavaFXUtil.addStyleClass(testNames, "test-names"); testNames.setItems(testEntries); testNames.setCellFactory(col -> new TestResultCell()); mainDivider.getItems().add(testNames); VBox content = new VBox(); mainDivider.getItems().add(content); Config.rememberDividerPosition(frame, mainDivider, "bluej.testdisplay.dividerpos"); progressBar = new ProgressBar(); JavaFXUtil.addStyleClass(progressBar, "test-progress-bar"); progressBar.progressProperty().bind(Bindings.add(0.0, Bindings.size(testEntries)).divide(Bindings.max(1, testTotal))); hasFailuresOrErrors = Bindings.greaterThan(failureCount.add(errorCount), 0); JavaFXUtil.bindPseudoclass(progressBar, "bj-error", hasFailuresOrErrors); content.getChildren().add(progressBar); HBox counterPanel = new HBox(); JavaFXUtil.addStyleClass(counterPanel, "counter-panel"); Label fNumberOfErrors = new Label(); Label fNumberOfFailures = new Label(); Label fNumberOfRuns = new Label(); Label fTotalTime = new Label(); HBox.setHgrow(fNumberOfRuns, Priority.ALWAYS); HBox.setHgrow(fTotalTime, Priority.ALWAYS); fNumberOfErrors.textProperty().bind(errorCount.asString()); fNumberOfFailures.textProperty().bind(failureCount.asString()); fNumberOfRuns.textProperty().bind(Bindings.size(testEntries).asString().concat("/").concat(testTotal.asString())); fTotalTime.textProperty().bind(totalTimeMs.asString().concat("ms")); HBox errorPanel = new HBox(new ImageView(errorIcon), new Label(Config.getString("testdisplay.counter.errors")), fNumberOfErrors); JavaFXUtil.addStyleClass(errorPanel, "error-panel"); hasErrors = Bindings.greaterThan(errorCount, 0); JavaFXUtil.bindPseudoclass(errorPanel, "bj-non-zero", hasErrors); HBox.setHgrow(errorPanel, Priority.ALWAYS); HBox failurePanel = new HBox(new ImageView(failureIcon), new Label(Config.getString("testdisplay.counter.failures")), fNumberOfFailures); JavaFXUtil.addStyleClass(failurePanel, "error-panel"); hasFailures = Bindings.greaterThan(failureCount, 0); JavaFXUtil.bindPseudoclass(failurePanel, "bj-non-zero", hasFailures); HBox.setHgrow(failurePanel, Priority.ALWAYS); counterPanel.getChildren().addAll( new Label(Config.getString("testdisplay.counter.runs")), fNumberOfRuns, errorPanel, failurePanel, new Label(Config.getString("testdisplay.counter.totalTime")), fTotalTime ); content.getChildren().add(counterPanel); exceptionMessageField = new TextArea(""); JavaFXUtil.addStyleClass(exceptionMessageField, "test-output"); VBox.setVgrow(exceptionMessageField, Priority.ALWAYS); exceptionMessageField.setEditable(false); exceptionMessageField.setFocusTraversable(false); content.getChildren().add(exceptionMessageField); showSourceButton = new Button(Config.getString("testdisplay.showsource")); showSourceButton.setOnAction(e -> showSource(testNames.getSelectionModel().getSelectedItem())); showSourceButton.setDisable(true); JavaFXUtil.addStyleClass(showSourceButton, "test-show-source"); Button closeButton = new Button(Config.getString("close")); closeButton.setOnAction(e -> frame.hide()); BorderPane buttonPanel = new BorderPane(); buttonPanel.setLeft(showSourceButton); buttonPanel.setRight(closeButton); content.getChildren().add(buttonPanel); VBox surround = new VBox(mainDivider); VBox.setVgrow(mainDivider, Priority.ALWAYS); JavaFXUtil.addStyleClass(surround, "test-results"); JavaFXUtil.addStyleClass(content, "test-results-content"); frame.setScene(new Scene(surround)); Config.addTestsStylesheets(frame.getScene()); surround.addEventFilter(KeyEvent.KEY_PRESSED, e -> { if (e.getCode() == KeyCode.ESCAPE) { frame.hide(); e.consume(); } }); } protected void reset() { testEntries.clear(); errorCount.set(0); failureCount.set(0); totalTimeMs.set(0); testTotal.set(0); exceptionMessageField.setText(""); showSourceButton.setDisable(true); }
| Indicate that we are starting a bunch of tests. | | @param num The total number of tests to be run | public void startMultipleTests(Project proj, int num) { this.project = proj; doingMultiple = true; reset(); testTotal.set(num); showTestDisplay(true); } public void endMultipleTests() { doingMultiple = false; }
| Tell the dialog we are about to start a test run. | | @param num the number of tests we will run | public void startTest(Project proj, int num) { this.project = proj; if (! doingMultiple) { reset(); testTotal.set(num); } }
| Add a test result to the test displayer. | | @param dtr The test result to add | @param quiet True if the result should be added "quietly" (do not make |* test frame visible or bring it to front) */ public void addResult(DebuggerTestResult dtr, boolean quiet) { addResultQuietly(dtr); if (! quiet) { showTestDisplay(true); | |} | |} | |/** | Add a test result to the test displayer but do not | bring the test display window to the front. | | @param dtr The test result to add | public void addResultQuietly(final DebuggerTestResult dtr) { if (!dtr.isSuccess()) { if (dtr.isFailure()) failureCount.set(failureCount.get() + 1); else{ errorCount.set(errorCount.get() + 1); } } totalTimeMs.set(totalTimeMs.get() + dtr.getRunTimeMs()); testEntries.add(dtr); } public Window getWindow() { return frame; } @OnThread(Tag.FX) private class TestResultCell extends ListCell<DebuggerTestResult> { private final ImageView imageView; public TestResultCell() { setEditable(false); setText(""); imageView = new ImageView(); imageView.setMouseTransparent(true); setGraphic(imageView); setOnMouseClicked(e -> { if (e.getClickCount() == 2 && e.getButton() == MouseButton.PRIMARY) showSource(getItem()); }); } @Override public void updateItem(DebuggerTestResult item, boolean empty) { super.updateItem(item, empty); if (item == null || empty) { imageView.setImage(null); setText(""); } else { if (item.isSuccess()) imageView.setImage(okIcon); else if (item.isFailure()) imageView.setImage(failureIcon); else{ imageView.setImage(errorIcon); } if (item.getRunTimeMs() == 0) { setText(item.getName()); } else { setText(item.getName() + " (" + item.getRunTimeMs() + "ms)"); } } } } private void selected(DebuggerTestResult dtr) { if (dtr != null && (dtr.isError() || dtr.isFailure())) { exceptionMessageField.setText(dtr.getExceptionMessage() + "\n---\n" + dtr.getTrace()); exceptionMessageField.positionCaret(0); showSourceButton.setDisable(dtr.getExceptionLocation() == null); } else { exceptionMessageField.setText(""); showSourceButton.setDisable(true); } }
| Set the total execution time of tests. | @param value the value to which the totalTimeMs variable will be set to | @OnThread(Tag.FXPlatform) public void setTotalTimeMs(int value) { totalTimeMs.set(value); } private void showSource(DebuggerTestResult dtr) { if ((dtr != null) && (dtr.isError() || dtr.isFailure())) { SourceLocation exceptionLocation = dtr.getExceptionLocation(); if (exceptionLocation == null) { return; } String packageName = JavaNames.getPrefix(exceptionLocation.getClassName()); Package spackage = project.getPackage(packageName); if (spackage == null) { return; } String sourceName = exceptionLocation.getFileName(); int lineno = exceptionLocation.getLineNumber(); spackage.showSource(sourceName, lineno); } } }

.   - TestDisplayFrame
.   getTestDisplay
.   showingProperty
.   TestDisplayFrame
.   showTestDisplay
.   createUI
.   reset
.   startMultipleTests
.   endMultipleTests
.   startTest
.   addResultQuietly
.   getWindow
.   TestResultCell
.   updateItem
.   selected
.   setTotalTimeMs
.   showSource




517 neLoCode + 26 LoComm