From 631ece962ef927dfb732420b57bea6da4bd7a63f Mon Sep 17 00:00:00 2001 From: Thomas Schultz Date: Tue, 10 Jan 2017 19:11:48 -0500 Subject: [PATCH] Refactor tests, fix naming. --- vision/google/cloud/vision/_gax.py | 9 ++- vision/google/cloud/vision/_http.py | 5 +- vision/google/cloud/vision/annotations.py | 26 +++--- vision/google/cloud/vision/entity.py | 2 +- vision/google/cloud/vision/geometry.py | 10 +-- vision/unit_tests/test__gax.py | 46 ++++++++--- vision/unit_tests/test__http.py | 27 ++++++- vision/unit_tests/test_annotations.py | 98 +++++++++++++---------- 8 files changed, 142 insertions(+), 81 deletions(-) diff --git a/vision/google/cloud/vision/_gax.py b/vision/google/cloud/vision/_gax.py index 3ac927dbdb3bd..079c2d8f53273 100644 --- a/vision/google/cloud/vision/_gax.py +++ b/vision/google/cloud/vision/_gax.py @@ -30,7 +30,7 @@ class _GAPICVisionAPI(object): """ def __init__(self, client=None): self._client = client - self._api = image_annotator_client.ImageAnnotatorClient() + self._annotator_client = image_annotator_client.ImageAnnotatorClient() def annotate(self, image, features): """Annotate images through GAX. @@ -49,9 +49,10 @@ def annotate(self, image, features): request = image_annotator_pb2.AnnotateImageRequest( image=gapic_image, features=gapic_features) requests = [request] - api = self._api - responses = api.batch_annotate_images(requests) - return Annotations.from_pb(responses.responses[0]) + annotator_client = self._annotator_client + images = annotator_client.batch_annotate_images(requests) + if len(images.responses) == 1: + return Annotations.from_pb(images.responses[0]) def _to_gapic_feature(feature): diff --git a/vision/google/cloud/vision/_http.py b/vision/google/cloud/vision/_http.py index c4948e7065b3e..d109924d35ddd 100644 --- a/vision/google/cloud/vision/_http.py +++ b/vision/google/cloud/vision/_http.py @@ -49,8 +49,9 @@ def annotate(self, image, features): data = {'requests': [request]} api_response = self._connection.api_request( method='POST', path='/images:annotate', data=data) - responses = api_response.get('responses') - return Annotations.from_api_repr(responses[0]) + images = api_response.get('responses') + if len(images) == 1: + return Annotations.from_api_repr(images[0]) def _make_request(image, features): diff --git a/vision/google/cloud/vision/annotations.py b/vision/google/cloud/vision/annotations.py index 5ec994f7e61da..5d5e6209b9c77 100644 --- a/vision/google/cloud/vision/annotations.py +++ b/vision/google/cloud/vision/annotations.py @@ -95,11 +95,11 @@ def from_api_repr(cls, response): @classmethod def from_pb(cls, response): - """Factory: construct an instance of ``Annotations`` from gRPC response. + """Factory: construct an instance of ``Annotations`` from protobuf. :type response: :class:`~google.cloud.grpc.vision.v1.\ image_annotator_pb2.AnnotateImageResponse` - :param response: ``AnnotateImageResponse`` from gRPC call. + :param response: ``AnnotateImageResponse`` from protobuf call. :rtype: :class:`~google.cloud.vision.annotations.Annotations` :returns: ``Annotations`` instance populated from gRPC response. @@ -109,21 +109,21 @@ def from_pb(cls, response): def _process_image_annotations(image): - """Helper for processing annotation types from gRPC responses. + """Helper for processing annotation types from protobuf. :type image: :class:`~google.cloud.grpc.vision.v1.image_annotator_pb2.\ AnnotateImageResponse` - :param image: ``AnnotateImageResponse`` from gRPC response. + :param image: ``AnnotateImageResponse`` from protobuf. :rtype: dict :returns: Dictionary populated with entities from response. """ - annotations = {} - annotations['labels'] = _make_entity_from_pb(image.label_annotations) - annotations['landmarks'] = _make_entity_from_pb(image.landmark_annotations) - annotations['logos'] = _make_entity_from_pb(image.logo_annotations) - annotations['texts'] = _make_entity_from_pb(image.text_annotations) - return annotations + return { + 'labels': _make_entity_from_pb(image.label_annotations), + 'landmarks': _make_entity_from_pb(image.landmark_annotations), + 'logos': _make_entity_from_pb(image.logo_annotations), + 'texts': _make_entity_from_pb(image.text_annotations), + } def _make_entity_from_pb(annotations): @@ -136,11 +136,7 @@ def _make_entity_from_pb(annotations): :rtype: list :returns: List of ``EntityAnnotation``. """ - - entities = [] - for annotation in annotations: - entities.append(EntityAnnotation.from_pb(annotation)) - return entities + return [EntityAnnotation.from_pb(annotation) for annotation in annotations] def _entity_from_response_type(feature_type, results): diff --git a/vision/google/cloud/vision/entity.py b/vision/google/cloud/vision/entity.py index ca699cd65ce8d..38a88125aee95 100644 --- a/vision/google/cloud/vision/entity.py +++ b/vision/google/cloud/vision/entity.py @@ -63,7 +63,7 @@ def from_api_repr(cls, response): bounds = Bounds.from_api_repr(response.get('boundingPoly')) description = response['description'] locale = response.get('locale', None) - locations = [LocationInformation.from_api_repr(location) + locations = [LocationInformation.from_api_repr(location['latLng']) for location in response.get('locations', [])] mid = response.get('mid', None) score = response.get('score', None) diff --git a/vision/google/cloud/vision/geometry.py b/vision/google/cloud/vision/geometry.py index 23115f92ddb98..1b0bc3692ae0a 100644 --- a/vision/google/cloud/vision/geometry.py +++ b/vision/google/cloud/vision/geometry.py @@ -87,18 +87,18 @@ def __init__(self, latitude, longitude): self._longitude = longitude @classmethod - def from_api_repr(cls, response): + def from_api_repr(cls, location_info): """Factory: construct location information from Vision API response. - :type response: dict - :param response: Dictionary response of locations. + :type location_info: dict + :param location_info: Dictionary response of locations. :rtype: :class:`~google.cloud.vision.geometry.LocationInformation` :returns: ``LocationInformation`` with populated latitude and longitude. """ - latitude = response['latLng']['latitude'] - longitude = response['latLng']['longitude'] + latitude = location_info['latitude'] + longitude = location_info['longitude'] return cls(latitude, longitude) @classmethod diff --git a/vision/unit_tests/test__gax.py b/vision/unit_tests/test__gax.py index 940764720e011..2316f8f0286d4 100644 --- a/vision/unit_tests/test__gax.py +++ b/vision/unit_tests/test__gax.py @@ -37,25 +37,53 @@ def test_annotation(self): from google.cloud.vision.feature import FeatureTypes from google.cloud.vision.image import Image - client = mock.Mock() + client = mock.Mock(spec_set=[]) feature = Feature(FeatureTypes.LABEL_DETECTION, 5) image_content = b'abc 1 2 3' image = Image(client, content=image_content) with mock.patch('google.cloud.vision._gax.image_annotator_client.' 'ImageAnnotatorClient'): - api = self._make_one(client) + gax_api = self._make_one(client) - api._api = mock.Mock() - mock_response = mock.Mock(responses=['mock response data']) - api._api.batch_annotate_images.return_value = mock_response + mock_response = { + 'batch_annotate_images.return_value': + mock.Mock(responses=['mock response data']), + } + + gax_api._annotator_client = mock.Mock( + spec_set=['batch_annotate_images'], **mock_response) with mock.patch('google.cloud.vision._gax.Annotations') as mock_anno: - api.annotate(image, [feature]) + gax_api.annotate(image, [feature]) mock_anno.from_pb.assert_called_with('mock response data') - api._api.batch_annotate_images.assert_called() + gax_api._annotator_client.batch_annotate_images.assert_called() + + def test_annotate_no_results(self): + from google.cloud.vision.feature import Feature + from google.cloud.vision.feature import FeatureTypes + from google.cloud.vision.image import Image + + client = mock.Mock(spec_set=[]) + feature = Feature(FeatureTypes.LABEL_DETECTION, 5) + image_content = b'abc 1 2 3' + image = Image(client, content=image_content) + with mock.patch('google.cloud.vision._gax.image_annotator_client.' + 'ImageAnnotatorClient'): + gax_api = self._make_one(client) + + mock_response = { + 'batch_annotate_images.return_value': mock.Mock(responses=[]), + } + + gax_api._annotator_client = mock.Mock( + spec_set=['batch_annotate_images'], **mock_response) + with mock.patch('google.cloud.vision._gax.Annotations'): + self.assertIsNone(gax_api.annotate(image, [feature])) + + gax_api._annotator_client.batch_annotate_images.assert_called() -class TestToGAPICFeature(unittest.TestCase): +class Test__to_gapic_feature(unittest.TestCase): def _call_fut(self, feature): from google.cloud.vision._gax import _to_gapic_feature return _to_gapic_feature(feature) @@ -72,7 +100,7 @@ def test__to_gapic_feature(self): self.assertEqual(feature_pb.max_results, 5) -class TestToGAPICImage(unittest.TestCase): +class Test__to_gapic_image(unittest.TestCase): def _call_fut(self, image): from google.cloud.vision._gax import _to_gapic_image return _to_gapic_image(image) diff --git a/vision/unit_tests/test__http.py b/vision/unit_tests/test__http.py index d6c237d9747ca..0872c510969f7 100644 --- a/vision/unit_tests/test__http.py +++ b/vision/unit_tests/test__http.py @@ -15,12 +15,38 @@ import base64 import unittest +import mock + IMAGE_CONTENT = b'/9j/4QNURXhpZgAASUkq' PROJECT = 'PROJECT' B64_IMAGE_CONTENT = base64.b64encode(IMAGE_CONTENT).decode('ascii') +class Test_HTTPVisionAPI(unittest.TestCase): + def _get_target_class(self): + from google.cloud.vision._http import _HTTPVisionAPI + return _HTTPVisionAPI + + def _make_one(self, *args, **kwargs): + return self._get_target_class()(*args, **kwargs) + + def test_call_annotate_with_no_results(self): + from google.cloud.vision.feature import Feature + from google.cloud.vision.feature import FeatureTypes + from google.cloud.vision.image import Image + + client = mock.Mock(spec_set=['_connection']) + feature = Feature(FeatureTypes.LABEL_DETECTION, 5) + image_content = b'abc 1 2 3' + image = Image(client, content=image_content) + + http_api = self._make_one(client) + http_api._connection = mock.Mock(spec_set=['api_request']) + http_api._connection.api_request.return_value = {'responses': []} + self.assertIsNone(http_api.annotate(image, [feature])) + + class TestVisionRequest(unittest.TestCase): @staticmethod def _get_target_function(): @@ -44,7 +70,6 @@ def test_call_vision_request(self): features = request['features'] self.assertEqual(len(features), 1) feature = features[0] - print(feature) self.assertEqual(feature['type'], FeatureTypes.FACE_DETECTION) self.assertEqual(feature['maxResults'], 3) diff --git a/vision/unit_tests/test_annotations.py b/vision/unit_tests/test_annotations.py index 609b9fcf6213b..b176f8490859f 100644 --- a/vision/unit_tests/test_annotations.py +++ b/vision/unit_tests/test_annotations.py @@ -15,6 +15,35 @@ import unittest +def _make_pb_entity(): + from google.cloud.grpc.vision.v1 import geometry_pb2 + from google.cloud.grpc.vision.v1 import image_annotator_pb2 + from google.type import latlng_pb2 + + description = 'testing 1 2 3' + locale = 'US' + mid = 'm/w/45342234' + score = 0.235434231 + + entity_annotation = image_annotator_pb2.EntityAnnotation( + mid=mid, + locale=locale, + description=description, + score=score, + bounding_poly=geometry_pb2.BoundingPoly( + vertices=[ + geometry_pb2.Vertex(x=1, y=2), + ], + ), + locations=[ + image_annotator_pb2.LocationInfo( + lat_lng=latlng_pb2.LatLng(latitude=1.0, longitude=2.0), + ), + ], + ) + return entity_annotation + + class TestAnnotations(unittest.TestCase): @staticmethod def _get_target_class(): @@ -29,52 +58,40 @@ def test_ctor(self): annotations = self._make_one( faces=[True], properties=[True], labels=[True], landmarks=[True], logos=[True], safe_searches=[True], texts=[True]) - self.assertTrue(annotations.faces[0]) - self.assertTrue(annotations.properties[0]) - self.assertTrue(annotations.labels[0]) - self.assertTrue(annotations.landmarks[0]) - self.assertTrue(annotations.logos[0]) - self.assertTrue(annotations.safe_searches[0]) - self.assertTrue(annotations.texts[0]) + self.assertEqual(annotations.faces, [True]) + self.assertEqual(annotations.properties, [True]) + self.assertEqual(annotations.labels, [True]) + self.assertEqual(annotations.landmarks, [True]) + self.assertEqual(annotations.logos, [True]) + self.assertEqual(annotations.safe_searches, [True]) + self.assertEqual(annotations.texts, [True]) def test_from_pb(self): from google.cloud.grpc.vision.v1 import image_annotator_pb2 image_response = image_annotator_pb2.AnnotateImageResponse() annotations = self._make_one().from_pb(image_response) - self.assertEquals(annotations.labels, []) - self.assertEquals(annotations.logos, []) - self.assertEquals(annotations.faces, ()) - self.assertEquals(annotations.landmarks, []) - self.assertEquals(annotations.texts, []) - self.assertEquals(annotations.safe_searches, ()) - self.assertEquals(annotations.properties, ()) + self.assertEqual(annotations.labels, []) + self.assertEqual(annotations.logos, []) + self.assertEqual(annotations.faces, ()) + self.assertEqual(annotations.landmarks, []) + self.assertEqual(annotations.texts, []) + self.assertEqual(annotations.safe_searches, ()) + self.assertEqual(annotations.properties, ()) -class TestMakeEntityFromPB(unittest.TestCase): +class Test__make_entity_from_pb(unittest.TestCase): def _call_fut(self, annotations): from google.cloud.vision.annotations import _make_entity_from_pb - return _make_entity_from_pb(annotations) - def test_make_entity_from_pb(self): - from google.cloud.grpc.vision.v1 import image_annotator_pb2 + return _make_entity_from_pb(annotations) + def test_it(self): description = 'testing 1 2 3' locale = 'US' mid = 'm/w/45342234' score = 0.235434231 - entity_annotation = image_annotator_pb2.EntityAnnotation() - entity_annotation.mid = mid - entity_annotation.locale = locale - entity_annotation.description = description - entity_annotation.score = score - entity_annotation.bounding_poly.vertices.add() - entity_annotation.bounding_poly.vertices[0].x = 1 - entity_annotation.bounding_poly.vertices[0].y = 2 - entity_annotation.locations.add() - entity_annotation.locations[0].lat_lng.latitude = 1.0 - entity_annotation.locations[0].lat_lng.longitude = 2.0 - + entity_annotation = _make_pb_entity() entities = self._call_fut([entity_annotation]) self.assertEqual(len(entities), 1) entity = entities[0] @@ -82,40 +99,31 @@ def test_make_entity_from_pb(self): self.assertEqual(entity.mid, mid) self.assertEqual(entity.locale, locale) self.assertEqual(entity.score, score) + self.assertEqual(len(entity.bounds.vertices), 1) self.assertEqual(entity.bounds.vertices[0].x_coordinate, 1) self.assertEqual(entity.bounds.vertices[0].y_coordinate, 2) + self.assertEqual(len(entity.locations), 1) self.assertEqual(entity.locations[0].latitude, 1.0) self.assertEqual(entity.locations[0].longitude, 2.0) -class TestProcessImageAnnotations(unittest.TestCase): +class Test__process_image_annotations(unittest.TestCase): def _call_fut(self, image): from google.cloud.vision.annotations import _process_image_annotations return _process_image_annotations(image) - def test_process_image_annotations(self): + def test_it(self): from google.cloud.grpc.vision.v1 import image_annotator_pb2 description = 'testing 1 2 3' locale = 'US' mid = 'm/w/45342234' score = 0.235434231 - entity_annotation = image_annotator_pb2.EntityAnnotation() - entity_annotation.mid = mid - entity_annotation.locale = locale - entity_annotation.description = description - entity_annotation.score = score - entity_annotation.bounding_poly.vertices.add() - entity_annotation.bounding_poly.vertices[0].x = 1 - entity_annotation.bounding_poly.vertices[0].y = 2 - entity_annotation.locations.add() - entity_annotation.locations[0].lat_lng.latitude = 1.0 - entity_annotation.locations[0].lat_lng.longitude = 2.0 + entity_annotation = _make_pb_entity() image_response = image_annotator_pb2.AnnotateImageResponse( label_annotations=[entity_annotation]) - print(image_response.label_annotations) annotations = self._call_fut(image_response) self.assertEqual(len(annotations['labels']), 1) @@ -125,7 +133,9 @@ def test_process_image_annotations(self): self.assertEqual(entity.mid, mid) self.assertEqual(entity.locale, locale) self.assertEqual(entity.score, score) + self.assertEqual(len(entity.bounds.vertices), 1) self.assertEqual(entity.bounds.vertices[0].x_coordinate, 1) self.assertEqual(entity.bounds.vertices[0].y_coordinate, 2) + self.assertEqual(len(entity.locations), 1) self.assertEqual(entity.locations[0].latitude, 1.0) self.assertEqual(entity.locations[0].longitude, 2.0)