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

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

see #15182 - deprecate Main.getLayerManager(). Replacement: gui.MainApplication.getLayerManager()

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