package jmri.util; import java.awt.BorderLayout; import java.awt.GraphicsEnvironment; import java.util.concurrent.Callable; import javax.swing.JButton; import javax.swing.JTextField; import javax.swing.JWindow; import javax.swing.SwingUtilities; import jmri.util.junit.annotations.DisabledIfHeadless; import org.junit.jupiter.api.*; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertInstanceOf; import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertTrue; /** * * @author Svata Dedic Copyright (c) 2019 */ public class ValidatingInputPaneTest { /** * Checks that inconvertible input produces an error message. */ @Test public void testInconvertibleInputFails() { ValidatingInputPane intValidator = new ValidatingInputPane<>((s) -> Integer.parseInt(s)); intValidator.validateText("nonInteger"); assertTrue( intValidator.hasError(), "Non-number must produce error"); NumberFormatException ex = assertInstanceOf(NumberFormatException.class, intValidator.getException()); assertNotNull( ex, "Converter exception expected"); } /** * Empty input should be accepted as no-op */ @Test public void testEmptyInputOK() { ValidatingInputPane intValidator = new ValidatingInputPane<>((s) -> Integer.parseInt(s)); intValidator.validateText(""); assertFalse( intValidator.hasError(), "Empty string must be accepted"); } @Test public void testValidationRejects() { ValidatingInputPane intValidator = new ValidatingInputPane<>((s) -> Integer.parseInt(s)); intValidator.validator((i) -> i >= 5); intValidator.validateText("3"); assertTrue( intValidator.hasError(), "Bad number should produce error"); assertNull( intValidator.getException(), "Validator rejection does not record an exception"); assertEquals( Bundle.getMessage("InputDialogError"), intValidator.getErrorMessage(), "General message expected"); } @Test public void testValidationPasses() { ValidatingInputPane intValidator = new ValidatingInputPane<>((s) -> Integer.parseInt(s)); intValidator.validator((i) -> i >= 5); intValidator.validateText("6"); assertFalse( intValidator.hasError(), "Validation must pass"); assertNull( intValidator.getException(), "Exception must be null on OK result"); assertNull( intValidator.getErrorMessage(), "No message for OK result"); } @Test public void testValidationThrowsCustomMessage() { setupLargeValidator(); // now should fail: intValidator.validateText("6"); assertNotNull( intValidator.getException(), "Exception must non-null"); assertEquals( "Large", intValidator.getException().getMessage(), "Exception must non-null"); assertEquals( "Large", intValidator.getErrorMessage(), "Invalid message"); // should pass intValidator.validateText("3"); assertNull( intValidator.getErrorMessage(), "Message must be null on successful validation"); assertFalse( intValidator.hasError(), "No error must be indicated"); } private ValidatingInputPane intValidator; private JButton control; private void setupLargeValidator() { setupLargeValidator(false); } private void setupLargeValidator(boolean useControl) { intValidator = new ValidatingInputPane<>((s) -> Integer.parseInt(s)); if (useControl) { control = new JButton("OK"); intValidator = intValidator.attachConfirmUI(control); } intValidator.validator((i) -> { if (i >= 5) { throw new IllegalArgumentException("Large"); } return true; }); } @Test public void testErrorStatusClearedAfterCorrection() { setupLargeValidator(); // now should fail: intValidator.validateText("6"); assertNotNull( intValidator.getException(), "Exception must non-null"); // should pass intValidator.validateText("3"); assertNull( intValidator.getErrorMessage(), "Message must be null on successful validation"); assertFalse( intValidator.hasError(), "No error must be indicated"); } /** * Wraps the tested panel into a Window and makes it visible. Runs the * Callable in EDT to properly process UI. Terminate * the test without failure in headless env. */ private void testInGUI(Callable check) throws Exception { // terminate tests which require GUI assertFalse(GraphicsEnvironment.isHeadless()); // display the panel JWindow dlg = new JWindow(); dlg.setLayout(new BorderLayout()); dlg.add(intValidator, BorderLayout.CENTER); if (control != null) { dlg.add(control, BorderLayout.SOUTH); } dlg.setVisible(true); Exception[] out = new Exception[1]; SwingUtilities.invokeAndWait(() -> { try { check.call(); } catch (Exception ex) { out[0] = ex; } }); dlg.dispose(); if (out[0] != null) { throw out[0]; } } @Test @DisabledIfHeadless public void testValidationHappensWhenAfterDisplayed() throws Exception { setupLargeValidator(); intValidator.setText("6"); // no validation yet. assertNull(intValidator.getErrorMessage()); testInGUI(() -> { assertTrue(intValidator.hasError()); return null; }); } @Test @DisabledIfHeadless public void testTextChangeValidatesFalse() throws Exception { setupLargeValidator(); intValidator.setText("3"); // no validation yet. assertNull(intValidator.getErrorMessage()); testInGUI(() -> { assertFalse(intValidator.hasError()); JTextField input = intValidator.getTextField(); input.getDocument().insertString(1, "3", null); // now the text should be validated and error message present assertTrue(intValidator.hasError()); assertNotNull(intValidator.getException()); return null; }); } @Test @DisabledIfHeadless public void testClearAllNoError() throws Exception { setupLargeValidator(); intValidator.setText("6"); // no validation yet. assertNull(intValidator.getErrorMessage()); testInGUI(() -> { assertTrue(intValidator.hasError()); JTextField input = intValidator.getTextField(); input.setSelectionStart(0); input.setSelectionEnd(1); // should clear the text input.replaceSelection(""); // now the text should be validated and error message present assertFalse(intValidator.hasError()); assertNull(intValidator.getException()); return null; }); } @Test @DisabledIfHeadless public void testControlBecomesDisabled() throws Exception { setupLargeValidator(true); intValidator.setText("4"); testInGUI(() -> { assertTrue(control.isEnabled()); intValidator.getTextField().replaceSelection("44"); assertFalse(control.isEnabled()); return null; }); } @BeforeEach public void setUp() { JUnitUtil.setUp(); } @AfterEach public void tearDown() { JUnitUtil.resetWindows(false, false); JUnitUtil.tearDown(); } }