22 #include "google/protobuf/map.h"
23 #include "tensorflow/cc/saved_model/signature_constants.h"
24 #include "tensorflow/core/example/example.pb.h"
25 #include "tensorflow/core/example/feature.pb.h"
26 #include "tensorflow/core/framework/types.pb.h"
27 #include "tensorflow/core/lib/core/errors.h"
28 #include "tensorflow/core/lib/core/status.h"
29 #include "tensorflow/core/lib/core/status_test_util.h"
30 #include "tensorflow/core/platform/mutex.h"
31 #include "tensorflow/core/platform/threadpool_options.h"
32 #include "tensorflow/core/platform/types.h"
33 #include "tensorflow/core/protobuf/error_codes.pb.h"
34 #include "tensorflow/core/public/session.h"
35 #include "tensorflow/core/tfrt/utils/tensor_util.h"
36 #include "tsl/platform/errors.h"
37 #include "tensorflow_serving/apis/input.pb.h"
38 #include "tensorflow_serving/apis/model.pb.h"
39 #include "tensorflow_serving/apis/regression.pb.h"
40 #include "tensorflow_serving/config/model_server_config.pb.h"
41 #include "tensorflow_serving/core/availability_preserving_policy.h"
42 #include "tensorflow_serving/model_servers/model_platform_types.h"
43 #include "tensorflow_serving/model_servers/platform_config_util.h"
44 #include "tensorflow_serving/model_servers/server_core.h"
45 #include "tensorflow_serving/servables/tensorflow/session_bundle_config.pb.h"
46 #include "tensorflow_serving/servables/tensorflow/test_util/mock_tfrt_saved_model.h"
47 #include "tensorflow_serving/servables/tensorflow/tfrt_regressor.h"
48 #include "tensorflow_serving/servables/tensorflow/tfrt_saved_model_source_adapter.pb.h"
49 #include "tensorflow_serving/servables/tensorflow/tfrt_servable.h"
50 #include "tensorflow_serving/test_util/test_util.h"
52 namespace tensorflow {
57 using ::testing::DoAll;
58 using ::testing::HasSubstr;
59 using ::testing::Return;
60 using ::testing::WithArgs;
62 constexpr
char kTestModelName[] =
"test_model";
63 constexpr
int kTestModelVersion = 123;
65 class TfrtRegressorTest :
public ::testing::Test {
67 static void SetUpTestSuite() {
68 tfrt_stub::SetGlobalRuntime(
69 tfrt_stub::Runtime::Create(4));
72 void SetUp()
override {
73 ModelServerConfig config;
74 auto model_config = config.mutable_model_config_list()->add_config();
75 model_config->set_name(kTestModelName);
76 model_config->set_base_path(
77 test_util::TestSrcDirPath(
"servables/tensorflow/"
78 "testdata/saved_model_half_plus_two_cpu"));
79 model_config->set_model_platform(kTensorFlowModelPlatform);
83 ServerCore::Options options;
84 options.model_server_config = config;
85 PlatformConfigMap platform_config_map;
86 ::google::protobuf::Any source_adapter_config;
87 TfrtSavedModelSourceAdapterConfig saved_model_bundle_source_adapter_config;
88 source_adapter_config.PackFrom(saved_model_bundle_source_adapter_config);
89 (*(*platform_config_map
90 .mutable_platform_configs())[kTensorFlowModelPlatform]
91 .mutable_source_adapter_config()) = source_adapter_config;
92 options.platform_config_map = platform_config_map;
93 options.aspired_version_policy =
94 std::unique_ptr<AspiredVersionPolicy>(
new AvailabilityPreservingPolicy);
97 options.num_initial_load_threads = options.num_load_threads;
100 request_ = test_util::CreateProto<RegressionRequest>(
102 " name: \"test_model\""
103 " signature_name: \"regress_x_to_y\""
123 static void TearDownTestSuite() { server_core_ =
nullptr; }
126 Status GetSavedModelServableHandle(ServerCore* server_core,
127 ServableHandle<Servable>* servable) {
128 ModelSpec model_spec;
129 model_spec.set_name(kTestModelName);
130 return server_core->GetServableHandle(model_spec, servable);
133 Status CallRegress(ServerCore* server_core,
const RegressionRequest& request,
134 RegressionResponse* response) {
135 ServableHandle<Servable> servable;
136 TF_RETURN_IF_ERROR(GetSavedModelServableHandle(server_core, &servable));
137 tfrt::SavedModel::RunOptions run_options;
139 run_options, kTestModelVersion,
140 &(down_cast<TfrtSavedModelServable*>(servable.get()))->saved_model(),
144 static std::unique_ptr<ServerCore> server_core_;
146 RegressionRequest request_;
149 std::unique_ptr<ServerCore> TfrtRegressorTest::server_core_;
151 TEST_F(TfrtRegressorTest, Basic) {
152 auto request = test_util::CreateProto<RegressionRequest>(
154 " name: \"test_model\""
155 " signature_name: \"regress_x_to_y\""
173 " value: [ \"pt_BR\" ]"
201 RegressionResponse response;
203 TF_EXPECT_OK(CallRegress(server_core_.get(), request, &response));
206 test_util::EqualsProto(
207 "result { regressions { value: 42 } regressions { value: 12 }}"
209 " name: \"test_model\""
210 " signature_name: \"regress_x_to_y\""
211 " version { value: 123 }"
215 TEST_F(TfrtRegressorTest, BasicWithContext) {
216 auto request = test_util::CreateProto<RegressionRequest>(
218 " name: \"test_model\""
219 " signature_name: \"regress_x_to_y\""
222 " example_list_with_context {"
237 " value: [ \"pt_BR\" ]"
277 RegressionResponse response;
279 TF_EXPECT_OK(CallRegress(server_core_.get(), request, &response));
282 test_util::EqualsProto(
283 "result { regressions { value: 42 } regressions { value: 12 }}"
285 " name: \"test_model\""
286 " signature_name: \"regress_x_to_y\""
287 " version { value: 123 }"
291 TEST_F(TfrtRegressorTest, EmptyExampleList) {
292 auto request = test_util::CreateProto<RegressionRequest>(
294 " name: \"test_model\""
295 " signature_name: \"regress_x_to_y\""
301 RegressionResponse response;
303 Status status = CallRegress(server_core_.get(), request, &response);
304 EXPECT_EQ(status.code(), error::INVALID_ARGUMENT);
305 EXPECT_THAT(status.message(), ::testing::HasSubstr(
"Input is empty"));
308 TEST_F(TfrtRegressorTest, EmptyExampleListWithContext) {
309 auto request = test_util::CreateProto<RegressionRequest>(
311 " name: \"test_model\""
312 " signature_name: \"regress_x_to_y\""
315 " example_list_with_context {"
330 RegressionResponse response;
332 Status status = CallRegress(server_core_.get(), request, &response);
333 EXPECT_EQ(status.code(), error::INVALID_ARGUMENT);
334 EXPECT_THAT(status.message(), ::testing::HasSubstr(
"Input is empty"));
337 TEST_F(TfrtRegressorTest, EmptyInput) {
338 auto request = test_util::CreateProto<RegressionRequest>(
340 " name: \"test_model\""
341 " signature_name: \"regress_x_to_y\""
345 RegressionResponse response;
347 Status status = CallRegress(server_core_.get(), request, &response);
348 EXPECT_EQ(status.code(), error::INVALID_ARGUMENT);
349 EXPECT_THAT(status.message(), ::testing::HasSubstr(
"Input is empty"));
352 TEST_F(TfrtRegressorTest, InvalidFunctionName) {
353 RegressionResponse response;
354 std::unique_ptr<test_util::MockSavedModel> saved_model(
355 (
new test_util::MockSavedModel()));
356 EXPECT_CALL(*saved_model, GetFunctionMetadata(_))
358 .WillRepeatedly(Return(std::nullopt));
359 auto status = RunRegress(tfrt::SavedModel::RunOptions(), kTestModelVersion,
360 saved_model.get(), request_, &response);
361 EXPECT_EQ(status.code(), absl::StatusCode::kFailedPrecondition);
362 EXPECT_THAT(status.message(), HasSubstr(
"not found"));
365 TEST_F(TfrtRegressorTest, InvalidFunctionUnmatchedInputSize) {
366 RegressionResponse response;
367 std::unique_ptr<test_util::MockSavedModel> saved_model(
368 (
new test_util::MockSavedModel()));
369 tfrt::internal::Signature signature;
370 signature.input_names = {kRegressInputs,
"wrong input"};
371 signature.output_names = {kRegressOutputs};
372 tfrt::FunctionMetadata function_metadata(&signature);
373 EXPECT_CALL(*saved_model, GetFunctionMetadata(_))
375 .WillRepeatedly(Return(function_metadata));
376 auto status = RunRegress(tfrt::SavedModel::RunOptions(), kTestModelVersion,
377 saved_model.get(), request_, &response);
378 EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
379 EXPECT_THAT(status.message(), HasSubstr(
"Expected one input Tensor."));
382 TEST_F(TfrtRegressorTest, InvalidFunctionUnmatchedOutputSize) {
383 RegressionResponse response;
384 std::unique_ptr<test_util::MockSavedModel> saved_model(
385 (
new test_util::MockSavedModel()));
386 tfrt::internal::Signature signature;
387 signature.input_names = {kRegressInputs};
388 signature.output_names = {kRegressOutputs,
"wrong output"};
389 tfrt::FunctionMetadata function_metadata(&signature);
390 EXPECT_CALL(*saved_model, GetFunctionMetadata(_))
392 .WillRepeatedly(Return(function_metadata));
393 auto status = RunRegress(tfrt::SavedModel::RunOptions(), kTestModelVersion,
394 saved_model.get(), request_, &response);
395 EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
396 EXPECT_THAT(status.message(), HasSubstr(
"Expected one output Tensor."));
399 TEST_F(TfrtRegressorTest, InvalidFunctionInvalidInputName) {
400 RegressionResponse response;
401 std::unique_ptr<test_util::MockSavedModel> saved_model(
402 (
new test_util::MockSavedModel()));
403 tfrt::internal::Signature signature;
404 signature.input_names = {
"wrong input"};
405 signature.output_names = {kRegressOutputs};
406 tfrt::FunctionMetadata function_metadata(&signature);
407 EXPECT_CALL(*saved_model, GetFunctionMetadata(_))
409 .WillRepeatedly(Return(function_metadata));
410 auto status = RunRegress(tfrt::SavedModel::RunOptions(), kTestModelVersion,
411 saved_model.get(), request_, &response);
412 EXPECT_EQ(status.code(), absl::StatusCode::kFailedPrecondition);
413 EXPECT_THAT(status.message(),
414 HasSubstr(
"No regression inputs found in function's metadata"));
417 TEST_F(TfrtRegressorTest, InvalidFunctionInvalidOutputName) {
418 RegressionResponse response;
419 std::unique_ptr<test_util::MockSavedModel> saved_model(
420 (
new test_util::MockSavedModel()));
421 tfrt::internal::Signature signature;
422 signature.input_names = {kRegressInputs};
423 signature.output_names = {
"wrong output"};
424 tfrt::FunctionMetadata function_metadata(&signature);
425 EXPECT_CALL(*saved_model, GetFunctionMetadata(_))
427 .WillRepeatedly(Return(function_metadata));
428 auto status = RunRegress(tfrt::SavedModel::RunOptions(), kTestModelVersion,
429 saved_model.get(), request_, &response);
430 EXPECT_EQ(status.code(), absl::StatusCode::kFailedPrecondition);
431 EXPECT_THAT(status.message(),
432 HasSubstr(
"No regression outputs found in function's metadata"));
435 TEST_F(TfrtRegressorTest, RunsFails) {
436 RegressionResponse response;
437 std::unique_ptr<test_util::MockSavedModel> saved_model(
438 (
new test_util::MockSavedModel()));
439 tfrt::internal::Signature signature;
440 signature.input_names = {kRegressInputs};
441 signature.output_names = {kRegressOutputs};
442 tfrt::FunctionMetadata function_metadata(&signature);
443 EXPECT_CALL(*saved_model, GetFunctionMetadata(_))
445 .WillRepeatedly(Return(function_metadata));
446 EXPECT_CALL(*saved_model,
447 Run(_, _, ::testing::An<absl::Span<const Tensor>>(), _))
449 .WillRepeatedly(Return(errors::InvalidArgument(
"test error")));
450 auto status = RunRegress(tfrt::SavedModel::RunOptions(), kTestModelVersion,
451 saved_model.get(), request_, &response);
452 EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
453 EXPECT_THAT(status.message(), HasSubstr(
"test error"));
456 TEST_F(TfrtRegressorTest, UnexpectedOutputTensorNumber) {
457 RegressionResponse response;
458 std::unique_ptr<test_util::MockSavedModel> saved_model(
459 (
new test_util::MockSavedModel()));
460 tfrt::internal::Signature signature;
461 signature.input_names = {kRegressInputs};
462 signature.output_names = {kRegressOutputs};
463 tfrt::FunctionMetadata function_metadata(&signature);
464 EXPECT_CALL(*saved_model, GetFunctionMetadata(_))
466 .WillRepeatedly(Return(function_metadata));
468 EXPECT_CALL(*saved_model,
469 Run(_, _, ::testing::An<absl::Span<const Tensor>>(), _))
472 DoAll(WithArgs<3>([&](std::vector<Tensor>* output_tensors) {
473 output_tensors->push_back(output);
474 output_tensors->push_back(output);
476 Return(absl::OkStatus())));
477 auto status = RunRegress(tfrt::SavedModel::RunOptions(), kTestModelVersion,
478 saved_model.get(), request_, &response);
479 EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
480 EXPECT_THAT(status.message(),
481 HasSubstr(
"Expected output_tensors and output_tensor_names to "
482 "have the same size."));
485 TEST_F(TfrtRegressorTest, UnexpectedOutputTensorShape) {
486 RegressionResponse response;
487 std::unique_ptr<test_util::MockSavedModel> saved_model(
488 (
new test_util::MockSavedModel()));
489 tfrt::internal::Signature signature;
490 signature.input_names = {kRegressInputs};
491 signature.output_names = {kRegressOutputs};
492 tfrt::FunctionMetadata function_metadata(&signature);
493 EXPECT_CALL(*saved_model, GetFunctionMetadata(_))
495 .WillRepeatedly(Return(function_metadata));
496 Tensor output(DT_FLOAT, TensorShape({1, 1, 1}));
497 EXPECT_CALL(*saved_model,
498 Run(_, _, ::testing::An<absl::Span<const Tensor>>(), _))
501 DoAll(WithArgs<3>([&](std::vector<Tensor>* output_tensors) {
502 output_tensors->push_back(output);
504 Return(absl::OkStatus())));
505 auto status = RunRegress(tfrt::SavedModel::RunOptions(), kTestModelVersion,
506 saved_model.get(), request_, &response);
507 EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
508 EXPECT_THAT(status.message(),
509 HasSubstr(
"Expected output Tensor shape to be either"));
512 TEST_F(TfrtRegressorTest, UnexpectedOutputTensorSize) {
513 RegressionResponse response;
514 std::unique_ptr<test_util::MockSavedModel> saved_model(
515 (
new test_util::MockSavedModel()));
516 tfrt::internal::Signature signature;
517 signature.input_names = {kRegressInputs};
518 signature.output_names = {kRegressOutputs};
519 tfrt::FunctionMetadata function_metadata(&signature);
520 EXPECT_CALL(*saved_model, GetFunctionMetadata(_))
522 .WillRepeatedly(Return(function_metadata));
523 Tensor output(DT_FLOAT, TensorShape({3}));
524 EXPECT_CALL(*saved_model,
525 Run(_, _, ::testing::An<absl::Span<const Tensor>>(), _))
528 DoAll(WithArgs<3>([&](std::vector<Tensor>* output_tensors) {
529 output_tensors->push_back(output);
531 Return(absl::OkStatus())));
532 auto status = RunRegress(tfrt::SavedModel::RunOptions(), kTestModelVersion,
533 saved_model.get(), request_, &response);
534 EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
535 EXPECT_THAT(status.message(),
536 HasSubstr(
"Input batch size did not match output batch size"));
539 TEST_F(TfrtRegressorTest, UnexpectedOutputTensorType) {
540 RegressionResponse response;
541 std::unique_ptr<test_util::MockSavedModel> saved_model(
542 (
new test_util::MockSavedModel()));
543 tfrt::internal::Signature signature;
544 signature.input_names = {kRegressInputs};
545 signature.output_names = {kRegressOutputs};
546 tfrt::FunctionMetadata function_metadata(&signature);
547 EXPECT_CALL(*saved_model, GetFunctionMetadata(_))
549 .WillRepeatedly(Return(function_metadata));
550 Tensor output(DT_STRING, TensorShape({1}));
551 EXPECT_CALL(*saved_model,
552 Run(_, _, ::testing::An<absl::Span<const Tensor>>(), _))
555 DoAll(WithArgs<3>([&](std::vector<Tensor>* output_tensors) {
556 output_tensors->push_back(output);
558 Return(absl::OkStatus())));
559 auto status = RunRegress(tfrt::SavedModel::RunOptions(), kTestModelVersion,
560 saved_model.get(), request_, &response);
561 EXPECT_EQ(status.code(), absl::StatusCode::kInvalidArgument);
562 EXPECT_THAT(status.message(),
563 HasSubstr(
"Expected output Tensor of DT_FLOAT."));
static Status Create(Options options, std::unique_ptr< ServerCore > *core)