sábado, 31 de outubro de 2009

ZeosLib for Delphi 2010

Este sem dúvida é um dos projetos open source e freeware destinado aos desenvolvedores Delphi que mais eu respeito.
ZeosLib é uma biblioteca com um conjunto de ferramentas que oferece conexão nativa com vários de tipos de banco de dados (MySQL, ADO, PostgresSQL, Oracle, SQLite, Firebird, Interbase, MSSQL, Sybase).
Além de ser open source e freeware, uma das grandes vantagens em utilizar o ZeosLib é a flexibilidade e facilidade durante a migração de um banco de dados para outro, justamente pelos mesmos componentes serem utilizados para todos os tipos banco de dados que são suportados.

O objetivo desse artigo não é descrever sobre os recursos e funcionalidades do ZeosLib, mas sim anunciar que hoje essa biblioteca é compatível com o Delphi 2010. Para ser mais específico, já faz alguns meses que essa versão já é suportada pelo Delphi 2010, mas há pouco tempo alcançou um grau de estabilidade aceitável.
A última release estável é a versão 6.6.5 que foi publicada em 25/maio/2009 e oferece suporte até o Delphi 2007.
Apesar de algumas versões não oficiais serem lançadas posteriormente, usuário do Delphi 2009 e, hoje, Delphi 2010 têm ansiosamente aguardado por atualizações dessa biblioteca.
O suporte oficial da biblioteca ao Delphi 2009/2010 só está previsto para a versão 7.0 do ZeosLib que não tem um data definida para ser lançado e, acredito, ainda deve demorar um pouco. Entretanto já está disponível uma versão beta que, apesar de ainda requerer um bom trabalho, é suportado pelo Delphi 2010 e está em fase de teste (svn://zeos.firmos.at/zeos/branches/testing), mas totalmente funcional.


Download


A comunidade ZeosLib mantém o repositório svn (svn://zeos.firmos.at/zeos) onde você pode encontrar todas as versões já lançadas e as versões que estão em desenvolvimento.
Como alguma ferramenta svn client (ex. Tortoise) faça checkout do link svn://zeos.firmos.at/zeos/branches/testing para um pasta qualquer.

No final você deverá ter uma estrutura de diretórios igual ao da imagem ao lado.
Apesar de úteis (não sei se essa é a melhor classificação), apenas os diretórios packages e src são realmente relevantes (considerando que o objetivo é a instalação do pacote no RAD Studio 2010). Sendo assim, manter os demais diretórios é opcional.


Instalando o ZeosLib
A biblioteca ZeosLib é muito organizada e o processo de instalação é simples.


1. No IDE do Delphi, abra o projeto ZeosDbo.groupproj;
2. Compile todos os projetos (menu Project -> Build All Projects);
3. Instale o pacote de componentes selecionando a opção "Install" (no menu de contexto) do pacote "ZComponentDesign140.bpl";


Dependendo de como o RAD Studio está configurado, é possível que o IDE apresente uma mensagem de erro quando o segundo pacote (ZPlain140.bpl) for buildado;

[DCC Fatal Error] ZPlain.dpk(30): E2202 Required package 'ZCore' not found

Essa mensagem informa que o pacote ZCore (ZCore140.bpl) não foi encontrado.
Apesar ser clara, a mensagem não é muito intuitiva principalmente depois de ter sido buildado o primeiro pacote (ZCore140.bpl) e, consequentemente, o arquivo existir.
Na verdade o arquivo foi criado e está localizado em C:\Documents and Settings\All Users\Documents\RAD Studio\7.0\Bpl (o diretório depende da versão do sistema operacional e o idioma do mesmo), apenas não está sendo visível pelo RAD Studio.
Para corrigir, basta incluir $(BDSCOMMONDIR)\Bpl no "Library path", como mostra a imagem abaixo.



Após isso você poderá observar a aba "Zeos Access" na Tool Palette.


Usando o Zeos DBO
Quando ainda usava o Delphi 2009, baixei uma versão super-mega-alfa, baseada na release 6.6.5, que dizia ser suportada pelo Delphi 2009. No entanto, não consegui nem mesmo realizar uma conexão com o PostgresSQL.
Com o objetivo de validar o quão estável está essa versão, resolvi fazer alguns testes com os componentes usando uma base de dados no PostgresSQL 8.4.1.

Após configurar o componente de conexão (TZConnection) com os dados para acessar uma base de dados no PostgresSQL, tentei ativar a conexão.
Nesse momento o componente apresentou a mensagem de erro abaixo.



Um detalhe que pode ser corrigido facilmente apenas acrescentando o diretório bin da instalação do postgres (provavelmente C:\Program Files\PostgreSQL\8.4\bin) no system path. Você pode também copiar os arquivos: Comerr32.dll; krb5_32.dll; libconv-2.dll; libeay32.dll; libintl-2.dll; ssleay32.dll para a pasta system32 do Windows!

Acrescentado o diretório no system path, só foi necessário reiniciar o Delphi e tudo correu muito bem.



E por falar em open source, o que acha de ajudar essa versão do ZeosLib a deixar de ser beta?

quarta-feira, 14 de outubro de 2009

Alternativas freeware

Até não muito tempo atrás o mundo open source não era tão expressivo quanto hoje e, muitas vezes, associado a baixa qualidade. No entanto, hoje podemos vislumbrar projetos open source que competem de igual para igual com soluções pagas.
Dedico esse artigo para citar e descrever sobre alguns pacotes de componentes open source e freeware que são excelentes alternativas para produtos comerciais.


The Fastcode Challanges
The Fastcode Challanges é um projeto, ou melhor, é um desafio público de quem escreve o código mais eficiente para substituir funções e procedimentos da RTL e VCL do Delphi. Atualmente a biblioteca conta com mais de 300 funções publicadas para download e geralmente implementadas em assembly inline.
O projeto é realmente sério, tanto que algumas funções foram adotadas pela Borland substituindo as versões originais.


 Synapse TCP/IP Library
Synapse é uma biblioteca de comunicação TCP/IP muito light que preza a simplicidade e é um excelente alternativa para o pessado Indy.
Sua abordagem é um pouco diferente, utiliza comunicação síncrona (blocking). Também oferece comunicação assíncrona, mas de forma limitada.
Uma característica curiosa é a retrocompatibilidade com o Delphi 3. A versão atual (release .39) tem suporte até o Delphi 2009, sendo esse último experimental.
Observando a frequência de atualização dessa biblioteca, pressumo que muito em breve será lançado uma release com suporte ao Delphi 2010.


DCPCrypt
DCPCrypt é um bliblioteca open source que trás vários componentes que implementam algortimos de criptografia e hash.
É compatível com o Delphi 4, 5, 6, 7, 2005, 2006, 2007, 2009. Apesar de não possuir pacotes de instalação exclusivos, também é compatível com a versão 2010 do Delphi.

Algorimos de Criptografia
  • Blowfish
  • Cast 128
  • Cast 256
  • DES, 3DES
  • Ice, Thin Ice, Ice2
  • IDEA
  • Mars
  • Misty1
  • RC2, RC4, RC5, RC6
  • Rijndael (the new AES)
  • Serpent
  • Tea
  • Twofish

  Algoritmos de Hash
  • Haval
  • MD4
  • MD5
  • RipeMD-128
  • RipeMD-160
  • SHA-1
  • SHA-256, SHA-384, SHA-512
  • Tiger

Se você usa o Delphi 2010, pode utilizar os pacotes de instalação do 2009 ou, então, baixar uma versão customizada do DCPCrypt 2.0 b3 (http://rapidshare.com/files/293117004/DCPCrypt_2b2_D2010.7z).
Eu particularmente já tive a oportunidade de usa essa fantástica biblioteca e recomendo.


Delphi Fast ZLib
Uma biblioteca para compressão e descompressão de arquivos no formato zib muito mais rápida do que a que é fornecida pelo Delphi e, além disso, não requer DLL externa.
Esse bliblioteca possui várias otimizações, incluindo código para processadores P6 e alinhamento de 64 bits. Tais otimizações são automaticamente utilizadas conforme as características do hardware.

O exemplo abaixo foi extraído do site official (http://www.dellapasqua.com/delphizlib/) e demonstra como utilizar a biblioteca.
procedure TForm1.Button1Click(Sender: TObject);
var
  InputStream, OutputStream: TFileStream;
  DeCompressionStream: TZDecompressionStream;
  CompressionStream: TZCompressionStream;
  InputFileName, OutputFileName: string;
begin
  //compress
  InputFileName := 'c:\image.png';
  OutputFilename := 'c:\image.png.bzip';
  InputStream := TFileStream.Create(InputFileName, fmOpenRead);
  OutputStream := TFileStream.Create(OutputFileName, fmCreate);
  CompressionStream := TZCompressionStream.Create(OutputStream, zcFastest);
  CompressionStream.CopyFrom(InputStream, InputStream.Size);
  CompressionStream.Free;
  OutputStream.Free;
  InputStream.Free;

  // decompress
  InputFileName := 'c:\image.png.bzip';
  OutputFilename := 'c:\image2.png'; //rename to original into final code
  InputStream := TFileStream.Create(InputFileName, fmOpenRead);
  OutputStream := TFileStream.Create(OutputFileName, fmCreate);
  DecompressionStream := TZDecompressionStream.Create(InputStream);
  OutputStream.CopyFrom(DecompressionStream, 0);
  DecompressionStream.Free;
  OutputStream.Free;
  InputStream.Free;
end;


NewAC Delphi Components
NewAC é um pacote de componente destinados a tarefas de processamento de áudio.
Oferece suporte a vários tipos de arquivos de áudio (wav, Ogg Vorbis, FLAC, Monkey Audio, WavPack, MP3, Windows WMA, True Audio (TTA), OptimFROG, TAK, Musepack, DTS 5.1, AC-3).

Uma rápida descrição das caractrísticas desse pacote são:
  • Audio recording and playback using DirectSound
  • Audio recording and playback using low latency ASIO drivers
  • CD-ripping
  • Supported formats: wav, Ogg Vorbis, FLAC, Monkey Audio, WavPack, MP3, Windows WMA, DTS, AC-3, TTA, OptimFROG, TAK, Musepack
  • Sound mixing, concatenation, cutting and insertion of audio fragments.
  • Audio resampling
  • 8, 16, 24, 32 bit sound support
  • Multi-channel sound
  • Id3v*, Ape, Vorbis tags support
  • Ready for Delphi 2009
  • License: NewAC is free for both open source and commercial use

No link http://symmetrica.net/newac/formats.htm você pode ter uma noção melhor dos formatos de arquivos em comparação com as funcionalidades suportadas.


Professional Screen Camera Component
Como o próprio nome sugere, esse é um excelente componente para que deseja fazer captura do desktop em gravar em avi.
Algumas das suas características são:
  • Save desktop screen to avi file by arbitrary region.
  • Select region from 5 way. (SelObject, FreeHand, FixedMoving, FixedStable, FullScreen).
  • Auto selecting best video codecs in your system. (DivX, XviD, MP4, WMV and Standard AVIs).
  • Capability recording audio from selected input device.
  • Full support recording from single monitor and/or multi monitor.
  • Change arbitrary recording frame rate. (Up to 1000 fps — super fast machines).
  • Capability recording with set timer.
  • Change on time video priority at recording mode.
  • Capability preview before recording or with recording.
  • On time show recording status.
  • On time drawing objects, texts, images and… over video canvas. (Overlay option).
  • On time filter effective (GrayScale Drawing, Reverse Color, Rotation, Brightness, Contrast, Color Adjusting, Saturation, Noise).

 Link para acesso: http://delphitutorial.info/professional-screen-camera-component-v4710.html

quarta-feira, 7 de outubro de 2009

SafeMM - Debug Memory Manager para Delphi e C++

Já faz um tempinho que a Embarcadero publicou o SafeMM. De lá para cá eu posterguei inúmeras vezes a realização de testes para entender melhor a proposta desse gerenciador de memória.

Definição
O SafeMM é um gerenciador de memória "prova de conceito" que tem a finalidade de identificar alguns tipos de AV (Access Violation) que normalmente não são caputrados pelo gerenciador de memória padrão do Delphi.

Vamos para um exemplo.



procedure TForm1.Create(Sender: TObject);
const
  DEFAULT_OPTIONS: array[0..2] of string = ('Opção 1', 'Opção 2', 'Opção 3');
var
  i: integer;
  lMyStrList: TStringList;
begin
  lMyStrList := TStringList.Create;
  try
    for i := low(DEFAULT_OPTIONS) to high(DEFAULT_OPTIONS) do
      lMyStrList.Add(DEFAULT_OPTIONS[i]);

    // ... outras instruções
    Self.cbbAction.Items.Assign(lMyStrList);
    // ... outras instruções

    {$IFDEF DEBUG}
    ShowMessage('String list count: ' + IntToStr(lMyStrList.Count));
    {$ENDIF}
  finally
    lMyStrList.Free;
  end;

  Self.cbbAction.ItemIndex := lMyStrList.Count - 1;
  ShowMessage('String list count: ' + IntToStr(lMyStrList.Count));
end;

No exemplo acima uma lista de strings é criada e preenchida com as entradas de um array constante; essa lista é, então, atribuída para o componente "cbbAction"; por último, é posto em seleção o último item da lista.
Um trecho de código bastante comum, mas há uma falha nas duas últimas linhas. A variável "lMyStrList", naquele ponto do código, já está com sua área de memória liberada.

Se você compilar esse exemplo no Delphi (não sei se o comportamento é idêntico para todas as versões - utilizei o Delphi 2010), o segundo ShowMessage retorna o valor 0 (zero) quando tenta acessar o método Count do objeto lMyStrList, mas nenhuma exceção ou falha ocorre. No entanto prever o comportamento da instrução "lMyStrList.Count - 1;" é classe impossível, já que a memória foi liberada e não há qualquer garantia de que os dados anteriormente pertencentes ao objeto ainda estejam intactos na memória.

Apesar do exemplo aqui ser simples, essa falha na lógica de programação pode resultar em um bug complicado e consumir um tempo razoável para rastreá-lo.

Usando o SafeMM
É justamente para situações como essa que o SafeMM se propõem.
Se você utilizar esse gerenciador de memória qualquer instrução de leitura ou escrita à uma área de memória que não deveria estar acessível resultará em um AV (Access Violation).

A sua instalação é muito simples:
Step 1. Acesso o site da Embarcadero (http://cc.embarcadero.com/Item/27241) e faça o download do SafeMM.
Apesar de o código fonte ser livre, é necessário que você seja um usuário registrado. Caso não seja, registre-se e faça o download (a Embarcadero agradece);
Step 2. Descompacte o arquivo em uma pasta qualquer (apenas os arquivos 'SafeMM.pas' e 'SafeMMInstall.pas' são serão utilizados, os demais pode se apagados se assim você desejar);
Step 3. No Delphi, abra o seu projeto e adicione os arquivos 'SafeMM.pas' e 'SafeMMInstall.pas'; build e já está pronto para depurar;

OBS: Eu testei em um projeto relativamente grande, com um executável principal de muitos módulos (.bpl). Só foi necessário acrescentar a referência desses dois arquivos no projeto principal.

Exemplo de um projeto incluindo o SafeMM.
program Project2;

uses
  {$IFDEF DEBUG}
  SafeMM,
  SafeMMInstall,
  {$ENDIF}
  Forms,
  fuPrincipal in 'fuPrincipal.pas' {Form1};

{$R *.res}

begin
  Application.Initialize;
  Application.MainFormOnTaskbar := True;
  Application.CreateForm(TForm1, Form1);
  Application.Run;
end.

Eu utilizei um a diretiva {$IFDEF DEBUG} para garantir que o SafeMM não fosse incluído quando o projeto for buildado para release, ou seja, para a versão final.

Como funciona
O código fonte desse gerenciador de memória é bem pequeno e recomendo dedicar um tempinho para compreender seu funcionamento mais detalhado.
Vou explanar num alto nível como funciona e como é feito para detectar esse tipo de acesso indevido.

O SafeMM substitui as rotinas de gerenciamento de memória (GetMem, FreeMem, ReallocMem e AllocMem) por rotinas próprias (SafeGetMem, SafeFreeMem, SafeAllocMem e SafeReallocMem, respectivamente). Essas rotinas utilizaram listas auxiliares para armazenar registros com informações extras para cada bloco de memória que você for alocado durante a execução do código fonte.
Por exemplo, a linha de código do exemplo acima "lMyStrList := TStrings.Create" necessita alocar memória (que é feito pelo internamente pela rotina SafeGetMem). Nesse momento, uma entrada nessa lista auxiliar é adicionada com diversas informações sobre esse bloco de memória e, entre elas, uma se destaca: "Magic".
A propriedade "Magic", como o próprio nome sugere, é usada para armazenar um código mágico (123123 - decimal). E é utilizada, juntamente com outras variáveis, para manter a integridade desses blocos de informações.
Quando o objeto local lMyStrList é desalocado, o SafeMM realiza várias operações internas e, por último, bloqueia o acesso à região de memória onde o TStringList estava alocado (através da rotina VirtualProtect). Assim, quando você tentar acessar novamente a variável lMyStrList, um AV (Access Violation) é levantado fazendo com que a depuração seja interrompida na linha exata onde ocorreu o acesso indevido.

Simples e funcional!

Considerações
O SafeMM não tem o propósito de substituir o gerenciador de memória padrão do Delphi, ele foi concebido para o processo de depuração.
Um aplicativo normal pode chegar a ter a quantidade memória alocada multiplicada em até 4 vezes. A versão atual é 0.3 e é a mesma desde 29 de março de 2007.

segunda-feira, 5 de outubro de 2009

Assertivas - Boas práticas de programação

Olá a todos.

Há alguns dias atrás eu estava corrigindo um daqueles bugs que só ocorrem muito esporadicamente e que você mal sabe como reproduzi-lo, menos ainda o motivo dele estar ocorrendo.
Provavelmente vocês concordarão que na maioria das vezes esse tipo de bug é ocasionado por um descuido durante a programação. E que a correção desse tipo de falha costuma ser um detalhe em uma única linha de código.
Quem nunca gastou horas de depuração para descobrir que usou a variável errada ou o teste errado dentro de, por exemplo, uma condição "if"?
Por essa razão resolvi revisar muitas daquelas boas práticas de programação que costumamos aprender, mas que deixamos de utilizá-las por achar que somos super-programadores ou por qualquer outro motivo.
O uso de assertivas é um assunto bastante velho e não é um recurso exclusivo do Delphi, muito pelo contrário, quase todas as linguagens que tive a oportunidade de aprender oferecem suporte a esse recurso tão simples e útil.

Definição
As assertivas são validações lógicas de uma condição julgada necessária para o correto funcionamento do algoritmo no momento em que são verificadas. A condição testada pela assertiva representa uma pressuposição assumida e instrui o sistema a notificar sempre que essa condição não for satisfeita, garantindo que não haja um comportamento imprevisto.
Asserções no Delphi são implementadas através da rotina Assert. Apesar do IDE informar que esse procedimento faz parte da unit System, você não encontrará seu código fonte porque é uma função interna do compilador.
A sintaxe de uma assertiva, em Delphi, é a seguinte:
Assert(Condition: Boolean; [Message: string]);

O parâmetro "Condition" é uma expressão booleana que representa a condição a ser satisfeita. O parâmetro "Message" é opcional e pode ser utilizado para especificar uma mensagem customizada exibida quando a condição não for satisfeita.
O funcionamento do Assert é bastante simples. A condição especificada pelo parâmetro Condition é validado e, se o resultado for True, o fluxo de execução continua normalmente. Caso contrário, se a condição resultar em False, uma exceção do tipo EAssertFailed será levantada apresentando a mensagem de texto especificada pelo parâmetro "Message".
Caso você não especifique o parâmetro Message, o Delphi irá utilizar por padrão o texto ‘Assertion failure'.
Um exemplo pode ser observado na imagem abaixo:



Por si só a mensagem padrão exibida já é suficientemente útil. Para a maioria das falhas detectada com as assertivas, sabendo-se o local exato no código fonte onde a assertiva encontra-se e obtendo a stack tracing do depurador, é possível identificar e corrigir o bug.

Relevância
Mesmo sendo seu uso muito simples é importante ficar atendo para a finalidade na qual as assertivas se propõem e, assim, evitar que sejam usadas erroneamente.
Tenha em mente que o Assert é uma prática de programação defensiva que instrumenta o código fonte e o instrui a identificar falhas e comportamentos não originalmente previstos e, assim, detectar bugs em seus estágios iniciais.
Apesar de ser na prática bastante usada para validar as entradas de métodos/rotinas, sua proposta não é essa e sim validar condições consideradas obrigatórias e que, quando não satisfeitas, identifica uma falha no sistema.
Muitas vezes a validação de entradas e/ou saídas das rotinas carrega uma pressuposição e, conseqüentemente, passível de uso de assertivas (e recomendável). No entanto, essa sutil diferença de conceito pode fazer a diferença entre incrementar o grau de corretude do seu código fonte ou acrescentar mais bugs.

Versão Interna x Versão de Produção
Uma característica peculiar das assertivas é a possibilidade de "anular" a geração de código binário.
Essa característica é muito útil quando o sistema é buildado e posto em produção, ou seja, quando a intenção é distribuir o software.
Considerando que a finalidade das assertivas é identificar pressuposições não satisfeitas, quando a versão de sistema é considerada estável e, portanto, passível de ser distribuída, a validação dessas condições não são mais relevantes do ponto de vista dos usuários (clientes) e, portanto, não há necessidade que seja gerado código binário.
O Delphi possui um item de configuração específico que permite habilita/desabilita a compilação das assertivas.
Acesso o menu Projet -> Options -> aba Compiler -> grupo Debugging -> opção "Assertions", conforme mostra a imagem abaixo:



Também existe uma alternativa que pode ser utilizada para forçar a compilação das assertivas ou, então, para desabilitá-las em trechos específicos de código fonte.
Nesse caso há as diretivas de compilação {$ASSERTIONS ON / OFF} e {$C + / -}. Com elas você pode delimitar uma região do código fonte que habilitarão ou não a geração de código para as assertivas.

Use Case 1:
Esse é um exemplo muito simples e não tem uma funcionalidade prática, mas servirá para demonstrar o uso desse recurso fantástico.

function TfrmPrincipal.GetCount: Integer;
begin
  Assert(Self.FItems <> nil);

  Result := Self.FItems.Count;
end;

Observe que nossa assertiva está pressupondo que a propriedade FItems não pode ser nil.
É fácil prever que se não houvesse essa assertiva e a propriedade FItems não fosse inicializada, quando tal método fosse chamado o sistema certamente falharia. O que caracteriza uma brecha para um bug se instalar e que pode ser facilmente detectado com o acréscimo do Assert.
Entretanto dizer se essa assertiva está ou não empregada corretamente irá depender da finalidade do método e da sua visibilidade (private, protected, public).
Nos próximos exemplos essa questão será abordada.

Use Case 2:
Vamos imaginar um programa simples que disponibiliza ao cliente dois campos de edição para valores numéricos e, ao pressionar um botão, é apresentado um dialog com o valor da divisão entre os valores desses dois campos.
Sabemos que a divisão por zero ocasiona uma exceção (EDivByZero) e necessita ser tratado.

function Dividir(const Dividento, Divisor: Integer): Double;
begin
  Assert(Divisor <> 0);

  Result := Dividento / Divisor;
end;

procedure TfrmPrincipal.btnResultadoClick(Sender: TObject);
var
  lResultado: Double;
begin
  lResultado := Dividir(sedtDividento.Value, sedtDivisor.Value);

  ShowMessage('Resultado da divisão: ' + FloatToStr(lResultado));
end;

Quando o botão é pressionado, o evento “btnResultadoClick” é gerado. Este chama a rotina “Dividir” repassando os valores dos dois campos e apresentando um resultado em um dialog.
No método Dividir há uma assertiva que pressupõem que o valor do parâmetro Divisor não seja 0 (zero).
Esse é um exemplo do emprego incorreto do Assert.
No momento em que a compilação de assertivas for desabilitada, a possibilidade de falha seria alta, principalmente porque os valores da divisão são fornecidos diretamente pelo cliente.
A visibilidade da rotina “Dividir” vai além de simplesmente ser usada internamente. Além disso, as assertivas não devem ser utilizadas para validar regras de negócio e, portanto, seu emprego no exemplo acima deve ser revisto.

Use Case 3:
O exemplo abaixo apresenta uma situação bastante problemática e que certamente resultará em dores de cabeça no momento em que o sistema for posto em produção.

procedure TfrmPrincipal.AddItem(Item: TObject);
begin
  Assert(Self.FItems.Add(Item) >= 0);
end;

O método “AddItem” tem a finalidade de acrescentar um objeto, passado por parâmetro, a uma lista interna. E há uma pressuposição de que esse item sempre será acrescentado à lista.
Mas aí eu questiono: O que acontecerá se a compilação dos Assertions for desabilitada?
A resposta é muito simples, o sistema falhará. E pior, a falha pode ocorrer em um ponto do código que aparentemente não apresenta relação com o verdadeiro causador do bug.
Então você houve aquela famosa frase: “Sim, mas eu testei e estava funcionando perfeitamente!”

Use Case 4:
Na maioria das vezes o Assert é utilizado no início de métodos/rotinas que exigem a obrigatoriedade de parâmetros, faixa de valores específicos, recursos alocados, variáveis ou objetos inicializados ou qualquer outra condição necessária para o correto funcionamento desse método/rotina. Mas como dito no início desse artigo, o uso de Assert é uma prática de programação defensiva.
Imaginemos uma situação onde o sistema realize um cálculo relativamente complexo e que, baseando-se nos parâmetros fornecidos, o resultado nunca deverá ultrapassar uma faixa de valores específica.

procedure TfrmPrincipal.btnResultadoClick(Sender: TObject);
var
  lResultado: Integer;
begin
  lResultado := Calcular(Param1, Param2, Param3, Param4);
  Assert(lResultado <= 80, 'Resultado fora da faixa permitida.');

  // ...
end;

A assertiva aqui é de grande valia e possibilita identificar uma situação falha, seja ela porque os valores dos parâmetros fornecidos não estavam corretos ou porque a função “Calcular” não está realizando o cálculo de forma correta. Independente do motivo, o Assert garante que a inconsistência não seja levada adiante na execução do sistema.

Use Case 5:
Esse próximo exemplo é um pouco semelhante ao caso acima, mas é uma situação comum.
Imagine que o sistema mantenha uma lista dos componentes relacionados com uma funcionalidade qualquer. A referência desses componentes são armazenadas em um objeto TList e em um dado momento é necessário obter o índice de um desses componente dentro da lista.

procedure TfrmPrincipal.btnResultadoClick(Sender: TObject);
var
  lResultado: Integer;
begin
  lResultado := Self.FItems.IndexOf(Self.FSelected);
  Assert(lResultado >= 0, 'Componente não encontrado.');

  // ...
end;

Aqui há a pressuposição de que o componente selecionado deve obrigatoriamente ter usa referência armazenada em um dos itens da lista. Caso isso não ocorra, há o indício de um bug.
Novamente temos um exemplo de programação defensiva que pode evitar dores de cabeça.

Use Case 6:
A situação abaixo não é muito comum e, particularmente, nunca li qualquer relato, mas é uma prática que costumo utilizar quando estou implementando eventos de um formulário.
Vamos supor que um dado sistema deva possuir uma tela de cadastro de clientes, e uma das informações requeridas pelo cadastro é o estado civil.
Então, durante a fase de construção do formulário, você acrescente dois componentes TRadioButtom. Um para representar o estado civil ‘Solteiro’ e o outro para representar o estado civil ‘Casado’.
O sistema determina que se o cliente não for solteiro, alguns campos devem ser habilitados e preenchidos durante o cadastro.
Então você resolve implementar essa regra de negócio da seguinte forma:

procedure TfrmPrincipal.rdbEstadoCivilClick(Sender: TObject); begin
  if Sender = rdbCasado then
  begin
    tabConjugue.Visible := True;
    // Outras tarefas
  end
  else
    tabConjugue.Visible := False;
end;

Ou seja, você compartilha o mesmo evento OnClick com os dois componentes TRadioButtom, tornando visível uma parte do formulário caso o componente rdbCasado esteja marcado.
Depois de um tempo você julga necessário acrescentar outro estado civil (Viúvo) e, então, insere um novo TRadioButtom e associa o mesmo método ao evento OnClick deste novo componente.
Se você esquecer-se de modificar o código prevendo essa nova condição, o sistema não irá falhar e, dependendo do caso, pode ser facilmente detectado em testes funcionais. Entretanto, o uso de assertivas aqui pode economizar um tempo considerável e tornar a vida da equipe de testes mais tranqüila.

procedure TfrmPrincipal.rdbEstadoCivilClick(Sender: TObject); begin
  Assert((Sender = rdbCasado) or (Sender = rdbSolteiro));

  if Sender = rdbCasado then
  begin
    tabConjugue.Visible := True;
    // Outras tarefas
  end
  else
    tabConjugue.Visible := False;
end;

Aqui o Assert está pressupondo que esse evento só será gerado pelos componentes “rdbCasado” e “rdbSolteiro”.
Assim, se outro componente fosse associado com esse método, a assertiva irá lembrar-te de que algo está faltando.

Use Case 7:
A criação de componentes ou de classes persistentes (que herdam de TPersistent) exige a implementação do método Assign, cuja finalidade é copiar os dados de um outro objeto de mesmo tipo (ou não) para as respectivas propriedades.
Durante a fase de criação dessas classes é muito comum ocorrer constantes modificações na lista de propriedades, o que exige a atualização do método Assign. No entanto, muitas vezes você não atualiza o método Assign imediatamente após ter acrescentado novas propriedades, deixando para um momento mais apropriado.
Então outras tarefas surgem e os métodos de importação/exportação ficam desatualizados.
Vamos analisar o exemplo abaixo:

TTeste = class(TPersistent)   private
    FID: integer;
    FNome: string;

  public
    property ID:   integer read FID;
    property Nome: string  read FNome write FNome;

    procedure AssignTo(Dest: TPersistent); override;
end;

implementation

procedure TTeste.AssignTo(Dest: TPersistent);
begin
  Assert(Dest <> nil);
  Assert(Dest.inheritsFrom(TTeste));
  Assert(Dest.InstanceSize = 16, ‘A classe TTeste foi modificada, revise o método Tteste.AssignTo()!’);

  TTeste(Dest).FID   := Self.FID;
  TTeste(Dest).FNome := Self.FNome;
end;

Observe o Assert que está em cor vermelha. Se uma nova propriedade fosse acrescentada à classe, o Assert notificaria que o método Assign está desatualizado (logicamente quando ele fosse chamado).
É claro que fazer uma validação utilizando o InstanceSize da classe não irá identificar todos os tipos de mudanças. Você poderia, por exemplo, modificar o tipo da propriedade “FID” para ShortInt e acrescentar outra propriedade, também ShortInt, que e o tamanho da classe permaneceria os mesmos 16 bytes.
Mesmo assim, a assertiva é de grande ajuda. Principalmente em caso onde há dependência de classes.
Métodos de exportação e importação também são exemplos semelhantes onde esse mesmo caso se aplica.

Customizando o Assert
Até agora vimos diversas situações que podemos empregar o uso do Assert para identificar falhas ou nos ajudar a revisar trechos de código fonte que são dependentes de alguma recurso (classe, record, ...).
Para a maioria das necessidades dos desenvolvedores/testers, o uso padrão do comado Assert já é suficiente, mas este comando ainda guarda um recurso muito interessante, que é a sua customização.
Sempre que um asserção é levantada, o Delphi executa a rotina referenciada pela variável AssertErrorProc.
A variável AssertErrorProc está localizada na unit System e é, na verdade, um ponteiro para uma função com a seguinte assinatura:

procedure (const Message, Filename: string; LineNumber: Integer; ErrorAddr: Pointer);

Isso significa que você pode escrever o seu próprio tratador de Assertivas bastando. escrever uma rotina com a mesma assinatura e atribui-la para a variável global AssertErrorProc.

Exemplo:
procedure MeuTratador(const Message, Filename: string; LineNumber: integer; ErrorAddr: Pointer);
begin
   // Seu código fonte.
end;

AssertErrorProc := MeuTratador;

Talvez você se pergunte: “Mas para que eu necessitaria implementar um tratador de assertiva?”
Há algumas situações que podem ser muito úteis como por exemplo o envio de um email de notificação para o time desenvolvimento; manter um log de falhas em arquivo; automatizar alguma ação relacionada a testes; abrir um ocorrência de falhas em um sistema externo;...