250 lines
7.0 KiB
Python
250 lines
7.0 KiB
Python
import os
|
|
|
|
import mercantile
|
|
import pytest
|
|
import requests
|
|
|
|
import xyzservices.providers as xyz
|
|
|
|
flat_free = xyz.filter(requires_token=False).flatten()
|
|
|
|
|
|
def check_provider(provider):
|
|
for key in ["attribution", "name"]:
|
|
assert key in provider
|
|
assert provider.url.startswith("http")
|
|
for option in ["{z}", "{y}", "{x}"]:
|
|
assert option in provider.url
|
|
|
|
|
|
def get_tile(provider):
|
|
bounds = provider.get("bounds", [[-180, -90], [180, 90]])
|
|
lat = (bounds[0][0] + bounds[1][0]) / 2
|
|
lon = (bounds[0][1] + bounds[1][1]) / 2
|
|
zoom = (provider.get("min_zoom", 0) + provider.get("max_zoom", 20)) // 2
|
|
tile = mercantile.tile(lon, lat, zoom)
|
|
z = tile.z
|
|
x = tile.x
|
|
y = tile.y
|
|
return (z, x, y)
|
|
|
|
|
|
def get_response(url):
|
|
s = requests.Session()
|
|
a = requests.adapters.HTTPAdapter(max_retries=3)
|
|
s.mount("http://", a)
|
|
s.mount("https://", a)
|
|
try:
|
|
r = s.get(url, timeout=30)
|
|
except requests.ConnectionError:
|
|
pytest.xfail("Timeout.")
|
|
return r.status_code
|
|
|
|
|
|
def get_test_result(provider, allow_403=True):
|
|
if provider.get("status"):
|
|
pytest.xfail("Provider is known to be broken.")
|
|
|
|
z, x, y = get_tile(provider)
|
|
|
|
try:
|
|
r = get_response(provider.build_url(z=z, x=x, y=y))
|
|
assert r == requests.codes.ok
|
|
except AssertionError:
|
|
if r == 403 and allow_403:
|
|
pytest.xfail("Provider not available due to API restrictions (Error 403).")
|
|
|
|
elif r == 503:
|
|
pytest.xfail("Service temporarily unavailable (Error 503).")
|
|
|
|
elif r == 502:
|
|
pytest.xfail("Bad Gateway (Error 502).")
|
|
|
|
# check another tiles
|
|
elif r == 404:
|
|
# in some cases, the computed tile is not available. trying known tiles.
|
|
options = [
|
|
(12, 2154, 1363),
|
|
(6, 13, 21),
|
|
(16, 33149, 22973),
|
|
(0, 0, 0),
|
|
(2, 6, 7),
|
|
(6, 21, 31),
|
|
(6, 21, 32),
|
|
(6, 21, 33),
|
|
(6, 22, 31),
|
|
(6, 22, 32),
|
|
(6, 22, 33),
|
|
(6, 23, 31),
|
|
(6, 23, 32),
|
|
(6, 23, 33),
|
|
(9, 259, 181),
|
|
(12, 2074, 1410),
|
|
]
|
|
results = []
|
|
for o in options:
|
|
z, x, y = o
|
|
r = get_response(provider.build_url(z=z, x=x, y=y))
|
|
results.append(r)
|
|
if not any(x == requests.codes.ok for x in results):
|
|
raise ValueError(f"Response code: {r}")
|
|
else:
|
|
raise ValueError(f"Response code: {r}")
|
|
|
|
|
|
@pytest.mark.parametrize("provider_name", xyz.flatten())
|
|
def test_minimal_provider_metadata(provider_name):
|
|
provider = xyz.flatten()[provider_name]
|
|
check_provider(provider)
|
|
|
|
|
|
@pytest.mark.request
|
|
@pytest.mark.parametrize("name", flat_free)
|
|
def test_free_providers(name):
|
|
provider = flat_free[name]
|
|
if "Stadia" in name:
|
|
pytest.skip("Stadia doesn't support tile download in this way.")
|
|
elif "GeoportailFrance" in name:
|
|
try:
|
|
get_test_result(provider)
|
|
except ValueError:
|
|
pytest.xfail("GeoportailFrance API is unstable.")
|
|
else:
|
|
get_test_result(provider)
|
|
|
|
|
|
# test providers requiring API keys. Store API keys in GitHub secrets and load them as
|
|
# environment variables in CI Action. Note that env variable is loaded as empty on PRs
|
|
# from a fork.
|
|
|
|
|
|
@pytest.mark.request
|
|
@pytest.mark.parametrize("provider_name", xyz.Thunderforest)
|
|
def test_thunderforest(provider_name):
|
|
try:
|
|
token = os.environ["THUNDERFOREST"]
|
|
except KeyError:
|
|
pytest.xfail("Missing API token.")
|
|
if token == "":
|
|
pytest.xfail("Token empty.")
|
|
|
|
provider = xyz.Thunderforest[provider_name](apikey=token)
|
|
get_test_result(provider, allow_403=False)
|
|
|
|
|
|
@pytest.mark.request
|
|
@pytest.mark.parametrize("provider_name", xyz.Jawg)
|
|
def test_jawg(provider_name):
|
|
try:
|
|
token = os.environ["JAWG"]
|
|
except KeyError:
|
|
pytest.xfail("Missing API token.")
|
|
if token == "":
|
|
pytest.xfail("Token empty.")
|
|
|
|
provider = xyz.Jawg[provider_name](accessToken=token)
|
|
get_test_result(provider, allow_403=False)
|
|
|
|
|
|
@pytest.mark.request
|
|
def test_mapbox():
|
|
try:
|
|
token = os.environ["MAPBOX"]
|
|
except KeyError:
|
|
pytest.xfail("Missing API token.")
|
|
if token == "":
|
|
pytest.xfail("Token empty.")
|
|
|
|
provider = xyz.MapBox(accessToken=token)
|
|
get_test_result(provider, allow_403=False)
|
|
|
|
|
|
@pytest.mark.request
|
|
@pytest.mark.parametrize("provider_name", xyz.MapTiler)
|
|
def test_maptiler(provider_name):
|
|
try:
|
|
token = os.environ["MAPTILER"]
|
|
except KeyError:
|
|
pytest.xfail("Missing API token.")
|
|
if token == "":
|
|
pytest.xfail("Token empty.")
|
|
|
|
provider = xyz.MapTiler[provider_name](key=token)
|
|
get_test_result(provider, allow_403=False)
|
|
|
|
|
|
@pytest.mark.request
|
|
@pytest.mark.parametrize("provider_name", xyz.TomTom)
|
|
def test_tomtom(provider_name):
|
|
try:
|
|
token = os.environ["TOMTOM"]
|
|
except KeyError:
|
|
pytest.xfail("Missing API token.")
|
|
if token == "":
|
|
pytest.xfail("Token empty.")
|
|
|
|
provider = xyz.TomTom[provider_name](apikey=token)
|
|
get_test_result(provider, allow_403=False)
|
|
|
|
|
|
@pytest.mark.request
|
|
@pytest.mark.parametrize("provider_name", xyz.OpenWeatherMap)
|
|
def test_openweathermap(provider_name):
|
|
try:
|
|
token = os.environ["OPENWEATHERMAP"]
|
|
except KeyError:
|
|
pytest.xfail("Missing API token.")
|
|
if token == "":
|
|
pytest.xfail("Token empty.")
|
|
|
|
provider = xyz.OpenWeatherMap[provider_name](apiKey=token)
|
|
get_test_result(provider, allow_403=False)
|
|
|
|
|
|
# HEREV3 seems to block GHA as it errors with E429
|
|
# @pytest.mark.request
|
|
# @pytest.mark.parametrize("provider_name", xyz.HEREv3)
|
|
# def test_herev3(provider_name):
|
|
# try:
|
|
# token = os.environ["HEREV3"]
|
|
# except KeyError:
|
|
# pytest.xfail("Missing API token.")
|
|
# if token == "":
|
|
# pytest.xfail("Token empty.")
|
|
|
|
# provider = xyz.HEREv3[provider_name](apiKey=token)
|
|
# get_test_result(provider, allow_403=False)
|
|
|
|
|
|
@pytest.mark.request
|
|
@pytest.mark.parametrize("provider_name", xyz.Stadia)
|
|
def test_stadia(provider_name):
|
|
try:
|
|
token = os.environ["STADIA"]
|
|
except KeyError:
|
|
pytest.xfail("Missing API token.")
|
|
if token == "":
|
|
pytest.xfail("Token empty.")
|
|
|
|
provider = xyz.Stadia[provider_name](api_key=token)
|
|
provider["url"] = provider["url"] + "?api_key={api_key}"
|
|
get_test_result(provider, allow_403=False)
|
|
|
|
|
|
@pytest.mark.request
|
|
@pytest.mark.parametrize("provider_name", xyz.OrdnanceSurvey)
|
|
def test_os(provider_name):
|
|
try:
|
|
token = os.environ["ORDNANCESURVEY"]
|
|
except KeyError:
|
|
pytest.xfail("Missing API token.")
|
|
if token == "":
|
|
pytest.xfail("Token empty.")
|
|
|
|
provider = xyz.OrdnanceSurvey[provider_name](key=token)
|
|
get_test_result(provider, allow_403=False)
|
|
|
|
|
|
# NOTE: AzureMaps are not tested as their free account is limited to
|
|
# 5000 downloads (total, not per month)
|