testing

Par nvidia · skills

Système de test pour Megatron-LM. Couvre l'organisation des tests, la structure YAML des recettes, l'ajout et l'exécution de tests unitaires et fonctionnels, les valeurs de référence (golden values), les filtres de marqueurs et la parité avec la CI.

npx skills add https://github.com/nvidia/skills --skill testing

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 -brokenne 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.toml définit addopts = --durations=15 -s -rA — stdout n'est pas capturé (-s), donc les rangs s'entrelacent lors d'exécutions multi-rangs. Remplacez par --capture=fd lors du débogage d'un rang spécifique.
  • tests/unit_tests/conftest.py cherche les données de test sous /opt/data et 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

  1. Créez tests/unit_tests/<category>/test_<name>.py.
  2. Utilisez les fixtures de tests/unit_tests/conftest.py.
  3. Appliquez les marqueurs si nécessaire :
    • @pytest.mark.internal — skippé sur tag legacy
    • @pytest.mark.flaky_in_dev — skippé en environnement dev (CI par défaut ; utilisez ceci pour désactiver un test instable sans bloquer le pipeline standard)
    • @pytest.mark.flaky — skippé en environnement lts
    • @pytest.mark.experimental — tag latest uniquement
  4. Vérifiez localement (voir Exécuter des unit tests localement ci-dessus).
  5. 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

  1. Créez tests/functional_tests/test_cases/<model>/<test_name>/.

  2. Écrivez model_config.yaml avec MODEL_ARGS, ENV_VARS, et TEST_TYPE.

  3. Ajoutez une recette YAML sous tests/test_utils/recipes/h100/ (et gb200/ si nécessaire). Champs requis : scope, environment, platform, n_repeat, time_limit.

  4. Poussez la PR, ajoutez le label "Run functional tests" pour déclencher une exécution complète.

  5. 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>
  6. 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

Skills similaires