Tests de régression du logiciel BYO sur Rescale

Modèle de blog 46

 tests de régression2
Rescale est une ressource précieuse de tests de régression et de performances pour les éditeurs de logiciels. À l'aide de notre API et de nos outils de ligne de commande, nous expliquerons comment vous pouvez exécuter tout ou un sous-ensemble de vos tests de régression internes sur Rescale. Les avantages des tests sur Rescale sont les suivants :

  1. Les ressources de calcul sont à la demande, vous ne payez donc que lorsque vous exécutez réellement des tests
  2. Les ressources de calcul sont également évolutives, ce qui vous permet d'exécuter une vaste suite de tests en parallèle et d'obtenir des commentaires plus rapidement.
  3. Des ressources hétérogènes sont disponibles pour tester les performances logicielles sur diverses configurations matérielles (par exemple les coprocesseurs Infiniband, 10GigE, Tesla et Phi)
  4. Tester votre logiciel sur Rescale peut ensuite vous permettre éventuellement de fournir des versions bêta privées à d'autres clients sur Rescale.

Configuration de test

Pour le reste de cet article, nous supposerons que vous disposez des ensembles de fichiers suivants :

  1. Arborescence de construction complète de votre progiciel, dans un format d'archive couramment pris en charge (tar.gz, zip, etc.)
  2. Ensemble archivé d'entrées de test de référence et de sorties attendues
  3. Script ou ligne de commande pour exécuter votre logiciel sur une ou plusieurs entrées de test
  4. Script pour évaluer la sortie réelle du test avec la sortie attendue
  5. (Facultatif) Ensemble plus petit de produits de build incrémentiels à superposer au-dessus de l'arborescence de build complète

Dans les exemples ci-dessous, nous utiliserons notre python SDK. Une sélection d'exemples ci-dessous sont disponibles dans le dépôt SDK ici. Le SDK encapsule simplement notre API REST, vous pouvez donc porter ces exemples vers d'autres langages en utilisant les points de terminaison référencés dans https://github.com/rescale/python-sdk/blob/master/rescale/client.py.
Notez que tous ces exemples nécessitent :

  1. Un compte sur le Redimensionner la plateforme
  2. Un local RESCALE_API_KEY variable d'environnement définie sur votre clé API trouvée dans Paramètres->API à partir de la page principale de la plateforme

Exécuter des tests à partir d'une seule tâche

Nous commencerons par l'exemple le plus simple, en téléchargeant une version complète et des données de référence de test sous forme de fichiers d'entrée de travail, en exécutant les tests en série et en comparant les résultats. Commençons par quelques exemples de « logiciels » que nous allons télécharger et exécuter. Voici une liste du progiciel et des fichiers de test :

echoware/bin/echo.sh (fait juste écho au test d'entrée pour tester) test[0-9]/in (entrée de test) test[0-9]/expected_out (sortie de test attendue) test[0-9]/out ( sortie réelle du test)

Chaque version logicielle et scénario de test est archivé séparément. Voici les étapes pour préparer et exécuter notre travail de suite de tests :

  1. Téléchargez la version, les données de test de référence et le script de comparaison des résultats à l'aide du SDK Rescale Python :
#!/usr/bin/env python3 import rescale.client TEST_ARCHIVE = 'inputs/all_tests.tar.gz' BUILD_ARCHIVE = 'inputs/echoware0.1.tar.gz' POST_COMPARE_SCRIPT = 'inputs/compare_results.sh' input_files = [rescale .client.RescaleFile(file_path=TEST_ARCHIVE), rescale.client.RescaleFile(file_path=BUILD_ARCHIVE), ] post_process_file = \ rescale.client.RescaleFile(file_path=POST_COMPARE_SCRIPT)

RescaleFichier télécharge le contenu du fichier local vers Rescale et renvoie les métadonnées pour référencer ce fichier. À ce stade, vous pouvez consulter ces fichiers sur  https://www.rescale.com/route/files/.
      2. Créez le travail de suite de tests :

TEST_COMMANDE = """
pour le cas de test dans $(find . -name "test[0-9]*" -type d); do ./echoware/bin/echo.sh $testcase done """ POST_RUN_COMPARE_COMMAND = """ pour le testcase dans $(find . -name "test[0-9]*" -type d); do ./compare_results.sh $testcase done """ JOB_NAME = 'echoware0.1-all-tests' job_definition = { 'name' : JOB_NAME, 'isLowPriority' : True, 'jobanalyses' : [ { 'analyse' : { ' code' : 'custom' }, 'hardware' : { 'coresPerSlot' : 1, 'slots' : 1, 'coreType' : { 'code' : 'standard-plus' } }, 'inputFiles' : [{'id ' : inp.id} pour inp dans input_files], 'command' : TEST_COMMAND, 'postProcessScript' : {'id' : post_process_file.id}, 'postProcessScriptCommand' : POST_RUN_COMPARE_COMMAND } ], } job = rescale.client.RescaleJob(json_data = définition_travail)

Redimensionner le travail crée un nouveau travail que vous pouvez maintenant consulter sur https://www.rescale.com/route/jobs/. Notez ici que nous exécutons le travail sur un seul noyau Marble. Vous pouvez choisir d'exécuter plus de cœurs en augmentant cœursParSlot ou modifiez le type de noyau en sélectionnant un code de type de noyau différent dans RescaleConnect.get_core_types().
Notez que le commander et postProcessScriptCommand les champs peuvent être n'importe quel script bash valide, il y a donc une certaine flexibilité dans la façon dont vous exécutez votre test et évaluez les résultats. Dans notre exemple très simple, la comparaison des commandes post-test ne fait que comparer les ande et attendu_out fichiers dans chaque répertoire de scénario de test.

  1. Soumettez le travail pour exécution et attendez qu'il se termine :
job.submit() job.wait()

Une fois le cluster de tâches provisionné, les fichiers d'entrée sont transférés vers le cluster, non chiffrés, puis décompressés dans le répertoire de travail. Ensuite, le TEST_COMMAND est exécuté, suivi du POST_RUN_COMPARE_COMMAND.

  1. Téléchargez les résultats des tests. Toutes les commandes de travail Rescale ont été redirigées vers processus_output.log téléchargeons simplement ce fichier pour obtenir le résumé des résultats du test.
STDOUT_LOG = 'process_output.log' test_log = job.get_file(STDOUT_LOG) test_log.download(target=test_log.name)

Il est important de noter ici qu'en effectuant notre comparaison des résultats de test comme étape de post-traitement dans notre travail, nous évitons de télécharger des fichiers de sortie potentiellement volumineux, ce qui retarderait le temps nécessaire pour obtenir les résultats des tests. Cela ne résout pas le problème selon lequel nous devons toujours télécharger les sorties de référence de test, qui seront souvent de taille similaire à la sortie réelle. La clé est que nous devons uniquement télécharger les fichiers vers Rescale lorsqu'ils changent, et non pour chaque tâche de test que nous lançons. En supposant que nos cas de test de référence ne changent pas très souvent, nous pouvons désormais réutiliser les fichiers que nous venons de télécharger sur Rescale lors de tests ultérieurs.
Vous pouvez retrouver cet exemple dans son intégralité ici.

Réutilisation des données de tests de référence

Nous allons maintenant modifier la procédure ci-dessus pour éviter de télécharger des données de test de référence pour chaque tâche de test soumise.

  1. (modifié) Recherchez les métadonnées du fichier de test sur Rescale et utilisez-les comme fichier d'entrée pour les tâches suivantes :
# en omettant la configuration de var ci-dessus original_test_file = rescale.client.RescaleFile.get_newest_by_name(TEST_ARCHIVE) input_files = [original_test_file, rescale.client.RescaleFile(file_path=BUILD_ARCHIVE), ] post_process_file = \ rescale.client.RescaleFile.get_newest_by_name(POST_COMPARE_SCRIPT)

RescaleFile.get_newest_by_name récupère simplement les métadonnées du fichier de test déjà téléchargé sur Rescale. Notez que si vous avez téléchargé plusieurs archives de test portant le même nom, celle-ci sera sélectionnée la plus récemment téléchargée.
Les étapes 2 à 4 sont les mêmes que dans l'exemple précédent.

Paralléliser les tests de longue durée

Les exemples précédents exécutent simplement tous vos tests de manière séquentielle, exécutons-en maintenant quelques-uns en parallèle. Pour cet exemple, nous supposons que nos tests sont divisés en tests « courts » et « longs ». Les tests courts se trouvent dans une archive appelée all_short_tests.tar.gz et chaque test long se trouve dans une archive distincte appelée long_test_.tar.gz.
Nous allons désormais lancer un seul job pour tous les tests courts et un job par test pour les tests longs. Nous supposons que ces fichiers de test ont déjà été téléchargés sur Rescale, comme cela a été fait dans le premier exemple.

# omettre la commande var setup SHORT_TEST_ARCHIVE = 'inputs/all_short_tests.tar.gz' LONG_TEST_FORMAT = 'inputs/long_test_{i}.tar.gz' LONG_TEST_COUNT = 10 BUILD_ARCHIVE = 'inputs/echoware0.1.tar.gz' POST_COMPARE_SCRIPT = 'inputs /compare_results.sh' # rechercher par nom sur Rescale au lieu de télécharger à partir de la copie locale short_test_bundle = \ rescale.client.RescaleFile.get_newest_by_name(SHORT_TEST_ARCHIVE) long_test_inputs = [ rescale.client.RescaleFile.get_newest_by_name(LONG_TEST_FORMAT.format(i=i)) pour i dans la plage (LONG_TEST_COUNT) : post_process_file = \ rescale.client.RescaleFile.get_newest_by_name(POST_COMPARE_SCRIPT) # télécharger la copie locale build_input = rescale.client.RescaleFile(file_path=BUILD_ARCHIVE) def create_job(name, test_input, core_type, core_count) : input_files = [build_input, test_input] job_definition = { 'name' : nom, 'isLowPriority' : True, 'jobanalyses' : [ { 'analysis' : { 'code' : 'custom' }, 'hardware' : { 'coresPerSlot' : core_count, 'slots' : 1, 'coreType' : { 'code' : core_type } }, 'inputFiles' : [{'id' : inp.id} pour inp dans input_files], 'command' : TEST_COMMAND, 'postProcessScript' : {'id': post_process_file.id}, 'postProcessScriptCommand': POST_RUN_COMPARE_COMMAND } ], } return rescale.client.RescaleJob(json_data=job_definition) # créer toutes les tâches de test short_test_job = create_job('echoware0.1-all-short-tests ', short_test_bundle, 'standard-plus', 1) long_test_jobs = [create_job('echoware0.1-long-test-{0}'.format(i), long_test, 'hpc-plus', 32) pour i, long_test in enumerate(long_test_inputs)] test_jobs = [short_test_job] + long_test_jobs # soumettre tout

[job.submit() pour le travail dans test_jobs]

# attendre que tout soit terminé

[job.wait() pour le travail dans test_jobs]

# obtenir les résultats [job.get_file(STDOUT_LOG).download(target='{0}.out'.format(job.name)) pour le travail dans test_jobs]

Dans cet exemple, nous avons lancé notre travail de test court avec un seul cœur Marble et chacun de nos tests longs avec un cluster Nickel MPI de 32 cœurs (2 nœuds).
Cette configuration de tâche de test est particulièrement adaptée aux tests de performances. Pour tester qu'une combinaison particulière de build + scénario de test évolue, vous pouvez lancer 4 tâches avec respectivement 1, 2, 4 et 8 nœuds.
Cet exemple peut être trouvé ici.

Constructions incrémentielles

Dans ce qui précède, nous avons évité de télécharger à nouveau les données de test pour chaque exécution de test en réutilisant les mêmes données déjà stockées sur Rescale. Si nous devons tester une version logicielle volumineuse, nous aimerions également réutiliser les données déjà téléchargées, mais chaque version testée sera généralement différente. Dans de nombreux cas cependant, seul un petit sous-ensemble de fichiers de l’ensemble du package changera d’une version à l’autre.
Pour tirer parti de la similarité des builds, nous pouvons fournir un delta de build incrémentiel qui sera décompressé au-dessus de l'arborescence de build de base que nous avons téléchargée lors du premier travail. Il n'y a que 2 exigences :

  1. Le delta de build doit avoir la même structure de répertoires que la build de base
  2. Nous devons spécifier l'archive delta de build comme fichier d'entrée APRÈS l'archive de build de base
Voici un extrait avec ce changement : FULL_BUILD_ARCHIVE = 'inputs/echoware0.1.tar.gz' BUILD_DELTA = 'inputs/echoware0.2-delta.tar.gz' # rechercher par nom sur Rescale base_build_input = \ rescale.client.RescaleFile .get_newest_by_name(FULL_BUILD_ARCHIVE) # télécharger la copie locale incrémental_build_input = rescale.client.RescaleFile(file_path=BUILD_DELTA) def create_job(name, test_input, core_type, core_count) : input_files = [base_build_input, test_input, incrémental_build_input] job_definition = { 'name': JOB_NAME , 'isLowPriority' : True, 'jobanalyses' : [ { 'analysis' : { 'code' : 'custom' }, 'hardware' : { 'coresPerSlot' : 1, 'slots' : core_count, 'coreType' : { ' code' : core_type } }, 'inputFiles' : [{'id' : inp.id} pour inp dans input_files], 'command' : TEST_COMMAND, 'postProcessScript' : {'id' : post_process_file.id}, 'postProcessScriptCommand' : POST_RUN_COMPARE_COMMAND } ], } renvoie rescale.client.RescaleJob(json_data=job_definition)

Au dessus, base_build_input vient du fichier déjà sur Rescale et incrémental_build_input est téléchargé à chaque fois.

Parallélisme dans les travaux de conception d'expériences (DOE)

Une autre façon d'exécuter des tests consiste à regrouper plusieurs tests dans une seule tâche DOE. Le nombre de tests pouvant s'exécuter en parallèle est ensuite défini par le nombre d'emplacements de tâches que vous configurez pour votre travail. Vous structureriez ensuite vos exécutions de tests afin qu'elles puissent être paramétrées par un fichier de configuration modèle, comme décrit dans https://www.rescale.com/resources/getting-started/doe/.
Cette méthode présente l’avantage d’éliminer le temps de configuration du cluster de tâches par rapport au cas multi-tâches. L'inconvénient est que chaque exécution de test est limitée à la même configuration matérielle que celle que vous définissez pour un emplacement de tâche. Pour un exemple sur la façon de configurer une tâche DOE avec le SDK Python, consultez https://github.com/rescale/python-sdk/tree/master/examples/doe.

Téléchargements de fichiers volumineux

Dans les exemples ci-dessus, nous avons téléchargé nos fichiers d'entrée avec une simple requête PUT. Cela sera lent et/ou ne fonctionnera souvent pas pour les fichiers de plusieurs gigaoctets. Une alternative consiste à utiliser l'outil Rescale CLI, qui permet des téléchargements et des téléchargements de fichiers optimisés en bande passante et peut reprendre les transferts s'ils sont interrompus.
Pour plus d’informations sur la CLI Rescale, voir ici : support@rescale.com.
L'exécution de tests sur Rescale est un excellent moyen de réduire le temps de test et la pression sur les ressources de calcul internes pour les grandes suites de tests de régression et de performances. L'API Rescale offre un moyen très flexible de lancer des groupes de tests sur diverses configurations matérielles, notamment des clusters à mémoire élevée, à stockage élevé, infiniband et compatibles GPU. Si vous souhaitez effectuer vos propres tests sur Rescale, consultez notre SDK et nos exemples de scripts sur https://github.com/rescale/python-sdk ou contactez-nous au support@rescale.com.

Auteur

  • Mark Whitney

    Mark Whitney est directeur de l'ingénierie chez Rescale. Ses domaines d'expertise comprennent les architectures de calcul haute performance, la recherche sur l'information quantique et le cloud computing. Il est titulaire d'un doctorat en informatique de l'Université de Californie à Berkeley.