#!/usr/bin/env python3 """Test for the ee.__init__ file.""" from unittest import mock import google.auth from google.oauth2 import credentials import unittest import ee from ee import apitestcase class EETestCase(apitestcase.ApiTestCase): def setUp(self): super().setUp() ee.Reset() ee.data._install_cloud_api_resource = lambda: None def testInitialization(self): """Verifies library initialization.""" def MockAlgorithms(): return {} ee.data.getAlgorithms = MockAlgorithms # Verify that the base state is uninitialized. self.assertFalse(ee.data._initialized) self.assertIsNone(ee.data._api_base_url) self.assertEqual(ee.ApiFunction._api, {}) self.assertFalse(ee.Image._initialized) # Verify that ee.Initialize() sets the URL and initializes classes. ee.Initialize(None, 'foo', project='my-project') self.assertTrue(ee.data._initialized) self.assertEqual(ee.data._api_base_url, 'foo/api') self.assertEqual(ee.data._cloud_api_user_project, 'my-project') self.assertEqual(ee.ApiFunction._api, {}) self.assertTrue(ee.Image._initialized) # Verify that ee.Initialize(None) does not override custom URLs. ee.Initialize(None) self.assertTrue(ee.data._initialized) self.assertEqual(ee.data._api_base_url, 'foo/api') # Verify that ee.Reset() reverts everything to the base state. ee.Reset() self.assertFalse(ee.data._initialized) self.assertIsNone(ee.data._api_base_url) self.assertIsNone(ee.data._cloud_api_user_project) self.assertEqual(ee.ApiFunction._api, {}) self.assertFalse(ee.Image._initialized) def testCallAndApply(self): """Verifies library initialization.""" # Use a custom set of known functions. def MockAlgorithms(): return { 'fakeFunction': { 'type': 'Algorithm', 'args': [{ 'name': 'image1', 'type': 'Image' }, { 'name': 'image2', 'type': 'Image' }], 'returns': 'Image' }, 'Image.constant': apitestcase.GetAlgorithms()['Image.constant'] } ee.data.getAlgorithms = MockAlgorithms ee.Initialize(None) image1 = ee.Image(1) image2 = ee.Image(2) expected = ee.Image( ee.ComputedObject( ee.ApiFunction.lookup('fakeFunction'), { 'image1': image1, 'image2': image2 })) applied_with_images = ee.apply('fakeFunction', { 'image1': image1, 'image2': image2 }) self.assertEqual(expected, applied_with_images) applied_with_numbers = ee.apply('fakeFunction', {'image1': 1, 'image2': 2}) self.assertEqual(expected, applied_with_numbers) called_with_numbers = ee.call('fakeFunction', 1, 2) self.assertEqual(expected, called_with_numbers) # Test call and apply() with a custom function. sig = {'returns': 'Image', 'args': [{'name': 'foo', 'type': 'Image'}]} func = ee.CustomFunction(sig, lambda foo: ee.call('fakeFunction', 42, foo)) expected_custom_function_call = ee.Image( ee.ComputedObject(func, {'foo': ee.Image(13)})) self.assertEqual(expected_custom_function_call, ee.call(func, 13)) self.assertEqual(expected_custom_function_call, ee.apply(func, {'foo': 13})) # Test None promotion. called_with_null = ee.call('fakeFunction', None, 1) self.assertIsNone(called_with_null.args['image1']) def testDynamicClasses(self): """Verifies dynamic class initialization.""" # Use a custom set of known functions. def MockAlgorithms(): return { 'Array': { 'type': 'Algorithm', 'args': [{ 'name': 'values', 'type': 'Serializable', 'description': '' }], 'description': '', 'returns': 'Array' }, 'Array.cos': { 'type': 'Algorithm', 'args': [{ 'type': 'Array', 'description': '', 'name': 'input' }], 'description': '', 'returns': 'Array' }, 'Kernel.circle': { 'returns': 'Kernel', 'args': [{ 'type': 'float', 'description': '', 'name': 'radius', }, { 'default': 1.0, 'type': 'float', 'optional': True, 'description': '', 'name': 'scale' }, { 'default': True, 'type': 'boolean', 'optional': True, 'description': '', 'name': 'normalize' }, { 'default': 1.0, 'type': 'float', 'optional': True, 'description': '', 'name': 'magnitude' }], 'type': 'Algorithm', 'description': '' }, 'Reducer.mean': { 'returns': 'Reducer', 'args': [] }, 'fakeFunction': { 'returns': 'Array', 'args': [{ 'type': 'Reducer', 'description': '', 'name': 'kernel', }] } } ee.data.getAlgorithms = MockAlgorithms ee.Initialize(None) # Verify that the expected classes got generated. self.assertTrue(hasattr(ee, 'Array')) self.assertTrue(hasattr(ee, 'Kernel')) self.assertTrue(hasattr(ee.Array, 'cos')) self.assertTrue(hasattr(ee.Kernel, 'circle')) # Try out the constructors. kernel = ee.ApiFunction('Kernel.circle').call(1, 'meters', True, 2) self.assertEqual(kernel, ee.Kernel.circle(1, 'meters', True, 2)) array = ee.ApiFunction('Array').call([1, 2]) self.assertEqual(array, ee.Array([1, 2])) self.assertEqual(array, ee.Array(ee.Array([1, 2]))) # Try out the member function. self.assertEqual( ee.ApiFunction('Array.cos').call(array), ee.Array([1, 2]).cos()) # Test argument promotion. f1 = ee.ApiFunction('Array.cos').call([1, 2]) f2 = ee.ApiFunction('Array.cos').call(ee.Array([1, 2])) self.assertEqual(f1, f2) self.assertIsInstance(f1, ee.Array) f3 = ee.call('fakeFunction', 'mean') f4 = ee.call('fakeFunction', ee.Reducer.mean()) self.assertEqual(f3, f4) with self.assertRaisesRegex( ee.EEException, 'Unknown algorithm: Reducer.moo'): ee.call('fakeFunction', 'moo') def testDynamicConstructor(self): # Test the behavior of the dynamic class constructor. # Use a custom set of known functions for classes Foo and Bar. # Foo Foo(arg1, [arg2]) # Bar Foo.makeBar() # Bar Foo.takeBar(Bar bar) # Baz Foo.baz() def MockAlgorithms(): return { 'Foo': { 'returns': 'Foo', 'args': [{ 'name': 'arg1', 'type': 'Object' }, { 'name': 'arg2', 'type': 'Object', 'optional': True }] }, 'Foo.makeBar': { 'returns': 'Bar', 'args': [{ 'name': 'foo', 'type': 'Foo' }] }, 'Foo.takeBar': { 'returns': 'Bar', 'args': [{ 'name': 'foo', 'type': 'Foo' }, { 'name': 'bar', 'type': 'Bar' }] }, 'Bar.baz': { 'returns': 'Baz', 'args': [{ 'name': 'bar', 'type': 'Bar' }] } } ee.data.getAlgorithms = MockAlgorithms ee.Initialize(None) # Try to cast something that's already of the right class. x = ee.Foo('argument') self.assertEqual(ee.Foo(x), x) # Tests for dynamic classes, where there is a constructor. # # If there's more than 1 arg, call the constructor. x = ee.Foo('a') y = ee.Foo(x, 'b') ctor = ee.ApiFunction.lookup('Foo') self.assertEqual(y.func, ctor) self.assertEqual(y.args, {'arg1': x, 'arg2': 'b'}) # Can't cast a primitive; call the constructor. self.assertEqual(ctor, ee.Foo(1).func) # A computed object, but not this class; call the constructor. self.assertEqual(ctor, ee.Foo(ee.List([1, 2, 3])).func) # Tests for dynamic classes, where there isn't a constructor. # # Foo.makeBar and Foo.takeBar should have caused Bar to be generated. self.assertTrue(hasattr(ee, 'Bar')) # Make sure we can create a Bar. bar = ee.Foo(1).makeBar() self.assertIsInstance(bar, ee.Bar) # Now cast something else to a Bar and verify it was just a cast. cast = ee.Bar(ee.Foo(1)) self.assertIsInstance(cast, ee.Bar) self.assertEqual(ctor, cast.func) # Tests for kwargs. foo = ee.Foo(arg1='a', arg2='b') self.assertEqual(foo.args, {'arg1': 'a', 'arg2': 'b'}) foo = ee.Foo('a', arg2='b') self.assertEqual(foo.args, {'arg1': 'a', 'arg2': 'b'}) foo = ee.Foo(arg2='b', arg1='a') self.assertEqual(foo.args, {'arg1': 'a', 'arg2': 'b'}) # We should get an error for an invalid kwarg. with self.assertRaisesRegex( ee.EEException, "Unrecognized arguments {'arg_invalid'} to function: Foo", ): ee.Foo('a', arg_invalid='b') # We shouldn't be able to cast with more than 1 arg. with self.assertRaisesRegex( ee.EEException, 'Too many arguments for ee.Bar'): ee.Bar(x, 'foo') # We shouldn't be able to cast a primitive. with self.assertRaisesRegex(ee.EEException, 'Must be a ComputedObject'): ee.Bar(1) def testDynamicConstructorCasting(self): """Test the behavior of casting with dynamic classes.""" self.InitializeApi() result = ee.Geometry.Rectangle(1, 1, 2, 2).bounds(0, 'EPSG:4326') expected = ( ee.Geometry.Polygon([[1, 2], [1, 1], [2, 1], [2, 2]]).bounds( ee.ErrorMargin(0), ee.Projection('EPSG:4326'))) self.assertEqual(expected, result) def testPromotion(self): """Verifies object promotion rules.""" self.InitializeApi() # Features and Images are both already Elements. self.assertIsInstance(ee._Promote(ee.Feature(None), 'Element'), ee.Feature) self.assertIsInstance(ee._Promote(ee.Image(0), 'Element'), ee.Image) # Promote an untyped object to an Element. untyped = ee.ComputedObject('foo', {}) self.assertIsInstance(ee._Promote(untyped, 'Element'), ee.Element) # Promote an untyped variable to an Element. untyped = ee.ComputedObject(None, None, 'foo') self.assertIsInstance(ee._Promote(untyped, 'Element'), ee.Element) self.assertEqual('foo', ee._Promote(untyped, 'Element').varName) def testUnboundMethods(self): """Verifies unbound method attachment to ee.Algorithms.""" # Use a custom set of known functions. def MockAlgorithms(): return { 'Foo': { 'type': 'Algorithm', 'args': [], 'description': '', 'returns': 'Object' }, 'Foo.bar': { 'type': 'Algorithm', 'args': [], 'description': '', 'returns': 'Object' }, 'Quux.baz': { 'type': 'Algorithm', 'args': [], 'description': '', 'returns': 'Object' }, 'last': { 'type': 'Algorithm', 'args': [], 'description': '', 'returns': 'Object' } } ee.data.getAlgorithms = MockAlgorithms ee.ApiFunction.importApi(lambda: None, 'Quux', 'Quux') ee._InitializeUnboundMethods() self.assertTrue(callable(ee.Algorithms.Foo)) self.assertTrue(callable(ee.Algorithms.Foo.bar)) self.assertNotIn('Quux', ee.Algorithms) self.assertEqual(ee.call('Foo.bar'), ee.Algorithms.Foo.bar()) self.assertNotEqual(ee.Algorithms.Foo.bar(), ee.Algorithms.last()) def testNonAsciiDocumentation(self): """Verifies that non-ASCII characters in documentation work.""" foo = u'\uFB00\u00F6\u01EB' bar = u'b\u00E4r' baz = u'b\u00E2\u00DF' def MockAlgorithms(): return { 'Foo': { 'type': 'Algorithm', 'args': [], 'description': foo, 'returns': 'Object' }, 'Image.bar': { 'type': 'Algorithm', 'args': [{ 'name': 'bar', 'type': 'Bar', 'description': bar }], 'description': '', 'returns': 'Object' }, 'Image.oldBar': { 'type': 'Algorithm', 'args': [], 'description': foo, 'returns': 'Object', 'deprecated': 'Causes fire' }, 'Image.baz': { 'type': 'Algorithm', 'args': [], 'description': baz, 'returns': 'Object' }, 'Image.newBaz': { 'type': 'Algorithm', 'args': [], 'description': baz, 'returns': 'Object', 'preview': True } } ee.data.getAlgorithms = MockAlgorithms ee.Initialize(None) # The initialisation shouldn't blow up. self.assertTrue(callable(ee.Algorithms.Foo)) # pytype: disable=attribute-error self.assertTrue(callable(ee.Image.bar)) self.assertTrue(callable(ee.Image.baz)) self.assertTrue(callable(ee.Image.baz)) self.assertEqual(ee.Algorithms.Foo.__doc__, foo) self.assertIn(foo, ee.Image.oldBar.__doc__) self.assertIn('DEPRECATED: Causes fire', ee.Image.oldBar.__doc__) self.assertIn('PREVIEW: This function is preview or internal only.', ee.Image.newBaz.__doc__) self.assertEqual(ee.Image.bar.__doc__, '\n\nArgs:\n bar: ' + bar) self.assertEqual(ee.Image.baz.__doc__, baz) # pytype: enable=attribute-error if __name__ == '__main__': unittest.main()