TensorFlow Serving C++ API Documentation
regressor_test.cc
1 /* Copyright 2017 Google Inc. All Rights Reserved.
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 ==============================================================================*/
15 
16 #include "tensorflow_serving/servables/tensorflow/regressor.h"
17 
18 #include <memory>
19 #include <string>
20 #include <utility>
21 #include <vector>
22 
23 #include "google/protobuf/map.h"
24 #include "absl/types/optional.h"
25 #include "tensorflow/cc/saved_model/signature_constants.h"
26 #include "tensorflow/core/example/example.pb.h"
27 #include "tensorflow/core/example/feature.pb.h"
28 #include "tensorflow/core/lib/core/errors.h"
29 #include "tensorflow/core/lib/core/status.h"
30 #include "tensorflow/core/lib/core/status_test_util.h"
31 #include "tensorflow/core/platform/mutex.h"
32 #include "tensorflow/core/platform/threadpool_options.h"
33 #include "tensorflow/core/platform/types.h"
34 #include "tensorflow/core/public/session.h"
35 #include "tensorflow_serving/apis/input.pb.h"
36 #include "tensorflow_serving/apis/model.pb.h"
37 #include "tensorflow_serving/apis/regression.pb.h"
38 #include "tensorflow_serving/core/test_util/mock_session.h"
39 #include "tensorflow_serving/servables/tensorflow/util.h"
40 #include "tensorflow_serving/test_util/test_util.h"
41 
42 namespace tensorflow {
43 namespace serving {
44 namespace {
45 
46 using test_util::EqualsProto;
47 using test_util::MockSession;
48 using ::testing::_;
49 
50 const char kInputTensor[] = "input:0";
51 const char kOutputTensor[] = "output:0";
52 const char kOutputPlusOneTensor[] = "outputPlusOne:0";
53 const char kImproperlySizedOutputTensor[] = "ImproperlySizedOutput:0";
54 const char kOutputFeature[] = "output";
55 
56 const char kOutputPlusOneSignature[] = "output_plus_one";
57 const char kInvalidNamedSignature[] = "invalid_classification_signature";
58 const char kImproperlySizedOutputSignature[] = "ImproperlySizedOutputSignature";
59 
60 // Fake Session used for testing TensorFlowRegressor
61 // Assumes the input Tensor "input:0" has serialized tensorflow::Example values.
62 // Copies the "output" float feature from each Example.
63 class FakeSession : public tensorflow::Session {
64  public:
65  explicit FakeSession(absl::optional<int64_t> expected_timeout)
66  : expected_timeout_(expected_timeout) {}
67  ~FakeSession() override = default;
68  Status Create(const GraphDef& graph) override {
69  return errors::Unimplemented("not available in fake");
70  }
71  Status Extend(const GraphDef& graph) override {
72  return errors::Unimplemented("not available in fake");
73  }
74 
75  Status Close() override {
76  return errors::Unimplemented("not available in fake");
77  }
78 
79  Status ListDevices(std::vector<DeviceAttributes>* response) override {
80  return errors::Unimplemented("not available in fake");
81  }
82 
83  Status Run(const std::vector<std::pair<string, Tensor>>& inputs,
84  const std::vector<string>& output_names,
85  const std::vector<string>& target_nodes,
86  std::vector<Tensor>* outputs) override {
87  if (expected_timeout_) {
88  LOG(FATAL) << "Run() without RunOptions not expected to be called";
89  }
90  RunMetadata run_metadata;
91  return Run(RunOptions(), inputs, output_names, target_nodes, outputs,
92  &run_metadata);
93  }
94 
95  Status Run(const RunOptions& run_options,
96  const std::vector<std::pair<string, Tensor>>& inputs,
97  const std::vector<string>& output_names,
98  const std::vector<string>& target_nodes,
99  std::vector<Tensor>* outputs, RunMetadata* run_metadata) override {
100  return Run(run_options, inputs, output_names, target_nodes, outputs,
101  run_metadata, thread::ThreadPoolOptions());
102  }
103 
104  Status Run(const RunOptions& run_options,
105  const std::vector<std::pair<string, Tensor>>& inputs,
106  const std::vector<string>& output_names,
107  const std::vector<string>& target_nodes,
108  std::vector<Tensor>* outputs, RunMetadata* run_metadata,
109  const thread::ThreadPoolOptions& thread_pool_options) override {
110  if (expected_timeout_) {
111  CHECK_EQ(*expected_timeout_, run_options.timeout_in_ms());
112  }
113  if (inputs.size() != 1 || inputs[0].first != kInputTensor) {
114  return errors::Internal("Expected one input Tensor.");
115  }
116  const Tensor& input = inputs[0].second;
117  std::vector<Example> examples;
118  TF_RETURN_IF_ERROR(GetExamples(input, &examples));
119  Tensor output;
120  TF_RETURN_IF_ERROR(GetOutputTensor(examples, output_names[0], &output));
121  outputs->push_back(output);
122  return absl::OkStatus();
123  }
124 
125  // Parses TensorFlow Examples from a string Tensor.
126  static Status GetExamples(const Tensor& input,
127  std::vector<Example>* examples) {
128  examples->clear();
129  const int batch_size = input.dim_size(0);
130  const auto& flat_input = input.flat<tstring>();
131  for (int i = 0; i < batch_size; ++i) {
132  Example example;
133  if (!example.ParseFromArray(flat_input(i).data(), flat_input(i).size())) {
134  return errors::Internal("failed to parse example");
135  }
136  examples->push_back(example);
137  }
138  return absl::OkStatus();
139  }
140 
141  // Gets the Feature from an Example with the given name. Returns empty
142  // Feature if the name does not exist.
143  static Feature GetFeature(const Example& example, const string& name) {
144  const auto it = example.features().feature().find(name);
145  if (it != example.features().feature().end()) {
146  return it->second;
147  }
148  return Feature();
149  }
150 
151  // Creates a Tensor by copying the "output" feature from each Example.
152  // Requires each Example have an bytes feature called "class" which is of the
153  // same non-zero length.
154  static Status GetOutputTensor(const std::vector<Example>& examples,
155  const string& output_tensor_name,
156  Tensor* tensor) {
157  if (examples.empty()) {
158  return errors::Internal("empty example list");
159  }
160  const int batch_size = examples.size();
161  if (output_tensor_name == kImproperlySizedOutputTensor) {
162  // Insert a rank 3 tensor which should be an error because outputs are
163  // expected to be of shape [batch_size] or [batch_size, 1].
164  *tensor = Tensor(DT_FLOAT, TensorShape({batch_size, 1, 10}));
165  return absl::OkStatus();
166  }
167  // Both tensor shapes are valid, so make one of shape [batch_size, 1] and
168  // the rest of shape [batch_size].
169  *tensor = output_tensor_name == kOutputPlusOneTensor
170  ? Tensor(DT_FLOAT, TensorShape({batch_size, 1}))
171  : Tensor(DT_FLOAT, TensorShape({batch_size}));
172 
173  const float offset = output_tensor_name == kOutputPlusOneTensor ? 1 : 0;
174  for (int i = 0; i < batch_size; ++i) {
175  const Feature feature = GetFeature(examples[i], kOutputFeature);
176  if (feature.float_list().value_size() != 1) {
177  return errors::Internal("incorrect number of values in output feature");
178  }
179  tensor->flat<float>()(i) = feature.float_list().value(0) + offset;
180  }
181  return absl::OkStatus();
182  }
183 
184  private:
185  const absl::optional<int64_t> expected_timeout_;
186 };
187 
188 class RegressorTest : public ::testing::TestWithParam<bool> {
189  public:
190  void SetUp() override {
191  SetSignatureMethodNameCheckFeature(IsMethodNameCheckEnabled());
192  saved_model_bundle_.reset(new SavedModelBundle);
193  meta_graph_def_ = &saved_model_bundle_->meta_graph_def;
194  absl::optional<int64_t> expected_timeout = GetRunOptions().timeout_in_ms();
195  fake_session_ = new FakeSession(expected_timeout);
196  saved_model_bundle_->session.reset(fake_session_);
197 
198  auto* signature_defs = meta_graph_def_->mutable_signature_def();
199  SignatureDef sig_def;
200  TensorInfo input_tensor_info;
201  input_tensor_info.set_name(kInputTensor);
202  (*sig_def.mutable_inputs())[kRegressInputs] = input_tensor_info;
203  TensorInfo scores_tensor_info;
204  scores_tensor_info.set_name(kOutputTensor);
205  (*sig_def.mutable_outputs())[kRegressOutputs] = scores_tensor_info;
206  if (IsMethodNameCheckEnabled()) sig_def.set_method_name(kRegressMethodName);
207  (*signature_defs)[kDefaultServingSignatureDefKey] = sig_def;
208 
209  AddNamedSignatureToSavedModelBundle(
210  kInputTensor, kOutputPlusOneTensor, kOutputPlusOneSignature,
211  true /* is_regression */, meta_graph_def_);
212  AddNamedSignatureToSavedModelBundle(
213  kInputTensor, kOutputPlusOneTensor, kInvalidNamedSignature,
214  false /* is_regression */, meta_graph_def_);
215 
216  // Add a named signature where the output is not valid.
217  AddNamedSignatureToSavedModelBundle(
218  kInputTensor, kImproperlySizedOutputTensor,
219  kImproperlySizedOutputSignature, true /* is_regression */,
220  meta_graph_def_);
221  }
222 
223  protected:
224  bool IsMethodNameCheckEnabled() { return GetParam(); }
225 
226  // Return an example with the feature "output" = [output].
227  Example example_with_output(const float output) {
228  Feature feature;
229  feature.mutable_float_list()->add_value(output);
230  Example example;
231  (*example.mutable_features()->mutable_feature())["output"] = feature;
232  return example;
233  }
234 
235  Status Create() {
236  std::unique_ptr<SavedModelBundle> saved_model(new SavedModelBundle);
237  saved_model->meta_graph_def = saved_model_bundle_->meta_graph_def;
238  saved_model->session = std::move(saved_model_bundle_->session);
239  return CreateRegressorFromSavedModelBundle(
240  GetRunOptions(), std::move(saved_model), &regressor_);
241  }
242 
243  RunOptions GetRunOptions() const {
244  RunOptions run_options;
245  run_options.set_timeout_in_ms(42);
246  return run_options;
247  }
248 
249  // Add a named signature to the mutable meta_graph_def* parameter.
250  // If is_regression is false, will add a classification signature, which is
251  // invalid in classification requests.
252  void AddNamedSignatureToSavedModelBundle(
253  const string& input_tensor_name, const string& output_scores_tensor_name,
254  const string& signature_name, const bool is_regression,
255  tensorflow::MetaGraphDef* meta_graph_def) {
256  auto* signature_defs = meta_graph_def->mutable_signature_def();
257  SignatureDef sig_def;
258  string method_name;
259  if (is_regression) {
260  TensorInfo input_tensor_info;
261  input_tensor_info.set_name(input_tensor_name);
262  (*sig_def.mutable_inputs())[kRegressInputs] = input_tensor_info;
263  TensorInfo scores_tensor_info;
264  scores_tensor_info.set_name(output_scores_tensor_name);
265  (*sig_def.mutable_outputs())[kRegressOutputs] = scores_tensor_info;
266  method_name = kRegressMethodName;
267  } else {
268  TensorInfo input_tensor_info;
269  input_tensor_info.set_name(input_tensor_name);
270  (*sig_def.mutable_inputs())[kClassifyInputs] = input_tensor_info;
271  TensorInfo class_tensor_info;
272  class_tensor_info.set_name(kOutputPlusOneTensor);
273  (*sig_def.mutable_outputs())[kClassifyOutputClasses] = class_tensor_info;
274  method_name = kClassifyMethodName;
275  }
276  if (IsMethodNameCheckEnabled()) sig_def.set_method_name(method_name);
277  (*signature_defs)[signature_name] = sig_def;
278  }
279 
280  // Variables used to create the regression model
281  tensorflow::MetaGraphDef* meta_graph_def_;
282  FakeSession* fake_session_;
283  std::unique_ptr<SavedModelBundle> saved_model_bundle_;
284 
285  // Regression model valid after calling create.
286  std::unique_ptr<RegressorInterface> regressor_;
287 
288  // Convenience variables.
289  RegressionRequest request_;
290  RegressionResult result_;
291 };
292 
293 TEST_P(RegressorTest, BasicExampleList) {
294  TF_ASSERT_OK(Create());
295  auto* examples =
296  request_.mutable_input()->mutable_example_list()->mutable_examples();
297  *examples->Add() = example_with_output(2.0);
298  *examples->Add() = example_with_output(3.0);
299  TF_ASSERT_OK(regressor_->Regress(request_, &result_));
300  EXPECT_THAT(result_, EqualsProto(" regressions { "
301  " value: 2.0 "
302  " } "
303  " regressions { "
304  " value: 3.0 "
305  " } "));
306  RegressionResponse response;
307  TF_ASSERT_OK(RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def,
308  {}, fake_session_, request_, &response));
309  EXPECT_THAT(response.result(), EqualsProto(" regressions { "
310  " value: 2.0 "
311  " } "
312  " regressions { "
313  " value: 3.0 "
314  " } "));
315 }
316 
317 TEST_P(RegressorTest, BasicExampleListWithContext) {
318  TF_ASSERT_OK(Create());
319  auto* list_with_context =
320  request_.mutable_input()->mutable_example_list_with_context();
321  // Add two empty examples.
322  list_with_context->add_examples();
323  list_with_context->add_examples();
324  // Add the context which contains the output predictions.
325  *list_with_context->mutable_context() = example_with_output(3.0);
326  TF_ASSERT_OK(regressor_->Regress(request_, &result_));
327  EXPECT_THAT(result_, EqualsProto(" regressions { "
328  " value: 3.0 "
329  " } "
330  " regressions { "
331  " value: 3.0 "
332  " } "));
333  RegressionResponse response;
334  TF_ASSERT_OK(RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def,
335  {}, fake_session_, request_, &response));
336  EXPECT_THAT(response.result(), EqualsProto(" regressions { "
337  " value: 3.0 "
338  " } "
339  " regressions { "
340  " value: 3.0 "
341  " } "));
342 }
343 
344 TEST_P(RegressorTest, ValidNamedSignature) {
345  TF_ASSERT_OK(Create());
346  request_.mutable_model_spec()->set_signature_name(kOutputPlusOneSignature);
347  auto* examples =
348  request_.mutable_input()->mutable_example_list()->mutable_examples();
349  *examples->Add() = example_with_output(2.0);
350  *examples->Add() = example_with_output(3.0);
351  TF_ASSERT_OK(regressor_->Regress(request_, &result_));
352  EXPECT_THAT(result_, EqualsProto(" regressions { "
353  " value: 3.0 "
354  " } "
355  " regressions { "
356  " value: 4.0 "
357  " } "));
358 
359  RegressionResponse response;
360  TF_ASSERT_OK(RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def,
361  {}, fake_session_, request_, &response));
362  EXPECT_THAT(response.result(), EqualsProto(" regressions { "
363  " value: 3.0 "
364  " } "
365  " regressions { "
366  " value: 4.0 "
367  " } "));
368 }
369 
370 TEST_P(RegressorTest, InvalidNamedSignature) {
371  TF_ASSERT_OK(Create());
372  request_.mutable_model_spec()->set_signature_name(kInvalidNamedSignature);
373  auto* examples =
374  request_.mutable_input()->mutable_example_list()->mutable_examples();
375  *examples->Add() = example_with_output(2.0);
376  *examples->Add() = example_with_output(3.0);
377  Status status = regressor_->Regress(request_, &result_);
378  ASSERT_FALSE(status.ok());
379  EXPECT_EQ(static_cast<absl::StatusCode>(absl::StatusCode::kInvalidArgument),
380  status.code())
381  << status;
382 
383  RegressionResponse response;
384  status = RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def, {},
385  fake_session_, request_, &response);
386  ASSERT_FALSE(status.ok());
387  EXPECT_EQ(static_cast<absl::StatusCode>(absl::StatusCode::kInvalidArgument),
388  status.code())
389  << status;
390 }
391 
392 TEST_P(RegressorTest, MalformedOutputs) {
393  TF_ASSERT_OK(Create());
394  request_.mutable_model_spec()->set_signature_name(
395  kImproperlySizedOutputSignature);
396  auto* examples =
397  request_.mutable_input()->mutable_example_list()->mutable_examples();
398  *examples->Add() = example_with_output(2.0);
399  *examples->Add() = example_with_output(3.0);
400  Status status = regressor_->Regress(request_, &result_);
401 
402  ASSERT_FALSE(status.ok());
403  EXPECT_EQ(static_cast<absl::StatusCode>(absl::StatusCode::kInvalidArgument),
404  status.code())
405  << status;
406  // Test RunRegress
407  RegressionResponse response;
408  status = RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def, {},
409  fake_session_, request_, &response);
410  ASSERT_FALSE(status.ok());
411  EXPECT_EQ(static_cast<absl::StatusCode>(absl::StatusCode::kInvalidArgument),
412  status.code())
413  << status;
414 }
415 
416 TEST_P(RegressorTest, EmptyInput) {
417  TF_ASSERT_OK(Create());
418  // Touch input.
419  request_.mutable_input();
420  Status status = regressor_->Regress(request_, &result_);
421  ASSERT_FALSE(status.ok());
422  EXPECT_EQ(status.code(), error::Code::INVALID_ARGUMENT);
423  EXPECT_THAT(status.message(), ::testing::HasSubstr("Input is empty"));
424  RegressionResponse response;
425  status = RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def, {},
426  fake_session_, request_, &response);
427  ASSERT_FALSE(status.ok());
428  EXPECT_EQ(status.code(), error::Code::INVALID_ARGUMENT);
429  EXPECT_THAT(status.message(), ::testing::HasSubstr("Input is empty"));
430 }
431 
432 TEST_P(RegressorTest, EmptyExampleList) {
433  TF_ASSERT_OK(Create());
434  request_.mutable_input()->mutable_example_list();
435  Status status = regressor_->Regress(request_, &result_);
436  ASSERT_FALSE(status.ok());
437  EXPECT_EQ(status.code(), error::Code::INVALID_ARGUMENT);
438  EXPECT_THAT(status.message(), ::testing::HasSubstr("Input is empty"));
439  RegressionResponse response;
440  status = RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def, {},
441  fake_session_, request_, &response);
442  ASSERT_FALSE(status.ok());
443  EXPECT_EQ(status.code(), error::Code::INVALID_ARGUMENT);
444  EXPECT_THAT(status.message(), ::testing::HasSubstr("Input is empty"));
445 }
446 
447 TEST_P(RegressorTest, EmptyExampleListWithContext) {
448  TF_ASSERT_OK(Create());
449  // Add a ExampleListWithContext which has context but no examples.
450  *request_.mutable_input()
451  ->mutable_example_list_with_context()
452  ->mutable_context() = example_with_output(3);
453  Status status = regressor_->Regress(request_, &result_);
454  ASSERT_FALSE(status.ok());
455  EXPECT_EQ(status.code(), error::Code::INVALID_ARGUMENT);
456  EXPECT_THAT(status.message(), ::testing::HasSubstr("Input is empty"));
457  RegressionResponse response;
458  status = RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def, {},
459  fake_session_, request_, &response);
460  ASSERT_FALSE(status.ok());
461  EXPECT_EQ(status.code(), error::Code::INVALID_ARGUMENT);
462  EXPECT_THAT(status.message(), ::testing::HasSubstr("Input is empty"));
463 }
464 
465 TEST_P(RegressorTest, RunsFails) {
466  MockSession* mock = new MockSession;
467  saved_model_bundle_->session.reset(mock);
468  EXPECT_CALL(*mock, Run(_, _, _, _, _, _, _))
469  .WillRepeatedly(
470  ::testing::Return(errors::Internal("Run totally failed")));
471  TF_ASSERT_OK(Create());
472  *request_.mutable_input()->mutable_example_list()->mutable_examples()->Add() =
473  example_with_output(2.0);
474  Status status = regressor_->Regress(request_, &result_);
475  ASSERT_FALSE(status.ok());
476  EXPECT_THAT(status.ToString(), ::testing::HasSubstr("Run totally failed"));
477  RegressionResponse response;
478  status = RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def, {},
479  mock, request_, &response);
480  ASSERT_FALSE(status.ok());
481  EXPECT_THAT(status.ToString(), ::testing::HasSubstr("Run totally failed"));
482 }
483 
484 TEST_P(RegressorTest, UnexpectedOutputTensorSize) {
485  MockSession* mock = new MockSession;
486  saved_model_bundle_->session.reset(mock);
487  std::vector<Tensor> outputs = {Tensor(DT_FLOAT, TensorShape({2}))};
488  EXPECT_CALL(*mock, Run(_, _, _, _, _, _, _))
489  .WillOnce(::testing::DoAll(::testing::SetArgPointee<4>(outputs),
490  ::testing::Return(absl::OkStatus())));
491  TF_ASSERT_OK(Create());
492  *request_.mutable_input()->mutable_example_list()->mutable_examples()->Add() =
493  example_with_output(2.0);
494  Status status = regressor_->Regress(request_, &result_);
495  ASSERT_FALSE(status.ok());
496  EXPECT_THAT(status.ToString(), ::testing::HasSubstr("output batch size"));
497  EXPECT_CALL(*mock, Run(_, _, _, _, _, _, _))
498  .WillOnce(::testing::DoAll(::testing::SetArgPointee<4>(outputs),
499  ::testing::Return(absl::OkStatus())));
500  RegressionResponse response;
501  status = RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def, {},
502  mock, request_, &response);
503  ASSERT_FALSE(status.ok());
504  EXPECT_THAT(status.ToString(), ::testing::HasSubstr("output batch size"));
505 }
506 
507 TEST_P(RegressorTest, UnexpectedOutputTensorType) {
508  MockSession* mock = new MockSession;
509  saved_model_bundle_->session.reset(mock);
510  // We expect a FLOAT output type; test returning a STRING.
511  std::vector<Tensor> outputs = {Tensor(DT_STRING, TensorShape({1}))};
512  EXPECT_CALL(*mock, Run(_, _, _, _, _, _, _))
513  .WillOnce(::testing::DoAll(::testing::SetArgPointee<4>(outputs),
514  ::testing::Return(absl::OkStatus())));
515  TF_ASSERT_OK(Create());
516  *request_.mutable_input()->mutable_example_list()->mutable_examples()->Add() =
517  example_with_output(2.0);
518  Status status = regressor_->Regress(request_, &result_);
519  ASSERT_FALSE(status.ok());
520  EXPECT_THAT(status.ToString(),
521  ::testing::HasSubstr("Expected output Tensor of DT_FLOAT"));
522  EXPECT_CALL(*mock, Run(_, _, _, _, _, _, _))
523  .WillOnce(::testing::DoAll(::testing::SetArgPointee<4>(outputs),
524  ::testing::Return(absl::OkStatus())));
525  RegressionResponse response;
526  status = RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def, {},
527  mock, request_, &response);
528  ASSERT_FALSE(status.ok());
529  EXPECT_THAT(status.ToString(),
530  ::testing::HasSubstr("Expected output Tensor of DT_FLOAT"));
531 }
532 
533 TEST_P(RegressorTest, MissingRegressionSignature) {
534  auto* signature_defs = meta_graph_def_->mutable_signature_def();
535  SignatureDef sig_def;
536  (*signature_defs)[kDefaultServingSignatureDefKey] = sig_def;
537  TF_ASSERT_OK(Create());
538  Feature feature;
539  feature.mutable_bytes_list()->add_value("uno");
540  Example example;
541  (*example.mutable_features()->mutable_feature())["class"] = feature;
542  *request_.mutable_input()->mutable_example_list()->mutable_examples()->Add() =
543  example;
544  // TODO(b/26220896): This error should move to construction time.
545  Status status = regressor_->Regress(request_, &result_);
546  ASSERT_FALSE(status.ok());
547  EXPECT_EQ(static_cast<absl::StatusCode>(absl::StatusCode::kInvalidArgument),
548  status.code())
549  << status;
550  RegressionResponse response;
551  status = RunRegress(GetRunOptions(), saved_model_bundle_->meta_graph_def, {},
552  fake_session_, request_, &response);
553  ASSERT_FALSE(status.ok());
554  EXPECT_EQ(static_cast<absl::StatusCode>(absl::StatusCode::kInvalidArgument),
555  status.code())
556  << status;
557 }
558 
559 TEST_P(RegressorTest, MethodNameCheck) {
560  RegressionResponse response;
561  *request_.mutable_input()->mutable_example_list()->mutable_examples()->Add() =
562  example_with_output(2.0);
563  auto* signature_defs = meta_graph_def_->mutable_signature_def();
564 
565  // Legit method name. Should always work.
566  (*signature_defs)[kDefaultServingSignatureDefKey].set_method_name(
567  kRegressMethodName);
568  TF_EXPECT_OK(RunRegress(GetRunOptions(), *meta_graph_def_, {}, fake_session_,
569  request_, &response));
570 
571  // Unsupported method name will fail when method check is enabled.
572  (*signature_defs)[kDefaultServingSignatureDefKey].set_method_name(
573  "not/supported/method");
574  EXPECT_EQ(RunRegress(GetRunOptions(), *meta_graph_def_, {}, fake_session_,
575  request_, &response)
576  .ok(),
577  !IsMethodNameCheckEnabled());
578 
579  // Empty method name will fail when method check is enabled.
580  (*signature_defs)[kDefaultServingSignatureDefKey].clear_method_name();
581  EXPECT_EQ(RunRegress(GetRunOptions(), *meta_graph_def_, {}, fake_session_,
582  request_, &response)
583  .ok(),
584  !IsMethodNameCheckEnabled());
585 }
586 
587 INSTANTIATE_TEST_SUITE_P(Regressor, RegressorTest, ::testing::Bool());
588 
589 } // namespace
590 } // namespace serving
591 } // namespace tensorflow