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

Last change on this file since 10956 was 10899, checked in by Don-vip, 8 years ago

fix #13318 - Clean up program startup (parameters, logging) - patch by michael2402 - gsoc-core

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