Bem ... sim , na verdade, se todos os caminhos "através" do programa forem testados. Mas isso significa que todo caminho possível em todo o espaço de todos os estados possíveis que o programa pode ter, incluindo todas as variáveis. Mesmo para um programa estaticamente compilado muito simples - digamos, um antigo triturador de números Fortran - isso não é viável, embora possa pelo menos ser imaginável: se você tiver apenas duas variáveis inteiras, estará basicamente lidando com todas as maneiras possíveis de conectar pontos. uma grade bidimensional; na verdade, parece muito com o Travelling Salesman. Para n tais variáveis, você está lidando com um espaço n- dimensional, portanto, para qualquer programa real, a tarefa é completamente intratável.
Pior: para coisas sérias, você não possui apenas um número fixo de variáveis primitivas, mas cria variáveis dinamicamente em chamadas de função, ou possui variáveis de tamanho variável ... ou qualquer coisa assim, quanto possível, em uma linguagem completa de Turing. Isso torna o espaço de estado infinito-dimensional, destruindo todas as esperanças de cobertura total, mesmo com equipamentos de teste absurdamente poderosos.
Dito isto ... na verdade as coisas não são tão sombrias. Ele é possível proove programas inteiros para ser correto, mas você tem que desistir de algumas idéias.
Primeiro: é altamente recomendável mudar para um idioma declarativo. Linguagens imperativas, por algum motivo, sempre foram de longe as mais populares, mas a maneira como elas misturam algoritmos com interações no mundo real torna extremamente difícil até dizer o que você quer dizer com “correto”.
Muito mais fácil em linguagens de programação puramente funcionais : elas têm uma distinção clara entre as propriedades realmente interessantes das funções matemáticas e as interações nebulosas do mundo real sobre as quais você realmente não pode dizer nada. Para as funções, é muito fácil especificar "comportamento correto": se, para todas as entradas possíveis (dos tipos de argumento), o resultado desejado correspondente sair, a função se comportará corretamente.
Agora, você diz que isso ainda é intratável ... afinal, o espaço de todos os argumentos possíveis também é, em geral, de dimensão infinita. É verdade - embora para uma única função, até os testes de cobertura ingênuos o levem muito mais longe do que você jamais poderia esperar em um programa imperativo! No entanto, existe uma ferramenta poderosa e incrível que muda o jogo: quantificação universal / polimorfismo paramétrico . Basicamente, isso permite que você escreva funções em tipos muito gerais de dados, com a garantia de que, se funcionar para um exemplo simples dos dados, funcionará para qualquer entrada possível.
Pelo menos teoricamente. Não é fácil encontrar os tipos certos que são realmente tão gerais que você pode provar completamente isso - normalmente, você precisa de uma linguagem de tipo dependente , e eles tendem a ser bastante difíceis de usar. Porém, escrever em um estilo funcional apenas com polimorfismo paramétrico já aumenta seu "nível de segurança" de maneira enorme - você não encontrará necessariamente todos os bugs, mas precisará escondê-los muito bem para que o compilador não os encontre!