openQA para Desenvolvedores e Mantenedores
O KDE Linux utiliza o openQA para testar automaticamente cada imagem do sistema antes de sua publicação. O openQA inicializa cada imagem em uma máquina virtual e verifica se ela pode ser instalada, atualizada e utilizada com sucesso.
O openQA possui uma terminologia específica: um job é uma execução de uma suíte de testes em uma imagem, enquanto um worker é a máquina ou o contêiner que executa o job. A documentação do openQA explica esses e outros conceitos, incluindo módulos de teste, grupos de jobs, ativos e a interface web.
Como os testes funcionam
O worker de testes é executado em paralelo ao pipeline de CI, permitindo utilizar as imagens e os discos virtuais gerados pelo processo de compilação sem a necessidade de transferir esses arquivos grandes para outro local. Os testes são fornecidos à máquina virtual por meio de uma extensão do sistema e comunicam-se com ela via SSH.
Os testes de desktop utilizam a API de acessibilidade por meio do selenium-webdriver-at-spi, permitindo interagir com aplicativos e verificar seu estado sem depender exclusivamente da comparação de capturas de tela. O conjunto de testes também verifica falhas em serviços do sistema, travamentos de processos de desktop, problemas de rede e regressões em comandos do KDE Linux.
Pipeline de CI e fluxo de trabalho do mantenedor
Na branch padrão protegida do repositório upstream KDE Linux, o pipeline funciona da seguinte forma:
- O job de imaging cria, assina e prepara a imagem e seu canal de atualização do sistema no
storage.kde.org. - O job de trigger-openqa inicia o pipeline em os-autoinst-distri-kdelinux, passando a ele a URL da imagem preparada (staged image) e a URL do canal sysupdate.
- O pipeline subsequente executa os fluxos de
installeupgradedeclarados emtests/tests.toml, cada um com instalações simples e criptografadas. - O job de publish (publicação) seguinte é executado somente após a conclusão bem-sucedida do pipeline do openQA. Ele promove a imagem já preparada para disponibilização pública.
Isso faz com que uma falha no openQA atue como um bloqueio para a publicação da imagem. Comece abrindo o pipeline downstream a partir do job trigger-openqa e, em seguida, abra o job do openQA que falhou, acessando-o pelo log de CI. A página do job exibe os módulos de teste em ordem, permitindo identificar se a falha ocorreu na instalação, nos testes de sanidade do sistema instalado ou no processo de atualização.
Baixar uma imagem preparada
O link fornecido após a mensagem de log “In case of failure, inspect and download the image and sysupdate tree at…” abre um navegador de armazenamento para a build em teste. Baixe o arquivo .iso nesse local para inicializá-lo manualmente ou fornecê-lo a uma instância local do openQA. A árvore sysupdate contém os artefatos de atualização preparados para uso no teste de upgrade.
Investigar o job que falhou
Abra o job na interface web do openQA e selecione primeiro o módulo que falhou. O link para a interface web aparecerá após a mensagem de log “autoinst-log.txt referente ao worker do openQA e ao log do motor de testes (test-engine). Este é o log principal do próprio job.
Para testes que executam comandos no sistema em teste, o KDE Linux os executa em serviços systemd temporários e registra o diário do serviço como saída de diagnóstico no resultado do teste. Verifique o arquivo autoinst-log.txt para consultar a saída do comando e o diário. Você também pode baixar o arquivo *kde-linux-collected-logs.tar.zst contendo os logs capturados pela ferramenta collect-logs.
Executar testes localmente
Você pode usar o openQA para testar uma imagem compilada localmente antes de enviar uma alteração. Isso é necessário ao trabalhar a partir de um fork fora de kde-linux/kde-linux — o pipeline de CI desse repositório compila uma imagem de teste, mas não aciona o pipeline do openQA. Também é útil durante o desenvolvimento de testes para suas próprias alterações.
O repositório de testes openQA do KDE Linux inclui uma pilha local contendo tanto a interface web do openQA quanto um worker. Ele requer o Python 3.14, o Podman e o podman-compose, que estão incluídos no KDE Linux.
Clone o repositório de testes e, em seguida, coloque a imagem .iso do KDE Linux que você deseja testar no diretório raiz dele. Se nenhuma imagem estiver presente, o executor de testes baixará a imagem mais recente disponível publicamente.
Iniciar a pilha local a partir do repositório raiz
./qa-mock up
Assim que estiver pronto, acesse http://localhost:1080 para inspecionar a interface web do openQA. Os jobs não são enviados automaticamente; portanto, abra um shell no container e envie o fluxo install. Execute os comandos flow, job e build-sysext dentro deste container.
./qa-mock enter ./qa flow install
Para executar o fluxo de atualização:
./qa flow upgrade
Para uma instalação criptografada, passe FDE_INSTALL=1 como uma configuração do openQA. O sufixo da variante distingue os jobs criptografados na interface web:
./qa flow install --var FDE_INSTALL=1 --flavor-suffix=-encrypted ./qa flow upgrade --var FDE_INSTALL=1 --flavor-suffix=-encrypted
--var NOME=VALOR pode ser repetido, mas cada variável deve ser declarada na lista variables do fluxo selecionado em tests/tests.toml.
A suíte sanity-test utiliza o disco virtual criado pelo install-system. Para focar em testes específicos, edite a lista tests da suíte no arquivo tests/tests.toml ou declare outra suíte e o respectivo fluxo nesse arquivo. Você também pode executar uma suíte individual usando ./qa job --name <suíte>, levando em conta as dependências de disco associadas a ela. Os comandos flow, job e build-sysext aceitam o parâmetro --manifest-path <caminho> para utilizar um manifesto diferente de tests/tests.toml. Os caminhos dos testes especificados no manifesto são relativos ao diretório do próprio manifesto. Execute ./qa job --help para consultar as opções obrigatórias.
Por exemplo, compile a extensão do sistema e execute um job install-system com um arquivo .iso no diretório do repositório. Substitua o ID da compilação e a variante pelos da sua imagem:
./qa build-sysext
./qa job \
--live ./kde-linux_<id do build>.iso \
--hdd kde-linux_<id do build>.qcow2 \
--sysext ./openqa-sysext.img \
--build <id do build> \
--variant <variante> \
--name install-system \
--flavor live
Quando terminar, saia do shell do contêiner; em seguida, pare a pilha e remova seus volumes locais do host.
./qa-mock down -v
O ./qa-mock repassa os argumentos para o podman-compose. Por exemplo, use ./qa-mock up -d para iniciar a pilha em segundo plano.
Testar atualizações entre builds locais
Coloque a ISO base na raiz do repositório de testes. Ao iniciar a pilha a partir do host, defina KDE_LINUX_OUTPUT para o diretório mkosi.output que contém a build para a qual você deseja atualizar:
KDE_LINUX_OUTPUT=~/Repositories/kde-linux/mkosi.output ./qa-mock up
Entre no contêiner e execute o fluxo de atualização, substituindo <id do build> pelo ID de build da sua ISO base:
./qa-mock enter
./qa flow upgrade \
--upgrade-from /casedir/kde-linux_<id do build>.iso \
--upgrade-to /kde-linux-output
Ambas as opções devem ser fornecidas em conjunto. O repositório é montado em /casedir, e o diretório mkosi.output selecionado é montado em modo somente leitura em /kde-linux-output. A compilação mais recente nesse diretório é disponibilizada para a máquina virtual como uma fonte temporária para o sysupdate e deve ser mais recente do que a ISO base. Adicione --var FDE_INSTALL=1 --flavor-suffix=-encrypted para testar uma instalação com criptografia.
Declarar suítes e fluxos
tests/tests.toml define a distribuição (name e distri), as suítes e os fluxos. Uma suíte é uma lista ordenada de testes executados em um único job do openQA. Sua action determina como o job utiliza o disco virtual:
installinicializa a imagem live e cria o disco instalado.upgradeinicializa o disco instalado e defineDO_UPGRADEpara testes que realizam a atualização.testinicializa o disco instalado para validação.
Um fluxo executa conjuntos de testes na ordem declarada, passando o disco instalado entre as tarefas. O KDE Linux define estes fluxos:
[flows.install] suites = ["install-system", "sanity-test"] variables = ["FDE_INSTALL"] [flows.upgrade] suites = ["install-system", "upgrade-system", "sanity-test"] variables = ["FDE_INSTALL"]
O fluxo install instala e verifica a compilação em teste. O fluxo upgrade primeiro instala uma imagem mais antiga, atualiza-a para a compilação em teste e, em seguida, executa testes de sanidade. Para uma compilação de CI em etapas (staged), a base é a imagem publicada mais recente. Sem os parâmetros --upgrade-from e --upgrade-to, uma execução de atualização local terá como padrão a build pública mais recente como destino, utilizando uma ISO local mais antiga (caso esteja disponível) ou baixando a imagem anterior.
Antes de cada tarefa, o worker gera um diretório de teste temporário (CASEDIR) com um script main.pm para a suíte selecionada. Os testes são executados na ordem do manifesto, incluindo entradas repetidas. Edite o manifesto para alterar a sequência de execução.
Escrever um teste
As definições de teste e o código estão localizados em os-autoinst-distri-kdelinux. Adicione scripts de teste em tests/, junto a testes relacionados, e registre-os no array tests da suíte apropriada, dentro do arquivo tests/tests.toml. Por exemplo:
[suites.example]
action = "test"
tests = [
{ path = "common/bootup.py", type = "openqa" },
{ path = "common/basic_test.py", type = "python" },
{ path = "kdelinux/app/firefox.py", type = "selenium", user = "installed", timeout = 180 },
]
Os testes openqa são executados no worker e utilizam diretamente as APIs do openQA. Os testes em python e selenium são executados no sistema sob teste; a extensão do sistema empacota os scripts automaticamente, e o worker gera os wrappers de teste do openQA que os invocam.
Escolha o tipo de teste com base no que você está verificando:
- Use o
unittestpadrão do Python para ferramentas de linha de comando, serviços, estado do sistema de arquivos ou outros comportamentos não gráficos. - Use o Selenium por meio do
selenium-webdriver-at-spiquando o comportamento exigir a interação com uma aplicação gráfica.
Testes Python normais
Crie um teste unittest em tests/, por exemplo, tests/kdelinux/system/example.py. Ele deve realizar asserções sobre o sistema em teste e gravar os resultados em um arquivo XML no formato JUnit, o qual será coletado automaticamente como um ativo do openQA e um relatório do 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(TestesExemplo, "exemplo")
Adicione uma entrada ao array tests da suíte correspondente:
{ path = "kdelinux/system/example.py", type = "python" },
O wrapper gerado coleta o resultado do JUnit e a saída do comando, de modo que as falhas apareçam no job do openQA e nos artefatos de CI.
Testes gráficos com Selenium
Testes gráficos também são classes unittest do Python, mas utilizam o driver do Appium/Selenium para localizar e interagir com elementos de interface acessíveis. O executor do Selenium habilita a infraestrutura de acessibilidade e inicia o driver para você.
Por exemplo, um teste gráfico cria um driver para sua aplicação, interage com elementos acessíveis e fecha o driver em seguida:
import unittest
from appium import webdriver
from appium.options.common.base import AppiumOptions
from appium.webdriver.common.appiumby import AppiumBy
from lib.sut import openqa_junit_xml
class TestesExemplo(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(TestesExemplo, "exemplo")
Salve o script como, por exemplo, tests/kdelinux/app/exemplo.py e adicione-o à suíte correspondente com type = "selenium". Selecione o usuário de teste instalado para aplicativos de desktop:
{ path = "kdelinux/app/exemplo.py", type = "selenium", user = "installed", timeout = 180 },
Execute localmente o fluxo que contém sua suíte enquanto a desenvolve.
Opções de teste e wrappers personalizados
Para wrappers de SUT gerados, as entradas do manifesto podem definir user (root, live, installed ou plasma_setup), timeout em segundos (o padrão é 90), artifacts como uma lista de caminhos a serem coletados e as flags do openQA fatal = true ou always_run = true. Defina enabled = false para manter um teste declarado, mas excluí-lo do agendamento.
Quando um teste exigir algum tipo de orquestração personalizada para ser executado, coloque um wrapper ao lado do script do SUT e aponte para ele usando override:
{ path = "kdelinux/system/collect_logs.py", type = "selenium", override = "kdelinux/system/collect_logs.openqa.py" },
Este wrapper personalizado é responsável pelas flags, pelo usuário, pelo timeout e pela coleta de artefatos, devendo passar o caminho relativo do script do SUT para CliTest.run_python() ou CliTest.run_selenium(). Consulte o arquivo existente collect_logs.openqa.py para ver um exemplo.
Para mais informações sobre a criação de testes com Selenium, consulte Testes de automação com Appium e Testes de GUI com selenium-webdriver-at-spi.
Saiba mais
Para uma explicação mais detalhada do fluxo de testes e de sua implementação, consulte openQA Testing in KDE Linux. A infraestrutura de testes é desenvolvida no repositório os-autoinst-distri-kdelinux.
Artigo contribuído por Thomas Duckworth sob a licença CC-BY-4.0.