From 6269764196d35f2737cf62b7cb1c9007400dfc16 Mon Sep 17 00:00:00 2001
From: Robert Scott <code@humanleg.org.uk>
Date: Wed, 29 Aug 2018 20:10:21 +0100
Subject: [PATCH v1 2/2] tests: add new tests exercising handling of plugins
 that advertise multiple versions for compatibility

---
 test/data/__files/plugin/qux_plugin.v345.jar       | Bin 0 -> 3919 bytes
 test/data/__files/plugin/qux_plugin.v346.jar       | Bin 0 -> 3918 bytes
 test/data/__files/plugin/qux_plugin.v432.jar       | Bin 0 -> 3918 bytes
 test/data/__files/plugin/qux_plugin.v435.jar       | Bin 0 -> 3919 bytes
 .../plugin/PluginPreferenceHighLevelTest.java      | 126 +++++++++++-
 .../josm/plugins/PluginHandlerJOSMTooOldTest.java  |  76 ++++++++
 .../plugins/PluginHandlerMultiVersionTest.java     | 215 +++++++++++++++++++++
 7 files changed, 416 insertions(+), 1 deletion(-)
 create mode 100644 test/data/__files/plugin/qux_plugin.v345.jar
 create mode 100644 test/data/__files/plugin/qux_plugin.v346.jar
 create mode 100644 test/data/__files/plugin/qux_plugin.v432.jar
 create mode 100644 test/data/__files/plugin/qux_plugin.v435.jar
 create mode 100644 test/unit/org/openstreetmap/josm/plugins/PluginHandlerMultiVersionTest.java

diff --git a/test/data/__files/plugin/qux_plugin.v345.jar b/test/data/__files/plugin/qux_plugin.v345.jar
new file mode 100644
index 0000000000000000000000000000000000000000..4d8b0710cfd10535c74bd0cca9e94d2c642fea47
GIT binary patch
literal 3919
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
Oz~5(ZMtVH-xBmf_r$P1r

literal 0
HcmV?d00001

diff --git a/test/data/__files/plugin/qux_plugin.v346.jar b/test/data/__files/plugin/qux_plugin.v346.jar
new file mode 100644
index 0000000000000000000000000000000000000000..7b0a7ab71e0def48c20c64f806991a8dde9edfd9
GIT binary patch
literal 3918
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
L3G|UYH;4xSiP|ny

literal 0
HcmV?d00001

diff --git a/test/data/__files/plugin/qux_plugin.v432.jar b/test/data/__files/plugin/qux_plugin.v432.jar
new file mode 100644
index 0000000000000000000000000000000000000000..377a1c5190ba81139b8a8116dcd00fe487df0cf4
GIT binary patch
literal 3918
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
M_!H<Odu|X901=Tgx&QzG

literal 0
HcmV?d00001

diff --git a/test/data/__files/plugin/qux_plugin.v435.jar b/test/data/__files/plugin/qux_plugin.v435.jar
new file mode 100644
index 0000000000000000000000000000000000000000..a4218e18223ccb50206920bf094130617d82eb30
GIT binary patch
literal 3919
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
O{6P2<=pzSi5Dx$(mo^sw

literal 0
HcmV?d00001

diff --git a/test/unit/org/openstreetmap/josm/gui/preferences/plugin/PluginPreferenceHighLevelTest.java b/test/unit/org/openstreetmap/josm/gui/preferences/plugin/PluginPreferenceHighLevelTest.java
index de22b0332..e78ddf742 100644
--- a/test/unit/org/openstreetmap/josm/gui/preferences/plugin/PluginPreferenceHighLevelTest.java
+++ b/test/unit/org/openstreetmap/josm/gui/preferences/plugin/PluginPreferenceHighLevelTest.java
@@ -803,7 +803,6 @@ public class PluginPreferenceHighLevelTest {
         TestUtils.syncEDTAndWorkerThreads();
 
         assertEquals(1, jopsMocker.getInvocationLog().size());
-        org.openstreetmap.josm.tools.Logging.error(jopsMocker.getInvocationLog().get(0)[0].toString());
         Object[] invocationLogEntry = jopsMocker.getInvocationLog().get(0);
         assertEquals(JOptionPane.OK_OPTION, (int) invocationLogEntry[0]);
         assertEquals("Warning", invocationLogEntry[2]);
@@ -817,6 +816,11 @@ public class PluginPreferenceHighLevelTest {
         // dummy_plugin was fetched
         this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/dummy_plugin.v31772.jar")));
 
+        // the dummy_plugin jar has been installed
+        TestUtils.assertFileContentsEqual(this.referenceDummyJarNew, this.targetDummyJar);
+        // the baz_plugin jar has not
+        assertFalse(this.targetBazJar.exists());
+
         // loadPlugins(...) was called (with expected parameters)
         assertTrue(loadPluginsCalled[0]);
 
@@ -826,4 +830,124 @@ public class PluginPreferenceHighLevelTest {
         // questionably correct
         assertEquals("999", Config.getPref().get("pluginmanager.lastupdate", "111"));
     }
+
+    /**
+     * Tests installing a single plugin which has multiple versions advertised, with our JOSM version
+     * preventing us from using the latest version
+     * @throws Exception on failure
+     */
+    @JOSMTestRules.OverrideAssumeRevision("Revision: 7000\n")
+    @Test
+    public void testInstallMultiVersion() throws Exception {
+        TestUtils.assumeWorkingJMockit();
+
+        final String bazOldServePath = "/baz/old.jar";
+        final PluginServer pluginServer = new PluginServer(
+            new PluginServer.RemotePlugin(this.referenceDummyJarNew),
+            new PluginServer.RemotePlugin(this.referenceBazJarNew, ImmutableMap.of(
+                "6800_Plugin-Url", "6;http://localhost:" + this.pluginServerRule.port() + bazOldServePath
+            ))
+        );
+        pluginServer.applyToWireMockServer(this.pluginServerRule);
+        // need to actually serve this older jar from somewhere
+        this.pluginServerRule.stubFor(
+            WireMock.get(WireMock.urlEqualTo(bazOldServePath)).willReturn(
+                WireMock.aResponse().withStatus(200).withHeader("Content-Type", "application/java-archive").withBodyFile(
+                    "plugin/baz_plugin.v6.jar"
+                )
+            )
+        );
+        Config.getPref().putList("plugins", ImmutableList.of());
+
+        final HelpAwareOptionPaneMocker haMocker = new HelpAwareOptionPaneMocker(ImmutableMap.of(
+            "<html>The following plugin has been downloaded <strong>successfully</strong>:"
+            + "<ul><li>baz_plugin (6)</li></ul>"
+            + "You have to restart JOSM for some settings to take effect.<br/><br/>"
+            + "Would you like to restart now?</html>",
+            "Cancel"
+        ));
+        final JOptionPaneSimpleMocker jopsMocker = new JOptionPaneSimpleMocker();
+
+        final PreferenceTabbedPane tabbedPane = new PreferenceTabbedPane();
+
+        tabbedPane.buildGui();
+        // PluginPreference is already added to PreferenceTabbedPane by default
+        tabbedPane.selectTabByPref(PluginPreference.class);
+
+        GuiHelper.runInEDTAndWait(
+            () -> ((javax.swing.JButton) TestUtils.getComponentByName(tabbedPane, "downloadListButton")).doClick()
+        );
+
+        TestUtils.syncEDTAndWorkerThreads();
+
+        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
+        WireMock.resetAllRequests();
+
+        final PluginPreferencesModel model = (PluginPreferencesModel) TestUtils.getPrivateField(
+            tabbedPane.getPluginPreference(),
+            "model"
+        );
+
+        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
+        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
+        assertTrue(model.getPluginsScheduledForUpdateOrDownload().isEmpty());
+        assertEquals(model.getDisplayedPlugins(), model.getAvailablePlugins());
+
+        assertEquals(
+            ImmutableList.of("baz_plugin", "dummy_plugin"),
+            model.getAvailablePlugins().stream().map((pi) -> pi.getName()).collect(ImmutableList.toImmutableList())
+        );
+        assertTrue(model.getSelectedPlugins().isEmpty());
+        assertEquals(
+            ImmutableList.of("(null)", "(null)"),
+            model.getAvailablePlugins().stream().map(
+                (pi) -> pi.localversion == null ? "(null)" : pi.localversion
+            ).collect(ImmutableList.toImmutableList())
+        );
+        assertEquals(
+            ImmutableList.of("6", "31772"),
+            model.getAvailablePlugins().stream().map((pi) -> pi.version).collect(ImmutableList.toImmutableList())
+        );
+
+        // now we select dummy_plugin
+        model.setPluginSelected("baz_plugin", true);
+
+        // model should now reflect this
+        assertEquals(
+            ImmutableList.of("baz_plugin"),
+            model.getNewlyActivatedPlugins().stream().map(
+                pi -> pi.getName()
+            ).collect(ImmutableList.toImmutableList())
+        );
+        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
+
+        tabbedPane.savePreferences();
+
+        TestUtils.syncEDTAndWorkerThreads();
+
+        assertEquals(1, haMocker.getInvocationLog().size());
+        Object[] invocationLogEntry = haMocker.getInvocationLog().get(0);
+        assertEquals(1, (int) invocationLogEntry[0]);
+        assertEquals("Restart", invocationLogEntry[2]);
+
+        assertTrue(jopsMocker.getInvocationLog().isEmpty());
+
+        // any .jar.new files should have been deleted
+        assertFalse(targetDummyJarNew.exists());
+        assertFalse(targetBazJarNew.exists());
+
+        // dummy_plugin was fetched
+        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo(bazOldServePath)));
+
+        // the "old" baz_plugin jar has been installed
+        TestUtils.assertFileContentsEqual(this.referenceBazJarOld, this.targetBazJar);
+        // the dummy_plugin jar has not
+        assertFalse(this.targetDummyJar.exists());
+
+        // pluginmanager.version has been set to the current version
+        assertEquals(7000, Config.getPref().getInt("pluginmanager.version", 111));
+        // pluginmanager.lastupdate hasn't been updated
+        // questionably correct
+        assertEquals("999", Config.getPref().get("pluginmanager.lastupdate", "111"));
+    }
 }
diff --git a/test/unit/org/openstreetmap/josm/plugins/PluginHandlerJOSMTooOldTest.java b/test/unit/org/openstreetmap/josm/plugins/PluginHandlerJOSMTooOldTest.java
index ec880aee1..dd3785f1c 100644
--- a/test/unit/org/openstreetmap/josm/plugins/PluginHandlerJOSMTooOldTest.java
+++ b/test/unit/org/openstreetmap/josm/plugins/PluginHandlerJOSMTooOldTest.java
@@ -66,23 +66,37 @@ public class PluginHandlerJOSMTooOldTest {
         this.referenceDummyJarNew = new File(TestUtils.getTestDataRoot(), "__files/plugin/dummy_plugin.v31772.jar");
         this.referenceBazJarOld = new File(TestUtils.getTestDataRoot(), "__files/plugin/baz_plugin.v6.jar");
         this.referenceBazJarNew = new File(TestUtils.getTestDataRoot(), "__files/plugin/baz_plugin.v7.jar");
+        this.referenceQuxJarOld = new File(TestUtils.getTestDataRoot(), "__files/" + referencePathQuxJarOld);
+        this.referenceQuxJarNew = new File(TestUtils.getTestDataRoot(), "__files/" + referencePathQuxJarNew);
+        this.referenceQuxJarNewer = new File(TestUtils.getTestDataRoot(), "__files/" + referencePathQuxJarNewer);
         this.pluginDir = Preferences.main().getPluginsDirectory();
         this.targetDummyJar = new File(this.pluginDir, "dummy_plugin.jar");
         this.targetDummyJarNew = new File(this.pluginDir, "dummy_plugin.jar.new");
         this.targetBazJar = new File(this.pluginDir, "baz_plugin.jar");
         this.targetBazJarNew = new File(this.pluginDir, "baz_plugin.jar.new");
+        this.targetQuxJar = new File(this.pluginDir, "qux_plugin.jar");
+        this.targetQuxJarNew = new File(this.pluginDir, "qux_plugin.jar.new");
         this.pluginDir.mkdirs();
     }
 
+    private static final String referencePathQuxJarOld = "plugin/qux_plugin.v345.jar";
+    private static final String referencePathQuxJarNew = "plugin/qux_plugin.v346.jar";
+    private static final String referencePathQuxJarNewer = "plugin/qux_plugin.v432.jar";
+
     private File pluginDir;
     private File referenceDummyJarOld;
     private File referenceDummyJarNew;
     private File referenceBazJarOld;
     private File referenceBazJarNew;
+    private File referenceQuxJarOld;
+    private File referenceQuxJarNew;
+    private File referenceQuxJarNewer;
     private File targetDummyJar;
     private File targetDummyJarNew;
     private File targetBazJar;
     private File targetBazJarNew;
+    private File targetQuxJar;
+    private File targetQuxJarNew;
 
     private final String bazPluginVersionReqString = "JOSM version 8,001 required for plugin baz_plugin.";
     private final String dummyPluginVersionReqString = "JOSM version 7,001 required for plugin dummy_plugin.";
@@ -282,4 +296,66 @@ public class PluginHandlerJOSMTooOldTest {
         assertEquals(Config.getPref().getInt("pluginmanager.version", 111), 6000);
         assertNotEquals(Config.getPref().get("pluginmanager.lastupdate", "999"), "999");
     }
+
+    /**
+     * When a plugin advertises several versions for compatibility with older JOSMs, but even the
+     * oldest of those is newer than our JOSM version, the user is prompted to upgrade to the newest
+     * version anyway.
+     *
+     * While this behaviour is not incorrect, it's probably less helpful than it could be - the
+     * version that's most likely to work best in this case will be the "oldest" still-available
+     * version, however this test documents the behaviour.
+     * @throws IOException never
+     */
+    @Test
+    @JOSMTestRules.OverrideAssumeRevision("Revision: 7200\n")
+    public void testUpdatePluginsMultiVersionInsufficient() throws Exception {
+        TestUtils.assumeWorkingJMockit();
+
+        final String quxNewServePath = "/qux/new.jar";
+        final PluginServer pluginServer = new PluginServer(
+            new PluginServer.RemotePlugin(this.referenceBazJarOld),
+            new PluginServer.RemotePlugin(this.referenceQuxJarNewer, ImmutableMap.of(
+                "7499_Plugin-Url", "346;http://localhost:" + this.pluginServerRule.port() + "/dont/bother.jar"
+            ))
+        );
+        pluginServer.applyToWireMockServer(this.pluginServerRule);
+        Config.getPref().putList("plugins", ImmutableList.of("qux_plugin", "baz_plugin"));
+
+        new ExtendedDialogMocker(ImmutableMap.of("JOSM version 7,500 required for plugin qux_plugin.", "Download Plugin"));
+
+        Files.copy(this.referenceQuxJarOld.toPath(), this.targetQuxJar.toPath());
+        Files.copy(this.referenceBazJarOld.toPath(), this.targetBazJar.toPath());
+
+        final List<PluginInformation> updatedPlugins = PluginHandler.updatePlugins(
+            MainApplication.getMainFrame(),
+            null,
+            null,
+            false
+        ).stream().sorted((a, b) -> a.name.compareTo(b.name)).collect(ImmutableList.toImmutableList());
+
+        assertEquals(2, updatedPlugins.size());
+
+        assertEquals("baz_plugin", updatedPlugins.get(0).name);
+        assertEquals("6", updatedPlugins.get(0).localversion);
+
+        assertEquals("qux_plugin", updatedPlugins.get(1).name);
+        // questionably correct
+        assertEquals("432", updatedPlugins.get(1).localversion);
+
+        assertFalse(targetQuxJarNew.exists());
+
+        TestUtils.assertFileContentsEqual(this.referenceBazJarOld, this.targetBazJar);
+        // questionably correct
+        TestUtils.assertFileContentsEqual(this.referenceQuxJarNewer, this.targetQuxJar);
+
+        assertEquals(2, WireMock.getAllServeEvents().size());
+        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
+        // questionably correct
+        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/qux_plugin.v432.jar")));
+
+        assertEquals(7200, Config.getPref().getInt("pluginmanager.version", 111));
+        // not mocking the time so just check it's not its original value
+        assertNotEquals("999", Config.getPref().get("pluginmanager.lastupdate", "999"));
+    }
 }
diff --git a/test/unit/org/openstreetmap/josm/plugins/PluginHandlerMultiVersionTest.java b/test/unit/org/openstreetmap/josm/plugins/PluginHandlerMultiVersionTest.java
new file mode 100644
index 000000000..6ca9e853a
--- /dev/null
+++ b/test/unit/org/openstreetmap/josm/plugins/PluginHandlerMultiVersionTest.java
@@ -0,0 +1,215 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.plugins;
+
+import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+
+import java.io.File;
+import java.nio.file.Files;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.PluginServer;
+import org.openstreetmap.josm.testutils.mockers.ExtendedDialogMocker;
+
+import com.github.tomakehurst.wiremock.client.WireMock;
+import com.github.tomakehurst.wiremock.junit.WireMockRule;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
+/**
+ * Test parts of {@link PluginHandler} class with plugins that advertise multiple versions for compatibility.
+ */
+public class PluginHandlerMultiVersionTest {
+    /**
+     * Setup test.
+     */
+    @Rule
+    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
+    public JOSMTestRules test = new JOSMTestRules().preferences().main();
+
+    /**
+     * Plugin server mock.
+     */
+    @Rule
+    public WireMockRule pluginServerRule = new WireMockRule(
+        options().dynamicPort().usingFilesUnderDirectory(TestUtils.getTestDataRoot())
+    );
+
+    /**
+     * Setup test.
+     */
+    @Before
+    public void setUp() {
+        Config.getPref().putInt("pluginmanager.version", 999);
+        Config.getPref().put("pluginmanager.lastupdate", "999");
+        Config.getPref().putList("pluginmanager.sites",
+            ImmutableList.of(String.format("http://localhost:%s/plugins", this.pluginServerRule.port()))
+        );
+
+        this.referenceBazJarOld = new File(TestUtils.getTestDataRoot(), "__files/plugin/baz_plugin.v6.jar");
+        this.referenceBazJarNew = new File(TestUtils.getTestDataRoot(), "__files/plugin/baz_plugin.v7.jar");
+        this.referenceQuxJarOld = new File(TestUtils.getTestDataRoot(), "__files/" + referencePathQuxJarOld);
+        this.referenceQuxJarNew = new File(TestUtils.getTestDataRoot(), "__files/" + referencePathQuxJarNew);
+        this.referenceQuxJarNewer = new File(TestUtils.getTestDataRoot(), "__files/" + referencePathQuxJarNewer);
+        this.referenceQuxJarNewest = new File(TestUtils.getTestDataRoot(), "__files/" + referencePathQuxJarNewest);
+        this.pluginDir = Preferences.main().getPluginsDirectory();
+        this.targetBazJar = new File(this.pluginDir, "baz_plugin.jar");
+        this.targetBazJarNew = new File(this.pluginDir, "baz_plugin.jar.new");
+        this.targetQuxJar = new File(this.pluginDir, "qux_plugin.jar");
+        this.targetQuxJarNew = new File(this.pluginDir, "qux_plugin.jar.new");
+        this.pluginDir.mkdirs();
+    }
+
+    private static final String referencePathQuxJarOld = "plugin/qux_plugin.v345.jar";
+    private static final String referencePathQuxJarNew = "plugin/qux_plugin.v346.jar";
+    private static final String referencePathQuxJarNewer = "plugin/qux_plugin.v432.jar";
+    private static final String referencePathQuxJarNewest = "plugin/qux_plugin.v435.jar";
+
+    private File pluginDir;
+    private File referenceBazJarOld;
+    private File referenceBazJarNew;
+    private File referenceQuxJarOld;
+    private File referenceQuxJarNew;
+    private File referenceQuxJarNewer;
+    private File referenceQuxJarNewest;
+    private File targetBazJar;
+    private File targetBazJarNew;
+    private File targetQuxJar;
+    private File targetQuxJarNew;
+
+    /**
+     * test update of plugins when our current JOSM version prevents us from using the latest version,
+     * but an additional version is listed which *does* support our version
+     * @throws Exception on failure
+     */
+    @JOSMTestRules.OverrideAssumeRevision("Revision: 7501\n")
+    @Test
+    public void testUpdatePluginsOneMultiVersion() throws Exception {
+        TestUtils.assumeWorkingJMockit();
+
+        final String quxNewerServePath = "/qux/newer.jar";
+        final PluginServer pluginServer = new PluginServer(
+            new PluginServer.RemotePlugin(this.referenceBazJarOld),
+            new PluginServer.RemotePlugin(this.referenceQuxJarNewest, ImmutableMap.of(
+                "7500_Plugin-Url", "432;http://localhost:" + this.pluginServerRule.port() + quxNewerServePath,
+                "7499_Plugin-Url", "346;http://localhost:" + this.pluginServerRule.port() + "/not/served.jar",
+                "6999_Plugin-Url", "345;http://localhost:" + this.pluginServerRule.port() + "/not/served/either.jar"
+            ))
+        );
+        pluginServer.applyToWireMockServer(this.pluginServerRule);
+        // need to actually serve this older jar from somewhere
+        this.pluginServerRule.stubFor(
+            WireMock.get(WireMock.urlEqualTo(quxNewerServePath)).willReturn(
+                WireMock.aResponse().withStatus(200).withHeader("Content-Type", "application/java-archive").withBodyFile(
+                    referencePathQuxJarNewer
+                )
+            )
+        );
+        Config.getPref().putList("plugins", ImmutableList.of("qux_plugin", "baz_plugin"));
+
+        // catch any (unexpected) attempts to show us an ExtendedDialog
+        new ExtendedDialogMocker();
+
+        Files.copy(this.referenceQuxJarOld.toPath(), this.targetQuxJar.toPath());
+        Files.copy(this.referenceBazJarOld.toPath(), this.targetBazJar.toPath());
+
+        final List<PluginInformation> updatedPlugins = PluginHandler.updatePlugins(
+            MainApplication.getMainFrame(),
+            null,
+            null,
+            false
+        ).stream().sorted((a, b) -> a.name.compareTo(b.name)).collect(ImmutableList.toImmutableList());
+
+        assertEquals(2, updatedPlugins.size());
+
+        assertEquals("baz_plugin", updatedPlugins.get(0).name);
+        assertEquals("6", updatedPlugins.get(0).localversion);
+
+        assertEquals("qux_plugin", updatedPlugins.get(1).name);
+        assertEquals("432", updatedPlugins.get(1).localversion);
+
+        assertFalse(targetBazJarNew.exists());
+        assertFalse(targetQuxJarNew.exists());
+
+        TestUtils.assertFileContentsEqual(this.referenceBazJarOld, this.targetBazJar);
+        TestUtils.assertFileContentsEqual(this.referenceQuxJarNewer, this.targetQuxJar);
+
+        assertEquals(2, WireMock.getAllServeEvents().size());
+        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
+        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo(quxNewerServePath)));
+
+        assertEquals(7501, Config.getPref().getInt("pluginmanager.version", 111));
+        // not mocking the time so just check it's not its original value
+        assertNotEquals("999", Config.getPref().get("pluginmanager.lastupdate", "999"));
+    }
+
+    /**
+     * test update of plugins when our current JOSM version prevents us from using all but the version
+     * we already have, which is still listed.
+     * @throws Exception on failure
+     */
+    @JOSMTestRules.OverrideAssumeRevision("Revision: 7000\n")
+    @Test
+    public void testUpdatePluginsExistingVersionLatestPossible() throws Exception {
+        TestUtils.assumeWorkingJMockit();
+
+        final PluginServer pluginServer = new PluginServer(
+            new PluginServer.RemotePlugin(this.referenceBazJarOld),
+            new PluginServer.RemotePlugin(this.referenceQuxJarNewest, ImmutableMap.of(
+                "7500_Plugin-Url", "432;http://localhost:" + this.pluginServerRule.port() + "/dont.jar",
+                "7499_Plugin-Url", "346;http://localhost:" + this.pluginServerRule.port() + "/even.jar",
+                "6999_Plugin-Url", "345;http://localhost:" + this.pluginServerRule.port() + "/bother.jar"
+            ))
+        );
+        pluginServer.applyToWireMockServer(this.pluginServerRule);
+        Config.getPref().putList("plugins", ImmutableList.of("qux_plugin", "baz_plugin"));
+
+        // catch any (unexpected) attempts to show us an ExtendedDialog
+        new ExtendedDialogMocker();
+
+        Files.copy(this.referenceQuxJarOld.toPath(), this.targetQuxJar.toPath());
+        Files.copy(this.referenceBazJarOld.toPath(), this.targetBazJar.toPath());
+
+        final List<PluginInformation> updatedPlugins = PluginHandler.updatePlugins(
+            MainApplication.getMainFrame(),
+            null,
+            null,
+            false
+        ).stream().sorted((a, b) -> a.name.compareTo(b.name)).collect(ImmutableList.toImmutableList());
+
+        assertEquals(2, updatedPlugins.size());
+
+        assertEquals("baz_plugin", updatedPlugins.get(0).name);
+        assertEquals("6", updatedPlugins.get(0).localversion);
+
+        assertEquals("qux_plugin", updatedPlugins.get(1).name);
+        assertEquals("345", updatedPlugins.get(1).localversion);
+
+        assertFalse(targetBazJarNew.exists());
+        assertFalse(targetQuxJarNew.exists());
+
+        // should be as before
+        TestUtils.assertFileContentsEqual(this.referenceBazJarOld, this.targetBazJar);
+        TestUtils.assertFileContentsEqual(this.referenceQuxJarOld, this.targetQuxJar);
+
+        // only the plugins list should have been downloaded
+        assertEquals(1, WireMock.getAllServeEvents().size());
+        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
+
+        assertEquals(7000, Config.getPref().getInt("pluginmanager.version", 111));
+        // not mocking the time so just check it's not its original value
+        assertNotEquals("999", Config.getPref().get("pluginmanager.lastupdate", "999"));
+    }
+}
-- 
2.11.0

