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

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

fix unit tests

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