Changeset 36235 in osm for applications


Ignore:
Timestamp:
2024-04-03T17:47:14+02:00 (9 months ago)
Author:
taylor.smock
Message:

See #23584: Add non-regression test for changeset 97259223

Note: The test currently fails since the problem hasn't been fixed yet.

Location:
applications/editors/josm/plugins/reverter/test
Files:
10 added
2 edited

Legend:

Unmodified
Added
Removed
  • applications/editors/josm/plugins/reverter/test/data/regress/README.md

    r36233 r36235  
    1111```
    1212
     13**Warning**: JOSM will not copy deleted objects to clipboard.
     14
    1315Once you have the list, you can run the following command:
    1416```shell
    1517# Note: `pbpaste` is for mac, `xclip -selection clipboard -o` is for x11 linux, and `wl-paste` (from `wl-clipboard`) is for linux wayland
    1618$ wl-paste | \
    17   awk '{print "https://api.openstreetmap.org/api/0.6/$1/" $2 "/history.json" " -o " $1 "_" $2 ".json"}' | \
     19  awk '{print "https://api.openstreetmap.org/api/0.6/" $1 "/" $2 "/history.json" " -o " $1 "_" $2 ".json"}' | \
    1820  xargs curl -L
    1921$ jq -s '.[0].elements=([.[].elements]|flatten)|.[0]' node_*.json > nodes.json
  • applications/editors/josm/plugins/reverter/test/unit/reverter/ChangesetReverterTest.java

    r36233 r36235  
    33
    44import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig;
     5import static org.junit.jupiter.api.Assertions.assertAll;
    56import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
    67import static org.junit.jupiter.api.Assertions.assertEquals;
     
    1415import java.nio.file.Paths;
    1516import java.util.Arrays;
     17import java.util.Collection;
    1618import java.util.Collections;
    1719import java.util.HashMap;
     
    2325import java.util.stream.Collectors;
    2426
     27import javax.swing.JOptionPane;
     28
    2529import org.junit.jupiter.api.Test;
    2630import org.junit.jupiter.api.extension.RegisterExtension;
     
    3034import org.openstreetmap.josm.data.UndoRedoHandler;
    3135import org.openstreetmap.josm.data.osm.DataSet;
     36import org.openstreetmap.josm.data.osm.Node;
     37import org.openstreetmap.josm.data.osm.OsmPrimitive;
    3238import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    3339import org.openstreetmap.josm.data.osm.PrimitiveId;
     40import org.openstreetmap.josm.data.osm.Relation;
    3441import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
    3542import org.openstreetmap.josm.data.osm.Way;
     
    4350import org.openstreetmap.josm.testutils.annotations.Main;
    4451import org.openstreetmap.josm.testutils.annotations.Projection;
     52import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
    4553import org.openstreetmap.josm.tools.JosmRuntimeException;
    4654import org.openstreetmap.josm.tools.Logging;
     
    7280class ChangesetReverterTest {
    7381    @RegisterExtension
    74     static WireMockExtension wireMockExtension = WireMockExtension.newInstance()
    75             .options(wireMockConfig().dynamicPort().dynamicHttpsPort().extensions(new MultiplePrimitiveTransformer()))
     82    static WireMockExtension wireMockExtension = WireMockExtension.newInstance().options(
     83            wireMockConfig().dynamicPort().dynamicHttpsPort()
     84                    .extensions(new MultiplePrimitiveTransformer(), new PrimitiveTransformer()))
    7685            .build();
    7786
     
    8291    void testTicket22520(WireMockRuntimeInfo wireMockRuntimeInfo) throws ExecutionException, InterruptedException {
    8392        wireMockRuntimeInfo.getWireMock().loadMappingsFrom(TestUtils.getRegressionDataDir(22520));
    84         wireMockRuntimeInfo.getWireMock().register(WireMock.get(WireMock.urlPathMatching("/0.6/nodes")).willReturn(WireMock.aResponse().withTransformer("MultiplePrimitiveTransformer", "ticket", 22520)));
    85         wireMockRuntimeInfo.getWireMock().register(WireMock.get(WireMock.urlPathMatching("/0.6/relations")).willReturn(WireMock.aResponse().withTransformer("MultiplePrimitiveTransformer", "ticket", 22520)));
    86         wireMockRuntimeInfo.getWireMock().register(WireMock.get(WireMock.urlPathMatching("/0.6/ways")).willReturn(WireMock.aResponse().withTransformer("MultiplePrimitiveTransformer", "ticket", 22520)));
     93        MultiplePrimitiveTransformer.register(wireMockRuntimeInfo.getWireMock(), 22520);
    8794        Config.getPref().put("osm-server.url", wireMockRuntimeInfo.getHttpBaseUrl());
    8895        PrimitiveId building = new SimplePrimitiveId(233056719, OsmPrimitiveType.WAY);
    89         new DownloadOsmTask().loadUrl(new DownloadParams().withLayerName("testTicket22520"), wireMockRuntimeInfo.getHttpBaseUrl() + "/0.6/way/233056719/3", NullProgressMonitor.INSTANCE).get();
    90         OsmDataLayer layer = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream().filter(l -> "testTicket22520".equals(l.getName())).findFirst().orElse(null);
     96        new DownloadOsmTask().loadUrl(new DownloadParams().withLayerName("testTicket22520"),
     97                wireMockRuntimeInfo.getHttpBaseUrl() + "/0.6/way/233056719/3", NullProgressMonitor.INSTANCE).get();
     98        OsmDataLayer layer = MainApplication.getLayerManager().getLayersOfType(OsmDataLayer.class).stream()
     99                .filter(l -> "testTicket22520".equals(l.getName())).findFirst().orElse(null);
    91100        assertNotNull(layer);
    92101        layer.getDataSet().setSelected(building);
    93         RevertChangesetTask task = new RevertChangesetTask(NullProgressMonitor.INSTANCE, Collections.singletonList(36536612), ChangesetReverter.RevertType.SELECTION_WITH_UNDELETE, true, false);
     102        RevertChangesetTask task = new RevertChangesetTask(NullProgressMonitor.INSTANCE, Collections.singletonList(36536612),
     103                ChangesetReverter.RevertType.SELECTION_WITH_UNDELETE, true, false);
    94104        assertDoesNotThrow(task::realRun);
    95105        GuiHelper.runInEDTAndWait(() -> { /* Sync threads */ });
     
    104114    void testTicket23582(WireMockRuntimeInfo wireMockRuntimeInfo) {
    105115        wireMockRuntimeInfo.getWireMock().loadMappingsFrom(TestUtils.getRegressionDataDir(23582));
    106         wireMockRuntimeInfo.getWireMock().register(WireMock.get(WireMock.urlPathMatching("/0.6/nodes")).willReturn(WireMock.aResponse().withTransformer("MultiplePrimitiveTransformer", "ticket", 23582)));
    107         wireMockRuntimeInfo.getWireMock().register(WireMock.get(WireMock.urlPathMatching("/0.6/relations")).willReturn(WireMock.aResponse().withTransformer("MultiplePrimitiveTransformer", "ticket", 23582)));
    108         wireMockRuntimeInfo.getWireMock().register(WireMock.get(WireMock.urlPathMatching("/0.6/ways")).willReturn(WireMock.aResponse().withTransformer("MultiplePrimitiveTransformer", "ticket", 23582)));
     116        MultiplePrimitiveTransformer.register(wireMockRuntimeInfo.getWireMock(), 23582);
    109117        Config.getPref().put("osm-server.url", wireMockRuntimeInfo.getHttpBaseUrl());
    110118        final RevertChangesetTask task = new RevertChangesetTask(149181932, ChangesetReverter.RevertType.FULL, true, true);
     
    117125    }
    118126
     127    @Test
     128    void testTicket23584(WireMockRuntimeInfo wireMockRuntimeInfo) {
     129        new JOptionPaneSimpleMocker(Collections.singletonMap("Conflicts detected", JOptionPane.OK_OPTION));
     130        wireMockRuntimeInfo.getWireMock().loadMappingsFrom(TestUtils.getRegressionDataDir(23584));
     131        MultiplePrimitiveTransformer.register(wireMockRuntimeInfo.getWireMock(), 23584);
     132        PrimitiveTransformer.register(wireMockRuntimeInfo.getWireMock(), 23584);
     133        Config.getPref().put("osm-server.url", wireMockRuntimeInfo.getHttpBaseUrl());
     134        final RevertChangesetTask task = new RevertChangesetTask(NullProgressMonitor.INSTANCE, Collections.singleton(97259223),
     135                ChangesetReverter.RevertType.FULL, true, true);
     136        assertDoesNotThrow(task::realRun);
     137        GuiHelper.runInEDTAndWait(() -> { /* Sync UI thread (some actions are taken on this thread which modify primitives) */ });
     138        final DataSet reverted = MainApplication.getLayerManager().getEditDataSet();
     139        final Collection<OsmPrimitive> modified = reverted.allModifiedPrimitives();
     140        assertAll(() -> assertEquals(58, modified.size()),
     141                () -> assertEquals(18, modified.stream().filter(Relation.class::isInstance).count()),
     142                () -> assertEquals(24, modified.stream().filter(Way.class::isInstance).count()),
     143                () -> assertEquals(16, modified.stream().filter(Node.class::isInstance).count()));
     144    }
     145
    119146    /**
    120      * A transformer for the /nodes?node, /ways?ways, and /relations?relations endpoints. This is needed since we don't always do the requests in the same order.
     147     * A transformer for `/node/:id/:version`, `way/:id/:version`, and `relation/:id/:version` that will use
     148     * the same data as {@link MultiplePrimitiveTransformer}.
     149     */
     150    private static class PrimitiveTransformer extends ResponseTransformer {
     151        /**
     152         * Register the URLs for this transformer
     153         * @param wireMock The wiremock object to register stubs for
     154         * @param ticket The ticket to get data from
     155         */
     156        static void register(WireMock wireMock, int ticket) {
     157            wireMock.register(WireMock.get(WireMock.urlPathMatching("/0.6/node/\\d+/?\\d*")).willReturn(WireMock.aResponse()
     158                    .withTransformer("PrimitiveTransformer", "ticket", ticket)));
     159            wireMock.register(WireMock.get(WireMock.urlPathMatching("/0.6/way/\\d+/?\\d*")).willReturn(WireMock.aResponse()
     160                    .withTransformer("PrimitiveTransformer", "ticket", ticket)));
     161            wireMock.register(WireMock.get(WireMock.urlPathMatching("/0.6/relation/\\d+/?\\d*")).willReturn(WireMock.aResponse()
     162                    .withTransformer("PrimitiveTransformer", "ticket", ticket)));
     163        }
     164
     165        @Override
     166        public boolean applyGlobally() {
     167            return false;
     168        }
     169
     170        @Override
     171        public String getName() {
     172            return "PrimitiveTransformer";
     173        }
     174
     175        @Override
     176        public Response transform(Request request, Response response, FileSource files, Parameters parameters) {
     177            final int ticket = parameters.getInt("ticket");
     178            final String[] parts = request.getUrl().substring(1).split("/", -1);
     179            final int version = Integer.parseInt(parts[3]);
     180            final long id = Long.parseLong(parts[2]);
     181            final OsmParameterInformation info;
     182            switch (parts[1]) {
     183                case "node":
     184                    info = new OsmParameterInformation(OsmPrimitiveType.NODE, id, version);
     185                    break;
     186                case "way":
     187                    info = new OsmParameterInformation(OsmPrimitiveType.WAY, id, version);
     188                    break;
     189                case "relation":
     190                    info = new OsmParameterInformation(OsmPrimitiveType.RELATION, id, version);
     191                    break;
     192                default:
     193                    IllegalArgumentException e = new IllegalArgumentException("No OSM primitive path present");
     194                    Logging.error(e); // Log first since wiremock is really bad about propagating and logging exceptions.
     195                    throw e;
     196            }
     197            final String body = MultiplePrimitiveTransformer.getReturnXml(ticket, info.type, Collections.singleton(info));
     198            return Response.Builder.like(response).but().body(body).build();
     199        }
     200    }
     201
     202    /**
     203     * A transformer for the /nodes?node, /ways?ways, and /relations?relations endpoints.
     204     * This is needed since we don't always do the requests in the same order.
    121205     */
    122206    private static class MultiplePrimitiveTransformer extends ResponseTransformer {
     207
     208        /**
     209         * Register the URLs for this transformer
     210         * @param wireMock The wiremock object to register stubs for
     211         * @param ticket The ticket to get data from
     212         */
     213        static void register(WireMock wireMock, int ticket) {
     214            wireMock.register(WireMock.get(WireMock.urlPathMatching("/0.6/nodes")).willReturn(WireMock.aResponse()
     215                    .withTransformer("MultiplePrimitiveTransformer", "ticket", ticket)));
     216            wireMock.register(WireMock.get(WireMock.urlPathMatching("/0.6/relations")).willReturn(WireMock.aResponse()
     217                    .withTransformer("MultiplePrimitiveTransformer", "ticket", ticket)));
     218            wireMock.register(WireMock.get(WireMock.urlPathMatching("/0.6/ways")).willReturn(WireMock.aResponse()
     219                    .withTransformer("MultiplePrimitiveTransformer", "ticket", ticket)));
     220        }
     221
    123222        @Override
    124223        public String getName() {
     
    151250
    152251        private static String getReturnXml(int ticket, OsmPrimitiveType type, QueryParameter parameter) {
     252            final Set<OsmParameterInformation> objectIds = (parameter.isSingleValued()
     253                    ? Arrays.asList(parameter.values().get(0).split(",", -1))
     254                    : parameter.values())
     255                    .stream().map(s -> new OsmParameterInformation(type, s)).collect(Collectors.toSet());
     256            return getReturnXml(ticket, type, objectIds);
     257        }
     258
     259        private static String getReturnXml(int ticket, OsmPrimitiveType type, Collection<OsmParameterInformation> objectIds) {
    153260            final String file;
    154261            switch (type) {
     
    165272                    throw new IllegalArgumentException("No file for type " + type);
    166273            }
    167             final Set<OsmParameterInformation> objectIds = (parameter.isSingleValued()
    168                     ? Arrays.asList(parameter.values().get(0).split(",", -1))
    169                     : parameter.values())
    170                     .stream().map(s -> new OsmParameterInformation(type, s)).collect(Collectors.toSet());
     274            return getObjectInformation(ticket, file, objectIds);
     275        }
     276
     277        private static String getObjectInformation(int ticket, String file, Collection<OsmParameterInformation> objectIds) {
    171278            try (InputStream fis = Files.newInputStream(Paths.get(TestUtils.getRegressionDataDir(ticket), file));
    172279                 JsonReader reader = Json.createReader(fis)) {
     
    185292                        Optional<OsmParameterInformation> info = objectIds.stream()
    186293                                .filter(o -> !o.isVersioned() && o.type() == i.type() && o.id() == i.id()).findFirst();
    187                         if (info.isPresent() && (matchingElements.get(info.get()) == null || matchingElements.get(info.get()).getInt("version") < i.version())) {
     294                        if (info.isPresent() && (matchingElements.get(info.get()) == null
     295                                || matchingElements.get(info.get()).getInt("version") < i.version())) {
    188296                            Optional<OsmParameterInformation> previous = matchingElements.keySet().stream()
    189297                                    .filter(o -> o.type() == i.type() && o.id() == i.id()).findFirst();
     
    197305                    }
    198306                }
    199                 StringBuilder sb = new StringBuilder("<osm version=\"" + version + "\" generator=\"" + generator + "\" copyright=\"" + copyright + "\" attribution=\"" + attribution + "\" license=\"" + license + "\">");
     307                StringBuilder sb = new StringBuilder("<osm version=\"").append(version).append("\" generator=\"")
     308                        .append(generator).append("\" copyright=\"").append(copyright).append("\" attribution=\"")
     309                        .append(attribution).append("\" license=\"").append(license).append("\">");
    200310                for (JsonObject element : matchingElements.values()) {
    201311                    sb.append(elementToString(element));
     
    240350            if (object.containsKey("tags")) {
    241351                for (Map.Entry<String, JsonValue> tag : object.getJsonObject("tags").entrySet()) {
    242                     sb.append("<tag k=\"").append(tag.getKey()).append("\" v=\"").append(((JsonString) tag.getValue()).getString().replace("\"", "&quot;")).append("\"/>");
     352                    sb.append("<tag k=\"").append(tag.getKey()).append("\" v=\"")
     353                            .append(((JsonString) tag.getValue()).getString().replace("\"", "&quot;"))
     354                            .append("\"/>");
    243355                }
    244356            }
     
    261373            }
    262374        }
     375    }
     376
     377    /**
     378     * A record class for parameter information.
     379     */
     380    private static final class OsmParameterInformation {
     381        private final OsmPrimitiveType type;
     382        private final long id;
     383        private final int version;
    263384
    264385        /**
    265          * A record class for parameter information.
     386         * Create a new record
     387         * @param type The type
     388         * @param param The URl parameter bit (can be something like {@code "123v1"})
    266389         */
    267         private static final class OsmParameterInformation {
    268             private final OsmPrimitiveType type;
    269             private final long id;
    270             private final int version;
    271 
    272             /**
    273              * Create a new record
    274              * @param type The type
    275              * @param param The URl parameter bit (can be something like {@code "123v1"})
    276              */
    277             public OsmParameterInformation(OsmPrimitiveType type, String param) {
    278                 this(type, Long.parseLong(param.split("v")[0]),
    279                         param.split("v").length == 2 ? Integer.parseInt(param.split("v")[1]) : Integer.MIN_VALUE);
    280             }
    281 
    282             /**
    283              * Create a new record
    284              * @param type The type
    285              * @param id The primitive id
    286              * @param version The primitive version. Anything less than or equal to 0 means latest.`
    287              */
    288             public OsmParameterInformation(OsmPrimitiveType type, long id, int version) {
    289                 this.type = type;
    290                 this.id = id;
    291                 this.version = Math.max(version, 0);
    292             }
    293 
    294             public OsmPrimitiveType type() {
    295                 return this.type;
    296             }
    297 
    298             public long id() {
    299                 return this.id;
    300             }
    301 
    302             public int version() {
    303                 return this.version;
    304             }
    305 
    306             /**
    307              * Check if this is versioned
    308              * @return {@code true} if we are looking for a specific version instead of the latest.
    309              */
    310             public boolean isVersioned() {
    311                 return this.version > 0;
    312             }
    313 
    314             @Override
    315             public int hashCode() {
    316                 return Objects.hash(this.type, this.id, this.version);
    317             }
    318 
    319             @Override
    320             public boolean equals(Object obj) {
    321                 if (obj instanceof OsmParameterInformation) {
    322                     OsmParameterInformation other = (OsmParameterInformation) obj;
    323                     return this.type == other.type &&
    324                             this.id == other.id &&
    325                             this.version == other.version;
    326                 }
    327                 return false;
    328             }
    329 
    330             @Override
    331             public String toString() {
    332                 return "[type=\"" + this.type + "\" id=\"" + this.id + "\" version=\"" + this.version +"\"]";
    333             }
     390        OsmParameterInformation(OsmPrimitiveType type, String param) {
     391            this(type, Long.parseLong(param.split("v")[0]),
     392                    param.split("v").length == 2 ? Integer.parseInt(param.split("v")[1]) : Integer.MIN_VALUE);
     393        }
     394
     395        /**
     396         * Create a new record
     397         * @param type The type
     398         * @param id The primitive id
     399         * @param version The primitive version. Anything less than or equal to 0 means latest.`
     400         */
     401        OsmParameterInformation(OsmPrimitiveType type, long id, int version) {
     402            this.type = type;
     403            this.id = id;
     404            this.version = Math.max(version, 0);
     405        }
     406
     407        public OsmPrimitiveType type() {
     408            return this.type;
     409        }
     410
     411        public long id() {
     412            return this.id;
     413        }
     414
     415        public int version() {
     416            return this.version;
     417        }
     418
     419        /**
     420         * Check if this is versioned
     421         * @return {@code true} if we are looking for a specific version instead of the latest.
     422         */
     423        public boolean isVersioned() {
     424            return this.version > 0;
     425        }
     426
     427        @Override
     428        public int hashCode() {
     429            return Objects.hash(this.type, this.id, this.version);
     430        }
     431
     432        @Override
     433        public boolean equals(Object obj) {
     434            if (obj instanceof OsmParameterInformation) {
     435                OsmParameterInformation other = (OsmParameterInformation) obj;
     436                return this.type == other.type &&
     437                        this.id == other.id &&
     438                        this.version == other.version;
     439            }
     440            return false;
     441        }
     442
     443        @Override
     444        public String toString() {
     445            return "[type=\"" + this.type + "\" id=\"" + this.id + "\" version=\"" + this.version +"\"]";
    334446        }
    335447    }
Note: See TracChangeset for help on using the changeset viewer.