source: josm/trunk/test/unit/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditorTest.java@ 19050

Last change on this file since 19050 was 19050, checked in by taylor.smock, 15 months ago

Revert most var changes from r19048, fix most new compile warnings and checkstyle issues

Also, document why various ErrorProne checks were originally disabled and fix
generic SonarLint issues.

  • Property svn:eol-style set to native
File size: 14.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs.relation;
3
4import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
5import static org.junit.jupiter.api.Assertions.assertEquals;
6import static org.junit.jupiter.api.Assertions.assertFalse;
7import static org.junit.jupiter.api.Assertions.assertInstanceOf;
8import static org.junit.jupiter.api.Assertions.assertNotNull;
9import static org.junit.jupiter.api.Assertions.assertNull;
10import static org.junit.jupiter.api.Assertions.assertSame;
11import static org.junit.jupiter.api.Assertions.fail;
12import static org.openstreetmap.josm.tools.I18n.tr;
13
14import java.awt.Component;
15import java.awt.Container;
16import java.awt.event.MouseEvent;
17import java.awt.event.MouseListener;
18import java.awt.event.WindowEvent;
19import java.util.ArrayDeque;
20import java.util.Arrays;
21import java.util.Collection;
22import java.util.Collections;
23import java.util.concurrent.atomic.AtomicReference;
24import java.util.stream.Collectors;
25
26import javax.swing.Action;
27import javax.swing.JButton;
28import javax.swing.JLabel;
29import javax.swing.JOptionPane;
30import javax.swing.JPanel;
31
32import org.junit.jupiter.api.BeforeEach;
33import org.junit.jupiter.api.Test;
34import org.junit.platform.commons.support.ReflectionSupport;
35import org.openstreetmap.josm.TestUtils;
36import org.openstreetmap.josm.data.osm.DataSet;
37import org.openstreetmap.josm.data.osm.Node;
38import org.openstreetmap.josm.data.osm.OsmPrimitive;
39import org.openstreetmap.josm.data.osm.Relation;
40import org.openstreetmap.josm.data.osm.RelationMember;
41import org.openstreetmap.josm.data.osm.Way;
42import org.openstreetmap.josm.gui.ExtendedDialog;
43import org.openstreetmap.josm.gui.MainApplication;
44import org.openstreetmap.josm.gui.SideButton;
45import org.openstreetmap.josm.gui.dialogs.RelationListDialog;
46import org.openstreetmap.josm.gui.dialogs.relation.actions.AddSelectedAtStartAction;
47import org.openstreetmap.josm.gui.dialogs.relation.actions.IRelationEditorActionAccess;
48import org.openstreetmap.josm.gui.dialogs.relation.actions.PasteMembersAction;
49import org.openstreetmap.josm.gui.layer.OsmDataLayer;
50import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
51import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
52import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
53import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler;
54import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetLabel;
55import org.openstreetmap.josm.spi.preferences.Config;
56import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
57import org.openstreetmap.josm.testutils.annotations.Main;
58import org.openstreetmap.josm.testutils.annotations.Projection;
59import org.openstreetmap.josm.testutils.annotations.TaggingPresets;
60import org.openstreetmap.josm.testutils.mockers.ExtendedDialogMocker;
61import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
62import org.openstreetmap.josm.testutils.mockers.WindowMocker;
63import org.openstreetmap.josm.tools.JosmRuntimeException;
64
65import mockit.Invocation;
66import mockit.Mock;
67import mockit.MockUp;
68
69/**
70 * Unit tests of {@link GenericRelationEditor} class.
71 */
72@BasicPreferences
73@Main
74@Projection
75public class GenericRelationEditorTest {
76 /**
77 * Returns a new relation editor for unit tests.
78 * @param orig relation
79 * @param layer data layer
80 * @return new relation editor for unit tests
81 */
82 public static IRelationEditor newRelationEditor(final Relation orig, final OsmDataLayer layer) {
83 return new IRelationEditor() {
84 private Relation r = orig;
85
86 @Override
87 public void setRelation(Relation relation) {
88 r = relation;
89 }
90
91 @Override
92 public boolean isDirtyRelation() {
93 return false;
94 }
95
96 @Override
97 public Relation getRelationSnapshot() {
98 return r;
99 }
100
101 @Override
102 public Relation getRelation() {
103 return r;
104 }
105
106 @Override
107 public void reloadDataFromRelation() {
108 // Do nothing
109 }
110
111 @Override
112 public OsmDataLayer getLayer() {
113 return layer;
114 }
115 };
116 }
117
118 @BeforeEach
119 void setup() {
120 new PasteMembersActionMock();
121 new WindowMocker();
122 }
123
124 private static AtomicReference<RelationEditor> setupGuiMocks() {
125 AtomicReference<RelationEditor> editorReference = new AtomicReference<>();
126 new MockUp<RelationEditor>() {
127 @Mock public RelationEditor getEditor(Invocation invocation, OsmDataLayer layer, Relation r,
128 Collection<RelationMember> selectedMembers) {
129 editorReference.set(invocation.proceed(layer, r, selectedMembers));
130 return editorReference.get();
131 }
132 };
133 // We want to go through the `setVisible` code, just in case. So we have to mock the window location
134 new MockUp<GenericRelationEditor>() {
135 @Mock public void setVisible(boolean visible) {
136 // Do nothing. Ideally, we would just mock the awt methods called, but that would take a lot of mocking.
137 }
138 };
139 return editorReference;
140 }
141
142 /**
143 * Unit test of {@link GenericRelationEditor#addPrimitivesToRelation}.
144 */
145 @Test
146 void testAddPrimitivesToRelation() {
147 TestUtils.assumeWorkingJMockit();
148 final JOptionPaneSimpleMocker jopsMocker = new JOptionPaneSimpleMocker();
149
150 Relation r = TestUtils.addFakeDataSet(new Relation(1));
151 assertNull(GenericRelationEditor.addPrimitivesToRelation(r, Collections.<OsmPrimitive>emptyList()));
152 jopsMocker.getMockResultMap().put(
153 "<html>You are trying to add a relation to itself.<br><br>This generates a circular dependency of parent/child elements "
154 + "and is therefore discouraged.<br>Skipping relation 'incomplete'.</html>",
155 JOptionPane.OK_OPTION
156 );
157
158 assertNull(GenericRelationEditor.addPrimitivesToRelation(r, Collections.singleton(new Relation(1))));
159
160 assertEquals(1, jopsMocker.getInvocationLog().size());
161 Object[] invocationLogEntry = jopsMocker.getInvocationLog().get(0);
162 assertEquals(JOptionPane.OK_OPTION, (int) invocationLogEntry[0]);
163 assertEquals("Warning", invocationLogEntry[2]);
164
165 assertNotNull(GenericRelationEditor.addPrimitivesToRelation(r, Collections.singleton(new Node(1))));
166 assertNotNull(GenericRelationEditor.addPrimitivesToRelation(r, Collections.singleton(new Way(1))));
167 assertNotNull(GenericRelationEditor.addPrimitivesToRelation(r, Collections.singleton(new Relation(2))));
168
169 assertEquals(1, jopsMocker.getInvocationLog().size());
170 }
171
172 /**
173 * Unit test of {@code GenericRelationEditor#build*} methods.
174 * <p>
175 * This test only tests if they do not throw exceptions.
176 */
177 @Test
178 void testBuild() {
179 DataSet ds = new DataSet();
180 Relation relation = new Relation(1);
181 ds.addPrimitive(relation);
182 OsmDataLayer layer = new OsmDataLayer(ds, "test", null);
183 IRelationEditor re = newRelationEditor(relation, layer);
184
185 AutoCompletingTextField tfRole = GenericRelationEditor.buildRoleTextField(re);
186 assertNotNull(tfRole);
187
188 TagEditorPanel tagEditorPanel = new TagEditorPanel(relation, null);
189
190 JPanel top = GenericRelationEditor.buildTagEditorPanel(tagEditorPanel);
191 assertNotNull(top);
192 assertNotNull(tagEditorPanel.getModel());
193 }
194
195 @Test
196 void testNonRegression23091() throws Exception {
197 DataSet ds = new DataSet();
198 Relation relation = new Relation(1);
199 ds.addPrimitive(relation);
200 OsmDataLayer layer = new OsmDataLayer(ds, "test", null);
201
202 final GenericRelationEditor gr = new GenericRelationEditor(layer, relation, Collections.emptyList());
203 final IRelationEditorActionAccess iAccess = (IRelationEditorActionAccess)
204 ReflectionSupport.tryToReadFieldValue(GenericRelationEditor.class.getDeclaredField("actionAccess"), gr)
205 .get();
206 final TaggingPresetHandler handler = (TaggingPresetHandler)
207 ReflectionSupport.tryToReadFieldValue(MemberTableModel.class.getDeclaredField("presetHandler"), iAccess.getMemberTableModel())
208 .get();
209 final Collection<OsmPrimitive> selection = handler.getSelection();
210 assertEquals(1, selection.size());
211 assertSame(relation, selection.iterator().next(), "The selection should be the same");
212 }
213
214 /**
215 * Ensure that users can create new relations and modify them.
216 */
217 @Test
218 void testNonRegression23116() {
219 // Setup the mocks
220 final AtomicReference<RelationEditor> editorReference = setupGuiMocks();
221 // Set up the data
222 final DataSet dataSet = new DataSet();
223 MainApplication.getLayerManager().addLayer(new OsmDataLayer(dataSet, "GenericRelationEditorTest.testNonRegression23116", null));
224 dataSet.addPrimitive(TestUtils.newNode(""));
225 dataSet.setSelected(dataSet.allPrimitives());
226 final RelationListDialog relationListDialog = new RelationListDialog();
227 try {
228 final Action newAction = ((SideButton) getComponent(relationListDialog, 2, 0, 0)).getAction();
229 assertEquals("class org.openstreetmap.josm.gui.dialogs.RelationListDialog$NewAction",
230 newAction.getClass().toString());
231 // Now get the buttons we want to push
232 newAction.actionPerformed(null);
233 final GenericRelationEditor editor = assertInstanceOf(GenericRelationEditor.class, editorReference.get());
234 final JButton okAction = getComponent(editor, 0, 1, 0, 2, 0);
235 assertEquals(tr("Delete"), okAction.getText(), "OK is Delete until the relation actually has data");
236 assertNotNull(editor);
237 final TagEditorPanel tagEditorPanel = getComponent(editor, 0, 1, 0, 1, 0, 0, 1, 1);
238 // We need at least one tag for the action to not be "Delete".
239 tagEditorPanel.getModel().add("type", "someUnknownTypeHere");
240 final Action addAtStartAction = assertInstanceOf(AddSelectedAtStartAction.class,
241 ((JButton) getComponent(editor, 0, 1, 0, 1, 0, 0, 2, 0, 2, 1, 2, 0, 0)).getAction());
242 // Perform the actual test.
243 assertDoesNotThrow(() -> addAtStartAction.actionPerformed(null));
244 assertDoesNotThrow(() -> okAction.getAction().actionPerformed(null));
245 assertFalse(dataSet.getRelations().isEmpty());
246 assertSame(dataSet.getNodes().iterator().next(),
247 dataSet.getRelations().iterator().next().getMember(0).getNode());
248 } finally {
249 // This avoids an issue with the cleanup code and the mocks for this test
250 if (editorReference.get() != null) {
251 RelationDialogManager.getRelationDialogManager().windowClosed(new WindowEvent(editorReference.get(), 0));
252 }
253 }
254 }
255
256 /**
257 * Ensure that users can create new relations with a preset available and open the preset.
258 * See {@link TaggingPreset#showAndApply} for where a relation may exist without a dataset.
259 */
260 @BasicPreferences
261 @TaggingPresets
262 @Test
263 void testNonRegression23196() {
264 // This happens when the preset validator is enabled (Preferences -> `Tagging Presets` -> `Run data validator on user input`)
265 Config.getPref().putBoolean("taggingpreset.validator", true);
266 // Setup the mocks
267 final AtomicReference<RelationEditor> editorReference = setupGuiMocks();
268 new ExtendedDialogMocker(Collections.singletonMap("Change 1 object", "Apply Preset")) {
269 @Override
270 protected String getString(ExtendedDialog instance) {
271 return instance.getTitle();
272 }
273 };
274 // Set up the data
275 final DataSet dataSet = new DataSet();
276 final OsmDataLayer layer = new OsmDataLayer(dataSet, "GenericRelationEditorTest.testNonRegression23196", null);
277 MainApplication.getLayerManager().addLayer(layer);
278 dataSet.addPrimitive(TestUtils.newNode(""));
279 dataSet.setSelected(dataSet.allPrimitives());
280 try {
281 RelationEditor.getEditor(layer, TestUtils.newRelation("type=multipolygon"),
282 dataSet.getSelected().stream().map(p -> new RelationMember("", p)).collect(Collectors.toList()));
283 final GenericRelationEditor editor = assertInstanceOf(GenericRelationEditor.class, editorReference.get());
284 TaggingPresetLabel label = getComponentByNameOrText(TaggingPresetLabel.class, editor, "Relations/Multipolygon …");
285 final MouseEvent mouseEvent = new MouseEvent(label, 0, 0, 0, 0, 0, 0, false);
286 for (MouseListener listener : label.getMouseListeners()) {
287 assertDoesNotThrow(() -> listener.mouseClicked(mouseEvent));
288 }
289 } finally {
290 // This avoids an issue with the cleanup code and the mocks for this test
291 if (editorReference.get() != null) {
292 RelationDialogManager.getRelationDialogManager().windowClosed(new WindowEvent(editorReference.get(), 0));
293 }
294 }
295 }
296
297 private static <T extends Component> T getComponentByNameOrText(Class<T> clazz, Container parent, String name) {
298 final ArrayDeque<Component> componentDeque = new ArrayDeque<>(Collections.singletonList(parent));
299 while (!componentDeque.isEmpty()) {
300 final Component current = componentDeque.pop();
301 if (current instanceof Container) {
302 componentDeque.addAll(Arrays.asList(((Container) current).getComponents()));
303 }
304 if (clazz.isInstance(current)) {
305 T component = clazz.cast(current);
306 if (name.equals(component.getName())) {
307 return component;
308 } else if (component instanceof JLabel && name.equals(((JLabel) component).getText())) {
309 return component;
310 }
311 }
312 }
313 fail("Component with name " + name + " not found");
314 throw new JosmRuntimeException("This should never happen due to the fail line");
315 }
316
317 @SuppressWarnings("unchecked")
318 private static <T extends Container> T getComponent(Container parent, int... tree) {
319 Container current = parent;
320 for (int i : tree) {
321 current = (Container) current.getComponent(i);
322 }
323 return (T) current;
324 }
325
326 private static final class PasteMembersActionMock extends MockUp<PasteMembersAction> {
327 @Mock
328 public void updateEnabledState() {
329 // Do nothing
330 }
331 }
332}
Note: See TracBrowser for help on using the repository browser.