Pular para o conteúdo principal

Test Driven em C++ cross-compiled é possível

afe!

Enquanto não rola um estudo retrospectivo maneiro, aqui vão minhas primeiras impressões.

  1. por mais que pareça andar de ré, tenha um porting para o sistema do desenvolvedor, onde a compilação não é cruzada;
  2. não exija frameworks xUnit e faça bilhões de programecos se necessário;
  3. guarde no coração que não é preciso reflection nem mesmo shared objects para aplicar dependency injection;
  4. não despreze a tediosa tarefa de testar parte a parte, pequena parte a pequena parte;
  5. não tenha escrúpulos para satisfazer dependências aleatórias com stubs.
A maior revelação para mim nesse processo foram os pontos 2, 3 e 5 acima.

Após um longo tempo digerindo o problema, e após diversas discussões sobre que framework de testes usar, por fim completei a tarefa da forma mais tosca possível: cada teste é um programa executável individual.  O projeto usa cmake para configurar a construção, e o ctest funciona legal com essa lista de programas de teste.

O maior esforço nesse processo foi refatorar o código o suficiente para conseguir testar uma unidade isolada realmente. A dificuldade percebida pela equipe desde o início era devido ao acoplamento entre as diversas unidades teóricas do sistema, e a quantidade de código de inicialização de coisas irrelevantes ao teste. Eventualmente tropecei na idéia de dependency injection. Apesar de ser algo que ocorre em super frameworks de componentes empresariais, o conceito é simples e se mostrou aplicável mesmo em um sistema ligado estaticamente, desde que as unidades fossem desacopladas o suficiente uma da outra. No nosso caso, isso se dá na forma de um Factory central para todo o sistema, pré-existente ao meu esforço.

Claro que esse desacoplamento "suficiente" é um graal e nunca ocorre, especialmente em um ambiente tão confortável quanto o da ligação estática -- onde tudo se junta no final numa grande sopa de elementos. Ocorreu que algumas funções que eram parte da unidade mas não eram exercitadas faziam referência a algum nome que acabava indefinido no programa final -- função ou variável extern. Eventualmente realizei que isso simplesmente não tem solução prática e aceitei a seguinte possibilidade: se uma unidade não compila sem uma função de outra unidade, e a função é irrelevante para o teste, vamos enfiar um stub na jogada e ponto final. De certa forma, isso pode ser considerado um Mock do mundo pré-componentização.

Acaba que, por fim, se não lindamente xUnit, meu objeto de manutenção agora possui uma suite de testes de unidade. Entre os vinte e trinta testes bem básicos que eu bolei pra começar, já descobri umas cinco funções implementadas diferente do que o meu projeto previa. O cmake suporta bem a lista de testes e gera um relatoriozinho bem legal.

Comentários

Postagens mais visitadas deste blog

return void();

É uma pequeneza mas eu gostaria muito que as linguagens da família do C permitissem retornar void quando a função retorna void . Escrever da forma a seguir me aborrece. public class Foo  {    public void log (String msg) { }    public void bar (String x, String y) { }    public void bar (String x)    {      if (x == null)      {        log("x was null");        return;      }      bar(x, "default");    } } Eu ficaria mais feliz escrevendo assim: public class Foo  {    public void log (String msg) { }    public void bar (String x, String y) { }    public void bar (String x)    {      if (x == null) return log("x was null");      bar(x, "default");     } }

.NET COM Interop, IStream e Visual Studio

Investigando oportunidades para otimizar um componente, experimentei definir métodos que não solicitam dados (em arrays etc.) como argumentos mas sim instâncias de IStream , e me deparei com um problema interessante: interoperabilidade com .NET. Usar componentes COM em programas .NET não é particularmente difícil; no Visual Studio, é possível adicionar componentes COM como referências no projeto. Se o componente for capaz de Automation, é possível usar a ferramenta tlbimp.exe para gerar manualmente um callable wrapper para o componente, que, se eu entendi as coisas corretamente, não precisa ser distribuído com a aplicação. Porém, todos os métodos que eu aprendi tem essa peculiaridade: eles parecem assumir que um componente não usa interfaces definidas por outros componentes -- como, por exemplo, declarando parâmetros com tipo IStream , uma interface definida pelo sistema. Pelo método do callable wrapper , a ferramenta assume que as interfaces mencionadas integram o componente...

Eclipse PDE com Gradle

Na virada de 2016 para 2017, adotamos oficialmente o Gradle como ferramenta de construção de software na Prodist. A fila da migração está fluindo confortavelmente desde então. Já temos as provas de conceito necessárias para todos os projetos, inclusive os projetos nativos para compilador C++. Para fontes Java usamos Eclipse. Integramos projetos Gradle ao Eclipse usando Buildship . Acho que o Buildship ainda tem um longo caminho pela frente, mas realiza tudo o que necessitamos. Já estamos entregando com base nesse esquema. O problema mais importante que encontramos foi na integração de projetos OSGi. No Eclipse, trabalhamos projetos OSGi com o PDE. Nosso ciclo de desenvolvimento inclui rodar configurações "OSGi Framework" no Eclipse para teste. Infelizmente, o PDE assume uma estrutura de projeto própria e não funciona corretamente caso contrário. Estes problemas estão registrados em bugs como, por exemplo, o  bug 153023 . Nossos projetos OSGi com Gradle usam o layout ...