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

Last change on this file since 11777 was 11777, checked in by michael2402, 7 years ago

See #14495 - move style source reset to JOSM test rules.

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