mirror of
https://github.com/modelscope/modelscope.git
synced 2025-12-16 16:27:45 +01:00
1. Abstract keys of dicts needed by nlp metric classes into the init method
2. Add Preprocessor.save_pretrained to save preprocessor information
3. Abstract the config saving function, which can lead to normally saving in the direct call of from_pretrained, and the modification of cfg one by one when training.
4. Remove SbertTokenizer and VecoTokenizer, use transformers' tokenizers instead
5. Use model/preprocessor's from_pretrained in all nlp pipeline classes.
6. Add model_kwargs and preprocessor_kwargs in all nlp pipeline classes
7. Add base classes for fill-mask and text-classification preprocessor, as a demo for later changes
8. Fix user feedback: Re-train the model in continue training scenario
9. Fix user feedback: Too many checkpoint saved
10. Simplify the nlp-trainer
11. Fix user feedback: Split the default trainer's __init__ method, which makes user easier to override
12. Add safe_get to Config class
---------------------------- Another refactor from version 36 -------------------------
13. Name all nlp transformers' preprocessors from TaskNamePreprocessor to TaskNameTransformersPreprocessor, for example:
TextClassificationPreprocessor -> TextClassificationTransformersPreprocessor
14. Add a base class per task for all nlp tasks' preprocessors which has at least two sub-preprocessors
15. Add output classes of nlp models
16. Refactor the logic for token-classification
17. Fix bug: checkpoint_hook does not support pytorch_model.pt
18. Fix bug: Pipeline name does not match with task name, so inference will not succeed after training
NOTE: This is just a stop bleeding solution, the root cause is the uncertainty of the relationship between models and pipelines
Link: https://code.alibaba-inc.com/Ali-MaaS/MaaS-lib/codereview/10723513
* add save_pretrained to preprocessor
* save preprocessor config in hook
* refactor label-id mapping fetching logic
* test ok on sentence-similarity
* run on finetuning
* fix bug
* pre-commit passed
* fix bug
* Merge branch 'master' into feat/refactor_config
# Conflicts:
# modelscope/preprocessors/nlp/nlp_base.py
* add params to init
* 1. support max ckpt num 2. support ignoring others but bin file in continue training 3. add arguments to some nlp metrics
* Split trainer init impls to overridable methods
* remove some obsolete tokenizers
* unfinished
* support input params in pipeline
* fix bugs
* fix ut bug
* fix bug
* fix ut bug
* fix ut bug
* fix ut bug
* add base class for some preprocessors
* Merge commit '379867739548f394d0fa349ba07afe04adf4c8b6' into feat/refactor_config
* compatible with old code
* fix ut bug
* fix ut bugs
* fix bug
* add some comments
* fix ut bug
* add a requirement
* fix pre-commit
* Merge commit '0451b3d3cb2bebfef92ec2c227b2a3dd8d01dc6a' into feat/refactor_config
* fixbug
* Support function type in registry
* fix ut bug
* fix bug
* Merge commit '5f719e542b963f0d35457e5359df879a5eb80b82' into feat/refactor_config
# Conflicts:
# modelscope/pipelines/nlp/multilingual_word_segmentation_pipeline.py
# modelscope/pipelines/nlp/named_entity_recognition_pipeline.py
# modelscope/pipelines/nlp/word_segmentation_pipeline.py
# modelscope/utils/hub.py
* remove obsolete file
* rename init args
* rename params
* fix merge bug
* add default preprocessor config for ner-model
* move a method a util file
* remove unused config
* Fix a bug in pbar
* bestckptsaver:change default ckpt numbers to 1
* 1. Add assert to max_epoch 2. split init_dist and get_device 3. change cmp func name
* Fix bug
* fix bug
* fix bug
* unfinished refactoring
* unfinished
* uw
* uw
* uw
* uw
* Merge branch 'feat/refactor_config' into feat/refactor_trainer
# Conflicts:
# modelscope/preprocessors/nlp/document_segmentation_preprocessor.py
# modelscope/preprocessors/nlp/faq_question_answering_preprocessor.py
# modelscope/preprocessors/nlp/relation_extraction_preprocessor.py
# modelscope/preprocessors/nlp/text_generation_preprocessor.py
* uw
* uw
* unify nlp task outputs
* uw
* uw
* uw
* uw
* change the order of text cls pipeline
* refactor t5
* refactor tg task preprocessor
* fix
* unfinished
* temp
* refactor code
* unfinished
* unfinished
* unfinished
* unfinished
* uw
* Merge branch 'feat/refactor_config' into feat/refactor_trainer
* smoke test pass
* ut testing
* pre-commit passed
* Merge branch 'master' into feat/refactor_config
# Conflicts:
# modelscope/models/nlp/bert/document_segmentation.py
# modelscope/pipelines/nlp/__init__.py
# modelscope/pipelines/nlp/document_segmentation_pipeline.py
* merge master
* unifnished
* Merge branch 'feat/fix_bug_pipeline_name' into feat/refactor_config
* fix bug
* fix ut bug
* support ner batch inference
* fix ut bug
* fix bug
* support batch inference on three nlp tasks
* unfinished
* fix bug
* fix bug
* Merge branch 'master' into feat/refactor_config
# Conflicts:
# modelscope/models/base/base_model.py
# modelscope/pipelines/nlp/conversational_text_to_sql_pipeline.py
# modelscope/pipelines/nlp/dialog_intent_prediction_pipeline.py
# modelscope/pipelines/nlp/dialog_modeling_pipeline.py
# modelscope/pipelines/nlp/dialog_state_tracking_pipeline.py
# modelscope/pipelines/nlp/document_segmentation_pipeline.py
# modelscope/pipelines/nlp/faq_question_answering_pipeline.py
# modelscope/pipelines/nlp/feature_extraction_pipeline.py
# modelscope/pipelines/nlp/fill_mask_pipeline.py
# modelscope/pipelines/nlp/information_extraction_pipeline.py
# modelscope/pipelines/nlp/named_entity_recognition_pipeline.py
# modelscope/pipelines/nlp/sentence_embedding_pipeline.py
# modelscope/pipelines/nlp/summarization_pipeline.py
# modelscope/pipelines/nlp/table_question_answering_pipeline.py
# modelscope/pipelines/nlp/text2text_generation_pipeline.py
# modelscope/pipelines/nlp/text_classification_pipeline.py
# modelscope/pipelines/nlp/text_error_correction_pipeline.py
# modelscope/pipelines/nlp/text_generation_pipeline.py
# modelscope/pipelines/nlp/text_ranking_pipeline.py
# modelscope/pipelines/nlp/token_classification_pipeline.py
# modelscope/pipelines/nlp/word_segmentation_pipeline.py
# modelscope/pipelines/nlp/zero_shot_classification_pipeline.py
# modelscope/trainers/nlp_trainer.py
* pre-commit passed
* fix bug
* Merge branch 'master' into feat/refactor_config
# Conflicts:
# modelscope/preprocessors/__init__.py
* fix bug
* fix bug
* fix bug
* fix bug
* fix bug
* fixbug
* pre-commit passed
* fix bug
* fixbug
* fix bug
* fix bug
* fix bug
* fix bug
* self review done
* fixbug
* fix bug
* fix bug
* fix bugs
* remove sub-token offset mapping
* fix name bug
* add some tests
* 1. support batch inference of text-generation,text2text-generation,token-classification,text-classification 2. add corresponding UTs
* add old logic back
* tmp save
* add tokenize by words logic back
* move outputs file back
* revert veco token-classification back
* fix typo
* Fix description
* Merge commit '4dd99b8f6e4e7aefe047c68a1bedd95d3ec596d6' into feat/refactor_config
* Merge branch 'master' into feat/refactor_config
# Conflicts:
# modelscope/pipelines/builder.py
569 lines
20 KiB
Python
569 lines
20 KiB
Python
#!/usr/bin/env python
|
|
# Copyright (c) Alibaba, Inc. and its affiliates.
|
|
|
|
import argparse
|
|
import datetime
|
|
import math
|
|
import multiprocessing
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import tempfile
|
|
import time
|
|
import unittest
|
|
from fnmatch import fnmatch
|
|
from multiprocessing.managers import BaseManager
|
|
from pathlib import Path
|
|
from turtle import shape
|
|
from unittest import TestResult, TextTestResult
|
|
|
|
import pandas
|
|
# NOTICE: Tensorflow 1.15 seems not so compatible with pytorch.
|
|
# A segmentation fault may be raise by pytorch cpp library
|
|
# if 'import tensorflow' in front of 'import torch'.
|
|
# Puting a 'import torch' here can bypass this incompatibility.
|
|
import torch
|
|
import yaml
|
|
|
|
from modelscope.utils.logger import get_logger
|
|
from modelscope.utils.model_tag import ModelTag, commit_model_ut_result
|
|
from modelscope.utils.test_utils import (get_case_model_info, set_test_level,
|
|
test_level)
|
|
|
|
logger = get_logger()
|
|
|
|
|
|
def test_cases_result_to_df(result_list):
|
|
table_header = [
|
|
'Name', 'Result', 'Info', 'Start time', 'Stop time',
|
|
'Time cost(seconds)'
|
|
]
|
|
df = pandas.DataFrame(
|
|
result_list, columns=table_header).sort_values(
|
|
by=['Start time'], ascending=True)
|
|
return df
|
|
|
|
|
|
def statistics_test_result(df):
|
|
total_cases = df.shape[0]
|
|
# yapf: disable
|
|
success_cases = df.loc[df['Result'] == 'Success'].shape[0]
|
|
error_cases = df.loc[df['Result'] == 'Error'].shape[0]
|
|
failures_cases = df.loc[df['Result'] == 'Failures'].shape[0]
|
|
expected_failure_cases = df.loc[df['Result'] == 'ExpectedFailures'].shape[0]
|
|
unexpected_success_cases = df.loc[df['Result'] == 'UnexpectedSuccesses'].shape[0]
|
|
skipped_cases = df.loc[df['Result'] == 'Skipped'].shape[0]
|
|
# yapf: enable
|
|
|
|
if failures_cases > 0 or \
|
|
error_cases > 0 or \
|
|
unexpected_success_cases > 0:
|
|
final_result = 'FAILED'
|
|
else:
|
|
final_result = 'SUCCESS'
|
|
result_msg = '%s (Runs=%s,success=%s,failures=%s,errors=%s,\
|
|
skipped=%s,expected failures=%s,unexpected successes=%s)' % (
|
|
final_result, total_cases, success_cases, failures_cases, error_cases,
|
|
skipped_cases, expected_failure_cases, unexpected_success_cases)
|
|
|
|
model_cases = get_case_model_info()
|
|
for model_name, case_info in model_cases.items():
|
|
cases = df.loc[df['Name'].str.contains('|'.join(list(case_info)))]
|
|
results = cases['Result']
|
|
result = None
|
|
if any(results == 'Error') or any(results == 'Failures') or any(
|
|
results == 'UnexpectedSuccesses'):
|
|
result = ModelTag.MODEL_FAIL
|
|
elif any(results == 'Success'):
|
|
result = ModelTag.MODEL_PASS
|
|
elif all(results == 'Skipped'):
|
|
result = ModelTag.MODEL_SKIP
|
|
else:
|
|
print(f'invalid results for {model_name} \n{result}')
|
|
|
|
if result is not None:
|
|
commit_model_ut_result(model_name, result)
|
|
print('Testing result summary.')
|
|
print(result_msg)
|
|
if final_result == 'FAILED':
|
|
sys.exit(1)
|
|
|
|
|
|
def gather_test_suites_in_files(test_dir, case_file_list, list_tests):
|
|
test_suite = unittest.TestSuite()
|
|
for case in case_file_list:
|
|
test_case = unittest.defaultTestLoader.discover(
|
|
start_dir=test_dir, pattern=case)
|
|
test_suite.addTest(test_case)
|
|
if hasattr(test_case, '__iter__'):
|
|
for subcase in test_case:
|
|
if list_tests:
|
|
print(subcase)
|
|
else:
|
|
if list_tests:
|
|
print(test_case)
|
|
return test_suite
|
|
|
|
|
|
def gather_test_suites_files(test_dir, pattern):
|
|
case_file_list = []
|
|
for dirpath, dirnames, filenames in os.walk(test_dir):
|
|
for file in filenames:
|
|
if fnmatch(file, pattern):
|
|
case_file_list.append(file)
|
|
|
|
return case_file_list
|
|
|
|
|
|
def collect_test_results(case_results):
|
|
result_list = [
|
|
] # each item is Case, Result, Start time, Stop time, Time cost
|
|
for case_result in case_results.successes:
|
|
result_list.append(
|
|
(case_result.test_full_name, 'Success', '', case_result.start_time,
|
|
case_result.stop_time, case_result.time_cost))
|
|
for case_result in case_results.errors:
|
|
result_list.append(
|
|
(case_result[0].test_full_name, 'Error', case_result[1],
|
|
case_result[0].start_time, case_result[0].stop_time,
|
|
case_result[0].time_cost))
|
|
for case_result in case_results.skipped:
|
|
result_list.append(
|
|
(case_result[0].test_full_name, 'Skipped', case_result[1],
|
|
case_result[0].start_time, case_result[0].stop_time,
|
|
case_result[0].time_cost))
|
|
for case_result in case_results.expectedFailures:
|
|
result_list.append(
|
|
(case_result[0].test_full_name, 'ExpectedFailures', case_result[1],
|
|
case_result[0].start_time, case_result[0].stop_time,
|
|
case_result[0].time_cost))
|
|
for case_result in case_results.failures:
|
|
result_list.append(
|
|
(case_result[0].test_full_name, 'Failures', case_result[1],
|
|
case_result[0].start_time, case_result[0].stop_time,
|
|
case_result[0].time_cost))
|
|
for case_result in case_results.unexpectedSuccesses:
|
|
result_list.append((case_result.test_full_name, 'UnexpectedSuccesses',
|
|
'', case_result.start_time, case_result.stop_time,
|
|
case_result.time_cost))
|
|
return result_list
|
|
|
|
|
|
def run_command_with_popen(cmd):
|
|
with subprocess.Popen(
|
|
cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
bufsize=1,
|
|
encoding='utf8') as sub_process:
|
|
for line in iter(sub_process.stdout.readline, ''):
|
|
sys.stdout.write(line)
|
|
|
|
|
|
def async_run_command_with_popen(cmd, device_id):
|
|
logger.info('Worker id: %s args: %s' % (device_id, cmd))
|
|
env = os.environ.copy()
|
|
env['CUDA_VISIBLE_DEVICES'] = '%s' % device_id
|
|
sub_process = subprocess.Popen(
|
|
cmd,
|
|
stdout=subprocess.PIPE,
|
|
stderr=subprocess.STDOUT,
|
|
bufsize=1,
|
|
universal_newlines=True,
|
|
env=env,
|
|
encoding='utf8')
|
|
return sub_process
|
|
|
|
|
|
def save_test_result(df, args):
|
|
if args.result_dir is not None:
|
|
file_name = str(int(datetime.datetime.now().timestamp() * 1000))
|
|
os.umask(0)
|
|
Path(args.result_dir).mkdir(mode=0o777, parents=True, exist_ok=True)
|
|
Path(os.path.join(args.result_dir, file_name)).touch(
|
|
mode=0o666, exist_ok=True)
|
|
df.to_pickle(os.path.join(args.result_dir, file_name))
|
|
|
|
|
|
def run_command(cmd):
|
|
logger.info('Running command: %s' % ' '.join(cmd))
|
|
response = subprocess.run(
|
|
cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
|
|
try:
|
|
response.check_returncode()
|
|
logger.info(response.stdout.decode('utf8'))
|
|
except subprocess.CalledProcessError as error:
|
|
logger.error(
|
|
'stdout: %s, stderr: %s' %
|
|
(response.stdout.decode('utf8'), error.stderr.decode('utf8')))
|
|
|
|
|
|
def install_packages(pkgs):
|
|
cmd = [sys.executable, '-m', 'pip', 'install']
|
|
for pkg in pkgs:
|
|
cmd.append(pkg)
|
|
|
|
run_command(cmd)
|
|
|
|
|
|
def install_requirements(requirements):
|
|
for req in requirements:
|
|
cmd = [
|
|
sys.executable, '-m', 'pip', 'install', '-r',
|
|
'requirements/%s' % req, '-f',
|
|
'https://modelscope.oss-cn-beijing.aliyuncs.com/releases/repo.html'
|
|
]
|
|
run_command(cmd)
|
|
|
|
|
|
def wait_for_free_worker(workers):
|
|
while True:
|
|
for idx, worker in enumerate(workers):
|
|
if worker is None:
|
|
logger.info('return free worker: %s' % (idx))
|
|
return idx
|
|
if worker.poll() is None: # running, get output
|
|
for line in iter(worker.stdout.readline, ''):
|
|
if line != '':
|
|
sys.stdout.write(line)
|
|
else:
|
|
break
|
|
else: # worker process completed.
|
|
logger.info('Process end: %s' % (idx))
|
|
workers[idx] = None
|
|
return idx
|
|
time.sleep(0.001)
|
|
|
|
|
|
def wait_for_workers(workers):
|
|
while True:
|
|
for idx, worker in enumerate(workers):
|
|
if worker is None:
|
|
continue
|
|
# check worker is completed.
|
|
if worker.poll() is None:
|
|
for line in iter(worker.stdout.readline, ''):
|
|
if line != '':
|
|
sys.stdout.write(line)
|
|
else:
|
|
break
|
|
else:
|
|
logger.info('Process idx: %s end!' % (idx))
|
|
workers[idx] = None
|
|
|
|
is_all_completed = True
|
|
for idx, worker in enumerate(workers):
|
|
if worker is not None:
|
|
is_all_completed = False
|
|
break
|
|
|
|
if is_all_completed:
|
|
logger.info('All sub porcess is completed!')
|
|
break
|
|
time.sleep(0.001)
|
|
|
|
|
|
def parallel_run_case_in_env(env_name, env, test_suite_env_map, isolated_cases,
|
|
result_dir, parallel):
|
|
logger.info('Running case in env: %s' % env_name)
|
|
# install requirements and deps # run_config['envs'][env]
|
|
if 'requirements' in env:
|
|
install_requirements(env['requirements'])
|
|
if 'dependencies' in env:
|
|
install_packages(env['dependencies'])
|
|
# case worker processes
|
|
worker_processes = [None] * parallel
|
|
for test_suite_file in isolated_cases: # run case in subprocess
|
|
if test_suite_file in test_suite_env_map and test_suite_env_map[
|
|
test_suite_file] == env_name:
|
|
cmd = [
|
|
'python',
|
|
'tests/run.py',
|
|
'--pattern',
|
|
test_suite_file,
|
|
'--result_dir',
|
|
result_dir,
|
|
]
|
|
worker_idx = wait_for_free_worker(worker_processes)
|
|
worker_process = async_run_command_with_popen(cmd, worker_idx)
|
|
os.set_blocking(worker_process.stdout.fileno(), False)
|
|
worker_processes[worker_idx] = worker_process
|
|
else:
|
|
pass # case not in run list.
|
|
|
|
# run remain cases in a process.
|
|
remain_suite_files = []
|
|
for k, v in test_suite_env_map.items():
|
|
if k not in isolated_cases and v == env_name:
|
|
remain_suite_files.append(k)
|
|
if len(remain_suite_files) == 0:
|
|
wait_for_workers(worker_processes)
|
|
return
|
|
# roughly split case in parallel
|
|
part_count = math.ceil(len(remain_suite_files) / parallel)
|
|
suites_chunks = [
|
|
remain_suite_files[x:x + part_count]
|
|
for x in range(0, len(remain_suite_files), part_count)
|
|
]
|
|
for suites_chunk in suites_chunks:
|
|
worker_idx = wait_for_free_worker(worker_processes)
|
|
cmd = [
|
|
'python', 'tests/run.py', '--result_dir', result_dir, '--suites'
|
|
]
|
|
for suite in suites_chunk:
|
|
cmd.append(suite)
|
|
worker_process = async_run_command_with_popen(cmd, worker_idx)
|
|
os.set_blocking(worker_process.stdout.fileno(), False)
|
|
worker_processes[worker_idx] = worker_process
|
|
|
|
wait_for_workers(worker_processes)
|
|
|
|
|
|
def run_case_in_env(env_name, env, test_suite_env_map, isolated_cases,
|
|
result_dir):
|
|
# install requirements and deps # run_config['envs'][env]
|
|
if 'requirements' in env:
|
|
install_requirements(env['requirements'])
|
|
if 'dependencies' in env:
|
|
install_packages(env['dependencies'])
|
|
|
|
for test_suite_file in isolated_cases: # run case in subprocess
|
|
if test_suite_file in test_suite_env_map and test_suite_env_map[
|
|
test_suite_file] == env_name:
|
|
cmd = [
|
|
'python',
|
|
'tests/run.py',
|
|
'--pattern',
|
|
test_suite_file,
|
|
'--result_dir',
|
|
result_dir,
|
|
]
|
|
run_command_with_popen(cmd)
|
|
else:
|
|
pass # case not in run list.
|
|
|
|
# run remain cases in a process.
|
|
remain_suite_files = []
|
|
for k, v in test_suite_env_map.items():
|
|
if k not in isolated_cases and v == env_name:
|
|
remain_suite_files.append(k)
|
|
if len(remain_suite_files) == 0:
|
|
return
|
|
cmd = ['python', 'tests/run.py', '--result_dir', result_dir, '--suites']
|
|
for suite in remain_suite_files:
|
|
cmd.append(suite)
|
|
run_command_with_popen(cmd)
|
|
|
|
|
|
def run_in_subprocess(args):
|
|
# only case args.isolated_cases run in subporcess, all other run in a subprocess
|
|
test_suite_files = gather_test_suites_files(
|
|
os.path.abspath(args.test_dir), args.pattern)
|
|
run_config = None
|
|
isolated_cases = []
|
|
test_suite_env_map = {}
|
|
# put all the case in default env.
|
|
for test_suite_file in test_suite_files:
|
|
test_suite_env_map[test_suite_file] = 'default'
|
|
|
|
if args.run_config is not None and Path(args.run_config).exists():
|
|
with open(args.run_config, encoding='utf-8') as f:
|
|
run_config = yaml.load(f, Loader=yaml.FullLoader)
|
|
if 'isolated' in run_config:
|
|
isolated_cases = run_config['isolated']
|
|
|
|
if 'envs' in run_config:
|
|
for env in run_config['envs']:
|
|
if env != 'default':
|
|
for test_suite in run_config['envs'][env]['tests']:
|
|
if test_suite in test_suite_env_map:
|
|
test_suite_env_map[test_suite] = env
|
|
|
|
if args.subprocess: # run all case in subprocess
|
|
isolated_cases = test_suite_files
|
|
|
|
with tempfile.TemporaryDirectory() as temp_result_dir:
|
|
for env in set(test_suite_env_map.values()):
|
|
parallel_run_case_in_env(env, run_config['envs'][env],
|
|
test_suite_env_map, isolated_cases,
|
|
temp_result_dir, args.parallel)
|
|
|
|
result_dfs = []
|
|
result_path = Path(temp_result_dir)
|
|
for result in result_path.iterdir():
|
|
if Path.is_file(result):
|
|
df = pandas.read_pickle(result)
|
|
result_dfs.append(df)
|
|
result_pd = pandas.concat(
|
|
result_dfs) # merge result of every test suite.
|
|
print_table_result(result_pd)
|
|
print_abnormal_case_info(result_pd)
|
|
statistics_test_result(result_pd)
|
|
|
|
|
|
def get_object_full_name(obj):
|
|
klass = obj.__class__
|
|
module = klass.__module__
|
|
if module == 'builtins':
|
|
return klass.__qualname__
|
|
return module + '.' + klass.__qualname__
|
|
|
|
|
|
class TimeCostTextTestResult(TextTestResult):
|
|
"""Record test case time used!"""
|
|
|
|
def __init__(self, stream, descriptions, verbosity):
|
|
self.successes = []
|
|
return super(TimeCostTextTestResult,
|
|
self).__init__(stream, descriptions, verbosity)
|
|
|
|
def startTest(self, test):
|
|
test.start_time = datetime.datetime.now()
|
|
test.test_full_name = get_object_full_name(
|
|
test) + '.' + test._testMethodName
|
|
self.stream.writeln('Test case: %s start at: %s' %
|
|
(test.test_full_name, test.start_time))
|
|
|
|
return super(TimeCostTextTestResult, self).startTest(test)
|
|
|
|
def stopTest(self, test):
|
|
TextTestResult.stopTest(self, test)
|
|
test.stop_time = datetime.datetime.now()
|
|
test.time_cost = (test.stop_time - test.start_time).total_seconds()
|
|
self.stream.writeln(
|
|
'Test case: %s stop at: %s, cost time: %s(seconds)' %
|
|
(test.test_full_name, test.stop_time, test.time_cost))
|
|
if torch.cuda.is_available(
|
|
) and test.time_cost > 5.0: # print nvidia-smi
|
|
cmd = ['nvidia-smi']
|
|
run_command_with_popen(cmd)
|
|
super(TimeCostTextTestResult, self).stopTest(test)
|
|
|
|
def addSuccess(self, test):
|
|
self.successes.append(test)
|
|
super(TextTestResult, self).addSuccess(test)
|
|
|
|
|
|
class TimeCostTextTestRunner(unittest.runner.TextTestRunner):
|
|
resultclass = TimeCostTextTestResult
|
|
|
|
def run(self, test):
|
|
return super(TimeCostTextTestRunner, self).run(test)
|
|
|
|
def _makeResult(self):
|
|
result = super(TimeCostTextTestRunner, self)._makeResult()
|
|
return result
|
|
|
|
|
|
def gather_test_cases(test_dir, pattern, list_tests):
|
|
case_list = []
|
|
for dirpath, dirnames, filenames in os.walk(test_dir):
|
|
for file in filenames:
|
|
if fnmatch(file, pattern):
|
|
case_list.append(file)
|
|
|
|
test_suite = unittest.TestSuite()
|
|
|
|
for case in case_list:
|
|
test_case = unittest.defaultTestLoader.discover(
|
|
start_dir=test_dir, pattern=case)
|
|
test_suite.addTest(test_case)
|
|
if hasattr(test_case, '__iter__'):
|
|
for subcase in test_case:
|
|
if list_tests:
|
|
print(subcase)
|
|
else:
|
|
if list_tests:
|
|
print(test_case)
|
|
return test_suite
|
|
|
|
|
|
def print_abnormal_case_info(df):
|
|
df = df.loc[(df['Result'] == 'Error') | (df['Result'] == 'Failures')]
|
|
for _, row in df.iterrows():
|
|
print('Case %s run result: %s, msg:\n%s' %
|
|
(row['Name'], row['Result'], row['Info']))
|
|
|
|
|
|
def print_table_result(df):
|
|
df = df.loc[df['Result'] != 'Skipped']
|
|
df = df.drop('Info', axis=1)
|
|
formatters = {
|
|
'Name': '{{:<{}s}}'.format(df['Name'].str.len().max()).format,
|
|
'Result': '{{:<{}s}}'.format(df['Result'].str.len().max()).format,
|
|
}
|
|
with pandas.option_context('display.max_rows', None, 'display.max_columns',
|
|
None, 'display.width', None):
|
|
print(df.to_string(justify='left', formatters=formatters, index=False))
|
|
|
|
|
|
def main(args):
|
|
runner = TimeCostTextTestRunner()
|
|
if args.suites is not None and len(args.suites) > 0:
|
|
logger.info('Running: %s' % ' '.join(args.suites))
|
|
test_suite = gather_test_suites_in_files(args.test_dir, args.suites,
|
|
args.list_tests)
|
|
else:
|
|
test_suite = gather_test_cases(
|
|
os.path.abspath(args.test_dir), args.pattern, args.list_tests)
|
|
if not args.list_tests:
|
|
result = runner.run(test_suite)
|
|
logger.info('Running case completed, pid: %s, suites: %s' %
|
|
(os.getpid(), args.suites))
|
|
result = collect_test_results(result)
|
|
df = test_cases_result_to_df(result)
|
|
if args.result_dir is not None:
|
|
save_test_result(df, args)
|
|
else:
|
|
print_table_result(df)
|
|
print_abnormal_case_info(df)
|
|
statistics_test_result(df)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
parser = argparse.ArgumentParser('test runner')
|
|
parser.add_argument(
|
|
'--list_tests', action='store_true', help='list all tests')
|
|
parser.add_argument(
|
|
'--pattern', default='test_*.py', help='test file pattern')
|
|
parser.add_argument(
|
|
'--test_dir', default='tests', help='directory to be tested')
|
|
parser.add_argument(
|
|
'--level', default=0, type=int, help='2 -- all, 1 -- p1, 0 -- p0')
|
|
parser.add_argument(
|
|
'--disable_profile', action='store_true', help='disable profiling')
|
|
parser.add_argument(
|
|
'--run_config',
|
|
default=None,
|
|
help='specified case run config file(yaml file)')
|
|
parser.add_argument(
|
|
'--subprocess',
|
|
action='store_true',
|
|
help='run all test suite in subprocess')
|
|
parser.add_argument(
|
|
'--result_dir',
|
|
default=None,
|
|
help='Save result to directory, internal use only')
|
|
parser.add_argument(
|
|
'--parallel',
|
|
default=1,
|
|
type=int,
|
|
help='Set case parallels, default single process, set with gpu number.'
|
|
)
|
|
parser.add_argument(
|
|
'--suites',
|
|
nargs='*',
|
|
help='Run specified test suites(test suite files list split by space)')
|
|
args = parser.parse_args()
|
|
set_test_level(2)
|
|
os.environ['REGRESSION_BASELINE'] = '1'
|
|
logger.info(f'TEST LEVEL: {test_level()}')
|
|
if not args.disable_profile:
|
|
from utils import profiler
|
|
logger.info('enable profile ...')
|
|
profiler.enable()
|
|
if args.run_config is not None or args.subprocess:
|
|
run_in_subprocess(args)
|
|
else:
|
|
main(args)
|