* add test level support * update develop doc Link: https://code.alibaba-inc.com/Ali-MaaS/MaaS-lib/codereview/9021354master
| @@ -34,13 +34,62 @@ make linter | |||||
| ``` | ``` | ||||
| ## 2. Test | ## 2. Test | ||||
| ### 2.1 Unit test | |||||
| ### 2.1 Test level | |||||
| There are mainly three test levels: | |||||
| * level 0: tests for basic interface and function of framework, such as `tests/trainers/test_trainer_base.py` | |||||
| * level 1: important functional test which test end2end workflow, such as `tests/pipelines/test_image_matting.py` | |||||
| * level 2: scenario tests for all the implemented modules such as model, pipeline in different algorithm filed. | |||||
| Default test level is 0, which will only run those cases of level 0, you can set test level | |||||
| via environment variable `TEST_LEVEL`. For more details, you can refer to [test-doc](https://alidocs.dingtalk.com/i/nodes/mdvQnONayjBJKLXy1Bp38PY2MeXzp5o0?dontjump=true&nav=spaces&navQuery=spaceId%3Dnb9XJNlZxbgrOXyA) | |||||
| ```bash | ```bash | ||||
| # run all tests | |||||
| TEST_LEVEL=2 make test | |||||
| # run important functional tests | |||||
| TEST_LEVEL=1 make test | |||||
| # run core UT and basic functional tests | |||||
| make test | make test | ||||
| ``` | ``` | ||||
| ### 2.2 Test data | |||||
| TODO | |||||
| When writing test cases, you should assign a test level for your test case using | |||||
| following code. If left default, the test level will be 0, it will run in each | |||||
| test stage. | |||||
| File test_module.py | |||||
| ```python | |||||
| from modelscope.utils.test_utils import test_level | |||||
| class ImageCartoonTest(unittest.TestCase): | |||||
| @unittest.skipUnless(test_level() >= 1, 'skip test in current test level') | |||||
| def test_run_by_direct_model_download(self): | |||||
| pass | |||||
| ``` | |||||
| ### 2.2 Run tests | |||||
| 1. Run your own single test case to test your self-implemented function. You can run your | |||||
| test file directly, if it fails to run, pls check if variable `TEST_LEVEL` | |||||
| exists in the environment and unset it. | |||||
| ```bash | |||||
| python tests/path/to/your_test.py | |||||
| ``` | |||||
| 2. Remember to run core tests in local environment before start a codereview, by default it will | |||||
| only run test cases with level 0. | |||||
| ```bash | |||||
| make tests | |||||
| ``` | |||||
| 3. After you start a code review, ci tests will be triggered which will run test cases with level 1 | |||||
| 4. Daily regression tests will run all cases at 0 am each day using master branch. | |||||
| ## Code Review | ## Code Review | ||||
| @@ -0,0 +1,20 @@ | |||||
| #!/usr/bin/env python | |||||
| # Copyright (c) Alibaba, Inc. and its affiliates. | |||||
| import os | |||||
| TEST_LEVEL = 2 | |||||
| TEST_LEVEL_STR = 'TEST_LEVEL' | |||||
| def test_level(): | |||||
| global TEST_LEVEL | |||||
| if TEST_LEVEL_STR in os.environ: | |||||
| TEST_LEVEL = int(os.environ[TEST_LEVEL_STR]) | |||||
| return TEST_LEVEL | |||||
| def set_test_level(level: int): | |||||
| global TEST_LEVEL | |||||
| TEST_LEVEL = level | |||||
| @@ -7,6 +7,7 @@ import unittest | |||||
| from modelscope.fileio import File | from modelscope.fileio import File | ||||
| from modelscope.pipelines import pipeline | from modelscope.pipelines import pipeline | ||||
| from modelscope.utils.constant import Tasks | from modelscope.utils.constant import Tasks | ||||
| from modelscope.utils.test_utils import test_level | |||||
| class ImageCaptionTest(unittest.TestCase): | class ImageCaptionTest(unittest.TestCase): | ||||
| @@ -11,6 +11,7 @@ from modelscope.pipelines import pipeline | |||||
| from modelscope.pydatasets import PyDataset | from modelscope.pydatasets import PyDataset | ||||
| from modelscope.utils.constant import ModelFile, Tasks | from modelscope.utils.constant import ModelFile, Tasks | ||||
| from modelscope.utils.hub import get_model_cache_dir | from modelscope.utils.hub import get_model_cache_dir | ||||
| from modelscope.utils.test_utils import test_level | |||||
| class ImageMattingTest(unittest.TestCase): | class ImageMattingTest(unittest.TestCase): | ||||
| @@ -38,6 +39,7 @@ class ImageMattingTest(unittest.TestCase): | |||||
| ) | ) | ||||
| cv2.imwrite('result.png', result['output_png']) | cv2.imwrite('result.png', result['output_png']) | ||||
| @unittest.skipUnless(test_level() >= 1, 'skip test in current test level') | |||||
| def test_run_with_dataset(self): | def test_run_with_dataset(self): | ||||
| input_location = [ | input_location = [ | ||||
| 'http://pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/data/test/maas/image_matting/test.png' | 'http://pai-vision-data-hz.oss-cn-zhangjiakou.aliyuncs.com/data/test/maas/image_matting/test.png' | ||||
| @@ -52,6 +54,7 @@ class ImageMattingTest(unittest.TestCase): | |||||
| cv2.imwrite('result.png', next(result)['output_png']) | cv2.imwrite('result.png', next(result)['output_png']) | ||||
| print(f'Output written to {osp.abspath("result.png")}') | print(f'Output written to {osp.abspath("result.png")}') | ||||
| @unittest.skipUnless(test_level() >= 0, 'skip test in current test level') | |||||
| def test_run_modelhub(self): | def test_run_modelhub(self): | ||||
| img_matting = pipeline(Tasks.image_matting, model=self.model_id) | img_matting = pipeline(Tasks.image_matting, model=self.model_id) | ||||
| @@ -61,6 +64,7 @@ class ImageMattingTest(unittest.TestCase): | |||||
| cv2.imwrite('result.png', result['output_png']) | cv2.imwrite('result.png', result['output_png']) | ||||
| print(f'Output written to {osp.abspath("result.png")}') | print(f'Output written to {osp.abspath("result.png")}') | ||||
| @unittest.skipUnless(test_level() >= 0, 'skip test in current test level') | |||||
| def test_run_modelhub_default_model(self): | def test_run_modelhub_default_model(self): | ||||
| img_matting = pipeline(Tasks.image_matting) | img_matting = pipeline(Tasks.image_matting) | ||||
| @@ -8,6 +8,7 @@ import cv2 | |||||
| from modelscope.pipelines import pipeline | from modelscope.pipelines import pipeline | ||||
| from modelscope.pipelines.base import Pipeline | from modelscope.pipelines.base import Pipeline | ||||
| from modelscope.utils.constant import Tasks | from modelscope.utils.constant import Tasks | ||||
| from modelscope.utils.test_utils import test_level | |||||
| class ImageCartoonTest(unittest.TestCase): | class ImageCartoonTest(unittest.TestCase): | ||||
| @@ -36,10 +37,12 @@ class ImageCartoonTest(unittest.TestCase): | |||||
| img_cartoon = pipeline(Tasks.image_generation, model=model_dir) | img_cartoon = pipeline(Tasks.image_generation, model=model_dir) | ||||
| self.pipeline_inference(img_cartoon, self.test_image) | self.pipeline_inference(img_cartoon, self.test_image) | ||||
| @unittest.skipUnless(test_level() >= 1, 'skip test in current test level') | |||||
| def test_run_modelhub(self): | def test_run_modelhub(self): | ||||
| img_cartoon = pipeline(Tasks.image_generation, model=self.model_id) | img_cartoon = pipeline(Tasks.image_generation, model=self.model_id) | ||||
| self.pipeline_inference(img_cartoon, self.test_image) | self.pipeline_inference(img_cartoon, self.test_image) | ||||
| @unittest.skipUnless(test_level() >= 1, 'skip test in current test level') | |||||
| def test_run_modelhub_default_model(self): | def test_run_modelhub_default_model(self): | ||||
| img_cartoon = pipeline(Tasks.image_generation) | img_cartoon = pipeline(Tasks.image_generation) | ||||
| self.pipeline_inference(img_cartoon, self.test_image) | self.pipeline_inference(img_cartoon, self.test_image) | ||||
| @@ -12,6 +12,7 @@ from modelscope.preprocessors import SequenceClassificationPreprocessor | |||||
| from modelscope.pydatasets import PyDataset | from modelscope.pydatasets import PyDataset | ||||
| from modelscope.utils.constant import Hubs, Tasks | from modelscope.utils.constant import Hubs, Tasks | ||||
| from modelscope.utils.hub import get_model_cache_dir | from modelscope.utils.hub import get_model_cache_dir | ||||
| from modelscope.utils.test_utils import test_level | |||||
| class SequenceClassificationTest(unittest.TestCase): | class SequenceClassificationTest(unittest.TestCase): | ||||
| @@ -43,6 +44,7 @@ class SequenceClassificationTest(unittest.TestCase): | |||||
| break | break | ||||
| print(r) | print(r) | ||||
| @unittest.skipUnless(test_level() >= 2, 'skip test in current test level') | |||||
| def test_run(self): | def test_run(self): | ||||
| model_url = 'https://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com' \ | model_url = 'https://atp-modelzoo-sh.oss-cn-shanghai.aliyuncs.com' \ | ||||
| '/release/easynlp_modelzoo/alibaba-pai/bert-base-sst2.zip' | '/release/easynlp_modelzoo/alibaba-pai/bert-base-sst2.zip' | ||||
| @@ -67,6 +69,7 @@ class SequenceClassificationTest(unittest.TestCase): | |||||
| Tasks.text_classification, model=model, preprocessor=preprocessor) | Tasks.text_classification, model=model, preprocessor=preprocessor) | ||||
| print(pipeline2('Hello world!')) | print(pipeline2('Hello world!')) | ||||
| @unittest.skipUnless(test_level() >= 0, 'skip test in current test level') | |||||
| def test_run_with_model_from_modelhub(self): | def test_run_with_model_from_modelhub(self): | ||||
| model = Model.from_pretrained(self.model_id) | model = Model.from_pretrained(self.model_id) | ||||
| preprocessor = SequenceClassificationPreprocessor( | preprocessor = SequenceClassificationPreprocessor( | ||||
| @@ -77,6 +80,7 @@ class SequenceClassificationTest(unittest.TestCase): | |||||
| preprocessor=preprocessor) | preprocessor=preprocessor) | ||||
| self.predict(pipeline_ins) | self.predict(pipeline_ins) | ||||
| @unittest.skipUnless(test_level() >= 0, 'skip test in current test level') | |||||
| def test_run_with_model_name(self): | def test_run_with_model_name(self): | ||||
| text_classification = pipeline( | text_classification = pipeline( | ||||
| task=Tasks.text_classification, model=self.model_id) | task=Tasks.text_classification, model=self.model_id) | ||||
| @@ -85,6 +89,7 @@ class SequenceClassificationTest(unittest.TestCase): | |||||
| 'glue', name='sst2', target='sentence', hub=Hubs.huggingface)) | 'glue', name='sst2', target='sentence', hub=Hubs.huggingface)) | ||||
| self.printDataset(result) | self.printDataset(result) | ||||
| @unittest.skipUnless(test_level() >= 1, 'skip test in current test level') | |||||
| def test_run_with_default_model(self): | def test_run_with_default_model(self): | ||||
| text_classification = pipeline(task=Tasks.text_classification) | text_classification = pipeline(task=Tasks.text_classification) | ||||
| result = text_classification( | result = text_classification( | ||||
| @@ -92,6 +97,7 @@ class SequenceClassificationTest(unittest.TestCase): | |||||
| 'glue', name='sst2', target='sentence', hub=Hubs.huggingface)) | 'glue', name='sst2', target='sentence', hub=Hubs.huggingface)) | ||||
| self.printDataset(result) | self.printDataset(result) | ||||
| @unittest.skipUnless(test_level() >= 1, 'skip test in current test level') | |||||
| def test_run_with_dataset(self): | def test_run_with_dataset(self): | ||||
| model = Model.from_pretrained(self.model_id) | model = Model.from_pretrained(self.model_id) | ||||
| preprocessor = SequenceClassificationPreprocessor( | preprocessor = SequenceClassificationPreprocessor( | ||||
| @@ -8,6 +8,7 @@ from modelscope.models.nlp import PalmForTextGenerationModel | |||||
| from modelscope.pipelines import TextGenerationPipeline, pipeline | from modelscope.pipelines import TextGenerationPipeline, pipeline | ||||
| from modelscope.preprocessors import TextGenerationPreprocessor | from modelscope.preprocessors import TextGenerationPreprocessor | ||||
| from modelscope.utils.constant import Tasks | from modelscope.utils.constant import Tasks | ||||
| from modelscope.utils.test_utils import test_level | |||||
| class TextGenerationTest(unittest.TestCase): | class TextGenerationTest(unittest.TestCase): | ||||
| @@ -15,7 +16,7 @@ class TextGenerationTest(unittest.TestCase): | |||||
| input1 = "今日天气类型='晴'&温度变化趋势='大幅上升'&最低气温='28℃'&最高气温='31℃'&体感='湿热'" | input1 = "今日天气类型='晴'&温度变化趋势='大幅上升'&最低气温='28℃'&最高气温='31℃'&体感='湿热'" | ||||
| input2 = "今日天气类型='多云'&体感='舒适'&最低气温='26℃'&最高气温='30℃'" | input2 = "今日天气类型='多云'&体感='舒适'&最低气温='26℃'&最高气温='30℃'" | ||||
| @unittest.skip('skip temporarily to save test time') | |||||
| @unittest.skipUnless(test_level() >= 2, 'skip test in current test level') | |||||
| def test_run(self): | def test_run(self): | ||||
| cache_path = snapshot_download(self.model_id) | cache_path = snapshot_download(self.model_id) | ||||
| preprocessor = TextGenerationPreprocessor( | preprocessor = TextGenerationPreprocessor( | ||||
| @@ -29,6 +30,7 @@ class TextGenerationTest(unittest.TestCase): | |||||
| print() | print() | ||||
| print(f'input: {self.input2}\npipeline2: {pipeline2(self.input2)}') | print(f'input: {self.input2}\npipeline2: {pipeline2(self.input2)}') | ||||
| @unittest.skipUnless(test_level() >= 1, 'skip test in current test level') | |||||
| def test_run_with_model_from_modelhub(self): | def test_run_with_model_from_modelhub(self): | ||||
| model = Model.from_pretrained(self.model_id) | model = Model.from_pretrained(self.model_id) | ||||
| preprocessor = TextGenerationPreprocessor( | preprocessor = TextGenerationPreprocessor( | ||||
| @@ -37,11 +39,13 @@ class TextGenerationTest(unittest.TestCase): | |||||
| task=Tasks.text_generation, model=model, preprocessor=preprocessor) | task=Tasks.text_generation, model=model, preprocessor=preprocessor) | ||||
| print(pipeline_ins(self.input1)) | print(pipeline_ins(self.input1)) | ||||
| @unittest.skipUnless(test_level() >= 1, 'skip test in current test level') | |||||
| def test_run_with_model_name(self): | def test_run_with_model_name(self): | ||||
| pipeline_ins = pipeline( | pipeline_ins = pipeline( | ||||
| task=Tasks.text_generation, model=self.model_id) | task=Tasks.text_generation, model=self.model_id) | ||||
| print(pipeline_ins(self.input2)) | print(pipeline_ins(self.input2)) | ||||
| @unittest.skipUnless(test_level() >= 1, 'skip test in current test level') | |||||
| def test_run_with_default_model(self): | def test_run_with_default_model(self): | ||||
| pipeline_ins = pipeline(task=Tasks.text_generation) | pipeline_ins = pipeline(task=Tasks.text_generation) | ||||
| print(pipeline_ins(self.input2)) | print(pipeline_ins(self.input2)) | ||||
| @@ -7,6 +7,11 @@ import sys | |||||
| import unittest | import unittest | ||||
| from fnmatch import fnmatch | from fnmatch import fnmatch | ||||
| from modelscope.utils.logger import get_logger | |||||
| from modelscope.utils.test_utils import set_test_level, test_level | |||||
| logger = get_logger() | |||||
| def gather_test_cases(test_dir, pattern, list_tests): | def gather_test_cases(test_dir, pattern, list_tests): | ||||
| case_list = [] | case_list = [] | ||||
| @@ -49,5 +54,9 @@ if __name__ == '__main__': | |||||
| '--pattern', default='test_*.py', help='test file pattern') | '--pattern', default='test_*.py', help='test file pattern') | ||||
| parser.add_argument( | parser.add_argument( | ||||
| '--test_dir', default='tests', help='directory to be tested') | '--test_dir', default='tests', help='directory to be tested') | ||||
| parser.add_argument( | |||||
| '--level', default=0, help='2 -- all, 1 -- p1, 0 -- p0') | |||||
| args = parser.parse_args() | args = parser.parse_args() | ||||
| set_test_level(args.level) | |||||
| logger.info(f'TEST LEVEL: {test_level()}') | |||||
| main(args) | main(args) | ||||