// License: GPL. For details, see LICENSE file. package org.openstreetmap.josm.actions; import static org.junit.Assert.assertSame; import static org.junit.Assert.assertTrue; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import org.junit.Rule; import org.junit.Test; import org.openstreetmap.josm.Main; import org.openstreetmap.josm.data.coor.EastNorth; import org.openstreetmap.josm.data.coor.LatLon; import org.openstreetmap.josm.data.osm.BBox; import org.openstreetmap.josm.data.osm.DataSet; import org.openstreetmap.josm.data.osm.Node; import org.openstreetmap.josm.data.osm.OsmPrimitive; import org.openstreetmap.josm.data.osm.Way; import org.openstreetmap.josm.gui.layer.OsmDataLayer; import org.openstreetmap.josm.testutils.JOSMTestRules; import org.openstreetmap.josm.tools.GeoProperty; import org.openstreetmap.josm.tools.GeoPropertyIndex; import org.openstreetmap.josm.tools.Geometry; import org.openstreetmap.josm.tools.RightAndLefthandTraffic; import org.openstreetmap.josm.tools.Utils; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; /** * Unit tests for class {@link CreateCircleAction}. */ public final class CreateCircleActionTest { /** * Setup test. */ @Rule @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD") public JOSMTestRules test = new JOSMTestRules().platform().projection().commands(); /** * FIXME: Conveniance method to prevent Selection Change events. * A more proper way should be to define a TestingDataSet class with limited * functionalities, but DataSet is declare as final (due to Cloneable interface). * * I don't know why, but in other tests there are no problem to add selected primitives * but in this case there is a problem with an even listener of selection change. * @param p primitive * @param ds data set * @throws ReflectiveOperationException if an error occurs */ public void addSelected(OsmPrimitive p, DataSet ds) throws ReflectiveOperationException { Method method = ds.getClass().getDeclaredMethod("addSelected", new Class<?>[] {Collection.class, boolean.class}); Utils.setObjectsAccessible(method); method.invoke(ds, Collections.singleton(p), false); } /** * Test case: When Create Circle action is performed with a single way selected, * circle direction must equals way direction. * see #7421 * @throws ReflectiveOperationException if an error occurs */ @Test public void testTicket7421case0() throws ReflectiveOperationException { DataSet dataSet = new DataSet(); OsmDataLayer layer = new OsmDataLayer(dataSet, OsmDataLayer.createNewName(), null); Node n1 = new Node(new EastNorth(0, 0)); Node n2 = new Node(new EastNorth(-1, 1)); Node n3 = new Node(new EastNorth(1, 1)); dataSet.addPrimitive(n1); dataSet.addPrimitive(n2); dataSet.addPrimitive(n3); Way w = new Way(); // Way is Clockwize w.setNodes(Arrays.asList(new Node[] {n1, n2, n3})); dataSet.addPrimitive(w); addSelected(w, dataSet); CreateCircleAction action = new CreateCircleAction(); action.setEnabled(true); try { Main.getLayerManager().addLayer(layer); action.actionPerformed(null); } finally { // Ensure we clean the place before leaving, even if test fails. Main.getLayerManager().removeLayer(layer); } // Expected result: Dataset contain one closed way, clockwise Collection<Way> resultingWays = dataSet.getWays(); assertSame(String.format("Expect one way after perform action. %d found", resultingWays.size()), resultingWays.size(), 1); Way resultingWay = resultingWays.iterator().next(); assertTrue("Resulting way is not closed", resultingWay.isClosed()); assertTrue("Found anti-clockwize circle while way was clockwize", Geometry.isClockwise(resultingWay)); } /** * Mock left/right hand traffic database with constant traffic hand */ private static class ConstantTrafficHand implements GeoProperty<Boolean> { boolean isLeft; ConstantTrafficHand(boolean isLeft) { this.isLeft = isLeft; } @Override public Boolean get(LatLon ll) { return isLeft; } @Override public Boolean get(BBox box) { return isLeft; } } /** * Test case: When Create Circle action is performed with nodes, resulting * circle direction depend on traffic hand. Simulate a left hand traffic. * see #7421 * @throws ReflectiveOperationException if an error occurs */ @Test public void testTicket7421case1() throws ReflectiveOperationException { DataSet dataSet = new DataSet(); OsmDataLayer layer = new OsmDataLayer(dataSet, OsmDataLayer.createNewName(), null); Node n1 = new Node(new EastNorth(0, 0)); Node n2 = new Node(new EastNorth(-1, 1)); Node n3 = new Node(new EastNorth(1, 1)); dataSet.addPrimitive(n1); dataSet.addPrimitive(n2); dataSet.addPrimitive(n3); addSelected(n1, dataSet); addSelected(n2, dataSet); addSelected(n3, dataSet); // Mock left/right hand traffic database Field rlCache = RightAndLefthandTraffic.class.getDeclaredField("rlCache"); Utils.setObjectsAccessible(rlCache); Object origRlCache = rlCache.get(null); rlCache.set(null, new GeoPropertyIndex<>(new ConstantTrafficHand(true), 24)); try { CreateCircleAction action = new CreateCircleAction(); action.setEnabled(true); try { Main.getLayerManager().addLayer(layer); action.actionPerformed(null); } finally { // Ensure we clean the place before leaving, even if test fails. Main.getLayerManager().removeLayer(layer); } // Expected result: Dataset contain one closed way, clockwise Collection<Way> resultingWays = dataSet.getWays(); assertSame(String.format("Expect one way after perform action. %d found", resultingWays.size()), resultingWays.size(), 1); Way resultingWay = resultingWays.iterator().next(); assertTrue("Resulting way is not closed", resultingWay.isClosed()); assertTrue("Found anti-clockwise way while traffic is left hand.", Geometry.isClockwise(resultingWay)); } finally { // Restore left/right hand traffic database rlCache.set(null, origRlCache); } } }