Anonymous methods in unit testing

Anonymous methods (aka Closures) are rare visitors in Delphi code. I have upgraded to Delphi 2009 a year before and never used the anonimous methods. But last week it finally happened.
I was writing the unit tests for fast fourier transform code. The idea of some tests was to compare the results of fast fourier trasform and discrete fourier transform of arbitrary function, and it appeared that the anonymous method is the best way to pass the function to the test.

type
  TRealFunc = reference to function(N: Integer): Extended;

type
  TTestFFT = class(TTestCase)
  private
    procedure TestRealFunc(Func: TRealFunc; Count: Integer);
  published
    procedure TestRealFFT00;
    ...
  end;

All checks are done within TestRealFunc procedure. It receives a function to transform as an argument, makes fourier transform of the function using two different methods and compares the results:

procedure TTestFFT.TestRealFunc(Func: TRealFunc; Count: Integer);
var
  FFTData: array of Extended;
  DFTData: array of TksComplex;
  M, N: Integer;
  Arg: TksComplex;

begin
  SetLength(FFTData, Count);
  SetLength(DFTData, Count);
  for M:= 0 to Count - 1 do begin
    FFTData[M]:= Func(M);

// discrete Fourier transform;
    DFTData[M]:= 0;
    for N:= 0 to Count - 1 do begin
      Arg.Re:= 0;
      Arg.Im:= (2 * Pi * N * M) / Count;
      DFTData[M]:= DFTData[M] + Func(N) * TksComplex.Exp(Arg);
    end;
  end;

// fast Fourier transform
  RealFFT(@FFTData[0], Count, False);

  CheckEquals(FFTData[0], DFTData[0].Re, 1E-15);
  CheckEquals(FFTData[1], DFTData[Count shr 1 - 1].Re, 1E-15);
  CheckEquals(0, DFTData[0].Im, 1E-15);
  CheckEquals(0, DFTData[Count shr 1 - 1].Im, 1E-15);
  for N:= 1 to Count shr 1 - 1 do begin
    CheckEquals(FFTData[2 * N], DFTData[N].Re, 1E-15);
    CheckEquals(FFTData[2 * N + 1], DFTData[N].Im, 1E-15);
    CheckEquals(FFTData[2 * N], DFTData[Count - N].Re, 1E-15);
    CheckEquals(FFTData[2 * N + 1], - DFTData[Count - N].Im, 1E-15);
  end;
end;

The test procedures themselves just calls the TestRealFunc with different arguments. For example,

procedure TTestFFT.TestRealFFT00;
begin
  TestRealFunc(
    function(N: Integer): Extended
    begin
      Result:= Sin(pi / 4 * N) + 0.5 * Sin(pi / 2 * N + 3 * pi / 4);
    end,
    8
  );
end;

Though the syntax looks cumbersome, the solution based on anonimous methods is very logical.

Leave a comment