source: josm/trunk/test/functional/org/openstreetmap/josm/gui/mappaint/MapCSSRendererTest.java@ 12995

Last change on this file since 12995 was 12995, checked in by bastiK, 7 years ago

proper image dimensions in MapCSSRendererTest

  • Property svn:eol-style set to native
File size: 11.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint;
3
4import static org.junit.Assert.assertEquals;
5import static org.junit.Assert.fail;
6
7import java.awt.GraphicsEnvironment;
8import java.awt.Point;
9import java.awt.image.BufferedImage;
10import java.io.File;
11import java.io.FileInputStream;
12import java.io.FileNotFoundException;
13import java.io.IOException;
14import java.text.MessageFormat;
15import java.util.ArrayList;
16import java.util.Arrays;
17import java.util.Collection;
18import java.util.Collections;
19import java.util.List;
20import java.util.stream.Collectors;
21import java.util.stream.Stream;
22
23import javax.imageio.ImageIO;
24
25import org.junit.Assume;
26import org.junit.Before;
27import org.junit.Rule;
28import org.junit.Test;
29import org.junit.runner.RunWith;
30import org.junit.runners.Parameterized;
31import org.junit.runners.Parameterized.Parameters;
32import org.openstreetmap.josm.Main;
33import org.openstreetmap.josm.TestUtils;
34import org.openstreetmap.josm.data.Bounds;
35import org.openstreetmap.josm.data.ProjectionBounds;
36import org.openstreetmap.josm.data.osm.DataSet;
37import org.openstreetmap.josm.data.osm.OsmPrimitive;
38import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
39import org.openstreetmap.josm.io.IllegalDataException;
40import org.openstreetmap.josm.io.OsmReader;
41import org.openstreetmap.josm.testutils.JOSMTestRules;
42
43import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
44
45/**
46 * Test cases for {@link StyledMapRenderer} and the MapCSS classes.
47 * <p>
48 * This test uses the data and reference files stored in the test data directory {@value #TEST_DATA_BASE}
49 * @author Michael Zangl
50 */
51@RunWith(Parameterized.class)
52public class MapCSSRendererTest {
53 private static final String TEST_DATA_BASE = "/renderer/";
54 /**
55 * lat = 0..1, lon = 0..1
56 */
57 private static final Bounds AREA_DEFAULT = new Bounds(0, 0, 1, 1);
58 private static final int IMAGE_SIZE = 256;
59
60 /**
61 * Minimal test rules required
62 */
63 @Rule
64 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
65 public JOSMTestRules test = new JOSMTestRules().preferences().projection();
66
67 private TestConfig testConfig;
68
69 // development flag - set to true in order to update all reference images
70 private static final boolean UPDATE_ALL = false;
71
72 /**
73 * The different configurations of this test.
74 *
75 * @return The parameters.
76 */
77 @Parameters(name = "{1}")
78 public static Collection<Object[]> runs() {
79 return Stream.of(
80 /** Tests for StyledMapRenderer#drawNodeSymbol */
81 new TestConfig("node-shapes", AREA_DEFAULT),
82
83 /** Text for nodes */
84 new TestConfig("node-text", AREA_DEFAULT).usesFont("DejaVu Sans"),
85
86 /** Tests that StyledMapRenderer#drawWay respects width */
87 new TestConfig("way-width", AREA_DEFAULT),
88
89 /** Tests the way color property, including alpha */
90 new TestConfig("way-color", AREA_DEFAULT),
91
92 /** Tests dashed ways. */
93 new TestConfig("way-dashes", AREA_DEFAULT),
94
95 /** Tests dashed way clamping algorithm */
96 new TestConfig("way-dashes-clamp", AREA_DEFAULT),
97
98 /** Tests fill-color property */
99 new TestConfig("area-fill-color", AREA_DEFAULT),
100
101 /** Tests the fill-image property. */
102 new TestConfig("area-fill-image", AREA_DEFAULT),
103
104 /** Tests area label drawing/placement */
105 new TestConfig("area-text", AREA_DEFAULT),
106
107 /** Tests area icon drawing/placement */
108 new TestConfig("area-icon", AREA_DEFAULT),
109
110 /** Tests if all styles are sorted correctly. Tests {@link StyleRecord#compareTo(StyleRecord)} */
111 new TestConfig("order", AREA_DEFAULT),
112
113 /** Tests repeat-image feature for ways */
114 new TestConfig("way-repeat-image", AREA_DEFAULT),
115 /** Tests the clamping for repeat-images and repeat-image-phase */
116 new TestConfig("way-repeat-image-clamp", AREA_DEFAULT),
117
118 /** Tests text along a way */
119 new TestConfig("way-text", AREA_DEFAULT),
120
121 /** Another test for node shapes */
122 new TestConfig("node-shapes2").setImageWidth(600),
123 /** Tests default values for node shapes */
124 new TestConfig("node-shapes-default"),
125 /** Tests node shapes with both fill and stroke combined */
126 new TestConfig("node-shapes-combined"),
127 /** Another test for dashed ways */
128 new TestConfig("way-dashes2"),
129 /** Tests node text placement */
130 new TestConfig("node-text2"),
131 /** Tests relation link selector */
132 new TestConfig("relation-linkselector"),
133 /** Tests parent selector on relation */
134 new TestConfig("relation-parentselector"),
135
136 /** Tests evaluation of expressions */
137 new TestConfig("eval").setImageWidth(600)
138
139 ).map(e -> new Object[] {e, e.testDirectory})
140 .collect(Collectors.toList());
141 }
142
143 /**
144 * @param testConfig The config to use for this test.
145 * @param ignored The name to print it nicely
146 */
147 public MapCSSRendererTest(TestConfig testConfig, String ignored) {
148 this.testConfig = testConfig;
149 }
150
151 /**
152 * This test only runs on OpenJDK.
153 * It is ignored for other Java versions since they differ slightly in their rendering engine.
154 * @since 11691
155 */
156 @Before
157 public void forOpenJDK() {
158 String javaHome = System.getProperty("java.home");
159 Assume.assumeTrue("Test requires openJDK", javaHome != null && javaHome.contains("openjdk"));
160
161 List<String> fonts = Arrays.asList(GraphicsEnvironment.getLocalGraphicsEnvironment().getAvailableFontFamilyNames());
162 for (String font : testConfig.fonts) {
163 Assume.assumeTrue("Test requires font: " + font, fonts.contains(font));
164 }
165 }
166
167 /**
168 * Run the test using {@link #testConfig}
169 * @throws Exception if an error occurs
170 */
171 @Test
172 public void testRender() throws Exception {
173 // Force reset of preferences
174 StyledMapRenderer.PREFERENCE_ANTIALIASING_USE.put(true);
175 StyledMapRenderer.PREFERENCE_TEXT_ANTIALIASING.put("gasp");
176
177 // load the data
178 DataSet dataSet = testConfig.getOsmDataSet();
179 dataSet.allPrimitives().stream().forEach(this::loadPrimitiveStyle);
180 dataSet.setSelected(dataSet.allPrimitives().stream().filter(n -> n.isKeyTrue("selected")).collect(Collectors.toList()));
181
182 ProjectionBounds pb = new ProjectionBounds();
183 pb.extend(Main.getProjection().latlon2eastNorth(testConfig.getTestArea().getMin()));
184 pb.extend(Main.getProjection().latlon2eastNorth(testConfig.getTestArea().getMax()));
185 double scale = (pb.maxEast - pb.minEast) / testConfig.imageWidth;
186
187 RenderingHelper.StyleData sd = new RenderingHelper.StyleData();
188 sd.styleUrl = testConfig.getStyleSourceUrl();
189 RenderingHelper rh = new RenderingHelper(dataSet, testConfig.getTestArea(), scale, Collections.singleton(sd));
190 rh.setFillBackground(false);
191 BufferedImage image = rh.render();
192
193 if (UPDATE_ALL) {
194 ImageIO.write(image, "png", new File(testConfig.getTestDirectory() + "/reference.png"));
195 return;
196 }
197
198 BufferedImage reference = testConfig.getReference();
199
200 // now compute differences:
201 assertEquals(image.getWidth(), reference.getWidth());
202 assertEquals(image.getHeight(), reference.getHeight());
203
204 StringBuilder differences = new StringBuilder();
205 ArrayList<Point> differencePoints = new ArrayList<>();
206
207 for (int y = 0; y < reference.getHeight(); y++) {
208 for (int x = 0; x < reference.getWidth(); x++) {
209 int expected = reference.getRGB(x, y);
210 int result = image.getRGB(x, y);
211 if (!colorsAreSame(expected, result)) {
212 differencePoints.add(new Point(x, y));
213 if (differences.length() < 500) {
214 differences.append("\nDifference at ")
215 .append(x)
216 .append(",")
217 .append(y)
218 .append(": Expected ")
219 .append(Integer.toHexString(expected))
220 .append(" but got ")
221 .append(Integer.toHexString(result));
222 }
223 }
224 }
225 }
226
227 if (differencePoints.size() > 0) {
228 // You can use this to debug:
229 ImageIO.write(image, "png", new File(testConfig.getTestDirectory() + "/test-output.png"));
230
231 // Add a nice image that highlights the differences:
232 BufferedImage diffImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB);
233 for (Point p : differencePoints) {
234 diffImage.setRGB(p.x, p.y, 0xffff0000);
235 }
236 ImageIO.write(diffImage, "png", new File(testConfig.getTestDirectory() + "/test-differences.png"));
237
238 fail(MessageFormat.format("Images for test {0} differ at {1} points: {2}",
239 testConfig.testDirectory, differencePoints.size(), differences.toString()));
240 }
241 }
242
243 private void loadPrimitiveStyle(OsmPrimitive n) {
244 n.setHighlighted(n.isKeyTrue("highlight"));
245 if (n.isKeyTrue("disabled")) {
246 n.setDisabledState(false);
247 }
248 }
249
250 /**
251 * Check if two colors differ
252 * @param expected The expected color
253 * @param actual The actual color
254 * @return <code>true</code> if they differ.
255 */
256 private boolean colorsAreSame(int expected, int actual) {
257 int expectedAlpha = expected >> 24;
258 if (expectedAlpha == 0) {
259 return actual >> 24 == 0;
260 } else {
261 return expected == actual;
262 }
263 }
264
265 private static class TestConfig {
266 private final String testDirectory;
267 private Bounds testArea;
268 private final ArrayList<String> fonts = new ArrayList<>();
269 private DataSet ds;
270 private int imageWidth = IMAGE_SIZE;
271
272 TestConfig(String testDirectory, Bounds testArea) {
273 this.testDirectory = testDirectory;
274 this.testArea = testArea;
275 }
276
277 TestConfig(String testDirectory) {
278 this.testDirectory = testDirectory;
279 }
280
281 public TestConfig setImageWidth(int imageWidth) {
282 this.imageWidth = imageWidth;
283 return this;
284 }
285
286 public TestConfig usesFont(String string) {
287 this.fonts.add(string);
288 return this;
289 }
290
291 public BufferedImage getReference() throws IOException {
292 return ImageIO.read(new File(getTestDirectory() + "/reference.png"));
293 }
294
295 private String getTestDirectory() {
296 return TestUtils.getTestDataRoot() + TEST_DATA_BASE + testDirectory;
297 }
298
299 public String getStyleSourceUrl() {
300 return getTestDirectory() + "/style.mapcss";
301 }
302
303 public DataSet getOsmDataSet() throws FileNotFoundException, IllegalDataException {
304 if (ds == null) {
305 ds = OsmReader.parseDataSet(new FileInputStream(getTestDirectory() + "/data.osm"), null);
306 }
307 return ds;
308 }
309
310 public Bounds getTestArea() throws FileNotFoundException, IllegalDataException {
311 if (testArea == null) {
312 testArea = getOsmDataSet().getDataSourceBounds().get(0);
313 }
314 return testArea;
315 }
316
317 @Override
318 public String toString() {
319 return "TestConfig [testDirectory=" + testDirectory + ", testArea=" + testArea + ']';
320 }
321 }
322}
Note: See TracBrowser for help on using the repository browser.