source: josm/trunk/test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java@ 12848

Last change on this file since 12848 was 12848, checked in by bastiK, 7 years ago

see #15229 - set up Config for tests

File size: 15.2 KB
RevLine 
[10373]1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.testutils;
3
4import java.io.File;
5import java.io.IOException;
[11943]6import java.security.GeneralSecurityException;
[10569]7import java.text.MessageFormat;
[10467]8import java.util.TimeZone;
[10373]9
10import org.junit.rules.TemporaryFolder;
11import org.junit.rules.TestRule;
12import org.junit.runner.Description;
13import org.junit.runners.model.InitializationError;
14import org.junit.runners.model.Statement;
[10604]15import org.openstreetmap.josm.JOSMFixture;
[10373]16import org.openstreetmap.josm.Main;
[12751]17import org.openstreetmap.josm.actions.DeleteAction;
18import org.openstreetmap.josm.command.DeleteCommand;
[12747]19import org.openstreetmap.josm.data.UserIdentityManager;
[12750]20import org.openstreetmap.josm.data.osm.User;
[12070]21import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
[10402]22import org.openstreetmap.josm.data.projection.Projections;
[12557]23import org.openstreetmap.josm.gui.MainApplication;
[11777]24import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
[12807]25import org.openstreetmap.josm.gui.oauth.OAuthAuthorizationWizard;
[12568]26import org.openstreetmap.josm.gui.tagging.presets.TaggingPresets;
[10373]27import org.openstreetmap.josm.gui.util.GuiHelper;
[11943]28import org.openstreetmap.josm.io.CertificateAmendment;
[10373]29import org.openstreetmap.josm.io.OsmApi;
30import org.openstreetmap.josm.io.OsmApiInitializationException;
[12807]31import org.openstreetmap.josm.io.OsmConnection;
[10373]32import org.openstreetmap.josm.io.OsmTransferCanceledException;
33import org.openstreetmap.josm.tools.I18n;
[11943]34import org.openstreetmap.josm.tools.JosmRuntimeException;
[10899]35import org.openstreetmap.josm.tools.Logging;
[10588]36import org.openstreetmap.josm.tools.MemoryManagerTest;
[12556]37import org.openstreetmap.josm.tools.RightAndLefthandTraffic;
[12545]38import org.openstreetmap.josm.tools.Territories;
[10475]39import org.openstreetmap.josm.tools.date.DateUtils;
[10373]40
[10445]41import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
[12848]42import org.openstreetmap.josm.spi.preferences.Config;
[10445]43
[10373]44/**
45 * This class runs a test in an environment that resembles the one used by the JOSM main application.
46 * <p>
47 * The environment is reset before every test. You can specify the components to which you need access using the methods of this class.
48 * For example, invoking {@link #preferences()} gives you access to the (default) preferences.
49 *
50 * @author Michael Zangl
51 */
52public class JOSMTestRules implements TestRule {
[11251]53 private int timeout = isDebugMode() ? -1 : 10 * 1000;
[10373]54 private TemporaryFolder josmHome;
55 private boolean usePreferences = false;
56 private APIType useAPI = APIType.NONE;
57 private String i18n = null;
58 private boolean platform;
[10402]59 private boolean useProjection;
[12795]60 private boolean useProjectionNadGrids;
[10604]61 private boolean commands;
[10588]62 private boolean allowMemoryManagerLeaks;
[11777]63 private boolean useMapStyles;
[12568]64 private boolean usePresets;
[11943]65 private boolean useHttps;
[12545]66 private boolean territories;
[12556]67 private boolean rlTraffic;
[12557]68 private boolean main;
[10373]69
70 /**
71 * Disable the default timeout for this test. Use with care.
72 * @return this instance, for easy chaining
73 */
74 public JOSMTestRules noTimeout() {
[10569]75 timeout = -1;
[10373]76 return this;
77 }
78
79 /**
80 * Set a timeout for all tests in this class. Local method timeouts may only reduce this timeout.
81 * @param millis The timeout duration in milliseconds.
82 * @return this instance, for easy chaining
83 */
84 public JOSMTestRules timeout(int millis) {
[11251]85 timeout = isDebugMode() ? -1 : millis;
[10373]86 return this;
87 }
88
89 /**
90 * Enable the use of default preferences.
91 * @return this instance, for easy chaining
92 */
93 public JOSMTestRules preferences() {
94 josmHome();
95 usePreferences = true;
96 return this;
97 }
98
99 /**
100 * Set JOSM home to a valid, empty directory.
101 * @return this instance, for easy chaining
102 */
103 private JOSMTestRules josmHome() {
104 josmHome = new TemporaryFolder();
105 return this;
106 }
107
108 /**
109 * Enables the i18n module for this test in english.
110 * @return this instance, for easy chaining
111 */
112 public JOSMTestRules i18n() {
113 return i18n("en");
114 }
115
116 /**
117 * Enables the i18n module for this test.
118 * @param language The language to use.
119 * @return this instance, for easy chaining
120 */
121 public JOSMTestRules i18n(String language) {
122 i18n = language;
123 return this;
124 }
125
126 /**
127 * Enable {@link Main#platform} global variable.
128 * @return this instance, for easy chaining
129 */
130 public JOSMTestRules platform() {
131 platform = true;
132 return this;
133 }
134
135 /**
136 * Enable the dev.openstreetmap.org API for this test.
137 * @return this instance, for easy chaining
138 */
139 public JOSMTestRules devAPI() {
140 preferences();
141 useAPI = APIType.DEV;
142 return this;
143 }
144
145 /**
146 * Use the {@link FakeOsmApi} for testing.
147 * @return this instance, for easy chaining
148 */
149 public JOSMTestRules fakeAPI() {
150 useAPI = APIType.FAKE;
151 return this;
152 }
153
[10402]154 /**
155 * Set up default projection (Mercator)
156 * @return this instance, for easy chaining
157 */
158 public JOSMTestRules projection() {
159 useProjection = true;
160 return this;
161 }
162
[10588]163 /**
[12795]164 * Set up loading of NTV2 grit shift files to support projections that need them.
165 * @return this instance, for easy chaining
166 */
167 public JOSMTestRules projectionNadGrids() {
168 useProjectionNadGrids = true;
169 return this;
170 }
171
172 /**
[11943]173 * Set up HTTPS certificates
174 * @return this instance, for easy chaining
175 */
176 public JOSMTestRules https() {
177 useHttps = true;
[12249]178 platform = true;
[11943]179 return this;
180 }
181
182 /**
[12545]183 * Allow the execution of commands using {@link Main#undoRedo}
184 * @return this instance, for easy chaining
185 */
[10604]186 public JOSMTestRules commands() {
187 commands = true;
188 return this;
189 }
190
191 /**
[10588]192 * Allow the memory manager to contain items after execution of the test cases.
193 * @return this instance, for easy chaining
194 */
195 public JOSMTestRules memoryManagerLeaks() {
196 allowMemoryManagerLeaks = true;
197 return this;
198 }
199
[11777]200 /**
201 * Use map styles in this test.
202 * @return this instance, for easy chaining
203 * @since 11777
204 */
205 public JOSMTestRules mapStyles() {
206 preferences();
207 useMapStyles = true;
208 return this;
209 }
210
[12545]211 /**
[12568]212 * Use presets in this test.
213 * @return this instance, for easy chaining
214 * @since 12568
215 */
216 public JOSMTestRules presets() {
217 preferences();
218 usePresets = true;
219 return this;
220 }
221
222 /**
[12545]223 * Use boundaries dataset in this test.
224 * @return this instance, for easy chaining
225 * @since 12545
226 */
227 public JOSMTestRules territories() {
228 territories = true;
229 return this;
230 }
231
[12556]232 /**
233 * Use right and lefthand traffic dataset in this test.
234 * @return this instance, for easy chaining
235 * @since 12556
236 */
237 public JOSMTestRules rlTraffic() {
[12566]238 territories();
[12556]239 rlTraffic = true;
240 return this;
241 }
242
[12557]243 /**
[12632]244 * Use the {@link Main#main}, {@code Main.contentPanePrivate}, {@code Main.mainPanel},
245 * {@link Main#menu}, {@link Main#toolbar} global variables in this test.
[12557]246 * @return this instance, for easy chaining
[12558]247 * @since 12557
[12557]248 */
249 public JOSMTestRules main() {
[12632]250 platform();
[12557]251 main = true;
252 return this;
253 }
254
[10373]255 @Override
[10569]256 public Statement apply(Statement base, Description description) {
257 Statement statement = base;
258 if (timeout > 0) {
259 // TODO: new DisableOnDebug(timeout)
260 statement = new FailOnTimeoutStatement(statement, timeout);
[10373]261 }
[10569]262 statement = new CreateJosmEnvironment(statement);
[10373]263 if (josmHome != null) {
264 statement = josmHome.apply(statement, description);
265 }
266 return statement;
267 }
268
269 /**
270 * Set up before running a test
271 * @throws InitializationError If an error occured while creating the required environment.
272 */
273 protected void before() throws InitializationError {
[10553]274 // Tests are running headless by default.
275 System.setProperty("java.awt.headless", "true");
276
[10443]277 cleanUpFromJosmFixture();
278
[12848]279 Config.setPreferencesInstance(Main.pref);
[10467]280 // All tests use the same timezone.
[10475]281 TimeZone.setDefault(DateUtils.UTC);
[10467]282 // Set log level to info
[10899]283 Logging.setLogLevel(Logging.LEVEL_INFO);
[12747]284 // Assume anonymous user
285 UserIdentityManager.getInstance().setAnonymous();
[12750]286 User.clearUserMap();
[12751]287 // Setup callbacks
[12760]288 DeleteCommand.setDeletionCallback(DeleteAction.defaultDeletionCallback);
[12807]289 OsmConnection.setOAuthAccessTokenFetcher(OAuthAuthorizationWizard::obtainAccessToken);
[10373]290
291 // Set up i18n
292 if (i18n != null) {
293 I18n.set(i18n);
294 }
295
296 // Add JOSM home
297 if (josmHome != null) {
298 try {
299 File home = josmHome.newFolder();
300 System.setProperty("josm.home", home.getAbsolutePath());
301 } catch (IOException e) {
302 throw new InitializationError(e);
303 }
304 }
305
306 // Add preferences
307 if (usePreferences) {
[10876]308 Main.pref.resetToInitialState();
[10373]309 Main.pref.enableSaveOnPut(false);
310 // No pref init -> that would only create the preferences file.
311 // We force the use of a wrong API server, just in case anyone attempts an upload
312 Main.pref.put("osm-server.url", "http://invalid");
313 }
314
[11943]315 // Set Platform
316 if (platform) {
317 Main.determinePlatformHook();
318 }
319
320 if (useHttps) {
321 try {
322 CertificateAmendment.addMissingCertificates();
323 } catch (IOException | GeneralSecurityException ex) {
324 throw new JosmRuntimeException(ex);
325 }
326 }
327
[10402]328 if (useProjection) {
329 Main.setProjection(Projections.getProjectionByCode("EPSG:3857")); // Mercator
330 }
331
[12795]332 if (useProjectionNadGrids) {
333 MainApplication.setupNadGridSources();
334 }
335
[10373]336 // Set API
337 if (useAPI == APIType.DEV) {
338 Main.pref.put("osm-server.url", "http://api06.dev.openstreetmap.org/api");
339 } else if (useAPI == APIType.FAKE) {
340 FakeOsmApi api = FakeOsmApi.getInstance();
341 Main.pref.put("osm-server.url", api.getServerUrl());
342 }
343
344 // Initialize API
345 if (useAPI != APIType.NONE) {
346 try {
347 OsmApi.getOsmApi().initialize(null);
348 } catch (OsmTransferCanceledException | OsmApiInitializationException e) {
349 throw new InitializationError(e);
350 }
351 }
352
[11777]353 if (useMapStyles) {
354 // Reset the map paint styles.
355 MapPaintStyles.readFromPreferences();
356 }
357
[12568]358 if (usePresets) {
359 // Reset the presets.
360 TaggingPresets.readFromPreferences();
361 }
362
[12545]363 if (territories) {
364 Territories.initialize();
365 }
366
[12556]367 if (rlTraffic) {
368 RightAndLefthandTraffic.initialize();
369 }
370
[10604]371 if (commands) {
372 // TODO: Implement a more selective version of this once Main is restructured.
373 JOSMFixture.createUnitTestFixture().init(true);
[12557]374 } else {
375 if (main) {
376 new MainApplication();
[12562]377 JOSMFixture.initContentPane();
[12563]378 JOSMFixture.initMainPanel(true);
[12562]379 JOSMFixture.initToolbar();
[12643]380 JOSMFixture.initMainMenu();
[12557]381 }
[10604]382 }
[10373]383 }
384
385 /**
[10443]386 * Clean up what test not using these test rules may have broken.
387 */
[10445]388 @SuppressFBWarnings("DM_GC")
[10443]389 private void cleanUpFromJosmFixture() {
[10588]390 MemoryManagerTest.resetState(true);
[12070]391 cleanLayerEnvironment();
[10876]392 Main.pref.resetToInitialState();
[10443]393 Main.platform = null;
394 System.gc();
395 }
396
397 /**
[12070]398 * Cleans the Layer manager and the SelectionEventManager.
399 * You don't need to call this during tests, the test environment will do it for you.
400 * @since 12070
401 */
402 public static void cleanLayerEnvironment() {
403 // Get the instance before cleaning - this ensures that it is initialized.
404 SelectionEventManager eventManager = SelectionEventManager.getInstance();
[12636]405 MainApplication.getLayerManager().resetState();
[12070]406 eventManager.resetState();
407 }
408
409 /**
[10373]410 * Clean up after running a test
411 */
[10445]412 @SuppressFBWarnings("DM_GC")
[10373]413 protected void after() {
414 // Sync AWT Thread
415 GuiHelper.runInEDTAndWait(new Runnable() {
416 @Override
417 public void run() {
418 }
419 });
420 // Remove all layers
[12070]421 cleanLayerEnvironment();
[10588]422 MemoryManagerTest.resetState(allowMemoryManagerLeaks);
[10373]423
424 // TODO: Remove global listeners and other global state.
[11110]425 Main.pref.resetToInitialState();
[10373]426 Main.platform = null;
427 // Parts of JOSM uses weak references - destroy them.
428 System.gc();
429 }
430
[10569]431 private final class CreateJosmEnvironment extends Statement {
432 private final Statement base;
433
434 private CreateJosmEnvironment(Statement base) {
435 this.base = base;
436 }
437
438 @Override
439 public void evaluate() throws Throwable {
440 before();
441 try {
442 base.evaluate();
443 } finally {
444 after();
445 }
446 }
447 }
448
[10373]449 enum APIType {
450 NONE, FAKE, DEV
451 }
[10569]452
453 /**
454 * The junit timeout statement has problems when switchting timezones. This one does not.
455 * @author Michael Zangl
456 */
457 private static class FailOnTimeoutStatement extends Statement {
458
459 private int timeout;
460 private Statement original;
461
462 FailOnTimeoutStatement(Statement original, int timeout) {
463 this.original = original;
464 this.timeout = timeout;
465 }
466
467 @Override
468 public void evaluate() throws Throwable {
469 TimeoutThread thread = new TimeoutThread(original);
470 thread.setDaemon(true);
471 thread.start();
472 thread.join(timeout);
473 thread.interrupt();
474 if (!thread.isDone) {
475 Throwable exception = thread.getExecutionException();
476 if (exception != null) {
477 throw exception;
478 } else {
479 throw new Exception(MessageFormat.format("Test timed out after {0}ms", timeout));
480 }
481 }
482 }
483 }
484
485 private static final class TimeoutThread extends Thread {
486 public boolean isDone;
487 private Statement original;
488 private Throwable exceptionCaught;
489
490 private TimeoutThread(Statement original) {
491 super("Timeout runner");
492 this.original = original;
493 }
494
495 public Throwable getExecutionException() {
496 return exceptionCaught;
497 }
498
499 @Override
500 public void run() {
501 try {
502 original.evaluate();
503 isDone = true;
504 } catch (Throwable e) {
505 exceptionCaught = e;
506 }
507 }
508 }
[11251]509
510 private boolean isDebugMode() {
511 return java.lang.management.ManagementFactory.getRuntimeMXBean().
512 getInputArguments().toString().indexOf("-agentlib:jdwp") > 0;
513 }
[10373]514}
Note: See TracBrowser for help on using the repository browser.