Index: src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java
===================================================================
--- src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java	(revision 17429)
+++ src/org/openstreetmap/josm/tools/RightAndLefthandTraffic.java	(working copy)
@@ -47,6 +47,15 @@
         rlCache = new GeoPropertyIndex<>(geoProperty, 24);
     }
 
+    /**
+     * Uninitialize RightAndLefthandTraffic. This should <i>only</i> be used in tests,
+     * and only by the Territories extension. Calling this in production code
+     * <i>is an error</i> and will break stuff.
+     */
+    static void uninitialize() {
+        rlCache = null;
+    }
+
     static void appendLeftDrivingBoundaries(OsmPrimitive osm, Collection<Way> ways) {
         // Find all outer ways of left-driving countries. Many of them are adjacent (African and Asian states)
         if (LEFT.equals(osm.get(DRIVING_SIDE))) {
Index: src/org/openstreetmap/josm/tools/Territories.java
===================================================================
--- src/org/openstreetmap/josm/tools/Territories.java	(revision 17429)
+++ src/org/openstreetmap/josm/tools/Territories.java	(working copy)
@@ -125,6 +125,21 @@
     }
 
     /**
+     * Uninitialize territories. This should <i>only</i> be used in tests,
+     * and only by the Territories extension. Calling this in production code
+     * <i>is an error</i> and will break stuff.
+     */
+    public static synchronized void uninitialize() {
+        iso3166Cache = null;
+        taginfoCache = null;
+        customTagsCache = null;
+        dataSet = null;
+        taginfoGeofabrikCache = null;
+        customTagsCache = null;
+        RightAndLefthandTraffic.uninitialize();
+    }
+
+    /**
      * Initializes territories using the internal data only.
      */
     public static synchronized void initializeInternalData() {
Index: test/functional/org/openstreetmap/josm/data/BoundariesTestIT.java
===================================================================
--- test/functional/org/openstreetmap/josm/data/BoundariesTestIT.java	(revision 17429)
+++ test/functional/org/openstreetmap/josm/data/BoundariesTestIT.java	(working copy)
@@ -11,18 +11,18 @@
 import java.util.stream.Collectors;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.search.SearchCompiler;
 import org.openstreetmap.josm.io.OsmReader;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.IntegrationTest;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Test of boundaries OSM file.
  */
+@BasicPreferences
+@IntegrationTest
 class BoundariesTestIT {
 
     private static final List<String> RETIRED_ISO3166_1_CODES = Arrays.asList(
@@ -47,13 +47,6 @@
             "US-WY");
 
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
-
-    /**
      * Test of boundaries OSM file.
      * @throws Exception if an error occurs
      */
Index: test/functional/org/openstreetmap/josm/data/osm/TaginfoTestIT.java
===================================================================
--- test/functional/org/openstreetmap/josm/data/osm/TaginfoTestIT.java	(revision 17429)
+++ test/functional/org/openstreetmap/josm/data/osm/TaginfoTestIT.java	(working copy)
@@ -22,6 +22,7 @@
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
 import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.IntegrationTest;
 import org.openstreetmap.josm.tools.HttpClient;
 import org.xml.sax.SAXException;
 
@@ -30,6 +31,7 @@
 /**
  * Various integration tests with Taginfo.
  */
+@IntegrationTest
 class TaginfoTestIT {
 
     /**
Index: test/functional/org/openstreetmap/josm/tools/ImageProviderTest.java
===================================================================
--- test/functional/org/openstreetmap/josm/tools/ImageProviderTest.java	(revision 17429)
+++ test/functional/org/openstreetmap/josm/tools/ImageProviderTest.java	(working copy)
@@ -24,13 +24,10 @@
 
 import javax.swing.ImageIcon;
 
-import org.junit.jupiter.api.BeforeAll;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.params.ParameterizedTest;
 import org.junit.jupiter.params.provider.ValueSource;
-import org.openstreetmap.josm.JOSMFixture;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.Node;
@@ -37,7 +34,7 @@
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
 import org.openstreetmap.josm.gui.tagging.presets.items.Key;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 import org.xml.sax.SAXException;
 
 import com.kitfox.svg.SVGConst;
@@ -47,15 +44,8 @@
 /**
  * Unit tests of {@link ImageProvider} class.
  */
+@BasicPreferences
 class ImageProviderTest {
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
     private static final class LogHandler14319 extends Handler {
         boolean failed;
 
@@ -75,14 +65,6 @@
         }
     }
 
-    /**
-     * Setup test.
-     */
-    @BeforeAll
-    public static void setUp() {
-        JOSMFixture.createUnitTestFixture().init();
-    }
-
     @BeforeEach
     public void resetPixelDensity() {
         GuiSizesHelper.setPixelDensity(1.0f);
Index: test/performance/org/openstreetmap/josm/data/osm/KeyValuePerformanceTest.java
===================================================================
--- test/performance/org/openstreetmap/josm/data/osm/KeyValuePerformanceTest.java	(revision 17429)
+++ test/performance/org/openstreetmap/josm/data/osm/KeyValuePerformanceTest.java	(working copy)
@@ -16,11 +16,10 @@
 import org.junit.Before;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.Timeout;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.PerformanceTestUtils;
 import org.openstreetmap.josm.PerformanceTestUtils.PerformanceTestTimer;
 import org.openstreetmap.josm.data.osm.OsmDataGenerator.KeyValueDataGenerator;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
@@ -28,6 +27,7 @@
  * This test measures the performance of {@link OsmPrimitive#get(String)} and related.
  * @author Michael Zangl
  */
+@Projection
 @Timeout(value = 15*60, unit = TimeUnit.SECONDS)
 class KeyValuePerformanceTest {
     private static final int PUT_RUNS = 10000;
@@ -39,13 +39,6 @@
     private Random random;
 
     /**
-     * Prepare the test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projection();
-
-    /**
      * See if there is a big difference between Strings that are interned and those that are not.
      */
     @Test
Index: test/performance/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRendererPerformanceTest.java
===================================================================
--- test/performance/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRendererPerformanceTest.java	(revision 17429)
+++ test/performance/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRendererPerformanceTest.java	(working copy)
@@ -9,6 +9,7 @@
 import org.junit.jupiter.api.AfterAll;
 import org.junit.jupiter.api.BeforeAll;
 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
+import org.openstreetmap.josm.testutils.annotations.SlowTest;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
@@ -15,6 +16,7 @@
 /**
  * Performance test of {@code StyledMapRenderer}.
  */
+@SlowTest
 class StyledMapRendererPerformanceTest extends AbstractMapRendererPerformanceTestParent {
 
     @BeforeAll
Index: test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterTest.java
===================================================================
--- test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterTest.java	(revision 17429)
+++ test/performance/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSStyleSourceFilterTest.java	(working copy)
@@ -17,7 +17,7 @@
  * Tests how fast {@link MapCSSStyleSource} finds the right style candidates for one object.
  * @author Michael Zangl
  */
-@Timeout(value = 15*60, unit = TimeUnit.SECONDS)
+@Timeout(value = 15, unit = TimeUnit.MINUTES)
 class MapCSSStyleSourceFilterTest {
 
     private static final int TEST_RULE_COUNT = 10000;
Index: test/unit/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTaskTestParent.java
===================================================================
--- test/unit/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTaskTestParent.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/actions/downloadtasks/AbstractDownloadTaskTestParent.java	(working copy)
@@ -8,25 +8,16 @@
 
 import org.junit.Rule;
 import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 
 import com.github.tomakehurst.wiremock.junit.WireMockRule;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Superclass of {@link DownloadGpsTaskTest}, {@link DownloadOsmTaskTest} and {@link DownloadNotesTaskTest}.
  */
+@HTTPS
 public abstract class AbstractDownloadTaskTestParent {
-
     /**
-     * Setup test.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().https();
-
-    /**
      * HTTP mock.
      */
     @Rule
Index: test/unit/org/openstreetmap/josm/actions/downloadtasks/PluginDownloadTaskTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/actions/downloadtasks/PluginDownloadTaskTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/actions/downloadtasks/PluginDownloadTaskTest.java	(working copy)
@@ -11,7 +11,6 @@
 import java.nio.charset.StandardCharsets;
 import java.util.Collections;
 
-import org.junit.Rule;
 import org.junit.Test;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.Preferences;
@@ -18,26 +17,20 @@
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.plugins.PluginDownloadTask;
 import org.openstreetmap.josm.plugins.PluginInformation;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.AssumeRevision;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
 import org.openstreetmap.josm.tools.Utils;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests for class {@link PluginDownloadTask}.
  */
+@HTTPS
+@AssumeRevision("Revision: 8000\n")
+@FullPreferences
 public class PluginDownloadTaskTest extends AbstractDownloadTaskTestParent {
     protected String pluginPath;
 
-    /**
-     * Setup test.
-     */
-    @Rule
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules testRule = new JOSMTestRules().https().assumeRevision(
-        "Revision: 8000\n"
-    ).preferences();
-
     @Override
     protected String getRemoteContentType() {
         return "application/java-archive";
Index: test/unit/org/openstreetmap/josm/actions/upload/FixDataHookTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/actions/upload/FixDataHookTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/actions/upload/FixDataHookTest.java	(working copy)
@@ -10,7 +10,6 @@
 import java.util.Collection;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.command.PseudoCommand;
 import org.openstreetmap.josm.command.SequenceCommand;
 import org.openstreetmap.josm.data.APIDataSet;
@@ -19,23 +18,14 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests for class {@link FixDataHook}.
  */
+@BasicPreferences
 class FixDataHookTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().main();
-
-    /**
      * Test of {@link FixDataHook#checkUpload} method.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/actions/upload/ValidateUploadHookTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/actions/upload/ValidateUploadHookTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/actions/upload/ValidateUploadHookTest.java	(working copy)
@@ -3,26 +3,18 @@
 
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.data.APIDataSet;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.OsmApiType;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests for class {@link ValidateUploadHook}.
  */
+@Timeout(30)
+@OsmApiType(apiType = OsmApiType.APIType.FAKE)
 class ValidateUploadHookTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().fakeAPI().timeout(30000);
-
-    /**
      * Test of {@link ValidateUploadHook#checkUpload} method.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/command/AddPrimitivesCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/AddPrimitivesCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/AddPrimitivesCommandTest.java	(working copy)
@@ -12,7 +12,6 @@
 import java.util.List;
 
 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.osm.DataSet;
@@ -24,9 +23,8 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.WayData;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -33,16 +31,9 @@
 /**
  * Unit tests of {@link AddPrimitivesCommand} class.
  */
+@BasicPreferences // for Nodes
 class AddPrimitivesCommandTest {
-
     /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().i18n();
-
-    /**
      * Test if the add command is executed correctly and does not set the modified flag.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/command/ChangeCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/ChangeCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/ChangeCommandTest.java	(working copy)
@@ -4,8 +4,8 @@
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.ArrayList;
 import java.util.Collections;
@@ -13,7 +13,6 @@
 
 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.command.CommandTest.CommandTestData;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -25,9 +24,9 @@
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -34,14 +33,9 @@
 /**
  * Unit tests of {@link ChangeCommand} class.
  */
+@LayerEnvironment
+@BasicPreferences // Preferences are needed for Nodes
 class ChangeCommandTest {
-
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().i18n();
     private CommandTestData testData;
 
     /**
Index: test/unit/org/openstreetmap/josm/command/ChangeMembersCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/ChangeMembersCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/ChangeMembersCommandTest.java	(working copy)
@@ -9,7 +9,6 @@
 
 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.command.CommandTest.CommandTestDataWithRelation;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -18,9 +17,9 @@
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -27,14 +26,9 @@
 /**
  * Unit tests of {@link ChangeMembersCommand} class.
  */
+@LayerEnvironment
+@BasicPreferences // For nodes
 class ChangeMembersCommandTest {
-
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().i18n();
     private CommandTestDataWithRelation testData;
 
     /**
Index: test/unit/org/openstreetmap/josm/command/ChangePropertyCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/ChangePropertyCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/ChangePropertyCommandTest.java	(working copy)
@@ -15,7 +15,6 @@
 
 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.command.CommandTest.CommandTestData;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -26,9 +25,9 @@
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -35,14 +34,10 @@
 /**
  * Unit tests of {@link ChangePropertyCommand} class.
  */
+@BasicPreferences
+@LayerEnvironment
 class ChangePropertyCommandTest {
 
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().i18n();
     private CommandTestData testData;
 
     /**
Index: test/unit/org/openstreetmap/josm/command/ChangeRelationMemberRoleCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/ChangeRelationMemberRoleCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/ChangeRelationMemberRoleCommandTest.java	(working copy)
@@ -11,7 +11,6 @@
 
 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.command.CommandTest.CommandTestDataWithRelation;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -19,9 +18,9 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -28,14 +27,9 @@
 /**
  * Unit tests of {@link ChangeRelationMemberRoleCommand} class.
  */
+@LayerEnvironment
+@BasicPreferences // For nodes
 class ChangeRelationMemberRoleCommandTest {
-
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().i18n();
     private CommandTestDataWithRelation testData;
 
     /**
Index: test/unit/org/openstreetmap/josm/command/CommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/CommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/CommandTest.java	(working copy)
@@ -4,7 +4,6 @@
 import java.util.Arrays;
 
 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.osm.DataSet;
@@ -15,9 +14,8 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -24,16 +22,9 @@
 /**
  * Unit tests of {@link Command} class.
  */
+@BasicPreferences // Needed for nodes
 public class CommandTest {
-
     /**
-     * We need prefs for nodes / data sets.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().i18n();
-
-    /**
      * Unit test of methods {@link Command#equals} and {@link Command#hashCode}.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/command/DeleteCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/DeleteCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/DeleteCommandTest.java	(working copy)
@@ -4,8 +4,8 @@
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -15,7 +15,6 @@
 
 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.command.CommandTest.CommandTestDataWithRelation;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -26,9 +25,9 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.WaySegment;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.DeleteCommandCallback;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -35,14 +34,9 @@
 /**
  * Unit tests of {@link DeleteCommand} class.
  */
+@BasicPreferences
+@DeleteCommandCallback
 class DeleteCommandTest {
-
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().i18n();
     private CommandTestDataWithRelation testData;
 
     /**
Index: test/unit/org/openstreetmap/josm/command/MoveCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/MoveCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/MoveCommandTest.java	(working copy)
@@ -13,7 +13,6 @@
 
 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.command.CommandTest.CommandTestDataWithRelation;
 import org.openstreetmap.josm.data.coor.EastNorth;
@@ -24,9 +23,10 @@
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -33,13 +33,10 @@
 /**
  * Unit tests of {@link MoveCommand} class.
  */
+@Projection
+@BasicPreferences // Needed for nodes
+@LayerEnvironment
 class MoveCommandTest {
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().i18n().projection();
     private CommandTestDataWithRelation testData;
 
     /**
Index: test/unit/org/openstreetmap/josm/command/PurgeCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/PurgeCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/PurgeCommandTest.java	(working copy)
@@ -14,7 +14,6 @@
 
 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.command.CommandTest.CommandTestDataWithRelation;
 import org.openstreetmap.josm.data.conflict.Conflict;
@@ -27,9 +26,9 @@
 import org.openstreetmap.josm.data.osm.Storage;
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -36,13 +35,9 @@
 /**
  * Unit tests of {@link PurgeCommand} class.
  */
+@BasicPreferences // For nodes
+@LayerEnvironment
 class PurgeCommandTest {
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
     private CommandTestDataWithRelation testData;
 
     /**
Index: test/unit/org/openstreetmap/josm/command/RotateCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/RotateCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/RotateCommandTest.java	(working copy)
@@ -9,7 +9,6 @@
 
 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.command.CommandTest.CommandTestData;
 import org.openstreetmap.josm.data.coor.EastNorth;
@@ -19,9 +18,10 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -28,14 +28,10 @@
 /**
  * Unit tests of {@link RotateCommand} class.
  */
+@Projection
+@BasicPreferences
+@LayerEnvironment
 class RotateCommandTest {
-
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().projection();
     private CommandTestData testData;
 
     /**
Index: test/unit/org/openstreetmap/josm/command/SelectCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/SelectCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/SelectCommandTest.java	(working copy)
@@ -3,8 +3,8 @@
 
 import static org.junit.jupiter.api.Assertions.assertArrayEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -12,7 +12,6 @@
 
 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.command.CommandTest.CommandTestDataWithRelation;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -19,9 +18,9 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -28,14 +27,9 @@
 /**
  * Unit tests of {@link SelectCommand} class.
  */
+@LayerEnvironment
+@BasicPreferences // Needed for nodes
 class SelectCommandTest {
-
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
     private CommandTestDataWithRelation testData;
 
     /**
Index: test/unit/org/openstreetmap/josm/command/SequenceCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/SequenceCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/SequenceCommandTest.java	(working copy)
@@ -20,7 +20,6 @@
 
 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.command.CommandTest.CommandTestDataWithRelation;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -28,10 +27,10 @@
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
 import org.openstreetmap.josm.tools.bugreport.ReportedException;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -38,14 +37,9 @@
 /**
  * Unit tests of {@link SequenceCommand} class.
  */
+@LayerEnvironment
+@BasicPreferences // For nodes
 class SequenceCommandTest {
-
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
     private CommandTestDataWithRelation testData;
 
     /**
Index: test/unit/org/openstreetmap/josm/command/conflict/CoordinateConflictResolveCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/conflict/CoordinateConflictResolveCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/conflict/CoordinateConflictResolveCommandTest.java	(working copy)
@@ -7,7 +7,6 @@
 
 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.command.CommandTest.CommandTestData;
 import org.openstreetmap.josm.data.conflict.Conflict;
@@ -18,9 +17,8 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.conflict.pair.MergeDecisionType;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -27,20 +25,13 @@
 /**
  * Unit tests of {@link CoordinateConflictResolveCommand} class.
  */
+@BasicPreferences
 class CoordinateConflictResolveCommandTest {
 
     private CommandTestData testData;
-
     /**
      * Setup test.
      */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
-     * Setup test.
-     */
     @BeforeEach
     public void setUp() {
         testData = new CommandTestData();
Index: test/unit/org/openstreetmap/josm/command/conflict/RelationMemberConflictResolverCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/conflict/RelationMemberConflictResolverCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/conflict/RelationMemberConflictResolverCommandTest.java	(working copy)
@@ -1,7 +1,6 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.command.conflict;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.conflict.Conflict;
@@ -10,9 +9,8 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.User;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -19,16 +17,9 @@
 /**
  * Unit tests of {@link RelationMemberConflictResolverCommand} class.
  */
+@BasicPreferences // Needed for OsmPrimitive
 class RelationMemberConflictResolverCommandTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * Unit test of methods {@link RelationMemberConflictResolverCommand#equals} and {@link RelationMemberConflictResolverCommand#hashCode}.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/command/conflict/VersionConflictResolveCommandTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/command/conflict/VersionConflictResolveCommandTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/command/conflict/VersionConflictResolveCommandTest.java	(working copy)
@@ -1,8 +1,8 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.command.conflict;
 
+import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
-import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.conflict.Conflict;
 import org.openstreetmap.josm.data.osm.DataSet;
Index: test/unit/org/openstreetmap/josm/data/UserIdentityManagerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/UserIdentityManagerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/UserIdentityManagerTest.java	(working copy)
@@ -6,8 +6,8 @@
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.extension.RegisterExtension;
Index: test/unit/org/openstreetmap/josm/data/cache/HostLimitQueueTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/cache/HostLimitQueueTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/cache/HostLimitQueueTest.java	(working copy)
@@ -12,27 +12,20 @@
 
 import org.apache.commons.jcs3.access.behavior.ICacheAccess;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.data.imagery.TMSCachedTileLoader;
 import org.openstreetmap.josm.data.imagery.TileJobOptions;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
 import org.openstreetmap.josm.tools.Logging;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Simple tests for ThreadPoolExecutor / HostLimitQueue veryfing, that this pair works OK
  * @author Wiktor Niesiobedzki
  */
+@FullPreferences
+@Timeout(20)
 class HostLimitQueueTest {
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().timeout(20 * 1000);
-
-    /**
      * Mock class for tests
      */
     static class Task extends JCSCachedTileLoaderJob<String, CacheEntry> {
Index: test/unit/org/openstreetmap/josm/data/coor/LatLonTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/coor/LatLonTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/coor/LatLonTest.java	(working copy)
@@ -10,10 +10,9 @@
 import java.util.List;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.Bounds;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
@@ -21,15 +20,8 @@
 /**
  * Unit tests for class {@link LatLon}.
  */
+@Projection
 public class LatLonTest {
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projection();
-
     private static final double EPSILON = 1e-6;
 
     /**
Index: test/unit/org/openstreetmap/josm/data/coor/PolarCoorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/coor/PolarCoorTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/coor/PolarCoorTest.java	(working copy)
@@ -6,27 +6,18 @@
 
 import java.text.DecimalFormat;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 
 /**
  * Test the {@link PolarCoor} class.
  */
+@Projection
 class PolarCoorTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projection();
-
-    /**
      * Test {@link PolarCoor#PolarCoor}
      */
     @Test
Index: test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/gpx/GpxDataTest.java	(working copy)
@@ -20,7 +20,6 @@
 
 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.Bounds;
 import org.openstreetmap.josm.data.DataSource;
@@ -30,11 +29,11 @@
 import org.openstreetmap.josm.data.gpx.GpxData.GpxDataChangeListener;
 import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.io.GpxReaderTest;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.tools.ListenerList;
 import org.xml.sax.SAXException;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -41,15 +40,9 @@
 /**
  * Unit tests for class {@link GpxData}.
  */
+@BasicPreferences
+@Projection
 class GpxDataTest {
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projection();
-
     private GpxData data;
 
     /**
Index: test/unit/org/openstreetmap/josm/data/oauth/SignpostAdaptersTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/oauth/SignpostAdaptersTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/oauth/SignpostAdaptersTest.java	(working copy)
@@ -11,28 +11,21 @@
 import java.net.URL;
 
 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.HTTP;
+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;
 
 /**
  * Unit tests for class {@link SignpostAdapters}.
  */
+@HTTP
+@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 {
         return HttpClient.create(new URL("https://www.openstreetmap.org"));
     }
Index: test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/osm/DataSetMergerTest.java	(working copy)
@@ -18,28 +18,19 @@
 import org.junit.jupiter.api.AfterEach;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.coor.LatLon;
-import org.openstreetmap.josm.data.projection.ProjectionRegistry;
-import org.openstreetmap.josm.data.projection.Projections;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.tools.date.DateUtils;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests for class {@link DataSetMerger}.
  */
+@BasicPreferences // Needed for OsmPrimitive
+@Projection
 class DataSetMergerTest {
 
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
     private DataSet my;
     private DataSet their;
 
@@ -52,7 +43,6 @@
         my.setVersion("0.6");
         their = new DataSet();
         their.setVersion("0.6");
-        ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857")); // Mercator
     }
 
     private void runConsistencyTests(DataSet ds) {
Index: test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/osm/DataSetTest.java	(working copy)
@@ -12,7 +12,6 @@
 
 import org.junit.Assert;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.DataSource;
@@ -19,23 +18,14 @@
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.event.DataSourceAddedEvent;
 import org.openstreetmap.josm.data.osm.event.DataSourceRemovedEvent;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests for class {@link DataSet}.
  */
+@BasicPreferences // Needed for OsmPrimitive
 class DataSetTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * Unit test of method {@link DataSet#searchRelations}.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/data/osm/OsmPrimitiveKeyHandlingTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/OsmPrimitiveKeyHandlingTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/osm/OsmPrimitiveKeyHandlingTest.java	(working copy)
@@ -5,10 +5,9 @@
 import static org.junit.jupiter.api.Assertions.assertFalse;
 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.data.coor.LatLon;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 
@@ -16,16 +15,9 @@
  * Some unit test cases for basic tag management on {@link OsmPrimitive}. Uses
  * {@link Node} for the tests, {@link OsmPrimitive} is abstract.
  */
+@BasicPreferences // Needed for OsmPrimitive
 class OsmPrimitiveKeyHandlingTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * test query and get methods on a node withouth keys
      */
     @Test
Index: test/unit/org/openstreetmap/josm/data/osm/TagCollectionTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/TagCollectionTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/osm/TagCollectionTest.java	(working copy)
@@ -17,15 +17,13 @@
 import java.util.stream.Stream;
 
 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.FullPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Tests of {@link TagCollection}.
  * @author Michael Zangl
  */
+@FullPreferences // primitives require prefs
 class TagCollectionTest {
     private final Tag tagA = new Tag("k", "v");
     private final Tag tagB = new Tag("k", "b");
@@ -35,13 +33,6 @@
     private final Tag tagNullKey = new Tag(null, "b");
     private final Tag tagNullValue = new Tag("k2", null);
 
-    /**
-     * We need prefs for using primitives
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
-
     private void assertTagCounts(TagCollection collection, int a, int b, int c, int d) {
         assertEquals(a, collection.getTagOccurrence(tagA));
         assertEquals(b, collection.getTagOccurrence(tagB));
Index: test/unit/org/openstreetmap/josm/data/osm/search/SearchCompilerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/search/SearchCompilerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/osm/search/SearchCompilerTest.java	(working copy)
@@ -19,7 +19,7 @@
 
 import org.junit.Assert;
 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.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -41,11 +41,10 @@
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetType;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
 import org.openstreetmap.josm.gui.tagging.presets.items.Key;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.date.DateUtils;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 import nl.jqno.equalsverifier.Warning;
 
@@ -52,15 +51,9 @@
 /**
  * Unit tests for class {@link SearchCompiler}.
  */
+@BasicPreferences // For OSM Primitives
+@Timeout(30)
 class SearchCompilerTest {
-
-    /**
-     * We need prefs for this. We access preferences when creating OSM primitives.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().timeout(30000);
-
     private static final class SearchContext {
         final DataSet ds = new DataSet();
         final Node n1 = new Node(LatLon.ZERO);
Index: test/unit/org/openstreetmap/josm/data/osm/visitor/MergeSourceBuildingVisitorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/osm/visitor/MergeSourceBuildingVisitorTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/osm/visitor/MergeSourceBuildingVisitorTest.java	(working copy)
@@ -8,7 +8,6 @@
 
 import java.util.Collection;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -18,13 +17,12 @@
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.Way;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests for class {@link MergeSourceBuildingVisitor}.
  */
+@BasicPreferences // Due to OSM primitives
 class MergeSourceBuildingVisitorTest {
 
     protected OsmPrimitive lookupByName(Collection<? extends OsmPrimitive> primitives, String name) {
@@ -35,13 +33,6 @@
                 .findFirst().orElse(null);
     }
 
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
     @Test
     void testNodes() {
         DataSet source = new DataSet();
Index: test/unit/org/openstreetmap/josm/data/preferences/JosmUrlsTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/preferences/JosmUrlsTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/preferences/JosmUrlsTest.java	(working copy)
@@ -3,26 +3,16 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.spi.preferences.Config;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.OsmApiType;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link JosmUrls} class.
  */
+@OsmApiType(apiType = OsmApiType.APIType.DEV)
 class JosmUrlsTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().devAPI();
-
-    /**
      * Unit test of {@link JosmUrls#getBaseUserUrl}.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/data/projection/ProjectionRefTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/projection/ProjectionRefTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/projection/ProjectionRefTest.java	(working copy)
@@ -31,18 +31,21 @@
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.junit.Assert;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.data.Bounds;
 import org.openstreetmap.josm.data.coor.EastNorth;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.gui.preferences.projection.CodeProjectionChoice;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
+import org.openstreetmap.josm.testutils.annotations.ProjectionNadGrids;
 import org.openstreetmap.josm.tools.Pair;
 import org.openstreetmap.josm.tools.PlatformManager;
 
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
 /**
  * Test projections using reference data from external program.
  *
@@ -58,6 +61,10 @@
  * This means the test does not verify our definitions, but the correctness
  * of the algorithm, given a certain definition.
  */
+@Timeout(90)
+@ProjectionNadGrids
+@BasicPreferences
+@HTTP
 class ProjectionRefTest {
 
     private static final String CS2CS_EXE = "cs2cs";
@@ -83,13 +90,6 @@
     static List<String> forcedCodes;
 
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projectionNadGrids().timeout(90_000);
-
-    /**
      * Program entry point.
      * @param args optional comma-separated list of projections to update. If set, only these projections will be updated
      * @throws IOException in case of I/O error
Index: test/unit/org/openstreetmap/josm/data/projection/proj/LonLatTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/projection/proj/LonLatTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/projection/proj/LonLatTest.java	(working copy)
@@ -3,24 +3,13 @@
 
 import static org.junit.jupiter.api.Assertions.assertFalse;
 
-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;
-
 /**
  * Tests for {@link LonLat}.
  */
 class LonLatTest {
     /**
-     * Setup rule.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * Test {@link LonLat#lonIsLinearToEast}
      */
     @Test
Index: test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorTestIT.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorTestIT.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/validation/routines/DomainValidatorTestIT.java	(working copy)
@@ -46,9 +46,8 @@
 import java.util.regex.Matcher;
 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;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
@@ -58,16 +57,9 @@
  *
  * @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
      * Check if the internal TLD table is up to date
      * Check if the internal TLD tables have any spurious entries
Index: test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerTest.java	(working copy)
@@ -17,7 +17,6 @@
 
 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.command.ChangePropertyCommand;
 import org.openstreetmap.josm.command.ChangePropertyKeyCommand;
@@ -42,26 +41,19 @@
 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
 import org.openstreetmap.josm.io.OsmReader;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.Territories;
 import org.openstreetmap.josm.tools.Logging;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * JUnit Test of {@link MapCSSTagChecker}.
  */
+@BasicPreferences
+@Territories
 class MapCSSTagCheckerTest {
-
     /**
      * Setup test.
      */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projection().territories().preferences();
-
-    /**
-     * Setup test.
-     */
     @BeforeEach
     public void setUp() {
         MapCSSTagCheckerAsserts.clear();
Index: test/unit/org/openstreetmap/josm/data/validation/tests/TagCheckerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/data/validation/tests/TagCheckerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/data/validation/tests/TagCheckerTest.java	(working copy)
@@ -13,7 +13,6 @@
 import org.junit.Assert;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmUtils;
@@ -20,22 +19,15 @@
 import org.openstreetmap.josm.data.osm.Tag;
 import org.openstreetmap.josm.data.validation.Severity;
 import org.openstreetmap.josm.data.validation.TestError;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.Presets;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * JUnit Test of {@link TagChecker}.
  */
+@BasicPreferences
+@Presets
 class TagCheckerTest {
-
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules rule = new JOSMTestRules().presets();
-
     List<TestError> test(OsmPrimitive primitive) throws IOException {
         final TagChecker checker = new TagChecker() {
             @Override
Index: test/unit/org/openstreetmap/josm/gui/MainApplicationTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/MainApplicationTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/MainApplicationTest.java	(working copy)
@@ -1,12 +1,12 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.gui;
 
+import static org.junit.Assume.assumeFalse;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.Assume.assumeFalse;
 
 import java.awt.BorderLayout;
 import java.awt.event.KeyEvent;
@@ -27,7 +27,7 @@
 import javax.swing.UIManager;
 
 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;
 import org.openstreetmap.josm.actions.OpenLocationAction;
@@ -42,7 +42,10 @@
 import org.openstreetmap.josm.plugins.PluginListParseException;
 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.OsmApiType;
+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;
@@ -52,16 +55,13 @@
 /**
  * Unit tests of {@link MainApplication} class.
  */
+@Main
+@Projection
+@HTTPS
+@OsmApiType(apiType = OsmApiType.APIType.DEV)
+@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.
      */
     public static void initContentPane() {
Index: test/unit/org/openstreetmap/josm/gui/bbox/SizeButtonTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/bbox/SizeButtonTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/bbox/SizeButtonTest.java	(working copy)
@@ -5,25 +5,17 @@
 import static org.junit.jupiter.api.Assertions.assertFalse;
 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;
 import org.openstreetmap.josm.gui.bbox.SizeButton.AccessibleSizeButton;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link SizeButton} class.
  */
+@BasicPreferences
 class SizeButtonTest {
 
-    /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
 
     /**
      * Unit test of {@link SizeButton#SizeButton}.
Index: test/unit/org/openstreetmap/josm/gui/conflict/pair/nodes/NodeListMergeModelTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/conflict/pair/nodes/NodeListMergeModelTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/conflict/pair/nodes/NodeListMergeModelTest.java	(working copy)
@@ -1,10 +1,10 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.gui.conflict.pair.nodes;
 
-import static org.junit.jupiter.api.Assertions.fail;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.junit.jupiter.api.Assertions.fail;
 
 import java.beans.PropertyChangeEvent;
 import java.beans.PropertyChangeListener;
@@ -19,32 +19,23 @@
 import javax.swing.DefaultListSelectionModel;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.conflict.pair.AbstractListMergeModel;
 import org.openstreetmap.josm.testutils.DatasetFactory;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 import org.openstreetmap.josm.tools.Logging;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link NodeListMergeModel}.
  */
+@BasicPreferences
 class NodeListMergeModelTest {
 
     private final DatasetFactory my = new DatasetFactory();
     private final DatasetFactory their = new DatasetFactory();
 
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
     @SuppressWarnings("unchecked")
     protected List<Node> inspectNodeList(NodeListMergeModel model, String name) {
         try {
Index: test/unit/org/openstreetmap/josm/gui/datatransfer/ClipboardUtilsTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/datatransfer/ClipboardUtilsTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/datatransfer/ClipboardUtilsTest.java	(working copy)
@@ -1,12 +1,12 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.gui.datatransfer;
 
+import static org.junit.Assume.assumeTrue;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertSame;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.Assume.assumeTrue;
 
 import java.awt.GraphicsEnvironment;
 import java.awt.datatransfer.Clipboard;
@@ -16,11 +16,8 @@
 import java.awt.datatransfer.UnsupportedFlavorException;
 import java.io.IOException;
 
-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 net.trajano.commons.testing.UtilityClassTestUtil;
 
 /**
@@ -67,13 +64,6 @@
     }
 
     /**
-     * No dependencies
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * Test {@link ClipboardUtils#getClipboard()}
      */
     @Test
Index: test/unit/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandlerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandlerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/datatransfer/OsmTransferHandlerTest.java	(working copy)
@@ -6,7 +6,6 @@
 
 import java.util.Collections;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.actions.CopyAction;
 import org.openstreetmap.josm.data.coor.LatLon;
@@ -15,21 +14,19 @@
 import org.openstreetmap.josm.data.projection.ProjectionRegistry;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link OsmTransferHandler} class.
  */
+@FullPreferences
+@Projection
+@Main
+@LayerEnvironment
 class OsmTransferHandlerTest {
-    /**
-     * Prefs to use OSM primitives
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().projection().main();
-
     private final OsmTransferHandler transferHandler = new OsmTransferHandler();
 
     /**
Index: test/unit/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferableTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferableTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/datatransfer/PrimitiveTransferableTest.java	(working copy)
@@ -3,8 +3,8 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.awt.datatransfer.DataFlavor;
 import java.awt.datatransfer.UnsupportedFlavorException;
@@ -14,28 +14,19 @@
 import java.util.List;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.NodeData;
 import org.openstreetmap.josm.data.osm.PrimitiveData;
 import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
 import org.openstreetmap.josm.gui.datatransfer.data.TagTransferData;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link PrimitiveTransferable} class.
  */
+@BasicPreferences
 class PrimitiveTransferableTest {
     /**
-     * Prefs to use OSM primitives
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
-
-    /**
      * Test of {@link PrimitiveTransferable#getTransferDataFlavors()} method response order
      */
     @Test
Index: test/unit/org/openstreetmap/josm/gui/datatransfer/RelationMemberTransferableTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/datatransfer/RelationMemberTransferableTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/datatransfer/RelationMemberTransferableTest.java	(working copy)
@@ -3,8 +3,8 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.openstreetmap.josm.gui.datatransfer.RelationMemberTransferable.RELATION_MEMBER_DATA;
 
 import java.awt.datatransfer.DataFlavor;
@@ -13,29 +13,19 @@
 import java.util.Collections;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.PrimitiveData;
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.data.osm.RelationMemberData;
 import org.openstreetmap.josm.gui.datatransfer.data.PrimitiveTransferData;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link RelationMemberTransferable} class.
  */
+@BasicPreferences
 class RelationMemberTransferableTest {
-
     /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * Test of {@link RelationMemberTransferable#getTransferDataFlavors()} method.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/gui/datatransfer/TagTransferableTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/datatransfer/TagTransferableTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/datatransfer/TagTransferableTest.java	(working copy)
@@ -3,8 +3,8 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.awt.datatransfer.DataFlavor;
 import java.awt.datatransfer.UnsupportedFlavorException;
@@ -12,25 +12,13 @@
 import java.util.Map;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.gui.datatransfer.data.TagTransferData;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link TagTransferable} class.
  */
 class TagTransferableTest {
-
     /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * Test of {@link TagTransferable#isDataFlavorSupported} method.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/gui/dialogs/CommandStackDialogTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/dialogs/CommandStackDialogTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/dialogs/CommandStackDialogTest.java	(working copy)
@@ -4,7 +4,6 @@
 import static org.junit.jupiter.api.Assertions.assertFalse;
 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;
 import org.openstreetmap.josm.command.Command;
@@ -13,23 +12,18 @@
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.MapFrame;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link CommandStackDialog} class.
  */
+@FullPreferences
+@Projection
+@Main
 class CommandStackDialogTest {
-
     /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().main().projection().preferences();
-
-    /**
      * Unit test of {@link CommandStackDialog} class - empty case.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/gui/dialogs/ConflictDialogTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/dialogs/ConflictDialogTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/dialogs/ConflictDialogTest.java	(working copy)
@@ -7,7 +7,6 @@
 import java.awt.Color;
 import java.awt.image.BufferedImage;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -18,23 +17,16 @@
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.dialogs.ConflictDialog.ConflictPainter;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link ConflictDialog} class.
  */
+@Main
+@Projection
 class ConflictDialogTest {
-
     /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().main().projection();
-
-    /**
      * Unit test of {@link ConflictDialog#ConflictDialog}.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/gui/dialogs/relation/actions/AbstractRelationEditorActionTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/dialogs/relation/actions/AbstractRelationEditorActionTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/dialogs/relation/actions/AbstractRelationEditorActionTest.java	(working copy)
@@ -7,7 +7,6 @@
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.Relation;
@@ -22,23 +21,17 @@
 import org.openstreetmap.josm.gui.tagging.TagEditorModel;
 import org.openstreetmap.josm.gui.tagging.ac.AutoCompletingTextField;
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetHandler;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.Main;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * This class provides the basic test environment for relation editor actions.
  * @author Michael Zangl
  */
 @Disabled
+@FullPreferences
+@Main
 public abstract class AbstractRelationEditorActionTest {
-    /**
-     * Platform for tooltips.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().main();
-
     protected OsmDataLayer layer;
 
     private SelectionTableModel selectionTableModel;
Index: test/unit/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSorterTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSorterTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/dialogs/relation/sort/RelationSorterTest.java	(working copy)
@@ -10,7 +10,6 @@
 import org.junit.Assert;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
@@ -17,13 +16,14 @@
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.io.IllegalDataException;
 import org.openstreetmap.josm.io.OsmReader;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link RelationSorter} class.
  */
+@FullPreferences
+@Projection // defaults to Mercator projection
 class RelationSorterTest {
 
     private final RelationSorter sorter = new RelationSorter();
@@ -30,13 +30,6 @@
     private DataSet testDataset;
 
     /**
-     * Use Mercator projection
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().projection();
-
-    /**
      * Load the test data set
      * @throws IllegalDataException if an error was found while parsing the data
      * @throws IOException in case of I/O error
Index: test/unit/org/openstreetmap/josm/gui/help/HelpBrowserTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/help/HelpBrowserTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/help/HelpBrowserTest.java	(working copy)
@@ -9,15 +9,14 @@
 import javax.swing.JOptionPane;
 
 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;
 import org.openstreetmap.josm.tools.PlatformHook;
 import org.openstreetmap.josm.tools.PlatformManager;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import mockit.Expectations;
 import mockit.Injectable;
 import mockit.Mocked;
@@ -25,6 +24,8 @@
 /**
  * Unit tests of {@link HelpBrowser} class.
  */
+@FullPreferences
+@HTTPS
 class HelpBrowserTest {
 
     static final String URL_1 = "https://josm.openstreetmap.de/wiki/Help";
@@ -31,13 +32,6 @@
     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() {
         return new IHelpBrowser() {
 
Index: test/unit/org/openstreetmap/josm/gui/help/HyperlinkHandlerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/help/HyperlinkHandlerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/help/HyperlinkHandlerTest.java	(working copy)
@@ -10,26 +10,16 @@
 import javax.swing.text.html.HTMLDocument;
 import javax.swing.text.html.HTMLEditorKit;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.gui.widgets.JosmEditorPane;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link HyperlinkHandler} class.
  */
+@BasicPreferences // Needed for OsmPrimitive
 class HyperlinkHandlerTest {
-
     /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
-
-    /**
      * Non-regression test for ticket <a href="https://josm.openstreetmap.de/ticket/17338">#17338</a>.
      * @throws Exception if an error occurs
      */
Index: test/unit/org/openstreetmap/josm/gui/history/HistoryBrowserModelTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/history/HistoryBrowserModelTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/history/HistoryBrowserModelTest.java	(working copy)
@@ -1,6 +1,7 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.gui.history;
 
+import static org.junit.jupiter.api.Assertions.assertDoesNotThrow;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
@@ -7,8 +8,8 @@
 import static org.junit.jupiter.api.Assertions.assertNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
@@ -16,23 +17,17 @@
 import org.openstreetmap.josm.data.osm.history.History;
 import org.openstreetmap.josm.data.osm.history.HistoryDataSet;
 import org.openstreetmap.josm.data.osm.history.HistoryNode;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.OsmApiType;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link HistoryBrowserModel} class.
  */
+@FullPreferences
+@OsmApiType(apiType = OsmApiType.APIType.DEV)
+@Timeout(30)
 class HistoryBrowserModelTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().devAPI().timeout(30000);
-
-    /**
      * Test for {@link HistoryBrowserModel#HistoryBrowserModel}.
      */
     @Test
@@ -98,10 +93,10 @@
     void testSetPointsInTimeNullHistory() {
         HistoryBrowserModel model = new HistoryBrowserModel();
         VersionTableModel tableModel = model.getVersionTableModel();
-        tableModel.setValueAt(false, 0, 0); // code coverage
-        tableModel.setValueAt(true, 0, 1);  // reference point
-        tableModel.setValueAt(true, 1, 2);  // current point
-        tableModel.setValueAt(true, 3, 3);  // code coverage
+        assertDoesNotThrow(() -> tableModel.setValueAt(false, 0, 0)); // code coverage
+        assertDoesNotThrow(() -> tableModel.setValueAt(true, 0, 1));  // reference point
+        assertDoesNotThrow(() -> tableModel.setValueAt(true, 1, 2));  // current point
+        assertDoesNotThrow(() -> tableModel.setValueAt(true, 3, 3));  // code coverage
     }
 
     /**
Index: test/unit/org/openstreetmap/josm/gui/history/HistoryLoadTaskTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/history/HistoryLoadTaskTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/history/HistoryLoadTaskTest.java	(working copy)
@@ -6,8 +6,8 @@
 import java.io.IOException;
 import java.io.InputStream;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
@@ -21,24 +21,18 @@
 import org.openstreetmap.josm.io.OsmHistoryReader;
 import org.openstreetmap.josm.io.OsmServerHistoryReader;
 import org.openstreetmap.josm.io.OsmTransferException;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.OsmApiType;
 import org.xml.sax.SAXException;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link HistoryLoadTask} class.
  */
+@FullPreferences
+@OsmApiType(apiType = OsmApiType.APIType.DEV)
+@Timeout(20)
 class HistoryLoadTaskTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().devAPI().timeout(20000);
-
-    /**
      * Unit test of {@link HistoryLoadTask#getLoadingMessage}
      */
     @Test
Index: test/unit/org/openstreetmap/josm/gui/io/DownloadOpenChangesetsTaskTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/io/DownloadOpenChangesetsTaskTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/io/DownloadOpenChangesetsTaskTest.java	(working copy)
@@ -13,17 +13,16 @@
 import javax.swing.JOptionPane;
 import javax.swing.JPanel;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.UserIdentityManager;
 import org.openstreetmap.josm.gui.oauth.OAuthAuthorizationWizard;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.OsmApiType;
 import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
 import org.openstreetmap.josm.testutils.mockers.WindowMocker;
 import org.openstreetmap.josm.tools.UserCancelException;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import mockit.Invocation;
 import mockit.Mock;
 import mockit.MockUp;
@@ -31,16 +30,10 @@
 /**
  * Unit tests of {@link DownloadOpenChangesetsTask} class.
  */
+@FullPreferences
+@OsmApiType(apiType = OsmApiType.APIType.DEV)
 class DownloadOpenChangesetsTaskTest {
-
     /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().devAPI();
-
-    /**
      * OAuth wizard mocker.
      */
     public static class OAuthWizardMocker extends MockUp<OAuthAuthorizationWizard> {
Index: test/unit/org/openstreetmap/josm/gui/io/DownloadPrimitivesTaskTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/io/DownloadPrimitivesTaskTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/io/DownloadPrimitivesTaskTest.java	(working copy)
@@ -7,29 +7,23 @@
 
 import java.util.Arrays;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.SimplePrimitiveId;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.OsmApiType;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link DownloadPrimitivesTask} class.
  */
+@FullPreferences
+@OsmApiType(apiType = OsmApiType.APIType.DEV)
+@Timeout(20)
 class DownloadPrimitivesTaskTest {
-
     /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().devAPI().timeout(20000);
-
-    /**
      * Test of {@link DownloadPrimitivesTask} class.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/gui/layer/LayerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/layer/LayerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/layer/LayerTest.java	(working copy)
@@ -11,24 +11,19 @@
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.projection.ProjectionRegistry;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.LayerEnvironment;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Test of the base {@link Layer} class
  * @author Michael Zangl
  */
+@BasicPreferences
+@Projection
+@LayerEnvironment
 class LayerTest {
-    /**
-     * We need projection
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().projection();
-
     private Layer testLayer;
 
     /**
Index: test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/layer/OsmDataLayerTest.java	(working copy)
@@ -16,7 +16,6 @@
 
 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.actions.ExpertToggleAction;
 import org.openstreetmap.josm.data.Bounds;
@@ -35,25 +34,20 @@
 import org.openstreetmap.josm.gui.datatransfer.ClipboardUtils;
 import org.openstreetmap.josm.io.IllegalDataException;
 import org.openstreetmap.josm.io.OsmReader;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.testutils.mockers.ExtendedDialogMocker;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.date.DateUtils;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link OsmDataLayer} class.
  */
+@Projection
+@Main
+@BasicPreferences // for primitives
 class OsmDataLayerTest {
-
-    /**
-     * Setup tests
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projection().main();
-
     private DataSet ds;
     private OsmDataLayer layer;
 
Index: test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ChildOrParentSelectorTest.java	(working copy)
@@ -12,7 +12,6 @@
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Disabled;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.Node;
@@ -24,25 +23,17 @@
 import org.openstreetmap.josm.gui.mappaint.MultiCascade;
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.ChildOrParentSelector;
 import org.openstreetmap.josm.io.OsmReader;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link ChildOrParentSelector}.
  */
+@Projection
 class ChildOrParentSelectorTest {
 
     private DataSet ds;
 
     /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projection();
-
-    /**
      * Setup test
      */
     @BeforeEach
Index: test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/ConditionTest.java	(working copy)
@@ -7,7 +7,6 @@
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.OsmUtils;
 import org.openstreetmap.josm.gui.mappaint.Environment;
@@ -15,21 +14,14 @@
 import org.openstreetmap.josm.gui.mappaint.mapcss.Condition.ToTagConvertable;
 import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.Op;
 import org.openstreetmap.josm.gui.mappaint.mapcss.ConditionFactory.SimpleKeyValueCondition;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * This test universally tests all {@link Condition}s.
  * @author Michael Zangl
  */
+@FullPreferences // Nodes require preferences
 class ConditionTest {
-    /**
-     * We need prefs for nodes.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
     private OsmPrimitive node0;
     private OsmPrimitive node1;
     private OsmPrimitive node2;
Index: test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/mappaint/mapcss/MapCSSParserTest.java	(working copy)
@@ -16,7 +16,6 @@
 
 import org.junit.Assert;
 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.osm.DataSet;
@@ -38,14 +37,13 @@
 import org.openstreetmap.josm.gui.mappaint.mapcss.Selector.ChildOrParentSelector;
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.MapCSSParser;
 import org.openstreetmap.josm.gui.mappaint.mapcss.parsergen.ParseException;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.tools.ColorHelper;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link MapCSSParser}.
  */
+@Projection
 class MapCSSParserTest {
 
     protected static Environment getEnvironment(String key, String value) {
@@ -56,13 +54,6 @@
         return new MapCSSParser(new StringReader(stringToParse));
     }
 
-    /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().projection();
-
     @Test
     void testDeclarations() throws Exception {
         getParser("{ opacity: 0.5; color: rgb(1.0, 0.0, 0.0); }").declaration();
Index: test/unit/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreferenceTestIT.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreferenceTestIT.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreferenceTestIT.java	(working copy)
@@ -25,7 +25,7 @@
 import org.apache.commons.jcs3.access.CacheAccess;
 import org.junit.jupiter.api.AfterAll;
 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.Arguments;
 import org.junit.jupiter.params.provider.MethodSource;
@@ -59,17 +59,30 @@
 import org.openstreetmap.josm.data.projection.Projections;
 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.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.I18n;
+import org.openstreetmap.josm.testutils.annotations.IntegrationTest;
+import org.openstreetmap.josm.testutils.annotations.ProjectionNadGrids;
+import org.openstreetmap.josm.testutils.annotations.SlowTest;
 import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.HttpClient.Response;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Integration tests of {@link ImageryPreference} class.
  */
+@HTTP
+@HTTPS
+@I18n
+@FullPreferences
+@org.openstreetmap.josm.testutils.annotations.Projection
+@ProjectionNadGrids
+@Timeout(value = 40, unit = TimeUnit.MINUTES)
+@IntegrationTest
+@SlowTest
 public class ImageryPreferenceTestIT {
 
     private static final String ERROR_SEP = " -> ";
@@ -76,14 +89,6 @@
     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 */
     private final Map<String, Map<ImageryInfo, List<String>>> errors = Collections.synchronizedMap(new TreeMap<>());
     private final Map<String, Map<ImageryInfo, List<String>>> ignoredErrors = Collections.synchronizedMap(new TreeMap<>());
Index: test/unit/org/openstreetmap/josm/gui/preferences/map/MapPaintPreferenceTestIT.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/preferences/map/MapPaintPreferenceTestIT.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/preferences/map/MapPaintPreferenceTestIT.java	(working copy)
@@ -8,9 +8,10 @@
 import java.io.IOException;
 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;
 import org.openstreetmap.josm.TestUtils;
@@ -23,24 +24,20 @@
 import org.openstreetmap.josm.gui.mappaint.mapcss.Instruction.AssignmentInstruction;
 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.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.IntegrationTest;
+import org.openstreetmap.josm.testutils.annotations.SlowTest;
 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)
+@IntegrationTest
+@SlowTest
 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
      * @throws IOException in case of I/O error
      */
Index: test/unit/org/openstreetmap/josm/gui/preferences/map/TaggingPresetPreferenceTestIT.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/preferences/map/TaggingPresetPreferenceTestIT.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/preferences/map/TaggingPresetPreferenceTestIT.java	(working copy)
@@ -14,9 +14,10 @@
 import java.util.List;
 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;
 import org.openstreetmap.josm.TestUtils;
@@ -26,7 +27,10 @@
 import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetsTest;
 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.HTTP;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.IntegrationTest;
+import org.openstreetmap.josm.testutils.annotations.SlowTest;
 import org.openstreetmap.josm.tools.HttpClient;
 import org.openstreetmap.josm.tools.HttpClient.Response;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -33,21 +37,16 @@
 import org.openstreetmap.josm.tools.Logging;
 import org.xml.sax.SAXException;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Integration tests of {@link TaggingPresetPreference} class.
  */
+@HTTP
+@HTTPS
+@Timeout(value = 20, unit = TimeUnit.MINUTES)
+@IntegrationTest
+@SlowTest
 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
      * @throws IOException in case of I/O error
      */
Index: test/unit/org/openstreetmap/josm/gui/preferences/validator/ValidatorTagCheckerRulesPreferenceTestIT.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/preferences/validator/ValidatorTagCheckerRulesPreferenceTestIT.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/preferences/validator/ValidatorTagCheckerRulesPreferenceTestIT.java	(working copy)
@@ -9,27 +9,23 @@
 import java.util.Collection;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
+import org.junit.jupiter.api.Timeout;
 import org.openstreetmap.josm.data.preferences.sources.ExtendedSourceEntry;
 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker;
 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.ParseResult;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.IntegrationTest;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Integration tests of {@link ValidatorTagCheckerRulesPreference} class.
  */
+@HTTP
+@HTTPS
+@Timeout(20)
+@IntegrationTest
 class ValidatorTagCheckerRulesPreferenceTestIT {
-
     /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().https().timeout(20_000);
-
-    /**
      * Test that available tag checker rules are valid.
      * @throws Exception in case of error
      */
Index: test/unit/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetReaderTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetReaderTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/gui/tagging/presets/TaggingPresetReaderTest.java	(working copy)
@@ -14,28 +14,18 @@
 
 import org.junit.Assert;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.gui.tagging.presets.items.Check;
 import org.openstreetmap.josm.gui.tagging.presets.items.Key;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 import org.xml.sax.SAXException;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link TaggingPresetReader} class.
  */
+@BasicPreferences
 class TaggingPresetReaderTest {
-
     /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * #8954 - last checkbox in the preset is not added
      * @throws SAXException if any XML error occurs
      * @throws IOException if any I/O error occurs
Index: test/unit/org/openstreetmap/josm/io/CertificateAmendmentTestIT.java
===================================================================
--- test/unit/org/openstreetmap/josm/io/CertificateAmendmentTestIT.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/io/CertificateAmendmentTestIT.java	(working copy)
@@ -1,8 +1,9 @@
 // License: GPL. For details, see LICENSE file.
 package org.openstreetmap.josm.io;
 
-import static org.junit.Assume.assumeFalse;
 
+import static org.junit.jupiter.api.Assumptions.assumeFalse;
+
 import java.io.IOException;
 import java.net.URL;
 import java.net.URLConnection;
@@ -11,27 +12,25 @@
 
 import javax.net.ssl.SSLHandshakeException;
 
-import org.junit.Assert;
-import org.junit.ClassRule;
+import org.junit.jupiter.api.Assertions;
 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 org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.IntegrationTest;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Integration tests of {@link CertificateAmendment} class.
  */
+@IntegrationTest
+@Timeout(20)
+@HTTP
+@HTTPS
+@BasicPreferences
 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<>();
 
     /**
@@ -110,7 +109,7 @@
         String error = "Expected error: " + url;
         assumeFalse(errorsToIgnore.contains(error));
         if (!shouldWork) {
-            Assert.fail(error);
+            Assertions.fail(error);
         }
     }
 }
Index: test/unit/org/openstreetmap/josm/io/DiffResultProcessorTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/io/DiffResultProcessorTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/io/DiffResultProcessorTest.java	(working copy)
@@ -9,7 +9,6 @@
 import java.util.Collections;
 import java.util.Set;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.coor.LatLon;
 import org.openstreetmap.josm.data.osm.Changeset;
@@ -21,24 +20,15 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.io.DiffResultProcessor.DiffResultEntry;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.XmlParsingException;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link DiffResultProcessor}
  */
+@BasicPreferences
 class DiffResultProcessorTest {
-
-    /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
     private static void shouldFail(String s) {
         try {
             new DiffResultProcessor(null).parse(s, NullProgressMonitor.INSTANCE);
Index: test/unit/org/openstreetmap/josm/io/NetworkManagerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/io/NetworkManagerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/io/NetworkManagerTest.java	(working copy)
@@ -9,25 +9,21 @@
 import java.net.URL;
 import java.util.Map;
 
-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.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.OsmApiType;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link NetworkManager} class.
  */
+@HTTPS
+@Main
+@Projection
+@OsmApiType(apiType = OsmApiType.APIType.DEV)
 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},
      *              {@link NetworkManager#getNetworkErrors} and
      *              {@link NetworkManager#clearNetworkErrors}.
Index: test/unit/org/openstreetmap/josm/io/OsmReaderTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/io/OsmReaderTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/io/OsmReaderTest.java	(working copy)
@@ -17,7 +17,6 @@
 import java.nio.file.Paths;
 import java.util.Arrays;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.osm.DataSet;
@@ -26,22 +25,13 @@
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.io.OsmReader.Options;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link OsmReader} class.
  */
+@BasicPreferences // For primitives
 class OsmReaderTest {
-
-    /**
-     * Setup rule
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
     private static Options[][] options() {
         return new Options[][]{
                 new Options[]{},
Index: test/unit/org/openstreetmap/josm/io/auth/CredentialsAgentExceptionTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/io/auth/CredentialsAgentExceptionTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/io/auth/CredentialsAgentExceptionTest.java	(working copy)
@@ -3,25 +3,13 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-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;
-
 /**
  * Unit tests of {@link CredentialsAgentException} class.
  */
 class CredentialsAgentExceptionTest {
-
     /**
-     * Setup test
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * Unit test of {@code CredentialsAgentException#CredentialsAgentException}
      */
     @Test
Index: test/unit/org/openstreetmap/josm/io/remotecontrol/handler/AddNodeHandlerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/io/remotecontrol/handler/AddNodeHandlerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/io/remotecontrol/handler/AddNodeHandlerTest.java	(working copy)
@@ -5,27 +5,22 @@
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertThrows;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.io.remotecontrol.handler.RequestHandler.RequestHandlerBadRequestException;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.AssertionsInEDT;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link AddNodeHandler} class.
  */
+@Main
+@Projection
+@AssertionsInEDT
 class AddNodeHandlerTest {
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().main().assertionsInEDT().projection();
-
     private static AddNodeHandler newHandler(String url) throws RequestHandlerBadRequestException {
         AddNodeHandler req = new AddNodeHandler();
         if (url != null)
Index: test/unit/org/openstreetmap/josm/plugins/PluginExceptionTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/plugins/PluginExceptionTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/plugins/PluginExceptionTest.java	(working copy)
@@ -3,25 +3,13 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 
-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;
-
 /**
  * Unit tests of {@link PluginException} class.
  */
 class PluginExceptionTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * Unit test of {@link PluginException#PluginException}.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/plugins/PluginHandlerTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/plugins/PluginHandlerTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/plugins/PluginHandlerTest.java	(working copy)
@@ -16,7 +16,6 @@
 import javax.swing.JOptionPane;
 import javax.swing.JScrollPane;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.gui.MainApplication;
@@ -24,26 +23,18 @@
 import org.openstreetmap.josm.gui.widgets.JosmTextArea;
 import org.openstreetmap.josm.plugins.PluginHandler.DeprecatedPlugin;
 import org.openstreetmap.josm.plugins.PluginHandler.PluginInformationAction;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
 import org.openstreetmap.josm.testutils.mockers.HelpAwareOptionPaneMocker;
 import org.openstreetmap.josm.testutils.mockers.JOptionPaneSimpleMocker;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import nl.jqno.equalsverifier.EqualsVerifier;
 
 /**
  * Unit tests of {@link PluginHandler} class.
  */
+@BasicPreferences
 class PluginHandlerTest {
-
     /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules();
-
-    /**
      * Unit test of methods {@link DeprecatedPlugin#equals} and {@link DeprecatedPlugin#hashCode}.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java
===================================================================
--- test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/plugins/PluginHandlerTestIT.java	(working copy)
@@ -17,12 +17,13 @@
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
+import java.util.concurrent.TimeUnit;
 import java.util.function.Consumer;
 import java.util.stream.Collectors;
 
 import org.junit.jupiter.api.BeforeAll;
 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.data.Preferences;
 import org.openstreetmap.josm.data.gpx.GpxData;
@@ -33,26 +34,28 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 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.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.HTTP;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.IntegrationTest;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 import org.openstreetmap.josm.tools.Destroyable;
 import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Integration tests of {@link PluginHandler} class.
  */
+@FullPreferences
+@Projection
+@HTTP
+@HTTPS
+@Main
+@Timeout(value = 10, unit = TimeUnit.MINUTES)
+@IntegrationTest
 public class PluginHandlerTestIT {
-
     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().main().projection().preferences().https()
-            .timeout(10 * 60 * 1000);
 
     /**
      * Setup test
Index: test/unit/org/openstreetmap/josm/spi/lifecycle/LifecycleTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/spi/lifecycle/LifecycleTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/spi/lifecycle/LifecycleTest.java	(working copy)
@@ -4,24 +4,20 @@
 import static org.junit.jupiter.api.Assertions.assertFalse;
 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 org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.OsmApiType;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link Lifecycle} class.
  */
+@HTTPS
+@Main
+@Projection
+@OsmApiType(apiType = OsmApiType.APIType.DEV)
 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 {
 
         boolean updated;
Index: test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java	(working copy)
@@ -60,6 +60,15 @@
 import org.openstreetmap.josm.io.OsmTransferCanceledException;
 import org.openstreetmap.josm.spi.preferences.Config;
 import org.openstreetmap.josm.spi.preferences.Setting;
+import org.openstreetmap.josm.testutils.annotations.AssertionsInEDT;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.HTTPS;
+import org.openstreetmap.josm.testutils.annotations.JosmHome;
+import org.openstreetmap.josm.testutils.annotations.Main;
+import org.openstreetmap.josm.testutils.annotations.OsmApiType;
+import org.openstreetmap.josm.testutils.annotations.Presets;
+import org.openstreetmap.josm.testutils.annotations.Projection;
+import org.openstreetmap.josm.testutils.annotations.ProjectionNadGrids;
 import org.openstreetmap.josm.testutils.mockers.EDTAssertionMocker;
 import org.openstreetmap.josm.testutils.mockers.WindowlessMapViewStateMocker;
 import org.openstreetmap.josm.testutils.mockers.WindowlessNavigatableComponentMocker;
@@ -134,7 +143,9 @@
     /**
      * Enable the use of default preferences.
      * @return this instance, for easy chaining
+     * @deprecated Use {@link FullPreferences} instead.
      */
+    @Deprecated
     public JOSMTestRules preferences() {
         josmHome();
         usePreferences = true;
@@ -144,7 +155,9 @@
     /**
      * Set JOSM home to a valid, empty directory.
      * @return this instance, for easy chaining
+     * @deprecated Use {@link JosmHome} instead.
      */
+    @Deprecated
     private JOSMTestRules josmHome() {
         josmHome = new TemporaryFolder();
         return this;
@@ -153,7 +166,9 @@
     /**
      * Enables the i18n module for this test in english.
      * @return this instance, for easy chaining
+     * @deprecated Use {org.openstreetmap.josm.testutils.annotations.I18n} instead.
      */
+    @Deprecated
     public JOSMTestRules i18n() {
         return i18n("en");
     }
@@ -162,7 +177,9 @@
      * Enables the i18n module for this test.
      * @param language The language to use.
      * @return this instance, for easy chaining
+     * @deprecated Use {org.openstreetmap.josm.testutils.annotations.I18n} instead.
      */
+    @Deprecated
     public JOSMTestRules i18n(String language) {
         i18n = language;
         return this;
@@ -172,7 +189,9 @@
      * Mock this test's assumed JOSM version (as reported by {@link Version}).
      * @param revisionProperties mock contents of JOSM's {@code REVISION} properties file
      * @return this instance, for easy chaining
+     * @deprecated Use {@link OverrideAssumeRevision} instead.
      */
+    @Deprecated
     public JOSMTestRules assumeRevision(final String revisionProperties) {
         this.assumeRevisionString = revisionProperties;
         return this;
@@ -181,7 +200,9 @@
     /**
      * Enable the dev.openstreetmap.org API for this test.
      * @return this instance, for easy chaining
+     * @deprecated Use {@link OsmApiType} instead.
      */
+    @Deprecated
     public JOSMTestRules devAPI() {
         preferences();
         useAPI = APIType.DEV;
@@ -191,7 +212,9 @@
     /**
      * Use the {@link FakeOsmApi} for testing.
      * @return this instance, for easy chaining
+     * @deprecated Use {@link OsmApiType} instead.
      */
+    @Deprecated
     public JOSMTestRules fakeAPI() {
         useAPI = APIType.FAKE;
         return this;
@@ -200,7 +223,9 @@
     /**
      * Set up default projection (Mercator)
      * @return this instance, for easy chaining
+     * @deprecated Use {@link Projection} instead.
      */
+    @Deprecated
     public JOSMTestRules projection() {
         useProjection = true;
         return this;
@@ -209,7 +234,9 @@
     /**
      * Set up loading of NTV2 grit shift files to support projections that need them.
      * @return this instance, for easy chaining
+     * @deprecated Use {@link ProjectionNadGrids} instead.
      */
+    @Deprecated
     public JOSMTestRules projectionNadGrids() {
         useProjectionNadGrids = true;
         return this;
@@ -218,7 +245,9 @@
     /**
      * Set up HTTPS certificates
      * @return this instance, for easy chaining
+     * @deprecated Use {@link HTTPS} instead.
      */
+    @Deprecated
     public JOSMTestRules https() {
         useHttps = true;
         return this;
@@ -257,7 +286,9 @@
      * Use presets in this test.
      * @return this instance, for easy chaining
      * @since 12568
+     * @deprecated Use {@link Presets} instead.
      */
+    @Deprecated
     public JOSMTestRules presets() {
         preferences();
         usePresets = true;
@@ -268,7 +299,9 @@
      * Use boundaries dataset in this test.
      * @return this instance, for easy chaining
      * @since 12545
+     * @deprecated Use {@link org.openstreetmap.josm.testutils.annotations.Territories} instead.
      */
+    @Deprecated
     public JOSMTestRules territories() {
         territories = true;
         return this;
@@ -299,7 +332,9 @@
     /**
      * Re-raise AssertionErrors thrown in the EDT where they would have normally been swallowed.
      * @return this instance, for easy chaining
+     * @deprecated Use {@link AssertionsInEDT} instead
      */
+    @Deprecated
     public JOSMTestRules assertionsInEDT() {
         return this.assertionsInEDT(EDTAssertionMocker::new);
     }
@@ -309,7 +344,9 @@
      * @param edtAssertionMockingRunnable Runnable for initializing this functionality
      *
      * @return this instance, for easy chaining
+     * @deprecated Use {@link AssertionsInEDT} instead
      */
+    @Deprecated
     public JOSMTestRules assertionsInEDT(final Runnable edtAssertionMockingRunnable) {
         this.edtAssertionMockingRunnable = edtAssertionMockingRunnable;
         return this;
@@ -351,7 +388,9 @@
      *         global variables in this test.
      * @return this instance, for easy chaining
      * @since 12557
+     * @deprecated Use {@link Main} instead
      */
+    @Deprecated
     public JOSMTestRules main() {
         return this.main(
             WindowlessMapViewStateMocker::new,
@@ -368,7 +407,9 @@
      *        of {@link org.openstreetmap.josm.gui.NavigatableComponent}, null to skip.
      *
      * @return this instance, for easy chaining
+     * @deprecated Use {@link Main} instead
      */
+    @Deprecated
     public JOSMTestRules main(
         final Runnable mapViewStateMockingRunnable,
         final Runnable navigableComponentMockingRunnable
@@ -397,8 +438,11 @@
         return this;
     }
 
-    private static class MockVersion extends Version {
-        MockVersion(final String propertiesString) {
+    /*
+     * Mock the JOSM version. This should only be used in JOSM Core test extensions.
+     */
+    public static class MockVersion extends Version {
+        public MockVersion(final String propertiesString) {
             super.initFromRevisionInfo(
                 new ByteArrayInputStream(propertiesString.getBytes(StandardCharsets.UTF_8))
             );
@@ -409,7 +453,7 @@
     public Statement apply(Statement base, Description description) {
         // First process any Override* annotations for per-test overrides.
         // The following only work because "option" methods modify JOSMTestRules in-place
-        final OverrideAssumeRevision overrideAssumeRevision = description.getAnnotation(OverrideAssumeRevision.class);
+        OverrideAssumeRevision overrideAssumeRevision = description.getAnnotation(OverrideAssumeRevision.class);
         if (overrideAssumeRevision != null) {
             this.assumeRevision(overrideAssumeRevision.value());
         }
@@ -790,10 +834,13 @@
     /**
      * Override this test's assumed JOSM version (as reported by {@link Version}).
      * @see JOSMTestRules#assumeRevision(String)
+     * @deprecated Use {@link org.openstreetmap.josm.testutils.annotations.AssumeRevision}
+     *             when using JUnit 5 Extensions.
      */
     @Documented
     @Retention(RetentionPolicy.RUNTIME)
     @Target(ElementType.METHOD)
+    @Deprecated
     public @interface OverrideAssumeRevision {
         /**
          * Returns overridden assumed JOSM version.
Index: test/unit/org/openstreetmap/josm/testutils/annotations/AnnotationUtils.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/AnnotationUtils.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/AnnotationUtils.java	(working copy)
@@ -0,0 +1,57 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Modifier;
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.support.AnnotationSupport;
+
+/**
+ * Useful methods for annotation extensions
+ * @author Taylor Smock
+ * @since xxx
+ */
+final class AnnotationUtils {
+    private AnnotationUtils() {
+        // Utils class
+    }
+
+    /**
+     * Find the first parent annotation
+     * @param <T> The annotation to find
+     * @param context The context to search
+     * @param annotation The annotation to find
+     * @return See {@link AnnotationSupport#findAnnotation}
+     */
+    public static <T extends Annotation> Optional<T> findFirstParentAnnotation(ExtensionContext context, Class<T> annotation) {
+        ExtensionContext current = context;
+        do {
+            Optional<T> foundAnnotation = AnnotationSupport.findAnnotation(current.getElement(), annotation);
+            if (foundAnnotation.isPresent()) {
+                return foundAnnotation;
+            }
+            current = current.getParent().orElse(null);
+        } while (current != null);
+        return Optional.empty();
+    }
+
+    /**
+     * Reset a static class (all static fields are unset). If they are initialized as part of a static block, please be aware of NPEs.
+     * @param clazz The class to reset
+     * @throws ReflectiveOperationException If reflection doesn't work, for whatever reason.
+     */
+    public static void resetStaticClass(Class<?> clazz) throws ReflectiveOperationException {
+        for (Field field : clazz.getDeclaredFields()) {
+            // Only reset static fields, but not final static fields
+            if ((field.getModifiers() & Modifier.STATIC) != 0 && (field.getModifiers() & Modifier.FINAL) == 0) {
+                if (!field.isAccessible()) {
+                    field.setAccessible(true);
+                }
+                field.set(null, null);
+            }
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/AssertionsInEDT.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/AssertionsInEDT.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/AssertionsInEDT.java	(working copy)
@@ -0,0 +1,50 @@
+// 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.Retention;
+import java.lang.annotation.Target;
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.BeforeEachCallback;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.support.AnnotationSupport;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.mockers.EDTAssertionMocker;
+
+import mockit.MockUp;
+
+/**
+ * Raise assertions in the EDT.
+ * @author Taylor Smock
+ * @see JOSMTestRules#assertionsInEDT
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+public @interface AssertionsInEDT {
+    /**
+     * Get the mocker to use for the EDT
+     * @return The mock
+     */
+    Class<? extends MockUp<?>> value() default EDTAssertionMocker.class;
+    /**
+     * Initialize the mocker for the EDT
+     * @author Taylor Smock
+     *
+     */
+    class AssertionsInEDTExtension implements BeforeEachCallback {
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            Optional<AssertionsInEDT> annotation = AnnotationSupport.findAnnotation(context.getElement(), AssertionsInEDT.class);
+            if (annotation.isPresent()) {
+                Class<? extends MockUp<?>> clazz = annotation.get().value();
+                clazz.getConstructor().newInstance();
+            }
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/AssumeRevision.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/AssumeRevision.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/AssumeRevision.java	(working copy)
@@ -0,0 +1,67 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.AfterEachCallback;
+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.ExtensionContext.Namespace;
+import org.junit.jupiter.api.extension.ExtensionContext.Store;
+import org.junit.platform.commons.support.AnnotationSupport;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.Version;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+
+/**
+ * Override this test's assumed JOSM version (as reported by {@link Version}).
+ * @author Taylor Smock
+ * @see JOSMTestRules#assumeRevision(String)
+ * @since xxx
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+@ExtendWith(AssumeRevision.AssumeRevisionExtension.class)
+public @interface AssumeRevision {
+    /**
+     * Returns overridden assumed JOSM version.
+     * @return overridden assumed JOSM version
+     */
+    String value();
+
+    /**
+     * Override the JOSM revision information. Use {@link AssumeRevision} instead of directly using this extension.
+     * @author Taylor Smock
+     * @since xxx
+     */
+    class AssumeRevisionExtension implements BeforeEachCallback, AfterEachCallback {
+
+        @Override
+        public void afterEach(ExtensionContext context) throws Exception {
+            Store store = context.getStore(Namespace.create(AssumeRevisionExtension.class));
+            String originalVersion = store.getOrDefault(store, String.class, null);
+            if (originalVersion != null) {
+                TestUtils.setPrivateStaticField(Version.class, "instance", originalVersion);
+            }
+        }
+
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            Optional<AssumeRevision> annotation = AnnotationSupport.findAnnotation(context.getElement(), AssumeRevision.class);
+            if (annotation.isPresent()) {
+                Store store = context.getStore(Namespace.create(AssumeRevisionExtension.class));
+                store.put("originalVersion", Version.getInstance());
+                final Version replacementVersion = new JOSMTestRules.MockVersion(annotation.get().value());
+                TestUtils.setPrivateStaticField(Version.class, "instance", replacementVersion);
+            }
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/BasicPreferences.java	(working copy)
@@ -0,0 +1,53 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.data.preferences.JosmBaseDirectories;
+import org.openstreetmap.josm.data.preferences.JosmUrls;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Allow tests to use JOSM preferences (see {@link JOSMTestRules#preferences()})
+ * @author Taylor Smock
+ * @see FullPreferences
+ * @since xxx
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@ExtendWith(BasicPreferences.BasicPreferencesExtension.class)
+public @interface BasicPreferences {
+
+    /**
+     * Initialize basic preferences. This is often more than enough for basic tests.
+     * @author Taylor Smock
+     *
+     */
+    class BasicPreferencesExtension implements BeforeAllCallback, AfterAllCallback {
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            AnnotationUtils.resetStaticClass(Config.class);
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            Preferences pref = Preferences.main();
+            Config.setPreferencesInstance(pref);
+            Config.setBaseDirectoriesProvider(JosmBaseDirectories.getInstance());
+            Config.setUrlsProvider(JosmUrls.getInstance());
+            context.getStore(Namespace.create(BasicPreferencesExtension.class)).put("preferences", pref);
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/DeleteCommandCallback.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/DeleteCommandCallback.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/DeleteCommandCallback.java	(working copy)
@@ -0,0 +1,44 @@
+// 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.Retention;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.openstreetmap.josm.actions.DeleteAction;
+import org.openstreetmap.josm.command.DeleteCommand;
+
+/**
+ * Initialize the DeleteCommand callback with the default callback.
+ * @author Taylor Smock
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+@ExtendWith(DeleteCommandCallback.DeleteCommandCallbackExtension.class)
+public @interface DeleteCommandCallback {
+    /**
+     * Initialize and reset the DeleteCommand callback
+     * @author Taylor Smock
+     *
+     */
+    class DeleteCommandCallbackExtension implements BeforeAllCallback, AfterAllCallback {
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            AnnotationUtils.resetStaticClass(DeleteCommand.class);
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            DeleteCommand.setDeletionCallback(DeleteAction.defaultDeletionCallback);
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/FullPreferences.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/FullPreferences.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/FullPreferences.java	(working copy)
@@ -0,0 +1,53 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Map;
+
+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.ExtensionContext.Namespace;
+import org.openstreetmap.josm.TestUtils;
+import org.openstreetmap.josm.data.Preferences;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.spi.preferences.Setting;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences.BasicPreferencesExtension;
+
+/**
+ * Allow tests to use JOSM preferences (see {@link JOSMTestRules#preferences()})
+ * @author Taylor Smock
+ * @see BasicPreferences (often enough for simple tests).
+ * @since xxx
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@JosmHome
+@BasicPreferences
+@ExtendWith(FullPreferences.UsePreferencesExtension.class)
+public @interface FullPreferences {
+    /**
+     * Initialize preferences.
+     */
+    class UsePreferencesExtension implements BeforeEachCallback {
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            Preferences pref = context.getStore(Namespace.create(BasicPreferencesExtension.class)).get("preferences", Preferences.class);
+            @SuppressWarnings("unchecked")
+            final Map<String, Setting<?>> defaultsMap = (Map<String, Setting<?>>) TestUtils.getPrivateField(pref, "defaultsMap");
+            defaultsMap.clear();
+            pref.resetToInitialState();
+            pref.enableSaveOnPut(false);
+            // No pref init -> that would only create the preferences file.
+            // We force the use of a wrong API server, just in case anyone attempts an upload
+            Config.getPref().put("osm-server.url", "http://invalid");
+        }
+
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/HTTP.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/HTTP.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/HTTP.java	(working copy)
@@ -0,0 +1,44 @@
+// 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.Retention;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.openstreetmap.josm.tools.Http1Client;
+import org.openstreetmap.josm.tools.HttpClient;
+
+/**
+ * Set up the HttpClient factory
+ * @author Taylor Smock
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+@ExtendWith(HTTP.HTTPExtension.class)
+public @interface HTTP {
+    /**
+     * Initialize and reset HttpClient
+     * @author Taylor Smock
+     *
+     */
+    class HTTPExtension implements BeforeAllCallback, AfterAllCallback {
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            AnnotationUtils.resetStaticClass(HttpClient.class);
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            HttpClient.setFactory(Http1Client::new);
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/HTTPS.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/HTTPS.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/HTTPS.java	(working copy)
@@ -0,0 +1,48 @@
+// 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.Retention;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+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 HTTPS certificates
+ *
+ * @author Taylor Smock
+ * @see JOSMTestRules#https
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+@BasicPreferences
+@ExtendWith(HTTPS.HTTPSExtension.class)
+public @interface HTTPS {
+
+    /**
+     * Set up HTTPS certificates. Only run once.
+     * @author Taylor Smock
+     */
+    class HTTPSExtension implements BeforeAllCallback {
+        private static boolean initialized;
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            if (!initialized) {
+                synchronized (HTTPSExtension.class) {
+                    CertificateAmendment.addMissingCertificates();
+                    initialized = true;
+                }
+            }
+        }
+
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/I18n.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/I18n.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/I18n.java	(working copy)
@@ -0,0 +1,51 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.support.AnnotationSupport;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Enables the i18n module for this test.
+ * @author Taylor Smock
+ * @see JOSMTestRules#i18n(String)
+ *
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@ExtendWith(I18n.I18nExtension.class)
+public @interface I18n {
+    /**
+     * Get the language to use for i18n
+     * @return The language (default "en").
+     */
+    String language() default "en";
+
+    /**
+     * Enables the i18n module for this test.
+     * @author Taylor Smock
+     * @see JOSMTestRules#i18n(String)
+     *
+     */
+    class I18nExtension implements BeforeAllCallback {
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            Optional<I18n> annotation = AnnotationSupport.findAnnotation(context.getElement(), I18n.class);
+            String language = "en";
+            if (annotation.isPresent()) {
+                language = annotation.get().language();
+            }
+            org.openstreetmap.josm.tools.I18n.set(language);
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/IntegrationTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/IntegrationTest.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/IntegrationTest.java	(working copy)
@@ -0,0 +1,24 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.Tag;
+
+/**
+ * Integration Tests annotation (JUnit5 can filter tests with/without this annotation)
+ * @author Taylor Smock
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Tag("integrationTest")
+public @interface IntegrationTest {
+
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/JosmHome.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/JosmHome.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/JosmHome.java	(working copy)
@@ -0,0 +1,70 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.io.File;
+import java.io.IOException;
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.nio.file.FileVisitResult;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.SimpleFileVisitor;
+import java.nio.file.attribute.BasicFileAttributes;
+import java.util.UUID;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
+import org.openstreetmap.josm.data.preferences.JosmBaseDirectories;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Use the JOSM home directory. See {@link JOSMTestRules}.
+ * Typically only used by {@link FullPreferences}.
+ *
+ * @author Taylor Smock
+ *
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@ExtendWith(JosmHome.JosmHomeExtension.class)
+public @interface JosmHome {
+    /**
+     * Create a JOSM home directory. Prefer using {@link JosmHome}.
+     * @author Taylor Smock
+     */
+    class JosmHomeExtension implements BeforeAllCallback, AfterAllCallback {
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            Path tempDir = context.getStore(Namespace.create(JosmHome.class)).get("home", Path.class);
+            Files.walkFileTree(tempDir, new SimpleFileVisitor<Path>() {
+                @Override
+                public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
+                    Files.delete(dir);
+                    return FileVisitResult.CONTINUE;
+                }
+
+                @Override
+                public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
+                    Files.delete(file);
+                    return FileVisitResult.CONTINUE;
+                }
+            });
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            Path tempDir = Files.createTempDirectory(UUID.randomUUID().toString());
+            context.getStore(Namespace.create(JosmHome.class)).put("home", tempDir);
+            File home = tempDir.toFile();
+            System.setProperty("josm.home", home.getAbsolutePath());
+            JosmBaseDirectories.getInstance().clearMemos();
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/LayerEnvironment.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/LayerEnvironment.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/LayerEnvironment.java	(working copy)
@@ -0,0 +1,44 @@
+// 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.Retention;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.AfterEachCallback;
+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.testutils.JOSMTestRules;
+
+/**
+ * Clean up layers before/after the tests
+ * @author Taylor Smock
+ * @see JOSMTestRules#cleanLayerEnvironment()
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+@ExtendWith(LayerEnvironment.LayerEnvironmentExtension.class)
+public @interface LayerEnvironment {
+    /**
+     * Clean up layers
+     * @author Taylor Smock
+     *
+     */
+    class LayerEnvironmentExtension implements BeforeEachCallback, AfterEachCallback {
+        @Override
+        public void afterEach(ExtensionContext context) throws Exception {
+            beforeEach(context);
+        }
+
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            JOSMTestRules.cleanLayerEnvironment();
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/Main.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/Main.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/Main.java	(working copy)
@@ -0,0 +1,84 @@
+// 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.Retention;
+import java.lang.annotation.Target;
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.support.AnnotationSupport;
+import org.openstreetmap.josm.JOSMFixture;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.mockers.WindowlessMapViewStateMocker;
+import org.openstreetmap.josm.testutils.mockers.WindowlessNavigatableComponentMocker;
+
+/**
+ * Use the {@link MainApplication#main}, {@code Main.contentPanePrivate}, {@code Main.mainPanel}, global variables in this test.
+ * @author Taylor Smock
+ * @see JOSMTestRules#main()
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target(TYPE)
+@BasicPreferences
+@HTTP // Prevent MOTD from throwing
+@ExtendWith(Main.MainExtension.class)
+public @interface Main {
+    /**
+     * Get the class to use as the mocker for the map view
+     * @return The mocker class for the map view
+     */
+    Class<?> mapViewStateMocker() default WindowlessMapViewStateMocker.class;
+    /**
+     * Get the class to use for the navigable component
+     * @return The class to use for the navigable component.
+     */
+    Class<?> navigableComponentMocker() default WindowlessNavigatableComponentMocker.class;
+
+    /**
+     * Initialize the MainApplication
+     * @author Taylor Smock
+     */
+    class MainExtension implements BeforeAllCallback, AfterAllCallback {
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            MainApplication.getLayerManager().resetState();;
+            AnnotationUtils.resetStaticClass(MainApplication.class);
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            Optional<Main> annotation = AnnotationSupport.findAnnotation(context.getElement(), Main.class);
+            Class<?> mapViewStateMocker = null;
+            Class<?> navigableComponentMocker = null;
+            if (annotation.isPresent()) {
+                mapViewStateMocker = annotation.get().mapViewStateMocker();
+                navigableComponentMocker = annotation.get().navigableComponentMocker();
+            }
+
+            // apply mockers to MapViewState and NavigableComponent whether we're headless or not
+            // as we generally don't create the josm main window even in non-headless mode.
+            if (mapViewStateMocker != null) {
+                mapViewStateMocker.getConstructor().newInstance();
+            }
+            if (navigableComponentMocker != null) {
+                navigableComponentMocker.getConstructor().newInstance();
+            }
+
+            new MainApplication();
+            JOSMFixture.initContentPane();
+            JOSMFixture.initMainPanel(true);
+            JOSMFixture.initToolbar();
+            JOSMFixture.initMainMenu();
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/OsmApiType.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/OsmApiType.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/OsmApiType.java	(working copy)
@@ -0,0 +1,86 @@
+// 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.Retention;
+import java.lang.annotation.Target;
+import java.util.Optional;
+
+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.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.testutils.FakeOsmApi;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Specify the OSM API to use for the test. {@link APIType#NONE} has no effect.
+ *
+ * @author Taylor Smock
+ * @see JOSMTestRules#devAPI()
+ * @see JOSMTestRules#fakeAPI()
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({ TYPE, METHOD })
+@FullPreferences
+@HTTP
+@ExtendWith(OsmApiType.OsmApiTypeExtension.class)
+public @interface OsmApiType {
+    /**
+     * API types to initialize
+     * @author Taylor Smock
+     *
+     */
+    enum APIType {
+        NONE, FAKE, DEV;
+    }
+
+    /**
+     * The API type to use
+     * @return The API type to use (default NONE)
+     */
+    APIType apiType() default APIType.NONE;
+
+    /**
+     * Initialize the OSM api
+     * @author Taylor Smock
+     *
+     */
+    class OsmApiTypeExtension implements BeforeAllCallback, BeforeEachCallback {
+
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            Optional<OsmApiType> annotation = AnnotationUtils.findFirstParentAnnotation(context, OsmApiType.class);
+            APIType useAPI = APIType.NONE;
+            if (annotation.isPresent()) {
+                useAPI = annotation.get().apiType();
+            }
+            // Set API
+            if (useAPI == APIType.DEV) {
+                Config.getPref().put("osm-server.url", "https://api06.dev.openstreetmap.org/api");
+            } else if (useAPI == APIType.FAKE) {
+                FakeOsmApi api = FakeOsmApi.getInstance();
+                Config.getPref().put("osm-server.url", api.getServerUrl());
+            }
+
+            // Initialize API
+            if (useAPI != APIType.NONE) {
+                OsmApi.getOsmApi().initialize(null);
+            }
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            beforeEach(context);
+        }
+
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/Presets.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/Presets.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/Presets.java	(working copy)
@@ -0,0 +1,48 @@
+// 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.Retention;
+import java.lang.annotation.Target;
+
+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.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Use presets in this test.
+ *
+ * @author Taylor Smock
+ * @see JOSMTestRules#presets()
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({ TYPE, METHOD })
+@ExtendWith(Presets.PresetsExtension.class)
+public @interface Presets {
+    /**
+     * Initialize the presets
+     * @author Taylor Smock
+     *
+     */
+    class PresetsExtension implements BeforeAllCallback, BeforeEachCallback {
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            TaggingPresets.readFromPreferences();
+        }
+
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            // TODO only run if method level
+            beforeAll(context);
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/Projection.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/Projection.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/Projection.java	(working copy)
@@ -0,0 +1,67 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+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.platform.commons.support.AnnotationSupport;
+import org.openstreetmap.josm.data.projection.ProjectionRegistry;
+import org.openstreetmap.josm.data.projection.Projections;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Use projections in tests (Mercator).
+ * @author Taylor Smock
+ * @see JOSMTestRules#projection()
+ *
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ElementType.METHOD, ElementType.TYPE})
+@ExtendWith(Projection.ProjectionExtension.class)
+public @interface Projection {
+    /**
+     * The value to use for the projection. Defaults to EPSG:3857 (Mercator).
+     * @return The value to use to get the projection from {@link Projections#getProjectionByCode}.
+     */
+    String projectionCode() default "EPSG:3857";
+
+    /**
+     * Use projections in tests. Use {@link Projection} preferentially.
+     * @author Taylor Smock
+     *
+     */
+    class ProjectionExtension implements BeforeEachCallback, BeforeAllCallback, AfterAllCallback {
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            ProjectionRegistry.clearProjectionChangeListeners();
+            AnnotationUtils.resetStaticClass(ProjectionRegistry.class);
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            // Needed in order to run prior to Territories
+            beforeEach(context);
+        }
+
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            Optional<Projection> annotation = AnnotationSupport.findAnnotation(context.getElement(), Projection.class);
+            if (annotation.isPresent()) {
+                ProjectionRegistry.setProjection(Projections.getProjectionByCode(annotation.get().projectionCode()));
+            } else {
+                ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857")); // Mercator
+            }
+        }
+
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/ProjectionNadGrids.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/ProjectionNadGrids.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/ProjectionNadGrids.java	(working copy)
@@ -0,0 +1,39 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.openstreetmap.josm.gui.MainApplication;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Set up loading of NTV2 grit shift files to support projections that need them.
+ * @author Taylor Smock
+ * @see JOSMTestRules#projectionNadGrids()
+ * @since xxx
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@ExtendWith(ProjectionNadGrids.ProjectionNadGridsExtension.class)
+public @interface ProjectionNadGrids {
+    /**
+     * Set up loading of NTV2 grit shift files to support projections that need them.
+     * Use {@link ProjectionNadGrids} instead.
+     * @author Taylor Smock
+     * @see JOSMTestRules#projectionNadGrids()
+     */
+    class ProjectionNadGridsExtension implements BeforeAllCallback {
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            MainApplication.setupNadGridSources();
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/SlowTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/SlowTest.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/SlowTest.java	(working copy)
@@ -0,0 +1,24 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+import org.junit.jupiter.api.Tag;
+
+/**
+ * Use for any test which takes longer than 1 minute
+ * @author Taylor Smock
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@Tag("slowTest")
+public @interface SlowTest {
+
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/Territories.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/Territories.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/Territories.java	(working copy)
@@ -0,0 +1,78 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+import java.util.Optional;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+import org.junit.jupiter.api.extension.BeforeAllCallback;
+import org.junit.jupiter.api.extension.ExtendWith;
+import org.junit.jupiter.api.extension.ExtensionContext;
+import org.junit.platform.commons.support.AnnotationSupport;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
+
+/**
+ * Use boundaries dataset in this test.
+ * @see JOSMTestRules#territories()
+ * @author Taylor Smock
+ * @since xxx
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.TYPE)
+@BasicPreferences // Needed for nodes
+@ExtendWith(Territories.TerritoriesExtension.class)
+public @interface Territories {
+    /**
+     * Initialization states. Please note that the highest initialization state holds.
+     * @author Taylor Smock
+     */
+    enum Initialize {
+        /** Don't initialize */
+        NONE,
+        /** Initialize only internal data */
+        INTERNAL,
+        /** Initialize internal and external data */
+        ALL
+    }
+
+    /**
+     * The way to initialize Territories
+     * @return The value to use
+     */
+    Initialize value() default Initialize.INTERNAL;
+
+    /**
+     * Initialize boundaries prior to use
+     * @author Taylor Smock
+     *
+     */
+    class TerritoriesExtension implements BeforeAllCallback, AfterAllCallback {
+        @Override
+        public void afterAll(ExtensionContext context) throws Exception {
+            synchronized (TerritoriesExtension.class) {
+                org.openstreetmap.josm.tools.Territories.uninitialize();
+            }
+        }
+
+        @Override
+        public void beforeAll(ExtensionContext context) throws Exception {
+            Optional<Territories> annotation = AnnotationSupport.findAnnotation(context.getElement(), Territories.class);
+            if (annotation.isPresent()) {
+                Initialize current = annotation.get().value();
+                // Avoid potential race conditions if tests are parallelized
+                synchronized (TerritoriesExtension.class) {
+                    if (current == Initialize.INTERNAL) {
+                        org.openstreetmap.josm.tools.Territories.initializeInternalData();
+                    } else if (current == Initialize.ALL) {
+                        org.openstreetmap.josm.tools.Territories.initialize();
+                    }
+                }
+            }
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/testutils/annotations/TimeZoneAnnotation.java
===================================================================
--- test/unit/org/openstreetmap/josm/testutils/annotations/TimeZoneAnnotation.java	(nonexistent)
+++ test/unit/org/openstreetmap/josm/testutils/annotations/TimeZoneAnnotation.java	(working copy)
@@ -0,0 +1,44 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils.annotations;
+
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+import java.util.TimeZone;
+
+import org.junit.jupiter.api.extension.AfterAllCallback;
+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.tools.date.DateUtils;
+
+/**
+ * Set the default timezone
+ * @author Taylor Smock
+ * @since xxx
+ */
+@Documented
+@Retention(RUNTIME)
+@Target({ElementType.TYPE, ElementType.METHOD})
+@ExtendWith(TimeZoneAnnotation.TimeZoneExtension.class)
+public @interface TimeZoneAnnotation {
+    /**
+     * Set the default timezone
+     * @author Taylor Smock
+     */
+    class TimeZoneExtension implements BeforeEachCallback, AfterAllCallback {
+        @Override
+        public void afterAll(ExtensionContext context) {
+            TimeZone.setDefault(null);
+            TimeZone.getDefault().getDisplayName();
+        }
+
+        @Override
+        public void beforeEach(ExtensionContext context) throws Exception {
+            TimeZone.setDefault(DateUtils.UTC);
+        }
+    }
+}
Index: test/unit/org/openstreetmap/josm/tools/ExceptionUtilTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/tools/ExceptionUtilTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/tools/ExceptionUtilTest.java	(working copy)
@@ -12,7 +12,6 @@
 
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.openstreetmap.josm.io.ChangesetClosedException;
 import org.openstreetmap.josm.io.IllegalDataException;
 import org.openstreetmap.josm.io.MissingOAuthAccessTokenException;
@@ -21,24 +20,17 @@
 import org.openstreetmap.josm.io.OsmApiException;
 import org.openstreetmap.josm.io.OsmApiInitializationException;
 import org.openstreetmap.josm.io.auth.CredentialsManager;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.FullPreferences;
+import org.openstreetmap.josm.testutils.annotations.OsmApiType;
 import org.openstreetmap.josm.tools.date.DateUtils;
 import org.openstreetmap.josm.tools.date.DateUtilsTest;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link ExceptionUtil} class.
  */
+@FullPreferences
+@OsmApiType(apiType = OsmApiType.APIType.FAKE)
 class ExceptionUtilTest {
-
-    /**
-     * Setup rule.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().fakeAPI();
-
     private static String baseUrl;
     private static String serverUrl;
     private static String host;
Index: test/unit/org/openstreetmap/josm/tools/GeometryTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/tools/GeometryTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/tools/GeometryTest.java	(working copy)
@@ -14,7 +14,6 @@
 import java.util.List;
 
 import org.junit.Assert;
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
 import org.openstreetmap.josm.TestUtils;
 import org.openstreetmap.josm.data.coor.EastNorth;
@@ -27,22 +26,16 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.search.SearchCompiler;
 import org.openstreetmap.josm.io.OsmReader;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
+import org.openstreetmap.josm.testutils.annotations.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.Projection;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link Geometry} class.
  */
+@BasicPreferences // For primitives
+@Projection // For primitives
 class GeometryTest {
     /**
-     * Primitives need preferences and projection.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences().projection();
-
-    /**
      * Test of {@link Geometry#getLineLineIntersection} method.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/tools/OptionParserTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/tools/OptionParserTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/tools/OptionParserTest.java	(working copy)
@@ -3,8 +3,8 @@
 
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertThrows;
 import static org.junit.jupiter.api.Assertions.assertTrue;
-import static org.junit.jupiter.api.Assertions.assertThrows;
 
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -12,26 +12,15 @@
 import java.util.concurrent.atomic.AtomicBoolean;
 import java.util.concurrent.atomic.AtomicReference;
 
-import org.junit.jupiter.api.extension.RegisterExtension;
 import org.junit.jupiter.api.Test;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
 import org.openstreetmap.josm.tools.OptionParser.OptionCount;
 import org.openstreetmap.josm.tools.OptionParser.OptionParseException;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Test for {@link OptionParser}
  * @author Michael Zangl
  */
 class OptionParserTest {
-    /**
-     * Setup test.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().i18n();
-
     // A reason for moving to jupiter...
     @Test
     void testEmptyParserRejectsLongopt() {
Index: test/unit/org/openstreetmap/josm/tools/SearchCompilerQueryWizardTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/tools/SearchCompilerQueryWizardTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/tools/SearchCompilerQueryWizardTest.java	(working copy)
@@ -6,22 +6,14 @@
 
 import org.junit.jupiter.api.Disabled;
 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.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link SearchCompilerQueryWizard} class.
  */
+@org.openstreetmap.josm.testutils.annotations.I18n(language = "de")
+@BasicPreferences // Due to OsmPrimitives being used
 class SearchCompilerQueryWizardTest {
-    /**
-     * Base test environment is enough
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().i18n("de");
-
     private static String constructQuery(String s) {
         return SearchCompilerQueryWizard.constructQuery(s);
     }
Index: test/unit/org/openstreetmap/josm/tools/TextTagParserTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/tools/TextTagParserTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/tools/TextTagParserTest.java	(working copy)
@@ -10,24 +10,15 @@
 import java.util.List;
 import java.util.Map;
 
-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.BasicPreferences;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-
 /**
  * Unit tests of {@link TextTagParser} class.
  */
+@BasicPreferences
 class TextTagParserTest {
     /**
-     * Some of this depends on preferences.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().preferences();
-
-    /**
      * Test of {@link TextTagParser#unescape} method.
      */
     @Test
Index: test/unit/org/openstreetmap/josm/tools/UtilsTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/tools/UtilsTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/tools/UtilsTest.java	(working copy)
@@ -22,10 +22,7 @@
 import java.util.regex.Pattern;
 
 import org.junit.jupiter.api.Test;
-import org.junit.jupiter.api.extension.RegisterExtension;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import net.trajano.commons.testing.UtilityClassTestUtil;
 
 /**
@@ -33,13 +30,6 @@
  */
 class UtilsTest {
     /**
-     * Use default, basic test rules.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules rules = new JOSMTestRules();
-
-    /**
      * Tests that {@code Utils} satisfies utility class criteria.
      * @throws ReflectiveOperationException if an error occurs
      */
Index: test/unit/org/openstreetmap/josm/tools/date/DateUtilsTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/tools/date/DateUtilsTest.java	(revision 17429)
+++ test/unit/org/openstreetmap/josm/tools/date/DateUtilsTest.java	(working copy)
@@ -16,28 +16,19 @@
 
 import org.junit.jupiter.api.Disabled;
 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.BasicPreferences;
+import org.openstreetmap.josm.testutils.annotations.TimeZoneAnnotation;
 import org.openstreetmap.josm.tools.UncheckedParseException;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import net.trajano.commons.testing.UtilityClassTestUtil;
 
 /**
  * Unit tests of {@link DateUtils} class.
  */
+@TimeZoneAnnotation
+@BasicPreferences
 public class DateUtilsTest {
-
     /**
-     * Set the timezone and timeout.
-     * <p>
-     * Timeouts need to be disabled because we change the time zone.
-     */
-    @RegisterExtension
-    @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
-    public JOSMTestRules test = new JOSMTestRules().i18n().preferences();
-
-    /**
      * Tests that {@code DateUtils} satisfies utility class criteria.
      * @throws ReflectiveOperationException if an error occurs
      */
