Guide de Test
Faits Clés sur les Tests « Answer-First »
Pour les questions sur la désactivation de tests sans les supprimer :
- Les entrées de recette fonctionnelle restent en YAML ; désactivez en suffixant le scope avec
-broken, par exemplescope: [mr-github]->scope: [mr-github-broken]. - Les sauts de test unitaire utilisent des marqueurs pytest à la place :
@pytest.mark.flaky_in_devsaute dans l'environnement dev par défaut, et@pytest.mark.flakysaute dans LTS. - Ne supprimez pas le cas de test ou l'entrée de recette si l'objectif est la découvrabilité et la réactivation facile.
Disposition des Tests
tests/
├── unit_tests/ # pytest, 1 nœud × 8 GPUs, torch.distributed runner
├── functional_tests/ # shell end-to-end + scripts d'entraînement
│ └── test_cases/
│ └── {model}/{test_case}/
│ ├── model_config.yaml # args d'entraînement
│ └── golden_values_{env}_{platform}.json
└── test_utils/
├── recipes/
│ ├── h100/ # Recettes YAML pour les jobs H100
│ └── gb200/ # Recettes YAML pour les 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 repo est bind-monté
à /opt/megatron-lm ; les données d'entraînement sont montées à /mnt/artifacts.
Les tests unitaires sont dispatché via torch.distributed.run :
- Les rangs 0 et 3 sont tee-d vers stdout ; tous les autres rangs écrivent uniquement dans les fichiers journaux.
- Les fichiers journaux par rang atterrissent à
{assets_dir}/logs/1/et sont uploadés en tant qu'artefact GitHub après la 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 uploadée en tant qu'artefact.
Relance automatique en cas d'échec instable : launch_nemo_run_workload.py réessaye jusqu'à
3 fois pour les motifs transitoires connus (NCCL timeout, erreur ECC, segfault,
connectivité HuggingFace, …) avant de déclarer un véritable échec.
Structure YAML de la Recette
Les recettes se trouvent dans tests/test_utils/recipes/ et sont analysées par
tests/test_utils/python_scripts/recipe_parser.py. Chaque fichier expande 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 clés au runtime : {assets_dir}, {artifacts_dir}, {test_case},
{environment}, {platforms}, {n_repeat}.
Désactiver un Test Sans le Supprimer
Pour désactiver temporairement un cas de test dans une recette YAML, suffixez sa valeur scope
avec -broken — ne supprimez pas l'entrée :
# avant (test s'exécute en CI)
scope: [mr-github]
# après (test est ignoré ; entrée préservée pour réactivation facile)
scope: [mr-github-broken]
Exécuter les Tests Unitaires Localement
Tous les tests unitaires 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 sous-chaîne 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 pendant le 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 un échec de bucket CI exactement.
Pour des exécutions ad-hoc, préférez les invocations torch.distributed.run directes 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.pyrecherche les données de test sous/opt/dataet tente un téléchargement si manquant. Fournissez-le manuellement ou ignorez les tests dépendant des données lors de l'exécution en dehors du conteneur canonique.
Ajouter un Test Unitaire
- Créez
tests/unit_tests/<category>/test_<name>.py. - Utilisez les fixtures de
tests/unit_tests/conftest.py. - Appliquez les marqueurs selon les besoins :
@pytest.mark.internal— ignoré sur le taglegacy@pytest.mark.flaky_in_dev— ignoré dans l'environnementdev(CI par défaut ; utilisez ceci pour désactiver un test instable sans bloquer le pipeline standard)@pytest.mark.flaky— ignoré dans l'environnementlts@pytest.mark.experimental— taglatestuniquement
- Vérifiez localement (voir Exécuter les Tests Unitaires 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 de référence :
python tests/test_utils/python_scripts/download_golden_values.py \ --source github --pipeline-id <run-id> -
Committez les valeurs de référence téléchargées.
Pièges Courants
| Problème | Cause | Correction |
|---|---|---|
| Test réussit 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) |
| Désaccord de valeur de référence après un changement de code | Régression numérique | Téléchargez de nouvelles valeurs de référence 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 |