Skip to content

Commit cac5e64

Browse files
committed
test(zoo): use pytest.parametrize to parallelize zoo tests
Also only run zoo tests for the latest Python version
1 parent f01b6f3 commit cac5e64

File tree

3 files changed

+101
-77
lines changed

3 files changed

+101
-77
lines changed

.github/workflows/tests.yml

+44-5
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,6 @@ jobs:
3737
sudo apt-get install espeak espeak-ng
3838
- name: Install dependencies
3939
run: |
40-
sudo apt-get update
4140
sudo apt-get install -y --no-install-recommends git make gcc
4241
make system-deps
4342
- name: Install custom Trainer and/or Coqpit if requested
@@ -68,21 +67,20 @@ jobs:
6867
fail-fast: false
6968
matrix:
7069
python-version: ["3.9", "3.12"]
71-
subset: ["test_tts", "test_tts2", "test_vocoder", "test_xtts", "test_zoo0", "test_zoo1", "test_zoo2"]
70+
subset: ["test_tts", "test_tts2", "test_vocoder", "test_xtts"]
7271
steps:
7372
- uses: actions/checkout@v4
7473
- name: Setup uv
7574
uses: ./.github/actions/setup-uv
7675
- name: Set up Python ${{ matrix.python-version }}
7776
run: uv python install ${{ matrix.python-version }}
7877
- name: Install Espeak
79-
if: contains(fromJSON('["test_tts", "test_tts2", "test_xtts", "test_zoo0", "test_zoo1", "test_zoo2"]'), matrix.subset)
78+
if: contains(fromJSON('["test_tts", "test_tts2", "test_xtts"]'), matrix.subset)
8079
run: |
8180
sudo apt-get update
8281
sudo apt-get install espeak espeak-ng
8382
- name: Install dependencies
8483
run: |
85-
sudo apt-get update
8684
sudo apt-get install -y --no-install-recommends git make gcc
8785
make system-deps
8886
- name: Install custom Trainer and/or Coqpit if requested
@@ -107,9 +105,50 @@ jobs:
107105
name: coverage-data-${{ matrix.subset }}-${{ matrix.python-version }}
108106
path: .coverage.*
109107
if-no-files-found: ignore
108+
zoo:
109+
runs-on: ubuntu-latest
110+
strategy:
111+
fail-fast: false
112+
matrix:
113+
python-version: ["3.12"]
114+
partition: ["0", "1", "2"]
115+
steps:
116+
- uses: actions/checkout@v4
117+
- name: Setup uv
118+
uses: ./.github/actions/setup-uv
119+
- name: Set up Python ${{ matrix.python-version }}
120+
run: uv python install ${{ matrix.python-version }}
121+
- name: Install Espeak
122+
run: |
123+
sudo apt-get update
124+
sudo apt-get install espeak espeak-ng
125+
- name: Install dependencies
126+
run: |
127+
sudo apt-get install -y --no-install-recommends git make gcc
128+
make system-deps
129+
- name: Install custom Trainer and/or Coqpit if requested
130+
run: |
131+
if [[ -n "${{ github.event.inputs.trainer_branch }}" ]]; then
132+
uv add git+https://github.com/idiap/coqui-ai-Trainer --branch ${{ github.event.inputs.trainer_branch }}
133+
fi
134+
if [[ -n "${{ github.event.inputs.coqpit_branch }}" ]]; then
135+
uv add git+https://github.com/idiap/coqui-ai-coqpit --branch ${{ github.event.inputs.coqpit_branch }}
136+
fi
137+
- name: Zoo tests
138+
run: uv run --extra server --extra languages make test_zoo
139+
env:
140+
NUM_PARTITIONS: 3
141+
TEST_PARTITION: ${{ matrix.partition }}
142+
- name: Upload coverage data
143+
uses: actions/upload-artifact@v4
144+
with:
145+
include-hidden-files: true
146+
name: coverage-data-zoo-${{ matrix.partition }}
147+
path: .coverage.*
148+
if-no-files-found: ignore
110149
coverage:
111150
if: always()
112-
needs: [unit, integration]
151+
needs: [unit, integration, zoo]
113152
runs-on: ubuntu-latest
114153
steps:
115154
- uses: actions/checkout@v4

Makefile

+2-7
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,8 @@ test_aux: ## run aux tests.
2525
coverage run -m pytest -x -v --durations=0 tests/aux_tests
2626
./run_bash_tests.sh
2727

28-
test_zoo0: ## run zoo tests.
29-
coverage run -m pytest -x -v --durations=0 tests/zoo_tests/test_models.py \
30-
-k "test_models_offset_0_step_3 or test_voice_conversion"
31-
test_zoo1: ## run zoo tests.
32-
coverage run -m pytest -x -v --durations=0 tests/zoo_tests/test_models.py -k test_models_offset_1_step_3
33-
test_zoo2: ## run zoo tests.
34-
coverage run -m pytest -x -v --durations=0 tests/zoo_tests/test_models.py -k test_models_offset_2_step_3
28+
test_zoo: ## run zoo tests.
29+
coverage run -m pytest -x -v --durations=0 tests/zoo_tests/test_models.py
3530

3631
inference_tests: ## run inference tests.
3732
coverage run -m pytest -x -v --durations=0 tests/inference_tests

tests/zoo_tests/test_models.py

+55-65
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from trainer.io import get_user_data_dir
88

99
from tests import get_tests_data_path, run_cli
10+
from TTS.api import TTS
1011
from TTS.tts.utils.languages import LanguageManager
1112
from TTS.tts.utils.speakers import SpeakerManager
1213
from TTS.utils.manage import ModelManager
@@ -29,55 +30,61 @@ def run_around_tests(tmp_path):
2930
shutil.rmtree(tmp_path)
3031

3132

32-
def run_models(tmp_path, offset=0, step=1):
33-
"""Check if all the models are downloadable and tts models run correctly."""
34-
print(" > Run synthesizer with all the models.")
33+
@pytest.fixture
34+
def manager(tmp_path):
35+
"""Set up model manager."""
36+
return ModelManager(output_prefix=tmp_path, progress_bar=False)
37+
38+
39+
# To split tests into different CI jobs
40+
num_partitions = int(os.getenv("NUM_PARTITIONS", "1"))
41+
partition = int(os.getenv("TEST_PARTITION", "0"))
42+
model_names = [name for name in TTS.list_models() if name not in MODELS_WITH_SEP_TESTS]
43+
model_names = [name for i, name in enumerate(model_names) if i % num_partitions == partition]
44+
45+
46+
@pytest.mark.parametrize("model_name", model_names)
47+
def test_models(tmp_path, model_name, manager):
48+
print(f"\n > Run - {model_name}")
3549
output_path = tmp_path / "output.wav"
36-
manager = ModelManager(output_prefix=tmp_path, progress_bar=False)
37-
model_names = [name for name in manager.list_models() if name not in MODELS_WITH_SEP_TESTS]
38-
print("Model names:", model_names)
39-
for model_name in model_names[offset::step]:
40-
print(f"\n > Run - {model_name}")
41-
model_path, _, _ = manager.download_model(model_name)
42-
if "tts_models" in model_name:
43-
local_download_dir = model_path.parent
44-
# download and run the model
45-
speaker_files = list(local_download_dir.glob("speaker*"))
46-
language_files = list(local_download_dir.glob("language*"))
47-
speaker_arg = ""
48-
language_arg = ""
49-
if len(speaker_files) > 0:
50-
# multi-speaker model
51-
if "speaker_ids" in speaker_files[0].stem:
52-
speaker_manager = SpeakerManager(speaker_id_file_path=speaker_files[0])
53-
elif "speakers" in speaker_files[0].stem:
54-
speaker_manager = SpeakerManager(d_vectors_file_path=speaker_files[0])
55-
speakers = list(speaker_manager.name_to_id.keys())
56-
if len(speakers) > 1:
57-
speaker_arg = f'--speaker_idx "{speakers[0]}"'
58-
if len(language_files) > 0 and "language_ids" in language_files[0].stem:
59-
# multi-lingual model
60-
language_manager = LanguageManager(language_ids_file_path=language_files[0])
61-
languages = language_manager.language_names
62-
if len(languages) > 1:
63-
language_arg = f'--language_idx "{languages[0]}"'
64-
run_cli(
65-
f'tts --model_name {model_name} --text "This is an example." '
66-
f'--out_path "{output_path}" {speaker_arg} {language_arg} --no-progress_bar'
67-
)
68-
elif "voice_conversion_models" in model_name:
69-
speaker_wav = os.path.join(get_tests_data_path(), "ljspeech", "wavs", "LJ001-0001.wav")
70-
reference_wav = os.path.join(get_tests_data_path(), "ljspeech", "wavs", "LJ001-0032.wav")
71-
run_cli(
72-
f"tts --model_name {model_name} "
73-
f'--out_path "{output_path}" --source_wav "{speaker_wav}" --target_wav "{reference_wav}" --no-progress_bar'
74-
)
75-
else:
76-
# only download the model
77-
manager.download_model(model_name)
78-
# remove downloaded models
79-
shutil.rmtree(get_user_data_dir("tts"))
80-
print(f" | > OK: {model_name}")
50+
model_path, _, _ = manager.download_model(model_name)
51+
if "tts_models" in model_name:
52+
local_download_dir = model_path.parent
53+
# download and run the model
54+
speaker_files = list(local_download_dir.glob("speaker*"))
55+
language_files = list(local_download_dir.glob("language*"))
56+
speaker_arg = ""
57+
language_arg = ""
58+
if len(speaker_files) > 0:
59+
# multi-speaker model
60+
if "speaker_ids" in speaker_files[0].stem:
61+
speaker_manager = SpeakerManager(speaker_id_file_path=speaker_files[0])
62+
elif "speakers" in speaker_files[0].stem:
63+
speaker_manager = SpeakerManager(d_vectors_file_path=speaker_files[0])
64+
speakers = list(speaker_manager.name_to_id.keys())
65+
if len(speakers) > 1:
66+
speaker_arg = f'--speaker_idx "{speakers[0]}"'
67+
if len(language_files) > 0 and "language_ids" in language_files[0].stem:
68+
# multi-lingual model
69+
language_manager = LanguageManager(language_ids_file_path=language_files[0])
70+
languages = language_manager.language_names
71+
if len(languages) > 1:
72+
language_arg = f'--language_idx "{languages[0]}"'
73+
run_cli(
74+
f'tts --model_name {model_name} --text "This is an example." '
75+
f'--out_path "{output_path}" {speaker_arg} {language_arg} --no-progress_bar'
76+
)
77+
elif "voice_conversion_models" in model_name:
78+
speaker_wav = os.path.join(get_tests_data_path(), "ljspeech", "wavs", "LJ001-0001.wav")
79+
reference_wav = os.path.join(get_tests_data_path(), "ljspeech", "wavs", "LJ001-0032.wav")
80+
run_cli(
81+
f"tts --model_name {model_name} "
82+
f'--out_path "{output_path}" --source_wav "{speaker_wav}" --target_wav "{reference_wav}" --no-progress_bar'
83+
)
84+
else:
85+
# only download the model
86+
manager.download_model(model_name)
87+
print(f" | > OK: {model_name}")
8188

8289

8390
@pytest.mark.skipif(GITHUB_ACTIONS, reason="Model too big for CI")
@@ -263,20 +270,3 @@ def test_voice_conversion(tmp_path):
263270
f"tts --model_name {model_name}"
264271
f" --out_path {output_path} --speaker_wav {speaker_wav} --reference_wav {reference_wav} --language_idx {language_id} --no-progress_bar"
265272
)
266-
267-
268-
"""
269-
These are used to split tests into different actions on Github.
270-
"""
271-
272-
273-
def test_models_offset_0_step_3(tmp_path):
274-
run_models(tmp_path, offset=0, step=3)
275-
276-
277-
def test_models_offset_1_step_3(tmp_path):
278-
run_models(tmp_path, offset=1, step=3)
279-
280-
281-
def test_models_offset_2_step_3(tmp_path):
282-
run_models(tmp_path, offset=2, step=3)

0 commit comments

Comments
 (0)