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

Last change on this file was 19519, checked in by stoecker, 3 months ago

unify eol-style

  • Property svn:eol-style set to native
File size: 29.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.testutils;
3
4import java.awt.Color;
5import java.awt.Window;
6import java.awt.event.WindowEvent;
7import java.io.ByteArrayInputStream;
8import java.io.File;
9import java.io.IOException;
10import java.io.PrintWriter;
11import java.io.StringWriter;
12import java.lang.annotation.Documented;
13import java.lang.annotation.ElementType;
14import java.lang.annotation.Retention;
15import java.lang.annotation.RetentionPolicy;
16import java.lang.annotation.Target;
17import java.nio.charset.StandardCharsets;
18import java.security.GeneralSecurityException;
19import java.text.MessageFormat;
20import java.util.Arrays;
21import java.util.Map;
22import java.util.TimeZone;
23import java.util.concurrent.TimeUnit;
24import java.util.logging.Handler;
25
26import org.awaitility.Awaitility;
27import org.awaitility.Durations;
28import org.junit.jupiter.api.extension.AfterAllCallback;
29import org.junit.jupiter.api.extension.AfterEachCallback;
30import org.junit.jupiter.api.extension.BeforeAllCallback;
31import org.junit.jupiter.api.extension.BeforeEachCallback;
32import org.junit.jupiter.api.extension.ExtensionContext;
33import org.junit.rules.TemporaryFolder;
34import org.junit.rules.TestRule;
35import org.junit.runner.Description;
36import org.junit.runners.model.InitializationError;
37import org.junit.runners.model.Statement;
38import org.openstreetmap.josm.JOSMFixture;
39import org.openstreetmap.josm.TestUtils;
40import org.openstreetmap.josm.actions.DeleteAction;
41import org.openstreetmap.josm.command.DeleteCommand;
42import org.openstreetmap.josm.data.Preferences;
43import org.openstreetmap.josm.data.SystemOfMeasurement;
44import org.openstreetmap.josm.data.UserIdentityManager;
45import org.openstreetmap.josm.data.Version;
46import org.openstreetmap.josm.data.osm.User;
47import org.openstreetmap.josm.data.osm.event.SelectionEventManager;
48import org.openstreetmap.josm.data.preferences.JosmBaseDirectories;
49import org.openstreetmap.josm.data.preferences.JosmUrls;
50import org.openstreetmap.josm.data.projection.ProjectionRegistry;
51import org.openstreetmap.josm.data.projection.Projections;
52import org.openstreetmap.josm.gui.MainApplication;
53import org.openstreetmap.josm.gui.oauth.OAuthAuthorizationWizard;
54import org.openstreetmap.josm.gui.preferences.imagery.ImageryPreferenceTestIT;
55import org.openstreetmap.josm.gui.util.GuiHelper;
56import org.openstreetmap.josm.io.OsmApi;
57import org.openstreetmap.josm.io.OsmApiInitializationException;
58import org.openstreetmap.josm.io.OsmConnection;
59import org.openstreetmap.josm.io.OsmTransferCanceledException;
60import org.openstreetmap.josm.spi.preferences.Config;
61import org.openstreetmap.josm.spi.preferences.Setting;
62import org.openstreetmap.josm.testutils.annotations.HTTPS;
63import org.openstreetmap.josm.testutils.annotations.JosmDefaults;
64import org.openstreetmap.josm.testutils.annotations.MapPaintStyles;
65import org.openstreetmap.josm.testutils.annotations.TaggingPresets;
66import org.openstreetmap.josm.testutils.annotations.Territories;
67import org.openstreetmap.josm.testutils.mockers.EDTAssertionMocker;
68import org.openstreetmap.josm.testutils.mockers.WindowlessMapViewStateMocker;
69import org.openstreetmap.josm.testutils.mockers.WindowlessNavigatableComponentMocker;
70import org.openstreetmap.josm.tools.Http1Client;
71import org.openstreetmap.josm.tools.HttpClient;
72import org.openstreetmap.josm.tools.I18n;
73import org.openstreetmap.josm.tools.JosmRuntimeException;
74import org.openstreetmap.josm.tools.Logging;
75import org.openstreetmap.josm.tools.MemoryManagerTest;
76import org.openstreetmap.josm.tools.bugreport.ReportedException;
77import org.openstreetmap.josm.tools.date.DateUtils;
78
79import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
80import mockit.internal.state.SavePoint;
81
82/**
83 * This class runs a test in an environment that resembles the one used by the JOSM main application.
84 * <p>
85 * The environment is reset before every test. You can specify the components to which you need access using the methods of this class.
86 * For example, invoking {@link #preferences()} gives you access to the (default) preferences.
87 *
88 * @author Michael Zangl
89 */
90public class JOSMTestRules implements TestRule, AfterEachCallback, BeforeEachCallback, AfterAllCallback, BeforeAllCallback {
91 private int timeout = isDebugMode() ? -1 : 10 * 1000;
92 private TemporaryFolder josmHome;
93 private boolean usePreferences = false;
94 private APIType useAPI = APIType.NONE;
95 private String i18n = null;
96 private TileSourceRule tileSourceRule;
97 private String assumeRevisionString;
98 private Version originalVersion;
99 private Runnable mapViewStateMockingRunnable;
100 private Runnable navigableComponentMockingRunnable;
101 private Runnable edtAssertionMockingRunnable;
102 private boolean useProjection;
103 private boolean useProjectionNadGrids;
104 private boolean commands;
105 private boolean allowMemoryManagerLeaks;
106 private boolean useMapStyles;
107 private boolean usePresets;
108 private boolean useHttps;
109 private boolean territories;
110 private boolean metric;
111 private boolean main;
112 /**
113 * This boolean is only used to indicate if JUnit5 is used in a test. If it is,
114 * we must not call after in {@link JOSMTestRules.CreateJosmEnvironment#evaluate}.
115 * TODO: Remove JUnit4 as a whole sometime after 2021-01-01 (~6 month lead time for plugins)
116 */
117 private boolean junit5;
118
119 /**
120 * Disable the default timeout for this test. Use with care.
121 * @return this instance, for easy chaining
122 */
123 public JOSMTestRules noTimeout() {
124 timeout = -1;
125 return this;
126 }
127
128 /**
129 * Set a timeout for all tests in this class. Local method timeouts may only reduce this timeout.
130 * @param millis The timeout duration in milliseconds.
131 * @return this instance, for easy chaining
132 * @see org.junit.jupiter.api.Timeout
133 */
134 public JOSMTestRules timeout(int millis) {
135 timeout = isDebugMode() ? -1 : millis;
136 return this;
137 }
138
139 /**
140 * Enable the use of default preferences.
141 * @return this instance, for easy chaining
142 * @see org.openstreetmap.josm.testutils.annotations.BasicPreferences
143 * @see org.openstreetmap.josm.testutils.annotations.FullPreferences
144 */
145 public JOSMTestRules preferences() {
146 josmHome();
147 usePreferences = true;
148 return this;
149 }
150
151 /**
152 * Set JOSM home to a valid, empty directory.
153 * @return this instance, for easy chaining
154 */
155 private JOSMTestRules josmHome() {
156 josmHome = new TemporaryFolder();
157 return this;
158 }
159
160 /**
161 * Enables the i18n module for this test in english.
162 * @return this instance, for easy chaining
163 * @see org.openstreetmap.josm.testutils.annotations.I18n
164 */
165 public JOSMTestRules i18n() {
166 return i18n("en");
167 }
168
169 /**
170 * Enables the i18n module for this test.
171 * @param language The language to use.
172 * @return this instance, for easy chaining
173 * @see org.openstreetmap.josm.testutils.annotations.I18n
174 */
175 public JOSMTestRules i18n(String language) {
176 i18n = language;
177 return this;
178 }
179
180 /**
181 * Mock this test's assumed JOSM version (as reported by {@link Version}).
182 * @param revisionProperties mock contents of JOSM's {@code REVISION} properties file
183 * @return this instance, for easy chaining
184 * @see org.openstreetmap.josm.testutils.annotations.AssumeRevision
185 */
186 public JOSMTestRules assumeRevision(final String revisionProperties) {
187 this.assumeRevisionString = revisionProperties;
188 return this;
189 }
190
191 /**
192 * Enable the dev.openstreetmap.org API for this test.
193 * @return this instance, for easy chaining
194 * @see org.openstreetmap.josm.testutils.annotations.OsmApi.APIType#DEV
195 */
196 public JOSMTestRules devAPI() {
197 preferences();
198 useAPI = APIType.DEV;
199 return this;
200 }
201
202 /**
203 * Use the {@link FakeOsmApi} for testing.
204 * @return this instance, for easy chaining
205 * @see org.openstreetmap.josm.testutils.annotations.OsmApi.APIType#FAKE
206 */
207 public JOSMTestRules fakeAPI() {
208 useAPI = APIType.FAKE;
209 return this;
210 }
211
212 /**
213 * Set up default projection (Mercator)
214 * @return this instance, for easy chaining
215 * @see org.openstreetmap.josm.testutils.annotations.Projection
216 */
217 public JOSMTestRules projection() {
218 useProjection = true;
219 return this;
220 }
221
222 /**
223 * Set up loading of NTV2 grit shift files to support projections that need them.
224 * @return this instance, for easy chaining
225 * @see org.openstreetmap.josm.testutils.annotations.ProjectionNadGrids
226 */
227 public JOSMTestRules projectionNadGrids() {
228 useProjectionNadGrids = true;
229 return this;
230 }
231
232 /**
233 * Set up HTTPS certificates
234 * @return this instance, for easy chaining
235 * @see HTTPS
236 */
237 public JOSMTestRules https() {
238 useHttps = true;
239 return this;
240 }
241
242 /**
243 * Allow the execution of commands using {@code UndoRedoHandler}
244 * @return this instance, for easy chaining
245 */
246 public JOSMTestRules commands() {
247 commands = true;
248 return this;
249 }
250
251 /**
252 * Allow the memory manager to contain items after execution of the test cases.
253 * @return this instance, for easy chaining
254 * @see org.openstreetmap.josm.testutils.annotations.MemoryManagerLeaks
255 */
256 public JOSMTestRules memoryManagerLeaks() {
257 allowMemoryManagerLeaks = true;
258 return this;
259 }
260
261 /**
262 * Use map styles in this test.
263 * @return this instance, for easy chaining
264 * @see MapPaintStyles
265 * @since 11777
266 */
267 public JOSMTestRules mapStyles() {
268 preferences();
269 useMapStyles = true;
270 return this;
271 }
272
273 /**
274 * Use presets in this test.
275 * @return this instance, for easy chaining
276 * @see TaggingPresets
277 * @since 12568
278 */
279 public JOSMTestRules presets() {
280 preferences();
281 usePresets = true;
282 return this;
283 }
284
285 /**
286 * Use boundaries dataset in this test.
287 * @return this instance, for easy chaining
288 * @see Territories
289 * @since 12545
290 */
291 public JOSMTestRules territories() {
292 territories = true;
293 return this;
294 }
295
296 /**
297 * Use right and lefthand traffic dataset in this test.
298 * @return this instance, for easy chaining
299 * @since 12556
300 * @deprecated Use {@link #territories}
301 */
302 @Deprecated
303 public JOSMTestRules rlTraffic() {
304 territories();
305 return this;
306 }
307
308 /**
309 * Force metric measurement system.
310 * @return this instance, for easy chaining
311 * @see org.openstreetmap.josm.testutils.annotations.MeasurementSystem
312 * @since 15400
313 */
314 public JOSMTestRules metricSystem() {
315 metric = true;
316 return this;
317 }
318
319 /**
320 * Re-raise AssertionErrors thrown in the EDT where they would have normally been swallowed.
321 * @return this instance, for easy chaining
322 * @see org.openstreetmap.josm.testutils.annotations.AssertionsInEDT
323 */
324 public JOSMTestRules assertionsInEDT() {
325 return this.assertionsInEDT(EDTAssertionMocker::new);
326 }
327
328 /**
329 * Re-raise AssertionErrors thrown in the EDT where they would have normally been swallowed.
330 * @param edtAssertionMockingRunnable Runnable for initializing this functionality
331 *
332 * @return this instance, for easy chaining
333 * @see org.openstreetmap.josm.testutils.annotations.AssertionsInEDT
334 */
335 public JOSMTestRules assertionsInEDT(final Runnable edtAssertionMockingRunnable) {
336 this.edtAssertionMockingRunnable = edtAssertionMockingRunnable;
337 return this;
338 }
339
340 /**
341 * Replace imagery sources with a default set of mock tile sources
342 *
343 * @return this instance, for easy chaining
344 * @see org.openstreetmap.josm.testutils.annotations.FakeImagery
345 */
346 public JOSMTestRules fakeImagery() {
347 return this.fakeImagery(
348 new TileSourceRule(
349 true,
350 true,
351 true,
352 new TileSourceRule.ColorSource(Color.WHITE, "White Tiles", 256),
353 new TileSourceRule.ColorSource(Color.BLACK, "Black Tiles", 256),
354 new TileSourceRule.ColorSource(Color.MAGENTA, "Magenta Tiles", 256),
355 new TileSourceRule.ColorSource(Color.GREEN, "Green Tiles", 256)
356 )
357 );
358 }
359
360 /**
361 * Replace imagery sources with those from specific mock tile server setup
362 * @param tileSourceRule Tile source rule
363 *
364 * @return this instance, for easy chaining
365 * @see org.openstreetmap.josm.testutils.annotations.FakeImagery
366 */
367 public JOSMTestRules fakeImagery(TileSourceRule tileSourceRule) {
368 this.preferences();
369 this.tileSourceRule = tileSourceRule;
370 return this;
371 }
372
373 /**
374 * Use the {@code Main#main}, {@code Main.contentPanePrivate}, {@code Main.mainPanel},
375 * global variables in this test.
376 * @return this instance, for easy chaining
377 * @see org.openstreetmap.josm.testutils.annotations.Main
378 * @since 12557
379 */
380 public JOSMTestRules main() {
381 return this.main(
382 WindowlessMapViewStateMocker::new,
383 WindowlessNavigatableComponentMocker::new
384 );
385 }
386
387 /**
388 * Use the {@code Main#main}, {@code Main.contentPanePrivate}, {@code Main.mainPanel},
389 * global variables in this test.
390 * @param mapViewStateMockingRunnable Runnable to use for mocking out any required parts of
391 * {@link org.openstreetmap.josm.gui.MapViewState}, null to skip.
392 * @param navigableComponentMockingRunnable Runnable to use for mocking out any required parts
393 * of {@link org.openstreetmap.josm.gui.NavigatableComponent}, null to skip.
394 *
395 * @return this instance, for easy chaining
396 * @see org.openstreetmap.josm.testutils.annotations.AssertionsInEDT
397 */
398 public JOSMTestRules main(
399 final Runnable mapViewStateMockingRunnable,
400 final Runnable navigableComponentMockingRunnable
401 ) {
402 this.main = true;
403 this.mapViewStateMockingRunnable = mapViewStateMockingRunnable;
404 this.navigableComponentMockingRunnable = navigableComponentMockingRunnable;
405 return this;
406 }
407
408 /**
409 * Must be called if test run with Junit parameters
410 * @return this instance, for easy chaining
411 */
412 public JOSMTestRules parameters() {
413 try {
414 apply(new Statement() {
415 @Override
416 public void evaluate() throws Throwable {
417 // Do nothing. Hack needed because @Parameters are computed before anything else
418 }
419 }, Description.createSuiteDescription(ImageryPreferenceTestIT.class)).evaluate();
420 } catch (Throwable e) {
421 Logging.error(e);
422 }
423 return this;
424 }
425
426 private static class MockVersion extends Version {
427 MockVersion(final String propertiesString) {
428 super.initFromRevisionInfo(
429 new ByteArrayInputStream(propertiesString.getBytes(StandardCharsets.UTF_8))
430 );
431 }
432 }
433
434 @Override
435 public Statement apply(Statement base, Description description) {
436 // First process any Override* annotations for per-test overrides.
437 // The following only work because "option" methods modify JOSMTestRules in-place
438 final OverrideAssumeRevision overrideAssumeRevision = description.getAnnotation(OverrideAssumeRevision.class);
439 if (overrideAssumeRevision != null) {
440 this.assumeRevision(overrideAssumeRevision.value());
441 }
442 final OverrideTimeout overrideTimeout = description.getAnnotation(OverrideTimeout.class);
443 if (overrideTimeout != null) {
444 this.timeout(overrideTimeout.value());
445 }
446 Statement statement = base;
447 // counter-intuitively, Statements which need to have their setup routines performed *after* another one need to
448 // be added into the chain *before* that one, so that it ends up on the "inside".
449 if (timeout > 0) {
450 // TODO: new DisableOnDebug(timeout)
451 statement = new FailOnTimeoutStatement(statement, timeout);
452 }
453
454 // this half of TileSourceRule's initialization must happen after josm is set up
455 if (this.tileSourceRule != null) {
456 statement = this.tileSourceRule.applyRegisterLayers(statement, description);
457 }
458
459 statement = new CreateJosmEnvironment(statement);
460 if (josmHome != null) {
461 statement = josmHome.apply(statement, description);
462 }
463
464 // run mock tile server as the outermost Statement (started first) so it can hopefully be initializing in
465 // parallel with other setup
466 if (this.tileSourceRule != null) {
467 statement = this.tileSourceRule.applyRunServer(statement, description);
468 }
469 return statement;
470 }
471
472 @Override
473 public void beforeEach(ExtensionContext context) throws Exception {
474 this.junit5 = true;
475 Statement temporaryStatement = new Statement() {
476 @Override
477 public void evaluate() throws Throwable {
478 // do nothing
479 }
480 };
481 try {
482 this.apply(temporaryStatement,
483 Description.createTestDescription(this.getClass(), "JOSMTestRules JUnit5 Compatibility"))
484 .evaluate();
485 } catch (Throwable e) {
486 throw new Exception(e);
487 }
488 }
489
490 @Override
491 public void afterEach(ExtensionContext context) throws Exception {
492 after();
493 }
494
495 @Override
496 public void beforeAll(ExtensionContext context) throws Exception {
497 beforeEach(context);
498 }
499
500 @Override
501 public void afterAll(ExtensionContext context) throws Exception {
502 afterEach(context);
503 }
504
505 /**
506 * Set up before running a test
507 * @throws InitializationError If an error occurred while creating the required environment.
508 * @throws ReflectiveOperationException if a reflective access error occurs
509 */
510 protected void before() throws InitializationError, ReflectiveOperationException {
511 cleanUpFromJosmFixture();
512
513 if (this.assumeRevisionString != null) {
514 this.originalVersion = Version.getInstance();
515 final Version replacementVersion = new MockVersion(this.assumeRevisionString);
516 TestUtils.setPrivateStaticField(Version.class, "instance", replacementVersion);
517 }
518
519 // Add JOSM home
520 if (josmHome != null) {
521 try {
522 File home = josmHome.newFolder();
523 System.setProperty("josm.home", home.getAbsolutePath());
524 JosmBaseDirectories.getInstance().clearMemos();
525 } catch (IOException e) {
526 throw new InitializationError(e);
527 }
528 }
529
530 Preferences pref = Preferences.main();
531 Config.setPreferencesInstance(pref);
532 Config.setBaseDirectoriesProvider(JosmBaseDirectories.getInstance());
533 Config.setUrlsProvider(JosmUrls.getInstance());
534 // All tests use the same timezone.
535 TimeZone.setDefault(DateUtils.UTC);
536
537 // Force log handlers to reacquire reference to (junit's fake) stdout/stderr
538 for (Handler handler : Logging.getLogger().getHandlers()) {
539 if (handler instanceof Logging.ReacquiringConsoleHandler) {
540 handler.flush();
541 ((Logging.ReacquiringConsoleHandler) handler).reacquireOutputStream();
542 }
543 }
544 // Set log level to info
545 Logging.setLogLevel(Logging.LEVEL_INFO);
546
547 // Assume anonymous user
548 UserIdentityManager.getInstance().setAnonymous();
549 User.clearUserMap();
550 // Setup callbacks
551 DeleteCommand.setDeletionCallback(DeleteAction.defaultDeletionCallback);
552 OsmConnection.setOAuthAccessTokenFetcher(OAuthAuthorizationWizard::obtainAccessToken);
553 HttpClient.setFactory(Http1Client::new);
554
555 // Set up i18n
556 if (i18n != null) {
557 I18n.set(i18n);
558 }
559
560 // Add preferences
561 if (usePreferences) {
562 @SuppressWarnings("unchecked")
563 final Map<String, Setting<?>> defaultsMap = (Map<String, Setting<?>>) TestUtils.getPrivateField(pref, "defaultsMap");
564 defaultsMap.clear();
565 pref.resetToInitialState();
566 pref.enableSaveOnPut(false);
567 // No pref init -> that would only create the preferences file.
568 // We force the use of a wrong API server, just in case anyone attempts an upload
569 Config.getPref().put("osm-server.url", "http://invalid");
570 }
571
572 // Make sure we're using the metric system
573 if (metric) {
574 SystemOfMeasurement.setSystemOfMeasurement(SystemOfMeasurement.METRIC);
575 }
576
577 if (useHttps) {
578 try {
579 new HTTPS.HTTPSExtension().beforeEach(null);
580 } catch (IOException | GeneralSecurityException ex) {
581 throw new JosmRuntimeException(ex);
582 }
583 }
584
585 if (useProjection) {
586 ProjectionRegistry.setProjection(Projections.getProjectionByCode("EPSG:3857")); // Mercator
587 }
588
589 if (useProjectionNadGrids) {
590 MainApplication.setupNadGridSources();
591 }
592
593 // Set API
594 if (useAPI == APIType.DEV) {
595 Config.getPref().put("osm-server.url", "https://api06.dev.openstreetmap.org/api");
596 } else if (useAPI == APIType.FAKE) {
597 FakeOsmApi api = FakeOsmApi.getInstance();
598 Config.getPref().put("osm-server.url", api.getServerUrl());
599 }
600
601 // Initialize API
602 if (useAPI != APIType.NONE) {
603 try {
604 OsmApi.getOsmApi().initialize(null);
605 } catch (OsmTransferCanceledException | OsmApiInitializationException e) {
606 throw new InitializationError(e);
607 }
608 }
609
610 if (useMapStyles) {
611 // Reset the map paint styles.
612 MapPaintStyles.MapPaintStylesExtension.setup();
613 }
614
615 if (usePresets) {
616 // Reset the presets.
617 TaggingPresets.TaggingPresetsExtension.setup();
618 }
619
620 if (territories) {
621 Territories.TerritoriesExtension.setup(Territories.Initialize.INTERNAL);
622 }
623
624 if (this.edtAssertionMockingRunnable != null) {
625 this.edtAssertionMockingRunnable.run();
626 }
627
628 if (commands) {
629 // TODO: Implement a more selective version of this once Main is restructured.
630 JOSMFixture.createUnitTestFixture().init(true);
631 } else {
632 if (main) {
633 // apply mockers to MapViewState and NavigableComponent whether we're headless or not
634 // as we generally don't create the josm main window even in non-headless mode.
635 if (this.mapViewStateMockingRunnable != null) {
636 this.mapViewStateMockingRunnable.run();
637 }
638 if (this.navigableComponentMockingRunnable != null) {
639 this.navigableComponentMockingRunnable.run();
640 }
641
642 new MainApplication();
643 JOSMFixture.initContentPane();
644 JOSMFixture.initMainPanel(true);
645 JOSMFixture.initToolbar();
646 JOSMFixture.initMainMenu();
647 }
648 }
649 }
650
651 /**
652 * Clean up what test not using these test rules may have broken.
653 */
654 @SuppressFBWarnings("DM_GC")
655 private void cleanUpFromJosmFixture() {
656 MemoryManagerTest.resetState(true);
657 cleanLayerEnvironment();
658 Preferences.main().resetToInitialState();
659 JosmDefaults.DefaultsExtension.memoryCleanup();
660 }
661
662 /**
663 * Cleans the Layer manager and the SelectionEventManager.
664 * You don't need to call this during tests, the test environment will do it for you.
665 * @since 12070
666 */
667 public static void cleanLayerEnvironment() {
668 // Get the instance before cleaning - this ensures that it is initialized.
669 SelectionEventManager eventManager = SelectionEventManager.getInstance();
670 MainApplication.getLayerManager().resetState();
671 try {
672 eventManager.resetState();
673 } catch (IllegalArgumentException e) {
674 Logging.trace(e);
675 }
676 }
677
678 /**
679 * @return TileSourceRule which is automatically started by this rule
680 */
681 public TileSourceRule getTileSourceRule() {
682 return this.tileSourceRule;
683 }
684
685 /**
686 * Clean up after running a test
687 * @throws ReflectiveOperationException if a reflective access error occurs
688 */
689 @SuppressFBWarnings("DM_GC")
690 protected void after() throws ReflectiveOperationException {
691 // Sync AWT Thread
692 GuiHelper.runInEDTAndWait(() -> { });
693 // Sync worker thread
694 final boolean[] queueEmpty = {false};
695 MainApplication.worker.submit(() -> queueEmpty[0] = true);
696 // Default pollInterval is 100ms, which means that each test takes (at minimum) .1s.
697 Awaitility.await().pollDelay(0, TimeUnit.SECONDS).pollInterval(Durations.ONE_MILLISECOND).forever().until(() -> queueEmpty[0]);
698 // Remove all layers
699 cleanLayerEnvironment();
700 MemoryManagerTest.resetState(allowMemoryManagerLeaks);
701
702 // TODO: Remove global listeners and other global state.
703 ProjectionRegistry.clearProjectionChangeListeners();
704 Preferences.main().resetToInitialState();
705
706 if (this.assumeRevisionString != null && this.originalVersion != null) {
707 TestUtils.setPrivateStaticField(Version.class, "instance", this.originalVersion);
708 }
709
710 Window[] windows = Window.getWindows();
711 if (windows.length != 0) {
712 Logging.info(
713 "Attempting to close {0} windows left open by tests: {1}",
714 windows.length,
715 Arrays.toString(windows)
716 );
717 }
718 GuiHelper.runInEDTAndWait(() -> {
719 for (Window window : windows) {
720 window.dispatchEvent(new WindowEvent(window, WindowEvent.WINDOW_CLOSING));
721 window.dispose();
722 }
723 });
724
725 // Parts of JOSM uses weak references - destroy them.
726 JosmDefaults.DefaultsExtension.memoryCleanup();
727 }
728
729 private final class CreateJosmEnvironment extends Statement {
730 private final Statement base;
731
732 private CreateJosmEnvironment(Statement base) {
733 this.base = base;
734 }
735
736 @Override
737 public void evaluate() throws Throwable {
738 // Needed since JMockit doesn't clean up JUnit4 vintage tests. We really shouldn't have to touch JMockit internal classes. :(
739 SavePoint savePoint = new SavePoint();
740 before();
741 try {
742 base.evaluate();
743 } finally {
744 if (!junit5) {
745 after();
746 savePoint.rollback();
747 }
748 }
749 }
750 }
751
752 enum APIType {
753 NONE, FAKE, DEV
754 }
755
756 /**
757 * The junit timeout statement has problems when switching timezones. This one does not.
758 * @author Michael Zangl
759 */
760 private static class FailOnTimeoutStatement extends Statement {
761
762 private final int timeout;
763 private final Statement original;
764
765 FailOnTimeoutStatement(Statement original, int timeout) {
766 this.original = original;
767 this.timeout = timeout;
768 }
769
770 @Override
771 public void evaluate() throws Throwable {
772 TimeoutThread thread = new TimeoutThread(original);
773 thread.setDaemon(true);
774 thread.start();
775 thread.join(timeout);
776 thread.interrupt();
777 if (!thread.isDone) {
778 Throwable exception = thread.getExecutionException();
779 if (exception != null) {
780 throw exception;
781 } else {
782 if (Logging.isLoggingEnabled(Logging.LEVEL_DEBUG)) {
783 // i.e. skip expensive formatting of stack trace if it won't be shown
784 final StringWriter sw = new StringWriter();
785 new ReportedException(exception).printReportThreadsTo(new PrintWriter(sw));
786 Logging.debug("Thread state at timeout: {0}", sw);
787 }
788 throw new Exception(MessageFormat.format("Test timed out after {0}ms", timeout));
789 }
790 }
791 }
792 }
793
794 private static final class TimeoutThread extends Thread {
795 public boolean isDone;
796 private final Statement original;
797 private Throwable exceptionCaught;
798
799 private TimeoutThread(Statement original) {
800 super("Timeout runner");
801 this.original = original;
802 }
803
804 public Throwable getExecutionException() {
805 return exceptionCaught;
806 }
807
808 @Override
809 public void run() {
810 try {
811 original.evaluate();
812 isDone = true;
813 } catch (Throwable e) {
814 exceptionCaught = e;
815 }
816 }
817 }
818
819 private boolean isDebugMode() {
820 return java.lang.management.ManagementFactory.getRuntimeMXBean().
821 getInputArguments().toString().indexOf("-agentlib:jdwp") > 0;
822 }
823
824 /**
825 * Override this test's assumed JOSM version (as reported by {@link Version}).
826 * @see JOSMTestRules#assumeRevision(String)
827 */
828 @Documented
829 @Retention(RetentionPolicy.RUNTIME)
830 @Target(ElementType.METHOD)
831 public @interface OverrideAssumeRevision {
832 /**
833 * Returns overridden assumed JOSM version.
834 * @return overridden assumed JOSM version
835 */
836 String value();
837 }
838
839 /**
840 * Override this test's timeout.
841 * @see JOSMTestRules#timeout(int)
842 */
843 @Documented
844 @Retention(RetentionPolicy.RUNTIME)
845 @Target(ElementType.METHOD)
846 public @interface OverrideTimeout {
847 /**
848 * Returns overridden timeout value.
849 * @return overridden timeout value
850 */
851 int value();
852 }
853}
Note: See TracBrowser for help on using the repository browser.