BYO-Software-Regressionstests auf Rescale

Blogvorlage 46

 Regressionstests2
Rescale ist eine wertvolle Ressource für Regressions- und Performancetests für Softwareanbieter. Mithilfe unserer API und Kommandozeilentools zeigen wir Ihnen, wie Sie alle oder einen Teil Ihrer internen Regressionstests auf Rescale ausführen können. Die Vorteile von Tests mit Rescale sind:

  1. Rechenressourcen stehen auf Abruf zur Verfügung, Sie zahlen also nur, wenn Sie tatsächlich Tests durchführen
  2. Die Rechenressourcen sind zudem skalierbar, sodass Sie eine große Testsuite parallel ausführen und schneller Feedback erhalten können.
  3. Es stehen heterogene Ressourcen zur Verfügung, um die Softwareleistung auf verschiedenen Hardwarekonfigurationen zu testen (z. B. Infiniband, 10GigE, Tesla- und Phi-Coprozessoren).
  4. Durch das Testen Ihrer Software auf Rescale können Sie optional anderen Kunden auf Rescale private Betaversionen zur Verfügung stellen.

Test-Setup

Für den Rest dieses Artikels gehen wir davon aus, dass Sie über die folgenden Dateisätze verfügen:

  1. Vollständiger Build-Baum Ihres Softwarepakets in einem allgemein unterstützten Archivformat (tar.gz, zip usw.)
  2. Archivierter Satz von Referenztesteingaben und erwarteten Ausgaben
  3. Skript oder Befehlszeile zum Ausführen Ihres Software-Builds anhand einer oder mehrerer Testeingaben
  4. Skript zum Vergleichen der tatsächlichen Testausgabe mit der erwarteten Ausgabe
  5. (Optional) Kleinerer Satz inkrementeller Build-Produkte, die über den vollständigen Build-Baum gelegt werden

In den folgenden Beispielen verwenden wir unseren Python SDKEine Auswahl der folgenden Beispiele ist im SDK-Repository verfügbar werden auf dieser Seite erläutert. Das SDK umschließt lediglich unsere REST-API, sodass Sie diese Beispiele mithilfe der in https://github.com/rescale/python-sdk/blob/master/rescale/client.py.
Beachten Sie, dass für alle diese Beispiele Folgendes erforderlich ist:

  1. Ein Konto auf der Plattform neu skalieren
  2. Ein Einheimischer RESCALE_API_KEY Umgebungsvariable, die auf Ihren API-Schlüssel eingestellt ist, den Sie auf der Hauptseite der Plattform unter „Einstellungen“ -> „API“ finden.

Ausführen von Tests aus einem einzelnen Job

Wir beginnen mit dem einfachsten Beispiel: Wir laden einen vollständigen Build und Testreferenzdaten als Job-Eingabedateien hoch, führen die Tests seriell aus und vergleichen die Ergebnisse. Beginnen wir mit einer Beispielsoftware, die wir hochladen und ausführen. Hier ist eine Liste des Softwarepakets und der Testdateien:

echoware/bin/echo.sh (gibt einfach „Test in“ an „Test out“) test[0-9]/in (Testeingabe) test[0-9]/expected_out (erwartete Testausgabe) test[0-9]/out (tatsächliche Testlaufausgabe)

Jeder Software-Build und Testfall wird separat archiviert. Hier sind die Schritte zur Vorbereitung und Ausführung unseres Testsuite-Jobs:

  1. Laden Sie den Build, die Referenztestdaten und das Ergebnisvergleichsskript mit dem Rescale Python SDK hoch:
#!/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(Dateipfad=TEST_ARCHIVE), rescale.client.RescaleFile(Dateipfad=BUILD_ARCHIVE), ] post_process_file = \ rescale.client.RescaleFile(Dateipfad=POST_COMPARE_SCRIPT)

Datei neu skalieren lädt den lokalen Dateiinhalt in Rescale hoch und gibt Metadaten zurück, um auf diese Datei zu verweisen. An diesem Punkt können Sie diese Dateien unter  https://www.rescale.com/route/files/.
      2. Erstellen Sie den Testsuite-Job:

TEST_COMMAND = """
für Testfall in $(find . -name "test[0-9]*" -type d); do ./echoware/bin/echo.sh $testcase done """ POST_RUN_COMPARE_COMMAND = """ für Testfall in $(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': [ { 'analysis': { 'code': 'custom' }, 'hardware': { 'coresPerSlot': 1, 'slots': 1, 'coreType': { 'code': 'standard-plus' } }, 'inputFiles': [{'id': inp.id} für inp in input_files], 'command': TEST_COMMAND, 'postProcessScript': {'id': post_process_file.id}, 'postProcessScriptCommand': POST_RUN_COMPARE_COMMAND } ], } job = rescale.client.RescaleJob(json_data=job_definition)

NeuskalierenJob erstellt einen neuen Job, den Sie nun unter https://www.rescale.com/route/jobs/. Beachten Sie, dass wir den Job hier auf einem einzelnen Marble-Kern ausführen. Sie können weitere Kerne ausführen, indem Sie Entsprechender Steckplatz oder ändern Sie den Kerntyp, indem Sie einen anderen Kerntypcode auswählen RescaleConnect.get_core_types().
Beachten Sie, dass die Befehl und postProcessScriptCommand Felder können jedes gültige Bash-Skript sein, sodass Sie bei der Ausführung Ihres Tests und der Auswertung der Ergebnisse ziemlich flexibel sind. In unserem sehr einfachen Beispiel vergleicht der Post-Test-Befehlsvergleich lediglich die und erwartetes_Ausgangsergebnis Dateien in jedem Testfallverzeichnis.

  1. Senden Sie den Auftrag zur Ausführung und warten Sie, bis er abgeschlossen ist:
job.submit() job.wait()

Sobald der Job-Cluster bereitgestellt ist, werden die Eingabedateien unverschlüsselt in den Cluster übertragen und anschließend im Arbeitsverzeichnis dekomprimiert. Anschließend wird der TEST_COMMAND ausgeführt wird, gefolgt von der POST_RUN_COMPARE_COMMAND.

  1. Laden Sie die Testergebnisse herunter. Alle Rescale-Jobbefehle haben stdout umgeleitet auf process_output.log Laden wir einfach diese eine Datei herunter, um die Zusammenfassung der Testergebnisse zu erhalten.
STDOUT_LOG = 'Prozessausgabe.log' test_log = job.get_file(STDOUT_LOG) test_log.download(Ziel=test_log.name)

Wichtig ist, dass wir durch den Vergleich der Testergebnisse als Nachbearbeitungsschritt in unserem Job das Herunterladen potenziell großer Ausgabedateien vermeiden, was die Bearbeitungszeit der Testergebnisse verzögern würde. Dies löst jedoch nicht das Problem, dass wir die Testreferenzausgaben, die oft eine ähnliche Größe wie die tatsächliche Ausgabe haben, trotzdem hochladen müssen. Der Schlüssel liegt darin, dass wir Dateien nur dann zu Rescale hochladen müssen, wenn sie sich ändern, nicht bei jedem Testjob, den wir starten. Vorausgesetzt, unsere Referenztestfälle ändern sich nicht sehr oft, können wir die gerade zu Rescale hochgeladenen Dateien nun in späteren Testläufen wiederverwenden.
Dieses Beispiel finden Sie in voller Länge werden auf dieser Seite erläutert.

Wiederverwendung von Referenztestdaten

Wir werden nun das obige Verfahren ändern, um das Hochladen von Referenztestdaten für jeden übermittelten Testauftrag zu vermeiden.

  1. (geändert) Suchen Sie bei Rescale nach Testdatei-Metadaten und verwenden Sie diese als Eingabedatei für nachfolgende Jobs:
# Weglassen des Variablen-Setups von oben 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 ruft lediglich Metadaten für die Testdatei ab, die bereits in Rescale hochgeladen wurde. Beachten Sie: Wenn Sie mehrere Testarchive mit demselben Namen hochgeladen haben, wird das zuletzt hochgeladene Archiv ausgewählt.
Die Schritte 2 bis 4 sind dieselben wie im vorherigen Beispiel.

Parallelisieren Sie Tests mit langer Laufzeit

In den vorherigen Beispielen wurden alle Tests sequenziell ausgeführt. Lassen Sie uns nun einige parallel ausführen. Für dieses Beispiel gehen wir davon aus, dass unsere Tests in „kurze“ und „lange“ Tests unterteilt sind. Die kurzen Tests befinden sich in einem Archiv namens all_short_tests.tar.gz und jeder lange Test befindet sich in einem separaten Archiv namens long_test_.tar.gz.
Wir starten nun einen einzelnen Job für alle Kurztests und einen Job pro Test für die Langtests. Wir gehen davon aus, dass diese Testdateien bereits in Rescale hochgeladen wurden, wie im ersten Beispiel.

# Befehlsvariable Setup weglassen 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' # Suche nach Namen bei Rescale statt Hochladen von lokaler Kopie 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)) für i im Bereich(LONG_TEST_COUNT): post_process_file = \ rescale.client.RescaleFile.get_newest_by_name(POST_COMPARE_SCRIPT) # lokale Kopie hochladen 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': Name, 'isLowPriority': True, 'jobanalyses': [ { 'analysis': { 'code': 'custom' }, 'hardware': { 'coresPerSlot': core_count, 'slots': 1, 'coreType': { 'code': core_type } }, 'inputFiles': [{'id': inp.id} für inp in input_files], 'command': TEST_COMMAND, 'postProcessScript': {'id': post_process_file.id}, 'postProcessScriptCommand': POST_RUN_COMPARE_COMMAND } ], } return rescale.client.RescaleJob(json_data=job_definition) # alle Testjobs erstellen 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) für i, long_test in enumerate(long_test_inputs)] test_jobs = [short_test_job] + long_test_jobs # alle übermitteln

[job.submit() für Job in test_jobs]

# warte, bis alles abgeschlossen ist

[job.wait() für Job in test_jobs]

# Ergebnisse abrufen [job.get_file(STDOUT_LOG).download(target='{0}.out'.format(job.name)) für Job in test_jobs]

In diesem Beispiel haben wir unseren kurzen Testjob mit einem einzelnen Marble-Kern und jeden unserer langen Tests mit einem Nickel-MPI-Cluster mit 32 Kernen (2 Knoten) gestartet.
Diese Testjob-Konfiguration eignet sich besonders für Leistungstests. Um die Skalierbarkeit einer bestimmten Build- und Testfallkombination zu testen, können Sie 4 Jobs mit jeweils 1, 2, 4 und 8 Knoten starten.
Dieses Beispiel finden Sie werden auf dieser Seite erläutert.

Inkrementelle Builds

Im obigen Beispiel haben wir das erneute Hochladen von Testdaten für jeden Testlauf vermieden, indem wir dieselben bereits in Rescale gespeicherten Daten wiederverwendet haben. Wenn wir einen großen Software-Build testen müssen, möchten wir auch bereits hochgeladene Daten wiederverwenden, aber jeder getestete Build ist in der Regel anders. In vielen Fällen ändert sich jedoch nur eine kleine Teilmenge der Dateien des Gesamtpakets von Build zu Build.
Um die Ähnlichkeit der Builds zu nutzen, können wir ein inkrementelles Build-Delta bereitstellen, das über dem im ersten Job hochgeladenen Basis-Build-Baum dekomprimiert wird. Es gibt nur zwei Anforderungen:

  1. Das Build-Delta muss dieselbe Verzeichnisstruktur wie der Basis-Build haben
  2. Wir müssen das Build-Delta-Archiv als Eingabedatei NACH dem Basis-Build-Archiv angeben
Hier ist ein Auszug mit dieser Änderung: FULL_BUILD_ARCHIVE = 'inputs/echoware0.1.tar.gz' BUILD_DELTA = 'inputs/echoware0.2-delta.tar.gz' # nach Namen suchen bei Rescale base_build_input = \ rescale.client.RescaleFile.get_newest_by_name(FULL_BUILD_ARCHIVE) # lokale Kopie hochladen incremental_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, incremental_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} für inp in input_files], 'command': TEST_COMMAND, 'postProcessScript': {'id': post_process_file.id}, 'postProcessScriptCommand': POST_RUN_COMPARE_COMMAND } ], } return rescale.client.RescaleJob(json_data=job_definition)

In obigem, base_build_input stammt aus der Datei bereits auf Rescale und incremental_build_input wird jedes Mal hochgeladen.

Parallelität in Design-of-Experiment (DOE)-Jobs

Eine weitere Möglichkeit, Tests auszuführen, besteht darin, mehrere Tests in einem einzigen DOE-Job zu gruppieren. Die Anzahl der parallel ablaufenden Tests wird dann durch die Anzahl der für Ihren Job konfigurierten Task-Slots bestimmt. Sie strukturieren Ihre Testläufe dann so, dass sie durch eine vorlagenbasierte Konfigurationsdatei parametrisiert werden können, wie in beschrieben https://www.rescale.com/resources/getting-started/doe/.
Diese Methode bietet den Vorteil, dass die Einrichtungszeit für Jobcluster im Vergleich zum Multi-Job-Verfahren entfällt. Der Nachteil besteht darin, dass jeder Testlauf auf die gleiche Hardwarekonfiguration beschränkt ist, die Sie für einen Task-Slot definieren. Ein Beispiel zum Einrichten eines DOE-Jobs mit dem Python SDK finden Sie unter https://github.com/rescale/python-sdk/tree/master/examples/doe.

Große Datei-Uploads

In den obigen Beispielen haben wir unsere Eingabedateien mit einer einfachen PUT-Anfrage hochgeladen. Dies ist langsam und/oder funktioniert bei Dateien mit mehreren Gigabyte oft nicht. Alternativ können Sie das CLI-Tool Rescale verwenden, das bandbreitenoptimierte Datei-Uploads und -Downloads ermöglicht und Übertragungen nach Unterbrechungen fortsetzen kann.
Weitere Informationen zur Rescale-CLI finden Sie hier: support@rescale.com.
Das Ausführen von Tests mit Rescale ist eine hervorragende Möglichkeit, die Testzeit und die Belastung interner Rechenressourcen für umfangreiche Regressions- und Performance-Testsuiten zu reduzieren. Die Rescale-API bietet eine sehr flexible Möglichkeit, Testgruppen auf unterschiedlichen Hardwarekonfigurationen zu starten, darunter Cluster mit hohem Arbeitsspeicher, hohem Speicherbedarf, Infiniband und GPU-fähigen Clustern. Wenn Sie eigene Tests mit Rescale durchführen möchten, sehen Sie sich unser SDK und Beispielskripte an unter https://github.com/rescale/python-sdk oder Sie kontaktieren uns unter support@rescale.com.

Autorin

  • Mark Whitney

    Mark Whitney ist technischer Leiter bei Rescale. Zu seinen Fachgebieten gehören Hochleistungsrechnerarchitekturen, Quanteninformationsforschung und Cloud Computing. Er promovierte in Informatik an der University of California, Berkeley.