Skip to content

Commit 6636fc9

Browse files
authored
test: Add test for sending response after sending complete final flag (#7504)
1 parent a4285ff commit 6636fc9

File tree

4 files changed

+218
-0
lines changed

4 files changed

+218
-0
lines changed
Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
# Copyright 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# Redistribution and use in source and binary forms, with or without
4+
# modification, are permitted provided that the following conditions
5+
# are met:
6+
# * Redistributions of source code must retain the above copyright
7+
# notice, this list of conditions and the following disclaimer.
8+
# * Redistributions in binary form must reproduce the above copyright
9+
# notice, this list of conditions and the following disclaimer in the
10+
# documentation and/or other materials provided with the distribution.
11+
# * Neither the name of NVIDIA CORPORATION nor the names of its
12+
# contributors may be used to endorse or promote products derived
13+
# from this software without specific prior written permission.
14+
#
15+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
16+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18+
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23+
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+
import os
28+
import time
29+
import unittest
30+
31+
import numpy as np
32+
import tritonclient.grpc as grpcclient
33+
34+
35+
class ResponseSenderTest(unittest.TestCase):
36+
def _generate_streaming_callback_and_responses_pair(self):
37+
responses = [] # [{"result": result, "error": error}, ...]
38+
39+
def callback(result, error):
40+
responses.append({"result": result, "error": error})
41+
42+
return callback, responses
43+
44+
def test_respond_after_complete_final(self):
45+
with open(os.environ["SERVER_LOG"]) as f:
46+
server_log = f.read()
47+
self.assertNotIn("Test Passed", server_log)
48+
49+
model_name = "response_sender_complete_final"
50+
shape = [1, 1]
51+
inputs = [grpcclient.InferInput("INPUT0", shape, "FP32")]
52+
input0_np = np.array([[123.45]], np.float32)
53+
inputs[0].set_data_from_numpy(input0_np)
54+
55+
callback, responses = self._generate_streaming_callback_and_responses_pair()
56+
with grpcclient.InferenceServerClient("localhost:8001") as client:
57+
client.start_stream(callback)
58+
client.async_stream_infer(model_name, inputs)
59+
client.stop_stream()
60+
61+
self.assertEqual(len(responses), 1)
62+
for response in responses:
63+
output0_np = response["result"].as_numpy(name="OUTPUT0")
64+
self.assertTrue(np.allclose(input0_np, output0_np))
65+
self.assertIsNone(response["error"])
66+
67+
time.sleep(1) # make sure the logs are written before checking
68+
with open(os.environ["SERVER_LOG"]) as f:
69+
server_log = f.read()
70+
self.assertNotIn("Unexpected request length", server_log)
71+
self.assertNotIn("Expected exception not raised", server_log)
72+
self.assertNotIn("Test FAILED", server_log)
73+
self.assertIn("Test Passed", server_log)
74+
75+
76+
if __name__ == "__main__":
77+
unittest.main()

qa/L0_backend_python/response_sender/test.sh

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,37 @@ set -e
9797
kill $SERVER_PID
9898
wait $SERVER_PID
9999

100+
#
101+
# Test response sender to raise exception on response after complete final flag
102+
#
103+
rm -rf models && mkdir models
104+
mkdir -p models/response_sender_complete_final/1 && \
105+
cp ../../python_models/response_sender_complete_final/model.py models/response_sender_complete_final/1 && \
106+
cp ../../python_models/response_sender_complete_final/config.pbtxt models/response_sender_complete_final
107+
108+
TEST_LOG="response_sender_complete_final_test.log"
109+
SERVER_LOG="response_sender_complete_final_test.server.log"
110+
SERVER_ARGS="--model-repository=${MODELDIR}/response_sender/models --backend-directory=${BACKEND_DIR} --log-verbose=1"
111+
112+
run_server
113+
if [ "$SERVER_PID" == "0" ]; then
114+
echo -e "\n***\n*** Failed to start $SERVER\n***"
115+
cat $SERVER_LOG
116+
exit 1
117+
fi
118+
119+
set +e
120+
SERVER_LOG=$SERVER_LOG python3 -m pytest --junitxml=concurrency_test.report.xml response_sender_complete_final_test.py > $TEST_LOG 2>&1
121+
if [ $? -ne 0 ]; then
122+
echo -e "\n***\n*** response sender complete final test FAILED\n***"
123+
cat $TEST_LOG
124+
RET=1
125+
fi
126+
set -e
127+
128+
kill $SERVER_PID
129+
wait $SERVER_PID
130+
100131
#
101132
# Test async response sender under decoupled / non-decoupled
102133
#
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Copyright 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# Redistribution and use in source and binary forms, with or without
4+
# modification, are permitted provided that the following conditions
5+
# are met:
6+
# * Redistributions of source code must retain the above copyright
7+
# notice, this list of conditions and the following disclaimer.
8+
# * Redistributions in binary form must reproduce the above copyright
9+
# notice, this list of conditions and the following disclaimer in the
10+
# documentation and/or other materials provided with the distribution.
11+
# * Neither the name of NVIDIA CORPORATION nor the names of its
12+
# contributors may be used to endorse or promote products derived
13+
# from this software without specific prior written permission.
14+
#
15+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
16+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18+
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23+
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+
backend: "python"
28+
max_batch_size: 8
29+
30+
input [
31+
{
32+
name: "INPUT0"
33+
data_type: TYPE_FP32
34+
dims: [ -1 ]
35+
}
36+
]
37+
38+
output [
39+
{
40+
name: "OUTPUT0"
41+
data_type: TYPE_FP32
42+
dims: [ -1 ]
43+
}
44+
]
45+
46+
instance_group [{ kind: KIND_CPU }]
47+
model_transaction_policy { decoupled: True }
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# Copyright 2024, NVIDIA CORPORATION & AFFILIATES. All rights reserved.
2+
#
3+
# Redistribution and use in source and binary forms, with or without
4+
# modification, are permitted provided that the following conditions
5+
# are met:
6+
# * Redistributions of source code must retain the above copyright
7+
# notice, this list of conditions and the following disclaimer.
8+
# * Redistributions in binary form must reproduce the above copyright
9+
# notice, this list of conditions and the following disclaimer in the
10+
# documentation and/or other materials provided with the distribution.
11+
# * Neither the name of NVIDIA CORPORATION nor the names of its
12+
# contributors may be used to endorse or promote products derived
13+
# from this software without specific prior written permission.
14+
#
15+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
16+
# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18+
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
19+
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20+
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21+
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22+
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23+
# OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24+
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26+
27+
28+
import triton_python_backend_utils as pb_utils
29+
30+
31+
class TritonPythonModel:
32+
def execute(self, requests):
33+
# Expect exactly one request per execute() call.
34+
if len(requests) != 1:
35+
pb_utils.Logger.log_error(f"Unexpected request length: {len(requests)}")
36+
raise Exception("Test FAILED")
37+
38+
# Send a response with complete final flag, and then send another response and
39+
# and assert an exception is raised, for all requests.
40+
for request in requests:
41+
in_tensor = pb_utils.get_input_tensor_by_name(request, "INPUT0")
42+
out_tensor = pb_utils.Tensor("OUTPUT0", in_tensor.as_numpy())
43+
response = pb_utils.InferenceResponse([out_tensor])
44+
response_sender = request.get_response_sender()
45+
response_sender.send(
46+
response, flags=pb_utils.TRITONSERVER_RESPONSE_COMPLETE_FINAL
47+
)
48+
test_passed = False
49+
try:
50+
response_sender.send(response)
51+
except Exception as e:
52+
pb_utils.Logger.log_info(f"Raised exception: {e}")
53+
if (
54+
str(e)
55+
== "Unable to send response. Response sender has been closed."
56+
):
57+
test_passed = True
58+
finally:
59+
if not test_passed:
60+
pb_utils.Logger.log_error("Expected exception not raised")
61+
raise Exception("Test FAILED")
62+
pb_utils.Logger.log_info("Test Passed")
63+
return None

0 commit comments

Comments
 (0)