From ca0bb64cd0f1a4748d15bbd16e5bf11bf91eeaf0 Mon Sep 17 00:00:00 2001 From: xuyongfei Date: Mon, 18 Jan 2021 19:31:39 +0800 Subject: [PATCH] serving, ut5 --- cmake/external_libs/glog.cmake | 6 +- .../ccsrc/common/proto_tensor.cc | 18 +- mindspore_serving/ccsrc/common/status.h | 3 - mindspore_serving/ccsrc/common/tensor_base.cc | 2 +- mindspore_serving/ccsrc/common/tensor_base.h | 4 +- .../ccsrc/master/restful/http_handle.cc | 8 +- .../ccsrc/master/restful/http_process.cc | 93 +- .../ccsrc/master/restful/restful_request.cc | 92 +- .../ccsrc/master/restful/restful_server.cc | 12 +- mindspore_serving/ccsrc/python/serving_py.cc | 3 + mindspore_serving/ccsrc/python/tensor_py.cc | 6 +- mindspore_serving/client/python/client.py | 37 +- mindspore_serving/log.py | 10 +- mindspore_serving/master/__init__.py | 1 - requirements_test.txt | 2 + tests/ut/CMakeLists.txt | 17 +- tests/ut/cpp/runtest.sh | 7 +- tests/ut/cpp/tests/parse_restful.cc | 15 +- tests/ut/python/runtest.sh | 6 +- tests/ut/python/tests/common.py | 38 + tests/ut/python/tests/common_restful.py | 75 ++ .../python/tests/test_mater_worker_client.py | 176 +-- .../python/tests/test_restful_base64_data.py | 949 +++++++++++++++ .../ut/python/tests/test_restful_json_data.py | 1040 +++++++++++++++++ tests/ut/python/tests/test_restful_request.py | 335 ++++++ 25 files changed, 2693 insertions(+), 262 deletions(-) create mode 100644 tests/ut/python/tests/common_restful.py create mode 100644 tests/ut/python/tests/test_restful_base64_data.py create mode 100644 tests/ut/python/tests/test_restful_json_data.py create mode 100644 tests/ut/python/tests/test_restful_request.py diff --git a/cmake/external_libs/glog.cmake b/cmake/external_libs/glog.cmake index e65d474..81089e4 100644 --- a/cmake/external_libs/glog.cmake +++ b/cmake/external_libs/glog.cmake @@ -1,12 +1,12 @@ set(glog_CXXFLAGS "-D_FORTIFY_SOURCE=2 -O2 ${SECURE_CXX_FLAGS} -D_GLIBCXX_USE_CXX11_ABI=0") set(glog_CFLAGS "-D_FORTIFY_SOURCE=2 -O2") -if (ENABLE_GITEE) +if(ENABLE_GITEE) set(REQ_URL "https://gitee.com/mirrors/glog/repository/archive/v0.4.0.tar.gz") set(MD5 "22fe340ddc231e6c8e46bc295320f8ee") else() set(REQ_URL "https://github.com/google/glog/archive/v0.4.0.tar.gz") set(MD5 "0daea8785e6df922d7887755c3d100d0") -endif () +endif() mindspore_add_pkg(glog VER 0.4.0 LIBS glog @@ -14,4 +14,4 @@ mindspore_add_pkg(glog MD5 ${MD5} CMAKE_OPTION -DBUILD_TESTING=OFF -DCMAKE_POSITION_INDEPENDENT_CODE=ON -DBUILD_SHARED_LIBS=ON -DWITH_GFLAGS=OFF) include_directories(${glog_INC}) -add_library(mindspore::glog ALIAS glog::glog) +add_library(mindspore_serving::glog ALIAS glog::glog) diff --git a/mindspore_serving/ccsrc/common/proto_tensor.cc b/mindspore_serving/ccsrc/common/proto_tensor.cc index 12e1095..2b8fd9e 100644 --- a/mindspore_serving/ccsrc/common/proto_tensor.cc +++ b/mindspore_serving/ccsrc/common/proto_tensor.cc @@ -241,7 +241,7 @@ Status GrpcTensorHelper::CreateInstanceFromRequest(const proto::PredictRequest & MethodSignature method_signature; if (!servable_signature.GetMethodDeclare(request_spec->method_name, &method_signature)) { return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) - << "Method " << method_name << " is not registed for servable " << servable_name; + << "Method " << method_name << " is not registered for servable " << servable_name; } if (request.instances_size() == 0) { @@ -269,7 +269,7 @@ Status GrpcTensorHelper::CreateReplyFromInstances(const proto::PredictRequest &r MethodSignature method_signature; if (!servable_signature.GetMethodDeclare(method_name, &method_signature)) { return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) - << "Method " << method_name << " is not registed for servable " << servable_name; + << "Method " << method_name << " is not registered for servable " << servable_name; } *reply->mutable_servable_spec() = request.servable_spec(); @@ -305,7 +305,7 @@ Status GrpcTensorHelper::CreateReplyFromInstances(const proto::PredictRequest &r auto &output_tensor = output_intance.data[i]; auto &proto_tensor = (*proto_items)[method_signature.outputs[i]]; ProtoTensor result_tensor(&proto_tensor); - result_tensor.assgin(*output_tensor); + result_tensor.assign(*output_tensor); } } return SUCCESS; @@ -356,9 +356,15 @@ Status GrpcTensorHelper::CheckRequestTensor(const proto::Tensor &tensor) { } } else { size_t element_num = tensor_input.element_cnt(); - if (element_num == 0) { // shape dim invalid or element count > max element num - return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "Tensor check failed: input " - << " shape " << tensor_input.shape() << " invalid"; + bool zero_dim = false; + for (auto &shape_item : tensor_input.shape()) { + if (shape_item < 0 || zero_dim) { + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "Tensor check failed: input " + << " shape " << tensor_input.shape() << " invalid"; + } + if (shape_item == 0) { + zero_dim = true; + } } if (tensor_input.data_type() == kMSI_Unknown || tensor_input.itemsize() == 0) { return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "Tensor check failed: input " diff --git a/mindspore_serving/ccsrc/common/status.h b/mindspore_serving/ccsrc/common/status.h index b5d8e8f..4fd3712 100644 --- a/mindspore_serving/ccsrc/common/status.h +++ b/mindspore_serving/ccsrc/common/status.h @@ -63,9 +63,6 @@ class Status { } #define INFER_STATUS(code) mindspore::serving::Status(code) < mindspore::serving::LogStream() -#define ERROR_INFER_STATUS(status, type, msg) \ - MSI_LOG_ERROR << msg; \ - status = mindspore::serving::Status(type, msg) #define INFER_STATUS_LOG_ERROR(code) mindspore::serving::Status(code) = MSILOG_NOIF(ERROR) #define INFER_STATUS_LOG_WARNING(code) mindspore::serving::Status(code) = MSILOG_NOIF(WARNING) diff --git a/mindspore_serving/ccsrc/common/tensor_base.cc b/mindspore_serving/ccsrc/common/tensor_base.cc index 5b44c21..32994bf 100644 --- a/mindspore_serving/ccsrc/common/tensor_base.cc +++ b/mindspore_serving/ccsrc/common/tensor_base.cc @@ -68,7 +68,7 @@ size_t TensorBase::GetTypeSize(DataType type) { return 0; } -void TensorBase::assgin(const TensorBase &other) { +void TensorBase::assign(const TensorBase &other) { if (is_bytes_val_data()) { clear_bytes_data(); } diff --git a/mindspore_serving/ccsrc/common/tensor_base.h b/mindspore_serving/ccsrc/common/tensor_base.h index 29688fb..4dbf6f4 100644 --- a/mindspore_serving/ccsrc/common/tensor_base.h +++ b/mindspore_serving/ccsrc/common/tensor_base.h @@ -51,7 +51,7 @@ enum DataType { class TensorBase; using TensorBasePtr = std::shared_ptr; -class MS_API TensorBase { +class MS_API TensorBase : public std::enable_shared_from_this { public: TensorBase() = default; virtual ~TensorBase() = default; @@ -84,7 +84,7 @@ class MS_API TensorBase { // TensorBase(const TensorBase& other) = delete; // TensorBase& operator=(const TensorBase& other) = delete; - void assgin(const TensorBase &other); + void assign(const TensorBase &other); Status concat(const std::vector &inputs); bool is_bytes_val_data() const { return data_type() == kMSI_Bytes || data_type() == kMSI_String; } }; diff --git a/mindspore_serving/ccsrc/master/restful/http_handle.cc b/mindspore_serving/ccsrc/master/restful/http_handle.cc index 1a92a61..0a5d983 100644 --- a/mindspore_serving/ccsrc/master/restful/http_handle.cc +++ b/mindspore_serving/ccsrc/master/restful/http_handle.cc @@ -120,18 +120,16 @@ size_t GetB64OriginSize(size_t target_len, size_t tail_size) { size_t GetTailEqualSize(const std::string &str) { size_t length = str.size(); - if (length == 0 || length % 4 != 0) { + if (length % 4 != 0) { return UINT32_MAX; } size_t count = 0; - if (str.at(str.size() - 1) == '=') { + if (length >= 1 && str[length - 1] == '=') { count++; } - - if (str.at(str.size() - 2) == '=') { + if (length >= 2 && str[length - 2] == '=') { count++; } - return count; } diff --git a/mindspore_serving/ccsrc/master/restful/http_process.cc b/mindspore_serving/ccsrc/master/restful/http_process.cc index 053157b..bb25ac1 100644 --- a/mindspore_serving/ccsrc/master/restful/http_process.cc +++ b/mindspore_serving/ccsrc/master/restful/http_process.cc @@ -148,8 +148,9 @@ bool RestfulService::JsonMatchDataType(const json &js, DataType type) { flag = true; } } else if (js.is_string()) { - // string value can express all kinds type - flag = true; + if (type == kMSI_String) { + flag = true; + } } else if (js.is_boolean()) { if (type == kMSI_Bool) { flag = true; @@ -162,12 +163,9 @@ bool RestfulService::JsonMatchDataType(const json &js, DataType type) { std::vector RestfulService::GetObjShape(const json &js) { std::vector shape; auto it = js.find(kShape); - if (it == js.end()) { - shape.push_back(1); - } else { + if (it != js.end()) { shape = GetSpecifiedShape(it.value()); } - return shape; } @@ -330,17 +328,24 @@ Status RestfulService::CheckObj(const json &js) { if (!value.is_array()) { return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "json object, key is 'shape', value should be array type"; } - if (value.empty()) { - return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "json object, key is 'shape', array value should no be empty"; - } + bool zero_dims_before = false; for (auto it = value.begin(); it != value.end(); ++it) { - if (!(it->is_number())) { - return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "json object, key is 'shape', array value should be number"; + if (zero_dims_before) { + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) + << "json object, key is 'shape', invalid shape value " << value.dump(); + } + if (!(it->is_number_unsigned())) { + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) + << "json object, key is 'shape', array value should be unsigned integer"; } auto number = it->get(); - if (number <= 0) { + if (number < 0) { return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) - << "json object, key is 'shape', number value should be positive number"; + << "json object, key is 'shape', number value should not be negative number, shape value: " + << value.dump(); + } + if (number == 0) { + zero_dims_before = true; } } shape_count++; @@ -365,28 +370,29 @@ Status RestfulService::CheckObj(const json &js) { // 1. parse request common func Status RestfulService::ParseItem(const json &value, ProtoTensor *const pb_tensor) { Status status(SUCCESS); + std::vector scalar_shape = {}; if (value.is_number_integer()) { DataType type = kMSI_Int32; pb_tensor->set_data_type(type); - pb_tensor->set_shape({1}); + pb_tensor->set_shape(scalar_shape); pb_tensor->resize_data(pb_tensor->GetTypeSize(type)); status = GetScalarByType(type, value, 0, pb_tensor); } else if (value.is_number_float()) { DataType type = kMSI_Float32; pb_tensor->set_data_type(type); - pb_tensor->set_shape({1}); + pb_tensor->set_shape(scalar_shape); pb_tensor->resize_data(pb_tensor->GetTypeSize(type)); status = GetScalarByType(type, value, 0, pb_tensor); } else if (value.is_boolean()) { DataType type = kMSI_Bool; pb_tensor->set_data_type(type); - pb_tensor->set_shape({1}); + pb_tensor->set_shape(scalar_shape); pb_tensor->resize_data(pb_tensor->GetTypeSize(type)); status = GetScalarByType(type, value, 0, pb_tensor); } else if (value.is_string()) { DataType type = kMSI_String; pb_tensor->set_data_type(type); - pb_tensor->set_shape({1}); + pb_tensor->set_shape(scalar_shape); status = GetScalarByType(type, value, 0, pb_tensor); } else if (value.is_object()) { status = CheckObj(value); @@ -400,10 +406,6 @@ Status RestfulService::ParseItem(const json &value, ProtoTensor *const pb_tensor } std::vector shape = GetObjShape(value); - if (shape.empty()) { - return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "object json, shape is empty"; - } - bool is_tensor = false; if (type != kMSI_String && type != kMSI_Bytes) { is_tensor = true; @@ -437,7 +439,7 @@ Status RestfulService::ParseItem(const json &value, ProtoTensor *const pb_tensor is_tensor = true; } - // intances mode:only support one item + // instances mode:only support one item if (request_type_ == kInstanceType) { if (!is_tensor) { size_t elements_nums = std::accumulate(shape.begin(), shape.end(), 1LL, std::multiplies()); @@ -481,15 +483,15 @@ Status RestfulService::RecursiveGetArray(const json &json_data, size_t depth, si std::vector required_shape = request_tensor->shape(); if (depth >= required_shape.size()) { return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) - << "current depth:" << depth << " is more than shape dims:" << required_shape.size(); + << "invalid json array: current depth " << depth << " is more than shape dims " << required_shape.size(); } if (!json_data.is_array()) { - return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "json type is not array"; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "invalid json array: json type is not array"; } if (json_data.size() != static_cast(required_shape[depth])) { return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) - << "json size is:" << json_data.size() << "; the " << depth << " dim need" - << " shape size:" << required_shape[depth]; + << "invalid json array: json size is " << json_data.size() << ", the dim " << depth << " expected to be " + << required_shape[depth]; } if (depth + 1 < required_shape.size()) { size_t sub_element_cnt = @@ -618,9 +620,7 @@ Status RestfulService::GetScalarData(const json &js, size_t index, bool is_bytes auto value = js.get(); if (is_bytes) { - if (value.empty()) { - return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "string value is empty"; - } + DataType real_type = request_tensor->data_type(); auto tail_equal_size = GetTailEqualSize(value); if (tail_equal_size == UINT32_MAX) { return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "'" << value << "' is illegal b64 encode string"; @@ -631,7 +631,6 @@ Status RestfulService::GetScalarData(const json &js, size_t index, bool is_bytes if (target_size != origin_size) { return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "decode base64 failed, size is not matched."; } - DataType real_type = request_tensor->data_type(); if (real_type == kMSI_Bytes || real_type == kMSI_String) { request_tensor->add_bytes_data(buffer.data(), origin_size); } else { @@ -643,9 +642,10 @@ Status RestfulService::GetScalarData(const json &js, size_t index, bool is_bytes << "; Given info: type:" << GetStringByDataType(real_type) << "; type size:" << type_size << "; element nums:" << element_cnt; } - - auto data = reinterpret_cast(request_tensor->mutable_data()) + index; - memcpy_s(data, origin_size, buffer.data(), buffer.size()); + if (origin_size > 0) { + auto data = reinterpret_cast(request_tensor->mutable_data()) + index; + memcpy_s(data, origin_size, buffer.data(), buffer.size()); + } } } else { request_tensor->add_bytes_data(reinterpret_cast(value.data()), value.length()); @@ -680,7 +680,7 @@ Status RestfulService::RunRestful(const std::shared_ptr &restful MSI_TIME_STAMP_END(ParseRequest) if (status != SUCCESS) { std::string error_msg = status.StatusMessage(); - std::string msg = "Parser reqeust failed, " + error_msg; + std::string msg = "Parser request failed, " + error_msg; status = msg; return status; } @@ -730,7 +730,7 @@ Status RestfulService::ParseRequest(const std::shared_ptr &restf status = ParseInstancesMsg(js_msg, request); break; default: - return INFER_STATUS_LOG_ERROR(FAILED) << "restful reqeust only support instances mode"; + return INFER_STATUS_LOG_ERROR(FAILED) << "restful request only support instances mode"; } return status; @@ -741,8 +741,7 @@ Status RestfulService::ParseReqCommonMsg(const std::shared_ptr & Status status(SUCCESS); auto request_ptr = restful_request->decompose_event_request(); if (request_ptr == nullptr) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "Decompose event request is nullptr"); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "Decompose event request is nullptr"; } request->mutable_servable_spec()->set_name(request_ptr->model_name_); request->mutable_servable_spec()->set_version_number(request_ptr->version_); @@ -755,8 +754,7 @@ Status RestfulService::ParseInstancesMsg(const json &js_msg, PredictRequest *con auto type = GetReqTypeStr(request_type_); auto instances = js_msg.find(type); if (instances == js_msg.end()) { - ERROR_INFER_STATUS(status, FAILED, "instances request json should have instances key word"); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "instances request json should have instances key word"; } // get instances way:{key, value} or {value} @@ -845,9 +843,7 @@ Status RestfulService::ParseReplyDetail(const proto::Tensor &tensor, json *const Status status(SUCCESS); const ProtoTensor pb_tensor(const_cast(&tensor)); auto shape = pb_tensor.shape(); - size_t shape_size = std::accumulate(shape.begin(), shape.end(), 1LL, std::multiplies()); - - if (shape_size == 1) { + if (shape.empty()) { status = ParseScalar(pb_tensor, 0, js); if (status != SUCCESS) { return status; @@ -900,7 +896,7 @@ Status RestfulService::ParseScalar(const ProtoTensor &pb_tensor, size_t index, j status = ParseScalarData(pb_tensor, false, index, js); break; case kMSI_Float16: - ERROR_INFER_STATUS(status, FAILED, "fp16 reply is not supported"); + status = INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "fp16 reply is not supported"; break; case kMSI_Float32: status = ParseScalarData(pb_tensor, false, index, js); @@ -915,7 +911,7 @@ Status RestfulService::ParseScalar(const ProtoTensor &pb_tensor, size_t index, j status = ParseScalarData(pb_tensor, true, index, js); break; default: - ERROR_INFER_STATUS(status, FAILED, "reply data type is not supported"); + status = INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "reply data type is not supported"; break; } return status; @@ -981,10 +977,15 @@ Status RestfulService::RecursiveParseArray(const ProtoTensor &pb_tensor, size_t json *const out_json) { Status status(SUCCESS); std::vector required_shape = pb_tensor.shape(); - if (depth >= 10) { - return INFER_STATUS_LOG_ERROR(FAILED) << "result shape dims is larger than 10"; + if (depth >= required_shape.size()) { + return INFER_STATUS_LOG_ERROR(FAILED) + << "result shape dims is larger than result shape size " << required_shape.size(); } if (depth == required_shape.size() - 1) { + if (required_shape[depth] == 0) { // make empty array + out_json->push_back(json()); + out_json->clear(); + } for (int i = 0; i < required_shape[depth]; i++) { out_json->push_back(json()); json &scalar_json = out_json->back(); diff --git a/mindspore_serving/ccsrc/master/restful/restful_request.cc b/mindspore_serving/ccsrc/master/restful/restful_request.cc index 2a80c48..14b4c58 100644 --- a/mindspore_serving/ccsrc/master/restful/restful_request.cc +++ b/mindspore_serving/ccsrc/master/restful/restful_request.cc @@ -21,10 +21,12 @@ #include #include -static const char UrlKeyModel[] = "model"; -static const char UrlKeyVersion[] = "version"; -static const char UrlSplit[] = "/"; -static const char UrlKeyEnd[] = ":"; +namespace { +const char kUrlKeyModel[] = "model"; +const char kUrlKeyVersion[] = "version"; +const char kUrlSplit[] = "/"; +const char kUrlKeyEnd[] = ":"; +} // namespace namespace mindspore { namespace serving { @@ -40,8 +42,8 @@ DecomposeEvRequest::~DecomposeEvRequest() { std::string DecomposeEvRequest::UrlQuery(const std::string &url, const std::string &key) { std::string::size_type start_pos(0); - if (key == UrlKeyEnd) { - if ((start_pos = url_.find(UrlKeyEnd)) != std::string::npos) { + if (key == kUrlKeyEnd) { + if ((start_pos = url_.find(kUrlKeyEnd)) != std::string::npos) { return url_.substr(start_pos + 1, url_.size()); } } @@ -49,10 +51,11 @@ std::string DecomposeEvRequest::UrlQuery(const std::string &url, const std::stri int key_size = key.size() + 1; std::string::size_type end_pos(0); if ((start_pos = url.find(key)) != std::string::npos) { - end_pos = std::min(url.find(UrlSplit, start_pos + key_size), url.find(UrlKeyEnd, start_pos + key_size)); - if (end_pos != std::string::npos) { - return url.substr(start_pos + key_size, end_pos - start_pos - key_size); + end_pos = std::min(url.find(kUrlSplit, start_pos + key_size), url.find(kUrlKeyEnd, start_pos + key_size)); + if (end_pos == std::string::npos) { + return url.substr(start_pos + key_size); } + return url.substr(start_pos + key_size, end_pos - start_pos - key_size); } return ""; } @@ -62,21 +65,17 @@ Status DecomposeEvRequest::GetPostMessageToJson() { std::string message; size_t input_size = evbuffer_get_length(event_request_->input_buffer); if (input_size == 0) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "http message invalid"); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "http message invalid"; } else if (input_size > max_msg_size_) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "http message is bigger than " + std::to_string(max_msg_size_)); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "http message is bigger than " << max_msg_size_; } else { message.resize(input_size); auto src_data = evbuffer_pullup(event_request_->input_buffer, -1); if (src_data == nullptr) { - ERROR_INFER_STATUS(status, FAILED, "get http message failed."); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "get http message failed."; } if (memcpy_s(message.data(), input_size, src_data, input_size) != EOK) { - ERROR_INFER_STATUS(status, FAILED, "copy http message failed."); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "copy http message failed."; } } MSI_TIME_STAMP_START(ParseJson) @@ -84,9 +83,7 @@ Status DecomposeEvRequest::GetPostMessageToJson() { request_message_ = nlohmann::json::parse(message); } catch (nlohmann::json::exception &e) { std::string json_exception = e.what(); - std::string error_message = "Illegal JSON format." + json_exception; - ERROR_INFER_STATUS(status, INVALID_INPUTS, error_message); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "Illegal JSON format." + json_exception; } MSI_TIME_STAMP_END(ParseJson) @@ -100,8 +97,7 @@ Status DecomposeEvRequest::CheckRequestMethodValid() { request_method_ = "POST"; return status; default: - ERROR_INFER_STATUS(status, INVALID_INPUTS, "http message only support POST right now"); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "http message only support POST right now"; } } @@ -120,35 +116,37 @@ Status DecomposeEvRequest::Decompose() { // eg: /model/resnet/version/1:predict url_ = evhttp_request_get_uri(event_request_); if (url_.empty()) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "evhttp url is empty."); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "evhttp url is empty."; } MSI_LOG_INFO << "url_: " << url_; - model_name_ = UrlQuery(url_, UrlKeyModel); + model_name_ = UrlQuery(url_, kUrlKeyModel); if (model_name_.empty()) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "please check url, the keyword:[model] must contain."); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "please check url, the keyword:[model] must contain."; } MSI_LOG_INFO << "model_name_: " << model_name_; - if (url_.find(UrlKeyVersion) != std::string::npos) { - auto version_str = UrlQuery(url_, UrlKeyVersion); + if (url_.find(kUrlKeyVersion) != std::string::npos) { + auto version_str = UrlQuery(url_, kUrlKeyVersion); try { - version_ = std::stol(version_str); + auto version = std::stol(version_str); + if (version < 0 || version >= UINT32_MAX) { + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) + << "please check url, version number range failed, request version number " << version_str; + } + version_ = static_cast(version); } catch (const std::invalid_argument &) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "please check url, the keyword:[version] must contain."); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) + << "please check url, the keyword:[version] value invalid, request version number " << version_str; } catch (const std::out_of_range &) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "please check url, the keyword:[version] out of range."); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) + << "please check url, version number range failed, request version number " << version_str; } MSI_LOG_INFO << "version_: " << version_; } - service_method_ = UrlQuery(url_, UrlKeyEnd); + service_method_ = UrlQuery(url_, kUrlKeyEnd); if (service_method_.empty()) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "please check url, the keyword:[service method] must contain."); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "please check url, the keyword:[service method] must contain."; } MSI_LOG_INFO << "service_method_: " << service_method_; return status; @@ -164,37 +162,31 @@ RestfulRequest::~RestfulRequest() { } Status RestfulRequest::RestfulReplayBufferInit() { - Status status(SUCCESS); replay_buffer_ = evbuffer_new(); if (replay_buffer_ == nullptr) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "creat restful replay buffer fail"); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "create restful replay buffer fail"; } - return status; + return SUCCESS; } Status RestfulRequest::RestfulReplay(const std::string &replay) { - Status status(SUCCESS); if (replay_buffer_ == nullptr) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "replay_buffer_ is nullptr"); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "replay_buffer_ is nullptr"; } if (decompose_event_request_ == nullptr) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "replay_buffer_ is nullptr"); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "replay_buffer_ is nullptr"; } if (decompose_event_request_->event_request_ == nullptr) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "replay_buffer_ is nullptr"); - return status; + return INFER_STATUS_LOG_ERROR(INVALID_INPUTS) << "replay_buffer_ is nullptr"; } evbuffer_add(replay_buffer_, replay.data(), replay.size()); evhttp_send_reply(decompose_event_request_->event_request_, HTTP_OK, "Client", replay_buffer_); - return status; + return SUCCESS; } Status RestfulRequest::ErrorMessage(Status status) { Status error_status(SUCCESS); - nlohmann::json error_json = {{"error_message", status.StatusMessage()}}; + nlohmann::json error_json = {{"error_msg", status.StatusMessage()}}; std::string out_error_str = error_json.dump(); if ((error_status = RestfulReplay(out_error_str)) != SUCCESS) { return error_status; diff --git a/mindspore_serving/ccsrc/master/restful/restful_server.cc b/mindspore_serving/ccsrc/master/restful/restful_server.cc index 7a21990..fd9c91d 100644 --- a/mindspore_serving/ccsrc/master/restful/restful_server.cc +++ b/mindspore_serving/ccsrc/master/restful/restful_server.cc @@ -46,16 +46,8 @@ void RestfulServer::DispatchEvHttpRequest(evhttp_request *request) { Status status(SUCCESS); auto de_request = std::make_unique(request, max_msg_size_); - if (de_request == nullptr) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "de_request is nullptr."); - return; - } Status de_status = de_request->Decompose(); auto restful_request = std::make_shared(std::move(de_request)); - if (restful_request == nullptr) { - ERROR_INFER_STATUS(status, INVALID_INPUTS, "restful_request is nullptr."); - return; - } status = restful_request->RestfulReplayBufferInit(); if (status != SUCCESS) { if ((status = restful_request->ErrorMessage(status)) != SUCCESS) { @@ -129,7 +121,7 @@ Status RestfulServer::StartRestfulServer() { if (listener == nullptr) { status = INFER_STATUS_LOG_ERROR(SYSTEM_ERROR) - << "Serving Error: RESTful server start failed, create http listener faild, port " << restful_port_; + << "Serving Error: RESTful server start failed, create http listener failed, port " << restful_port_; free_event_base(); free_evhttp(); return status; @@ -137,7 +129,7 @@ Status RestfulServer::StartRestfulServer() { auto bound = evhttp_bind_listener(event_http_, listener); if (bound == nullptr) { status = INFER_STATUS_LOG_ERROR(SYSTEM_ERROR) - << "Serving Error: RESTful server start failed, bind http listener to server faild, port " + << "Serving Error: RESTful server start failed, bind http listener to server failed, port " << restful_port_; evconnlistener_free(listener); free_event_base(); diff --git a/mindspore_serving/ccsrc/python/serving_py.cc b/mindspore_serving/ccsrc/python/serving_py.cc index a691d26..7de691c 100644 --- a/mindspore_serving/ccsrc/python/serving_py.cc +++ b/mindspore_serving/ccsrc/python/serving_py.cc @@ -27,6 +27,9 @@ namespace mindspore::serving { PYBIND11_MODULE(_mindspore_serving, m) { + // avoid as numpy object memory copy in PyTensor::AsPythonData + py::class_(m, "Tensor_"); + py::class_>(m, "PreprocessStorage_") .def(py::init<>()) .def_static("get_instance", &PyPreprocessStorage::Instance) diff --git a/mindspore_serving/ccsrc/python/tensor_py.cc b/mindspore_serving/ccsrc/python/tensor_py.cc index 49f5c11..64a4131 100644 --- a/mindspore_serving/ccsrc/python/tensor_py.cc +++ b/mindspore_serving/ccsrc/python/tensor_py.cc @@ -196,7 +196,7 @@ py::object PyTensor::AsPythonData(TensorBasePtr tensor, bool copy) { py::buffer_info info(reinterpret_cast(const_cast(data)), sizeof(uint8_t), py::format_descriptor::format(), 1, shape, strides); if (!copy) { - py::array self; + py::object self = py::cast(tensor.get()); return py::array(py::dtype(info), info.shape, info.strides, info.ptr, self); } else { return py::array(py::dtype(info), info.shape, info.strides, info.ptr); @@ -210,7 +210,7 @@ py::object PyTensor::AsPythonData(TensorBasePtr tensor, bool copy) { static_cast(tensor_shape.size()), shape, strides); if (!copy) { - py::array self; + py::object self = py::cast(tensor.get()); return py::array(py::dtype(info), info.shape, info.strides, info.ptr, self); } else { return py::array(py::dtype(info), info.shape, info.strides, info.ptr); @@ -221,7 +221,7 @@ py::object PyTensor::AsPythonData(TensorBasePtr tensor, bool copy) { py::tuple PyTensor::AsNumpyTuple(const InstanceData &instance_data) { py::tuple numpy_inputs_tuple(instance_data.size()); for (size_t i = 0; i < instance_data.size(); i++) { // inputs - numpy_inputs_tuple[i] = PyTensor::AsPythonData(instance_data[i]); + numpy_inputs_tuple[i] = PyTensor::AsPythonData(instance_data[i], false); } return numpy_inputs_tuple; } diff --git a/mindspore_serving/client/python/client.py b/mindspore_serving/client/python/client.py index d6042a0..821fcca 100644 --- a/mindspore_serving/client/python/client.py +++ b/mindspore_serving/client/python/client.py @@ -126,17 +126,17 @@ def _check_str(arg_name, str_val): raise RuntimeError(f"Parameter '{arg_name}' should not be empty str") -def _check_int(arg_name, int_val, mininum=None, maximum=None): +def _check_int(arg_name, int_val, minimum=None, maximum=None): """Check whether the input parameters are reasonable int input""" if not isinstance(int_val, int): raise RuntimeError(f"Parameter '{arg_name}' should be int, but actually {type(int_val)}") - if mininum is not None and int_val < mininum: + if minimum is not None and int_val < minimum: if maximum is not None: - raise RuntimeError(f"Parameter '{arg_name}' should be in range [{mininum},{maximum}]") - raise RuntimeError(f"Parameter '{arg_name}' should be >= {mininum}") + raise RuntimeError(f"Parameter '{arg_name}' should be in range [{minimum},{maximum}]") + raise RuntimeError(f"Parameter '{arg_name}' should be >= {minimum}") if maximum is not None and int_val > maximum: - if mininum is not None: - raise RuntimeError(f"Parameter '{arg_name}' should be in range [{mininum},{maximum}]") + if minimum is not None: + raise RuntimeError(f"Parameter '{arg_name}' should be in range [{minimum},{maximum}]") raise RuntimeError(f"Parameter '{arg_name}' should be <= {maximum}") @@ -259,29 +259,6 @@ class Client: print(status_code.value) return ClientGrpcAsyncError({"error": "Grpc Error, " + str(status_code.value)}) - def close(self): - """ - Used to release gRPC connection avoiding influence on the connection to the next restarted Serving server. - - Examples: - >>> from mindspore_serving.client import Client - >>> import numpy as np - >>> instance = {"x1": np.ones((2, 2), np.int32), "x2": np.ones((2, 2), np.int32)} - >>> client = Client("localhost", 5500, "add", "add_cast") - >>> result = client.infer(instance) - >>> client.close() - >>> print(result) - """ - if self.stub: - del self.stub - self.stub = None - - def __enter__(self): - return self - - def __exit__(self, exc_type, exc_val, exc_tb): - self.close() - def _create_request(self, instances): """Used to create request spec.""" if not isinstance(instances, (tuple, list)): @@ -342,7 +319,7 @@ class Client: class ClientGrpcAsyncResult: """ - When Client.infer_async invoke sucessfully, a ClientGrpcAsyncResult object is returned. + When Client.infer_async invoke successfully, a ClientGrpcAsyncResult object is returned. Examples: >>> from mindspore_serving.client import Client diff --git a/mindspore_serving/log.py b/mindspore_serving/log.py index 6b36b6c..33bfe83 100644 --- a/mindspore_serving/log.py +++ b/mindspore_serving/log.py @@ -91,7 +91,7 @@ class _MultiCompatibleRotatingFileHandler(RotatingFileHandler): self.stream.close() self.stream = None - # Attain an exclusive lock with bloking mode by `fcntl` module. + # Attain an exclusive lock with blocking mode by `fcntl` module. with open(self.baseFilename, 'a') as file_pointer: if platform.system() != "Windows": fcntl.lockf(file_pointer.fileno(), fcntl.LOCK_EX) @@ -396,7 +396,7 @@ def _clear_handler(logger): logger.removeHandler(handler) -def _find_caller(stack_info=False): +def _find_caller(stack_info=False, _=1): """ Find the stack frame of the caller. @@ -432,13 +432,13 @@ def _find_caller(stack_info=False): def _get_stack_info(frame): """ - Get the stack informations. + Get the stack information. Args: - frame(frame): the frame requiring informations. + frame(frame): the frame requiring information. Returns: - str, the string of the stack informations. + str, the string of the stack information. """ sinfo = None stack_prefix = 'Stack (most recent call last):\n' diff --git a/mindspore_serving/master/__init__.py b/mindspore_serving/master/__init__.py index 6e2c4af..618f4c3 100644 --- a/mindspore_serving/master/__init__.py +++ b/mindspore_serving/master/__init__.py @@ -14,7 +14,6 @@ # ============================================================================ """MindSpore Serving Master""" -from mindspore_serving.worker import _check_version from ._master import start_grpc_server, start_restful_server, start_master_server, stop __all__ = [] diff --git a/requirements_test.txt b/requirements_test.txt index 7c2172c..876298a 100644 --- a/requirements_test.txt +++ b/requirements_test.txt @@ -1,2 +1,4 @@ numpy>=1.17.0 protobuf>=3.8.0 +grpcio>=1.27.3 +requests>=2.22.0 \ No newline at end of file diff --git a/tests/ut/CMakeLists.txt b/tests/ut/CMakeLists.txt index cc944f9..a4d4611 100644 --- a/tests/ut/CMakeLists.txt +++ b/tests/ut/CMakeLists.txt @@ -3,11 +3,7 @@ add_library(protobuf::libprotobuf ALIAS protobuf::protobuf) add_executable(protobuf::libprotoc ALIAS protobuf::protoc) set(_PROTOBUF_LIBPROTOBUF protobuf::libprotobuf) -if(CMAKE_CROSSCOMPILING) - find_program(_PROTOBUF_PROTOC protoc) -else() - set(_PROTOBUF_PROTOC $) -endif() +set(_PROTOBUF_PROTOC $) # Find gRPC installation # Looks for gRPCConfig.cmake file installed by gRPC's cmake installation. @@ -24,13 +20,8 @@ message(STATUS "Using gRPC ${gRPC_VERSION}") set(_GRPC_GRPCPP gRPC::grpc++) set(_REFLECTION gRPC::grpc++_reflection) -if(CMAKE_CROSSCOMPILING) - find_program(_GRPC_CPP_PLUGIN_EXECUTABLE grpc_cpp_plugin) - find_program(_GRPC_PYTHON_PLUGIN_EXECUTABLE grpc_python_plugin) -else() - set(_GRPC_CPP_PLUGIN_EXECUTABLE $) - set(_GRPC_PYTHON_PLUGIN_EXECUTABLE $) -endif() +set(_GRPC_CPP_PLUGIN_EXECUTABLE $) +set(_GRPC_PYTHON_PLUGIN_EXECUTABLE $) # Proto file # Generated sources @@ -96,7 +87,7 @@ add_library(serving_ut_common STATIC ${UT_SERVING_COMMON}) target_link_libraries(serving_ut_common PRIVATE ${_REFLECTION} ${_GRPC_GRPCPP} ${_PROTOBUF_LIBPROTOBUF}) target_link_libraries(serving_ut_common PRIVATE mindspore_serving::event mindspore_serving::event_pthreads) -target_link_libraries(serving_ut_common PRIVATE pthread mindspore::glog) +target_link_libraries(serving_ut_common PRIVATE pthread mindspore_serving::glog) target_link_libraries(serving_ut_common PRIVATE ${SECUREC_LIBRARY}) # copy libevent lib diff --git a/tests/ut/cpp/runtest.sh b/tests/ut/cpp/runtest.sh index 729c81d..687858b 100755 --- a/tests/ut/cpp/runtest.sh +++ b/tests/ut/cpp/runtest.sh @@ -14,9 +14,12 @@ # limitations under the License. # ============================================================================ set -e -BASEPATH=$(cd "$(dirname "$0")"; pwd) +BASEPATH=$( + cd "$(dirname "$0")" + pwd +) PROJECT_PATH=${BASEPATH}/../../.. -if [ $BUILD_PATH ];then +if [ $BUILD_PATH ]; then echo "BUILD_PATH = $BUILD_PATH" else BUILD_PATH=${PROJECT_PATH}/build diff --git a/tests/ut/cpp/tests/parse_restful.cc b/tests/ut/cpp/tests/parse_restful.cc index 1f8588a..e6f59e0 100644 --- a/tests/ut/cpp/tests/parse_restful.cc +++ b/tests/ut/cpp/tests/parse_restful.cc @@ -859,15 +859,26 @@ TEST_F(TestParseReply, test_reply_SUCCESS) { std::cout << "===start====" << std::endl; for (auto &it : element.items()) { if (it.key() == "key_int") { - ASSERT_EQ(it.value(), 1); + ASSERT_TRUE(it.value().is_array()); + ASSERT_EQ(it.value().size(), 1); + auto array_items = it.value().items(); + auto int_val = *(array_items.begin()); + ASSERT_TRUE(int_val.value().is_number_integer()); + ASSERT_EQ(int_val.value(), 1); count++; } else if (it.key() == "key_bool") { + ASSERT_TRUE(it.value().is_boolean()); ASSERT_EQ(it.value(), false); count++; } else if (it.key() == "key_float") { - ASSERT_FLOAT_EQ(it.value(), 2.3); + ASSERT_TRUE(it.value().is_array()); + ASSERT_EQ(it.value().size(), 1); + auto array_items = it.value().items(); + auto float_val = *(array_items.begin()); + ASSERT_FLOAT_EQ(float_val.value(), 2.3); count++; } else if (it.key() == "key_str") { + ASSERT_TRUE(it.value().is_string()); ASSERT_EQ(it.value(), "ut_test"); count++; } else if (it.key() == "key_bytes") { diff --git a/tests/ut/python/runtest.sh b/tests/ut/python/runtest.sh index 39b56c5..7e3a0f1 100755 --- a/tests/ut/python/runtest.sh +++ b/tests/ut/python/runtest.sh @@ -44,10 +44,12 @@ echo "LD_LIBRARY_PATH=LD_LIBRARY_PATH" unset http_proxy unset https_proxy +cd - +cd ${PROJECT_PATH}/tests/ut/python/tests/ if [ $# -gt 0 ]; then - pytest -v ${PROJECT_PATH}/tests/ut/python/tests/ -k $1 + pytest -v . -k "$1" else - pytest -v ${PROJECT_PATH}/tests/ut/python/tests + pytest -v . fi RET=$? diff --git a/tests/ut/python/tests/common.py b/tests/ut/python/tests/common.py index e5e73b8..670e6bd 100644 --- a/tests/ut/python/tests/common.py +++ b/tests/ut/python/tests/common.py @@ -79,5 +79,43 @@ def serving_test(func): finally: master.stop() worker.stop() + servable_dir = os.path.join(os.getcwd(), "serving_python_ut_servables") + rmtree(servable_dir, True) return wrap_test + + +def release_client(client): + del client.stub + client.stub = None + + +# test servable_config.py with client +servable_config_import = r""" +import numpy as np +from mindspore_serving.worker import register +""" + +servable_config_declare_servable = r""" +register.declare_servable(servable_file="tensor_add.mindir", model_format="MindIR", with_batch_dim=False) +""" + +servable_config_preprocess_cast = r""" +def add_trans_datatype(x1, x2): + return x1.astype(np.float32), x2.astype(np.float32) +""" + +servable_config_method_add_common = r""" +@register.register_method(output_names=["y"]) +def add_common(x1, x2): # only support float32 inputs + y = register.call_servable(x1, x2) + return y +""" + +servable_config_method_add_cast = r""" +@register.register_method(output_names=["y"]) +def add_cast(x1, x2): + x1, x2 = register.call_preprocess(add_trans_datatype, x1, x2) # cast input to float32 + y = register.call_servable(x1, x2) + return y +""" diff --git a/tests/ut/python/tests/common_restful.py b/tests/ut/python/tests/common_restful.py new file mode 100644 index 0000000..9c16087 --- /dev/null +++ b/tests/ut/python/tests/common_restful.py @@ -0,0 +1,75 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ +"""test Serving, Common""" + +import json +import requests +import numpy as np + + +def compare_float_value(expect, result): + expect = np.array(expect) + result = np.array(result) + assert (np.abs(expect - result) < 0.001).all() + + +def create_multi_instances_fp32(instance_count): + instances = [] + # instance 1 + y_data_list = [] + for i in range(instance_count): + x1 = np.asarray([[1.1, 2.2], [3.3, 4.4]]).astype(np.float32) * (i + 1) + x2 = np.asarray([[5.5, 6.6], [7.7, 8.8]]).astype(np.float32) * (i + 1) + y_data_list.append(x1 + x2) + instances.append({"x1": x1.tolist(), "x2": x2.tolist()}) + return instances, y_data_list + + +def create_multi_instances_int32_input_fp32_output(instance_count): + instances = [] + # instance 1 + y_data_list = [] + for i in range(instance_count): + x1 = np.asarray([[1.1, 2.2], [3.3, 4.4]]).astype(np.int32) * (i + 1) + x2 = np.asarray([[5.5, 6.6], [7.7, 8.8]]).astype(np.int32) * (i + 1) + y_data_list.append((x1 + x2).astype(np.float32)) + instances.append({"x1": x1.tolist(), "x2": x2.tolist()}) + return instances, y_data_list + + +def check_result(result, y_data_list, output_name="y"): + result = result["instances"] + assert len(result) == len(y_data_list) + for result_item, expected_item in zip(result, y_data_list): + result_item = np.array(result_item[output_name]) + print("result", result_item) + print("expect:", expected_item) + assert result_item.shape == expected_item.shape + assert (np.abs(result_item - expected_item) < 0.001).all() + + +def post_restful(ip, restful_port, servable_name, method_name, json_instances, version_number=None): + instances_map = {"instances": json_instances} + post_payload = json.dumps(instances_map) + print("request:", post_payload) + if version_number is not None: + request_url = f"http://{ip}:{restful_port}/model/{servable_name}/version/{version_number}:{method_name}" + result = requests.post(request_url, data=post_payload) + else: + request_url = f"http://{ip}:{restful_port}/model/{servable_name}:{method_name}" + result = requests.post(request_url, data=post_payload) + print("result", result.text) + result = json.loads(result.text) + return result diff --git a/tests/ut/python/tests/test_mater_worker_client.py b/tests/ut/python/tests/test_mater_worker_client.py index 3449b9b..07238db 100644 --- a/tests/ut/python/tests/test_mater_worker_client.py +++ b/tests/ut/python/tests/test_mater_worker_client.py @@ -18,7 +18,9 @@ import numpy as np from mindspore_serving import master from mindspore_serving import worker from mindspore_serving.client import Client -from common import ServingTestBase, serving_test +from common import ServingTestBase, serving_test, release_client +from common import servable_config_import, servable_config_declare_servable, servable_config_preprocess_cast +from common import servable_config_method_add_common, servable_config_method_add_cast def create_multi_instances_fp32(instance_count): @@ -50,7 +52,7 @@ def test_master_worker_client_success(): instance_count = 3 instances, y_data_list = create_multi_instances_fp32(instance_count) result = client.infer(instances) - client.close() # avoid affecting the next use case + release_client(client) print(result) check_result(result, y_data_list) @@ -63,11 +65,12 @@ def test_master_worker_client_multi_times_success(): worker.start_servable_in_master(base.servable_dir, base.servable_name, 0) master.start_grpc_server("0.0.0.0", 5500) # Client, use with avoid affecting the next use case - with Client("localhost", 5500, base.servable_name, "add_common") as client: - for instance_count in range(1, 5): - instances, y_data_list = create_multi_instances_fp32(instance_count) - result = client.infer(instances) - check_result(result, y_data_list) + client = Client("localhost", 5500, base.servable_name, "add_common") + for instance_count in range(1, 5): + instances, y_data_list = create_multi_instances_fp32(instance_count) + result = client.infer(instances) + check_result(result, y_data_list) + release_client(client) @serving_test @@ -78,10 +81,11 @@ def test_master_worker_client_alone_success(): master.start_grpc_server("0.0.0.0", 5500) worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) # Client - with Client("localhost", 5500, base.servable_name, "add_common") as client: - instance_count = 3 - instances, y_data_list = create_multi_instances_fp32(instance_count) - result = client.infer(instances) + client = Client("localhost", 5500, base.servable_name, "add_common") + instance_count = 3 + instances, y_data_list = create_multi_instances_fp32(instance_count) + result = client.infer(instances) + release_client(client) check_result(result, y_data_list) @@ -93,11 +97,12 @@ def test_master_worker_client_alone_multi_times_success(): master.start_grpc_server("0.0.0.0", 5500) worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) # Client, use with avoid affecting the next use case - with Client("localhost", 5500, base.servable_name, "add_common") as client: - for instance_count in range(1, 5): - instances, y_data_list = create_multi_instances_fp32(instance_count) - result = client.infer(instances) - check_result(result, y_data_list) + client = Client("localhost", 5500, base.servable_name, "add_common") + for instance_count in range(1, 5): + instances, y_data_list = create_multi_instances_fp32(instance_count) + result = client.infer(instances) + check_result(result, y_data_list) + release_client(client) @serving_test @@ -112,7 +117,7 @@ def test_master_worker_client_async_success(): instances, y_data_list = create_multi_instances_fp32(instance_count) result_future = client.infer_async(instances) result = result_future.result() - client.close() # avoid affecting the next use case + release_client(client) # avoid affecting the next use case print(result) check_result(result, y_data_list) @@ -125,12 +130,13 @@ def test_master_worker_client_async_multi_times_success(): worker.start_servable_in_master(base.servable_dir, base.servable_name, 0) master.start_grpc_server("0.0.0.0", 5500) # Client, use with avoid affecting the next use case - with Client("localhost", 5500, base.servable_name, "add_common") as client: - for instance_count in range(1, 5): - instances, y_data_list = create_multi_instances_fp32(instance_count) - result_future = client.infer_async(instances) - result = result_future.result() - check_result(result, y_data_list) + client = Client("localhost", 5500, base.servable_name, "add_common") + for instance_count in range(1, 5): + instances, y_data_list = create_multi_instances_fp32(instance_count) + result_future = client.infer_async(instances) + result = result_future.result() + check_result(result, y_data_list) + release_client(client) @serving_test @@ -231,7 +237,7 @@ def test_master_worker_client_alone_repeat_grpc_and_restful_port_failed(): master.start_restful_server("0.0.0.0", 7600) assert False except RuntimeError as e: - assert "Serving Error: RESTful server start failed, create http listener faild, port" in str(e) + assert "Serving Error: RESTful server start failed, create http listener failed, port" in str(e) @serving_test @@ -246,37 +252,6 @@ def test_master_worker_client_alone_repeat_grpc_and_restful_port2_failed(): assert "Serving Error: Serving gRPC server start failed, create server failed, address" in str(e) -# test servable_config.py with client -servable_config_import = r""" -import numpy as np -from mindspore_serving.worker import register -""" - -servable_config_declare_servable = r""" -register.declare_servable(servable_file="tensor_add.mindir", model_format="MindIR", with_batch_dim=False) -""" - -servable_config_preprocess_cast = r""" -def add_trans_datatype(x1, x2): - return x1.astype(np.float32), x2.astype(np.float32) -""" - -servable_config_method_add_common = r""" -@register.register_method(output_names=["y"]) -def add_common(x1, x2): # only support float32 inputs - y = register.call_servable(x1, x2) - return y -""" - -servable_config_method_add_cast = r""" -@register.register_method(output_names=["y"]) -def add_cast(x1, x2): - x1, x2 = register.call_preprocess(add_trans_datatype, x1, x2) # cast input to float32 - y = register.call_servable(x1, x2) - return y -""" - - @serving_test def test_master_worker_client_servable_content_success(): base = ServingTestBase() @@ -292,8 +267,9 @@ def test_master_worker_client_servable_content_success(): # Client instance_count = 3 instances, y_data_list = create_multi_instances_fp32(instance_count) - with Client("localhost", 5500, base.servable_name, "add_common") as client: - result = client.infer(instances) + client = Client("localhost", 5500, base.servable_name, "add_common") + result = client.infer(instances) + release_client(client) print(result) check_result(result, y_data_list) @@ -320,9 +296,9 @@ def add_cast(x1, x2): # Client instance_count = 3 instances, _ = create_multi_instances_fp32(instance_count) - # Client, use with avoid affecting the next use case - with Client("localhost", 5500, base.servable_name, "add_cast") as client: - result = client.infer(instances) + client = Client("localhost", 5500, base.servable_name, "add_cast") + result = client.infer(instances) + release_client(client) print(result) assert "Preprocess Failed" in str(result[0]["error"]) @@ -349,9 +325,9 @@ def add_cast(x1, x2): # Client instance_count = 3 instances, _ = create_multi_instances_fp32(instance_count) - # Client, use with avoid affecting the next use case - with Client("localhost", 5500, base.servable_name, "add_cast") as client: - result = client.infer(instances) + client = Client("localhost", 5500, base.servable_name, "add_cast") + result = client.infer(instances) + release_client(client) print(result) assert "Postprocess Failed" in str(result[0]["error"]) @@ -387,9 +363,9 @@ def add_cast(x1, x2, label): for i, instance in enumerate(instances): instance["label"] = list_str[i] - # Client, use with avoid affecting the next use case - with Client("localhost", 5500, base.servable_name, "add_cast") as client: - result = client.infer(instances) + client = Client("localhost", 5500, base.servable_name, "add_cast") + result = client.infer(instances) + release_client(client) assert result[0]["text"] == "ABC123" assert result[1]["text"] == "DEF456" assert result[2]["text"] == "HIJ789" @@ -426,9 +402,9 @@ def add_cast(x1, x2, label): for i, instance in enumerate(instances): instance["label"] = str.encode(list_str[i]) - # Client, use with avoid affecting the next use case - with Client("localhost", 5500, base.servable_name, "add_cast") as client: - result = client.infer(instances) + client = Client("localhost", 5500, base.servable_name, "add_cast") + result = client.infer(instances) + release_client(client) assert bytes.decode(result[0]["text"]) == "ABC123" assert bytes.decode(result[1]["text"]) == "DEF456" assert bytes.decode(result[2]["text"]) == "HIJ789" @@ -458,9 +434,9 @@ def add_cast(x1, x2, bool_val): for i, instance in enumerate(instances): instance["bool_val"] = (i % 2 == 0) - # Client, use with avoid affecting the next use case - with Client("localhost", 5500, base.servable_name, "add_cast") as client: - result = client.infer(instances) + client = Client("localhost", 5500, base.servable_name, "add_cast") + result = client.infer(instances) + release_client(client) assert not result[0]["value"] assert result[1]["value"] assert not result[2]["value"] @@ -490,9 +466,9 @@ def add_cast(x1, x2, int_val): for i, instance in enumerate(instances): instance["int_val"] = i * 2 - # Client, use with avoid affecting the next use case - with Client("localhost", 5500, base.servable_name, "add_cast") as client: - result = client.infer(instances) + client = Client("localhost", 5500, base.servable_name, "add_cast") + result = client.infer(instances) + release_client(client) assert result[0]["value"] == 1 assert result[1]["value"] == 3 assert result[2]["value"] == 5 @@ -522,9 +498,53 @@ def add_cast(x1, x2, float_val): for i, instance in enumerate(instances): instance["float_val"] = i * 2.2 - # Client, use with avoid affecting the next use case - with Client("localhost", 5500, base.servable_name, "add_cast") as client: - result = client.infer(instances) + client = Client("localhost", 5500, base.servable_name, "add_cast") + result = client.infer(instances) + release_client(client) assert result[0]["value"] == 1 assert result[1]["value"] == (2.2 + 1) assert result[2]["value"] == (4.4 + 1) + + +@serving_test +def test_master_worker_client_preprocess_update_numpy_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def preprocess(x3): + x3[0] = 123 + return x3 + +def postprocess(x3, x4): + return x3 + 1, x4 + 2 + +@register.register_method(output_names=["x3", "x4"]) +def add_cast(x1, x2, x3): + x4 = register.call_preprocess(preprocess, x3) # [123, 1, 1], expect x3 is x4, same as python function call + y = register.call_servable(x1, x2) + x3, x4 = register.call_postprocess(postprocess, x3, x4) + return x3, x4 +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_grpc_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["x3"] = np.ones([3]) + + # Client, use with avoid affecting the next use case + client = Client("localhost", 5500, base.servable_name, "add_cast") + result = client.infer(instances) + release_client(client) + x3 = np.array([123, 1, 1]) + 1 + x4 = np.array([123, 1, 1]) + 2 + + assert (result[0]["x3"] == x3).all() + assert (result[1]["x3"] == x3).all() + assert (result[2]["x3"] == x3).all() + assert (result[0]["x4"] == x4).all() + assert (result[1]["x4"] == x4).all() + assert (result[2]["x4"] == x4).all() diff --git a/tests/ut/python/tests/test_restful_base64_data.py b/tests/ut/python/tests/test_restful_base64_data.py new file mode 100644 index 0000000..76f6527 --- /dev/null +++ b/tests/ut/python/tests/test_restful_base64_data.py @@ -0,0 +1,949 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ +"""test Serving RESTful, with master, worker and client""" + +import base64 +import numpy as np + +from common import ServingTestBase, serving_test +from common import servable_config_import, servable_config_declare_servable +from common_restful import create_multi_instances_fp32, check_result, post_restful +from mindspore_serving import master +from mindspore_serving import worker + + +def start_str_restful_server(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +index = 0 +list_str = ["123", "456", "789"] +def postprocess(y, label): + global index + text = list_str[index] + index = (index + 1) if index + 1 < len(list_str) else 0 + return y.astype(np.int32), label + text + +@register.register_method(output_names=["y", "text"]) +def add_common(x1, x2, label): + y = register.call_servable(x1, x2) + y, text = register.call_postprocess(postprocess, y, label) + return y, text + +def empty_postprocess(y, label): + global index + if len(label) == 0: + text = list_str[index] + else: + text = "" + index = (index + 1) if index + 1 < len(list_str) else 0 + return y.astype(np.int32), text + +@register.register_method(output_names=["y", "text"]) +def add_empty(x1, x2, label): + y = register.call_servable(x1, x2) + y, text = register.call_postprocess(empty_postprocess, y, label) + return y, text +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + return base + + +def start_bytes_restful_server(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +index = 0 +list_str = ["123", "456", "789"] +def postprocess(y, label): + global index + label = bytes.decode(label.tobytes()) # bytes decode to str + text = list_str[index] + index = (index + 1) if index + 1 < len(list_str) else 0 + return y.astype(np.int32), str.encode(label + text) # str encode to bytes + +@register.register_method(output_names=["y", "text"]) +def add_common(x1, x2, label): + y = register.call_servable(x1, x2) + y, text = register.call_postprocess(postprocess, y, label) + return y, text + +def empty_postprocess(y, label): + global index + label = bytes.decode(label.tobytes()) # bytes decode to str + if len(label) == 0: + text = list_str[index] + else: + text = "" + index = (index + 1) if index + 1 < len(list_str) else 0 + return y.astype(np.int32), str.encode(text) # str encode to bytes + +@register.register_method(output_names=["y", "text"]) +def add_empty(x1, x2, label): + y = register.call_servable(x1, x2) + y, text = register.call_postprocess(empty_postprocess, y, label) + return y, text +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + return base + + +def start_bool_int_float_restful_server(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def bool_postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_bool(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(bool_postprocess, y, bool_val) + return y, value + +def int_postprocess(y, int_val): + return y.astype(np.int32), int_val + 1 + +@register.register_method(output_names=["y", "value"]) +def add_int(x1, x2, int_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(int_postprocess, y, int_val) + return y, value + +def float_postprocess(y, float_val): + value = float_val + 1 + if value.dtype == np.float16: + value = value.astype(np.float32) + return y, value + +@register.register_method(output_names=["y", "value"]) +def add_float(x1, x2, float_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(float_postprocess, y, float_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + return base + + +def common_test_restful_base64_str_scalar_input_output_success(shape): + base = start_str_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + if shape is None: + instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str"} + else: + instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str", + 'shape': shape} + + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) + result = result["instances"] + assert result[0]["text"] == "ABC123" + assert result[1]["text"] == "DEF456" + assert result[2]["text"] == "HIJ789" + + +@serving_test +def test_restful_base64_str_scalar_input_output_success(): + common_test_restful_base64_str_scalar_input_output_success(shape=None) + + +@serving_test +def test_restful_base64_str_scalar_shape1_input_output_success(): + common_test_restful_base64_str_scalar_input_output_success(shape=[1]) + + +@serving_test +def test_restful_base64_str_scalar_shape_empty_input_output_success(): + common_test_restful_base64_str_scalar_input_output_success(shape=[]) + + +@serving_test +def test_restful_base64_empty_str_input_output_success(): + base = start_str_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str"} + + result = post_restful("localhost", 5500, base.servable_name, "add_empty", instances) + result = result["instances"] + assert result[0]["text"] == "" + assert result[1]["text"] == "456" + assert result[2]["text"] == "" + + +@serving_test +def test_restful_base64_str_scalar_invalid_shape0_input_failed(): + base = start_str_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str", "shape": [0]} + + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) + assert "only support scalar when data type is string or bytes, please check 'type' or 'shape'" \ + in str(result["error_msg"]) + + +@serving_test +def test_restful_base64_str_scalar_invalid_shape_input_failed(): + base = start_str_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str", 'shape': [2]} + + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) + assert "json object, only support scalar when data type is string or bytes, please check 'type' or 'shape'" \ + in str(result["error_msg"]) + + +@serving_test +def test_restful_base64_str_1d_array_failed(): + base = start_str_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = [{"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str"}, + {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "type": "str"}] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, string or bytes type only support one item" in str(result["error_msg"]) + + +def common_test_restful_bytes_input_output_success(shape): + base = start_bytes_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + if shape is not None: + instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), "shape": shape} + else: + instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode()} + + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) + result = result["instances"] + b64_decode_to_str = lambda a: bytes.decode(base64.b64decode(a["b64"])) + assert b64_decode_to_str(result[0]["text"]) == "ABC123" + assert b64_decode_to_str(result[1]["text"]) == "DEF456" + assert b64_decode_to_str(result[2]["text"]) == "HIJ789" + + +@serving_test +def test_restful_bytes_input_output_success(): + common_test_restful_bytes_input_output_success(None) + + +@serving_test +def test_restful_bytes_empty_shape_success(): + common_test_restful_bytes_input_output_success([]) + + +@serving_test +def test_restful_bytes_shape1_success(): + common_test_restful_bytes_input_output_success([1]) + + +@serving_test +def test_restful_empty_bytes_input_output_success(): + base = start_bytes_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode()} + + result = post_restful("localhost", 5500, base.servable_name, "add_empty", instances) + result = result["instances"] + b64_decode_to_str = lambda a: bytes.decode(base64.b64decode(a["b64"])) + assert b64_decode_to_str(result[0]["text"]) == "" + assert b64_decode_to_str(result[1]["text"]) == "456" + assert b64_decode_to_str(result[2]["text"]) == "" + + +@serving_test +def test_restful_bytes_1d_array_failed(): + base = start_bytes_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = [{"b64": base64.b64encode(str.encode(list_str[i])).decode()}, + {"b64": base64.b64encode(str.encode(list_str[i])).decode()}] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, string or bytes type only support one item" in str(result["error_msg"]) + + +@serving_test +def test_restful_bytes_invalid_shape_input_failed(): + base = start_bytes_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = {"b64": base64.b64encode(str.encode(list_str[i])).decode(), 'shape': [0]} + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "only support scalar when data type is string or bytes, please check 'type' or 'shape'" \ + in result["error_msg"] + + +@serving_test +def test_restful_base64_bool_scalar_input_output_success(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = np.int8(i % 2 == 0) + instance["bool_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "bool"} + + result = post_restful("localhost", 5500, base.servable_name, "add_bool", instances) + result = result["instances"] + assert not result[0]["value"] + assert result[1]["value"] + assert not result[2]["value"] + + +@serving_test +def test_restful_base64_bool_1d_array_input_output_success(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = [(i % 2 == 0)] * (i + 1) + val = np.array(val) + instance["bool_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "bool", "shape": [i + 1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_bool", instances) + result = result["instances"] + assert result[0]["value"] == [False] + assert result[1]["value"] == [True, True] + assert result[2]["value"] == [False, False, False] + + +@serving_test +def test_restful_base64_bool_2d_array_input_output_success(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = (i % 2 == 0) + val = [[val] * (i + 1)] * (i + 1) + val = np.array(val) + instance["bool_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "bool", + "shape": [i + 1, i + 1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_bool", instances) + result = result["instances"] + assert result[0]["value"] == [[False]] + assert result[1]["value"] == [[True, True], [True, True]] + assert result[2]["value"] == [[False, False, False], [False, False, False], [False, False, False]] + + +@serving_test +def test_restful_base64_int_scalar_input_output_success(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = np.int32(i * 2) + instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "int32"} + + result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) + result = result["instances"] + assert result[0]["value"] == 1 + assert result[1]["value"] == 3 + assert result[2]["value"] == 5 + + +@serving_test +def test_restful_base64_int_1d_empty_input_output_success(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + if i % 2 == 0: + val = [] + else: + val = [i * 2] * (i + 1) + val = np.array(val).astype(np.int32) + instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "int32", "shape": val.shape} + + result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) + result = result["instances"] + assert result[0]["value"] == [] + assert result[1]["value"] == [3, 3] + assert result[2]["value"] == [] + + +@serving_test +def test_restful_base64_int_2d_empty_input_output_success(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + if i % 2 == 0: + val = [[]] + else: + val = [i * 2] * (i + 1) + val = np.array(val).astype(np.int32) + instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "int32", "shape": val.shape} + + result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) + result = result["instances"] + assert result[0]["value"] == [[]] + assert result[1]["value"] == [3, 3] + assert result[2]["value"] == [[]] + + +@serving_test +def test_restful_base64_int_2d_empty_invalid_shape_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for _, instance in enumerate(instances): + val = [[]] + val = np.array(val).astype(np.int32) + instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "int32", "shape": [1, 2, 0, 1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) + assert "json object, key is 'shape', invalid shape value" in result["error_msg"] + + +@serving_test +def test_restful_base64_int_1d_array_input_output_success(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = i * 2 + val = [val] * (i + 1) + val = np.array(val).astype(np.int32) + instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "int32", "shape": val.shape} + + result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) + result = result["instances"] + assert result[0]["value"] == [1] + assert result[1]["value"] == [3, 3] + assert result[2]["value"] == [5, 5, 5] + + +def common_test_restful_base64_int_type_2d_array_input_output_success(dtype): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + dtype_str_map = {np.int8: "int8", np.int16: "int16", np.int32: "int32", np.int64: "int64"} + assert dtype in dtype_str_map + for i, instance in enumerate(instances): + val = (i + 1) * 2 * (-1 if i % 2 == 0 else 1) # -2, 4, -6 + val = [[val] * (i + 1)] * (i + 1) + val = np.array(val).astype(dtype) + instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], + "shape": val.shape} + + result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) + result = result["instances"] + assert result[0]["value"] == [[-1]] + assert result[1]["value"] == [[5, 5], [5, 5]] + assert result[2]["value"] == [[-5, -5, -5], [-5, -5, -5], [-5, -5, -5]] + + +@serving_test +def test_restful_base64_int8_2d_array_input_output_success(): + common_test_restful_base64_int_type_2d_array_input_output_success(np.int8) + + +@serving_test +def test_restful_base64_int16_2d_array_input_output_success(): + common_test_restful_base64_int_type_2d_array_input_output_success(np.int16) + + +@serving_test +def test_restful_base64_int32_2d_array_input_output_success(): + common_test_restful_base64_int_type_2d_array_input_output_success(np.int32) + + +@serving_test +def test_restful_base64_int64_2d_array_input_output_success(): + common_test_restful_base64_int_type_2d_array_input_output_success(np.int64) + + +def common_test_restful_base64_uint_type_2d_array_input_output_success(dtype): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + dtype_str_map = {np.uint8: "uint8", np.uint16: "uint16", np.uint32: "uint32", np.uint64: "uint64"} + assert dtype in dtype_str_map + for i, instance in enumerate(instances): + val = i * 2 + val = [[val] * (i + 1)] * (i + 1) + val = np.array(val).astype(dtype) + instance["int_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], + "shape": val.shape} + + result = post_restful("localhost", 5500, base.servable_name, "add_int", instances) + result = result["instances"] + assert result[0]["value"] == [[1]] + assert result[1]["value"] == [[3, 3], [3, 3]] + assert result[2]["value"] == [[5, 5, 5], [5, 5, 5], [5, 5, 5]] + + +@serving_test +def test_restful_base64_uint8_2d_array_input_output_success(): + common_test_restful_base64_uint_type_2d_array_input_output_success(np.uint8) + + +@serving_test +def test_restful_base64_uint16_2d_array_input_output_success(): + common_test_restful_base64_uint_type_2d_array_input_output_success(np.uint16) + + +@serving_test +def test_restful_base64_uint32_2d_array_input_output_success(): + common_test_restful_base64_uint_type_2d_array_input_output_success(np.uint32) + + +@serving_test +def test_restful_base64_uint64_2d_array_input_output_success(): + common_test_restful_base64_uint_type_2d_array_input_output_success(np.uint64) + + +@serving_test +def test_restful_base64_float_scalar_input_output_success(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = np.float32(i * 2.2) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp32"} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + result = result["instances"] + assert result[0]["value"] == 1.0 + assert abs(result[1]["value"] - (2.2 + 1)) < 0.001 + assert abs(result[2]["value"] - (4.4 + 1)) < 0.001 + + +@serving_test +def test_restful_base64_float_1d_array_input_output_success(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + y_data_list = [] + for i, instance in enumerate(instances): + val = [i * 2.2 * (-1 if i % 2 == 0 else 1)] * (i + 1) # [0], [2.2, 2.2], [-4.4, -4.4, -4.4] + val = np.array(val).astype(np.float32) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp32", 'shape': [i + 1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + check_result(result, y_data_list, "value") + + +def common_test_restful_base64_float_type_2d_array_input_output_success(dtype): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} + assert dtype in dtype_str_map + + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 1)] * (i + 1) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], + 'shape': [i + 1, i + 1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + check_result(result, y_data_list, "value") + + +@serving_test +def test_restful_base64_float16_2d_array_input_output_success(): + common_test_restful_base64_float_type_2d_array_input_output_success(np.float16) + + +@serving_test +def test_restful_base64_float32_2d_array_input_output_success(): + common_test_restful_base64_float_type_2d_array_input_output_success(np.float32) + + +@serving_test +def test_restful_base64_float64_2d_array_input_output_success(): + common_test_restful_base64_float_type_2d_array_input_output_success(np.float64) + + +@serving_test +def test_restful_base64_float16_2d_array_not_support_fp16_output_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, float_val): + return y, float_val + 1 + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, float_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, float_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} + assert dtype in dtype_str_map + + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 1)] * (i + 1) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], + 'shape': [i + 1, i + 1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "fp16 reply is not supported" in result["error_msg"] + + +@serving_test +def test_restful_base64_dtype_unknow_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} + assert dtype in dtype_str_map + + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 1)] * (i + 1) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "dtype_unknow", + 'shape': [i + 1, i + 1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "Parser request failed, json object, specified type:'dtype_unknow' is illegal" in result["error_msg"] + + +@serving_test +def test_restful_base64_dtype_empty_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} + assert dtype in dtype_str_map + + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 1)] * (i + 1) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "", + 'shape': [i + 1, i + 1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "Parser request failed, json object, specified type:'' is illegal" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_shape_not_match1_large_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} + assert dtype in dtype_str_map + + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 1)] * (i + 1) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], + 'shape': [i + 2, i + 2]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "Parser request failed, size is not matched" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_shape_not_match2_small_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} + assert dtype in dtype_str_map + + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 2)] * (i + 2) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], + 'shape': [i + 1, i + 1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "Parser request failed, size is not matched" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_shape_not_match3_small_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + dtype_str_map = {np.float16: "fp16", np.float32: "fp32", np.float64: "fp64"} + assert dtype in dtype_str_map + + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 2)] * (i + 2) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': dtype_str_map[dtype], + 'shape': [i + 2, i]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "Parser request failed, size is not matched" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_dtype_not_match4_empty_data_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + y_data_list = [] + for i, instance in enumerate(instances): + val = [[]] + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", + 'shape': [i + 1, i + 1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "Parser request failed, size is not matched" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_dtype_not_match5_empty_shape_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 2)] * (i + 2) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", + 'shape': []} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "Parser request failed, size is not matched" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_dtype_not_match6_empty_shape3_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 2)] * (i + 2) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16"} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "Parser request failed, size is not matched" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_dtype_not_match_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 2)] * (i + 2) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp32", + 'shape': [i + 2, i + 2]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "Parser request failed, size is not matched" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_invalid_shape_2d_shape_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 2)] * (i + 2) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", "shape": [[]]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "json object, key is 'shape', array value should be unsigned integer" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_invalid_shape2_str_shape_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 2)] * (i + 2) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", "shape": ["abc"]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "json object, key is 'shape', array value should be unsigned integer" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_invalid_shape3_float_shape_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 2)] * (i + 2) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", "shape": [1.1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "json object, key is 'shape', array value should be unsigned integer" in result["error_msg"] + + +@serving_test +def test_restful_base64_float16_2d_array_invalid_shape4_negative_shape_failed(): + base = start_bool_int_float_restful_server() + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + dtype = np.float16 + y_data_list = [] + for i, instance in enumerate(instances): + val = i * 2.2 * (-1 if i % 2 == 0 else 1) # 0, 2.2 ,-4.4 + val = [[val] * (i + 2)] * (i + 2) + val = np.array(val).astype(dtype) + y_data_list.append(val + 1) + instance["float_val"] = {"b64": base64.b64encode(val.tobytes()).decode(), 'type': "fp16", "shape": [-1]} + + result = post_restful("localhost", 5500, base.servable_name, "add_float", instances) + assert "json object, key is 'shape', array value should be unsigned integer" in result["error_msg"] diff --git a/tests/ut/python/tests/test_restful_json_data.py b/tests/ut/python/tests/test_restful_json_data.py new file mode 100644 index 0000000..de065c7 --- /dev/null +++ b/tests/ut/python/tests/test_restful_json_data.py @@ -0,0 +1,1040 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ +"""test Serving RESTful, with master, worker and client""" + +from mindspore_serving import master +from mindspore_serving import worker +from common import ServingTestBase, serving_test +from common import servable_config_import, servable_config_declare_servable +from common_restful import compare_float_value, create_multi_instances_fp32, post_restful + + +@serving_test +def test_restful_str_scalar_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +index = 0 +list_str = ["123", "456", "789"] +def postprocess(y, label): + global index + text = list_str[index] + index = (index + 1) if index + 1 < len(list_str) else 0 + return y.astype(np.int32), label + text + +@register.register_method(output_names=["y", "text"]) +def add_cast(x1, x2, label): + y = register.call_servable(x1, x2) + y, text = register.call_postprocess(postprocess, y, label) + return y, text +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = list_str[i] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + assert result[0]["text"] == "ABC123" + assert result[1]["text"] == "DEF456" + assert result[2]["text"] == "HIJ789" + + +@serving_test +def test_restful_str_scalar_shape1_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +index = 0 +list_str = ["123", "456", "789"] +def postprocess(y, label): + global index + text = list_str[index] + index = (index + 1) if index + 1 < len(list_str) else 0 + return y.astype(np.int32), label + text + +@register.register_method(output_names=["y", "text"]) +def add_cast(x1, x2, label): + y = register.call_servable(x1, x2) + y, text = register.call_postprocess(postprocess, y, label) + return y, text +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = [list_str[i]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + assert result[0]["text"] == "ABC123" + assert result[1]["text"] == "DEF456" + assert result[2]["text"] == "HIJ789" + + +@serving_test +def test_restful_empty_str_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +index = 0 +list_str = ["123", "456", "789"] +def postprocess(y, label): + global index + if len(label) == 0: + text = list_str[index] + else: + text = "" + index = (index + 1) if index + 1 < len(list_str) else 0 + return y.astype(np.int32), text + +@register.register_method(output_names=["y", "text"]) +def add_cast(x1, x2, label): + y = register.call_servable(x1, x2) + y, text = register.call_postprocess(postprocess, y, label) + return y, text +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = list_str[i] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + assert result[0]["text"] == "" + assert result[1]["text"] == "456" + assert result[2]["text"] == "" + + +@serving_test +def test_restful_str_2d_array_one_item_input_output_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +index = 0 +list_str = ["123", "456", "789"] +def postprocess(y, label): + global index + if len(label) == 0: + text = list_str[index] + else: + text = "" + index = (index + 1) if index + 1 < len(list_str) else 0 + return y.astype(np.int32), text + +@register.register_method(output_names=["y", "text"]) +def add_cast(x1, x2, label): + y = register.call_servable(x1, x2) + y, text = register.call_postprocess(postprocess, y, label) + return y, text +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = [[list_str[i]]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "bytes or string type input shape can only be (1,) or empty, but given shape is [1, 1]" \ + in result["error_msg"] + + +@serving_test +def test_restful_str_1d_array_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +index = 0 +list_str = ["123", "456", "789"] +def postprocess(y, label): + global index + text = list_str[index] + index = (index + 1) if index + 1 < len(list_str) else 0 + return y.astype(np.int32), label + text + +@register.register_method(output_names=["y", "text"]) +def add_cast(x1, x2, label): + y = register.call_servable(x1, x2) + y, text = register.call_postprocess(postprocess, y, label) + return y, text +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = [list_str[i], list_str[i]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, string or bytes type only support one item" in str(result["error_msg"]) + + +@serving_test +def test_restful_str_invalid_array_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +index = 0 +list_str = ["123", "456", "789"] +def postprocess(y, label): + global index + text = list_str[index] + index = (index + 1) if index + 1 < len(list_str) else 0 + return y.astype(np.int32), label + text + +@register.register_method(output_names=["y", "text"]) +def add_cast(x1, x2, label): + y = register.call_servable(x1, x2) + y, text = register.call_postprocess(postprocess, y, label) + return y, text +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + list_str = ["ABC", "DEF", "HIJ"] + for i, instance in enumerate(instances): + instance["label"] = [list_str[i], [list_str[i]]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, string or bytes type only support one item" in str(result["error_msg"]) + + +@serving_test +def test_restful_bool_scalar_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), not bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + instance["bool_val"] = (i % 2 == 0) + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + assert not result[0]["value"] + assert result[1]["value"] + assert not result[2]["value"] + + +@serving_test +def test_restful_bool_1d_array_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + instance["bool_val"] = [(i % 2 == 0)] * (i + 1) + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + assert result[0]["value"] == [False] + assert result[1]["value"] == [True, True] + assert result[2]["value"] == [False, False, False] + + +@serving_test +def test_restful_bool_2d_array_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = (i % 2 == 0) + val = [[val] * (i + 1)] * (i + 1) + instance["bool_val"] = val + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + assert result[0]["value"] == [[False]] + assert result[1]["value"] == [[True, True], [True, True]] + assert result[2]["value"] == [[False, False, False], [False, False, False], [False, False, False]] + + +@serving_test +def test_restful_bool_invalid_array_array_scalar_mix_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[False], True] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "invalid json array: json type is not array" in result['error_msg'] + + +@serving_test +def test_restful_bool_invalid_array2_scalar_array_mix_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [False, [True]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, data should be number, bool, string or bytes" in result['error_msg'] + + +@serving_test +def test_restful_bool_invalid_array3_array_dim_not_match_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[False, True], [True]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "invalid json array: json size is 1, the dim 1 expected to be 2" in result['error_msg'] + + +@serving_test +def test_restful_bool_invalid_array4_array_dim_not_match_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[[False, True]], [[True]]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "invalid json array: json size is 1, the dim 2 expected to be 2" in result['error_msg'] + + +@serving_test +def test_restful_int_scalar_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, int_val): + return y.astype(np.int32), int_val + 1 + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, int_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, int_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = i * 2 + instance["int_val"] = val + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + assert result[0]["value"] == 1 + assert result[1]["value"] == 3 + assert result[2]["value"] == 5 + + +@serving_test +def test_restful_int_empty_input_output_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, int_val): + return y.astype(np.int32), int_val + 1 + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, int_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, int_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + if i % 2 == 0: + val = [] + else: + val = [i * 2] * (i + 1) + instance["int_val"] = val + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, shape is empty" in result["error_msg"] + + +@serving_test +def test_restful_int_1d_array_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, int_val): + return y.astype(np.int32), int_val + 1 + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, int_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, int_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = i * 2 + val = [val] * (i + 1) + instance["int_val"] = val + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + assert result[0]["value"] == [1] + assert result[1]["value"] == [3, 3] + assert result[2]["value"] == [5, 5, 5] + + +@serving_test +def test_restful_int_2d_array_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, int_val): + print("-----------------------int val ", int_val, int_val + 1) + return y.astype(np.int32), int_val + 1 + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, int_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, int_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = i * 2 + val = [[val] * (i + 1)] * (i + 1) + instance["int_val"] = val + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + assert result[0]["value"] == [[1]] + assert result[1]["value"] == [[3, 3], [3, 3]] + assert result[2]["value"] == [[5, 5, 5], [5, 5, 5], [5, 5, 5]] + + +@serving_test +def test_restful_float_scalar_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, float_val): + return y, float_val + 1 + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, float_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, float_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = i * 2.2 + instance["float_val"] = val + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + assert result[0]["value"] == 1 + assert abs(result[1]["value"] - (2.2 + 1)) < 0.001 + assert abs(result[2]["value"] - (4.4 + 1)) < 0.001 + + +@serving_test +def test_restful_float_1d_array_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, float_val): + return y, float_val + 1 + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, float_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, float_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = [i * 2.2] * (i + 1) + instance["float_val"] = val + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + compare_float_value(result[0]["value"], [1]) + compare_float_value(result[1]["value"], [3.2, 3.2]) + compare_float_value(result[2]["value"], [5.4, 5.4, 5.4]) + + +@serving_test +def test_restful_float_2d_array_input_output_success(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, float_val): + return y, float_val + 1 + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, float_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, float_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for i, instance in enumerate(instances): + val = i * 2.2 + val = [[val] * (i + 1)] * (i + 1) + instance["float_val"] = val + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + result = result["instances"] + compare_float_value(result[0]["value"], [[1]]) + compare_float_value(result[1]["value"], [[3.2, 3.2], [3.2, 3.2]]) + compare_float_value(result[2]["value"], [[5.4, 5.4, 5.4], [5.4, 5.4, 5.4], [5.4, 5.4, 5.4]]) + + +@serving_test +def test_restful_mix_bool_int_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[False, True], [1, 1]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, elements type is not equal" in result['error_msg'] + + +@serving_test +def test_restful_mix_bool_int2_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[False, 1]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, elements type is not equal" in result['error_msg'] + + +@serving_test +def test_restful_mix_float_int_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[1.1, 1.2], [1, 1]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, elements type is not equal" in result['error_msg'] + + +@serving_test +def test_restful_mix_float_int2_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[1.1, 1]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, elements type is not equal" in result['error_msg'] + + +@serving_test +def test_restful_mix_int_float_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[1, 1], [1.1, 1.2]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, elements type is not equal" in result['error_msg'] + + +@serving_test +def test_restful_mix_int_float2_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[1, 1.2]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, elements type is not equal" in result['error_msg'] + + +@serving_test +def test_restful_mix_str_float_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [["a", "b"], [1.1, 1.2]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "string or bytes type only support one item" in result['error_msg'] + + +@serving_test +def test_restful_mix_str_float2_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [["a", 1.2]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "string or bytes type only support one item" in result['error_msg'] + + +@serving_test +def test_restful_mix_float_str_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[1.1, 1.2], ["a", "b"]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, elements type is not equal" in result['error_msg'] + + +@serving_test +def test_restful_mix_float_str2_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[1.1, "b"]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, elements type is not equal" in result['error_msg'] + + +@serving_test +def test_restful_mix_bytes_str_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[{"b64": ""}, {"b64": ""}], ["a", "b"]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "string or bytes type only support one item" in result['error_msg'] + + +@serving_test +def test_restful_mix_bytes_bool_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[{"b64": ""}, {"b64": ""}], [True, False]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "string or bytes type only support one item" in result['error_msg'] + + +@serving_test +def test_restful_mix_bool_bytes_input_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def postprocess(y, bool_val): + return y.astype(np.int32), ~bool_val + +@register.register_method(output_names=["y", "value"]) +def add_cast(x1, x2, bool_val): + y = register.call_servable(x1, x2) + y, value = register.call_postprocess(postprocess, y, bool_val) + return y, value +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + for instance in instances: + instance["bool_val"] = [[True, False], [{"b64": ""}, {"b64": ""}]] + + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "json array, data should be number, bool, string or bytes" in result['error_msg'] diff --git a/tests/ut/python/tests/test_restful_request.py b/tests/ut/python/tests/test_restful_request.py new file mode 100644 index 0000000..114a8af --- /dev/null +++ b/tests/ut/python/tests/test_restful_request.py @@ -0,0 +1,335 @@ +# Copyright 2020 Huawei Technologies Co., Ltd +# +# 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. +# ============================================================================ +"""test Serving RESTful, with master, worker and client""" + +import json +import requests +from mindspore_serving import master +from mindspore_serving import worker +from common import ServingTestBase, serving_test +from common import servable_config_import, servable_config_declare_servable +from common_restful import create_multi_instances_fp32, check_result, post_restful +from common_restful import create_multi_instances_int32_input_fp32_output + + +@serving_test +def test_restful_request_success(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + worker.start_servable_in_master(base.servable_dir, base.servable_name, 0) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, y_data_list = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) + check_result(result, y_data_list) + + +@serving_test +def test_restful_request_multi_times_success(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + worker.start_servable_in_master(base.servable_dir, base.servable_name, 0) + master.start_restful_server("0.0.0.0", 5500) + for instance_count in range(1, 5): + instances, y_data_list = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) + check_result(result, y_data_list) + + +@serving_test +def test_restful_request_multi_times_int32_success(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + worker.start_servable_in_master(base.servable_dir, base.servable_name, 0) + master.start_restful_server("0.0.0.0", 5500) + for instance_count in range(1, 5): + instances, y_data_list = create_multi_instances_int32_input_fp32_output(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + check_result(result, y_data_list) + + +@serving_test +def test_restful_request_worker_alone_success(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, y_data_list = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) + check_result(result, y_data_list) + + +@serving_test +def test_restful_request_worker_alone_multi_times_success(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + for instance_count in range(1, 5): + instances, y_data_list = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances) + check_result(result, y_data_list) + + +@serving_test +def test_restful_request_worker_alone_servable_invalid_failed(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name + "_error", "add_common", instances) + assert "servable is not available" in str(result["error_msg"]) + + +@serving_test +def test_restful_request_worker_alone_method_invalid_failed(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_common" + "_error", instances) + assert "method is not available" in str(result["error_msg"]) + + +@serving_test +def test_restful_request_worker_alone_with_version_number_0_success(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, y_data_list = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances, 0) + check_result(result, y_data_list) + + +@serving_test +def test_restful_request_worker_alone_with_version_number_1_success(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, y_data_list = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances, 1) + check_result(result, y_data_list) + + +@serving_test +def test_restful_request_worker_alone_with_version_number_2_invalid_failed(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances, 2) + assert "servable is not available" in str(result["error_msg"]) + + +@serving_test +def test_restful_request_worker_alone_version_number_negative_failed(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_common", instances, -1) + assert "please check url, version number range failed" in str(result["error_msg"]) + + +@serving_test +def test_restful_request_worker_alone_without_model_invalid_failed(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + instances_map = {"instances": instances} + post_payload = json.dumps(instances_map) + print("request:", post_payload) + request_url = f"http://localhost:5500/x/:add_common" + result = requests.post(request_url, data=post_payload) + print("result", result.text) + result = json.loads(result.text) + assert "please check url, the keyword:[model] must contain" in str(result["error_msg"]) + + +@serving_test +def test_restful_request_worker_alone_without_method_invalid_failed(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + instances_map = {"instances": instances} + post_payload = json.dumps(instances_map) + print("request:", post_payload) + request_url = f"http://localhost:5500/model/{base.servable_name}" + result = requests.post(request_url, data=post_payload) + print("result", result.text) + result = json.loads(result.text) + assert "please check url, the keyword:[service method] must contain." in str(result["error_msg"]) + + +@serving_test +def test_restful_request_without_method_invalid_failed(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + + instances_map = {"instances": instances} + post_payload = json.dumps(instances_map) + print("request:", post_payload) + request_url = f"http://localhost:5500/model/{base.servable_name}" + result = requests.post(request_url, data=post_payload) + print("result", result.text) + result = json.loads(result.text) + assert "please check url, the keyword:[service method] must contain." in str(result["error_msg"]) + + +@serving_test +def test_restful_request_worker_alone_servable_version_reverse_success(): + base = ServingTestBase() + base.init_servable(1, "add_servable_config.py") + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, y_data_list = create_multi_instances_fp32(instance_count) + + instances_map = {"instances": instances} + post_payload = json.dumps(instances_map) + print("request:", post_payload) + request_url = f"http://localhost:5500/version/0/model/{base.servable_name}:add_common" + result = requests.post(request_url, data=post_payload) + print("result", result.text) + result = json.loads(result.text) + check_result(result, y_data_list) + + +@serving_test +def test_restful_request_preprocess_outputs_count_not_match_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def add_trans_datatype(x1, x2): + return x1.astype(np.float32) + +@register.register_method(output_names=["y"]) +def add_cast(x1, x2): + x1, x2 = register.call_preprocess(add_trans_datatype, x1, x2) # cast input to float32 + y = register.call_servable(x1, x2) + return y +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + + print(result) + assert "Preprocess Failed" in str(result["instances"][0]["error_msg"]) + + +@serving_test +def test_restful_request_postprocess_outputs_count_not_match_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def add_trans_datatype(x1, x2): + return x1.astype(np.float32) + +@register.register_method(output_names=["y"]) +def add_cast(x1, x2): + y = register.call_servable(x1, x2) + y, y2 = register.call_postprocess(add_trans_datatype, y, x2) + return y +""" + base.init_servable_with_servable_config(1, servable_content) + worker.start_servable_in_master(base.servable_dir, base.servable_name) + master.start_restful_server("0.0.0.0", 5500) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name, "add_cast", instances) + assert "Postprocess Failed" in str(result["instances"][0]["error_msg"]) + + +@serving_test +def test_restful_request_worker_alone_outputs_count_not_match_failed(): + base = ServingTestBase() + servable_content = servable_config_import + servable_content += servable_config_declare_servable + servable_content += r""" +def add_trans_datatype(x1, x2): + return x1.astype(np.float32) + +@register.register_method(output_names=["y"]) +def add_cast(x1, x2): + y = register.call_servable(x1, x2) + y, y2 = register.call_postprocess(add_trans_datatype, y, x2) + return y +""" + base.init_servable_with_servable_config(1, servable_content) + master.start_master_server(master_port=7600) + master.start_restful_server("0.0.0.0", 5500) + worker.start_servable(base.servable_dir, base.servable_name, master_port=7600, worker_port=6600) + # Client + instance_count = 3 + instances, _ = create_multi_instances_fp32(instance_count) + result = post_restful("localhost", 5500, base.servable_name + "_error", "add_common", instances) + assert "servable is not available" in str(result["error_msg"])