Ticket #16567: 16567.separate_all_each.patch
| File 16567.separate_all_each.patch, 19.3 KB (added by , 5 years ago) |
|---|
-
test/unit/org/openstreetmap/josm/testutils/JOSMTestRules.java
1 1 // License: GPL. For details, see LICENSE file. 2 2 package org.openstreetmap.josm.testutils; 3 3 4 import static org.junit.jupiter.api.Assertions.fail; 5 4 6 import java.awt.Color; 5 7 import java.awt.Window; 6 8 import java.awt.event.WindowEvent; 7 9 import java.io.ByteArrayInputStream; 8 import java.io.File;9 10 import java.io.IOException; 10 11 import java.io.PrintWriter; 11 12 import java.io.StringWriter; 13 import java.lang.annotation.Annotation; 12 14 import java.lang.annotation.Documented; 13 15 import java.lang.annotation.ElementType; 14 16 import java.lang.annotation.Retention; 15 17 import java.lang.annotation.RetentionPolicy; 16 18 import java.lang.annotation.Target; 19 import java.lang.reflect.Method; 17 20 import java.nio.charset.StandardCharsets; 21 import java.nio.file.Files; 22 import java.nio.file.Path; 18 23 import java.security.GeneralSecurityException; 19 24 import java.text.MessageFormat; 20 25 import java.util.Arrays; 21 26 import java.util.Map; 27 import java.util.Optional; 22 28 import java.util.TimeZone; 29 import java.util.UUID; 23 30 import java.util.logging.Handler; 24 31 25 32 import org.awaitility.Awaitility; … … 85 92 */ 86 93 public class JOSMTestRules implements TestRule, AfterEachCallback, BeforeEachCallback, AfterAllCallback, BeforeAllCallback { 87 94 private int timeout = isDebugMode() ? -1 : 10 * 1000; 95 private final Path josmHomeTemp; 88 96 private TemporaryFolder josmHome; 89 97 private boolean usePreferences = false; 90 98 private APIType useAPI = APIType.NONE; … … 105 113 private boolean territories; 106 114 private boolean metric; 107 115 private boolean main; 116 108 117 /** 109 118 * This boolean is only used to indicate if JUnit5 is used in a test. If it is, 110 119 * we must not call after in {@link JOSMTestRules.CreateJosmEnvironment#evaluate}. … … 112 121 */ 113 122 private boolean junit5; 114 123 124 private FailOnTimeoutStatement failOnTimeout; 125 115 126 /** 127 * Create a new JOSMTestRules object 128 */ 129 public JOSMTestRules() { 130 Path tempHome = null; 131 try { 132 tempHome = Files.createTempDirectory("josm-test-rules-home"); 133 } catch (IOException e) { 134 fail(e); 135 } 136 this.josmHomeTemp = tempHome; 137 } 138 139 /** 116 140 * Disable the default timeout for this test. Use with care. 117 141 * @return this instance, for easy chaining 118 142 */ … … 443 467 return statement; 444 468 } 445 469 470 /** 471 * Get an annotation for a context 472 * @param <T> The annotation type 473 * @param context The context to check 474 * @param annotation The annotation to get 475 * @return The annotation, or null 476 */ 477 private static <T extends Annotation> T getAnnotation(ExtensionContext context, Class<T> annotation) { 478 Optional<Method> method = context.getTestMethod(); 479 if (method.isPresent() && method.get().getAnnotation(annotation) != null) { 480 return method.get().getAnnotation(annotation); 481 } 482 Optional<Class<?>> clazz = context.getTestClass(); 483 if (clazz.isPresent() && clazz.get().getAnnotation(annotation) != null) { 484 return clazz.get().getAnnotation(annotation); 485 } 486 return null; 487 } 488 446 489 @Override 447 490 public void beforeEach(ExtensionContext context) throws Exception { 448 491 this.junit5 = true; 492 493 // Temporary statement is necessary until we are able to remove JUnit4. 449 494 Statement temporaryStatement = new Statement() { 450 495 @Override 451 496 public void evaluate() throws Throwable { … … 452 497 // do nothing 453 498 } 454 499 }; 500 // First process any Override* annotations for per-test overrides. 501 // The following only work because "option" methods modify JOSMTestRules in-place 502 final OverrideAssumeRevision overrideAssumeRevision = getAnnotation(context, OverrideAssumeRevision.class); 503 if (overrideAssumeRevision != null) { 504 this.assumeRevision(overrideAssumeRevision.value()); 505 } 506 final OverrideTimeout overrideTimeout = getAnnotation(context, OverrideTimeout.class); 507 if (overrideTimeout != null) { 508 this.timeout(overrideTimeout.value()); 509 } 455 510 try { 456 this.apply(temporaryStatement, 457 Description.createTestDescription(this.getClass(), "JOSMTestRules JUnit5 Compatibility")) 458 .evaluate(); 511 beforeEachTest(); 512 513 if (this.timeout > 0) { 514 this.failOnTimeout = new FailOnTimeoutStatement(temporaryStatement, this.timeout); 515 this.failOnTimeout.beforeEach(context); 516 } 459 517 } catch (Throwable e) { 460 518 throw new Exception(e); 461 519 } … … 463 521 464 522 @Override 465 523 public void afterEach(ExtensionContext context) throws Exception { 524 if (this.failOnTimeout != null) { 525 this.failOnTimeout.afterEach(context); 526 } 466 527 after(); 467 528 } 468 529 469 530 @Override 470 531 public void beforeAll(ExtensionContext context) throws Exception { 471 beforeEach(context); 532 if (this.tileSourceRule != null) { 533 this.tileSourceRule.beforeAll(context); 534 } 535 this.beforeAllTests(); 472 536 } 473 537 474 538 @Override 475 539 public void afterAll(ExtensionContext context) throws Exception { 476 540 afterEach(context); 541 if (this.tileSourceRule != null) { 542 this.tileSourceRule.afterAll(context); 543 } 477 544 } 478 545 479 546 /** … … 481 548 * @throws InitializationError If an error occurred while creating the required environment. 482 549 * @throws ReflectiveOperationException if a reflective access error occurs 483 550 */ 484 protected void before() throws InitializationError, ReflectiveOperationException { 485 cleanUpFromJosmFixture(); 486 551 protected void beforeEachTest() throws InitializationError, ReflectiveOperationException { 487 552 if (this.assumeRevisionString != null) { 488 553 this.originalVersion = Version.getInstance(); 489 554 final Version replacementVersion = new MockVersion(this.assumeRevisionString); … … 490 555 TestUtils.setPrivateStaticField(Version.class, "instance", replacementVersion); 491 556 } 492 557 493 // Add JOSM home 494 if (josmHome != null) { 558 // Add JOSM home. Take advantage of the fact that josmHome is only initialized with preferences. 559 // And is always initialized with them. 560 if (this.josmHome != null && this.josmHomeTemp != null) { 495 561 try { 496 File home = josmHome.newFolder();497 System.setProperty("josm.home", home. getAbsolutePath());562 final Path home = Files.createTempDirectory(josmHomeTemp, UUID.randomUUID().toString()); 563 System.setProperty("josm.home", home.toAbsolutePath().toString()); 498 564 JosmBaseDirectories.getInstance().clearMemos(); 499 565 } catch (IOException e) { 500 566 throw new InitializationError(e); … … 622 688 } 623 689 } 624 690 691 protected void beforeAllTests() { 692 cleanUpFromJosmFixture(); 693 } 694 625 695 /** 626 696 * Clean up what test not using these test rules may have broken. 627 697 */ … … 704 774 705 775 @Override 706 776 public void evaluate() throws Throwable { 707 before(); 777 beforeAllTests(); 778 beforeEachTest(); 708 779 try { 709 780 base.evaluate(); 710 781 } finally { … … 723 794 * The junit timeout statement has problems when switching timezones. This one does not. 724 795 * @author Michael Zangl 725 796 */ 726 private static class FailOnTimeoutStatement extends Statement {797 private static class FailOnTimeoutStatement extends Statement implements BeforeEachCallback, AfterEachCallback { 727 798 728 799 private final int timeout; 729 800 private final Statement original; 801 private TimeoutThread thread; 730 802 731 803 FailOnTimeoutStatement(Statement original, int timeout) { 732 804 this.original = original; … … 735 807 736 808 @Override 737 809 public void evaluate() throws Throwable { 738 TimeoutThread thread = new TimeoutThread(original); 739 thread.setDaemon(true); 740 thread.start(); 810 beforeEach(null); 811 afterEach(null); 812 } 813 814 @Override 815 public void afterEach(ExtensionContext context) throws Exception { 741 816 thread.join(timeout); 742 817 thread.interrupt(); 743 818 if (!thread.isDone) { 744 819 Throwable exception = thread.getExecutionException(); 745 820 if (exception != null) { 746 throw exception; 821 if (exception instanceof Exception) { 822 throw (Exception) exception; 823 } 824 throw new Exception(exception); 747 825 } else { 748 826 if (Logging.isLoggingEnabled(Logging.LEVEL_DEBUG)) { 749 827 // i.e. skip expensive formatting of stack trace if it won't be shown … … 754 832 throw new Exception(MessageFormat.format("Test timed out after {0}ms", timeout)); 755 833 } 756 834 } 835 836 757 837 } 838 839 @Override 840 public void beforeEach(ExtensionContext context) throws Exception { 841 this.thread = new TimeoutThread(original); 842 this.thread.setDaemon(true); 843 this.thread.start(); 844 } 758 845 } 759 846 760 847 private static final class TimeoutThread extends Thread { -
test/unit/org/openstreetmap/josm/testutils/TileSourceRule.java
2 2 package org.openstreetmap.josm.testutils; 3 3 4 4 import static com.github.tomakehurst.wiremock.core.WireMockConfiguration.options; 5 import static org.junit.jupiter.api.Assertions.fail; 5 6 import static org.openstreetmap.josm.TestUtils.getPrivateStaticField; 6 7 7 8 import java.awt.Color; … … 10 11 import java.io.ByteArrayOutputStream; 11 12 import java.io.IOException; 12 13 import java.util.Arrays; 14 import java.util.Collection; 13 15 import java.util.Collections; 14 16 import java.util.HashMap; 15 17 import java.util.List; 16 18 import java.util.Objects; 19 import java.util.stream.Collectors; 17 20 18 21 import javax.imageio.ImageIO; 19 22 23 import org.junit.jupiter.api.extension.AfterAllCallback; 24 import org.junit.jupiter.api.extension.BeforeAllCallback; 25 import org.junit.jupiter.api.extension.ExtensionContext; 20 26 import org.junit.runner.Description; 21 27 import org.junit.runners.model.Statement; 22 28 import org.openstreetmap.josm.data.imagery.ImageryInfo; 23 29 import org.openstreetmap.josm.data.imagery.ImageryLayerInfo; 30 import org.openstreetmap.josm.gui.bbox.JosmMapViewer.TileSourceProvider; 24 31 import org.openstreetmap.josm.gui.bbox.SlippyMapBBoxChooser; 25 32 import org.openstreetmap.josm.tools.Logging; 26 33 … … 28 35 import com.github.tomakehurst.wiremock.client.ResponseDefinitionBuilder; 29 36 import com.github.tomakehurst.wiremock.client.WireMock; 30 37 import com.github.tomakehurst.wiremock.junit.WireMockRule; 38 import com.github.tomakehurst.wiremock.verification.LoggedRequest; 31 39 32 40 /** 33 41 * A JUnit rule, based on {@link WireMockRule} to provide a test with a simple mock tile server serving multiple tile 34 42 * sources. 35 43 */ 36 public class TileSourceRule extends WireMockRule {44 public class TileSourceRule extends WireMockRule implements BeforeAllCallback, AfterAllCallback { 37 45 private static class ByteArrayWrapper { 38 46 public final byte[] byteArray; 39 47 … … 165 173 protected final boolean clearLayerList; 166 174 protected final boolean clearSlippyMapSources; 167 175 protected final boolean registerInLayerList; 176 private List<TileSourceProvider> slippyMapProviders; 177 private TileSourceProvider slippyMapDefaultProvider; 178 private List<ImageryInfo> originalImageryInfoList; 168 179 169 180 /** 170 181 * Construct a TileSourceRule for use with a JUnit test. … … 238 249 return super.apply(new Statement() { 239 250 @Override 240 251 public void evaluate() throws Throwable { 241 try { 242 // a hack to circumvent a WireMock bug concerning delayed server startup. sending an early request 243 // to the mock server seems to prompt it to start earlier (though this request itself is not 244 // expected to succeed). see https://github.com/tomakehurst/wiremock/issues/97 245 new java.net.URL(TileSourceRule.this.url("/_poke")).getContent(); 246 } catch (IOException e) { 247 Logging.trace(e); 248 } 252 applyRunServerEarlyStart(); 249 253 base.evaluate(); 250 254 } 251 255 }, description); 252 256 } 253 257 258 private void applyRunServerEarlyStart() { 259 try { 260 // a hack to circumvent a WireMock bug concerning delayed server startup. sending an early request 261 // to the mock server seems to prompt it to start earlier (though this request itself is not 262 // expected to succeed). see https://github.com/tomakehurst/wiremock/issues/97 263 new java.net.URL(TileSourceRule.this.url("/_poke")).getContent(); 264 } catch (IOException e) { 265 Logging.trace(e); 266 } 267 } 268 254 269 /** 255 270 * A junit-rule {@code apply} method exposed separately, containing initialization steps which can only be performed 256 271 * once more of josm's environment has been set up. … … 264 279 if (this.registerInLayerList || this.clearLayerList) { 265 280 return new Statement() { 266 281 @Override 267 @SuppressWarnings("unchecked")268 282 public void evaluate() throws Throwable { 269 List<SlippyMapBBoxChooser.TileSourceProvider> slippyMapProviders = null; 270 SlippyMapBBoxChooser.TileSourceProvider slippyMapDefaultProvider = null; 271 List<ImageryInfo> originalImageryInfoList = null; 272 if (TileSourceRule.this.clearSlippyMapSources) { 273 try { 274 slippyMapProviders = (List<SlippyMapBBoxChooser.TileSourceProvider>) getPrivateStaticField( 275 SlippyMapBBoxChooser.class, 276 "providers" 277 ); 278 // pop this off the beginning of the list, keep for later 279 slippyMapDefaultProvider = slippyMapProviders.remove(0); 280 } catch (ReflectiveOperationException e) { 281 Logging.warn("Failed to remove default SlippyMapBBoxChooser TileSourceProvider"); 282 } 283 } 284 285 if (TileSourceRule.this.clearLayerList) { 286 originalImageryInfoList = ImageryLayerInfo.instance.getLayers(); 287 ImageryLayerInfo.instance.clear(); 288 } 289 if (TileSourceRule.this.registerInLayerList) { 290 for (ConstSource source : TileSourceRule.this.sourcesList) { 291 ImageryLayerInfo.addLayer(source.getImageryInfo(TileSourceRule.this.port())); 292 } 293 } 294 283 realBeforeRegisterLayers(); 295 284 try { 296 285 base.evaluate(); 297 286 } finally { 298 // clean up to original state 299 if (slippyMapDefaultProvider != null && slippyMapProviders != null) { 300 slippyMapProviders.add(0, slippyMapDefaultProvider); 301 } 302 if (originalImageryInfoList != null) { 303 ImageryLayerInfo.instance.clear(); 304 ImageryLayerInfo.addLayers(originalImageryInfoList); 305 } 287 realAfterRegisterLayers(); 306 288 } 307 289 } 308 290 }; … … 311 293 } 312 294 } 313 295 296 @SuppressWarnings("unchecked") 297 private void realBeforeRegisterLayers() { 298 if (this.registerInLayerList || this.clearLayerList) { 299 List<SlippyMapBBoxChooser.TileSourceProvider> slippyMapProviders = null; 300 if (TileSourceRule.this.clearSlippyMapSources) { 301 try { 302 slippyMapProviders = (List<SlippyMapBBoxChooser.TileSourceProvider>) getPrivateStaticField( 303 SlippyMapBBoxChooser.class, 304 "providers" 305 ); 306 // pop this off the beginning of the list, keep for later 307 this.slippyMapDefaultProvider = slippyMapProviders.remove(0); 308 } catch (ReflectiveOperationException e) { 309 Logging.warn("Failed to remove default SlippyMapBBoxChooser TileSourceProvider"); 310 } 311 } 312 313 if (TileSourceRule.this.clearLayerList) { 314 originalImageryInfoList = ImageryLayerInfo.instance.getLayers(); 315 ImageryLayerInfo.instance.clear(); 316 } 317 if (TileSourceRule.this.registerInLayerList) { 318 for (ConstSource source : TileSourceRule.this.sourcesList) { 319 ImageryLayerInfo.addLayer(source.getImageryInfo(TileSourceRule.this.port())); 320 } 321 } 322 } 323 } 324 325 private void realAfterRegisterLayers() { 326 // clean up to original state 327 if (slippyMapDefaultProvider != null && slippyMapProviders != null) { 328 slippyMapProviders.add(0, slippyMapDefaultProvider); 329 } 330 if (originalImageryInfoList != null) { 331 ImageryLayerInfo.instance.clear(); 332 ImageryLayerInfo.addLayers(originalImageryInfoList); 333 } 334 } 335 314 336 /** 315 337 * A standard implementation of apply which simply calls both sub- {@code apply} methods, {@link #applyRunServer} 316 338 * and {@link #applyRegisterLayers}. Called when used as a standard junit rule. … … 319 341 public Statement apply(Statement base, Description description) { 320 342 return applyRunServer(applyRegisterLayers(base, description), description); 321 343 } 344 345 @Override 346 public void afterAll(ExtensionContext context) throws Exception { 347 after(); 348 Collection<LoggedRequest> missedRequests = super.findAllUnmatchedRequests(); 349 if (!missedRequests.isEmpty()) { 350 String message = missedRequests.stream().map(m -> m.getUrl()).collect(Collectors.joining("\n\n")); 351 fail(message); 352 } 353 stop(); 354 realAfterRegisterLayers(); 355 } 356 357 @Override 358 public void beforeAll(ExtensionContext context) throws Exception { 359 super.start(); 360 WireMock.configureFor("localhost", port()); 361 before(); 362 applyRunServerEarlyStart(); 363 realBeforeRegisterLayers(); 364 } 322 365 }
