Skip to content

Commit d4367e9

Browse files
authored
Add test template (#3140)
* Add test template based on mnist * Add section on testing in CONTRIBUTING.md * Fix spellcheck error
1 parent 9419344 commit d4367e9

File tree

3 files changed

+137
-2
lines changed

3 files changed

+137
-2
lines changed

CONTRIBUTING.md

+16-2
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,21 @@ Your contributions will fall into two categories:
4040

4141
Once you finish implementing a feature or bug-fix, please send a Pull Request to https://github.com/pytorch/serve.
4242

43-
For more non-technical guidance about how to contribute to PyTorch, see the Contributing Guide.
43+
New features should always be covered by at least one integration test.
44+
For guidance please have a look at our [current suite of pytest tests](https://github.com/pytorch/serve/tree/master/test/pytest) and orient yourself on a test that covers a similar use case as your new feature.
45+
A simplified version of an example test can be found in the [mnist template test](https://github.com/pytorch/serve/blob/master/test/pytest/test_mnist_template.py) which shows how to create a mar file on the fly and register it with TorchServe from within a test.
46+
You can run most tests by simply executing:
47+
```bash
48+
pytest test/pytest/test_mnist_template.py
49+
```
50+
To have a look at the TorchServe and/or test output add `-s` like this:
51+
```bash
52+
pytest -s test/pytest/test_mnist_template.py
53+
```
54+
To run only a subset or a single test from a file use `-k` like this:
55+
```bash
56+
pytest -k test/pytest/test_mnist_template.py
57+
```
4458

4559
### Install TorchServe for development
4660

@@ -50,7 +64,7 @@ Ensure that you have `python3` installed, and the user has access to the site-pa
5064

5165
Run the following script from the top of the source directory.
5266

53-
NOTE: This script force reinstalls `torchserve`, `torch-model-archiver` and `torch-workflow-archiver` if existing installations are found
67+
NOTE: This script force re-installs `torchserve`, `torch-model-archiver` and `torch-workflow-archiver` if existing installations are found
5468

5569
#### For Debian Based Systems/ MacOS
5670

test/pytest/test_mnist_template.py

+120
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import shutil
2+
from pathlib import Path
3+
from unittest.mock import patch
4+
5+
import pytest
6+
import requests
7+
import test_utils
8+
from model_archiver import ModelArchiverConfig
9+
10+
CURR_FILE_PATH = Path(__file__).parent
11+
REPO_ROOT_DIR = CURR_FILE_PATH.parents[1]
12+
config_file = REPO_ROOT_DIR / "test/resources/config_token.properties"
13+
data_file_zero = REPO_ROOT_DIR / "test/pytest/test_data/0.png"
14+
handler_py_file = REPO_ROOT_DIR / "examples/image_classifier/mnist/mnist_handler.py"
15+
model_py_file = REPO_ROOT_DIR / "examples/image_classifier/mnist/mnist.py"
16+
model_pt_file = REPO_ROOT_DIR / "examples/image_classifier/mnist/mnist_cnn.pt"
17+
18+
19+
HANDLER_PY = """
20+
import torch
21+
from ts.torch_handler.base_handler import BaseHandler
22+
23+
class customHandler(BaseHandler):
24+
25+
def initialize(self, context):
26+
super().initialize(context)
27+
"""
28+
29+
MODEL_CONFIG_YAML = """
30+
#frontend settings
31+
# TorchServe frontend parameters
32+
minWorkers: 1
33+
batchSize: 1
34+
maxWorkers: 1
35+
"""
36+
37+
38+
@pytest.fixture(scope="module")
39+
def model_name():
40+
yield "some_model"
41+
42+
43+
@pytest.fixture(scope="module")
44+
def work_dir(tmp_path_factory, model_name):
45+
return Path(tmp_path_factory.mktemp(model_name))
46+
47+
48+
@pytest.fixture(scope="module", name="mar_file_path")
49+
def create_mar_file(work_dir, model_archiver, model_name):
50+
mar_file_path = work_dir.joinpath(model_name + ".mar")
51+
52+
model_config_yaml_file = work_dir / "model_config.yaml"
53+
model_config_yaml_file.write_text(MODEL_CONFIG_YAML)
54+
55+
config = ModelArchiverConfig(
56+
model_name=model_name,
57+
version="1.0",
58+
serialized_file=model_pt_file.as_posix(),
59+
model_file=model_py_file.as_posix(),
60+
handler=handler_py_file.as_posix(),
61+
extra_files=None,
62+
export_path=work_dir,
63+
requirements_file=None,
64+
runtime="python",
65+
force=False,
66+
archive_format="default",
67+
config_file=model_config_yaml_file.as_posix(),
68+
)
69+
70+
with patch("archiver.ArgParser.export_model_args_parser", return_value=config):
71+
model_archiver.generate_model_archive()
72+
73+
assert mar_file_path.exists()
74+
75+
yield mar_file_path.as_posix()
76+
77+
# Clean up files
78+
79+
mar_file_path.unlink(missing_ok=True)
80+
81+
# Clean up files
82+
83+
84+
@pytest.fixture(scope="module", name="model_name")
85+
def register_model(mar_file_path, model_store, torchserve):
86+
"""
87+
Register the model in torchserve
88+
"""
89+
shutil.copy(mar_file_path, model_store)
90+
91+
file_name = Path(mar_file_path).name
92+
93+
model_name = Path(file_name).stem
94+
95+
params = (
96+
("model_name", model_name),
97+
("url", file_name),
98+
("initial_workers", "1"),
99+
("synchronous", "true"),
100+
("batch_size", "1"),
101+
)
102+
103+
test_utils.reg_resp = test_utils.register_model_with_params(params)
104+
105+
yield model_name
106+
107+
test_utils.unregister_model(model_name)
108+
109+
110+
def test_mnist_template(model_name):
111+
response = requests.get(f"http://localhost:8081/models/{model_name}")
112+
assert response.status_code == 200, "Describe Failed"
113+
114+
with open(data_file_zero, "rb") as f:
115+
response = requests.post(
116+
f"http://localhost:8080/predictions/{model_name}",
117+
data=f,
118+
)
119+
120+
assert response.content.decode("utf-8") == "0", "Wrong prediction"

ts_scripts/spellcheck_conf/wordlist.txt

+1
Original file line numberDiff line numberDiff line change
@@ -1238,3 +1238,4 @@ lora
12381238
vllm
12391239
sql
12401240
TimeUnit
1241+
Aopen

0 commit comments

Comments
 (0)