openQA per sviluppatori e responsabili
KDE Linux usa openQA per eseguire test automatici su ogni immagine di sistema prima della sua pubblicazione. openQA avvia ciascuna immagine in una macchina virtuale e controlla che possa essere installata, aggiornata e utilizzata correttamente.
openQA possiede una sua terminologia specifica (che verrà mantenuta): un job è una esecuzione di una suite di test su un'immagine, mentre un worker è la macchina o il contenitore che esegue il job. La documentazione openQA spiega tutti i concetti, inclusi moduli dei test, gruppi di job, risorse e interfaccia web.
Come funzionano i test
Il worker del test viene eseguito insieme alla pipeline CI, in modo che possa usare le immagini e i dischi virtuali prodotti dalla generazione senza caricare quei file di grandi dimensioni altrove. I test sono inviati alla macchina virtuale tramite un'estensione del sistema e comunicano con essa su SSH.
I test desktop usano l'API di accessibilità attraverso selenium-webdriver-at-spi, che permette loro di interagire con le applicazioni e verificare il loro stato senza dover fare affidamento esclusivamente sulla corrispondenza delle schermate (screenshot). La suite controlla anche gli errori dei servizi di sistema, i processi desktop con crash, i problemi di rete e le regressioni nei comandi di KDE Linux.
Pipeline CI e flusso di lavoro dei responsabili
Nel ramo predefinito protetto del deposito upstream di KDE Linux, la pipeline funziona come segue:
- Il job di creazione dell'immagine genera, firma e crea le fasi dell'immagine e del suo canale sysupdate su
storage.kde.org. - Il job trigger-openqa avvia la pipeline in os-autoinst-distri-kdelinux, passandogli l'URL dell'immagine modificata e l'URL del canale sysupdate.
- La pipeline downstream esegue il normale flusso install-and-sanity e aggiorna il flusso.
- Il job upstream publish viene eseguito solo dopo che la pipeline openQA è stata completata correttamente. Promuove l'immagine già modificata per l'utilizzo pubblico.
Questo fa sì che un errore di openQA diventi un ostacolo alla pubblicazione dell'immagine. Inizia aprendo la pipeline downstream dal job trigger-openqa, quindi apri il job openQA che ha restituito l'errore dal relativo registro CI. La pagina del job mostra i moduli di test in ordine e ti permette di identificare se l'errore è nell'installazione, nei test di integrità del sistema installato o nel percorso di aggiornamento.
Scaricare un'immagine modificata
Il link fornito dopo il messaggio di log “In case of failure, you can inspect and download the .iso image and sysupdate tree at…” apre un browser di archiviazione per la build in fase di test. Scarica il file .iso da lì per avviarlo manualmente o forniscilo a uno stack openQA locale. L'albero sysupdate contiene gli artefatti di aggiornamento modificati utilizzati dal test di aggiornamento.
Investigare un job con errore
Apri il job nell'interfaccia web di openQA e seleziona prima il modulo che ha generato l'errore. Il collegamento all'interfaccia web comparirà dopo il messaggio di registro del “job del test autoinst-log.txt per il worker openQA e il registro test-engine. Questo è il registro principale per lo stesso job.
Per i test che eseguono comandi sul sistema in esame, KDE Linux li esegue in servizi systemd temporanei e registra il registro (journal) del servizio come output diagnostico nel risultato del test. Esamina il file autoinst-log.txt per visualizzare l'output del comando e il registro. Puoi anche scaricare il file *kde-linux-collected-logs.tar.zst per i registri acquisiti dallo strumento collect-logs.
Eseguire i test localmente
Prima di inviare una modifica, puoi usare openQA per un'immagine generata localmente. Ciò è richiesto quando lavori da un fork esterno a kde-linux/kde-linux - la sua pipeline CI genera un'immagine di test ma non attiva la pipeline openQA. È anche utile durante lo sviluppo di test per le tue modifiche personali.
Il deposito dei test openQA di KDE Linux include uno stack locale che contiene sia un'interfaccia web openQA, sia un worker. Richiede Podman e podman-compose, inclusi in KDE Linux.
Clona il deposito dei test, quindi posiziona l'immagine .iso di KDE Linux che vuoi testare nella sua cartella di root. Se non è presente alcuna immagine, l'esecutore dei test scaricherà l'ultima immagine disponibile pubblicamente.
Avvia lo stack locale.
./mock.sh up
Una volta pronta, apri http://localhost:1080 per ispezionare l'interfaccia web di openQA. I job non vengono inviati automaticamente, pertanto apri una shell nel container e invia il normale flusso install e sanity-test.
podman exec -it openqa-single-instance bash ./qa flow
Per eseguire invece un flusso di aggiornamento, aggiungi --upgrade:
./qa flow --upgrade
Per eseguire il flusso disk-encryption install-test, aggiungi --encrypt:
./qa flow --encrypt
Puoi anche combinare le opzioni --upgrade e --encrypt:
./qa flow --upgrade --encrypt
Il job sanity-test ha bisogno che sia eseguito prima il job install-system perché utilizza il disco virtuale creato dall'installazione. Per concentrarti su un test specifico, regola i job inviati in lib/worker/job_flow.py o eseguire un job individuale con ./qa job tenendo presente quella dipendenza. Esegui ./qa job --help per vedere le opzioni richieste.
Per esempio, per eseguire un job install-system in cui hai scaricato un .iso nella cartella del deposito:
./qa job \
--live ./kde-linux_<build-id>.iso \
--hdd kde-linux_<build-id>.qcow2 \
--sysext ./openqa-sysext.img \
--build <build-id> \
--name install-system \
--flavor live
Quando hai terminato, ferma lo stack e rimuovi i suoi volumi locali.
./mock.sh down -v
Scrivere un test
Le definizioni dei test e il codice di test si trovano in os-autoinst-distri-kdelinux. Aggiungi il test al flusso appropriato in main.pm: il flusso live-install, il flusso installed-system sanity o quello upgrade. I test sono in genere composti da un piccolo wrapper openQA in tests/ e da un test Python che viene eseguito sul sistema da testare da extensions/openqa/usr/lib/kde-linux-openqa/tests/.
Scegli il tipo di test in base a quello che stai verificando:
- Usa un
unittestPython standard per gli strumenti a riga di comando, i servizi, lo stato del filesystem o altri comportamenti non grafici. - Usa Selenium tramite
selenium-webdriver-at-spiquando il comportamento richiede l'interazione con un'applicazione grafica.
Test Python standard
Crea un unittest nella cartella di test dell'estensione di sistema menzionata in precedenza. Dovrebbe eseguire delle asserzioni sul sistema in fase di test e scrivere i risultati in un file XML JUnit, che verrà automaticamente raccolto come risorsa openQA e report di GitLab CI.
import unittest
from lib.sut import openqa_junit_xml
class ExampleTests(unittest.TestCase):
def test_expected_behaviour(self):
self.assertTrue(True)
if __name__ == "__main__":
openqa_junit_xml.run(ExampleTests, "esempio")
Crea un wrapper corrispondente nella cartella tests/ che permetta a openQA di eseguire il test sull'host.
from testapi import *
from lib.test import cli_test
def run(self):
cli_test.CliTest("esempio").run_python()
Infine, registra il wrapper nel flusso pertinente in main.pm. Il wrapper raccogli il risultato JUnit e l'output dei comandi in modo che gli errori vengano visualizzati nel job openQA job e negli artefatti CI.
Test grafici con Selenium
I test grafici sono anch'essi classi unittest Python, ma utilizzano il driver Appium/Selenium per trovare e interagire con gli elementi di interfaccia accessibili. L'esecutore Selenium abilita l'infrastruttura per l'accessibilità e avvia il driver al posto tuo.
Per esempio, un test grafico crea un driver per la sua applicazione, interagisce con gli elementi accessibili e poi chiude il driver:
import unittest
from appium import webdriver
from appium.options.common.base import AppiumOptions
from appium.webdriver.common.appiumby import AppiumBy
from lib.test import openqa_junit_xml
class ExampleTests(unittest.TestCase):
@classmethod
def setUpClass(cls):
options = AppiumOptions()
options.set_capability("app", "org.kde.example.desktop")
cls.driver = webdriver.Remote("http://127.0.0.1:4723", options=options)
@classmethod
def tearDownClass(cls):
cls.driver.quit()
def test_expected_behaviour(self):
self.driver.find_element(AppiumBy.ACCESSIBILITY_ID, "example-control").click()
if __name__ == "__main__":
openqa_junit_xml.run(ExampleTests, "esempio")
Esegui il wrapper corrispondente con run_selenium() anziché run_python(). Passa l'utente di test installato per le applicazioni desktop:
from testapi import *
from lib.test import cli_test
from lib.common import user_manager
def run(self):
cli_test.CliTest("esempio").run_selenium(user=user_manager.installed())
Analogamente ai test Python standard, aggiungi il suo wrapper a main.pm, eseguilo localmente mentre lo sviluppi e and includilo nel flusso del test che simula la funzionalità.
Per ulteriori informazioni su come scrivere test con Selenium, vedi le pagine Appium automation testing e GUI Testing with selenium-webdriver-at-spi (in inglese).
Ulteriori informazioni
Per una spiegazione più dettagliata del flusso dei test e la sua implementazione, vedi openQA Testing in KDE Linux. L'infrastruttura dei test è sviluppata nel deposito os-autoinst-distri-kdelinux.
Articolo scritto da Thomas Duckworth sotto licenza CC-BY-4.0.