Lorsque vous écrivez un test runner, il est important de penser à l'évolutivité. Demandez-vous : "Si mon test runner devait exécuter 200 000 cas de test, combien de temps cela prendrait-il ?"
Le partitionnement est l'une des réponses disponibles dans Trade Federation. Cela nécessite de diviser tous les tests dont le runner a besoin en plusieurs blocs pouvant être parallélisés.
Cette page explique comment rendre votre exécutant fragmentable pour Tradefed.
Interface à implémenter
L'interface la plus importante à implémenter pour être considérée comme fragmentable par TF est IShardableTest, qui contient deux méthodes : split(int numShard)
et split()
.
Si votre partitionnement dépend du nombre de partitions demandées, vous devez implémenter split(int numShard)
. Sinon, implémentez split()
.
Lorsqu'une commande de test TF est exécutée avec les paramètres de partitionnement --shard-count
et --shard-index
, TF parcourt tous les IRemoteTest
pour rechercher ceux qui implémentent IShardableTest
. Si elle est trouvée, elle appelle split
pour obtenir un nouvel objet IRemoteTest
afin d'exécuter un sous-ensemble de cas de test pour un shard spécifique.
Que dois-je savoir sur l'implémentation fractionnée ?
- Votre exécuteur peut être fragmenté uniquement sous certaines conditions. Dans ce cas, renvoyez
null
lorsque vous n'avez pas fragmenté. - Essayez de diviser autant que possible : divisez votre coureur en unités d'exécution qui ont du sens pour lui. Cela dépend vraiment de votre runner. Par exemple, HostTest est fragmenté au niveau de la classe, chaque classe de test étant placée dans un fragment distinct.
- Si cela a du sens, ajoutez quelques options pour contrôler un peu le partitionnement.
Par exemple :
AndroidJUnitTest
comporte un
ajur-max-shard
pour spécifier le nombre maximal de fragments dans lesquels il peut être divisé, quel que soit le nombre demandé.
Exemple d'implémentation détaillée
Voici un exemple d'extrait de code implémentant IShardableTest
que vous pouvez consulter. Le code complet est disponible à l'adresse (https://android.googlesource.com/platform/tools/tradefederation/+/refs/heads/android16-release/test_framework/com/android/tradefed/testtype/InstalledInstrumentationsTest.java).
/**
* Runs all instrumentation found on current device.
*/
@OptionClass(alias = "installed-instrumentation")
public class InstalledInstrumentationsTest
implements IDeviceTest, IResumableTest, IShardableTest {
...
/** {@inheritDoc} */
@Override
public Collection<IRemoteTest> split(int shardCountHint) {
if (shardCountHint > 1) {
Collection<IRemoteTest> shards = new ArrayList<>(shardCountHint);
for (int index = 0; index < shardCountHint; index++) {
shards.add(getTestShard(shardCountHint, index));
}
return shards;
}
// Nothing to shard
return null;
}
private IRemoteTest getTestShard(int shardCount, int shardIndex) {
InstalledInstrumentationsTest shard = new InstalledInstrumentationsTest();
try {
OptionCopier.copyOptions(this, shard);
} catch (ConfigurationException e) {
CLog.e("failed to copy instrumentation options: %s", e.getMessage());
}
shard.mShardIndex = shardIndex;
shard.mTotalShards = shardCount;
return shard;
}
...
}
Cet exemple crée simplement une instance d'elle-même et lui définit des paramètres de partition. Toutefois, la logique de fractionnement peut être totalement différente d'un test à l'autre. Tant qu'elle est déterministe et produit des sous-ensembles collectivement exhaustifs, elle est acceptable.
Indépendance
Les partitions doivent être indépendantes. Deux fragments créés par votre implémentation de split
dans votre programme d'exécution ne doivent pas avoir de dépendances les uns envers les autres ni partager de ressources.
Le fractionnement des partitions doit être déterministe. Cela est également obligatoire. Dans les mêmes conditions, votre méthode split
doit toujours renvoyer exactement la même liste de fragments dans le même ordre.
REMARQUE : Étant donné que chaque shard peut s'exécuter sur différentes instances TF, il est essentiel de s'assurer que la logique split
génère des sous-ensembles qui sont mutuellement exclusifs et collectivement exhaustifs de manière déterministe.
Fragmenter un test en local
Pour fragmenter un test sur un TF local, il vous suffit d'ajouter l'option --shard-count
à la ligne de commande.
tf >run host --class com.android.tradefed.UnitTests --shard-count 3
TF génère ensuite automatiquement des commandes pour chaque partition et les exécute.
tf >l i
Command Id Exec Time Device State
3 0m:03 [null-device-2] running stub on build 0 (shard 1 of 3)
3 0m:03 [null-device-1] running stub on build 0 (shard 0 of 3)
3 0m:03 [null-device-3] running stub on build 0 (shard 2 of 3)
Agrégation des résultats des tests
Étant donné que TF n'agrège pas les résultats des tests pour les appels fragmentés, vous devez vous assurer que votre service de reporting le prend en charge.