Index: /trunk/src/org/openstreetmap/josm/actions/AddImageryLayerAction.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/actions/AddImageryLayerAction.java	(revision 10372)
+++ /trunk/src/org/openstreetmap/josm/actions/AddImageryLayerAction.java	(revision 10373)
@@ -79,5 +79,5 @@
                     ? getWMSLayerInfo() : info;
             if (infoToAdd != null) {
-                Main.main.addLayer(ImageryLayer.create(infoToAdd));
+                Main.getLayerManager().addLayer(ImageryLayer.create(infoToAdd));
                 AlignImageryPanel.addNagPanelIfNeeded(infoToAdd);
             }
Index: /trunk/src/org/openstreetmap/josm/io/OsmApi.java
===================================================================
--- /trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 10372)
+++ /trunk/src/org/openstreetmap/josm/io/OsmApi.java	(revision 10373)
@@ -90,7 +90,11 @@
         if (api == null) {
             api = new OsmApi(serverUrl);
-            instances.put(serverUrl, api);
+            cacheInstance(api);
         }
         return api;
+    }
+
+    protected static void cacheInstance(OsmApi api) {
+        instances.put(api.getServerUrl(), api);
     }
 
Index: /trunk/test/unit/org/openstreetmap/josm/actions/AddImageryLayerActionTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/actions/AddImageryLayerActionTest.java	(revision 10372)
+++ /trunk/test/unit/org/openstreetmap/josm/actions/AddImageryLayerActionTest.java	(revision 10373)
@@ -8,11 +8,11 @@
 import java.util.List;
 
-import org.junit.BeforeClass;
+import org.junit.Rule;
 import org.junit.Test;
-import org.openstreetmap.josm.JOSMFixture;
 import org.openstreetmap.josm.Main;
 import org.openstreetmap.josm.data.imagery.ImageryInfo;
 import org.openstreetmap.josm.gui.layer.TMSLayer;
 import org.openstreetmap.josm.gui.layer.WMSLayer;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
 
 /**
@@ -20,12 +20,9 @@
  */
 public final class AddImageryLayerActionTest {
-
     /**
-     * Setup test.
+     * We need prefs for this. We need platform for actions and the OSM API for checking blacklist.
      */
-    @BeforeClass
-    public static void setUp() {
-        JOSMFixture.createUnitTestFixture().init(true);
-    }
+    @Rule
+    public JOSMTestRules test = new JOSMTestRules().preferences().platform().fakeAPI();
 
     /**
@@ -35,5 +32,4 @@
     public void testEnabledState() {
         assertFalse(new AddImageryLayerAction(new ImageryInfo()).isEnabled());
-        assertFalse(new AddImageryLayerAction(new ImageryInfo("google", "http://maps.google.com/api", "tms", null, null)).isEnabled());
         assertTrue(new AddImageryLayerAction(new ImageryInfo("foo_tms", "http://bar", "tms", null, null)).isEnabled());
         assertTrue(new AddImageryLayerAction(new ImageryInfo("foo_bing", "http://bar", "bing", null, null)).isEnabled());
Index: /trunk/test/unit/org/openstreetmap/josm/actions/search/SearchCompilerTest.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/actions/search/SearchCompilerTest.java	(revision 10372)
+++ /trunk/test/unit/org/openstreetmap/josm/actions/search/SearchCompilerTest.java	(revision 10373)
@@ -5,8 +5,8 @@
 import static org.junit.Assert.assertFalse;
 import static org.junit.Assert.assertTrue;
-
-import org.junit.Before;
+import static org.junit.Assert.fail;
+
+import org.junit.Rule;
 import org.junit.Test;
-import org.openstreetmap.josm.JOSMFixture;
 import org.openstreetmap.josm.actions.search.SearchCompiler.Match;
 import org.openstreetmap.josm.actions.search.SearchCompiler.ParseError;
@@ -23,4 +23,5 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.data.osm.WayData;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
 import org.openstreetmap.josm.tools.date.DateUtils;
 
@@ -31,10 +32,8 @@
 
     /**
-     * Setup test.
-     */
-    @Before
-    public void setUp() {
-        JOSMFixture.createUnitTestFixture().init();
-    }
+     * We need prefs for this. We access preferences when creating OSM primitives.
+     */
+    @Rule
+    public JOSMTestRules test = new JOSMTestRules().preferences();
 
     private static final class SearchContext {
@@ -390,5 +389,5 @@
         try {
             SearchCompiler.compile("foo type bar");
-            throw new RuntimeException();
+            fail();
         } catch (ParseError parseError) {
             assertEquals("<html>Expecting <code>:</code> after <i>type</i>", parseError.getMessage());
Index: /trunk/test/unit/org/openstreetmap/josm/testutils/FakeOsmApi.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/testutils/FakeOsmApi.java	(revision 10373)
+++ /trunk/test/unit/org/openstreetmap/josm/testutils/FakeOsmApi.java	(revision 10373)
@@ -0,0 +1,121 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils;
+
+import org.openstreetmap.josm.data.coor.LatLon;
+import org.openstreetmap.josm.data.notes.Note;
+import org.openstreetmap.josm.data.osm.Changeset;
+import org.openstreetmap.josm.data.osm.IPrimitive;
+import org.openstreetmap.josm.data.osm.OsmPrimitive;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.Capabilities;
+import org.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.io.OsmApiInitializationException;
+import org.openstreetmap.josm.io.OsmTransferCanceledException;
+import org.openstreetmap.josm.io.OsmTransferException;
+
+/**
+ * A fake OSM API server. It is used to test again.
+ * <p>
+ * It provides only basic features.
+ * <p>
+ * These image servers are blacklisted:
+ * <ul>
+ * <li>.*blacklisted.*</li>
+ * <li>^(invalid|bad).*</li>
+ * </ul>
+ *
+ * @author Michael Zangl
+ */
+public class FakeOsmApi extends OsmApi {
+
+    private static FakeOsmApi instance;
+
+    private boolean initialized = false;
+
+    protected FakeOsmApi() {
+        super("http://fake.xxx/api");
+    }
+
+    @Override
+    public void initialize(ProgressMonitor monitor, boolean fastFail)
+            throws OsmTransferCanceledException, OsmApiInitializationException {
+        // we do not connect to any server so we do not need that.
+        initialized = true;
+    }
+
+    @Override
+    public synchronized Capabilities getCapabilities() {
+        if (!initialized) {
+            return null;
+        } else {
+            Capabilities capabilities = new Capabilities();
+            capabilities.put("blacklist", "regex", ".*blacklisted.*");
+            capabilities.put("blacklist", "regex", "^https?://(invalid|bad).*");
+            capabilities.put("version", "minimum", "0.6");
+            capabilities.put("version", "maximum", "0.6");
+            return capabilities;
+        }
+    }
+
+    @Override
+    public void createPrimitive(IPrimitive osm, ProgressMonitor monitor) throws OsmTransferException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void modifyPrimitive(IPrimitive osm, ProgressMonitor monitor) throws OsmTransferException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void deletePrimitive(OsmPrimitive osm, ProgressMonitor monitor) throws OsmTransferException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void openChangeset(Changeset changeset, ProgressMonitor progressMonitor) throws OsmTransferException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void updateChangeset(Changeset changeset, ProgressMonitor monitor) throws OsmTransferException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public void closeChangeset(Changeset changeset, ProgressMonitor monitor) throws OsmTransferException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Note createNote(LatLon latlon, String text, ProgressMonitor monitor) throws OsmTransferException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Note addCommentToNote(Note note, String comment, ProgressMonitor monitor) throws OsmTransferException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Note closeNote(Note note, String closeMessage, ProgressMonitor monitor) throws OsmTransferException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    @Override
+    public Note reopenNote(Note note, String reactivateMessage, ProgressMonitor monitor) throws OsmTransferException {
+        throw new UnsupportedOperationException("Not implemented");
+    }
+
+    /**
+     * Gets and caches an instance of this API.
+     * @return The API intance. Always the same object.
+     */
+    public static synchronized FakeOsmApi getInstance() {
+        if (instance == null) {
+            instance = new FakeOsmApi();
+            cacheInstance(instance);
+        }
+        return instance;
+    }
+}
Index: /trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
===================================================================
--- /trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java	(revision 10373)
+++ /trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java	(revision 10373)
@@ -0,0 +1,226 @@
+// License: GPL. For details, see LICENSE file.
+package org.openstreetmap.josm.testutils;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.junit.rules.TemporaryFolder;
+import org.junit.rules.TestRule;
+import org.junit.rules.Timeout;
+import org.junit.runner.Description;
+import org.junit.runners.model.InitializationError;
+import org.junit.runners.model.Statement;
+import org.openstreetmap.josm.Main;
+import org.openstreetmap.josm.gui.layer.MainLayerManager;
+import org.openstreetmap.josm.gui.util.GuiHelper;
+import org.openstreetmap.josm.io.OsmApi;
+import org.openstreetmap.josm.io.OsmApiInitializationException;
+import org.openstreetmap.josm.io.OsmTransferCanceledException;
+import org.openstreetmap.josm.tools.I18n;
+
+/**
+ * This class runs a test in an environment that resembles the one used by the JOSM main application.
+ * <p>
+ * The environment is reset before every test. You can specify the components to which you need access using the methods of this class.
+ * For example, invoking {@link #preferences()} gives you access to the (default) preferences.
+ *
+ * @author Michael Zangl
+ */
+public class JOSMTestRules implements TestRule {
+    //We should make this the default when running from ant: Timeout.seconds(10);
+    private Timeout timeout = null;
+    private TemporaryFolder josmHome;
+    private boolean usePreferences = false;
+    private APIType useAPI = APIType.NONE;
+    private String i18n = null;
+    private boolean platform;
+
+    /**
+     * Disable the default timeout for this test. Use with care.
+     * @return this instance, for easy chaining
+     */
+    public JOSMTestRules noTimeout() {
+        timeout = null;
+        return this;
+    }
+
+    /**
+     * Set a timeout for all tests in this class. Local method timeouts may only reduce this timeout.
+     * @param millis The timeout duration in milliseconds.
+     * @return this instance, for easy chaining
+     */
+    public JOSMTestRules timeout(int millis) {
+        timeout = Timeout.millis(millis);
+        return this;
+    }
+
+    /**
+     * Enable the use of default preferences.
+     * @return this instance, for easy chaining
+     */
+    public JOSMTestRules preferences() {
+        josmHome();
+        usePreferences = true;
+        return this;
+    }
+
+    /**
+     * Set JOSM home to a valid, empty directory.
+     * @return this instance, for easy chaining
+     */
+    private JOSMTestRules josmHome() {
+        josmHome = new TemporaryFolder();
+        return this;
+    }
+
+    /**
+     * Enables the i18n module for this test in english.
+     * @return this instance, for easy chaining
+     */
+    public JOSMTestRules i18n() {
+        return i18n("en");
+    }
+
+    /**
+     * Enables the i18n module for this test.
+     * @param language The language to use.
+     * @return this instance, for easy chaining
+     */
+    public JOSMTestRules i18n(String language) {
+        i18n = language;
+        return this;
+    }
+
+    /**
+     * Enable {@link Main#platform} global variable.
+     * @return this instance, for easy chaining
+     */
+    public JOSMTestRules platform() {
+        platform = true;
+        return this;
+    }
+
+    /**
+     * Enable the dev.openstreetmap.org API for this test.
+     * @return this instance, for easy chaining
+     */
+    public JOSMTestRules devAPI() {
+        preferences();
+        useAPI = APIType.DEV;
+        return this;
+    }
+
+    /**
+     * Use the {@link FakeOsmApi} for testing.
+     * @return this instance, for easy chaining
+     */
+    public JOSMTestRules fakeAPI() {
+        useAPI = APIType.FAKE;
+        return this;
+    }
+
+    @Override
+    public Statement apply(final Statement base, Description description) {
+        Statement statement = new Statement() {
+            @Override
+            public void evaluate() throws Throwable {
+                before();
+                try {
+                    base.evaluate();
+                } finally {
+                    after();
+                }
+            }
+        };
+        if (timeout != null) {
+            statement = timeout.apply(statement, description);
+        }
+        if (josmHome != null) {
+            statement = josmHome.apply(statement, description);
+        }
+        return statement;
+    }
+
+    /**
+     * Set up before running a test
+     * @throws InitializationError If an error occured while creating the required environment.
+     */
+    protected void before() throws InitializationError {
+        // Tests are running headless by default.
+        System.setProperty("java.awt.headless", "true");
+
+        // Set up i18n
+        if (i18n != null) {
+            I18n.set(i18n);
+        }
+
+        // Add JOSM home
+        if (josmHome != null) {
+            try {
+                File home = josmHome.newFolder();
+                System.setProperty("josm.home", home.getAbsolutePath());
+            } catch (IOException e) {
+                throw new InitializationError(e);
+            }
+        }
+
+        // Add preferences
+        if (usePreferences) {
+            Main.initApplicationPreferences();
+            Main.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
+            Main.pref.put("osm-server.url", "http://invalid");
+        }
+
+        // Set API
+        if (useAPI == APIType.DEV) {
+            Main.pref.put("osm-server.url", "http://api06.dev.openstreetmap.org/api");
+        } else if (useAPI == APIType.FAKE) {
+            FakeOsmApi api = FakeOsmApi.getInstance();
+            Main.pref.put("osm-server.url", api.getServerUrl());
+        }
+
+        // Initialize API
+        if (useAPI != APIType.NONE) {
+            try {
+                OsmApi.getOsmApi().initialize(null);
+            } catch (OsmTransferCanceledException | OsmApiInitializationException e) {
+                throw new InitializationError(e);
+            }
+        }
+
+        // Set Platform
+        if (platform) {
+            Main.determinePlatformHook();
+        }
+    }
+
+    /**
+     * Clean up after running a test
+     */
+    protected void after() {
+        // Sync AWT Thread
+        GuiHelper.runInEDTAndWait(new Runnable() {
+            @Override
+            public void run() {
+            }
+        });
+        // Remove all layers
+        MainLayerManager lm = Main.getLayerManager();
+        while (!lm.getLayers().isEmpty()) {
+            lm.removeLayer(lm.getLayers().get(0));
+        }
+
+        // TODO: Remove global listeners and other global state.
+
+        Main.pref = null;
+        Main.platform = null;
+        // Parts of JOSM uses weak references - destroy them.
+        System.gc();
+    }
+
+    enum APIType {
+        NONE, FAKE, DEV
+    }
+}
