Escribe un ejecutor de pruebas fragmentado de IRemoteTest

Cuando se escribe un ejecutor de pruebas, es importante considerar la escalabilidad. Haz las siguientes preguntas tú mismo, “si mi ejecutor de pruebas tuviera que ejecutar 200,000 casos de prueba” ¿Cuánto tiempo tardaría?

La fragmentación es una de las respuestas disponibles en Trade Federation. Requiere y dividir todas las pruebas que necesita el ejecutor en varios fragmentos que se pueden paralelizar.

En esta página, se describe cómo hacer que tu ejecutor se pueda fragmentar para Tradefed.

Interfaz que se implementará

La interfaz más importante que se debe implementar para que se considere fragmentable TF es IShardableTest; que contiene dos métodos: split(int numShard) y split().

Si tu fragmentación dependerá de la cantidad de fragmentos solicitados, puedes debes implementar split(int numShard). De lo contrario, implementa split().

Cuando se ejecuta un comando de prueba de TF con los parámetros de fragmentación --shard-count y --shard-index, TF itera a través de todos los IRemoteTest para buscar unos. implementando IShardableTest. Si lo encuentra, llamará a split para obtén un nuevo objeto IRemoteTest para ejecutar un subconjunto de casos de prueba de un fragmento.

¿Qué debo saber sobre la implementación de divisiones?

  • El ejecutor puede hacer fragmentación solo según algunas condiciones; En ese caso, muestra null. cuando no fragmentaste.
  • Trate de dividirlo tanto como sea razonable: divídalo en unidades de ejecución que tenga sentido para ello. Realmente depende de tu ejecutor. Para ejemplo: Prueba de host se fragmenta a nivel de la clase, cada clase de prueba se coloca en un fragmento separado.
  • Si tiene sentido, agrega algunas opciones para controlar un poco la fragmentación. Por ejemplo: AndroidJUnitTest. tiene un ajur-max-shard para especificar la cantidad máxima de fragmentos que podría dividir, independientemente del número solicitado.

Ejemplo detallado de implementación

Este es un ejemplo de fragmento de código que implementa IShardableTest referencia. El código completo está disponible en (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;
    }
    ...
}

Este ejemplo simplemente crea una nueva instancia de sí misma y establece un fragmento parámetros. Sin embargo, la lógica de división puede ser totalmente diferente de prueba a prueba; y siempre que sea determinista y rinda colectivamente y exhaustivos, está bien.

Independencia

Los fragmentos deben ser independientes. Dos fragmentos creados por tu implementación de Los split del ejecutor no deben tener dependencias entre sí ni compartirse de Google Cloud.

La división de fragmentos debe ser determinista. Esto también es obligatorio, debido a la las mismas condiciones, tu método split siempre debe mostrar exactamente la misma lista de fragmentos en el mismo orden.

NOTA: Dado que cada fragmento puede ejecutarse en diferentes instancias de TF, es fundamental asegúrate de que la lógica split genere subconjuntos que sean mutuamente excluyentes colectivamente exhaustiva de manera determinista.

Fragmenta una prueba de forma local

Para fragmentar una prueba en un TF local, puedes agregar la opción --shard-count a la línea de comandos.

tf >run host --class com.android.tradefed.UnitTests --shard-count 3

Luego, TF generará automáticamente comandos para cada fragmento y los ejecutará.

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)

Agregación de resultados de pruebas

Como TF no hace ninguna agregación de resultados de prueba para las invocaciones fragmentadas, necesita asegurarse de que su servicio de informes lo admita.