source: josm/trunk/test/unit/org/openstreetmap/josm/gui/mappaint/MapCSSRendererTest.java@ 11691

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

See #13999: Use MapCSS test only for OpenJDK

  • Property svn:eol-style set to native
File size: 9.4 KB
RevLine 
[11433]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
[11691]7import java.awt.Graphics2D;
[11433]8import java.awt.Point;
[11691]9import java.awt.RenderingHints;
[11433]10import java.awt.image.BufferedImage;
11import java.io.File;
12import java.io.FileInputStream;
13import java.io.FileNotFoundException;
14import java.io.IOException;
[11691]15import java.text.MessageFormat;
16import java.util.ArrayList;
[11433]17import java.util.Arrays;
18import java.util.Collection;
19import java.util.stream.Collectors;
20import java.util.stream.Stream;
21
22import javax.imageio.ImageIO;
23
[11691]24import org.junit.Assume;
25import org.junit.Before;
[11433]26import org.junit.Rule;
27import org.junit.Test;
28import org.junit.runner.RunWith;
29import org.junit.runners.Parameterized;
30import org.junit.runners.Parameterized.Parameters;
31import org.openstreetmap.josm.TestUtils;
32import org.openstreetmap.josm.data.Bounds;
33import org.openstreetmap.josm.data.osm.DataSet;
34import org.openstreetmap.josm.data.osm.visitor.paint.StyledMapRenderer;
35import org.openstreetmap.josm.gui.NavigatableComponent;
36import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
37import org.openstreetmap.josm.gui.preferences.SourceEntry;
38import org.openstreetmap.josm.io.IllegalDataException;
39import org.openstreetmap.josm.io.OsmReader;
40import org.openstreetmap.josm.testutils.JOSMTestRules;
41
42import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
43
44/**
45 * Test cases for {@link StyledMapRenderer} and the MapCSS classes.
46 * <p>
47 * This test uses the data and reference files stored in the test data directory {@value #TEST_DATA_BASE}
48 * @author Michael Zangl
49 */
50@RunWith(Parameterized.class)
51public class MapCSSRendererTest {
52 private static final String TEST_DATA_BASE = "/renderer/";
53 /**
54 * lat = 0..1, lon = 0..1
55 */
56 private static final Bounds AREA_DEFAULT = new Bounds(0, 0, 1, 1);
57 private static final int IMAGE_SIZE = 256;
58
59 /**
60 * Minimal test rules required
61 */
62 @Rule
63 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
64 public JOSMTestRules test = new JOSMTestRules().preferences().projection();
65
66 private TestConfig testConfig;
67
68 /**
69 * The different configurations of this test.
70 * @return The parameters.
71 */
[11691]72 @Parameters(name="{1}")
[11436]73 public static Collection<Object[]> runs() {
[11433]74 return Stream.of(
75 /** Tests for StyledMapRenderer#drawNodeSymbol */
76 new TestConfig("node-shapes", AREA_DEFAULT),
77
78 /** Tests that StyledMapRenderer#drawWay respects width */
79 new TestConfig("way-width", AREA_DEFAULT)
80
[11691]81 ).map(e -> new Object[] {e, e.testDirectory})
[11433]82 .collect(Collectors.toList());
83 }
84
85 /**
86 * @param testConfig The config to use for this test.
[11691]87 * @param ignored The name to print it nicely
[11433]88 */
[11691]89 public MapCSSRendererTest(TestConfig testConfig, String ignored) {
[11433]90 this.testConfig = testConfig;
91 }
92
93 /**
[11691]94 * This test only runs on OpenJDK.
95 * It is ignored for other Java versions since they differ slightly in their rendering engine.
96 * @since 11691
97 */
98 @Before
99 public void testForOpenJDK() {
100 String javaHome = System.getProperty("java.home");
101 Assume.assumeTrue(javaHome != null && javaHome.contains("openjdk"));
102 }
103
104 /**
[11433]105 * Run the test using {@link #testConfig}
106 * @throws Exception if an error occurs
107 */
108 @Test
[11436]109 public void testRender() throws Exception {
[11433]110 // load the data
111 DataSet dataSet = testConfig.getOsmDataSet();
112
113 // load the style
114 MapCSSStyleSource.STYLE_SOURCE_LOCK.writeLock().lock();
115 try {
116 MapPaintStyles.getStyles().clear();
117
118 MapCSSStyleSource source = new MapCSSStyleSource(testConfig.getStyleSourceEntry());
119 source.loadStyleSource();
120 if (!source.getErrors().isEmpty()) {
121 fail("Failed to load style file. Errors: " + source.getErrors());
122 }
123 MapPaintStyles.getStyles().setStyleSources(Arrays.asList(source));
124
125 } finally {
126 MapCSSStyleSource.STYLE_SOURCE_LOCK.writeLock().unlock();
127 }
128
129 // create the renderer
130 BufferedImage image = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE, BufferedImage.TYPE_INT_ARGB);
131 NavigatableComponent nc = new NavigatableComponent() {
132 {
133 setBounds(0, 0, IMAGE_SIZE, IMAGE_SIZE);
134 updateLocationState();
135 }
136
137 @Override
138 protected boolean isVisibleOnScreen() {
139 return true;
140 }
141
142 @Override
143 public Point getLocationOnScreen() {
144 return new Point(0, 0);
145 }
146 };
147 nc.zoomTo(testConfig.testArea);
148 dataSet.allPrimitives().stream().forEach(n -> n.setHighlighted(n.isKeyTrue("highlight")));
[11691]149 Graphics2D g = image.createGraphics();
150 // Force all render hints to be defaults - do not use platform values
151 g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
152 g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
153 g.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY);
154 g.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE);
155 g.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON);
156 g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
157 g.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
158 g.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_NORMALIZE);
159 g.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
160 new StyledMapRenderer(g, nc, false).render(dataSet, false, testConfig.testArea);
[11433]161
162 BufferedImage reference = testConfig.getReference();
163
164 // now compute differences:
165 assertEquals(IMAGE_SIZE, reference.getWidth());
166 assertEquals(IMAGE_SIZE, reference.getHeight());
167
168 StringBuilder differences = new StringBuilder();
[11691]169 ArrayList<Point> differencePoints = new ArrayList<>();
[11433]170
171 for (int y = 0; y < reference.getHeight(); y++) {
172 for (int x = 0; x < reference.getWidth(); x++) {
173 int expected = reference.getRGB(x, y);
174 int result = image.getRGB(x, y);
[11691]175 if (!colorsAreSame(expected, result)) {
176 differencePoints.add(new Point(x, y));
177 if (differences.length() < 500) {
178 differences.append("\nDifference at ")
179 .append(x)
180 .append(",")
181 .append(y)
182 .append(": Expected ")
183 .append(Integer.toHexString(expected))
184 .append(" but got ")
185 .append(Integer.toHexString(result));
186 }
[11433]187 }
188 }
189 }
190
[11691]191 if (differencePoints.size() > 0) {
[11433]192 // You can use this to debug:
193 ImageIO.write(image, "png", new File(testConfig.getTestDirectory() + "/test-output.png"));
[11691]194
195 // Add a nice image that highlights the differences:
196 BufferedImage diffImage = new BufferedImage(IMAGE_SIZE, IMAGE_SIZE, BufferedImage.TYPE_INT_ARGB);
197 for (Point p : differencePoints) {
198 diffImage.setRGB(p.x, p.y, 0xffff0000);
199 }
200 ImageIO.write(diffImage, "png", new File(testConfig.getTestDirectory() + "/test-differences.png"));
201
202 fail(MessageFormat.format("Images for test {1} differ at {2} points: {3}",
203 testConfig.testDirectory, differencePoints.size(), differences.toString()));
[11433]204 }
205 }
206
[11691]207 /**
208 * Check if two colors differ
209 * @param expected
210 * @param result
211 * @return <code>true</code> if they differ.
212 */
213 private boolean colorsAreSame(int expected, int result) {
214 int expectedAlpha = expected >> 24;
215 if (expectedAlpha == 0) {
216 return (result & 0xff000000) == 0;
217 } else {
218 return expected == result;
219 }
220 }
221
[11433]222 private static class TestConfig {
223 private final String testDirectory;
224 private final Bounds testArea;
225
226 TestConfig(String testDirectory, Bounds testArea) {
227 this.testDirectory = testDirectory;
228 this.testArea = testArea;
229 }
230
231 public BufferedImage getReference() throws IOException {
232 return ImageIO.read(new File(getTestDirectory() + "/reference.png"));
233 }
234
235 private String getTestDirectory() {
236 return TestUtils.getTestDataRoot() + TEST_DATA_BASE + testDirectory;
237 }
238
239 public SourceEntry getStyleSourceEntry() {
240 return new SourceEntry(getTestDirectory() + "/style.mapcss",
241 "test style", "a test style", true // active
242 );
243 }
244
245 public DataSet getOsmDataSet() throws FileNotFoundException, IllegalDataException {
246 return OsmReader.parseDataSet(new FileInputStream(getTestDirectory() + "/data.osm"), null);
247 }
248
249 @Override
250 public String toString() {
251 return "TestConfig [testDirectory=" + testDirectory + ", testArea=" + testArea + ']';
252 }
253 }
254}
Note: See TracBrowser for help on using the repository browser.