Guide de test
Structure de test
tests/
├── unit_tests/ # pytest, 1 nœud × 8 GPUs, torch.distributed runner
├── functional_tests/ # shell et scripts d'entraînement end-to-end
│ └── test_cases/
│ └── {model}/{test_case}/
│ ├── model_config.yaml # arguments d'entraînement
│ └── golden_values_{env}_{platform}.json
└── test_utils/
├── recipes/
│ ├── h100/ # Recettes YAML pour jobs H100
│ └── gb200/ # Recettes YAML pour jobs GB200
└── python_scripts/ # helpers (recipe_parser, golden-value download, …)
Comment les tests s'exécutent
Le runner GitHub Actions invoque launch_nemo_run_workload.py, qui utilise
nemo-run pour lancer un conteneur DockerExecutor. Le dépôt est monté en bind
à /opt/megatron-lm ; les données d'entraînement sont montées à /mnt/artifacts.
Les unit tests sont dispatché via torch.distributed.run :
- Les rangs 0 et 3 sont redirigés vers stdout ; tous les autres rangs écrivent uniquement dans des fichiers journaux.
- Les fichiers journaux par rang aboutissent à
{assets_dir}/logs/1/et sont téléchargés en tant qu'artefact GitHub après l'exécution.
Les tests fonctionnels sont pilotés par
tests/functional_tests/shell_test_utils/run_ci_test.sh. Seul le rang 0 exécute l'étape de validation pytest ; la sortie d'entraînement de tous les rangs est téléchargée en tant qu'artefact.
Retry automatique des défaillances instables : launch_nemo_run_workload.py réessaye jusqu'à
3 fois pour les patterns transitoires connus (timeout NCCL, erreur ECC, segfault,
connectivité HuggingFace, …) avant de déclarer une défaillance réelle.
Structure de recette YAML
Les recettes résident dans tests/test_utils/recipes/ et sont parsées par
tests/test_utils/python_scripts/recipe_parser.py. Chaque fichier étend un bloc
products cartésien en spécifications de workload individuelles :
type: basic
format_version: 1
maintainers: [mcore]
loggers: [stdout]
spec:
name: "{test_case}_{environment}_{platforms}"
model: gpt # mappe à tests/functional_tests/test_cases/{model}/
build: mcore-pyt-{environment}
nodes: 1
gpus: 8
n_repeat: 5
platforms: dgx_h100
time_limit: 1800
script_setup: |
...
script: |-
bash tests/functional_tests/shell_test_utils/run_ci_test.sh ...
products:
- test_case: [my_test]
products:
- environment: [dev, lts]
scope: [mr-github]
platforms: [dgx_h100]
Placeholders runtime clés : {assets_dir}, {artifacts_dir}, {test_case},
{environment}, {platforms}, {n_repeat}.
Désactiver un test sans le supprimer
Pour désactiver temporairement un test case dans une recette YAML, suffixez sa valeur scope
par -broken — ne supprimez pas l'entrée :
# avant (test s'exécute en CI)
scope: [mr-github]
# après (test est skippé ; entrée préservée pour réactivation facile)
scope: [mr-github-broken]
Exécuter des unit tests localement
Tous les unit tests initialisent un groupe torch.distributed, donc chaque invocation
nécessite un accès GPU et doit passer par torch.distributed.run :
# Suite complète
uv run python -m torch.distributed.run --nproc-per-node 8 -m pytest -q \
tests/unit_tests
# Fichier unique
uv run python -m torch.distributed.run --nproc-per-node 8 -m pytest -q \
tests/unit_tests/models/test_gpt_model.py
# Test unique
uv run python -m torch.distributed.run --nproc-per-node 8 -m pytest -q \
tests/unit_tests/models/test_gpt_model.py::TestGPTModel::test_constructor
# Filtrer par substring de nom
uv run python -m torch.distributed.run --nproc-per-node 8 -m pytest -q \
tests/unit_tests -k optimizer
Filtres de marqueurs
# Exclure les tests instables lors du développement
uv run python -m torch.distributed.run --nproc-per-node 8 -m pytest -q \
tests/unit_tests -m "not flaky and not flaky_in_dev"
# Inclure les tests expérimentaux
uv run python -m torch.distributed.run --nproc-per-node 8 -m pytest -q \
tests/unit_tests --experimental
Parité CI
Utilisez tests/unit_tests/run_ci_test.sh pour reproduire exactement une défaillance CI bucket.
Pour les exécutions ad-hoc, préférez les invocations directes torch.distributed.run ci-dessus.
Pièges
pyproject.tomldéfinitaddopts = --durations=15 -s -rA— stdout n'est pas capturé (-s), donc les rangs s'entrelacent lors d'exécutions multi-rangs. Remplacez par--capture=fdlors du débogage d'un rang spécifique.tests/unit_tests/conftest.pycherche les données de test sous/opt/dataet tente un téléchargement s'il est manquant. Fournissez-le manuellement ou skipper les tests dépendant des données lors de l'exécution en dehors du conteneur canonique.
Ajouter un unit test
- Créez
tests/unit_tests/<category>/test_<name>.py. - Utilisez les fixtures de
tests/unit_tests/conftest.py. - Appliquez les marqueurs si nécessaire :
@pytest.mark.internal— skippé sur taglegacy@pytest.mark.flaky_in_dev— skippé en environnementdev(CI par défaut ; utilisez ceci pour désactiver un test instable sans bloquer le pipeline standard)@pytest.mark.flaky— skippé en environnementlts@pytest.mark.experimental— taglatestuniquement
- Vérifiez localement (voir Exécuter des unit tests localement ci-dessus).
- Si le test nécessite un bucket CI dédié, ajoutez une entrée à
tests/test_utils/recipes/h100/unit-tests.yaml.
Ajouter un test fonctionnel / d'intégration
-
Créez
tests/functional_tests/test_cases/<model>/<test_name>/. -
Écrivez
model_config.yamlavecMODEL_ARGS,ENV_VARS, etTEST_TYPE. -
Ajoutez une recette YAML sous
tests/test_utils/recipes/h100/(etgb200/si nécessaire). Champs requis :scope,environment,platform,n_repeat,time_limit. -
Poussez la PR, ajoutez le label "Run functional tests" pour déclencher une exécution complète.
-
Après une exécution réussie, téléchargez les valeurs golden :
python tests/test_utils/python_scripts/download_golden_values.py \ --source github --pipeline-id <run-id> -
Committez les valeurs golden téléchargées.
Pièges courants
| Problème | Cause | Solution |
|---|---|---|
| Le test passe localement mais échoue en CI | Environnement ou chemin de données différent | Vérifiez DATA_PATH, DATA_CACHE_PATH, et le tag environment (dev vs lts) |
| Mismatch de valeur golden après un changement de code | Régression numérique | Téléchargez de nouvelles valeurs golden via download_golden_values.py après une exécution propre |
cicd-integration-tests-gb200 non déclenché |
Les jobs GB200 nécessitent le statut de mainteneur | Demandez à un mainteneur de déclencher, ou ajoutez le label Run functional tests |