blob: c997eaae76b4f442898e9072e04561fa3abbfd41 [file] [log] [blame]
// Copyright 2017, Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package vision
import (
"context"
"fmt"
"reflect"
"testing"
"github.com/golang/protobuf/proto"
pb "google.golang.org/genproto/googleapis/cloud/vision/v1"
"google.golang.org/genproto/googleapis/rpc/status"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
)
var batchResponse = &pb.BatchAnnotateImagesResponse{
Responses: []*pb.AnnotateImageResponse{{
FaceAnnotations: []*pb.FaceAnnotation{
{RollAngle: 1}, {RollAngle: 2}},
LandmarkAnnotations: []*pb.EntityAnnotation{{Mid: "landmark"}},
LogoAnnotations: []*pb.EntityAnnotation{{Mid: "logo"}},
LabelAnnotations: []*pb.EntityAnnotation{{Mid: "label"}},
TextAnnotations: []*pb.EntityAnnotation{{Mid: "text"}},
FullTextAnnotation: &pb.TextAnnotation{Text: "full"},
SafeSearchAnnotation: &pb.SafeSearchAnnotation{Spoof: pb.Likelihood_POSSIBLE},
ImagePropertiesAnnotation: &pb.ImageProperties{DominantColors: &pb.DominantColorsAnnotation{}},
CropHintsAnnotation: &pb.CropHintsAnnotation{CropHints: []*pb.CropHint{{Confidence: 0.5}}},
WebDetection: &pb.WebDetection{WebEntities: []*pb.WebDetection_WebEntity{{EntityId: "web"}}},
}},
}
// Verify that all the "shortcut" methods use the underlying
// BatchAnnotateImages RPC correctly.
func TestClientMethods(t *testing.T) {
ctx := context.Background()
c, err := NewImageAnnotatorClient(ctx, clientOpt)
if err != nil {
t.Fatal(err)
}
mockImageAnnotator.resps = []proto.Message{batchResponse}
img := &pb.Image{Source: &pb.ImageSource{ImageUri: "http://foo.jpg"}}
ictx := &pb.ImageContext{LanguageHints: []string{"en", "fr"}}
req := &pb.AnnotateImageRequest{
Image: img,
ImageContext: ictx,
Features: []*pb.Feature{
{Type: pb.Feature_LABEL_DETECTION, MaxResults: 3},
{Type: pb.Feature_FACE_DETECTION, MaxResults: 4},
},
}
for i, test := range []struct {
call func() (interface{}, error)
wantFeatures []*pb.Feature
wantRes interface{}
}{
{
func() (interface{}, error) { return c.AnnotateImage(ctx, req) },
req.Features, batchResponse.Responses[0],
},
{
func() (interface{}, error) { return c.DetectFaces(ctx, img, ictx, 2) },
[]*pb.Feature{{Type: pb.Feature_FACE_DETECTION, MaxResults: 2}},
batchResponse.Responses[0].FaceAnnotations,
},
{
func() (interface{}, error) { return c.DetectLandmarks(ctx, img, ictx, 2) },
[]*pb.Feature{{Type: pb.Feature_LANDMARK_DETECTION, MaxResults: 2}},
batchResponse.Responses[0].LandmarkAnnotations,
},
{
func() (interface{}, error) { return c.DetectLogos(ctx, img, ictx, 2) },
[]*pb.Feature{{Type: pb.Feature_LOGO_DETECTION, MaxResults: 2}},
batchResponse.Responses[0].LogoAnnotations,
},
{
func() (interface{}, error) { return c.DetectLabels(ctx, img, ictx, 2) },
[]*pb.Feature{{Type: pb.Feature_LABEL_DETECTION, MaxResults: 2}},
batchResponse.Responses[0].LabelAnnotations,
},
{
func() (interface{}, error) { return c.DetectTexts(ctx, img, ictx, 2) },
[]*pb.Feature{{Type: pb.Feature_TEXT_DETECTION, MaxResults: 2}},
batchResponse.Responses[0].TextAnnotations,
},
{
func() (interface{}, error) { return c.DetectDocumentText(ctx, img, ictx) },
[]*pb.Feature{{Type: pb.Feature_DOCUMENT_TEXT_DETECTION, MaxResults: 0}},
batchResponse.Responses[0].FullTextAnnotation,
},
{
func() (interface{}, error) { return c.DetectSafeSearch(ctx, img, ictx) },
[]*pb.Feature{{Type: pb.Feature_SAFE_SEARCH_DETECTION, MaxResults: 0}},
batchResponse.Responses[0].SafeSearchAnnotation,
},
{
func() (interface{}, error) { return c.DetectImageProperties(ctx, img, ictx) },
[]*pb.Feature{{Type: pb.Feature_IMAGE_PROPERTIES, MaxResults: 0}},
batchResponse.Responses[0].ImagePropertiesAnnotation,
},
{
func() (interface{}, error) { return c.DetectWeb(ctx, img, ictx) },
[]*pb.Feature{{Type: pb.Feature_WEB_DETECTION, MaxResults: 0}},
batchResponse.Responses[0].WebDetection,
},
{
func() (interface{}, error) { return c.CropHints(ctx, img, ictx) },
[]*pb.Feature{{Type: pb.Feature_CROP_HINTS, MaxResults: 0}},
batchResponse.Responses[0].CropHintsAnnotation,
},
{
func() (interface{}, error) { return c.LocalizeObjects(ctx, img, ictx) },
[]*pb.Feature{{Type: pb.Feature_OBJECT_LOCALIZATION, MaxResults: 0}},
batchResponse.Responses[0].LocalizedObjectAnnotations,
},
{
func() (interface{}, error) { return c.ProductSearch(ctx, img, ictx) },
[]*pb.Feature{{Type: pb.Feature_PRODUCT_SEARCH, MaxResults: 0}},
batchResponse.Responses[0].ProductSearchResults,
},
} {
mockImageAnnotator.reqs = nil
res, err := test.call()
if err != nil {
t.Fatal(err)
}
got := mockImageAnnotator.reqs[0]
want := &pb.BatchAnnotateImagesRequest{
Requests: []*pb.AnnotateImageRequest{{
Image: img,
ImageContext: ictx,
Features: test.wantFeatures,
}},
}
if !testEqual(got, want) {
t.Errorf("#%d:\ngot %v\nwant %v", i, got, want)
}
if got, want := res, test.wantRes; !testEqual(got, want) {
t.Errorf("#%d:\ngot %v\nwant %v", i, got, want)
}
}
}
func testEqual(a, b interface{}) bool {
if a == nil && b == nil {
return true
}
if a == nil || b == nil {
return false
}
t := reflect.TypeOf(a)
if t != reflect.TypeOf(b) {
return false
}
if am, ok := a.(proto.Message); ok {
return proto.Equal(am, b.(proto.Message))
}
if t.Kind() != reflect.Slice {
panic(fmt.Sprintf("testEqual can only handle proto.Message and slices, got %s", t))
}
va := reflect.ValueOf(a)
vb := reflect.ValueOf(b)
if va.Len() != vb.Len() {
return false
}
for i := 0; i < va.Len(); i++ {
if !testEqual(va.Index(i).Interface(), vb.Index(i).Interface()) {
return false
}
}
return true
}
func TestAnnotateOneError(t *testing.T) {
ctx := context.Background()
c, err := NewImageAnnotatorClient(ctx, clientOpt)
if err != nil {
t.Fatal(err)
}
mockImageAnnotator.resps = []proto.Message{
&pb.BatchAnnotateImagesResponse{
Responses: []*pb.AnnotateImageResponse{{
Error: &status.Status{Code: int32(codes.NotFound), Message: "not found"},
}},
},
}
_, err = c.annotateOne(ctx,
&pb.Image{Source: &pb.ImageSource{ImageUri: "http://foo.jpg"}},
nil, pb.Feature_LOGO_DETECTION, 1, nil)
if c := grpc.Code(err); c != codes.NotFound {
t.Errorf("got %v, want NotFound", c)
}
}