| 1 | // License: GPL. For details, see LICENSE file.
|
|---|
| 2 | package relcontext.actions;
|
|---|
| 3 |
|
|---|
| 4 | import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
|
|---|
| 5 | import static org.junit.jupiter.api.Assertions.assertEquals;
|
|---|
| 6 | import static org.junit.jupiter.api.Assertions.assertTrue;
|
|---|
| 7 |
|
|---|
| 8 | import java.io.IOException;
|
|---|
| 9 | import java.util.Collection;
|
|---|
| 10 | import java.util.stream.Stream;
|
|---|
| 11 |
|
|---|
| 12 | import org.junit.jupiter.api.AfterEach;
|
|---|
| 13 | import org.junit.jupiter.api.BeforeEach;
|
|---|
| 14 | import org.junit.jupiter.api.Test;
|
|---|
| 15 | import org.openstreetmap.josm.TestUtils;
|
|---|
| 16 | import org.openstreetmap.josm.actions.DeleteAction;
|
|---|
| 17 | import org.openstreetmap.josm.command.DeleteCommand;
|
|---|
| 18 | import org.openstreetmap.josm.data.UndoRedoHandler;
|
|---|
| 19 | import org.openstreetmap.josm.data.osm.DataSet;
|
|---|
| 20 | import org.openstreetmap.josm.data.osm.DatasetConsistencyTest;
|
|---|
| 21 | import org.openstreetmap.josm.data.osm.OsmPrimitive;
|
|---|
| 22 | import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
|
|---|
| 23 | import org.openstreetmap.josm.data.osm.Relation;
|
|---|
| 24 | import org.openstreetmap.josm.data.osm.RelationMember;
|
|---|
| 25 | import org.openstreetmap.josm.data.osm.RelationToChildReference;
|
|---|
| 26 | import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
|
|---|
| 27 | import org.openstreetmap.josm.data.osm.Way;
|
|---|
| 28 | import org.openstreetmap.josm.gui.MainApplication;
|
|---|
| 29 | import org.openstreetmap.josm.gui.layer.OsmDataLayer;
|
|---|
| 30 | import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
|
|---|
| 31 | import org.openstreetmap.josm.gui.util.GuiHelper;
|
|---|
| 32 | import org.openstreetmap.josm.io.IllegalDataException;
|
|---|
| 33 | import org.openstreetmap.josm.io.OsmReader;
|
|---|
| 34 | import org.openstreetmap.josm.testutils.annotations.Main;
|
|---|
| 35 | import org.openstreetmap.josm.testutils.annotations.Projection;
|
|---|
| 36 |
|
|---|
| 37 | import junit.framework.AssertionFailedError;
|
|---|
| 38 | import relcontext.ChosenRelation;
|
|---|
| 39 |
|
|---|
| 40 | /**
|
|---|
| 41 | * Test class for {@link ReconstructPolygonAction}
|
|---|
| 42 | */
|
|---|
| 43 | @Projection
|
|---|
| 44 | @Main
|
|---|
| 45 | class ReconstructPolygonActionTest {
|
|---|
| 46 | private DataSet ds;
|
|---|
| 47 | private ChosenRelation chosenRelation;
|
|---|
| 48 | private Way way1;
|
|---|
| 49 | private Way way2;
|
|---|
| 50 | private Way way3;
|
|---|
| 51 | private Relation relation;
|
|---|
| 52 | private ReconstructPolygonAction action;
|
|---|
| 53 |
|
|---|
| 54 | @BeforeEach
|
|---|
| 55 | void setup() {
|
|---|
| 56 | DeleteCommand.setDeletionCallback(new DeleteCommand.DeletionCallback() {
|
|---|
| 57 | @Override
|
|---|
| 58 | public boolean checkAndConfirmOutlyingDelete(Collection<? extends OsmPrimitive> primitives, Collection<? extends OsmPrimitive> ignore) {
|
|---|
| 59 | return true;
|
|---|
| 60 | }
|
|---|
| 61 |
|
|---|
| 62 | @Override
|
|---|
| 63 | public boolean confirmRelationDeletion(Collection<Relation> relations) {
|
|---|
| 64 | return true;
|
|---|
| 65 | }
|
|---|
| 66 |
|
|---|
| 67 | @Override
|
|---|
| 68 | public boolean confirmDeletionFromRelation(Collection<RelationToChildReference> references) {
|
|---|
| 69 | return true;
|
|---|
| 70 | }
|
|---|
| 71 | });
|
|---|
| 72 | ds = new DataSet();
|
|---|
| 73 | MainApplication.getLayerManager().addLayer(new OsmDataLayer(ds, "ReconstructPolygonActionTest#testNonRegression23170", null));
|
|---|
| 74 | chosenRelation = new ChosenRelation();
|
|---|
| 75 | way1 = TestUtils.newWay("", TestUtils.newNode("name=1"), TestUtils.newNode("name=2"));
|
|---|
| 76 | way2 = TestUtils.newWay("", way1.lastNode(), TestUtils.newNode("name=3"));
|
|---|
| 77 | way3 = TestUtils.newWay("", way2.lastNode(), way1.firstNode());
|
|---|
| 78 | relation = TestUtils.newRelation("type=multipolygon landuse=orchard",
|
|---|
| 79 | new RelationMember("outer", way1),
|
|---|
| 80 | new RelationMember("outer", way2),
|
|---|
| 81 | new RelationMember("outer", way3));
|
|---|
| 82 | ds.addPrimitiveRecursive(relation);
|
|---|
| 83 | chosenRelation.set(relation);
|
|---|
| 84 | action = new ReconstructPolygonAction(chosenRelation);
|
|---|
| 85 | }
|
|---|
| 86 |
|
|---|
| 87 | @AfterEach
|
|---|
| 88 | void tearDown() {
|
|---|
| 89 | DeleteCommand.setDeletionCallback(DeleteAction.defaultDeletionCallback);
|
|---|
| 90 | }
|
|---|
| 91 |
|
|---|
| 92 | /**
|
|---|
| 93 | * Check that the reconstruct code works on a minimal level
|
|---|
| 94 | */
|
|---|
| 95 | @Test
|
|---|
| 96 | void testPolygonReconstructSimple() {
|
|---|
| 97 | assertDoesNotThrow(() -> action.actionPerformed(null));
|
|---|
| 98 | assertTrue(relation.isDeleted());
|
|---|
| 99 | assertEquals(2, Stream.of(way1, way2, way3).filter(Way::isDeleted).count());
|
|---|
| 100 | final Way keptWay = Stream.of(way1, way2, way3).filter(w -> !w.isDeleted()).findFirst().orElseThrow(AssertionFailedError::new);
|
|---|
| 101 | assertTrue(keptWay.isClosed());
|
|---|
| 102 | assertEquals(4, keptWay.getNodesCount());
|
|---|
| 103 | assertEquals(1, keptWay.getNodes().stream().distinct().filter(n -> "1".equals(n.get("name"))).count());
|
|---|
| 104 | assertEquals(1, keptWay.getNodes().stream().distinct().filter(n -> "2".equals(n.get("name"))).count());
|
|---|
| 105 | assertEquals(1, keptWay.getNodes().stream().distinct().filter(n -> "3".equals(n.get("name"))).count());
|
|---|
| 106 | assertEmpty(DatasetConsistencyTest.runTests(ds));
|
|---|
| 107 | assertDoesNotThrow(() -> UndoRedoHandler.getInstance().undo());
|
|---|
| 108 | assertEmpty(DatasetConsistencyTest.runTests(ds));
|
|---|
| 109 | }
|
|---|
| 110 |
|
|---|
| 111 | /**
|
|---|
| 112 | * Ensure that we bail if a way in the relation to be simplified will be deleted from another relation.
|
|---|
| 113 | */
|
|---|
| 114 | @Test
|
|---|
| 115 | void testPolygonReconstructComplex() {
|
|---|
| 116 | final Relation otherRelation = TestUtils.newRelation("type=multipolygon landuse=retail",
|
|---|
| 117 | new RelationMember("outer", way1),
|
|---|
| 118 | new RelationMember("outer", way2),
|
|---|
| 119 | new RelationMember("outer", way3),
|
|---|
| 120 | new RelationMember("label", TestUtils.newNode("name=4")));
|
|---|
| 121 | ds.addPrimitiveRecursive(otherRelation);
|
|---|
| 122 | assertDoesNotThrow(() -> GuiHelper.runInEDTAndWait(() -> action.actionPerformed(null)));
|
|---|
| 123 | assertEmpty(DatasetConsistencyTest.runTests(ds));
|
|---|
| 124 | assertDoesNotThrow(() -> UndoRedoHandler.getInstance().undo());
|
|---|
| 125 | assertEmpty(DatasetConsistencyTest.runTests(ds));
|
|---|
| 126 | }
|
|---|
| 127 |
|
|---|
| 128 | /**
|
|---|
| 129 | * Ensure that we bail if a way in the relation to be simplified will be deleted from another relation.
|
|---|
| 130 | */
|
|---|
| 131 | @Test
|
|---|
| 132 | void testPolygonReconstructDuplicate() {
|
|---|
| 133 | final Relation otherRelation = TestUtils.newRelation("type=multipolygon landuse=retail",
|
|---|
| 134 | new RelationMember("outer", way1),
|
|---|
| 135 | new RelationMember("outer", way2),
|
|---|
| 136 | new RelationMember("outer", way3));
|
|---|
| 137 | ds.addPrimitiveRecursive(otherRelation);
|
|---|
| 138 | assertDoesNotThrow(() -> GuiHelper.runInEDTAndWait(() -> action.actionPerformed(null)));
|
|---|
| 139 | assertEmpty(DatasetConsistencyTest.runTests(ds));
|
|---|
| 140 | assertDoesNotThrow(() -> UndoRedoHandler.getInstance().undo());
|
|---|
| 141 | assertEmpty(DatasetConsistencyTest.runTests(ds));
|
|---|
| 142 | }
|
|---|
| 143 |
|
|---|
| 144 | @Test
|
|---|
| 145 | void testPolygonReconstructR1585888() throws IOException, IllegalDataException {
|
|---|
| 146 | ds.clear();
|
|---|
| 147 | ds.mergeFrom(OsmReader.parseDataSet(TestUtils.getRegressionDataStream(23170, "r1585888.osm"), NullProgressMonitor.INSTANCE));
|
|---|
| 148 | assertEmpty(DatasetConsistencyTest.runTests(ds));
|
|---|
| 149 |
|
|---|
| 150 | ds.setSelected(new SimplePrimitiveId(1585888, OsmPrimitiveType.RELATION));
|
|---|
| 151 | chosenRelation.set((Relation) ds.getPrimitiveById(1585888, OsmPrimitiveType.RELATION));
|
|---|
| 152 | assertDoesNotThrow(() -> GuiHelper.runInEDTAndWait(() -> action.actionPerformed(null)));
|
|---|
| 153 | assertEmpty(DatasetConsistencyTest.runTests(ds));
|
|---|
| 154 |
|
|---|
| 155 | final Collection<Way> selectedWays = ds.getSelectedWays();
|
|---|
| 156 | assertEquals(selectedWays.size(), ds.getSelected().size());
|
|---|
| 157 | assertTrue(selectedWays.stream().allMatch(Way::isClosed));
|
|---|
| 158 | assertTrue(selectedWays.stream().mapToInt(Way::getNodesCount).anyMatch(i -> i == 15));
|
|---|
| 159 | assertTrue(selectedWays.stream().mapToInt(Way::getNodesCount).anyMatch(i -> i == 23));
|
|---|
| 160 | assertTrue(selectedWays.stream().mapToInt(Way::getNodesCount).anyMatch(i -> i == 37));
|
|---|
| 161 | assertTrue(selectedWays.stream().allMatch(way -> "residential".equals(way.get("landuse"))));
|
|---|
| 162 | assertDoesNotThrow(() -> UndoRedoHandler.getInstance().undo());
|
|---|
| 163 | assertEmpty(DatasetConsistencyTest.runTests(ds));
|
|---|
| 164 | }
|
|---|
| 165 |
|
|---|
| 166 | /**
|
|---|
| 167 | * Check that a string is empty
|
|---|
| 168 | * @param string The string to check. Will be printed to log if not empty.
|
|---|
| 169 | */
|
|---|
| 170 | private static void assertEmpty(String string) {
|
|---|
| 171 | assertTrue(string.isEmpty(), string);
|
|---|
| 172 | }
|
|---|
| 173 | }
|
|---|