You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

201 lines
6.4 KiB

  1. // Copyright 2017, Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package vision
  15. import (
  16. "fmt"
  17. "reflect"
  18. "testing"
  19. "github.com/golang/protobuf/proto"
  20. "golang.org/x/net/context"
  21. pb "google.golang.org/genproto/googleapis/cloud/vision/v1"
  22. "google.golang.org/genproto/googleapis/rpc/status"
  23. "google.golang.org/grpc"
  24. "google.golang.org/grpc/codes"
  25. )
  26. var batchResponse = &pb.BatchAnnotateImagesResponse{
  27. Responses: []*pb.AnnotateImageResponse{{
  28. FaceAnnotations: []*pb.FaceAnnotation{
  29. {RollAngle: 1}, {RollAngle: 2}},
  30. LandmarkAnnotations: []*pb.EntityAnnotation{{Mid: "landmark"}},
  31. LogoAnnotations: []*pb.EntityAnnotation{{Mid: "logo"}},
  32. LabelAnnotations: []*pb.EntityAnnotation{{Mid: "label"}},
  33. TextAnnotations: []*pb.EntityAnnotation{{Mid: "text"}},
  34. FullTextAnnotation: &pb.TextAnnotation{Text: "full"},
  35. SafeSearchAnnotation: &pb.SafeSearchAnnotation{Spoof: pb.Likelihood_POSSIBLE},
  36. ImagePropertiesAnnotation: &pb.ImageProperties{DominantColors: &pb.DominantColorsAnnotation{}},
  37. CropHintsAnnotation: &pb.CropHintsAnnotation{CropHints: []*pb.CropHint{{Confidence: 0.5}}},
  38. WebDetection: &pb.WebDetection{WebEntities: []*pb.WebDetection_WebEntity{{EntityId: "web"}}},
  39. }},
  40. }
  41. // Verify that all the "shortcut" methods use the underlying
  42. // BatchAnnotateImages RPC correctly.
  43. func TestClientMethods(t *testing.T) {
  44. ctx := context.Background()
  45. c, err := NewImageAnnotatorClient(ctx, clientOpt)
  46. if err != nil {
  47. t.Fatal(err)
  48. }
  49. mockImageAnnotator.resps = []proto.Message{batchResponse}
  50. img := &pb.Image{Source: &pb.ImageSource{ImageUri: "http://foo.jpg"}}
  51. ictx := &pb.ImageContext{LanguageHints: []string{"en", "fr"}}
  52. req := &pb.AnnotateImageRequest{
  53. Image: img,
  54. ImageContext: ictx,
  55. Features: []*pb.Feature{
  56. {Type: pb.Feature_LABEL_DETECTION, MaxResults: 3},
  57. {Type: pb.Feature_FACE_DETECTION, MaxResults: 4},
  58. },
  59. }
  60. for i, test := range []struct {
  61. call func() (interface{}, error)
  62. wantFeatures []*pb.Feature
  63. wantRes interface{}
  64. }{
  65. {
  66. func() (interface{}, error) { return c.AnnotateImage(ctx, req) },
  67. req.Features, batchResponse.Responses[0],
  68. },
  69. {
  70. func() (interface{}, error) { return c.DetectFaces(ctx, img, ictx, 2) },
  71. []*pb.Feature{{Type: pb.Feature_FACE_DETECTION, MaxResults: 2}},
  72. batchResponse.Responses[0].FaceAnnotations,
  73. },
  74. {
  75. func() (interface{}, error) { return c.DetectLandmarks(ctx, img, ictx, 2) },
  76. []*pb.Feature{{Type: pb.Feature_LANDMARK_DETECTION, MaxResults: 2}},
  77. batchResponse.Responses[0].LandmarkAnnotations,
  78. },
  79. {
  80. func() (interface{}, error) { return c.DetectLogos(ctx, img, ictx, 2) },
  81. []*pb.Feature{{Type: pb.Feature_LOGO_DETECTION, MaxResults: 2}},
  82. batchResponse.Responses[0].LogoAnnotations,
  83. },
  84. {
  85. func() (interface{}, error) { return c.DetectLabels(ctx, img, ictx, 2) },
  86. []*pb.Feature{{Type: pb.Feature_LABEL_DETECTION, MaxResults: 2}},
  87. batchResponse.Responses[0].LabelAnnotations,
  88. },
  89. {
  90. func() (interface{}, error) { return c.DetectTexts(ctx, img, ictx, 2) },
  91. []*pb.Feature{{Type: pb.Feature_TEXT_DETECTION, MaxResults: 2}},
  92. batchResponse.Responses[0].TextAnnotations,
  93. },
  94. {
  95. func() (interface{}, error) { return c.DetectDocumentText(ctx, img, ictx) },
  96. []*pb.Feature{{Type: pb.Feature_DOCUMENT_TEXT_DETECTION, MaxResults: 0}},
  97. batchResponse.Responses[0].FullTextAnnotation,
  98. },
  99. {
  100. func() (interface{}, error) { return c.DetectSafeSearch(ctx, img, ictx) },
  101. []*pb.Feature{{Type: pb.Feature_SAFE_SEARCH_DETECTION, MaxResults: 0}},
  102. batchResponse.Responses[0].SafeSearchAnnotation,
  103. },
  104. {
  105. func() (interface{}, error) { return c.DetectImageProperties(ctx, img, ictx) },
  106. []*pb.Feature{{Type: pb.Feature_IMAGE_PROPERTIES, MaxResults: 0}},
  107. batchResponse.Responses[0].ImagePropertiesAnnotation,
  108. },
  109. {
  110. func() (interface{}, error) { return c.DetectWeb(ctx, img, ictx) },
  111. []*pb.Feature{{Type: pb.Feature_WEB_DETECTION, MaxResults: 0}},
  112. batchResponse.Responses[0].WebDetection,
  113. },
  114. {
  115. func() (interface{}, error) { return c.CropHints(ctx, img, ictx) },
  116. []*pb.Feature{{Type: pb.Feature_CROP_HINTS, MaxResults: 0}},
  117. batchResponse.Responses[0].CropHintsAnnotation,
  118. },
  119. } {
  120. mockImageAnnotator.reqs = nil
  121. res, err := test.call()
  122. if err != nil {
  123. t.Fatal(err)
  124. }
  125. got := mockImageAnnotator.reqs[0]
  126. want := &pb.BatchAnnotateImagesRequest{
  127. Requests: []*pb.AnnotateImageRequest{{
  128. Image: img,
  129. ImageContext: ictx,
  130. Features: test.wantFeatures,
  131. }},
  132. }
  133. if !testEqual(got, want) {
  134. t.Errorf("#%d:\ngot %v\nwant %v", i, got, want)
  135. }
  136. if got, want := res, test.wantRes; !testEqual(got, want) {
  137. t.Errorf("#%d:\ngot %v\nwant %v", i, got, want)
  138. }
  139. }
  140. }
  141. func testEqual(a, b interface{}) bool {
  142. if a == nil && b == nil {
  143. return true
  144. }
  145. if a == nil || b == nil {
  146. return false
  147. }
  148. t := reflect.TypeOf(a)
  149. if t != reflect.TypeOf(b) {
  150. return false
  151. }
  152. if am, ok := a.(proto.Message); ok {
  153. return proto.Equal(am, b.(proto.Message))
  154. }
  155. if t.Kind() != reflect.Slice {
  156. panic(fmt.Sprintf("testEqual can only handle proto.Message and slices, got %s", t))
  157. }
  158. va := reflect.ValueOf(a)
  159. vb := reflect.ValueOf(b)
  160. if va.Len() != vb.Len() {
  161. return false
  162. }
  163. for i := 0; i < va.Len(); i++ {
  164. if !testEqual(va.Index(i).Interface(), vb.Index(i).Interface()) {
  165. return false
  166. }
  167. }
  168. return true
  169. }
  170. func TestAnnotateOneError(t *testing.T) {
  171. ctx := context.Background()
  172. c, err := NewImageAnnotatorClient(ctx, clientOpt)
  173. if err != nil {
  174. t.Fatal(err)
  175. }
  176. mockImageAnnotator.resps = []proto.Message{
  177. &pb.BatchAnnotateImagesResponse{
  178. Responses: []*pb.AnnotateImageResponse{{
  179. Error: &status.Status{Code: int32(codes.NotFound), Message: "not found"},
  180. }},
  181. },
  182. }
  183. _, err = c.annotateOne(ctx,
  184. &pb.Image{Source: &pb.ImageSource{ImageUri: "http://foo.jpg"}},
  185. nil, pb.Feature_LOGO_DETECTION, 1, nil)
  186. if c := grpc.Code(err); c != codes.NotFound {
  187. t.Errorf("got %v, want NotFound", c)
  188. }
  189. }