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

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

fix #12478, fix #12565, fix #11114 - Use ​Swing Copy/Paste instead of CopyAction/PasteAction with custom buffer (patch by michael2402, modified) - gsoc-core

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