Data Flow Testing
Vários critérios de adequação até aqui
Baseado em entradas de função (funcional) Baseado na estrutura do programa (estrutural) Baseado em faltas injetadas (mutação)
Algumas limitações também... Funcional
Inadequado para construir entradas que leva a caminhos específicos do programa
Estrutural Pode gerar muitos caminhos desnecessários (e.g.,
exercitar mesmo loop repetidamente.) Baseado em falhas
Modelo de falhas possivelmente irrealista, alto custo de execução de mutantes, alto custo de identificação de programas equivalentes
Critério baseado em fluxo de dados
Recordar origem de um bug: Falta modifica estado de um programa
incorretamente Falha manifesta-se através da leitura deste
estado Observação
Relação de leitura e escrita entre dados é importante!
Critério baseado em fluxo de dados
Abordagem Suíte deve incluir testes que executam
statements logicamente relacionadosE.g., escrita e posterior leitura de um campo de um objeto devem aparecer em um mesmo teste
DU Pair
Associação de definição e uso de variáveis do programa Neste contexto, uma definição é uma nova
atribuição de valor a uma variável
Exemplo
01: public static int foo(int a) {02: if (a > 10) {03: a++;04: }05: ...06: return a; 07: }
DU pairs: (01,02), (01,03), ...
Assuma que parâmetro define um valor
Exemplo
01: public static int foo(int a) {02: if (a > 10) {03: a++;04: }05: ...// nova definição de a?06: return a; 07: }
DU pairs: (01,02), (01,03), (03,06)?
Linha 05 pode redefinir variável a.
DU Path
Caminho que associa uma definição ao uso de uma variável
Vários DU Paths para um DU Pair E.g.
01: int x = 1;02: if (...) {03: ...04: } else {05: ...07: }08: System.out.println(“x value”+ x);
Cobertura de uma suíte de testes
du pairs: fração de todos os pares du que são cobertos por pelo menos um teste
du paths: fração de todos os caminhos (desconsiderando loop) du que são cobertos por pelo menos um teste
definitions: fração de definições cobertas por pelo menos um teste
Desafios gerais de análises estáticas Abrangência: whole-program analysis, main Profundidade: intraprocedural, interprocedural Características de programas: encapsulamento
de dados, ponteiros e arrays
Interprocedural
public class Sample { int x; public static void main(String[] args){ Sample s = new Sample(); s.x = read(System.in); if (s.x > 10) { s.x--; } s.f(); } public void f() { s.x++; }}
Definição de s.x se propaga até Sample.f
Interprocedural
public class Sample { int x; public static void main(String[] args){ Sample s = new Sample(); s.x = read(System.in); if (s.x > 10) { s.x--; } s.f(); } public void f() { s.x++; }}
Definição de s.x se propaga até Sample.f
Duas definições alcançam a entrada de f
Interprocedural
public class Sample { int x; public static void main(String[] args){ Sample s = new Sample(); s.x = read(System.in); if (s.x > 10) { s.x--; } s.f(); } public void f() { s.x++; }}
Definição de s.x se propaga até Sample.f
Duas definições alcançam a entrada de f
Análise é feita sobre funções alcançáveis a partir de main
Análise a partir de função main
Reduz complexidade da análise estática Muitas funções não são alcançáveis (usadas)
No contexto de testes é útil para medir cobertura Requisitos de teste (cobertura) são construídos a
partir de uma sequência de teste Mas desconsidera vários comportamentos
Análise a partir de função main
Reduz complexidade da análise estática Muitas funções não são alcançáveis (usadas)
No contexto de testes é útil para medir cobertura Requisitos de teste (cobertura) são construídos a
partir de uma sequência de teste Mas desconsidera vários comportamentos
Nota: as vezes, não há uma função main a analisar
Alternativa: construção de contextos
Mas que contextos são esses? (além de main) Heurística para OO: assuma que métodos de uma
classe estão relacionados
Várias funções e encapsulamento
public class Stack<T> { int num; T[] elems; public void push(T t) { num++; elems[num]=t; } public T pop() { elems[num] = null; num--; } public T peek() { return elem[num]; }}
Várias funções e encapsulamento
public class Stack<T> { int num; T[] elems; public void push(T t) { num = num + 1; elems[num]=t; } public T pop() { elems[num] = null; num = num - 1; } public T peek() { return elem[num]; }}
definições e uso
Note que não há função main associando peek, push, e pop.
Encapsulamento de dados
public class Foo { Stack<?> s; public Stack<?> getStack() { return s; } public void foo() {...getStack().peek();...} public void bar(int k) {...getStack().push(k);...}}
public class Stack<T> { int num; T[] elems; public void push(T t) { num++; ... } public T pop() { ... ; num--; } public T peek() { return elem[num]; }}
Foo.foo usa definições construídas em Foo.bar.
Encapsulamento de dados
public class Stack<T> { int num; T[] elems; public void push(T t) { num++; ... } public T pop() { ... ; num--; } public T peek() { return elem[num]; }}
Campo do tipo Stack<?> é estado encapsulado em Foo.
Foo.foo usa definições construídas em Foo.bar.public class Foo {
Stack<?> s; public Stack<?> getStack() { return s; } public void foo() {...getStack().peek();...} public void bar(int k) {...getStack().push(k);...}}
Encapsulamento de dados
public class Foo { Stack<?> s; public Stack<?> getStack() { return s; } public void foo() {...getStack().peek();...} public void bar(int k) {...getStack().push(k);...}}public class Foo2 { Stack<?> s2; public Stack<?> getStack2() { return s2; } public void foo2() {...getStack().peek();...} public void bar2(int k) {...getStack().push(k);...}}
Note, porém, que bar e foo2 (ou bar2 e foo) não estão associados!
Leituras adicionais
Capítulo 13 (Data Flow Testing) do livro “Software Testing and Analysis”
“Efficient Computation of Interprocedural Definition-Use Chains”, M. J. Harrold and M. L. Soffa, ACM TOPLAS 1994
Nota: análise que consideram encapsulamento ainda são poucas!
Top Related