melhor prática quando testes unitários para desenvolvimento embarcado

43

Estou procurando algumas estratégias de melhores práticas para o código de teste de unidade escrito para o sistema embarcado. Por sistema embarcado, quero dizer código como drivers de dispositivo, manipuladores ISR, etc, coisas que são muito próximas do metal.

A maioria dos testes de unidade não é possível sem testá-lo no hardware com o auxílio de um ICE. Às vezes, a unidade embutida também precisa ser conectada a outros estímulos, como interruptores mecânicos, motores de passo e lâmpadas. Isso geralmente ocorre de forma manual, a automação seria ótima, mas difícil e cara de se conseguir.

Atualizar

Me deparei com uma estrutura de testes C que parece ser bem-sucedida em testar projetos incorporados. Ele usa as idéias de hardware zombeteiro. Confira Unity , CMock e possivelmente Ceedling .

Atualização 06Jul2016

Surgiu em cmocka - parece ser mais ativamente trabalhado.

    
por tehnyit 29.08.2011 / 11:01
fonte

7 respostas

28

Eu abstraio as dependências de hardware o mais cedo possível, e construo o sistema em chicotes de teste / emulação de software, habilitando todos os tipos de estruturas de teste. Muitas vezes meu PC de desenvolvimento foi usado para testar até 95% ou mais do sistema completo. O custo da sobrecarga extra (outra camada de abstração) foi facilmente recuperado pelo código de limpeza gerado como resultado dessa abstração.

O teste das partes verdadeiramente baremetais de um sistema embarcado é geralmente uma aplicação separada (teste de unidade?) que martela o firmware muito além do que os aplicativos podem até esperar alcançar. A automação pode ser feita - a um custo, mas não é típica.

A menos que você tenha o orçamento para construir um equipamento de hardware de teste de unidade, incluindo o ICE completo. Isso é absolutamente bom, já que geralmente os testes funcionais são pequenos.

    
por 29.08.2011 / 11:41
fonte
15

Uma ferramenta necessária para desenvolver é um injetor de sinal. O sistema embarcado terá alguma forma de interface com um sistema host (normalmente através de uma porta serial reservada para depuração). Use isso para enviar dados de teste (a melhor opção é o formato terse ascii formatado para que seja facilmente simulado por humanos também).

Eu discordo completamente desta parte da sua pergunta: "a automação seria ótima, mas difícil e cara de se conseguir."

Usando o TeraTerm como um injetor de sinal de porta serial e gravando algumas macros TeraTerm (leva cerca de 20 minutos), há um enorme conjunto de testes automatizados que podem ser executados em qualquer parte de um sistema embarcado - seja camada de driver, / S, camada 4-5, etc. TeraTerm: link

Se a porta serial não estiver disponível no sistema embarcado, use uma ferramenta de hardware para converter dados de porta USB / serial em sinais digitais (também de baixo custo e fácil ). Enquanto você lê isso, estou usando uma placa microcontroladora de US $ 30 (UBW: link ) para testar um sistema embarcado para produção, injetando estímulo via macros TeraTerm que é enviado via USB / serial para o microcontrolador, que está executando o firmware modificado que exerce entradas digitais e monitora as saídas digitais do sistema embarcado de destino. Em conjunto com isso, desenvolvemos um script python (usa pyserial e pexpect) para automatizar a injeção de dados e validação de dados. Nada disso é difícil e nada disso é caro . Na minha experiência, os gerentes gastam muito dinheiro (como equipamento de teste de US $ 30.000) quando a equipe de teste é inexperiente e não consegue conceber essas soluções fáceis - infelizmente, o equipamento de uso geral geralmente não inclui os casos de teste. que capturam o pior momento / etc do sistema de destino. Portanto, o método barato é preferível para cobertura de teste. Acredite ou não.

    
por 01.09.2011 / 01:28
fonte
5

Este é um problema muito difícil.

Eu realmente projetei um chicote de testes de unidade para um sistema embarcado, que permitiria simular eventos / interrupções de hardware e controlar o tempo de execução (para garantir que cobrimos todos os possíveis intercalamentos devido à simultaneidade), e levou uma equipe de programadores de mais de 2 anos para realmente implementá-lo e colocá-lo para trabalhar. Esse projeto é um desenvolvimento proprietário, mas um projeto similar (mais simples no design) está disponível aqui .

Então, sim, a automação seria ótima. Sim, é muito difícil e caro de se conseguir. Sim, às vezes você tem que fazer isso. Raramente, porém, na minha experiência, na maioria dos casos, é mais rápido e mais barato usar os motores de passo e as lâmpadas e fazer tudo funcionar manualmente.

    
por 29.08.2011 / 11:12
fonte
3

Os simuladores de CPU incorporados geralmente podem ser programados para também simular hardware. Todas as tecnologias de virtualização, além do Xen, fazem isso. Mas você precisa escrever código que finge ter alguns registros em algum endereço físico ou, em x86, um endereço no barramento de E / S, e então você precisa responder a leituras e gravações nesses endereços como se o seu software fosse um físico. chip cujos registros de controle e status estavam sendo acessados.

Se você quiser fazer isso, sugiro modificar o QEMU. Mas isso não seria fácil. Esse tipo de coisa geralmente só é feito quando você está projetando um chip personalizado com um microcontrolador e alguns outros núcleos para o seu I / O.

O sistema de desenvolvimento vendido pela ARM Holdings prevê isso e é mais fácil de trabalhar do que hackear o QEMU, mas é muito caro.

Existem vários emuladores Open Source ARM que executam uma única sub-rotina, que pode chamar outras sub-rotinas, que você pode usar para depurar o desempenho de sub-rotinas que não dependem do acesso ao hardware. Eu usei um desses para obter grande sucesso para otimizar um criptografador AES para ARM7TDMI.

Você pode escrever um chicote simples de teste de unidade em C ou C ++, vincular a classe ou sub-rotina sob teste a ele e, em seguida, executá-lo no simulador.

Venho ponderando um problema semelhante há anos, como testar código de kernel Linux ou Mac OS X. Deve ser possível, mas eu nunca tentei. É possível construir um kernel completo em vez de testar seu código isoladamente, com a estrutura de teste de unidade vinculada diretamente ao seu kernel. Você então dispararia os testes de unidade de algum tipo de interface externa.

Talvez seja mais produtivo usar uma ferramenta de cobertura de código e testar seu firmware como um pacote completo por meio de sua interface externa. A ferramenta de cobertura encontraria caminhos de código que ainda não foram testados, para que você pudesse adicionar testes externos adicionais na tentativa de obter mais cobertura.

    
por 29.08.2011 / 12:13
fonte
3

Edit: minha resposta está perto de mattnz, eu acho ...

Eu quero relacionar este problema com outros, todos os testes que dependem de algo externo ao seu código (como o relógio do sistema, um sistema de arquivos persistente ou um banco de dados, entrar em contato com um serviço web externo ...). Sugiro a mesma política para todos eles, isole os dois níveis em duas camadas de código.

Testando uma única operação externa

Você pode querer testar fisicamente cada operação. Verifique se o relógio do sistema informa a hora correta, verifique se um arquivo realmente se lembra do que foi escrito, verifique se um dispositivo recebe uma única operação ...

Estes testes:

  • deve ser o mais simples possível: nenhum algoritmo, condição ou loop
  • pode depender da ordem e da máquina: por isso, é necessário seguir uma ordem estrita e repetir em cada hardware
  • são praticamente estáveis ao longo do seu projeto, então você não precisa executá-las com frequência
  • então executá-los manualmente é uma opção; automação é ainda melhor, se não excessivamente complexo
  • Observe que o que está sendo testado não é seu código , é uma ferramenta que seu código precisa ... Portanto, testar isso pode ser opcional para você, pode ter sido feito por uma equipe diferente ...

Testando a lógica (código, algoritmo) que une as operações externas

Por ter uma camada de código para fazer as operações externas reais, escondendo-as em uma interface que você pode facilmente zombar, sua lógica não depende mais dos dispositivos físicos reais ...

Você pode testar simplesmente, como qualquer projeto comum, você não está mais em um código de difícil teste incorporado .

    
por 29.08.2011 / 19:08
fonte
3

Assim como o TDD não incorporado, objetos simulados são definitivamente seus amigos.

Mantenha a interface do seu hardware subjacente limpa e simples para que tudo acima do nível mais baixo possa ser ridicularizado e você terá um tempo muito mais fácil - se você projetar seu aplicativo incorporado com a testabilidade em mente, os testes sempre serão muito mais suavemente.

Além disso, só porque você pode não conseguir fazer o teste on-line até muito tarde no projeto, não significa que você não deve preparar um conjunto de testes on-line também.

Estes (inicialmente) precisam apenas testar os bits que não puderam ser testados offline. Claro, não é o TDD (já que você está criando os testes com antecedência), mas seu desenvolvimento off-line do TDD deve lhe dar uma boa ideia de como sua interface de hardware precisa e, portanto, quais testes online você precisa realizar.

Além disso, se o desenvolvimento on-line custa muito mais do que o desenvolvimento off-line (como acontece onde eu trabalho), você pode poupar muito tempo on-line com um conjunto bem compreendido de testes.

    
por 31.08.2011 / 18:43
fonte
1

Em desenvolvimento incorporado, você costuma fazer verificações de limites para verificar se todo o aplicativo (incluindo hardware) funciona. Veja também JTAG para depuração do sistema.

Testar rotinas de software puro sem link para o hardware pode ser feito por uma estrutura padrão de Teste de Unidade C, como Verificar . Mas cuidado com as limitações de memória (especialmente stackspace etc. em dispositivos pequenos). Conheça seus contratos! Você também pode tentar abstrair as rotinas de software do hardware para obter uma cobertura de teste maior, mas isso geralmente é caro em termos de desempenho em dispositivos embarcados, como pequenos PICs ou AVRs. No entanto, você pode simular portas de hardware para obter uma cobertura maior (e, claro, também é possível testar essa simulação).

Você também pode tentar usar emuladores para os simuladores de circuito ou circuito, mas esses tipos de ferramentas são caros (especialmente em combinação) e complicados.

    
por 29.08.2011 / 11:14
fonte