Lors de l'écriture d'un exécuteur de test, il est important de penser à l'évolutivité. Demandez-vous : « si mon exécuteur 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. Cela nécessite de diviser tous les tests dont le coureur a besoin en plusieurs morceaux pouvant être parallélisés.
Cette page décrit comment rendre votre coureur partageable pour Tradefed.
Interface à mettre en œuvre
L'interface la plus importante à implémenter pour être considérée comme partageable 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 implémentant IShardableTest
. S'il est trouvé, il appellera split
pour obtenir un nouvel objet IRemoteTest
afin d'exécuter un sous-ensemble de cas de test pour une partition spécifique.
Que dois-je savoir sur la mise en œuvre fractionnée ?
- Votre coureur peut éclater sous certaines conditions uniquement ; dans ce cas, renvoyez
null
lorsque vous n'avez pas partitionné. - Essayez de diviser autant que cela a du sens : divisez votre coureur en unités d'exécution qui lui conviennent. Cela dépend vraiment de votre coureur. Par exemple : HostTest est fragmenté au niveau de la classe, chaque classe de test est placée dans une partition distincte.
- Si cela a du sens, ajoutez quelques options pour contrôler un peu le partitionnement. Par exemple : AndroidJUnitTest a un
ajur-max-shard
pour spécifier le nombre maximum de fragments dans lesquels il peut être divisé, quel que soit le nombre demandé.
Exemple détaillé de mise en œuvre
Voici un exemple d'extrait de code implémentant IShardableTest
que vous pouvez référencer. Le code complet est disponible sur (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 nouvelle instance de lui-même et lui définit les paramètres de partition. Cependant, la logique de fractionnement peut être totalement différente d’un test à l’autre ; et tant qu'il est déterministe et produit des sous-ensembles collectivement exhaustifs, tout va bien.
Indépendance
Les fragments doivent être indépendants ! Deux fragments créés par votre implémentation de split
dans votre runner ne doivent pas avoir de dépendances l'un sur l'autre ni partager de ressources.
Le fractionnement des fragments doit être déterministe ! Ceci 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 partition peut s'exécuter sur différentes instances TF, il est essentiel de garantir que la logique split
génère des sous-ensembles mutuellement exclusifs et collectivement exhaustifs de manière déterministe.
Partager un test localement
Pour partager un test sur un TF local, vous pouvez simplement 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 fragment 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 fragmentés, vous devez vous assurer que votre service de reporting le prend en charge.