prática recomendada ao testar a unidade para desenvolvimento incorporado

45

Estou procurando algumas estratégias de práticas recomendadas para código de teste de unidade escrito para sistema incorporado. Por sistema incorporado, refiro-me a códigos como drivers de dispositivo, manipuladores ISR etc., coisas bem 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 incorporada também precisa ser conectada a outros estímulos, como interruptores mecânicos, motores de passo e lâmpadas. Isso geralmente ocorre de maneira manual, a automação seria ótima, mas difícil e cara de se conseguir.

Atualizar

Me deparei com uma estrutura de teste C que parece ser bastante bem-sucedida no teste de projetos incorporados. Ele usa as idéias de zombar de hardware. Confira Unity , CMock e, possivelmente, Ceedling .

Atualização 06Jul2016

Me deparei com cmocka - parece ser mais ativamente trabalhado.

tehnyit
fonte
1
Em circunstâncias semelhantes, optamos por Cmocka
Mawg 06/07
Eu escrevi um tutorial muito completo sobre o tema: Unidade de teste de aplicativos C encaixados com Ceedling
Dmitry Frank

Respostas:

28

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

O teste das partes verdadeiramente baremetais de um sistema incorporado geralmente é um aplicativo separado (teste de unidade?) Que martela o firmware muito além do que os aplicativos podem 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 teste de unidade, incluindo o ICE completo. Isso é absolutamente bom, pois geralmente os testes funcionais são pequenos.

mattnz
fonte
Isso combinado com a resposta de jonathan cline ieee é uma estratégia bem-sucedida. Use a abstração para tornar a maior parte do código testável e, usando uma estrutura de teste simples, teste os bits não abstratáveis ​​no hardware real. Eu pessoalmente vi esse trabalho com várias plataformas.
Tim Williscroft
3
Cada produto que fabricamos possui Camada de Abstração de Hardware com implementação de drivers para a plataforma de destino e PC. Isso nos permite executar testes de unidade facilmente. Outras vantagens: também podemos executar testes rápidos do sistema e desenvolver a maioria dos softwares sem hardware (como está disponível posteriormente).
MaR
15

Uma ferramenta necessária para desenvolver é um injetor de sinal. O sistema incorporado terá alguma maneira de interagir com um sistema host (normalmente via porta serial reservada para depuração). Use isso para enviar dados de teste (a melhor opção é no formato ASCII conciso, para que seja facilmente simulada por humanos também).

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

Usando o TeraTerm como um injetor de sinal de porta serial e escrevendo algumas macros do TeraTerm (leva cerca de 20 minutos), há um enorme conjunto de testes automatizados que podem ser executados em qualquer parte de um sistema incorporado - seja a camada do driver, O / S, camada 4-5, etc. TeraTerm: http://en.sourceforge.jp/projects/ttssh2/

Se a porta serial não estiver disponível no sistema incorporado, use uma ferramenta de hardware para converter os dados da porta USB / serial em sinais digitais (também baratos e fáceis de alcançar). Enquanto você lê isso, estou usando uma placa de microcontrolador de US $ 30 (UBW: http://www.schmalzhaus.com/UBW32/ ) para testar um sistema embarcado para produção, injetando estímulo por meio de macros TeraTerm enviadas via USB / serial para o microcontrolador, que está executando o firmware modificado, que exercita 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 a validação de dados. Nada disso é difícil e nada é caro. Na minha experiência, os gerentes gastam muito dinheiro (como equipamentos 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 ferro grande para uso geral geralmente não inclui os casos de teste que capturam o timing / etc do pior caso do sistema de destino. Portanto, o método barato é preferível para a cobertura do teste. Acredite ou não.

Jonathan Cline IEEE
fonte
1
Bem, eu fiz a afirmação cara enquanto trabalho na indústria automotiva e tudo precisa ser determinístico, repetível e geralmente leva alguns engenheiros para se desenvolver. Além disso, quando mais itens são usados ​​na cadeia de teste, a manutenção também se torna um problema. Obrigado por nos informar sobre o UBW, parece uma boa opção.
tehnyit 1/09/11
2
Apenas não me inicie no LabView. Normalmente, essas coisas são horríveis.
Jonathan Cline IEEE
1
Nossos engenheiros de teste adoram o LabView, eu mesmo não entendo direito.
tehnyit 2/09/11
Isso é bem parecido com o que faço para vários testes, apenas eu uso o Python e suas bibliotecas seriais. Eu poderia então conectar meus testes de baixo nível aos testadores de unidade do Python, juntamente com algo como o Flask / Qt, para fornecer também um frontend fácil de usar.
Radix07
5

Este é um problema muito difícil.

Na verdade, projetei um equipamento de teste de unidade para um sistema incorporado, o que permitiria simular eventos / interrupções de hardware e controlar o tempo da execução (para garantir a cobertura de todas as intercalações possíveis devido à simultaneidade), e foi necessária uma equipe de programadores há mais de 2 anos para realmente implementá-lo e colocá-lo em funcionamento. Esse projeto é um desenvolvimento proprietário, mas um projeto semelhante (mais simples no design) está disponível aqui .

Então, sim, a automação seria ótima. Sim, é muito difícil e caro de conseguir. Sim, às vezes você tem que fazer isso. Raramente, 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 com que tudo funcione manualmente.

littleadv
fonte
Eu descobri que a unidade manualmente é propensa a erros, geralmente na geração do estímulo ou na medição dos resultados. Especialmente verdadeiro se o teste de unidade for complicado. Se você precisar refazer o teste de unidade novamente, ele ficará ainda mais sujeito a erros.
tehnyit
@tehnyit - sim, foi por isso que decidimos desenvolver o sistema de automação. Às vezes, as coisas não podem ser feitas manualmente, mas o teste de unidade precisa ser abrangente e cobrir problemas de tempo. Portanto, você não tem muita escolha, mas a automação nesse nível é uma coisa muito cara a se fazer.
Littleadv
4

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


Quero relacionar esse problema com outras pessoas, 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, entrando em contato com um serviço da 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 testar fisicamente cada operação. Verifique se o relógio do sistema fornece 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: é necessário seguir uma ordem estrita e repetir em cada hardware
  • permanecem estáveis ​​ao longo do projeto, portanto você não precisa executá-los com tanta frequência
  • portanto, executá-los manualmente é uma opção; a automação é ainda melhor, se não excessivamente complexa
  • 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 realizar as operações externas reais, ocultando-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 em qualquer projeto comum, não está mais em um código incorporado de difícil teste .

KLE
fonte
3

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

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

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

Existem vários emuladores ARM de código aberto que executam uma única sub-rotina, que por si só pode chamar outras sub-rotinas, que você pode usar para depuração, ajustando o desempenho de sub-rotinas que não dependem do acesso ao hardware. Eu usei um desses com grande sucesso para otimizar um criptografador AES para ARM7TDMI.

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

Estou pensando em um problema semelhante há anos, como testar o código do kernel Linux ou Mac OS X na unidade. Deveria ser possível, mas nunca tentei. Uma possibilidade é 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ê acionaria os testes de unidade a partir 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ê possa adicionar testes externos adicionais na tentativa de obter mais cobertura.

Mike Crawford
fonte
3

Como no TDD não incorporado, objetos simulados são definitivamente seus amigos.

Mantenha a interface do hardware subjacente limpa e simples para que tudo acima do nível mais baixo possa ser ridicularizado e você terá muito mais facilidade - se você projetar seu aplicativo incorporado com a testabilidade em mente, o teste sempre será muito mais tranquilo .

Além disso, o fato de você não conseguir testar on-line até muito tarde no projeto não significa que você também não deve preparar um conjunto de testes on-line.

Estes (inicialmente) precisam apenas testar os bits que não puderam ser testados offline. Certamente, não é o TDD (já que você está criando os testes antecipadamente), mas seu desenvolvimento offline do TDD deve fornecer uma boa idéia de como deve ser a interface de seu hardware e, portanto, quais testes online você precisa executar.

Além disso, se o desenvolvimento on-line custar muito mais do que o desenvolvimento off-line (como acontece no local em que trabalho), você poderá economizar muito tempo on-line, com um conjunto de testes bem compreendido para executar.

Mark Booth
fonte
+1 para colocar objetos simulados no prato, @mark. O único problema é que garantir a precisão dos objetos simulados, o que significa que a compreensão do objeto a ser ridicularizado deve ser bastante profunda. Isso é bom, pois força o desenvolvedor a entender o comportamento dos objetos externos aos quais está fazendo interface.
tehnyit 1/09/11
1

No desenvolvimento incorporado, você costuma fazer varreduras de limites para verificar se todo o aplicativo (incluindo hardware) funciona. Consulte também JTAG para depuração do sistema.

O teste de rotinas de software puro sem link para o hardware pode ser feito por uma estrutura padrão de teste de unidade C como o Check . Mas cuidado com as limitações de memória (especialmente o espaço de pilha, 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 incorporados, como pequenos PICs ou AVRs. No entanto, você pode zombar das portas de hardware para obter uma cobertura maior (e, é claro, também pode testar essa zombaria).

Você também pode tentar usar emuladores para os simuladores de chip ou circuito, mas esse tipo de ferramenta é caro (especialmente em combinação) e complicado.

Falcão
fonte
Concorde com a verificação de limites e o JTAG, mas, devido ao design do hardware, nem sempre é possível ou disponível.
tehnyit