Changeset 18866 in josm


Ignore:
Timestamp:
2023-10-12T17:42:13+02:00 (7 months ago)
Author:
taylor.smock
Message:

Fix #23196: DataIntegrityProblemException: Primitive must be part of the dataset

It turns out that FilterModel doesn't care whether a primitive's referrers are
in the dataset or not.

This additionally adds a non-regression test and modifies WindowMocker to cover
some more headless exceptions. This additionally lets us stop mocking
ExtendedDialog#setupDialog.

Location:
trunk
Files:
5 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/FilterModel.java

    r18571 r18866  
    440440            }
    441441
    442             for (OsmPrimitive ref: p.getReferrers()) {
     442            for (OsmPrimitive ref: p.getReferrers(true)) {
    443443                stack.push(ref);
    444444            }
  • trunk/src/org/openstreetmap/josm/gui/tagging/presets/TaggingPreset.java

    r18607 r18866  
    8585 * read in all predefined presets, either shipped with JOSM or that are
    8686 * in the config directory.
    87  *
     87 * <p>
    8888 * It is also able to construct dialogs out of preset definitions.
    8989 * @since 294
     
    405405            /**
    406406             * This hack allows the items to have their own orientation.
    407              *
     407             * <p>
    408408             * The problem is that
    409409             * {@link org.openstreetmap.josm.gui.ExtendedDialog#showDialog ExtendedDialog} calls
    410410             * {@code applyComponentOrientation} very late in the dialog construction process thus
    411411             * overwriting the orientation the components have chosen for themselves.
    412              *
     412             * <p>
    413413             * This stops the propagation of {@code applyComponentOrientation}, thus all
    414414             * {@code TaggingPresetItem}s may (and have to) set their own orientation.
     
    446446
    447447        if (selected.size() == 1 && Boolean.TRUE.equals(USE_VALIDATOR.get())) {
    448             // Fail early -- validateAsync requires the primitive(s) to be part of a dataset. Failing later in validateAsync ''does not'' give us
    449             // a usable stack trace. See #21829 for details.
    450             selected.forEach(OsmPrimitive::checkDataset);
    451448            itemGuiSupport.addListener((source, key, newValue) ->
    452449                    TaggingPresetValidation.validateAsync(selected.iterator().next(), validationLabel, getChangedTags()));
  • trunk/test/unit/org/openstreetmap/josm/gui/dialogs/relation/GenericRelationEditorTest.java

    r18804 r18866  
    99import static org.junit.jupiter.api.Assertions.assertNull;
    1010import static org.junit.jupiter.api.Assertions.assertSame;
     11import static org.junit.jupiter.api.Assertions.fail;
    1112import static org.openstreetmap.josm.tools.I18n.tr;
    1213
     14import java.awt.Component;
    1315import java.awt.Container;
     16import java.awt.event.MouseEvent;
     17import java.awt.event.MouseListener;
    1418import java.awt.event.WindowEvent;
     19import java.util.ArrayDeque;
     20import java.util.Arrays;
    1521import java.util.Collection;
    1622import java.util.Collections;
    1723import java.util.concurrent.atomic.AtomicReference;
     24import java.util.stream.Collectors;
    1825
    1926import javax.swing.Action;
    2027import javax.swing.JButton;
     28import javax.swing.JLabel;
    2129import javax.swing.JOptionPane;
    2230import javax.swing.JPanel;
    23 
    24 import mockit.Invocation;
    25 import mockit.Mock;
    26 import mockit.MockUp;
    2731
    2832import org.junit.jupiter.api.BeforeEach;
     
    3640import org.openstreetmap.josm.data.osm.RelationMember;
    3741import org.openstreetmap.josm.data.osm.Way;
     42import org.openstreetmap.josm.gui.ExtendedDialog;
    3843import org.openstreetmap.josm.gui.MainApplication;
    3944import org.openstreetmap.josm.gui.SideButton;
     
    4550import org.openstreetmap.josm.gui.tagging.TagEditorPanel;
    4651import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
     52import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
    4753import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler;
     54import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetLabel;
     55import org.openstreetmap.josm.spi.preferences.Config;
    4856import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
    4957import org.openstreetmap.josm.testutils.annotations.Main;
    5058import org.openstreetmap.josm.testutils.annotations.Projection;
     59import org.openstreetmap.josm.testutils.annotations.TaggingPresets;
     60import org.openstreetmap.josm.testutils.mockers.ExtendedDialogMocker;
    5161import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
    5262import org.openstreetmap.josm.testutils.mockers.WindowMocker;
     63import org.openstreetmap.josm.tools.JosmRuntimeException;
     64
     65import mockit.Invocation;
     66import mockit.Mock;
     67import mockit.MockUp;
    5368
    5469/**
     
    105120        new PasteMembersActionMock();
    106121        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;
    107140    }
    108141
     
    185218    void testNonRegression23116() {
    186219        // Setup the mocks
    187         final AtomicReference<RelationEditor> editorReference = new AtomicReference<>();
    188         new MockUp<RelationEditor>() {
    189             @Mock public RelationEditor getEditor(Invocation invocation, OsmDataLayer layer, Relation r,
    190                     Collection<RelationMember> selectedMembers) {
    191                 editorReference.set(invocation.proceed(layer, r, selectedMembers));
    192                 return editorReference.get();
    193             }
    194         };
    195         // We want to go through the `setVisible` code, just in case. So we have to mock the window location
    196         new MockUp<GenericRelationEditor>() {
    197             @Mock public void setVisible(boolean visible) {
    198                 // Do nothing. Ideally, we would just mock the awt methods called, but that would take a lot of mocking.
    199             }
    200         };
     220        final AtomicReference<RelationEditor> editorReference = setupGuiMocks();
    201221        // Set up the data
    202222        final DataSet dataSet = new DataSet();
     
    234254    }
    235255
     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
    236317    @SuppressWarnings("unchecked")
    237318    private static <T extends Container> T getComponent(Container parent, int... tree) {
  • trunk/test/unit/org/openstreetmap/josm/testutils/mockers/ExtendedDialogMocker.java

    r18690 r18866  
    171171        }
    172172        return resultField;
    173     }
    174 
    175     @Mock
    176     private void setupDialog(final Invocation invocation) {
    177         if (!GraphicsEnvironment.isHeadless()) {
    178             invocation.proceed();
    179         }
    180         // else do nothing - WindowMocker-ed Windows doesn't work well enough for some of the
    181         // component constructions
    182173    }
    183174
  • trunk/test/unit/org/openstreetmap/josm/testutils/mockers/WindowMocker.java

    r14081 r18866  
    22package org.openstreetmap.josm.testutils.mockers;
    33
     4import java.awt.Component;
    45import java.awt.Frame;
    56import java.awt.GraphicsConfiguration;
     
    3738    private void $init(final Invocation invocation, final Window window, final GraphicsConfiguration gc) {
    3839    }
     40
     41    @Mock
     42    public void pack() {
     43    }
     44
     45    @Mock
     46    public void setLocationRelativeTo(final Component c) {
     47    }
    3948}
Note: See TracChangeset for help on using the changeset viewer.