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

Last change on this file since 16657 was 16657, checked in by simon04, 4 years ago

Fix typos in Javadoc

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