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

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

fix #14649 - load Dutch Government (G2 & G3) certificates from Windows keystore if not found in Java keystore

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