source: josm/trunk/test/unit/org/openstreetmap/josm/testutils/mockers/HelpAwareOptionPaneMocker.java@ 17360

Last change on this file since 17360 was 17275, checked in by Don-vip, 3 years ago

see #16567 - upgrade almost all tests to JUnit 5, except those depending on WiremockRule

See https://github.com/tomakehurst/wiremock/issues/684

  • Property svn:eol-style set to native
File size: 7.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.testutils.mockers;
3
4import static org.junit.jupiter.api.Assertions.fail;
5
6import java.awt.Component;
7import java.util.Arrays;
8import java.util.Map;
9import java.util.OptionalInt;
10import java.util.stream.IntStream;
11
12import javax.swing.Icon;
13import javax.swing.JOptionPane;
14
15import org.openstreetmap.josm.gui.HelpAwareOptionPane;
16import org.openstreetmap.josm.tools.Logging;
17
18import mockit.Mock;
19
20/**
21 * MockUp for {@link HelpAwareOptionPane} allowing a test to pre-seed uses of
22 * {@link HelpAwareOptionPane} with mock "responses". This works best with
23 * calls to {@link HelpAwareOptionPane#showOptionDialog(Component, Object, String, int, Icon,
24 * HelpAwareOptionPane.ButtonSpec[], HelpAwareOptionPane.ButtonSpec, String)} which use simple string-based
25 * {@code msg} parameters. In such a case, responses can be defined through a mapping from content
26 * {@link String}s to button indexes ({@link Integer}s) or button labels ({@link String}s). Example:
27 *
28 * <pre>
29 * new HelpAwareOptionPaneMocker(ImmutableMap.&lt;String, Object&gt;builder()
30 * .put("Do you want to save foo bar?", 2)
31 * .put("Please restart JOSM to activate the downloaded plugins.", "OK")
32 * .build()
33 * );
34 * </pre>
35 *
36 * Testing examples with more complicated contents would require overriding
37 * {@link #getStringFromMessage(Object)} or even {@link #getMockResultForMessage(Object)} with custom
38 * logic. The class is implemented as a number of small methods with the main aim being to allow overriding
39 * of only the parts necessary for a particular case.
40 *
41 * The default {@link #getMockResultForMessage(Object)} will raise an
42 * {@link AssertionError} on an {@link #showOptionDialog(Component, Object, String,
43 * int, Icon, HelpAwareOptionPane.ButtonSpec[], HelpAwareOptionPane.ButtonSpec, String)}
44 * activation without a matching mapping entry or if the named button doesn't exist.
45 *
46 * The public {@link #getMockResultMap()} method returns the modifiable result map to allow for situations
47 * where the desired result might need to be changed mid-test.
48 */
49public class HelpAwareOptionPaneMocker extends BaseDialogMockUp<HelpAwareOptionPane> {
50 /**
51 * Construct a {@link HelpAwareOptionPaneMocker} with an empty {@link #mockResultMap}.
52 */
53 public HelpAwareOptionPaneMocker() {
54 this(null);
55 }
56
57 /**
58 * Construct an {@link HelpAwareOptionPane} with the provided {@link #mockResultMap}.
59 * @param mockResultMap mapping of {@link HelpAwareOptionPane} {@code msg} string to
60 * result button label or integer index.
61 */
62 public HelpAwareOptionPaneMocker(
63 final Map<String, Object> mockResultMap
64 ) {
65 super(mockResultMap);
66 }
67
68 protected String getStringFromMessage(final Object message) {
69 return message.toString();
70 }
71
72 protected Object getMockResultForMessage(final Object message) {
73 final String messageString = this.getStringFromMessage(message);
74 if (!this.getMockResultMap().containsKey(messageString)) {
75 fail("Unexpected HelpAwareOptionPane message string: " + messageString);
76 }
77 return this.getMockResultMap().get(messageString);
78 }
79
80 /**
81 * Target for overriding, similar to {@link #getMockResultForMessage} except with the implication it
82 * will only be invoked once per dialog display, therefore ideal opportunity to perform any mutating
83 * actions, e.g. making a selection on a widget.
84 * @param message message
85 */
86 protected void act(final Object message) {
87 // Override in sub-classes
88 }
89
90 protected int getButtonPositionFromLabel(
91 final HelpAwareOptionPane.ButtonSpec[] options,
92 final String label
93 ) {
94 if (options == null) {
95 if (!label.equals("OK")) {
96 fail(String.format(
97 "Only valid result for HelpAwareOptionPane with options = null is \"OK\": received %s",
98 label
99 ));
100 }
101 return JOptionPane.OK_OPTION;
102 } else {
103 final OptionalInt optIndex = IntStream.range(0, options.length)
104 .filter(i -> options[i].text.equals(label))
105 .findFirst();
106 if (!optIndex.isPresent()) {
107 fail(String.format(
108 "Unable to find button labeled \"%s\". Instead found %s",
109 label,
110 Arrays.toString(Arrays.stream(options).map((buttonSpec) -> buttonSpec.text).toArray())
111 ));
112 }
113 return optIndex.getAsInt();
114 }
115 }
116
117 protected Object[] getInvocationLogEntry(
118 final Object msg,
119 final String title,
120 final Integer messageType,
121 final Icon icon,
122 final HelpAwareOptionPane.ButtonSpec[] options,
123 final HelpAwareOptionPane.ButtonSpec defaultOption,
124 final String helpTopic,
125 final Integer mockResult
126 ) {
127 return new Object[] {
128 mockResult,
129 this.getStringFromMessage(msg),
130 title
131 };
132 }
133
134 @Mock
135 protected int showOptionDialog(
136 final Component parentComponent,
137 final Object msg,
138 final String title,
139 final int messageType,
140 final Icon icon,
141 final HelpAwareOptionPane.ButtonSpec[] options,
142 final HelpAwareOptionPane.ButtonSpec defaultOption,
143 final String helpTopic
144 ) {
145 try {
146 this.act(msg);
147 final Object result = this.getMockResultForMessage(msg);
148
149 if (result == null) {
150 fail(
151 "Invalid result for HelpAwareOptionPane: null (HelpAwareOptionPane returns"
152 + "JOptionPane.OK_OPTION for closed windows if that was the intent)"
153 );
154 }
155
156 Integer retval = null;
157 if (result instanceof String) {
158 retval = this.getButtonPositionFromLabel(options, (String) result);
159 } else if (result instanceof Integer) {
160 retval = (Integer) result;
161 } else {
162 throw new IllegalArgumentException(
163 "HelpAwareOptionPane message mapped to unsupported type of Object: " + result
164 );
165 }
166
167 // check the returned integer for validity
168 if (retval < 0) {
169 fail(String.format(
170 "Invalid result for HelpAwareOptionPane: %s (HelpAwareOptionPane returns "
171 + "JOptionPane.OK_OPTION for closed windows if that was the intent)",
172 retval
173 ));
174 } else if (retval > (options == null ? 0 : options.length-1)) {
175 fail(String.format(
176 "Invalid result for HelpAwareOptionPane: %s (in call with options = %s)",
177 retval,
178 Arrays.asList(options)
179 ));
180 }
181
182 Logging.info(
183 "{0} answering {1} to HelpAwareOptionPane with message {2}",
184 this.getClass().getName(),
185 retval,
186 this.getStringFromMessage(msg)
187 );
188
189 this.getInvocationLogInternal().add(this.getInvocationLogEntry(
190 msg,
191 title,
192 messageType,
193 icon,
194 options,
195 defaultOption,
196 helpTopic,
197 retval
198 ));
199
200 return retval;
201 } catch (AssertionError e) {
202 // in case this exception gets ignored by the calling thread we want to signify this failure
203 // in the invocation log. it's hard to know what to add to the log in these cases as it's
204 // probably unsafe to call getInvocationLogEntry, so add the exception on its own.
205 this.getInvocationLogInternal().add(new Object[] {e});
206 throw e;
207 }
208 }
209}
Note: See TracBrowser for help on using the repository browser.