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

Last change on this file since 13078 was 13078, checked in by Don-vip, 6 years ago

fix #15508 - add TileSourceRule, use in MinimapDialogTest (patch by ris, modified) + update WireMock to 2.10.1

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