Lorsque vous écrivez un lanceur de test, il est important de penser à l'évolutivité. Demandez-vous : "Si mon outil d'exécution de tests devait exécuter 200 000 cas de test, combien de temps cela prendrait-il ?"
Le sharding est l'une des réponses disponibles dans Trade Federation. Elle nécessite de diviser tous les tests dont l'exécuteur a besoin en plusieurs fragments pouvant être parallélisés.
Cette page explique comment rendre votre exécuteur fragmentable pour Tradefed.
Interface d'implémentation
L'interface la plus importante à mettre en œuvre pour être considérée comme pouvant être segmentée par TF est IShardableTest, qui contient deux méthodes: split(int numShard)
et split()
.
Si la segmentation est destinée à dépendre du nombre de segments demandés, 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 fractionnement --shard-count
et --shard-index
, TF itère sur tous les IRemoteTest
pour rechercher ceux qui implémentent IShardableTest
. S'il le trouve, il appelle split
pour obtenir un nouvel objet IRemoteTest
afin d'exécuter un sous-ensemble de scénarios de test pour un segment spécifique.
Que dois-je savoir sur l'implémentation de la division ?
- Votre exécuteur peut effectuer un fractionnement uniquement dans certaines conditions. Dans ce cas, renvoyez
null
lorsque vous n'avez pas effectué de fractionnement. - Essayez de diviser autant que nécessaire: divisez votre exécuteur en unité d'exécution appropriée. Cela dépend vraiment de votre exécuteur. Par exemple, HostTest est partitionné au niveau de la classe. Chaque classe de test est placée dans un segment distinct.
- Si cela est pertinent, ajoutez quelques options pour contrôler un peu le fractionnement.
Par exemple, AndroidJUnitTest possède un
ajur-max-shard
pour spécifier le nombre maximal de fragments qu'il peut diviser, quel que soit le nombre demandé.
Exemple d'implémentation détaillé
Voici un exemple d'extrait de code implémentant IShardableTest
que vous pouvez consulter. Le code complet est disponible à l'adresse suivante : (https://android.googlesource.com/platform/tools/tradefederation/+/refs/heads/main/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 de lui-même et lui attribue des paramètres de fragment. Cependant, la logique de répartition peut être totalement différente d'un test à l'autre. Tant qu'elle est déterministe et qu'elle génère des sous-ensembles collectivement exhaustifs, tout va bien.
Independence
Les segments doivent être indépendants ! Les deux fragments créés par votre implémentation de split
dans votre exécuteur ne doivent pas dépendre les uns des autres ni partager de ressources.
Le fractionnement des fragments doit être déterministe. Cela est également obligatoire, étant donné les mêmes conditions, la méthode split
doit toujours renvoyer exactement la même liste de segments dans le même ordre.
REMARQUE: Étant donné que chaque fragment 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 mutuellement exclusifs et collectivement exhaustifs de manière déterministe.
Diviser un test en local
Pour segmenter un test sur un fichier 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
Ensuite, TF générera automatiquement des commandes pour chaque segment et les exécutera.
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'effectue aucune agrégation des résultats de test pour les appels segmentés, vous devez vous assurer que votre service de création de rapports est compatible avec cette fonctionnalité.