earthengine-api/python/ee/apitestcase.py
Kurt Schwehr ff7a5fbdbc ee pyupgrade --py39-plus
PiperOrigin-RevId: 780619373
2025-07-08 10:27:19 -07:00

377 lines
15 KiB
Python

"""A TestCase that initializes the library with standard API methods."""
from collections.abc import Iterable
import contextlib
import json
import os
from typing import Any, Optional
from googleapiclient import discovery
import unittest
import ee
from ee import _cloud_api_utils
# Cached algorithms list
_algorithms_cache: Optional[dict[str, Any]] = None
def GetAlgorithms() -> dict[str, Any]:
"""Returns a static version of the ListAlgorithms call.
After ApiTestCase.setUp is called, ee.data.getAlgorithms() is patched to use
this data. This function may be called explicitly if the test data is needed
at a time before the test case has started executing.
"""
global _algorithms_cache
if _algorithms_cache is not None:
return _algorithms_cache
algorithms_path = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
"tests/algorithms.json")
with open(algorithms_path, encoding='utf-8') as algorithms_file:
algorithms = json.load(algorithms_file)
_algorithms_cache = _cloud_api_utils.convert_algorithms(algorithms)
return _algorithms_cache
class ApiTestCase(unittest.TestCase):
"""A TestCase that initializes the library with standard API methods."""
last_download_call: Optional[Any]
last_thumb_call: Optional[Any]
last_table_call: Optional[Any]
last_mapid_call: Optional[Any]
def setUp(self):
super().setUp()
# Store the state of ee.data before it is overwritten. We need to restore to
# this state in tearDown to prevent state leakage between tests.
self.old_get_algorithms = ee.data.getAlgorithms
self.old_compute_value = ee.data.computeValue
self.old_get_map_id = ee.data.getMapId
self.old_get_download_id = ee.data.getDownloadId
self.old_get_thumb_id = ee.data.getThumbId
self.old_get_table_download_id = ee.data.getTableDownloadId
# pylint: disable=protected-access
self.old_install_cloud_api_resource = ee.data._install_cloud_api_resource
self.old_cloud_api_resource = ee.data._cloud_api_resource
self.old_cloud_api_resource_raw = ee.data._cloud_api_resource_raw
self.old_initialized = ee.data._initialized
self.old_fetch_data_catalog_stac = ee.deprecation._FetchDataCatalogStac
# pylint: enable=protected-access
self.InitializeApi()
def tearDown(self):
super().tearDown()
ee.data.getAlgorithms = self.old_get_algorithms
ee.data.computeValue = self.old_compute_value
ee.data.getMapId = self.old_get_map_id
ee.data.getDownloadId = self.old_get_download_id
ee.data.getThumbId = self.old_get_thumb_id
ee.data.getTableDownloadId = self.old_get_table_download_id
# pylint: disable=protected-access
ee.data._install_cloud_api_resource = self.old_install_cloud_api_resource
ee.data._cloud_api_resource = self.old_cloud_api_resource
ee.data._cloud_api_resource_raw = self.old_cloud_api_resource_raw
ee.data._initialized = self.old_initialized
ee.deprecation._FetchDataCatalogStac = self.old_fetch_data_catalog_stac
# pylint: enable=protected-access
def InitializeApi(self):
"""Initializes the library with standard API methods.
This is normally invoked during setUp(), but subclasses may invoke
it manually instead if they prefer.
"""
self.last_download_call = None
self.last_thumb_call = None
self.last_table_call = None
self.last_mapid_call = None
ee.Reset()
ee.data._install_cloud_api_resource = lambda: None # pylint: disable=protected-access
ee.data.getAlgorithms = GetAlgorithms
ee.data.computeValue = lambda x: {'value': 'fakeValue'}
ee.data.getMapId = self._MockMapId
ee.data.getDownloadId = self._MockDownloadUrl
ee.data.getThumbId = self._MockThumbUrl
ee.data.getTableDownloadId = self._MockTableDownload
# pylint: disable-next=protected-access
ee.deprecation._FetchDataCatalogStac = self._MockFetchDataCatalogStac
ee.Initialize(None, '', project='my-project')
# We are mocking the url here so the unit tests are happy.
def _MockMapId(self, params: dict[str, Any]) -> dict[str, str]:
self.last_mapid_call = {'url': '/mapid', 'data': params}
return {'mapid': 'fakeMapId', 'token': 'fakeToken'}
def _MockDownloadUrl(self, params: dict[str, Any]) -> dict[str, str]:
self.last_download_call = {'url': '/download', 'data': params}
return {'docid': '1', 'token': '2'}
def _MockThumbUrl(
self,
params: dict[str, Any],
# pylint: disable-next=invalid-name
thumbType: Optional[str] = None,
) -> dict[str, str]:
del thumbType # Unused.
# Hang on to the call arguments.
self.last_thumb_call = {'url': '/thumb', 'data': params}
return {'thumbid': '3', 'token': '4'}
def _MockTableDownload(self, params: dict[str, Any]) -> dict[str, str]:
self.last_table_call = {'url': '/table', 'data': params}
return {'docid': '5', 'token': '6'}
def _MockFetchDataCatalogStac(self) -> dict[str, Any]:
return {}
def _GenerateCloudApiResource(mock_http: Any, raw: Any) -> discovery.Resource:
"""Returns a Cloud API resource for testing."""
discovery_doc_path = os.path.join(
os.path.dirname(os.path.realpath(__file__)),
"tests/cloud_api_discovery_document.json")
with open(discovery_doc_path) as discovery_doc_file:
discovery_doc_str = discovery_doc_file.read()
return _cloud_api_utils.build_cloud_resource_from_document(
json.loads(discovery_doc_str),
http_transport=mock_http,
headers_supplier=ee.data._make_request_headers, # pylint: disable=protected-access
response_inspector=ee.data._handle_profiling_response, # pylint: disable=protected-access
raw=raw,
)
@contextlib.contextmanager # pytype: disable=wrong-arg-types
def UsingCloudApi(
cloud_api_resource: Optional[Any] = None,
cloud_api_resource_raw: Optional[Any] = None,
mock_http: Optional[Any] = None,
) -> Iterable[Any]: # pytype: disable=wrong-arg-types
"""Returns a context manager under which the Cloud API is enabled."""
old_cloud_api_resource = ee.data._cloud_api_resource # pylint: disable=protected-access
old_cloud_api_resource_raw = ee.data._cloud_api_resource_raw # pylint: disable=protected-access
old_initialized = ee.data._initialized # pylint: disable=protected-access
try:
if cloud_api_resource is None:
cloud_api_resource = _GenerateCloudApiResource(mock_http, False)
if cloud_api_resource_raw is None:
cloud_api_resource_raw = _GenerateCloudApiResource(mock_http, True)
ee.data._cloud_api_resource = cloud_api_resource # pylint: disable=protected-access
ee.data._cloud_api_resource_raw = cloud_api_resource_raw # pylint: disable=protected-access
ee.data._initialized = True # pylint: disable=protected-access
yield
finally:
ee.data._cloud_api_resource = old_cloud_api_resource # pylint: disable=protected-access
ee.data._cloud_api_resource_raw = old_cloud_api_resource_raw # pylint: disable=protected-access
ee.data._initialized = old_initialized # pylint: disable=protected-access
# A sample of encoded EE API JSON, used by SerializerTest and DeserializerTest.
ENCODED_JSON_SAMPLE = {
'type': 'CompoundValue',
'scope': [
['0', {
'type': 'Invocation',
'functionName': 'Date',
'arguments': {
'value': 1234567890000
}
}],
['1', {
'type': 'LineString',
'coordinates': [[1, 2], [3, 4]],
'crs': {
'type': 'name',
'properties': {
'name': 'SR-ORG:6974'
}
}
}],
['2', {
'evenOdd': True,
'type': 'Polygon',
'coordinates': [
[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]],
[[5, 6], [7, 6], [7, 8], [5, 8]],
[[1, 1], [2, 1], [2, 2], [1, 2]]
]
}],
['3', {
'type': 'Bytes',
'value': 'aGVsbG8='
}],
['4', {
'type': 'Invocation',
'functionName': 'String.cat',
'arguments': {
'string1': 'x',
'string2': 'y'
}
}],
['5', {
'type': 'Dictionary',
'value': {
'foo': 'bar',
'baz': {'type': 'ValueRef', 'value': '4'}
}
}],
['6', {
'type': 'Function',
'argumentNames': ['x', 'y'],
'body': {'type': 'ArgumentRef', 'value': 'y'}
}],
['7', [
None,
True,
5,
7,
3.4,
112233445566778899,
'hello',
{'type': 'ValueRef', 'value': '0'},
{'type': 'ValueRef', 'value': '1'},
{'type': 'ValueRef', 'value': '2'},
{'type': 'ValueRef', 'value': '3'},
{'type': 'ValueRef', 'value': '5'},
{'type': 'ValueRef', 'value': '4'},
{'type': 'ValueRef', 'value': '6'}
]]
],
'value': {'type': 'ValueRef', 'value': '7'}
}
# A sample of encoded EE API JSON for the Cloud API, used by SerializerTest.
ENCODED_CLOUD_API_JSON_SAMPLE = {
'values': {
'0': {
'arrayValue': {
'values': [
{'constantValue': None},
{'constantValue': True},
{'constantValue': 5},
{'constantValue': 7},
{'constantValue': 3.4},
{'integerValue': '112233445566778899'},
{'constantValue': 'hello'},
{'functionInvocationValue': {
'functionName': 'Date',
'arguments': {'value': {'constantValue': 1234567890000}}
}},
{'functionInvocationValue': {
'functionName': 'GeometryConstructors.LineString',
'arguments': {
'crs': {'functionInvocationValue': {
'functionName': 'Projection',
'arguments': {
'crs': {'constantValue': 'SR-ORG:6974'}}
}},
'coordinates': {'arrayValue': {'values': [
{'valueReference': '1'},
{'constantValue': [3, 4]}
]}}
}}},
{'functionInvocationValue': {
'functionName': 'GeometryConstructors.Polygon',
'arguments': {
'coordinates': {'arrayValue': {'values': [
{'arrayValue': {'values': [
{'valueReference': '2'},
{'constantValue': [10, 0]},
{'constantValue': [10, 10]},
{'constantValue': [0, 10]},
{'valueReference': '2'}]}},
{'constantValue':
[[5, 6], [7, 6], [7, 8], [5, 8]]},
{'arrayValue': {'values': [
{'constantValue': [1, 1]},
{'constantValue': [2, 1]},
{'constantValue': [2, 2]},
{'valueReference': '1'}]}}
]}},
'evenOdd': {'constantValue': True}}}},
{'bytesValue': 'aGVsbG8='},
{'dictionaryValue': {
'values': {
'baz': {'valueReference': '3'},
'foo': {'constantValue': 'bar'},
}
}},
{'valueReference': '3'},
{'functionDefinitionValue': {
'argumentNames': ['x', 'y'],
'body': '4'}
}
]}},
'1': {'constantValue': [1, 2]},
'2': {'constantValue': [0, 0]},
'3': {'functionInvocationValue': {
'functionName': 'String.cat',
'arguments': {
'string1': {'constantValue': 'x'},
'string2': {'constantValue': 'y'}
}}},
'4': {'argumentReference': 'y'},
},
'result': '0'
}
ENCODED_CLOUD_API_JSON_SAMPLE_PRETTY = {
'arrayValue': {
'values': [
{'constantValue': None},
{'constantValue': True},
{'constantValue': 5},
{'constantValue': 7},
{'constantValue': 3.4},
{'integerValue': '112233445566778899'},
{'constantValue': 'hello'},
{'functionInvocationValue': {
'functionName': 'Date',
'arguments': {'value': {'constantValue': 1234567890000}}
}},
{'functionInvocationValue': {
'functionName': 'GeometryConstructors.LineString',
'arguments': {
'crs': {'functionInvocationValue': {
'functionName': 'Projection',
'arguments': {
'crs': {'constantValue': 'SR-ORG:6974'}}
}},
'coordinates': {'constantValue': [[1, 2], [3, 4]]}
}}},
{'functionInvocationValue': {
'functionName': 'GeometryConstructors.Polygon',
'arguments': {
'coordinates': {
'constantValue':
[[[0, 0], [10, 0], [10, 10], [0, 10], [0, 0]],
[[5, 6], [7, 6], [7, 8], [5, 8]],
[[1, 1], [2, 1], [2, 2], [1, 2]]]},
'evenOdd': {'constantValue': True}}}},
{'bytesValue': 'aGVsbG8='},
{'dictionaryValue': {
'values': {
'baz': {'functionInvocationValue': {
'functionName': 'String.cat',
'arguments': {
'string1': {'constantValue': 'x'},
'string2': {'constantValue': 'y'}
}}},
'foo': {'constantValue': 'bar'},
}}},
{'functionInvocationValue': {
'functionName': 'String.cat',
'arguments': {
'string1': {'constantValue': 'x'},
'string2': {'constantValue': 'y'}
}}},
{'functionDefinitionValue': {
'argumentNames': ['x', 'y'],
'body': {'argumentReference': 'y'}}
}
]}}