Index: trunk/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java
===================================================================
--- trunk/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java	(revision 18892)
+++ trunk/test/functional/org/openstreetmap/josm/io/OsmServerBackreferenceReaderTest.java	(revision 18893)
@@ -12,9 +12,9 @@
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
-import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.OutputStreamWriter;
 import java.io.PrintWriter;
 import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
 import java.text.MessageFormat;
 import java.util.HashSet;
@@ -26,5 +26,4 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.openstreetmap.josm.JOSMFixture;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.APIDataSet;
@@ -43,4 +42,6 @@
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.testutils.annotations.TestUser;
+import org.openstreetmap.josm.tools.JosmRuntimeException;
 import org.openstreetmap.josm.tools.Logging;
 
@@ -52,4 +53,6 @@
  */
 @SuppressFBWarnings(value = "CRLF_INJECTION_LOGS")
+@org.openstreetmap.josm.testutils.annotations.OsmApi(org.openstreetmap.josm.testutils.annotations.OsmApi.APIType.DEV)
+@TestUser
 class OsmServerBackreferenceReaderTest {
     private static final Logger logger = Logger.getLogger(OsmServerBackreferenceReader.class.getName());
@@ -76,5 +79,5 @@
         }
         fail("Cannot find relation "+i);
-        return null;
+        throw new JosmRuntimeException("Cannot reach this point due to fail() above");
     }
 
@@ -166,6 +169,4 @@
         logger.info("initializing ...");
 
-        JOSMFixture.createFunctionalTestFixture().init();
-
         Config.getPref().put("osm-server.auth-method", "basic");
 
@@ -197,5 +198,5 @@
         try (
             PrintWriter pw = new PrintWriter(
-                    new OutputStreamWriter(new FileOutputStream(dataSetCacheOutputFile), StandardCharsets.UTF_8)
+                    new OutputStreamWriter(Files.newOutputStream(dataSetCacheOutputFile.toPath()), StandardCharsets.UTF_8)
         )) {
             logger.info(MessageFormat.format("caching test data set in ''{0}'' ...", dataSetCacheOutputFile.toString()));
Index: trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterPerformanceTest.java
===================================================================
--- trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterPerformanceTest.java	(revision 18893)
+++ trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterPerformanceTest.java	(revision 18893)
@@ -0,0 +1,140 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.gui.mappaint.mapcss;
+
+import java.util.concurrent.TimeUnit;
+
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.openstreetmap.josm.PerformanceTestUtils;
+import org.openstreetmap.josm.PerformanceTestUtils.PerformanceTestTimer;
+import org.openstreetmap.josm.data.osm.OsmDataGenerator;
+import org.openstreetmap.josm.data.osm.OsmDataGenerator.KeyValueDataGenerator;
+import org.openstreetmap.josm.gui.mappaint.MultiCascade;
+import org.openstreetmap.josm.testutils.annotations.Projection;
+
+/**
+ * Tests how fast {@link MapCSSStyleSource} finds the right style candidates for one object.
+ * @author Michael Zangl
+ */
+@Projection
+@Timeout(value = 15, unit = TimeUnit.MINUTES)
+class MapCSSStyleSourceFilterPerformanceTest {
+
+    private static final int TEST_RULE_COUNT = 10000;
+
+    private static class CssGenerator {
+        StringBuilder sb = new StringBuilder();
+        private final KeyValueDataGenerator generator;
+
+        /**
+         * Create a new CSS generator.
+         * @param generator A generator to get the keys from.
+         */
+        CssGenerator(KeyValueDataGenerator generator) {
+            this.generator = generator;
+        }
+
+        private CssGenerator addKeyValueRules(int count) {
+            for (int i = 0; i < count; i++) {
+                String key = generator.randomKey();
+                String value = generator.randomValue();
+                addRule("node[\"" + key + "\"=\"" + value + "\"]");
+            }
+            return this;
+        }
+
+        private CssGenerator addKeyRegexpRules(int count) {
+            for (int i = 0; i < count; i++) {
+                String key = generator.randomKey();
+                String value = generator.randomValue();
+                value = value.substring(i % value.length());
+                addRule("node[\"" + key + "\"=~/.*" + value + ".*/]");
+            }
+            return this;
+        }
+
+        public CssGenerator addHasKeyRules(int count) {
+            for (int i = 0; i < count; i++) {
+                String key = generator.randomKey();
+                addRule("node[\"" + key + "\"]");
+            }
+            return this;
+        }
+
+        public CssGenerator addIsTrueRules(int count) {
+            for (int i = 0; i < count; i++) {
+                String key = generator.randomKey();
+                addRule("node[\"" + key + "\"?]");
+            }
+            return this;
+        }
+
+        private void addRule(String selector) {
+            sb.append(selector).append(" {}\n");
+        }
+
+        public String getCss() {
+            return sb.toString();
+        }
+    }
+
+    private static final int APPLY_CALLS = 100000;
+
+    /**
+     * Time how long it takes to evaluate [key=value] rules
+     */
+    @Test
+    void testKeyValueRules() {
+        KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
+        data.generateDataSet();
+        CssGenerator css = new CssGenerator(data).addKeyValueRules(TEST_RULE_COUNT);
+        runTest(data, css, "only key=value rules");
+    }
+
+    /**
+     * Time how long it takes to evaluate [key] rules
+     */
+    @Test
+    void testKeyOnlyRules() {
+        KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
+        data.generateDataSet();
+        CssGenerator css = new CssGenerator(data).addHasKeyRules(TEST_RULE_COUNT);
+        runTest(data, css, "only has key rules");
+    }
+
+    /**
+     * Time how long it takes to evaluate [key=~...] rules
+     */
+    @Test
+    void testRegularExpressionRules() {
+        KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
+        data.generateDataSet();
+        CssGenerator css = new CssGenerator(data).addKeyRegexpRules(TEST_RULE_COUNT);
+        runTest(data, css, "regular expressions");
+    }
+
+    /**
+     * Time how long it takes to evaluate [key?] rules
+     */
+    @Test
+    void testIsTrueRules() {
+        KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
+        data.generateDataSet();
+        CssGenerator css = new CssGenerator(data).addIsTrueRules(TEST_RULE_COUNT);
+        runTest(data, css, "is true");
+    }
+
+    private void runTest(KeyValueDataGenerator data, CssGenerator css, String description) {
+        MapCSSStyleSource source = new MapCSSStyleSource(css.getCss());
+        PerformanceTestTimer timer = PerformanceTestUtils.startTimer("MapCSSStyleSource#loadStyleSource(...) for " + description);
+        source.loadStyleSource();
+        timer.done();
+
+        timer = PerformanceTestUtils.startTimer(APPLY_CALLS + "x MapCSSStyleSource#apply(...) for " + description);
+        for (int i = 0; i < APPLY_CALLS; i++) {
+            MultiCascade mc = new MultiCascade();
+            source.apply(mc, data.randomNode(), 1, false);
+        }
+        timer.done();
+    }
+}
Index: trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterTest.java
===================================================================
--- trunk/test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterTest.java	(revision 18892)
+++ 	(revision )
@@ -1,140 +1,0 @@
-// License: GPL. For details, see LICENSE file.
-package org.openstreetmap.josm.gui.mappaint.mapcss;
-
-import java.util.concurrent.TimeUnit;
-
-import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.Timeout;
-import org.openstreetmap.josm.PerformanceTestUtils;
-import org.openstreetmap.josm.PerformanceTestUtils.PerformanceTestTimer;
-import org.openstreetmap.josm.data.osm.OsmDataGenerator;
-import org.openstreetmap.josm.data.osm.OsmDataGenerator.KeyValueDataGenerator;
-import org.openstreetmap.josm.gui.mappaint.MultiCascade;
-import org.openstreetmap.josm.testutils.annotations.Projection;
-
-/**
- * Tests how fast {@link MapCSSStyleSource} finds the right style candidates for one object.
- * @author Michael Zangl
- */
-@Projection
-@Timeout(value = 15, unit = TimeUnit.MINUTES)
-class MapCSSStyleSourceFilterTest {
-
-    private static final int TEST_RULE_COUNT = 10000;
-
-    private static class CssGenerator {
-        StringBuilder sb = new StringBuilder();
-        private final KeyValueDataGenerator generator;
-
-        /**
-         * Create a new CSS generator.
-         * @param generator A generator to get the keys from.
-         */
-        CssGenerator(KeyValueDataGenerator generator) {
-            this.generator = generator;
-        }
-
-        private CssGenerator addKeyValueRules(int count) {
-            for (int i = 0; i < count; i++) {
-                String key = generator.randomKey();
-                String value = generator.randomValue();
-                addRule("node[\"" + key + "\"=\"" + value + "\"]");
-            }
-            return this;
-        }
-
-        private CssGenerator addKeyRegexpRules(int count) {
-            for (int i = 0; i < count; i++) {
-                String key = generator.randomKey();
-                String value = generator.randomValue();
-                value = value.substring(i % value.length());
-                addRule("node[\"" + key + "\"=~/.*" + value + ".*/]");
-            }
-            return this;
-        }
-
-        public CssGenerator addHasKeyRules(int count) {
-            for (int i = 0; i < count; i++) {
-                String key = generator.randomKey();
-                addRule("node[\"" + key + "\"]");
-            }
-            return this;
-        }
-
-        public CssGenerator addIsTrueRules(int count) {
-            for (int i = 0; i < count; i++) {
-                String key = generator.randomKey();
-                addRule("node[\"" + key + "\"?]");
-            }
-            return this;
-        }
-
-        private void addRule(String selector) {
-            sb.append(selector).append(" {}\n");
-        }
-
-        public String getCss() {
-            return sb.toString();
-        }
-    }
-
-    private static final int APPLY_CALLS = 100000;
-
-    /**
-     * Time how long it takes to evaluate [key=value] rules
-     */
-    @Test
-    void testKeyValueRules() {
-        KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
-        data.generateDataSet();
-        CssGenerator css = new CssGenerator(data).addKeyValueRules(TEST_RULE_COUNT);
-        runTest(data, css, "only key=value rules");
-    }
-
-    /**
-     * Time how long it takes to evaluate [key] rules
-     */
-    @Test
-    void testKeyOnlyRules() {
-        KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
-        data.generateDataSet();
-        CssGenerator css = new CssGenerator(data).addHasKeyRules(TEST_RULE_COUNT);
-        runTest(data, css, "only has key rules");
-    }
-
-    /**
-     * Time how long it takes to evaluate [key=~...] rules
-     */
-    @Test
-    void testRegularExpressionRules() {
-        KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
-        data.generateDataSet();
-        CssGenerator css = new CssGenerator(data).addKeyRegexpRules(TEST_RULE_COUNT);
-        runTest(data, css, "regular expressions");
-    }
-
-    /**
-     * Time how long it takes to evaluate [key?] rules
-     */
-    @Test
-    void testIsTrueRules() {
-        KeyValueDataGenerator data = OsmDataGenerator.getKeyValue();
-        data.generateDataSet();
-        CssGenerator css = new CssGenerator(data).addIsTrueRules(TEST_RULE_COUNT);
-        runTest(data, css, "is true");
-    }
-
-    private void runTest(KeyValueDataGenerator data, CssGenerator css, String description) {
-        MapCSSStyleSource source = new MapCSSStyleSource(css.getCss());
-        PerformanceTestTimer timer = PerformanceTestUtils.startTimer("MapCSSStyleSource#loadStyleSource(...) for " + description);
-        source.loadStyleSource();
-        timer.done();
-
-        timer = PerformanceTestUtils.startTimer(APPLY_CALLS + "x MapCSSStyleSource#apply(...) for " + description);
-        for (int i = 0; i < APPLY_CALLS; i++) {
-            MultiCascade mc = new MultiCascade();
-            source.apply(mc, data.randomNode(), 1, false);
-        }
-        timer.done();
-    }
-}
Index: trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTaskTestParent.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTaskTestParent.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTaskTestParent.java	(revision 18893)
@@ -6,24 +6,15 @@
 import static com.github.tomakehurst.wiremock.client.WireMock.urlEqualTo;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
 import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 
 import com.github.tomakehurst.wiremock.WireMockServer;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Superclass of {@link DownloadGpsTaskTest}, {@link DownloadOsmTaskTest} and {@link DownloadNotesTaskTest}.
  */
+@BasicWiremock
+@HTTPS
 public abstract class AbstractDownloadTaskTestParent {
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    JOSMTestRules test = new JOSMTestRules().https();
-
     /**
      * HTTP mock.
Index: trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTaskTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTaskTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadGpsTaskTest.java	(revision 18893)
@@ -10,10 +10,8 @@
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.gpx.GpxData;
-import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
 /**
  * Unit tests for class {@link DownloadGpsTask}.
  */
-@BasicWiremock
 class DownloadGpsTaskTest extends AbstractDownloadTaskTestParent {
 
Index: trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTaskTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTaskTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadNotesTaskTest.java	(revision 18893)
@@ -10,10 +10,8 @@
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.osm.NoteData;
-import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
 /**
  * Unit tests for class {@link DownloadNotesTask}.
  */
-@BasicWiremock
 class DownloadNotesTaskTest extends AbstractDownloadTaskTestParent {
 
Index: trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/DownloadOsmTaskTest.java	(revision 18893)
@@ -10,10 +10,8 @@
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.osm.DataSet;
-import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
 
 /**
  * Unit tests for class {@link DownloadOsmTask}.
  */
-@BasicWiremock
 class DownloadOsmTaskTest extends AbstractDownloadTaskTestParent {
 
Index: trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/PluginDownloadTaskTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/PluginDownloadTaskTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/actions/downloadtasks/PluginDownloadTaskTest.java	(revision 18893)
@@ -13,5 +13,4 @@
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.Preferences;
@@ -19,27 +18,13 @@
 import org.openstreetmap.josm.plugins.PluginDownloadTask;
 import org.openstreetmap.josm.plugins.PluginInformation;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
-import org.openstreetmap.josm.testutils.annotations.BasicWiremock;
+import org.openstreetmap.josm.testutils.annotations.AssumeRevision;
 import org.openstreetmap.josm.tools.Utils;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Unit tests for class {@link PluginDownloadTask}.
  */
-@BasicWiremock
-@BasicPreferences
+@AssumeRevision("Revision: 8000\n")
 class PluginDownloadTaskTest extends AbstractDownloadTaskTestParent {
     protected String pluginPath;
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules testRule = new JOSMTestRules().https().assumeRevision(
-        "Revision: 8000\n"
-    );
 
     @Override
Index: trunk/test/unit/org/openstreetmap/josm/data/oauth/SignpostAdaptersTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/oauth/SignpostAdaptersTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/data/oauth/SignpostAdaptersTest.java	(revision 18893)
@@ -12,12 +12,10 @@
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.oauth.SignpostAdapters.HttpRequest;
 import org.openstreetmap.josm.data.oauth.SignpostAdapters.HttpResponse;
 import org.openstreetmap.josm.data.oauth.SignpostAdapters.OAuthConsumer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 import org.openstreetmap.josm.tools.HttpClient;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import net.trajano.commons.testing.UtilityClassTestUtil;
 
@@ -25,12 +23,6 @@
  * Unit tests for class {@link SignpostAdapters}.
  */
+@HTTPS
 class SignpostAdaptersTest {
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().https();
 
     private static HttpClient newClient() throws MalformedURLException {
Index: trunk/test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorTestIT.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorTestIT.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorTestIT.java	(revision 18893)
@@ -46,7 +46,6 @@
 import java.util.regex.Pattern;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 import org.openstreetmap.josm.tools.Logging;
 
@@ -58,13 +57,6 @@
  * @version $Revision: 1723861 $
  */
+@HTTPS
 class DomainValidatorTestIT {
-
-    /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().https();
-
     /**
      * Download and process local copy of http://data.iana.org/TLD/tlds-alpha-by-domain.txt
Index: trunk/test/unit/org/openstreetmap/josm/gui/MainApplicationTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/MainApplicationTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/MainApplicationTest.java	(revision 18893)
@@ -35,10 +35,8 @@
 import javax.swing.plaf.metal.MetalLookAndFeel;
 
-import mockit.Mock;
-import mockit.MockUp;
 import org.awaitility.Awaitility;
 import org.awaitility.Durations;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.actions.JosmAction;
@@ -57,24 +55,26 @@
 import org.openstreetmap.josm.plugins.PluginListParser;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.OsmApi;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.PlatformManager;
 import org.openstreetmap.josm.tools.Shortcut;
+import org.openstreetmap.josm.tools.bugreport.BugReportQueue;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import org.openstreetmap.josm.tools.bugreport.BugReportQueue;
+import mockit.Mock;
+import mockit.MockUp;
 
 /**
  * Unit tests of {@link MainApplication} class.
  */
+@HTTPS
+@Main
+@OsmApi(OsmApi.APIType.DEV)
+@Projection
+@Timeout(20)
 public class MainApplicationTest {
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().main().projection().https().devAPI().timeout(20000);
-
     /**
      * Make sure {@link MainApplication#contentPanePrivate} is initialized.
@@ -249,5 +249,5 @@
     void testPostConstructorProcessCmdLineEmpty() {
         // Check the method accepts no arguments
-        MainApplication.postConstructorProcessCmdLine(new ProgramArguments());
+        assertDoesNotThrow(() -> MainApplication.postConstructorProcessCmdLine(new ProgramArguments()));
     }
 
Index: trunk/test/unit/org/openstreetmap/josm/gui/dialogs/MinimapDialogTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/dialogs/MinimapDialogTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/dialogs/MinimapDialogTest.java	(revision 18893)
@@ -28,13 +28,9 @@
 import javax.swing.JPopupMenu;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.awaitility.Awaitility;
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.DataSource;
-import org.openstreetmap.josm.data.SystemOfMeasurement;
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
@@ -52,29 +48,23 @@
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.testutils.ImagePatternMatching;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FakeImagery;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.MeasurementSystem;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
 /**
  * Unit tests of {@link MinimapDialog} class.
  */
-public class MinimapDialogTest {
-
-    /**
-     * Setup tests
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules josmTestRules = new JOSMTestRules().main().projection().fakeImagery();
-
-    @Before
-    public void beforeAll() {
-        // Needed since testShowDownloadedAreaLayerSwitching expects the measurement to be imperial
-        SystemOfMeasurement.setSystemOfMeasurement(SystemOfMeasurement.IMPERIAL);
-    }
-
+@FakeImagery
+@Main
+@Projection
+// Needed since testShowDownloadedAreaLayerSwitching expects the measurement to be imperial
+@MeasurementSystem("Imperial")
+class MinimapDialogTest {
     /**
      * Unit test of {@link MinimapDialog} class.
      */
     @Test
-    public void testMinimapDialog() {
+    void testMinimapDialog() {
         MinimapDialog dlg = new MinimapDialog();
         dlg.showDialog();
@@ -206,5 +196,5 @@
      */
     @Test
-    public void testSourceSwitching() {
+    void testSourceSwitching() {
         // relevant prefs starting out empty, should choose the first source and have shown download area enabled
         // (not that there's a data layer for it to use)
@@ -252,5 +242,5 @@
      */
     @Test
-    public void testRefreshSourcesRetainsSelection() {
+    void testRefreshSourcesRetainsSelection() {
         // relevant prefs starting out empty, should choose the first source and have shown download area enabled
         // (not that there's a data layer for it to use)
@@ -289,5 +279,5 @@
      */
     @Test
-    public void testRemovedSourceStillSelected() {
+    void testRemovedSourceStillSelected() {
         // relevant prefs starting out empty, should choose the first source and have shown download area enabled
         // (not that there's a data layer for it to use)
@@ -321,5 +311,5 @@
      */
     @Test
-    public void testTileSourcesFromCurrentLayers() {
+    void testTileSourcesFromCurrentLayers() {
         // relevant prefs starting out empty, should choose the first (ImageryLayerInfo) source and have shown download area enabled
         // (not that there's a data layer for it to use)
@@ -454,5 +444,5 @@
      */
     @Test
-    public void testSourcePrefObeyed() {
+    void testSourcePrefObeyed() {
         Config.getPref().put("slippy_map_chooser.mapstyle", "Green Tiles");
 
@@ -480,5 +470,5 @@
      */
     @Test
-    public void testSourcePrefInvalid() {
+    void testSourcePrefInvalid() {
         Config.getPref().put("slippy_map_chooser.mapstyle", "Hooloovoo Tiles");
 
@@ -501,5 +491,5 @@
      */
     @Test
-    public void testViewportAspectRatio() {
+    void testViewportAspectRatio() {
         // Add a test layer to the layer manager to get the MapFrame & MapView
         MainApplication.getLayerManager().addLayer(new TestLayer());
@@ -640,5 +630,5 @@
      */
     @Test
-    public void testShowDownloadedArea() {
+    void testShowDownloadedArea() {
         Config.getPref().put("slippy_map_chooser.mapstyle", "Green Tiles");
         Config.getPref().putBoolean("slippy_map_chooser.show_downloaded_area", false);
@@ -798,5 +788,5 @@
      */
     @Test
-    public void testShowDownloadedAreaLayerSwitching() {
+    void testShowDownloadedAreaLayerSwitching() {
         Config.getPref().put("slippy_map_chooser.mapstyle", "Green Tiles");
         Config.getPref().putBoolean("slippy_map_chooser.show_downloaded_area", true);
Index: trunk/test/unit/org/openstreetmap/josm/gui/dialogs/layer/CycleLayerActionTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/dialogs/layer/CycleLayerActionTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/dialogs/layer/CycleLayerActionTest.java	(revision 18893)
@@ -5,7 +5,6 @@
 import static org.openstreetmap.josm.tools.I18n.tr;
 
-import org.junit.Before;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
@@ -15,7 +14,7 @@
 import org.openstreetmap.josm.gui.layer.MainLayerManager;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.openstreetmap.josm.testutils.annotations.FakeImagery;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
 /**
@@ -24,10 +23,8 @@
  * @author Taylor Smock
  */
-public class CycleLayerActionTest {
-    /** Layers need a projection */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().main().preferences().projection().fakeImagery();
-
+@FakeImagery
+@Main
+@Projection
+class CycleLayerActionTest {
     private CycleLayerDownAction cycleDown;
     private CycleLayerUpAction cycleUp;
@@ -37,6 +34,6 @@
      * Set up common items (make layers, etc.)
      */
-    @Before
-    public void setUp() {
+    @BeforeEach
+    void setUp() {
         cycleDown = new CycleLayerDownAction();
         cycleUp = new CycleLayerUpAction();
@@ -51,5 +48,5 @@
      */
     @Test
-    public void testDownBottom() {
+    void testDownBottom() {
         manager.setActiveLayer(manager.getLayers().get(0));
         cycleDown.actionPerformed(null);
@@ -61,5 +58,5 @@
      */
     @Test
-    public void testUpTop() {
+    void testUpTop() {
         manager.setActiveLayer(manager.getLayers().get(manager.getLayers().size() - 1));
         cycleUp.actionPerformed(null);
@@ -71,5 +68,5 @@
      */
     @Test
-    public void testDown() {
+    void testDown() {
         manager.setActiveLayer(manager.getLayers().get(3));
         cycleDown.actionPerformed(null);
@@ -81,5 +78,5 @@
      */
     @Test
-    public void testUp() {
+    void testUp() {
         manager.setActiveLayer(manager.getLayers().get(3));
         cycleUp.actionPerformed(null);
@@ -91,5 +88,5 @@
      */
     @Test
-    public void testNoLayers() {
+    void testNoLayers() {
         manager.getLayers().forEach(manager::removeLayer);
         cycleUp.actionPerformed(null);
@@ -102,5 +99,5 @@
      */
     @Test
-    public void testWithAerialImagery() {
+    void testWithAerialImagery() {
         final ImageryInfo magentaTilesInfo = ImageryLayerInfo.instance.getLayers().stream()
                 .filter(i -> i.getName().equals("Magenta Tiles")).findAny().get();
Index: trunk/test/unit/org/openstreetmap/josm/gui/help/HelpBrowserTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/help/HelpBrowserTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/help/HelpBrowserTest.java	(revision 18893)
@@ -10,7 +10,7 @@
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
 import org.openstreetmap.josm.tools.LanguageInfo.LocaleType;
@@ -18,5 +18,4 @@
 import org.openstreetmap.josm.tools.PlatformManager;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import mockit.Expectations;
 import mockit.Injectable;
@@ -26,4 +25,6 @@
  * Unit tests of {@link HelpBrowser} class.
  */
+@FullPreferences
+@HTTPS
 class HelpBrowserTest {
 
@@ -31,11 +32,4 @@
     static final String URL_2 = "https://josm.openstreetmap.de/wiki/Introduction";
     static final String URL_3 = "https://josm.openstreetmap.de/javadoc";
-
-    /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    static JOSMTestRules test = new JOSMTestRules().preferences().https();
 
     static IHelpBrowser newHelpBrowser() {
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/GpxLayerTest.java	(revision 18893)
@@ -23,5 +23,4 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.gpx.GpxData;
@@ -36,22 +35,19 @@
 import org.openstreetmap.josm.gui.widgets.HtmlPanel;
 import org.openstreetmap.josm.io.GpxReaderTest;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.I18n;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.MeasurementSystem;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.tools.date.DateUtils;
 import org.xml.sax.SAXException;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Unit tests of {@link GpxLayer} class.
  */
+@I18n
+@Main
+@MeasurementSystem
+@Projection
 public class GpxLayerTest {
-
-    /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().main().projection().i18n().metricSystem();
-
     /**
      * Setup test.
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/MainLayerManagerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/MainLayerManagerTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/MainLayerManagerTest.java	(revision 18893)
@@ -13,8 +13,6 @@
 import java.util.logging.LogRecord;
 
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.openstreetmap.josm.JOSMFixture;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
@@ -62,5 +60,5 @@
     protected static class LoggingHandler extends Handler {
 
-        private List<LogRecord> records = new ArrayList<>();
+        private final List<LogRecord> records = new ArrayList<>();
 
         @Override
@@ -79,9 +77,4 @@
         }
 
-    }
-
-    @BeforeAll
-    public static void setUpClass() {
-        JOSMFixture.createUnitTestFixture().init();
     }
 
@@ -153,6 +146,6 @@
         CapturingActiveLayerChangeListener listener2 = new CapturingActiveLayerChangeListener();
         layerManagerWithActive.addAndFireActiveLayerChangeListener(listener2);
-        assertSame(listener2.lastEvent.getPreviousActiveLayer(), null);
-        assertSame(listener2.lastEvent.getPreviousDataLayer(), null);
+        assertNull(listener2.lastEvent.getPreviousActiveLayer());
+        assertNull(listener2.lastEvent.getPreviousDataLayer());
 
         layerManagerWithActive.setActiveLayer(layer1);
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/DownloadWmsAlongTrackActionTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/DownloadWmsAlongTrackActionTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/gpx/DownloadWmsAlongTrackActionTest.java	(revision 18893)
@@ -11,6 +11,7 @@
 
 import org.awaitility.Awaitility;
-import org.junit.Rule;
-import org.junit.Test;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
+import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.actions.MergeLayerActionTest.MergeLayerExtendedDialogMocker;
 import org.openstreetmap.josm.data.gpx.GpxData;
@@ -19,22 +20,17 @@
 import org.openstreetmap.josm.gui.layer.TMSLayer;
 import org.openstreetmap.josm.gui.layer.gpx.DownloadWmsAlongTrackAction.PrecacheWmsTask;
-import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-import org.openstreetmap.josm.testutils.TileSourceRule;
+import org.openstreetmap.josm.testutils.annotations.FakeImagery;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Unit tests of {@link DownloadWmsAlongTrackAction} class.
  */
-public class DownloadWmsAlongTrackActionTest {
-
-    /**
-     * Setup test.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().main().projection().fakeImagery().timeout(20000);
+@FakeImagery
+@Main
+@Projection
+@Timeout(20)
+class DownloadWmsAlongTrackActionTest {
 
     /**
@@ -42,5 +38,5 @@
      */
     @Test
-    public void testNoLayer() {
+    void testNoLayer() {
         TestUtils.assumeWorkingJMockit();
         final JOptionPaneSimpleMocker jopsMocker = new JOptionPaneSimpleMocker(
@@ -61,13 +57,11 @@
      */
     @Test
-    public void testTMSLayer() throws Exception {
+    void testTMSLayer(FakeImagery.FakeImageryWireMockExtension fakeImageryWireMockExtension) throws Exception {
         TestUtils.assumeWorkingJMockit();
         final MergeLayerExtendedDialogMocker edMocker = new MergeLayerExtendedDialogMocker();
         edMocker.getMockResultMap().put("Please select the imagery layer.", "Download");
 
-        final TileSourceRule tileSourceRule = this.test.getTileSourceRule();
-
         final TMSLayer layer = new TMSLayer(
-            tileSourceRule.getSourcesList().get(0).getImageryInfo(tileSourceRule.port())
+                fakeImageryWireMockExtension.getSourcesList().get(0).getImageryInfo(fakeImageryWireMockExtension.getRuntimeInfo().getHttpPort())
         );
         try {
Index: trunk/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/WebMarkerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/WebMarkerTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/layer/markerlayer/WebMarkerTest.java	(revision 18893)
@@ -7,14 +7,12 @@
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.gpx.GpxData;
 import org.openstreetmap.josm.data.gpx.WayPoint;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.tools.PlatformHook;
 import org.openstreetmap.josm.tools.PlatformManager;
-import org.openstreetmap.josm.tools.PlatformHook;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 import mockit.Expectations;
@@ -25,13 +23,7 @@
  * Unit tests of {@link WebMarker} class.
  */
+@FullPreferences
+@HTTPS
 class WebMarkerTest {
-
-    /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().https();
-
     /**
      * Unit test of {@link WebMarker#WebMarker}.
Index: trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTestIT.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTestIT.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTestIT.java	(revision 18893)
@@ -4,22 +4,14 @@
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
 /**
  * Integration tests of {@link MapCSSParser}.
  */
+@HTTPS
+@Projection
 class MapCSSParserTestIT {
-
-    /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().https().projection();
-
     /**
      * Checks Kothic stylesheets
Index: trunk/test/unit/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreferenceTestIT.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreferenceTestIT.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreferenceTestIT.java	(revision 18893)
@@ -30,5 +30,5 @@
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.Timeout;
 import org.junit.jupiter.api.parallel.Execution;
 import org.junit.jupiter.api.parallel.ExecutionMode;
@@ -66,5 +66,7 @@
 import org.openstreetmap.josm.io.imagery.ApiKeyProvider;
 import org.openstreetmap.josm.io.imagery.WMSImagery.WMSGetCapabilitiesException;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.I18n;
+import org.openstreetmap.josm.testutils.annotations.ProjectionNadGrids;
 import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.HttpClient.Response;
@@ -72,9 +74,12 @@
 import org.openstreetmap.josm.tools.Utils;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Integration tests of {@link ImageryPreference} class.
  */
+@HTTPS
+@I18n
+@org.openstreetmap.josm.testutils.annotations.Projection
+@ProjectionNadGrids
+@Timeout(value = 40, unit = TimeUnit.MINUTES)
 public class ImageryPreferenceTestIT {
 
@@ -82,12 +87,4 @@
     private static final LatLon GREENWICH = new LatLon(51.47810, -0.00170);
     private static final int DEFAULT_ZOOM = 12;
-
-    /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    static JOSMTestRules test = new JOSMTestRules().https().i18n().preferences().projection().projectionNadGrids()
-                                                   .timeout((int) TimeUnit.MINUTES.toMillis(40));
 
     /** Entry to test */
Index: trunk/test/unit/org/openstreetmap/josm/gui/preferences/map/MapPaintPreferenceTestIT.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/preferences/map/MapPaintPreferenceTestIT.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/preferences/map/MapPaintPreferenceTestIT.java	(revision 18893)
@@ -9,7 +9,8 @@
 import java.util.ArrayList;
 import java.util.List;
+import java.util.concurrent.TimeUnit;
 
 import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.Timeout;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -22,24 +23,16 @@
 import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.AssignmentInstruction;
-import org.openstreetmap.josm.gui.preferences.AbstractExtendedSourceEntryTestCase;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSRule;
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.gui.preferences.AbstractExtendedSourceEntryTestCase;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 import org.openstreetmap.josm.tools.ImageProvider;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Integration tests of {@link MapPaintPreference} class.
  */
+@HTTPS
+@Timeout(value = 15, unit = TimeUnit.MINUTES)
 class MapPaintPreferenceTestIT extends AbstractExtendedSourceEntryTestCase {
-
-    /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public static JOSMTestRules test = new JOSMTestRules().https().timeout(15000*60).parameters();
-
     /**
      * Setup test
@@ -66,9 +59,8 @@
      * @param url URL
      * @param source source entry to test
-     * @throws Exception in case of error
      */
     @ParameterizedTest(name = "{0} - {1}")
     @MethodSource("data")
-    void testStyleValidity(String displayName, String url, ExtendedSourceEntry source) throws Exception {
+    void testStyleValidity(String displayName, String url, ExtendedSourceEntry source) {
         assumeFalse(isIgnoredSubstring(source, source.url));
         StyleSource style = MapPaintStyles.addStyle(source);
Index: trunk/test/unit/org/openstreetmap/josm/gui/preferences/map/TaggingPresetPreferenceTestIT.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/preferences/map/TaggingPresetPreferenceTestIT.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/preferences/map/TaggingPresetPreferenceTestIT.java	(revision 18893)
@@ -15,7 +15,8 @@
 import java.util.Locale;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 
 import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.Timeout;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -28,5 +29,5 @@
 import org.openstreetmap.josm.gui.tagging.presets.items.Link;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.HttpClient.Response;
@@ -35,18 +36,10 @@
 import org.xml.sax.SAXException;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Integration tests of {@link TaggingPresetPreference} class.
  */
+@HTTPS
+@Timeout(value = 20, unit = TimeUnit.MINUTES)
 class TaggingPresetPreferenceTestIT extends AbstractExtendedSourceEntryTestCase {
-
-    /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public static JOSMTestRules test = new JOSMTestRules().https().timeout(10000*120).parameters();
-
     /**
      * Setup test
@@ -78,9 +71,8 @@
      * @param url URL
      * @param source source entry to test
-     * @throws Exception in case of error
      */
     @ParameterizedTest(name = "{0} - {1}")
     @MethodSource("data")
-    void testPresetsValidity(String displayName, String url, ExtendedSourceEntry source) throws Exception {
+    void testPresetsValidity(String displayName, String url, ExtendedSourceEntry source) {
         assumeFalse(isIgnoredSubstring(source, source.url));
         List<String> ignoredErrors = new ArrayList<>();
Index: trunk/test/unit/org/openstreetmap/josm/gui/preferences/validator/ValidatorTagCheckerRulesPreferenceTestIT.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/gui/preferences/validator/ValidatorTagCheckerRulesPreferenceTestIT.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/gui/preferences/validator/ValidatorTagCheckerRulesPreferenceTestIT.java	(revision 18893)
@@ -14,5 +14,5 @@
 
 import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.Timeout;
 import org.junit.jupiter.api.parallel.Execution;
 import org.junit.jupiter.api.parallel.ExecutionMode;
@@ -24,20 +24,12 @@
 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.ParseResult;
 import org.openstreetmap.josm.gui.preferences.AbstractExtendedSourceEntryTestCase;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 
 /**
  * Integration tests of {@link ValidatorTagCheckerRulesPreference} class.
  */
+@HTTPS
+@Timeout(20)
 class ValidatorTagCheckerRulesPreferenceTestIT extends AbstractExtendedSourceEntryTestCase {
-
-    /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    static JOSMTestRules test = new JOSMTestRules().https().timeout(20_000);
-
     /**
      * Setup test
Index: trunk/test/unit/org/openstreetmap/josm/io/CertificateAmendmentTestIT.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/io/CertificateAmendmentTestIT.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/io/CertificateAmendmentTestIT.java	(revision 18893)
@@ -13,24 +13,16 @@
 import javax.net.ssl.SSLHandshakeException;
 
-import org.junit.ClassRule;
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 
 /**
  * Integration tests of {@link CertificateAmendment} class.
  */
+@HTTPS
+@Timeout(20)
 class CertificateAmendmentTestIT {
-
-    /**
-     * Setup rule
-     */
-    @ClassRule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public static JOSMTestRules test = new JOSMTestRules().https().preferences().timeout(20000);
-
     private static final List<String> errorsToIgnore = new ArrayList<>();
 
Index: trunk/test/unit/org/openstreetmap/josm/io/NetworkManagerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/io/NetworkManagerTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/io/NetworkManagerTest.java	(revision 18893)
@@ -10,22 +10,18 @@
 import java.util.Map;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.OsmApi;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
 /**
  * Unit tests of {@link NetworkManager} class.
  */
+@HTTPS
+@Main
+@OsmApi(OsmApi.APIType.DEV)
+@Projection
 class NetworkManagerTest {
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().https().devAPI().main().projection();
-
     /**
      * Unit test of {@link NetworkManager#addNetworkError},
Index: trunk/test/unit/org/openstreetmap/josm/io/remotecontrol/RemoteControlTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/io/remotecontrol/RemoteControlTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/io/remotecontrol/RemoteControlTest.java	(revision 18893)
@@ -14,24 +14,17 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.AssertionsInEDT;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 import org.openstreetmap.josm.tools.Utils;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
  * Unit tests for Remote Control
  */
+@AssertionsInEDT
+@HTTPS
 class RemoteControlTest {
 
     private String httpBase;
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().https().assertionsInEDT();
 
     /**
@@ -65,5 +58,5 @@
         HttpURLConnection connection = (HttpURLConnection) new URL(url).openConnection();
         connection.connect();
-        assertEquals(connection.getResponseCode(), HttpURLConnection.HTTP_BAD_REQUEST);
+        assertEquals(HttpURLConnection.HTTP_BAD_REQUEST, connection.getResponseCode());
         try (InputStream is = connection.getErrorStream()) {
             String responseBody = new String(Utils.readBytesFromStream(is), StandardCharsets.UTF_8);
Index: trunk/test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java	(revision 18893)
@@ -29,5 +29,4 @@
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.platform.commons.util.ReflectionUtils;
 import org.openstreetmap.josm.TestUtils;
@@ -41,6 +40,6 @@
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
 import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 import org.openstreetmap.josm.testutils.annotations.Main;
 import org.openstreetmap.josm.testutils.annotations.Projection;
@@ -50,10 +49,9 @@
 import org.openstreetmap.josm.tools.Utils;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Integration tests of {@link PluginHandler} class.
  */
 @BasicPreferences
+@HTTPS
 @Main
 @Projection
@@ -63,10 +61,4 @@
 
     private static final List<String> errorsToIgnore = new ArrayList<>();
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public static JOSMTestRules test = new JOSMTestRules().https();
 
     /**
Index: trunk/test/unit/org/openstreetmap/josm/spi/lifecycle/LifecycleTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/spi/lifecycle/LifecycleTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/spi/lifecycle/LifecycleTest.java	(revision 18893)
@@ -5,22 +5,18 @@
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.OsmApi;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
 /**
  * Unit tests of {@link Lifecycle} class.
  */
+@HTTPS
+@Main
+@OsmApi(OsmApi.APIType.DEV)
+@Projection
 class LifecycleTest {
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().https().devAPI().main().projection();
-
     private static class InitStatusListenerStub implements InitStatusListener {
 
Index: trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java	(revision 18893)
@@ -58,5 +58,4 @@
 import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreferenceTestIT;
 import org.openstreetmap.josm.gui.util.GuiHelper;
-import org.openstreetmap.josm.io.CertificateAmendment;
 import org.openstreetmap.josm.io.OsmApi;
 import org.openstreetmap.josm.io.OsmApiInitializationException;
@@ -65,4 +64,5 @@
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.spi.preferences.Setting;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 import org.openstreetmap.josm.testutils.annotations.JosmDefaults;
 import org.openstreetmap.josm.testutils.annotations.MapPaintStyles;
@@ -136,4 +136,5 @@
      * @param millis The timeout duration in milliseconds.
      * @return this instance, for easy chaining
+     * @see org.junit.jupiter.api.Timeout
      */
     public JOSMTestRules timeout(int millis) {
@@ -145,4 +146,6 @@
      * Enable the use of default preferences.
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.BasicPreferences
+     * @see org.openstreetmap.josm.testutils.annotations.FullPreferences
      */
     public JOSMTestRules preferences() {
@@ -164,4 +167,5 @@
      * Enables the i18n module for this test in english.
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.I18n
      */
     public JOSMTestRules i18n() {
@@ -173,4 +177,5 @@
      * @param language The language to use.
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.I18n
      */
     public JOSMTestRules i18n(String language) {
@@ -183,4 +188,5 @@
      * @param revisionProperties mock contents of JOSM's {@code REVISION} properties file
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.AssumeRevision
      */
     public JOSMTestRules assumeRevision(final String revisionProperties) {
@@ -192,4 +198,5 @@
      * Enable the dev.openstreetmap.org API for this test.
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.OsmApi.APIType#DEV
      */
     public JOSMTestRules devAPI() {
@@ -202,4 +209,5 @@
      * Use the {@link FakeOsmApi} for testing.
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.OsmApi.APIType#FAKE
      */
     public JOSMTestRules fakeAPI() {
@@ -211,4 +219,5 @@
      * Set up default projection (Mercator)
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.Projection
      */
     public JOSMTestRules projection() {
@@ -220,4 +229,5 @@
      * Set up loading of NTV2 grit shift files to support projections that need them.
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.ProjectionNadGrids
      */
     public JOSMTestRules projectionNadGrids() {
@@ -229,4 +239,5 @@
      * Set up HTTPS certificates
      * @return this instance, for easy chaining
+     * @see HTTPS
      */
     public JOSMTestRules https() {
@@ -247,4 +258,5 @@
      * Allow the memory manager to contain items after execution of the test cases.
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.MemoryManagerLeaks
      */
     public JOSMTestRules memoryManagerLeaks() {
@@ -256,4 +268,5 @@
      * Use map styles in this test.
      * @return this instance, for easy chaining
+     * @see MapPaintStyles
      * @since 11777
      */
@@ -267,4 +280,5 @@
      * Use presets in this test.
      * @return this instance, for easy chaining
+     * @see TaggingPresets
      * @since 12568
      */
@@ -278,4 +292,5 @@
      * Use boundaries dataset in this test.
      * @return this instance, for easy chaining
+     * @see Territories
      * @since 12545
      */
@@ -300,4 +315,5 @@
      * Force metric measurement system.
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.MeasurementSystem
      * @since 15400
      */
@@ -310,4 +326,5 @@
      * Re-raise AssertionErrors thrown in the EDT where they would have normally been swallowed.
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.AssertionsInEDT
      */
     public JOSMTestRules assertionsInEDT() {
@@ -320,4 +337,5 @@
      *
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.AssertionsInEDT
      */
     public JOSMTestRules assertionsInEDT(final Runnable edtAssertionMockingRunnable) {
@@ -330,4 +348,5 @@
      *
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.FakeImagery
      */
     public JOSMTestRules fakeImagery() {
@@ -350,4 +369,5 @@
      *
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.FakeImagery
      */
     public JOSMTestRules fakeImagery(TileSourceRule tileSourceRule) {
@@ -361,4 +381,5 @@
      *         global variables in this test.
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.Main
      * @since 12557
      */
@@ -379,4 +400,5 @@
      *
      * @return this instance, for easy chaining
+     * @see org.openstreetmap.josm.testutils.annotations.AssertionsInEDT
      */
     public JOSMTestRules main(
@@ -561,5 +583,5 @@
         if (useHttps) {
             try {
-                CertificateAmendment.addMissingCertificates();
+                new HTTPS.HTTPSExtension().beforeEach(null);
             } catch (IOException | GeneralSecurityException ex) {
                 throw new JosmRuntimeException(ex);
Index: trunk/test/unit/org/openstreetmap/josm/testutils/annotations/BasicWiremock.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/testutils/annotations/BasicWiremock.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/testutils/annotations/BasicWiremock.java	(revision 18893)
@@ -8,4 +8,5 @@
 import java.lang.annotation.Documented;
 import java.lang.annotation.ElementType;
+import java.lang.annotation.Inherited;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
@@ -51,4 +52,5 @@
  * @since 18106
  */
+@Inherited
 @Documented
 @Retention(RetentionPolicy.RUNTIME)
Index: trunk/test/unit/org/openstreetmap/josm/testutils/annotations/FakeImagery.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/testutils/annotations/FakeImagery.java	(revision 18893)
+++ trunk/test/unit/org/openstreetmap/josm/testutils/annotations/FakeImagery.java	(revision 18893)
@@ -0,0 +1,240 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+import static org.openstreetmap.josm.TestUtils.getPrivateStaticField;
+
+import java.awt.Color;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.AfterEachCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ParameterContext;
+import org.junit.jupiter.api.extension.ParameterResolutionException;
+import org.junit.jupiter.api.extension.ParameterResolver;
+import org.openstreetmap.josm.data.imagery.ImageryInfo;
+import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
+import org.openstreetmap.josm.gui.bbox.JosmMapViewer;
+import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.TileSourceRule;
+import org.openstreetmap.josm.tools.Logging;
+
+import com.github.tomakehurst.wiremock.junit5.WireMockExtension;
+import com.github.tomakehurst.wiremock.junit5.WireMockRuntimeInfo;
+
+/**
+ * Replace imagery sources with a default set of mock tile sources.
+ * @author Taylor Smock
+ * @since 18893
+ * @see JOSMTestRules#fakeImagery()
+ */
+@Inherited
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+@BasicPreferences
+@ExtendWith(FakeImagery.FakeImageryExtension.class)
+public @interface FakeImagery {
+    /**
+     * This is a stop-gap for <a href="https://github.com/wiremock/wiremock/pull/1981">WireMock #1981</a>.
+     * We just wrap everything.
+     */
+    class FakeImageryExtension implements ParameterResolver,
+            BeforeEachCallback,
+            BeforeAllCallback,
+            AfterEachCallback,
+            AfterAllCallback {
+
+        @Override
+        public void afterAll(ExtensionContext extensionContext) throws Exception {
+            getActualExtension(extensionContext).afterAll(extensionContext);
+        }
+
+        @Override
+        public void afterEach(ExtensionContext extensionContext) throws Exception {
+            final FakeImageryWireMockExtension extension = getActualExtension(extensionContext);
+            extension.afterEach(extensionContext);
+            extension.onAfterEach(extensionContext, getWireMockRuntimeInfo(extensionContext));
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext extensionContext) throws Exception {
+            getActualExtension(extensionContext).beforeAll(extensionContext);
+        }
+
+        @Override
+        public void beforeEach(ExtensionContext extensionContext) throws Exception {
+            final FakeImageryWireMockExtension extension = getActualExtension(extensionContext);
+            extension.beforeEach(extensionContext);
+            extension.onBeforeEach(extensionContext, getWireMockRuntimeInfo(extensionContext));
+        }
+
+        @Override
+        public boolean supportsParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+                throws ParameterResolutionException {
+            if (parameterContext.getParameter().getType().equals(FakeImageryWireMockExtension.class)) {
+                return true;
+            }
+            return getActualExtension(extensionContext).supportsParameter(parameterContext, extensionContext);
+        }
+
+        @Override
+        public Object resolveParameter(ParameterContext parameterContext, ExtensionContext extensionContext)
+                throws ParameterResolutionException {
+            if (parameterContext.getParameter().getType().equals(FakeImageryWireMockExtension.class)) {
+                return getActualExtension(extensionContext);
+            }
+            return getActualExtension(extensionContext).resolveParameter(parameterContext, extensionContext);
+        }
+
+        private static FakeImageryWireMockExtension getActualExtension(ExtensionContext extensionContext) {
+            return FakeImageryWireMockExtension.getStore(extensionContext)
+                    .getOrComputeIfAbsent(FakeImageryWireMockExtension.class, ignored -> new FakeImageryWireMockExtension(),
+                            FakeImageryWireMockExtension.class);
+        }
+
+        private static WireMockRuntimeInfo getWireMockRuntimeInfo(ExtensionContext extensionContext) {
+            return FakeImageryWireMockExtension.getStore(extensionContext)
+                    .getOrComputeIfAbsent(WireMockRuntimeInfo.class, ignored -> getActualExtension(extensionContext).getRuntimeInfo(),
+                            WireMockRuntimeInfo.class);
+
+        }
+    }
+
+    class FakeImageryWireMockExtension extends WireMockExtension {
+
+        private final boolean clearLayerList;
+        private final boolean clearSlippyMapSources;
+        private final boolean registerInLayerList;
+        private final List<TileSourceRule.ConstSource> sources;
+
+        static ExtensionContext.Store getStore(ExtensionContext extensionContext) {
+            return extensionContext.getStore(ExtensionContext.Namespace.create(FakeImageryWireMockExtension.class));
+        }
+
+        /**
+         * See {@link FakeImageryWireMockExtension#FakeImageryWireMockExtension(boolean, boolean, boolean, TileSourceRule.ConstSource...)}.
+         * This provides tile sources for that are white, black, magenta, or green.
+         */
+        FakeImageryWireMockExtension() {
+            this(
+                    true,
+                    true,
+                    true,
+                    new TileSourceRule.ColorSource(Color.WHITE, "White Tiles", 256),
+                    new TileSourceRule.ColorSource(Color.BLACK, "Black Tiles", 256),
+                    new TileSourceRule.ColorSource(Color.MAGENTA, "Magenta Tiles", 256),
+                    new TileSourceRule.ColorSource(Color.GREEN, "Green Tiles", 256)
+            );
+        }
+
+        /**
+         * Construct a FakeImageryWireMockExtension for use with a JUnit test.
+         * <p>
+         * This is hidden for now, since all internal used {@link JOSMTestRules#fakeImagery()} instead of
+         * {@link JOSMTestRules#fakeImagery(TileSourceRule)}. Before making this public, we'll probably want to move
+         * {@link TileSourceRule.ConstSource} and it's subclasses around.
+         * <p>
+         * The three boolean parameters control whether to perform various steps registering the tile sources with parts
+         * of JOSM's internals as part of the setup process. It is advised to only enable any of these if it can be ensured
+         * that this rule will have its setup routine executed *after* the relevant parts of JOSM have been set up, e.g.
+         * when handled by {@link FakeImagery}.
+         *
+         * @param clearLayerList whether to clear ImageryLayerInfo's layer list of any pre-existing entries
+         * @param clearSlippyMapSources whether to clear SlippyMapBBoxChooser's stubborn fallback Mapnik TileSource
+         * @param registerInLayerList whether to add sources to ImageryLayerInfo's layer list
+         * @param sources tile sources to serve from this mock server
+         */
+        private FakeImageryWireMockExtension(boolean clearLayerList, boolean clearSlippyMapSources, boolean registerInLayerList,
+                                             TileSourceRule.ConstSource... sources) {
+            super(WireMockExtension.extensionOptions());
+            this.clearLayerList = clearLayerList;
+            this.clearSlippyMapSources = clearSlippyMapSources;
+            this.registerInLayerList = registerInLayerList;
+            this.sources = Collections.unmodifiableList(Arrays.asList(sources.clone()));
+        }
+
+        /**
+         * Get the tile sources served by this {@link FakeImageryWireMockExtension}.
+         *
+         * @return an unmodifiable list of the tile sources served by this {@link FakeImageryWireMockExtension}
+         */
+        public List<TileSourceRule.ConstSource> getSourcesList() {
+            return this.sources;
+        }
+
+        protected void onBeforeEach(ExtensionContext extensionContext, WireMockRuntimeInfo wireMockRuntimeInfo) {
+            super.onBeforeEach(wireMockRuntimeInfo);
+            final ExtensionContext.Store store = getStore(extensionContext);
+            registerLayers(store, wireMockRuntimeInfo);
+            for (TileSourceRule.ConstSource source : this.sources) {
+                this.stubFor(source.getMappingBuilder().willReturn(source.getResponseDefinitionBuilder()));
+            }
+        }
+
+        protected void onAfterEach(ExtensionContext extensionContext, WireMockRuntimeInfo wireMockRuntimeInfo) {
+            super.onAfterEach(wireMockRuntimeInfo);
+            final ExtensionContext.Store store = getStore(extensionContext);
+            unregisterLayers(store);
+        }
+
+        private void registerLayers(ExtensionContext.Store store, WireMockRuntimeInfo wireMockRuntimeInfo) {
+            if (this.clearSlippyMapSources) {
+                try {
+                    @SuppressWarnings("unchecked")
+                    List<SlippyMapBBoxChooser.TileSourceProvider> slippyMapProviders =
+                            (List<SlippyMapBBoxChooser.TileSourceProvider>) getPrivateStaticField(
+                                    SlippyMapBBoxChooser.class,
+                                    "providers"
+                            );
+                    // pop this off the beginning of the list, keep for later
+                    SlippyMapBBoxChooser.TileSourceProvider slippyMapDefaultProvider = slippyMapProviders.remove(0);
+                    store.put("slippyMapProviders", slippyMapProviders);
+                    store.put("slippyMapDefaultProvider", slippyMapDefaultProvider);
+                } catch (ReflectiveOperationException e) {
+                    Logging.warn("Failed to remove default SlippyMapBBoxChooser TileSourceProvider");
+                }
+            }
+
+            if (this.clearLayerList) {
+                store.put("originalImageryInfoList", ImageryLayerInfo.instance.getLayers());
+                ImageryLayerInfo.instance.clear();
+            }
+            if (this.registerInLayerList) {
+                for (TileSourceRule.ConstSource source : this.sources) {
+                    ImageryLayerInfo.addLayer(source.getImageryInfo(wireMockRuntimeInfo.getHttpPort()));
+                }
+            }
+        }
+
+        private void unregisterLayers(ExtensionContext.Store store) {
+            @SuppressWarnings("unchecked")
+            final List<SlippyMapBBoxChooser.TileSourceProvider> slippyMapProviders =
+                    (List<SlippyMapBBoxChooser.TileSourceProvider>) store.get("slippyMapProviders", List.class);
+            SlippyMapBBoxChooser.TileSourceProvider slippyMapDefaultProvider =
+                    store.get("slippyMapDefaultProvider", JosmMapViewer.TileSourceProvider.class);
+            @SuppressWarnings("unchecked")
+            List<ImageryInfo> originalImageryInfoList = (List<ImageryInfo>) store.get("originalImageryInfoList", List.class);
+            // clean up to original state
+            if (slippyMapDefaultProvider != null && slippyMapProviders != null) {
+                slippyMapProviders.add(0, slippyMapDefaultProvider);
+            }
+            if (originalImageryInfoList != null) {
+                ImageryLayerInfo.instance.clear();
+                ImageryLayerInfo.addLayers(originalImageryInfoList);
+            }
+        }
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/testutils/annotations/HTTPS.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/testutils/annotations/HTTPS.java	(revision 18893)
+++ trunk/test/unit/org/openstreetmap/josm/testutils/annotations/HTTPS.java	(revision 18893)
@@ -0,0 +1,43 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.io.IOException;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.security.GeneralSecurityException;
+
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.openstreetmap.josm.io.CertificateAmendment;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Set up the HTTPS certificates
+ * @author Taylor Smock
+ * @since 18893
+ * @see JOSMTestRules#https()
+ */
+@Inherited
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+@BasicPreferences
+@ExtendWith(HTTPS.HTTPSExtension.class)
+public @interface HTTPS {
+    class HTTPSExtension implements BeforeEachCallback {
+        private static boolean initialized;
+        @Override
+        public void beforeEach(ExtensionContext extensionContext) throws IOException, GeneralSecurityException {
+            if (!initialized) {
+                CertificateAmendment.addMissingCertificates();
+                initialized = true;
+            }
+        }
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/testutils/annotations/JosmDefaults.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/testutils/annotations/JosmDefaults.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/testutils/annotations/JosmDefaults.java	(revision 18893)
@@ -65,5 +65,5 @@
         @Override
         public void afterEach(ExtensionContext context) throws Exception {
-            MemoryManagerTest.resetState(false);
+            MemoryManagerTest.resetState(AnnotationUtils.findFirstParentAnnotation(context, MemoryManagerLeaks.class).isPresent());
 
             Window[] windows = Window.getWindows();
Index: trunk/test/unit/org/openstreetmap/josm/testutils/annotations/MeasurementSystem.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/testutils/annotations/MeasurementSystem.java	(revision 18893)
+++ trunk/test/unit/org/openstreetmap/josm/testutils/annotations/MeasurementSystem.java	(revision 18893)
@@ -0,0 +1,53 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.openstreetmap.josm.data.SystemOfMeasurement;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Set up the system of measurement
+ * @author Taylor Smock
+ * @since 18893
+ * @see JOSMTestRules#metricSystem()
+ */
+@Inherited
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+@BasicPreferences
+@ExtendWith(MeasurementSystem.SystemOfMeasurementExtension.class)
+public @interface MeasurementSystem {
+    /**
+     * The measurement system to use. See {@link SystemOfMeasurement#ALL_SYSTEMS} for all currently implemented measurement systems.
+     * Currently known measurement systems are:
+     * <ul>
+     *     <li>Chinese</li>
+     *     <li>Imperial</li>
+     *     <li>Metric</li>
+     *     <li>Nautical Mile</li>
+     * </ul>
+     * @return The measurement system name.
+     */
+    String value() default "Metric";
+
+    class SystemOfMeasurementExtension implements BeforeEachCallback {
+        @Override
+        public void beforeEach(ExtensionContext extensionContext) throws Exception {
+            final String system = AnnotationUtils.findFirstParentAnnotation(extensionContext, MeasurementSystem.class)
+                    .map(MeasurementSystem::value)
+                    .orElse(SystemOfMeasurement.METRIC.getName());
+            SystemOfMeasurement.setSystemOfMeasurement(SystemOfMeasurement.ALL_SYSTEMS.get(system));
+        }
+    }
+}
Index: trunk/test/unit/org/openstreetmap/josm/testutils/annotations/MemoryManagerLeaks.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/testutils/annotations/MemoryManagerLeaks.java	(revision 18893)
+++ trunk/test/unit/org/openstreetmap/josm/testutils/annotations/MemoryManagerLeaks.java	(revision 18893)
@@ -0,0 +1,27 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.ElementType.TYPE;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.Inherited;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Allow the memory manager to contain items after execution of the test cases.
+ * @author Taylor Smock
+ * @since 18893
+ * @see JOSMTestRules#memoryManagerLeaks()
+ */
+@Inherited
+@Documented
+@Retention(RUNTIME)
+@Target({TYPE, METHOD})
+@BasicPreferences
+public @interface MemoryManagerLeaks {
+}
Index: trunk/test/unit/org/openstreetmap/josm/tools/MemoryManagerTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/tools/MemoryManagerTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/tools/MemoryManagerTest.java	(revision 18893)
@@ -2,5 +2,4 @@
 package org.openstreetmap.josm.tools;
 
-import static org.junit.jupiter.api.Assertions.fail;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -8,14 +7,12 @@
 import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 import java.util.List;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.MemoryManagerLeaks;
 import org.openstreetmap.josm.tools.MemoryManager.MemoryHandle;
 import org.openstreetmap.josm.tools.MemoryManager.NotEnoughMemoryException;
-
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
 /**
@@ -23,12 +20,6 @@
  * @author Michael Zangl
  */
+@MemoryManagerLeaks
 public class MemoryManagerTest {
-    /**
-     * Base test environment
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().memoryManagerLeaks();
-
     /**
      * Test {@link MemoryManager#allocateMemory(String, long, java.util.function.Supplier)}
Index: trunk/test/unit/org/openstreetmap/josm/tools/PlatformHookWindowsTest.java
===================================================================
--- trunk/test/unit/org/openstreetmap/josm/tools/PlatformHookWindowsTest.java	(revision 18892)
+++ trunk/test/unit/org/openstreetmap/josm/tools/PlatformHookWindowsTest.java	(revision 18893)
@@ -2,4 +2,5 @@
 package org.openstreetmap.josm.tools;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -17,10 +18,8 @@
 import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import mockit.Expectations;
 import mockit.Mocked;
@@ -29,13 +28,6 @@
  * Unit tests of {@link PlatformHookWindows} class.
  */
+@HTTPS
 class PlatformHookWindowsTest {
-
-    /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().https();
-
     static PlatformHookWindows hook;
 
@@ -53,5 +45,7 @@
     @Test
     void testStartupHook() {
-        hook.startupHook((a, b, c, d) -> System.out.println("java callback"), u -> System.out.println("webstart callback"));
+        final PlatformHook.JavaExpirationCallback javaCallback = (a, b, c, d) -> System.out.println("java callback");
+        final PlatformHook.WebStartMigrationCallback webstartCallback = u -> System.out.println("webstart callback");
+        assertDoesNotThrow(() -> hook.startupHook(javaCallback, webstartCallback));
     }
 
@@ -79,5 +73,5 @@
     @Test
     void testAfterPrefStartupHook() {
-        hook.afterPrefStartupHook();
+        assertDoesNotThrow(hook::afterPrefStartupHook);
     }
 
@@ -96,5 +90,5 @@
         }};
 
-        hook.openUrl(Config.getUrls().getJOSMWebsite());
+        assertDoesNotThrow(() -> hook.openUrl(Config.getUrls().getJOSMWebsite()));
     }
 
@@ -122,5 +116,5 @@
         }};
 
-        hook.openUrl(Config.getUrls().getJOSMWebsite());
+        assertDoesNotThrow(() -> hook.openUrl(Config.getUrls().getJOSMWebsite()));
     }
 
@@ -196,5 +190,5 @@
     @Test
     void testInitSystemShortcuts() {
-        hook.initSystemShortcuts();
+        assertDoesNotThrow(hook::initSystemShortcuts);
     }
 }
