source: josm/trunk/test/unit/org/openstreetmap/josm/gui/preferences/imagery/ImageryPreferenceTestIT.java@ 14527

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

see #16073 - fix timeouts

  • Property svn:eol-style set to native
File size: 9.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.preferences.imagery;
3
4import static org.junit.Assert.assertTrue;
5
6import java.io.IOException;
7import java.net.URL;
8import java.util.ArrayList;
9import java.util.Collections;
10import java.util.HashSet;
11import java.util.List;
12import java.util.Map;
13import java.util.Set;
14import java.util.TreeMap;
15import java.util.concurrent.TimeUnit;
16
17import org.junit.Rule;
18import org.junit.Test;
19import org.openstreetmap.gui.jmapviewer.Coordinate;
20import org.openstreetmap.gui.jmapviewer.TileXY;
21import org.openstreetmap.gui.jmapviewer.interfaces.ICoordinate;
22import org.openstreetmap.gui.jmapviewer.tilesources.AbstractTileSource;
23import org.openstreetmap.gui.jmapviewer.tilesources.BingAerialTileSource;
24import org.openstreetmap.gui.jmapviewer.tilesources.ScanexTileSource;
25import org.openstreetmap.gui.jmapviewer.tilesources.TemplatedTMSTileSource;
26import org.openstreetmap.josm.data.Bounds;
27import org.openstreetmap.josm.data.coor.LatLon;
28import org.openstreetmap.josm.data.imagery.CoordinateConversion;
29import org.openstreetmap.josm.data.imagery.ImageryInfo;
30import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryBounds;
31import org.openstreetmap.josm.data.imagery.ImageryInfo.ImageryType;
32import org.openstreetmap.josm.data.imagery.ImageryLayerInfo;
33import org.openstreetmap.josm.data.imagery.Shape;
34import org.openstreetmap.josm.data.imagery.TemplatedWMSTileSource;
35import org.openstreetmap.josm.data.imagery.WMSEndpointTileSource;
36import org.openstreetmap.josm.data.imagery.WMTSTileSource;
37import org.openstreetmap.josm.data.imagery.WMTSTileSource.WMTSGetCapabilitiesException;
38import org.openstreetmap.josm.data.projection.Projection;
39import org.openstreetmap.josm.data.projection.ProjectionRegistry;
40import org.openstreetmap.josm.data.projection.Projections;
41import org.openstreetmap.josm.testutils.JOSMTestRules;
42import org.openstreetmap.josm.tools.HttpClient;
43import org.openstreetmap.josm.tools.HttpClient.Response;
44import org.openstreetmap.josm.tools.Logging;
45import org.openstreetmap.josm.tools.Utils;
46
47import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
48
49/**
50 * Integration tests of {@link ImageryPreference} class.
51 */
52public class ImageryPreferenceTestIT {
53
54 /**
55 * Setup rule
56 */
57 @Rule
58 @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD")
59 public JOSMTestRules test = new JOSMTestRules().https().projection().projectionNadGrids().timeout(10000*60);
60
61 private final Map<String, Map<ImageryInfo, List<String>>> errors = Collections.synchronizedMap(new TreeMap<>());
62 private final Set<String> workingURLs = Collections.synchronizedSet(new HashSet<>());
63
64 private boolean addError(ImageryInfo info, String error) {
65 return errors.computeIfAbsent(info.getCountryCode(), x -> Collections.synchronizedMap(new TreeMap<>()))
66 .computeIfAbsent(info, x -> Collections.synchronizedList(new ArrayList<>()))
67 .add(error);
68 }
69
70 private void checkUrl(ImageryInfo info, String url) {
71 if (url != null && !url.isEmpty() && !workingURLs.contains(url)) {
72 try {
73 Response response = HttpClient.create(new URL(url))
74 .setConnectTimeout((int) TimeUnit.SECONDS.toMillis(30))
75 .setReadTimeout((int) TimeUnit.SECONDS.toMillis(60))
76 .connect();
77 if (response.getResponseCode() >= 400) {
78 addError(info, url + " -> HTTP " + response.getResponseCode());
79 } else if (response.getResponseCode() >= 300) {
80 Logging.warn(url + " -> HTTP " + response.getResponseCode());
81 } else {
82 workingURLs.add(url);
83 }
84 response.disconnect();
85 } catch (IOException e) {
86 addError(info, url + " -> " + e);
87 }
88 }
89 }
90
91 private void checkTileUrl(ImageryInfo info, AbstractTileSource tileSource, ICoordinate center, int zoom)
92 throws IOException {
93 TileXY xy = tileSource.latLonToTileXY(center, zoom);
94 for (int i = 0; i < 3; i++) {
95 try {
96 checkUrl(info, tileSource.getTileUrl(zoom, xy.getXIndex(), xy.getYIndex()));
97 return;
98 } catch (IOException e) {
99 // Try up to three times max to allow Bing source to initialize itself
100 // and avoid random network errors
101 Logging.trace(e);
102 if (i == 2) {
103 throw e;
104 }
105 try {
106 Thread.sleep(500);
107 } catch (InterruptedException ex) {
108 Logging.warn(ex);
109 }
110 }
111 }
112 }
113
114 private static LatLon getPointInShape(Shape shape) {
115 final Coordinate p1 = shape.getPoints().get(0);
116 final Bounds bounds = new Bounds(p1.getLat(), p1.getLon(), p1.getLat(), p1.getLon());
117 shape.getPoints().forEach(p -> bounds.extend(p.getLat(), p.getLon()));
118
119 final double w = bounds.getWidth();
120 final double h = bounds.getHeight();
121
122 final double x2 = bounds.getMinLon() + (w / 2.0);
123 final double y2 = bounds.getMinLat() + (h / 2.0);
124
125 final LatLon center = new LatLon(y2, x2);
126
127 // check to see if center is inside shape
128 if (shape.contains(center)) {
129 return center;
130 }
131
132 // if center position (C) is not inside shape, try naively some other positions as follows:
133 final double x1 = bounds.getMinLon() + (.25 * w);
134 final double x3 = bounds.getMinLon() + (.75 * w);
135 final double y1 = bounds.getMinLat() + (.25 * h);
136 final double y3 = bounds.getMinLat() + (.75 * h);
137 // +-----------+
138 // | 5 1 6 |
139 // | 4 C 2 |
140 // | 8 3 7 |
141 // +-----------+
142 for (LatLon candidate : new LatLon[] {
143 new LatLon(y1, x2),
144 new LatLon(y2, x3),
145 new LatLon(y3, x2),
146 new LatLon(y2, x1),
147 new LatLon(y1, x1),
148 new LatLon(y1, x3),
149 new LatLon(y3, x3),
150 new LatLon(y3, x1)
151 }) {
152 if (shape.contains(candidate)) {
153 return candidate;
154 }
155 }
156 return center;
157 }
158
159 private static LatLon getCenter(ImageryBounds bounds) {
160 List<Shape> shapes = bounds.getShapes();
161 return shapes != null && !shapes.isEmpty() ? getPointInShape(shapes.get(0)) : bounds.getCenter();
162 }
163
164 private void checkEntry(ImageryInfo info) {
165 Logging.info("Checking "+ info);
166
167 if (info.getAttributionImageRaw() != null && info.getAttributionImage() == null) {
168 addError(info, "Can't fetch attribution image: " + info.getAttributionImageRaw());
169 }
170
171 checkUrl(info, info.getAttributionImageURL());
172 checkUrl(info, info.getAttributionLinkURL());
173 String eula = info.getEulaAcceptanceRequired();
174 if (eula != null) {
175 checkUrl(info, eula.replaceAll("\\{lang\\}", ""));
176 }
177 checkUrl(info, info.getPermissionReferenceURL());
178 checkUrl(info, info.getTermsOfUseURL());
179
180 try {
181 ImageryBounds bounds = info.getBounds();
182 // Some imagery sources do not define tiles at (0,0). So pickup Greenwich Royal Observatory for global sources
183 ICoordinate center = CoordinateConversion.llToCoor(bounds != null ? getCenter(bounds) : new LatLon(51.47810, -0.00170));
184 AbstractTileSource tileSource = getTileSource(info);
185 checkTileUrl(info, tileSource, center, info.getMinZoom());
186 // checking max zoom for real is complex, see https://josm.openstreetmap.de/ticket/16073#comment:27
187 if (info.getMaxZoom() > 0 && info.getImageryType() != ImageryType.SCANEX) {
188 checkTileUrl(info, tileSource, center, Utils.clamp(12, info.getMinZoom() + 1, info.getMaxZoom()));
189 }
190 } catch (IOException | WMTSGetCapabilitiesException | IllegalArgumentException e) {
191 addError(info, e.toString());
192 }
193
194 for (ImageryInfo mirror : info.getMirrors()) {
195 checkEntry(mirror);
196 }
197 }
198
199 private static Projection getProjection(ImageryInfo info) {
200 if (!info.getServerProjections().isEmpty()) {
201 Projection proj = Projections.getProjectionByCode(info.getServerProjections().get(0));
202 if (proj != null) {
203 return proj;
204 }
205 }
206 return ProjectionRegistry.getProjection();
207 }
208
209 private static AbstractTileSource getTileSource(ImageryInfo info) throws IOException, WMTSGetCapabilitiesException {
210 switch (info.getImageryType()) {
211 case BING:
212 return new BingAerialTileSource(info);
213 case SCANEX:
214 return new ScanexTileSource(info);
215 case TMS:
216 return new TemplatedTMSTileSource(info);
217 case WMS:
218 return new TemplatedWMSTileSource(info, getProjection(info));
219 case WMS_ENDPOINT:
220 return new WMSEndpointTileSource(info, getProjection(info));
221 case WMTS:
222 return new WMTSTileSource(info, getProjection(info));
223 default:
224 throw new UnsupportedOperationException(info.toString());
225 }
226 }
227
228 /**
229 * Test that available imagery entries are valid.
230 * @throws Exception in case of error
231 */
232 @Test
233 public void testValidityOfAvailableImageryEntries() throws Exception {
234 ImageryLayerInfo.instance.load(false);
235 ImageryLayerInfo.instance.getDefaultLayers().parallelStream().forEach(this::checkEntry);
236 assertTrue(errors.toString().replaceAll("\\}, ", "\n\\}, ").replaceAll(", ImageryInfo\\{", "\n ,ImageryInfo\\{"),
237 errors.isEmpty());
238 }
239}
Note: See TracBrowser for help on using the repository browser.