Après avoir compilé un programme en ligne de commande sur un Mac dans le cloud, le plus facile à négliger n’est pas une erreur de compilation, mais le contrat d’appel : l’aide se retrouve dans le flux de données, un échec renvoie quand même un code nul, ou l’outil ne fonctionne que depuis le répertoire courant du développeur. Voici comment préparer une recette avant livraison avec un petit outil Swift. L’exemple ne nécessite pas de session graphique et convient à une tâche de compilation sans surveillance.
Définir d’abord le contrat d’appel
L’outil d’exemple taskprobe accepte --output et un texte, qu’il écrit dans le fichier indiqué. Le chemin du fichier est obligatoire ; une demande d’aide se termine avec succès, tandis qu’une erreur d’arguments entraîne un échec. Définissez la façon dont l’appelant reconnaîtra le résultat avant d’implémenter la fonctionnalité.
| Cas | Sortie standard | Sortie d’erreur standard | Code de sortie | Fichier |
|---|---|---|---|---|
--help |
Mode d’emploi | Vide | 0 | Non créé |
| Arguments manquants | Vide | Message d’erreur | Non nul | Non créé |
| Écriture réussie | Marqueur de fin | Vide | 0 | Contenu complet |
| Chemin non accessible en écriture | Vide | Message d’erreur | Non nul | Fichier cible non créé |
La sortie standard est réservée aux résultats d’une exécution réussie exploitables par un programme ; la sortie d’erreur standard sert uniquement aux diagnostics. Le script appelant doit vérifier à la fois le code de sortie et le fichier attendu : la présence d’un message de fin dans les journaux ne suffit pas à établir que l’opération a réussi.
L’écriture atomique évite que le fichier cible soit remplacé par un nouveau contenu incomplet. Elle ne garantit pas la persistance des données après une coupure de courant ; celle-ci nécessite une stratégie distincte d’écriture sur disque et de récupération.
Compiler un outil minimal testable
Enregistrez le code source dans un répertoire distinct, puis compilez-le. Le chemin de sortie n’est pas codé en dur dans le programme : l’appelant fournit un chemin absolu, ce qui permet de changer de répertoire de travail pendant les tests. L’option d’écriture atomique de Foundation évite qu’un contenu partiel apparaisse au chemin cible pendant une écriture normale.
WORK="$(mktemp -d)"
mkdir -p "$WORK/bin" "$WORK/home"
cat > "$WORK/main.swift" <<'SWIFT'
import Foundation
func fail(_ message: String) -> Never {
FileHandle.standardError.write(Data((message + "\n").utf8))
exit(2)
}
let args = Array(CommandLine.arguments.dropFirst())
if args == ["--help"] {
print("usage: taskprobe --output PATH TEXT")
exit(0)
}
guard args.count == 3, args[0] == "--output",
!args[1].isEmpty else {
fail("expected --output PATH TEXT")
}
let target = URL(fileURLWithPath: args[1])
do {
try Data(args[2].utf8).write(to: target, options: .atomic)
print("written")
} catch {
fail("cannot write output: \(error.localizedDescription)")
}
SWIFT
xcrun swiftc -O "$WORK/main.swift" -o "$WORK/bin/taskprobe"
xcrun utilise la chaîne d’outils de développement actuellement sélectionnée. Avant la compilation, vous pouvez vérifier ce choix avec xcode-select -p ; cela ne remplace pas une exécution sur la version cible de macOS. La recette doit porter sur le binaire même qui sera livré. Ne recompilez pas après l’avoir validé pour livrer ensuite un autre fichier.
Tester les cas de réussite et d’échec dans un répertoire isolé
Le script suivant reprend la variable WORK de la session shell précédente. Chaque vérification enregistre séparément les deux flux de sortie. Le test se place volontairement dans un répertoire de travail temporaire et élimine la plupart des variables d’environnement héritées. Si l’outil livré a réellement besoin d’une variable, ajoutez-la explicitement au contrat au lieu de compter sur sa présence fortuite dans un shell interactif.
set -eu
BIN="$WORK/bin/taskprobe"
cd "$WORK/home"
env -i PATH=/usr/bin:/bin HOME="$WORK/home" \
"$BIN" --help >"$WORK/help.out" 2>"$WORK/help.err"
grep -Fx 'usage: taskprobe --output PATH TEXT' "$WORK/help.out"
test ! -s "$WORK/help.err"
if "$BIN" --output >"$WORK/bad.out" 2>"$WORK/bad.err"; then
echo "missing arguments returned success" >&2
exit 1
fi
test ! -s "$WORK/bad.out"
grep -F 'expected --output' "$WORK/bad.err"
"$BIN" --output "$WORK/home/result.txt" 'hello 云端 Mac' \
>"$WORK/good.out" 2>"$WORK/good.err"
grep -Fx 'written' "$WORK/good.out"
test ! -s "$WORK/good.err"
grep -Fx 'hello 云端 Mac' "$WORK/home/result.txt"
if "$BIN" --output "$WORK/home/absent/result.txt" test \
>"$WORK/fail.out" 2>"$WORK/fail.err"; then
echo "unwritable path returned success" >&2
exit 1
fi
test ! -e "$WORK/home/absent/result.txt"
test ! -s "$WORK/fail.out"
test -s "$WORK/fail.err"
Le premier cas d’échec vérifie la gestion des arguments incorrects, le second celle d’une erreur d’écriture. N’utilisez pas chmod pour fabriquer un « répertoire non accessible en écriture » : le résultat peut varier si le processus de test dispose de privilèges particuliers. Un répertoire parent inexistant fournit un test plus direct, mais ne couvre qu’un seul type d’échec d’E/S et ne prouve pas que toutes les anomalies de disque seront traitées comme prévu.
Éviter trois faux positifs courants
Un pipeline masque le code de sortie
Le code de sortie de taskprobe ... | tee output.log provient généralement de la dernière commande du pipeline, et non de l’outil lui-même. Dans le script de recette, préférez rediriger directement les deux flux, puis vérifier le statut de la commande. Si un pipeline est indispensable, activez pipefail dans un shell qui prend cette option en charge et consignez le shell utilisé.
Le répertoire courant convient par hasard
Les chemins relatifs dépendent du répertoire depuis lequel le programme est lancé. Le test ci-dessus exécute le binaire depuis home et lui fournit un chemin de sortie absolu précisément pour déceler les hypothèses implicites sur le répertoire de travail. Si l’outil réel doit lire des ressources fournies avec lui, il doit les localiser à partir d’un emplacement défini explicitement, sans supposer que la tâche démarre toujours à la racine du code source.
Un message d’erreur est pris pour une donnée
Vérifier seulement que « la commande a affiché quelque chose » ne sert à rien. Une tâche en aval peut enregistrer directement la sortie standard comme fichier de résultat. La recette doit confirmer simultanément qu’en cas d’échec, la sortie standard est vide, la sortie d’erreur standard contient un message et le code de sortie est non nul ; en cas de succès, la sortie d’erreur doit être vide et le contenu réel du fichier généré doit être vérifié. Si l’outil produit des données structurées, validez aussi leur format au lieu de vous limiter à la taille du fichier.
Vérifier les limites avant la livraison
Placez le script de recette à côté de l’artefact de compilation et exécutez-le dans le cadre de la même tâche de compilation. Une fois les tests réussis, consignez la somme de contrôle du binaire et les informations sur la chaîne d’outils sélectionnée pour permettre leur vérification lors de la transmission. Le compte qui exécute les tests doit disposer des mêmes droits d’accès aux répertoires que celui de la tâche réelle. Utilisez un répertoire à usage unique pour les tests de fichiers, afin qu’un ancien artefact ne fasse pas réussir test -e par erreur.
Ces tests ne simulent ni une coupure de courant, ni un disque plein, ni la modification concurrente du fichier cible par un autre processus. Ils ne prouvent pas non plus que le programme fonctionne sur des versions du système non testées. Si ces garanties sont nécessaires, ajoutez respectivement des tests par injection de pannes, une coordination des accès concurrents et une recette sur le système cible. Avant la fin de la période de location, transférez le code source, les scripts et les résultats de recette, puis demandez à la personne qui prend le relais de relancer tous les contrôles dans le nouvel environnement. « Déjà validé » ne constitue pas une recette sur une nouvelle machine.
Questions fréquentes
Pourquoi ne pas écrire les erreurs dans la sortie standard ?
Un script peut enregistrer stdout comme fichier de données. Les erreurs doivent aller vers stderr et être signalées par un code de sortie non nul.
Un test du fichier final prouve-t-il que l’écriture est atomique ?
Non. Il ne vérifie que le résultat observable. L’implémentation doit aussi placer le fichier temporaire et la destination sur le même système de fichiers.
Besoin d’un Mac cloud dédié ?
Choisissez le modèle, la région et la durée de facturation. Vous bénéficiez d’une machine physique dédiée ; la disponibilité réelle est indiquée en temps réel dans la console.