source: josm/trunk/test/unit/org/openstreetmap/josm/data/projection/ProjectionRegressionTest.java@ 10061

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

see #11924 - reduce double precision in unit tests to avoid tiny differences with JDK9

  • Property svn:eol-style set to native
File size: 8.7 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.projection;
3
4import java.io.BufferedReader;
5import java.io.BufferedWriter;
6import java.io.File;
7import java.io.FileInputStream;
8import java.io.FileNotFoundException;
9import java.io.FileOutputStream;
10import java.io.IOException;
11import java.io.InputStreamReader;
12import java.io.OutputStreamWriter;
13import java.nio.charset.StandardCharsets;
14import java.util.ArrayList;
15import java.util.HashMap;
16import java.util.HashSet;
17import java.util.List;
18import java.util.Map;
19import java.util.Random;
20import java.util.Set;
21import java.util.TreeSet;
22
23import org.junit.BeforeClass;
24import org.junit.Test;
25import org.openstreetmap.josm.JOSMFixture;
26import org.openstreetmap.josm.TestUtils;
27import org.openstreetmap.josm.data.Bounds;
28import org.openstreetmap.josm.data.coor.EastNorth;
29import org.openstreetmap.josm.data.coor.LatLon;
30import org.openstreetmap.josm.tools.Pair;
31
32/**
33 * This test is used to monitor changes in projection code.
34 *
35 * It keeps a record of test data in the file data_nodist/projection/projection-regression-test-data.
36 * This record is generated from the current Projection classes available in JOSM. It needs to
37 * be updated, whenever a projection is added / removed or an algorithm is changed, such that
38 * the computed values are numerically different. There is no error threshold, every change is reported.
39 *
40 * So when this test fails, first check if the change is intended. Then update the regression
41 * test data, by running the main method of this class and commit the new data file.
42 */
43public class ProjectionRegressionTest {
44
45 private static final String PROJECTION_DATA_FILE = "data_nodist/projection/projection-regression-test-data";
46
47 private static class TestData {
48 public String code;
49 public LatLon ll;
50 public EastNorth en;
51 public LatLon ll2;
52 }
53
54 /**
55 * Program entry point to update reference projection file.
56 * @param args not used
57 * @throws IOException if any I/O errors occurs
58 */
59 public static void main(String[] args) throws IOException {
60 setUp();
61
62 Map<String, Projection> supportedCodesMap = new HashMap<>();
63 for (String code : Projections.getAllProjectionCodes()) {
64 supportedCodesMap.put(code, Projections.getProjectionByCode(code));
65 }
66
67 List<TestData> prevData = new ArrayList<>();
68 if (new File(PROJECTION_DATA_FILE).exists()) {
69 prevData = readData();
70 }
71 Map<String, TestData> prevCodesMap = new HashMap<>();
72 for (TestData data : prevData) {
73 prevCodesMap.put(data.code, data);
74 }
75
76 Set<String> codesToWrite = new TreeSet<>();
77 for (TestData data : prevData) {
78 if (supportedCodesMap.containsKey(data.code)) {
79 codesToWrite.add(data.code);
80 }
81 }
82 for (String code : supportedCodesMap.keySet()) {
83 if (!codesToWrite.contains(code)) {
84 codesToWrite.add(code);
85 }
86 }
87
88 Random rand = new Random();
89 try (BufferedWriter out = new BufferedWriter(new OutputStreamWriter(
90 new FileOutputStream(PROJECTION_DATA_FILE), StandardCharsets.UTF_8))) {
91 out.write("# Data for test/unit/org/openstreetmap/josm/data/projection/ProjectionRegressionTest.java\n");
92 out.write("# Format: 1. Projection code; 2. lat/lon; 3. lat/lon projected -> east/north; 4. east/north (3.) inverse projected\n");
93 for (String code : codesToWrite) {
94 Projection proj = supportedCodesMap.get(code);
95 Bounds b = proj.getWorldBoundsLatLon();
96 double lat, lon;
97 TestData prev = prevCodesMap.get(proj.toCode());
98 if (prev != null) {
99 lat = prev.ll.lat();
100 lon = prev.ll.lon();
101 } else {
102 lat = b.getMin().lat() + rand.nextDouble() * (b.getMax().lat() - b.getMin().lat());
103 lon = b.getMin().lon() + rand.nextDouble() * (b.getMax().lon() - b.getMin().lon());
104 }
105 EastNorth en = proj.latlon2eastNorth(new LatLon(lat, lon));
106 LatLon ll2 = proj.eastNorth2latlon(en);
107 out.write(String.format(
108 "%s%n ll %s %s%n en %s %s%n ll2 %s %s%n", proj.toCode(), lat, lon, en.east(), en.north(), ll2.lat(), ll2.lon()));
109 }
110 }
111 System.out.println("Update successful.");
112 }
113
114 private static EastNorth getRoundedToOsmPrecision(double east, double north) {
115 return new EastNorth(LatLon.roundToOsmPrecision(east), LatLon.roundToOsmPrecision(north));
116 }
117
118 private static List<TestData> readData() throws IOException, FileNotFoundException {
119 try (BufferedReader in = new BufferedReader(new InputStreamReader(new FileInputStream(PROJECTION_DATA_FILE),
120 StandardCharsets.UTF_8))) {
121 List<TestData> result = new ArrayList<>();
122 String line;
123 while ((line = in.readLine()) != null) {
124 if (line.startsWith("#")) {
125 continue;
126 }
127 TestData next = new TestData();
128
129 Pair<Double, Double> ll = readLine("ll", in.readLine());
130 Pair<Double, Double> en = readLine("en", in.readLine());
131 Pair<Double, Double> ll2 = readLine("ll2", in.readLine());
132
133 next.code = line;
134 next.ll = new LatLon(ll.a, ll.b);
135 next.en = new EastNorth(en.a, en.b);
136 next.ll2 = new LatLon(ll2.a, ll2.b);
137 if (TestUtils.getJavaVersion() >= 9) {
138 next.ll = next.ll.getRoundedToOsmPrecision();
139 next.en = getRoundedToOsmPrecision(en.a, en.b);
140 next.ll2 = next.ll2.getRoundedToOsmPrecision();
141 }
142
143 result.add(next);
144 }
145 return result;
146 }
147 }
148
149 private static Pair<Double, Double> readLine(String expectedName, String input) {
150 String[] fields = input.trim().split("[ ]+");
151 if (fields.length != 3) throw new AssertionError();
152 if (!fields[0].equals(expectedName)) throw new AssertionError();
153 double a = Double.parseDouble(fields[1]);
154 double b = Double.parseDouble(fields[2]);
155 return Pair.create(a, b);
156 }
157
158 /**
159 * Setup test.
160 */
161 @BeforeClass
162 public static void setUp() {
163 JOSMFixture.createUnitTestFixture().init();
164 }
165
166 /**
167 * Non-regression unit test.
168 * @throws IOException if any I/O error occurs
169 */
170 @Test
171 public void regressionTest() throws IOException {
172 List<TestData> allData = readData();
173 Set<String> dataCodes = new HashSet<>();
174 for (TestData data : allData) {
175 dataCodes.add(data.code);
176 }
177
178 StringBuilder fail = new StringBuilder();
179
180 for (String code : Projections.getAllProjectionCodes()) {
181 if (!dataCodes.contains(code)) {
182 fail.append("Did not find projection "+code+" in test data!\n");
183 }
184 }
185
186 for (TestData data : allData) {
187 Projection proj = Projections.getProjectionByCode(data.code);
188 if (proj == null) {
189 fail.append("Projection "+data.code+" from test data was not found!\n");
190 continue;
191 }
192 EastNorth en = proj.latlon2eastNorth(data.ll);
193 if (TestUtils.getJavaVersion() >= 9) {
194 en = getRoundedToOsmPrecision(en.east(), en.north());
195 }
196 if (!en.equals(data.en)) {
197 String error = String.format("%s (%s): Projecting latlon(%s,%s):%n" +
198 " expected: eastnorth(%s,%s),%n" +
199 " but got: eastnorth(%s,%s)!%n",
200 proj.toString(), data.code, data.ll.lat(), data.ll.lon(), data.en.east(), data.en.north(), en.east(), en.north());
201 fail.append(error);
202 }
203 LatLon ll2 = proj.eastNorth2latlon(data.en);
204 if (TestUtils.getJavaVersion() >= 9) {
205 ll2 = ll2.getRoundedToOsmPrecision();
206 }
207 if (!ll2.equals(data.ll2)) {
208 String error = String.format("%s (%s): Inverse projecting eastnorth(%s,%s):%n" +
209 " expected: latlon(%s,%s),%n" +
210 " but got: latlon(%s,%s)!%n",
211 proj.toString(), data.code, data.en.east(), data.en.north(), data.ll2.lat(), data.ll2.lon(), ll2.lat(), ll2.lon());
212 fail.append(error);
213 }
214 }
215
216 if (fail.length() > 0) {
217 System.err.println(fail.toString());
218 throw new AssertionError(fail.toString());
219 }
220 }
221}
Note: See TracBrowser for help on using the repository browser.