Uma Nova Técnica de Seleção de Testes com Compiladores IBM XL C/C++ e Fortran para Desenvolver Ambientes de Teste Mais Inteligentes

Reduza o número de casos de teste usados para teste de regressão sem degradar a cobertura de teste

Igor Todorovski, Jean Saad e Francesco Cassullo apresentam uma técnica de seleção para reduzir o número de casos de teste usados em teste de regressão sem degradar a cobertura. A técnica usa os dados de perfil do recurso de rastreio de função (functrace) dos compiladores XL C/C++ e Fortran para selecionar um subconjunto minimizado e preciso de casos de teste.

Igor Todorovski, Software Developer, IBM

Author1 photoIgor Todorovski é desenvolvedor de software no grupo IBM XL Compilers. Ele trabalha na IBM desde 2008 e é especializado em compiladores C/C++ do z/OS.



Jean Saad, Software Developer, IBM

Author1 photoJean Saad é desenvolvedor de software no grupo IBM XL Compilers. Ele trabalha na IBM desde 2008 e trabalhou com compiladores C/C++ e COBOL.



Francesco Cassullo, Software Developer, IBM

Francesco Cassullo é desenvolvedor de software no grupo IBM XL Compilers. Ele trabalha na IBM desde 2008 e trabalhou com compiladores Fortran, C/C++ e COBOL.



20/Jul/2012

Introdução a profile-directed selection

O teste de regressão é frequentemente necessário para garantir a qualidade do software. No entanto, é considerado um processo caro. Para reduzir esse custo, técnicas de seleção de teste são aplicadas à suíte de testes completa.

O resultado de uma técnica de seleção de teste é uma suíte de testes reduzida. Existem várias técnicas de seleção que tentam reduzir o tamanho da suíte e, ao mesmo tempo, manter uma alta taxa de detecção de erros.

Este artigo apresenta uma técnica de seleção de testes rápida e eficiente, que usa dados de perfil para selecionar os casos de teste. Nós chamamos essa técnica de profile-directed selection (PDS).

PDS opera em duas fases:

Preenchendo o banco de dados com dados de perfil
A primeira fase relaciona o código fonte do aplicativo com os casos de teste. Ela executa a suíte de testes completa em uma cobertura de código ou aplicativo com o recurso de perfil. Os dados de perfil resultante catalogam os arquivos de origem, nomes de funções e instruções de linha chamados por cada caso de teste. Em seguida, cada caso de teste é associado a arquivos e funções do aplicativo encontrados. Esse relacionamento é armazenado em um banco de dados relacional. Os dados de cobertura do código podem ser aplicados com maior granularidade, mas isso tem um custo: mais dados significam mais tempo de processamento e manutenção.
 
Selecionando os casos de teste com base no conjunto de mudanças
A segunda fase, seleção do teste, desenvolve e executa uma consulta baseada nas modificações feitas no código fonte do aplicativo. A consulta seleciona os casos de teste, que atravessam uma função modificada pela alteração no código.
 
Figura 1. Seleção de teste em testes de regressão
Seleção de teste em testes de regressão

Legenda para abreviaturas

S para aplicativo de software

S' para uma versão modificada de S

T={Ti,...,Tn) para a suíte de testes original

Instrumentação do gerenciador de perfis

PDS depende de dados de perfil para relacionar casos de teste com o código fonte de um aplicativo. Portanto, é necessário instrumentar o aplicativo para gerar dados de perfil. Ao selecionar um gerenciador de perfis, leve em conta o seguinte:

  • Impacto relativo no desempenho
  • Usabilidade da saída para conjunto de ferramentas
  • Requisitos de armazenamento

Em nossos experimentos, escolhemos usar os ganchos de função através do recurso functrace nos compiladores IBM XL C/C++ e Fortran. Usamos esse recurso de compilador para colocar um gancho func_trace_enter e func_trace_exit em cada função em nosso aplicativo de amostra. Essas funções geram os dados do perfil.

PDS usa um banco de dados relacional para relacionar cada função e arquivo de origem com cada caso de teste. Usamos IBM DB2® em nossos experimentos.

PDS é uma técnica de seleção granular no nível de função. A granularidade de cobertura de código pode ser maior ou menor, conforme necessário. Escolhemos a granularidade no nível de função porque representa uma média entre os custos do processamento da técnica de seleção e a redução no tamanho da suíte de testes. Técnicas de seleção mais finas, como técnicas de fluxo de controle baseadas em gráfico, podem selecionar menos casos de teste, mas têm um custo maior em tempo de processamento e requisitos de armazenamento. Para aplicativos que consistem em centenas de milhares de casos de teste, o custo de processamento pode exceder o benefício da técnica. Por isso, a granularidade deve ser levada em conta.

Detecção de conjunto de mudanças de função

Modificações feitas nos arquivos de origem entre S e S' são chamadas de conjunto de mudanças. A extração das funções e arquivos de origem modificados no conjunto de mudanças foi feita manualmente nos nossos experimentos.

A maioria dos aplicativos de ambiente de desenvolvimento integrado (IDE) pode detectar as alterações entre S e S' automaticamente.

Modelagem da eficácia da técnica de seleção

A eficácia de profile-directed selection foi baseada na eficácia da detecção de defeitos e na taxa de redução média da suíte de testes.


Técnica profile-directed selection, ou PDS

A técnica PDS procura selecionar casos de teste que foram afetados pelo conjunto de mudanças entre S e S'.

Como mencionamos, PDS consiste em duas fases:

  1. Preenchendo o banco de dados com dados de perfil
  2. Selecionando os casos de teste com base no conjunto de mudanças
Figura 2. Visão geral de profile-directed selection
Visão geral de profile-directed selection

Algoritmo

PDS pode ser expresso no seguinte pseudocódigo:

1. Test S with T to generate P={P1,…,Pn}, the profile data of S. 
2. For each test case Ti for i=1 to n, relate Pi to Ti in a relational database. 
3. Let P' be the source code change set between S and S'. 
4. For i=1 to n, if |P'∩Pi|>0 then return Ti.

Fase 1. Preenchendo o banco de dados de casos de teste

A Fase 1 do PDS preenche o banco de dados relacionando os casos de teste ao código fonte.

Um pré-requisito da Fase 1 é que S deve ser instrumentado com um gerenciador de perfis. Em nosso caso, nós usamos o recurso functrace no compilador IBM XL C/C++ e Fortran para inserir ganchos func_trace_enter e func_trace_exit em cada função.

Figura 3. Desenvolver um aplicativo que use perfis com functrace
Desenvolver um aplicativo que use perfis com functrace

Nossas definições de gancho geraram dados de perfil que incluíam os caminhos dos arquivos de origem e as funções que foram exercitados. Para reduzir os requisitos de armazenamento totais, usamos uma estrutura de dados Set para armazenar nomes de função exclusivos e caminhos de origem exercitados. Também retivemos a frequência de chamada de cada função. Os dados de perfil resultantes possuem o seguinte formato para cada caso de teste:

 Source:Function:CallFrequency main.c:main():1 main.c:foo():2 boo.c:goo():1

A primeira coluna representa o arquivo de origem, a segunda representa o nome da função e a última representa a frequência de chamada.

Com esse design, executamos toda a suíte de testes T em S. Isso gerou os dados de perfil P para cada caso de teste.

Para cada caso de teste Ti para i=1 a n, relacionamos os dados de perfil Pi com Ti em um banco de dados relacional.

Este é o esquema do banco de dados:

Tabela: TestCaseToSourceCode

 Test Case Path, Function Name, Source Path

Em nossa implementação, como mostra a Listagem 1, normalizamos essa tabela e criamos três tabelas para o caminho de caso de teste, arquivo de origem e nome da função.

Listagem 1. Exemplo de implementação usando functrace
#include <iostream>
#include <sstream>
#include <fstream>
#include <map>
#include <demangle.h>

static bool exitedMain = false;

struct ProfileData
{
    std::string sourceFile;
    std::string functionName;
};

struct Comparator
{
    bool operator()(ProfileData s1, ProfileData s2) const
    {
        return strcmp((s1.sourceFile+ s1.functionName).c_str(), 
        (s2.sourceFile + s2.functionName).c_str()) < 0;
    }
};

static std::map<ProfileData, int, Comparator>* tccMap = NULL;

static std::string getDemangledName(std::string mangledFunctionName)
{
    char *rest;
    Name* name;
    if ((name = Demangle(const_cast<char*>(mangledFunctionName.c_str()), rest)) != NULL)  
        return ((FunctionName *)name)->Text();    
    else
        return mangledFunctionName;    
}

static void printData(const ProfileData &data, int callFrequency)
{
    std::string functionName = getDemangledName(data.functionName);
    
    std::ofstream outputFile("func_trace_profile.dat", std::ios::out | std::ios::app); 
    outputFile << data.sourceFile << "|" 
    << functionName << "|" << callFrequency << std::endl; 
    outputFile.close();
}

std::string basename(std::string path)
{
    std::string::size_type size = path.find_last_of ('/');

    if ( size == std::string::npos )
        return path;

    return path.substr(size + 1);
}

extern "C"
void __func_trace_enter(const char *functionName, 
const char *fileName, int lineNumber, void** const id)
{ 
    if (tccMap == NULL)
        tccMap = new std::map<ProfileData, int, Comparator>;

    ProfileData data;
    data.sourceFile = basename(fileName);
    data.functionName = functionName;

    if (tccMap->find(data) == tccMap->end()) 
    {
        if (exitedMain) 
            printData(data, 1);

        (*tccMap)[data] = 1;
    }
    else 
    {
        (*tccMap)[data]++;
    }
}

extern "C"
void __func_trace_exit(const char *functionName, 
const char*fileName, int lineNumber, void** const id)
{
    if (strcmp(functionName, "main") == 0) 
    {
        std::map<ProfileData, int, Comparator>::iterator myIterator;

       for(myIterator = tccMap->begin(); myIterator != tccMap->end(); myIterator++)
        {
    int callFrequency = myIterator->second;
    printData(myIterator->first, callFrequency);
        }
        exitedMain = true;
	}	 
}

Nós omitimos a frequência de chamada dos dados de perfil em nossos experimentos, mas planejamos usá-la para fins de classificação no futuro.

Quando o software modificado S' ficou disponível, prosseguimos para a Fase 2.

Fase 2. Seleção de teste

Na Fase 2, detectamos manualmente as funções modificadas, incluídas e excluídas entre S e S'. No caso de funções excluídas e incluídas, identificamos a função-pai. A partir desse conjunto de mudanças, criamos uma consulta para o banco de dados relacional, que produziu uma suíte de testes reduzida T'.


Análise empírica

Após estabelecer um mecanismo para selecionar casos de teste com base no conjunto de mudanças do software, realizamos um experimento para analisar a eficácia da técnica PDS.

Experimento

Nosso experimento comparou três técnicas de seleção de teste (aleatório, seleção manual e PDS) com uma suíte de testes completa. Testamos a suíte usando os compiladores IBM XL C/C++ Versão 11.1 para AIX®.

Selecionamos duas suítes de teste internas. Uma tinha 832 casos de teste a outra, 781.

Análise

Usando as suítes de teste internas, houve um total de 16 defeitos localizados entre os dois desenvolvimentos do compilador IBM XL C/C++ V11.1 que escolhemos testar. Demorou aproximadamente 900 segundos para executar cada suíte de teste.

As técnicas de seleção aleatória e manual foram muito baratas, demorando menos de 50% de uma execução da suíte de testes completa. No entanto, elas apresentaram taxas de detecção de defeito muito menores, encontrando apenas 37,5% (6 de 16) e 43,75% (7 de 16) do número total de defeitos.

A técnica PDS também reduziu o tempo de processamento em aproximadamente 50%, em média, entre as duas suítes de testes. Ela foi significativamente mais eficaz na detecção de defeitos, apresentando uma taxa de detecção de 100% (16 de 16) em relação à execução aleatória e de seleção manual.

Portanto, ao determinar a efetividade em custo de cada técnica, examinamos o número de defeitos encontrados no tempo de execução de cada suíte. A técnica PDS apresentou um resultado de 0,3 defeito por segundo, mas as técnicas completa, aleatória e seletiva (seleção manual) tiveram resultados de menos de 0,2 defeito por segundo.

Figura 4. Dados técnicos empíricos, por técnica
Dados técnicos empíricos, por técnica
Figura 5. Eficácia das técnicas de seleção
Eficácia das técnicas de seleção

Instruções futuras

No futuro, estudaremos o impacto de várias granularidades. Mais especificamente, pretendemos comparar os custos de processamento e manutenção do preenchimento do relacionamento entre caso de teste e código fonte. Além disso, pretendemos encontrar a granularidade que fornecerá o menor custo de processamento enquanto mantém uma alta taxa de redução da suíte de testes.

Essa técnica poderia ser aplicada a execuções de teste de sanidade, nas quais é necessário um número fixo de casos de teste. O problema que precisa ser investigado é quais casos escolher e como classificá-los. Uma possibilidade é combinar essa técnica com técnicas de priorização, uma das quais seria o uso da frequência de chamada como classificação.

Integração de ferramenta para verificação automática do código

No futuro, gostaríamos de ver esse algoritmo implementado em aplicativos IBM existentes, como Rational Application Developer, Rational Developer para System z, Rational Developer para Software Power Systems e Rational Team Concert™. O ideal é que, quando um desenvolvedor enviar mudanças, uma lista de casos de teste afetados pelo conjunto de mudanças dado seja selecionado e executado.

Os casos de teste devem ser executados on demand, com base nas mudanças do código fonte. Isso permite que os desenvolvedores verifiquem automaticamente as alterações em seu código, evitando a necessidade de executar regressões completas.

Convidamos você a enviar sugestões, feedback ou outros comentários sobre a técnica PDS e seu uso em potencial no campo de testes.


Agradecimentos

Os autores agradecem às seguintes pessoas, que tornaram este artigo possível: Preston Koo, Roch Archambault e Kobi Vinayagamoorthy.


Downloads

DescriçãoNomeTamanho
Código de amostrafunc_trace_profile.dat241KB
Código de amostrafunctrace.C.zip3KB

Recursos

Aprender

  • Referências relacionadas a este artigo:
    • G. Rothermel e M.J. Harrold. A Safe, Efficient Regression Test Selection Technique. In ACM Trans. Software Eng. and Methodology, vol. 6, no. 2, pp. 173-210, abril de 1997.
    • Todd L. Graves, Mary Jean Harrold, Jung-Min Kim, Adam Porter, Gregg Rothermel. An empirical study of regression test selection techniques. In Proceedings of the 20th international conference on Software engineering, p.188-197, 19-25 de abril de 1998, Kyoto, Japão.
    • IBM XL C/C++ for AIX, V11.1, Compiler Reference, 2009.
  • Para mais informações sobre os compiladores:
    • Compiladores IBM XL C/C++ para AIX, Linux, IBM z/OS®, IBM z/VM®, mais a XS C/C++ Advanced Edition para Blue Gene® e Development Studio para IBM System i®.
    • Compiladores IBM Fortran: XL Fortran para AIX, XL Fortran para Linux, XL Fortran Advanced Edition para Blue Gene, VS Fortran
  • Acesse a área do software Rational no developerWorks para visualizar os recursos técnicos e as melhores práticas dos produtos da Rational Software Delivery Platform.
  • Assine a newsletter semanal de email do developerWorkse escolha os tópicos que irá seguir.
  • Fique por dentro dos eventos técnicos e Webcasts do developerWorks com ênfase em uma série de produtos IBM e assuntos relacionados ao segmento de mercado de TI.
  • Participe de um briefing ao vivo e gratuito do developerWorks para se atualizar rapidamente sobre produtos e ferramentas IBM, bem como tendências do segmento de mercado de TI.
  • Acompanhe os demos on demand do developerWorks, variando de demos de instalação e configuração de produtos para iniciantes a funcionalidades avançadas para desenvolvedores experientes.

Obter produtos e tecnologias

  • Faça download e avalie XL C/C++ para AIX, que oferece tecnologias avançadas de compilação e otimização criadas para AIX e Power Systems.
  • Faça download de uma versão gratuita do software Rational.
  • Avalie outros produtos de software da IBM da maneira que for melhor para você: faça o download da versão de teste, sua avaliação online, use-a em um ambiente de nuvem ou passe algumas horas no SOA Sandbox aprendendo a implementar Arquitetura Orientada a Serviços de forma eficiente.

Discutir

Comentários

developerWorks: Conecte-se

Los campos obligatorios están marcados con un asterisco (*).


Precisa de um ID IBM?
Esqueceu seu ID IBM?


Esqueceu sua senha?
Alterar sua senha

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


A primeira vez que você entrar no developerWorks, um perfil é criado para você. Informações no seu perfil (seu nome, país / região, e nome da empresa) é apresentado ao público e vai acompanhar qualquer conteúdo que você postar, a menos que você opte por esconder o nome da empresa. Você pode atualizar sua conta IBM a qualquer momento.

Todas as informações enviadas são seguras.

Elija su nombre para mostrar



Ao se conectar ao developerWorks pela primeira vez, é criado um perfil para você e é necessário selecionar um nome de exibição. O nome de exibição acompanhará o conteúdo que você postar no developerWorks.

Escolha um nome de exibição de 3 - 31 caracteres. Seu nome de exibição deve ser exclusivo na comunidade do developerWorks e não deve ser o seu endereço de email por motivo de privacidade.

Los campos obligatorios están marcados con un asterisco (*).

(Escolha um nome de exibição de 3 - 31 caracteres.)

Ao clicar em Enviar, você concorda com os termos e condições do developerWorks.

 


Todas as informações enviadas são seguras.


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=80
Zone=Rational
ArticleID=826293
ArticleTitle=Uma Nova Técnica de Seleção de Testes com Compiladores IBM XL C/C++ e Fortran para Desenvolver Ambientes de Teste Mais Inteligentes
publish-date=07202012