Ir para o conteúdo

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:

  1. O job de imaging cria, assina e prepara a imagem e seu canal de atualização do sistema no storage.kde.org.
  2. 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.
  3. O pipeline subsequente executa os fluxos de install e upgrade declarados em tests/tests.toml, cada um com instalações simples e criptografadas.
  4. 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 “ test job is now running.”. Os detalhes incluem o resultado do módulo, capturas de tela ou vídeo (quando aplicável) e a saída de diagnóstico. Na aba Logs & Assets, baixe ou visualize o arquivo 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:

  • install inicializa a imagem live e cria o disco instalado.
  • upgrade inicializa o disco instalado e define DO_UPGRADE para testes que realizam a atualização.
  • test inicializa 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 unittest padrã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-spi quando 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 sob a licença CC-BY-4.0.