Ticket #16010: v2-0005-testing-add-HelpAwareOptionPaneMocker.patch

File v2-0005-testing-add-HelpAwareOptionPaneMocker.patch, 7.8 KB (added by ris, 3 years ago)
  • new file test/unit/org/openstreetmap/josm/testutils/mockers/HelpAwareOptionPaneMocker.java

    From 1cf9c8435bec655e98b6beaa2cb01ccb96e2b9ef Mon Sep 17 00:00:00 2001
    From: Robert Scott <code@humanleg.org.uk>
    Date: Mon, 26 Mar 2018 20:30:48 +0100
    Subject: [PATCH v2 05/28] testing: add HelpAwareOptionPaneMocker
    
    ---
     .../mockers/HelpAwareOptionPaneMocker.java         | 192 +++++++++++++++++++++
     1 file changed, 192 insertions(+)
     create mode 100644 test/unit/org/openstreetmap/josm/testutils/mockers/HelpAwareOptionPaneMocker.java
    
    diff --git a/test/unit/org/openstreetmap/josm/testutils/mockers/HelpAwareOptionPaneMocker.java b/test/unit/org/openstreetmap/josm/testutils/mockers/HelpAwareOptionPaneMocker.java
    new file mode 100644
    index 000000000..a157df4e7
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.testutils;
     3
     4import org.openstreetmap.josm.gui.HelpAwareOptionPane;
     5
     6import static org.junit.Assert.fail;
     7
     8import java.awt.Component;
     9import javax.swing.Icon;
     10import javax.swing.JOptionPane;
     11
     12import java.util.Arrays;
     13import java.util.Map;
     14import java.util.OptionalInt;
     15import java.util.stream.IntStream;
     16
     17import org.openstreetmap.josm.tools.Logging;
     18
     19import mockit.Mock;
     20
     21/**
     22 * MockUp for {@link HelpAwareOptionPane} allowing a test to pre-seed uses of
     23 * {@link HelpAwareOptionPane} with mock "responses". This works best with
     24 * calls to {@link HelpAwareOptionPane#showOptionDialog(Component, Object, String, int, Icon,
     25 * HelpAwareOptionPane.ButtonSpec[], HelpAwareOptionPane.ButtonSpec, String)} which use simple string-based
     26 * {@code msg} parameters. In such a case, responses can be defined through a mapping from content
     27 * {@link String}s to button indexes ({@link Integer}s) or button labels ({@link String}s). Example:
     28 *
     29 * <pre>
     30 *      new HelpAwareOptionPaneMocker(ImmutableMap.&lt;String, Object&gt;builder()
     31 *          .put("Do you want to save foo bar?", 2)
     32 *          .put("Please restart JOSM to activate the downloaded plugins.", "OK")
     33 *          .build()
     34 *      );
     35 * </pre>
     36 *
     37 * Testing examples with more complicated contents would require overriding
     38 * {@link #getStringFromMessage(Object)} or even {@link #getMockResultForMessage(Object)} with custom
     39 * logic. The class is implemented as a number of small methods with the main aim being to allow overriding
     40 * of only the parts necessary for a particular case.
     41 *
     42 * The default {@link #getMockResultForMessage(Object)} will raise an
     43 * {@link junit.framework.AssertionFailedError} on an {@link #showOptionDialog(Component, Object, String,
     44 * int, Icon, HelpAwareOptionPane.ButtonSpec[], HelpAwareOptionPane.ButtonSpec, String)}
     45 * activation without a matching mapping entry or if the named button doesn't exist.
     46 *
     47 * The public {@link #getMockResultMap()} method returns the modifiable result map to allow for situations
     48 * where the desired result might need to be changed mid-test.
     49 */
     50public class HelpAwareOptionPaneMocker extends BaseDialogMockUp<HelpAwareOptionPane> {
     51    /**
     52     * Construct a {@link HelpAwareOptionPaneMocker} with an empty {@link #mockResultMap}.
     53     */
     54    public HelpAwareOptionPaneMocker() {
     55        this(null);
     56    }
     57
     58    /**
     59     * Construct an {@link HelpAwareOptionPane} with the provided {@link #mockResultMap}.
     60     * @param mockResultMap mapping of {@link HelpAwareOptionPane} {@code msg} string to
     61     *      result button label or integer index.
     62     */
     63    public HelpAwareOptionPaneMocker(
     64        final Map<String, Object> mockResultMap
     65    ) {
     66        super(mockResultMap);
     67    }
     68
     69    protected String getStringFromMessage(final Object message) {
     70        return message.toString();
     71    }
     72
     73    protected Object getMockResultForMessage(final Object message) {
     74        final String messageString = this.getStringFromMessage(message);
     75        if (!this.getMockResultMap().containsKey(messageString)) {
     76            fail("Unexpected HelpAwareOptionPane message string: " + messageString);
     77        }
     78        return this.getMockResultMap().get(messageString);
     79    }
     80
     81    protected int getButtonPositionFromLabel(
     82        final HelpAwareOptionPane.ButtonSpec[] options,
     83        final String label
     84    ) {
     85        if (options == null) {
     86            if (!label.equals("OK")) {
     87                fail(String.format(
     88                    "Only valid result for HelpAwareOptionPane with options = null is \"OK\": received %s",
     89                    label
     90                ));
     91            }
     92            return JOptionPane.OK_OPTION;
     93        } else {
     94            final OptionalInt optIndex = IntStream.range(0, options.length)
     95                .filter(i -> options[i].text.equals(label))
     96                .findFirst();
     97            if (!optIndex.isPresent()) {
     98                fail(String.format(
     99                    "Unable to find button labeled \"%s\". Instead found %s",
     100                    label,
     101                    Arrays.toString(Arrays.stream(options).map((buttonSpec) -> buttonSpec.text).toArray())
     102                ));
     103            }
     104            // buttons are numbered with 1-based indexing
     105            return optIndex.getAsInt() + 1;
     106        }
     107    }
     108
     109    protected Object[] getInvocationLogEntry(
     110        final Object msg,
     111        final String title,
     112        final Integer messageType,
     113        final Icon icon,
     114        final HelpAwareOptionPane.ButtonSpec[] options,
     115        final HelpAwareOptionPane.ButtonSpec defaultOption,
     116        final String helpTopic,
     117        final Integer mockResult
     118    ) {
     119        return new Object[] {
     120            mockResult,
     121            this.getStringFromMessage(msg),
     122            title
     123        };
     124    }
     125
     126    @Mock
     127    protected int showOptionDialog(
     128        final Component parentComponent,
     129        final Object msg,
     130        final String title,
     131        final int messageType,
     132        final Icon icon,
     133        final HelpAwareOptionPane.ButtonSpec[] options,
     134        final HelpAwareOptionPane.ButtonSpec defaultOption,
     135        final String helpTopic
     136    ) {
     137        final Object result = this.getMockResultForMessage(msg);
     138
     139        if (result == null) {
     140            fail(
     141                "Invalid result for HelpAwareOptionPane: null (HelpAwareOptionPane returns"
     142                + "JOptionPane.OK_OPTION for closed windows if that was the intent)"
     143            );
     144        }
     145
     146        Integer retval = null;
     147        if (result instanceof String) {
     148            retval = this.getButtonPositionFromLabel(options, (String) result);
     149        } else if (result instanceof Integer) {
     150            retval = (Integer) result;
     151        } else {
     152            throw new IllegalArgumentException(
     153                "HelpAwareOptionPane message mapped to unsupported type of Object: " + result
     154            );
     155        }
     156
     157        // check the returned integer for validity
     158        if (retval < 0) {
     159            fail(String.format(
     160                "Invalid result for HelpAwareOptionPane: %s (HelpAwareOptionPane returns "
     161                + "JOptionPane.OK_OPTION for closed windows if that was the intent)",
     162                retval
     163            ));
     164        } else if (retval > (options == null ? 0 : options.length)) {  // NOTE 1-based indexing
     165            fail(String.format(
     166                "Invalid result for HelpAwareOptionPane: %s (in call with options = %s)",
     167                retval,
     168                options
     169            ));
     170        }
     171
     172        Logging.info(
     173            "{0} answering {1} to HelpAwareOptionPane with message {2}",
     174            this.getClass().getName(),
     175            retval,
     176            this.getStringFromMessage(msg)
     177        );
     178
     179        this.getInvocationLogInternal().add(this.getInvocationLogEntry(
     180            msg,
     181            title,
     182            messageType,
     183            icon,
     184            options,
     185            defaultOption,
     186            helpTopic,
     187            retval
     188        ));
     189
     190        return retval;
     191    }
     192}