Ticket #16010: v2-0028-tests-add-PluginPreferenceHighLevelTest.patch

File v2-0028-tests-add-PluginPreferenceHighLevelTest.patch, 37.8 KB (added by ris, 15 months ago)
  • new file test/unit/org/openstreetmap/josm/gui/preferences/plugin/PluginPreferenceHighLevelTest.java

    From f7d85a4799fbcaef069eeac79399852f87bec0b4 Mon Sep 17 00:00:00 2001
    From: Robert Scott <code@humanleg.org.uk>
    Date: Sat, 12 May 2018 11:25:27 +0100
    Subject: [PATCH v2 28/28] tests: add PluginPreferenceHighLevelTest
    
    ---
     .../plugin/PluginPreferenceHighLevelTest.java      | 826 +++++++++++++++++++++
     1 file changed, 826 insertions(+)
     create mode 100644 test/unit/org/openstreetmap/josm/gui/preferences/plugin/PluginPreferenceHighLevelTest.java
    
    diff --git a/test/unit/org/openstreetmap/josm/gui/preferences/plugin/PluginPreferenceHighLevelTest.java b/test/unit/org/openstreetmap/josm/gui/preferences/plugin/PluginPreferenceHighLevelTest.java
    new file mode 100644
    index 000000000..b490ca00c
    - +  
     1// License: GPL. For details, see LICENSE file.
     2package org.openstreetmap.josm.gui.preferences.plugin;
     3
     4import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertTrue;
     6import static org.junit.Assert.assertFalse;
     7
     8import org.junit.Rule;
     9import org.junit.Test;
     10
     11import java.io.File;
     12import java.nio.file.Files;
     13import java.awt.Component;
     14import java.awt.Window;
     15import javax.swing.JOptionPane;
     16import java.util.Collection;
     17
     18import static java.util.concurrent.TimeUnit.MILLISECONDS;
     19
     20import org.junit.After;
     21import org.junit.Before;
     22import org.openstreetmap.josm.Main;
     23import org.openstreetmap.josm.TestUtils;
     24import org.openstreetmap.josm.gui.preferences.PreferenceTabbedPane;
     25import org.openstreetmap.josm.gui.util.GuiHelper;
     26import org.openstreetmap.josm.plugins.PluginHandler;
     27import org.openstreetmap.josm.plugins.PluginProxy;
     28import org.openstreetmap.josm.spi.preferences.Config;
     29import org.openstreetmap.josm.testutils.HelpAwareOptionPaneMocker;
     30import org.openstreetmap.josm.testutils.JOptionPaneSimpleMocker;
     31import org.openstreetmap.josm.testutils.JOSMTestRules;
     32import org.openstreetmap.josm.testutils.PluginServer;
     33
     34import org.awaitility.Awaitility;
     35
     36import com.google.common.collect.ImmutableList;
     37import com.google.common.collect.ImmutableMap;
     38
     39import com.github.tomakehurst.wiremock.client.WireMock;
     40import com.github.tomakehurst.wiremock.junit.WireMockRule;
     41
     42import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options;
     43
     44import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
     45
     46
     47
     48/**
     49 * Higher level tests of {@link PluginPreference} class.
     50 */
     51public class PluginPreferenceHighLevelTest {
     52    @Rule
     53    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
     54    public JOSMTestRules test = new JOSMTestRules().assumeRevision(
     55        "Revision: 10000\n"
     56    ).preferences().main().assertionsInEDT().platform();
     57
     58    /**
     59     * Plugin server mock.
     60     */
     61    @Rule
     62    public WireMockRule pluginServerRule = new WireMockRule(
     63        options().dynamicPort().usingFilesUnderDirectory(TestUtils.getTestDataRoot())
     64    );
     65
     66    @Before
     67    public void setUp() throws Exception {
     68        if (!java.awt.GraphicsEnvironment.isHeadless()) {
     69            originalMainParent = Main.parent;
     70            Main.parent = new Window(null);
     71        }
     72
     73        // some other tests actually go ahead and load plugins (notably at time of writing,
     74        // MainApplicationTest$testUpdateAndLoadPlugins), which really isn't a reversible operation.
     75        // it is, however, possible to pretend to our tests temporarily that they *aren't* loaded by
     76        // setting the PluginHandler#pluginList to empty for the duration of this test. ideally these
     77        // other tests wouldn't be so badly behaved or would at least do this from a separate batch
     78        // but this works for now
     79        @SuppressWarnings("unchecked")
     80        final Collection<PluginProxy> pluginList = (Collection<PluginProxy>) TestUtils.getPrivateStaticField(
     81            PluginHandler.class,
     82            "pluginList"
     83        );
     84        this.originalPluginList = ImmutableList.copyOf(pluginList);
     85        pluginList.clear();
     86
     87        Config.getPref().putInt("pluginmanager.version", 999);
     88        Config.getPref().put("pluginmanager.lastupdate", "999");
     89        Config.getPref().putList("pluginmanager.sites",
     90            ImmutableList.of(String.format("http://localhost:%s/plugins", this.pluginServerRule.port()))
     91        );
     92
     93        this.referenceDummyJarOld = new File(TestUtils.getTestDataRoot(), "__files/plugin/dummy_plugin.v31701.jar");
     94        this.referenceDummyJarNew = new File(TestUtils.getTestDataRoot(), "__files/plugin/dummy_plugin.v31772.jar");
     95        this.referenceBazJarOld = new File(TestUtils.getTestDataRoot(), "__files/plugin/baz_plugin.v6.jar");
     96        this.referenceBazJarNew = new File(TestUtils.getTestDataRoot(), "__files/plugin/baz_plugin.v7.jar");
     97        this.pluginDir = Main.pref.getPluginsDirectory();
     98        this.targetDummyJar = new File(this.pluginDir, "dummy_plugin.jar");
     99        this.targetDummyJarNew = new File(this.pluginDir, "dummy_plugin.jar.new");
     100        this.targetBazJar = new File(this.pluginDir, "baz_plugin.jar");
     101        this.targetBazJarNew = new File(this.pluginDir, "baz_plugin.jar.new");
     102        this.pluginDir.mkdirs();
     103    }
     104
     105    @After
     106    public void tearDown() throws Exception {
     107        // restore actual PluginHandler#pluginList
     108        @SuppressWarnings("unchecked")
     109        final Collection<PluginProxy> pluginList = (Collection<PluginProxy>) TestUtils.getPrivateStaticField(
     110            PluginHandler.class,
     111            "pluginList"
     112        );
     113        pluginList.clear();
     114        pluginList.addAll(this.originalPluginList);
     115
     116        if (!java.awt.GraphicsEnvironment.isHeadless()) {
     117            Main.parent = originalMainParent;
     118        }
     119    }
     120
     121    private Collection<PluginProxy> originalPluginList;
     122
     123    private Component originalMainParent;
     124
     125    private File pluginDir;
     126    private File referenceDummyJarOld;
     127    private File referenceDummyJarNew;
     128    private File referenceBazJarOld;
     129    private File referenceBazJarNew;
     130    private File targetDummyJar;
     131    private File targetDummyJarNew;
     132    private File targetBazJar;
     133    private File targetBazJarNew;
     134
     135    /**
     136     * Tests choosing a new plugin to install without upgrading an already-installed plugin
     137     */
     138    @Test
     139    public void testInstallWithoutUpdate() throws Exception {
     140        final PluginServer pluginServer = new PluginServer(
     141            new PluginServer.RemotePlugin(this.referenceDummyJarNew),
     142            new PluginServer.RemotePlugin(this.referenceBazJarOld),
     143            new PluginServer.RemotePlugin(null, ImmutableMap.of("Plugin-Version", "2"), "irrelevant_plugin")
     144        );
     145        pluginServer.applyToWireMockServer(this.pluginServerRule);
     146        Config.getPref().putList("plugins", ImmutableList.of("dummy_plugin"));
     147
     148        final HelpAwareOptionPaneMocker haMocker = new HelpAwareOptionPaneMocker(
     149            ImmutableMap.<String, Object>of(
     150                "<html>The following plugin has been downloaded <strong>successfully</strong>:"
     151                + "<ul><li>baz_plugin (6)</li></ul>"
     152                + "You have to restart JOSM for some settings to take effect."
     153                + "<br/><br/>Would you like to restart now?</html>",
     154                "Cancel"
     155            )
     156        );
     157
     158        Files.copy(this.referenceDummyJarOld.toPath(), this.targetDummyJar.toPath());
     159
     160        final PreferenceTabbedPane tabbedPane = new PreferenceTabbedPane();
     161
     162        tabbedPane.buildGui();
     163        // PluginPreference is already added to PreferenceTabbedPane by default
     164        tabbedPane.selectTabByPref(PluginPreference.class);
     165
     166        GuiHelper.runInEDTAndWait(
     167            () -> ((javax.swing.JButton) TestUtils.getComponentByName(tabbedPane, "downloadListButton")).doClick()
     168        );
     169
     170        Awaitility.await().atMost(2000, MILLISECONDS).until(() -> Config.getPref().getInt("pluginmanager.version", 999) != 999);
     171
     172        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
     173        WireMock.resetAllRequests();
     174
     175        final PluginPreferencesModel model = (PluginPreferencesModel) TestUtils.getPrivateField(
     176            tabbedPane.getPluginPreference(),
     177            "model"
     178        );
     179
     180        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
     181        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
     182        // questionably correct
     183        assertTrue(model.getPluginsScheduledForUpdateOrDownload().isEmpty());
     184        assertEquals(model.getDisplayedPlugins(), model.getAvailablePlugins());
     185
     186        assertEquals(
     187            ImmutableList.of("baz_plugin", "dummy_plugin", "irrelevant_plugin"),
     188            model.getAvailablePlugins().stream().map((pi) -> pi.getName()).collect(ImmutableList.toImmutableList())
     189        );
     190        assertEquals(
     191            ImmutableList.of("dummy_plugin"),
     192            model.getSelectedPlugins().stream().map((pi) -> pi.getName()).collect(ImmutableList.toImmutableList())
     193        );
     194        assertEquals(
     195            ImmutableList.of("(null)", "31701", "(null)"),
     196            model.getAvailablePlugins().stream().map(
     197                (pi) -> pi.localversion == null ? "(null)" : pi.localversion
     198            ).collect(ImmutableList.toImmutableList())
     199        );
     200        assertEquals(
     201            ImmutableList.of("6", "31772", "2"),
     202            model.getAvailablePlugins().stream().map((pi) -> pi.version).collect(ImmutableList.toImmutableList())
     203        );
     204
     205        // now we're going to choose to install baz_plugin
     206        model.setPluginSelected("baz_plugin", true);
     207
     208        assertEquals(
     209            ImmutableList.of("baz_plugin"),
     210            model.getNewlyActivatedPlugins().stream().map(
     211                (pi) -> pi.getName()
     212            ).collect(ImmutableList.toImmutableList())
     213        );
     214        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
     215        assertEquals(
     216            ImmutableList.of("baz_plugin"),
     217            model.getPluginsScheduledForUpdateOrDownload().stream().map(
     218                (pi) -> pi.getName()
     219            ).collect(ImmutableList.toImmutableList())
     220        );
     221
     222        tabbedPane.savePreferences();
     223
     224        TestUtils.syncEDTAndWorkerThreads();
     225
     226        assertEquals(1, haMocker.getInvocationLog().size());
     227        Object[] invocationLogEntry = haMocker.getInvocationLog().get(0);
     228        assertEquals(2, (int) invocationLogEntry[0]);
     229        assertEquals("Restart", (String) invocationLogEntry[2]);
     230
     231        // dummy_plugin jar shouldn't have been updated
     232        TestUtils.assertFileContentsEqual(this.referenceDummyJarOld, this.targetDummyJar);
     233        // baz_plugin jar should have been installed
     234        TestUtils.assertFileContentsEqual(this.referenceBazJarOld, this.targetBazJar);
     235
     236        // neither of these .jar.new files should have been left hanging round
     237        assertFalse(targetDummyJarNew.exists());
     238        assertFalse(targetBazJarNew.exists());
     239
     240        // the advertized version of dummy_plugin shouldn't have been fetched
     241        this.pluginServerRule.verify(0, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/dummy_plugin.v31772.jar")));
     242        // but the advertized version of baz_plugin *should* have
     243        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/baz_plugin.v6.jar")));
     244
     245        // pluginmanager.version has been set to the current version
     246        // questionably correct
     247        assertEquals(10000, Config.getPref().getInt("pluginmanager.version", 111));
     248        // however pluginmanager.lastupdate hasn't been updated
     249        // questionably correct
     250        assertEquals("999", Config.getPref().get("pluginmanager.lastupdate", "111"));
     251
     252        // baz_plugin should have been added to the plugins list
     253        assertEquals(
     254            ImmutableList.of("baz_plugin", "dummy_plugin"),
     255            Config.getPref().getList("plugins", null).stream().sorted().collect(ImmutableList.toImmutableList())
     256        );
     257    }
     258
     259    /**
     260     * Tests a plugin being disabled without applying available upgrades
     261     */
     262    @Test
     263    public void testDisablePluginWithUpdatesAvailable() throws Exception {
     264        final PluginServer pluginServer = new PluginServer(
     265            new PluginServer.RemotePlugin(this.referenceDummyJarNew),
     266            new PluginServer.RemotePlugin(this.referenceBazJarNew),
     267            new PluginServer.RemotePlugin(null, null, "irrelevant_plugin")
     268        );
     269        pluginServer.applyToWireMockServer(this.pluginServerRule);
     270        Config.getPref().putList("plugins", ImmutableList.of("baz_plugin", "dummy_plugin"));
     271
     272        final HelpAwareOptionPaneMocker haMocker = new HelpAwareOptionPaneMocker(
     273            ImmutableMap.<String, Object>of(
     274                "<html>You have to restart JOSM for some settings to take effect."
     275                + "<br/><br/>Would you like to restart now?</html>",
     276                "Cancel"
     277            )
     278        );
     279
     280        Files.copy(this.referenceDummyJarOld.toPath(), this.targetDummyJar.toPath());
     281        Files.copy(this.referenceBazJarOld.toPath(), this.targetBazJar.toPath());
     282
     283        final PreferenceTabbedPane tabbedPane = new PreferenceTabbedPane();
     284
     285        tabbedPane.buildGui();
     286        // PluginPreference is already added to PreferenceTabbedPane by default
     287        tabbedPane.selectTabByPref(PluginPreference.class);
     288
     289        GuiHelper.runInEDTAndWait(
     290            () -> ((javax.swing.JButton) TestUtils.getComponentByName(tabbedPane, "downloadListButton")).doClick()
     291        );
     292
     293        Awaitility.await().atMost(2000, MILLISECONDS).until(() -> Config.getPref().getInt("pluginmanager.version", 999) != 999);
     294
     295        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
     296        WireMock.resetAllRequests();
     297
     298        final PluginPreferencesModel model = (PluginPreferencesModel) TestUtils.getPrivateField(
     299            tabbedPane.getPluginPreference(),
     300            "model"
     301        );
     302
     303        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
     304        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
     305        // questionably correct
     306        assertTrue(model.getPluginsScheduledForUpdateOrDownload().isEmpty());
     307        assertEquals(model.getDisplayedPlugins(), model.getAvailablePlugins());
     308
     309        assertEquals(
     310            ImmutableList.of("baz_plugin", "dummy_plugin", "irrelevant_plugin"),
     311            model.getAvailablePlugins().stream().map((pi) -> pi.getName()).collect(ImmutableList.toImmutableList())
     312        );
     313        assertEquals(
     314            ImmutableList.of("baz_plugin", "dummy_plugin"),
     315            model.getSelectedPlugins().stream().map((pi) -> pi.getName()).collect(ImmutableList.toImmutableList())
     316        );
     317        assertEquals(
     318            ImmutableList.of("6", "31701", "(null)"),
     319            model.getAvailablePlugins().stream().map(
     320                (pi) -> pi.localversion == null ? "(null)" : pi.localversion
     321            ).collect(ImmutableList.toImmutableList())
     322        );
     323        assertEquals(
     324            ImmutableList.of("7", "31772", "(null)"),
     325            model.getAvailablePlugins().stream().map(
     326                (pi) -> pi.version == null ? "(null)" : pi.version
     327            ).collect(ImmutableList.toImmutableList())
     328        );
     329
     330        // now we're going to choose to disable baz_plugin
     331        model.setPluginSelected("baz_plugin", false);
     332
     333        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
     334        assertEquals(
     335            ImmutableList.of("baz_plugin"),
     336            model.getNewlyDeactivatedPlugins().stream().map(
     337                (pi) -> pi.getName()
     338            ).collect(ImmutableList.toImmutableList())
     339        );
     340        // questionably correct
     341        assertTrue(model.getPluginsScheduledForUpdateOrDownload().isEmpty());
     342
     343        tabbedPane.savePreferences();
     344
     345        TestUtils.syncEDTAndWorkerThreads();
     346
     347        assertEquals(1, haMocker.getInvocationLog().size());
     348        Object[] invocationLogEntry = haMocker.getInvocationLog().get(0);
     349        assertEquals(2, (int) invocationLogEntry[0]);
     350        assertEquals("Restart", (String) invocationLogEntry[2]);
     351
     352        // dummy_plugin jar shouldn't have been updated
     353        TestUtils.assertFileContentsEqual(this.referenceDummyJarOld, this.targetDummyJar);
     354        // baz_plugin jar shouldn't have been deleted
     355        TestUtils.assertFileContentsEqual(this.referenceBazJarOld, this.targetBazJar);
     356
     357        // neither of these .jar.new files have a reason to be here
     358        assertFalse(targetDummyJarNew.exists());
     359        assertFalse(targetBazJarNew.exists());
     360
     361        // neither of the new jars have been fetched
     362        this.pluginServerRule.verify(0, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/dummy_plugin.v31772.jar")));
     363        this.pluginServerRule.verify(0, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/baz_plugin.v6.jar")));
     364
     365        // pluginmanager.version has been set to the current version
     366        // questionably correct
     367        assertEquals(10000, Config.getPref().getInt("pluginmanager.version", 111));
     368        // however pluginmanager.lastupdate hasn't been updated
     369        // questionably correct
     370        assertEquals("999", Config.getPref().get("pluginmanager.lastupdate", "111"));
     371
     372        // baz_plugin should have been removed from the installed plugins list
     373        assertEquals(
     374            ImmutableList.of("dummy_plugin"),
     375            Config.getPref().getList("plugins", null).stream().sorted().collect(ImmutableList.toImmutableList())
     376        );
     377    }
     378
     379    /**
     380     * Demonstrates behaviour exhibited when attempting to update a single plugin when multiple updates
     381     * are available by deselecting it before clicking the update button then reselecting it.
     382     *
     383     * This is probably NOT desirable and should be fixed, however this test documents the behaviour.
     384     */
     385    @Test
     386    public void testUpdateOnlySelectedPlugin() throws Exception {
     387        final PluginServer pluginServer = new PluginServer(
     388            new PluginServer.RemotePlugin(this.referenceDummyJarNew),
     389            new PluginServer.RemotePlugin(this.referenceBazJarNew)
     390        );
     391        pluginServer.applyToWireMockServer(this.pluginServerRule);
     392        Config.getPref().putList("plugins", ImmutableList.of("baz_plugin", "dummy_plugin"));
     393
     394        final HelpAwareOptionPaneMocker haMocker = new HelpAwareOptionPaneMocker();
     395        final JOptionPaneSimpleMocker jopsMocker = new JOptionPaneSimpleMocker();
     396
     397        Files.copy(this.referenceDummyJarOld.toPath(), this.targetDummyJar.toPath());
     398        Files.copy(this.referenceBazJarOld.toPath(), this.targetBazJar.toPath());
     399
     400        final PreferenceTabbedPane tabbedPane = new PreferenceTabbedPane();
     401
     402        tabbedPane.buildGui();
     403        // PluginPreference is already added to PreferenceTabbedPane by default
     404        tabbedPane.selectTabByPref(PluginPreference.class);
     405
     406        GuiHelper.runInEDTAndWait(
     407            () -> ((javax.swing.JButton) TestUtils.getComponentByName(tabbedPane, "downloadListButton")).doClick()
     408        );
     409
     410        TestUtils.syncEDTAndWorkerThreads();
     411
     412        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
     413        WireMock.resetAllRequests();
     414
     415        final PluginPreferencesModel model = (PluginPreferencesModel) TestUtils.getPrivateField(
     416            tabbedPane.getPluginPreference(),
     417            "model"
     418        );
     419
     420        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
     421        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
     422        // questionably correct
     423        assertTrue(model.getPluginsScheduledForUpdateOrDownload().isEmpty());
     424        assertEquals(model.getDisplayedPlugins(), model.getAvailablePlugins());
     425
     426        assertEquals(
     427            ImmutableList.of("baz_plugin", "dummy_plugin"),
     428            model.getAvailablePlugins().stream().map((pi) -> pi.getName()).collect(ImmutableList.toImmutableList())
     429        );
     430        assertEquals(
     431            ImmutableList.of("baz_plugin", "dummy_plugin"),
     432            model.getSelectedPlugins().stream().map((pi) -> pi.getName()).collect(ImmutableList.toImmutableList())
     433        );
     434        assertEquals(
     435            ImmutableList.of("6", "31701"),
     436            model.getAvailablePlugins().stream().map(
     437                (pi) -> pi.localversion == null ? "(null)" : pi.localversion
     438            ).collect(ImmutableList.toImmutableList())
     439        );
     440        assertEquals(
     441            ImmutableList.of("7", "31772"),
     442            model.getAvailablePlugins().stream().map((pi) -> pi.version).collect(ImmutableList.toImmutableList())
     443        );
     444
     445        // now we're going to choose not to update baz_plugin
     446        model.setPluginSelected("baz_plugin", false);
     447
     448        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
     449        assertEquals(
     450            ImmutableList.of("baz_plugin"),
     451            model.getNewlyDeactivatedPlugins().stream().map(
     452                pi -> pi.getName()
     453            ).collect(ImmutableList.toImmutableList())
     454        );
     455        // questionably correct
     456        assertTrue(model.getPluginsScheduledForUpdateOrDownload().isEmpty());
     457
     458        // prepare haMocker to handle this message
     459        haMocker.getMockResultMap().put(
     460            "<html>The following plugin has been downloaded <strong>successfully</strong>:"
     461            + "<ul><li>dummy_plugin (31772)</li></ul>Please restart JOSM to activate the "
     462            + "downloaded plugins.</html>",
     463            "OK"
     464        );
     465
     466        GuiHelper.runInEDTAndWait(
     467            () -> ((javax.swing.JButton) TestUtils.getComponentByName(tabbedPane, "updatePluginsButton")).doClick()
     468        );
     469
     470        TestUtils.syncEDTAndWorkerThreads();
     471
     472        assertTrue(jopsMocker.getInvocationLog().isEmpty());
     473        assertEquals(1, haMocker.getInvocationLog().size());
     474        Object[] invocationLogEntry = haMocker.getInvocationLog().get(0);
     475        assertEquals(0, (int) invocationLogEntry[0]);
     476        assertEquals("Update plugins", (String) invocationLogEntry[2]);
     477
     478        // dummy_plugin jar should have been updated
     479        TestUtils.assertFileContentsEqual(this.referenceDummyJarNew, this.targetDummyJar);
     480        // but baz_plugin jar shouldn't have been
     481        TestUtils.assertFileContentsEqual(this.referenceBazJarOld, this.targetBazJar);
     482
     483        // any .jar.new files should have been removed
     484        assertFalse(targetDummyJarNew.exists());
     485        assertFalse(targetBazJarNew.exists());
     486
     487        // the plugin list was rechecked
     488        // questionably necessary
     489        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
     490        // dummy_plugin has been fetched
     491        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/dummy_plugin.v31772.jar")));
     492        // baz_plugin has not
     493        this.pluginServerRule.verify(0, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/baz_plugin.v7.jar")));
     494        WireMock.resetAllRequests();
     495
     496        // pluginmanager.version has been set to the current version
     497        // questionably correct
     498        assertEquals(10000, Config.getPref().getInt("pluginmanager.version", 111));
     499        // however pluginmanager.lastupdate hasn't been updated
     500        // questionably correct
     501        assertEquals("999", Config.getPref().get("pluginmanager.lastupdate", "111"));
     502
     503        // plugins list shouldn't have been altered, we haven't hit save yet
     504        assertEquals(
     505            ImmutableList.of("baz_plugin", "dummy_plugin"),
     506            Config.getPref().getList("plugins", null).stream().sorted().collect(ImmutableList.toImmutableList())
     507        );
     508
     509        // the model's selection state should be largely as before
     510        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
     511        assertEquals(
     512            ImmutableList.of("baz_plugin"),
     513            model.getNewlyDeactivatedPlugins().stream().map(
     514                (pi) -> pi.getName()
     515            ).collect(ImmutableList.toImmutableList())
     516        );
     517        assertTrue(model.getPluginsScheduledForUpdateOrDownload().isEmpty());
     518
     519        // but now we re-select baz_plugin so that it isn't removed/disabled
     520        model.setPluginSelected("baz_plugin", true);
     521
     522        // this has caused baz_plugin to be interpreted as a plugin "for download"
     523        // questionably correct
     524        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
     525        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
     526        assertEquals(
     527            ImmutableList.of("baz_plugin"),
     528            model.getPluginsScheduledForUpdateOrDownload().stream().map(
     529                (pi) -> pi.getName()
     530            ).collect(ImmutableList.toImmutableList())
     531        );
     532
     533        // prepare jopsMocker to handle this message
     534        jopsMocker.getMockResultMap().put(
     535            "<html>The following plugin has been downloaded <strong>successfully</strong>:"
     536            + "<ul><li>baz_plugin (7)</li></ul></html>",
     537            JOptionPane.OK_OPTION
     538        );
     539
     540        tabbedPane.savePreferences();
     541
     542        TestUtils.syncEDTAndWorkerThreads();
     543
     544        // from previous haMocker invocation
     545        assertEquals(1, haMocker.getInvocationLog().size());
     546        // we've been alerted that (the new version of) baz_plugin was installed
     547        // questionably correct
     548        assertEquals(1, jopsMocker.getInvocationLog().size());
     549        invocationLogEntry = jopsMocker.getInvocationLog().get(0);
     550        assertEquals(JOptionPane.OK_OPTION, (int) invocationLogEntry[0]);
     551        assertEquals("Warning", (String) invocationLogEntry[2]);
     552
     553        // dummy_plugin jar is still the updated version
     554        TestUtils.assertFileContentsEqual(this.referenceDummyJarNew, this.targetDummyJar);
     555        // but now the baz_plugin jar has been too
     556        // questionably correct
     557        TestUtils.assertFileContentsEqual(this.referenceBazJarNew, this.targetBazJar);
     558
     559        // all .jar.new files have been deleted
     560        assertFalse(targetDummyJarNew.exists());
     561        assertFalse(targetBazJarNew.exists());
     562
     563        // dummy_plugin was not fetched
     564        this.pluginServerRule.verify(0, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/dummy_plugin.v31772.jar")));
     565        // baz_plugin however was fetched
     566        // questionably correct
     567        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/baz_plugin.v7.jar")));
     568
     569        assertEquals(10000, Config.getPref().getInt("pluginmanager.version", 111));
     570        // questionably correct
     571        assertEquals("999", Config.getPref().get("pluginmanager.lastupdate", "111"));
     572    }
     573
     574    /**
     575     * Tests the effect of requesting a "plugin update" when everything is up to date
     576     */
     577    @Test
     578    public void testUpdateWithNoAvailableUpdates() throws Exception {
     579        final PluginServer pluginServer = new PluginServer(
     580            new PluginServer.RemotePlugin(this.referenceDummyJarOld),
     581            new PluginServer.RemotePlugin(this.referenceBazJarOld),
     582            new PluginServer.RemotePlugin(null, ImmutableMap.of("Plugin-Version", "123"), "irrelevant_plugin")
     583        );
     584        pluginServer.applyToWireMockServer(this.pluginServerRule);
     585        Config.getPref().putList("plugins", ImmutableList.of("baz_plugin", "dummy_plugin"));
     586
     587        final HelpAwareOptionPaneMocker haMocker = new HelpAwareOptionPaneMocker(
     588            ImmutableMap.<String, Object>of(
     589                "All installed plugins are up to date. JOSM does not have to download newer versions.",
     590                "OK"
     591            )
     592        );
     593        final JOptionPaneSimpleMocker jopsMocker = new JOptionPaneSimpleMocker();
     594
     595        Files.copy(this.referenceDummyJarOld.toPath(), this.targetDummyJar.toPath());
     596        Files.copy(this.referenceBazJarOld.toPath(), this.targetBazJar.toPath());
     597
     598        final PreferenceTabbedPane tabbedPane = new PreferenceTabbedPane();
     599
     600        tabbedPane.buildGui();
     601        // PluginPreference is already added to PreferenceTabbedPane by default
     602        tabbedPane.selectTabByPref(PluginPreference.class);
     603
     604        GuiHelper.runInEDTAndWait(
     605            () -> ((javax.swing.JButton) TestUtils.getComponentByName(tabbedPane, "downloadListButton")).doClick()
     606        );
     607
     608        TestUtils.syncEDTAndWorkerThreads();
     609
     610        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
     611        WireMock.resetAllRequests();
     612
     613        final PluginPreferencesModel model = (PluginPreferencesModel) TestUtils.getPrivateField(
     614            tabbedPane.getPluginPreference(),
     615            "model"
     616        );
     617
     618        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
     619        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
     620        assertTrue(model.getPluginsScheduledForUpdateOrDownload().isEmpty());
     621        assertEquals(model.getDisplayedPlugins(), model.getAvailablePlugins());
     622
     623        assertEquals(
     624            ImmutableList.of("baz_plugin", "dummy_plugin", "irrelevant_plugin"),
     625            model.getAvailablePlugins().stream().map((pi) -> pi.getName()).collect(ImmutableList.toImmutableList())
     626        );
     627        assertEquals(
     628            ImmutableList.of("baz_plugin", "dummy_plugin"),
     629            model.getSelectedPlugins().stream().map((pi) -> pi.getName()).collect(ImmutableList.toImmutableList())
     630        );
     631        assertEquals(
     632            ImmutableList.of("6", "31701", "(null)"),
     633            model.getAvailablePlugins().stream().map(
     634                (pi) -> pi.localversion == null ? "(null)" : pi.localversion
     635            ).collect(ImmutableList.toImmutableList())
     636        );
     637        assertEquals(
     638            ImmutableList.of("6", "31701", "123"),
     639            model.getAvailablePlugins().stream().map((pi) -> pi.version).collect(ImmutableList.toImmutableList())
     640        );
     641
     642        GuiHelper.runInEDTAndWait(
     643            () -> ((javax.swing.JButton) TestUtils.getComponentByName(tabbedPane, "updatePluginsButton")).doClick()
     644        );
     645
     646        TestUtils.syncEDTAndWorkerThreads();
     647
     648        assertTrue(jopsMocker.getInvocationLog().isEmpty());
     649        assertEquals(1, haMocker.getInvocationLog().size());
     650        Object[] invocationLogEntry = haMocker.getInvocationLog().get(0);
     651        assertEquals(0, (int) invocationLogEntry[0]);
     652        assertEquals("Plugins up to date", (String) invocationLogEntry[2]);
     653
     654        // neither jar should have changed
     655        TestUtils.assertFileContentsEqual(this.referenceDummyJarOld, this.targetDummyJar);
     656        TestUtils.assertFileContentsEqual(this.referenceBazJarOld, this.targetBazJar);
     657
     658        // no reason for any .jar.new files
     659        assertFalse(targetDummyJarNew.exists());
     660        assertFalse(targetBazJarNew.exists());
     661
     662        // the plugin list was rechecked
     663        // questionably necessary
     664        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
     665        // that should have been the only request to our PluginServer
     666        assertEquals(1, this.pluginServerRule.getAllServeEvents().size());
     667        WireMock.resetAllRequests();
     668
     669        // pluginmanager.version has been set to the current version
     670        assertEquals(10000, Config.getPref().getInt("pluginmanager.version", 111));
     671        // pluginmanager.lastupdate hasn't been updated
     672        // questionably correct
     673        assertEquals("999", Config.getPref().get("pluginmanager.lastupdate", "111"));
     674
     675        // plugins list shouldn't have been altered
     676        assertEquals(
     677            ImmutableList.of("baz_plugin", "dummy_plugin"),
     678            Config.getPref().getList("plugins", null).stream().sorted().collect(ImmutableList.toImmutableList())
     679        );
     680
     681        // the model's selection state should be largely as before
     682        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
     683        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
     684        assertTrue(model.getPluginsScheduledForUpdateOrDownload().isEmpty());
     685
     686        tabbedPane.savePreferences();
     687
     688        TestUtils.syncEDTAndWorkerThreads();
     689
     690        assertTrue(jopsMocker.getInvocationLog().isEmpty());
     691        assertEquals(1, haMocker.getInvocationLog().size());
     692
     693        // both jars are still the original version
     694        TestUtils.assertFileContentsEqual(this.referenceDummyJarOld, this.targetDummyJar);
     695        TestUtils.assertFileContentsEqual(this.referenceBazJarOld, this.targetBazJar);
     696
     697        // no reason for any .jar.new files
     698        assertFalse(targetDummyJarNew.exists());
     699        assertFalse(targetBazJarNew.exists());
     700
     701        // none of PluginServer's URLs should have been touched
     702        assertEquals(0, this.pluginServerRule.getAllServeEvents().size());
     703
     704        // pluginmanager.version has been set to the current version
     705        assertEquals(10000, Config.getPref().getInt("pluginmanager.version", 111));
     706        // pluginmanager.lastupdate hasn't been updated
     707        // questionably correct
     708        assertEquals("999", Config.getPref().get("pluginmanager.lastupdate", "111"));
     709    }
     710
     711    /**
     712     * Tests installing a single plugin which is marked as "Canloadatruntime"
     713     */
     714    @Test
     715    public void testInstallWithoutRestartRequired() throws Exception {
     716        final boolean[] loadPluginsCalled = new boolean[] {false};
     717        final mockit.MockUp<PluginHandler> pluginHandlerMocker = new mockit.MockUp<PluginHandler>() {
     718            @mockit.Mock
     719            private void loadPlugins(
     720                final Component parent,
     721                final Collection<org.openstreetmap.josm.plugins.PluginInformation> plugins,
     722                final org.openstreetmap.josm.gui.progress.ProgressMonitor monitor
     723            ) {
     724                assertEquals(1, plugins.size());
     725                assertEquals("dummy_plugin", plugins.iterator().next().name);
     726                assertEquals("31772", plugins.iterator().next().localversion);
     727                loadPluginsCalled[0] = true;
     728            }
     729        };
     730
     731        final PluginServer pluginServer = new PluginServer(
     732            new PluginServer.RemotePlugin(this.referenceDummyJarNew),
     733            new PluginServer.RemotePlugin(this.referenceBazJarNew)
     734        );
     735        pluginServer.applyToWireMockServer(this.pluginServerRule);
     736        Config.getPref().putList("plugins", ImmutableList.of());
     737
     738        final HelpAwareOptionPaneMocker haMocker = new HelpAwareOptionPaneMocker();
     739        final JOptionPaneSimpleMocker jopsMocker = new JOptionPaneSimpleMocker(ImmutableMap.<String, Object>of(
     740            "<html>The following plugin has been downloaded <strong>successfully</strong>:"
     741            + "<ul><li>dummy_plugin (31772)</li></ul></html>",
     742            JOptionPane.OK_OPTION
     743        ));
     744
     745        final PreferenceTabbedPane tabbedPane = new PreferenceTabbedPane();
     746
     747        tabbedPane.buildGui();
     748        // PluginPreference is already added to PreferenceTabbedPane by default
     749        tabbedPane.selectTabByPref(PluginPreference.class);
     750
     751        GuiHelper.runInEDTAndWait(
     752            () -> ((javax.swing.JButton) TestUtils.getComponentByName(tabbedPane, "downloadListButton")).doClick()
     753        );
     754
     755        TestUtils.syncEDTAndWorkerThreads();
     756
     757        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugins")));
     758        WireMock.resetAllRequests();
     759
     760        final PluginPreferencesModel model = (PluginPreferencesModel) TestUtils.getPrivateField(
     761            tabbedPane.getPluginPreference(),
     762            "model"
     763        );
     764
     765        assertTrue(model.getNewlyActivatedPlugins().isEmpty());
     766        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
     767        assertTrue(model.getPluginsScheduledForUpdateOrDownload().isEmpty());
     768        assertEquals(model.getDisplayedPlugins(), model.getAvailablePlugins());
     769
     770        assertEquals(
     771            ImmutableList.of("baz_plugin", "dummy_plugin"),
     772            model.getAvailablePlugins().stream().map((pi) -> pi.getName()).collect(ImmutableList.toImmutableList())
     773        );
     774        assertTrue(model.getSelectedPlugins().isEmpty());
     775        assertEquals(
     776            ImmutableList.of("(null)", "(null)"),
     777            model.getAvailablePlugins().stream().map(
     778                (pi) -> pi.localversion == null ? "(null)" : pi.localversion
     779            ).collect(ImmutableList.toImmutableList())
     780        );
     781        assertEquals(
     782            ImmutableList.of("7", "31772"),
     783            model.getAvailablePlugins().stream().map((pi) -> pi.version).collect(ImmutableList.toImmutableList())
     784        );
     785
     786        // now we select dummy_plugin
     787        model.setPluginSelected("dummy_plugin", true);
     788
     789        // model should now reflect this
     790        assertEquals(
     791            ImmutableList.of("dummy_plugin"),
     792            model.getNewlyActivatedPlugins().stream().map(
     793                pi -> pi.getName()
     794            ).collect(ImmutableList.toImmutableList())
     795        );
     796        assertTrue(model.getNewlyDeactivatedPlugins().isEmpty());
     797
     798        tabbedPane.savePreferences();
     799
     800        TestUtils.syncEDTAndWorkerThreads();
     801
     802        assertEquals(1, jopsMocker.getInvocationLog().size());
     803        org.openstreetmap.josm.tools.Logging.error(jopsMocker.getInvocationLog().get(0)[0].toString());
     804        Object[] invocationLogEntry = jopsMocker.getInvocationLog().get(0);
     805        assertEquals(JOptionPane.OK_OPTION, (int) invocationLogEntry[0]);
     806        assertEquals("Warning", (String) invocationLogEntry[2]);
     807
     808        assertTrue(haMocker.getInvocationLog().isEmpty());
     809
     810        // any .jar.new files should have been deleted
     811        assertFalse(targetDummyJarNew.exists());
     812        assertFalse(targetBazJarNew.exists());
     813
     814        // dummy_plugin was fetched
     815        this.pluginServerRule.verify(1, WireMock.getRequestedFor(WireMock.urlEqualTo("/plugin/dummy_plugin.v31772.jar")));
     816
     817        // loadPlugins(...) was called (with expected parameters)
     818        assertTrue(loadPluginsCalled[0]);
     819
     820        // pluginmanager.version has been set to the current version
     821        assertEquals(10000, Config.getPref().getInt("pluginmanager.version", 111));
     822        // pluginmanager.lastupdate hasn't been updated
     823        // questionably correct
     824        assertEquals("999", Config.getPref().get("pluginmanager.lastupdate", "111"));
     825    }
     826}