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

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

speed up TerritoriesTest

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