14 February 2010

Turbo Pascal Prime Factors Kata

Wow Retro Recently I started performing the Prime Factors Kata. After playing with Java and Ruby I had the weird idea of performing it in every programming language I ever knew. Well, maybe not every - I don't plan to use 6502 or 80x86 assembler, that wouldn't be fun. But I already did it in BASIC. Going forward in time Turbo Pascal would be next. So it's time for another retro post ;-)

Where Is My TPUnit?
I couldn't find any unit testing framework for Turbo Pascal. The closest thing I could find was FPCUnit packaged with Free Pascal. Unfortunately it's not compatible with good old TP. So I had to roll my own. I started with some minimalist infrastructure.
TYPE TestCase = OBJECT
    PROCEDURE AssertEquals(msg:String; expect, act:Longint);
    PROCEDURE AssertNil(msg:String; act:Pointer);
    { other asserts ... }
    PROCEDURE Fail(msg:String);
    { TestCase }
    PROCEDURE SetUp; VIRTUAL;
    PROCEDURE TearDown; VIRTUAL;
  END;

PROCEDURE TestCase.AssertEquals(msg:String; expect, act:Longint);
VAR ex, ac:String;
BEGIN
  IF expect <> act THEN
  BEGIN
    Str(expect, ex);
    Str(act, ac);
    Fail(Concat(msg,' expected ',ex,' but was ',ac));
  END;
END;

...

PROCEDURE TestCase.Fail(msg:String);
BEGIN
  TearDown;
  Writeln(' - FAILED');
  Writeln(msg);
  Halt(1);
END;

PROCEDURE TestCase.SetUp;
BEGIN
END;

PROCEDURE TestCase.TearDown;
BEGIN
END;
Subclasses may overwrite SetUp or TearDown, add test methods and call Asserts. To keep it simple the first failed assertion stops program execution. What's missing is some kind of procedure RunTest that would wrap a particular test method inside calls to SetUp and TearDown. Hmm - function variables might be handy here. In case you are not familiar with them, here is an example:
{$F+} {needs far calls for function variables}
TYPE FuncVar = PROCEDURE;

PROCEDURE FancyMethod(method:FuncVar);
BEGIN
  method; { invokes the method }
END;

PROCEDURE SomeMethod;
...

VAR v:FuncVar;
BEGIN
  v := SomeMethod;
  FancyMethod(v);
END.
Pascal's CalculatorUnfortunately Turbo Pascal does not allow class methods (e.g. TestCase.AssertEquals) to be used as function variables. (At least I couldn't figure.) Obviously self is an implicit parameter of all such methods. Well not obviously, but analysing the generated machine code helps ;-)
SomeMethod; { -> 0E E8 D1 FB }
push CS { because of $F+ }
call fbe1 { address of SomeMethod in CS }

cls.ClassProc; { -> BF 70 00 1E 57 0E E8 ED FB }
mov DI, #0070 { address of object cls in DS }
push DS
push DI { first parameter is self }
push CS
call fbed { address of ClassProc in CS }
Using this knowledge TestCase.RunTest is implemented a bit dirty using an untyped Pointer argument:
PROCEDURE CallClassPtr(pt:Pointer; VAR cls:TestCase);
VAR s,o:Word;
BEGIN
  s := Seg(cls);
  o := Ofs(cls);
  ASM
    mov DI, [o]
    mov AX, [s]
    push AX
    push DI
    call [pt.dword]
  END;
END;

PROCEDURE TestCase.RunTest(name:String; testMethod:Pointer);
BEGIN
  Write('TEST ', name);
  SetUp;
  CallClassPtr(testMethod, self);
  TearDown;
END;
The Prime Factors Kata
Having a simple TPUnit in place, it's time for the kata itself. The seven test methods of PrimeFactorsTest,
PROCEDURE PrimeFactorsTest.Run;
BEGIN
  RunTest('TestOne', @PrimeFactorsTest.TestOne );
  RunTest('TestTwo', @PrimeFactorsTest.TestTwo );
  RunTest('TestThree', @PrimeFactorsTest.TestThree );
  RunTest('TestFour', @PrimeFactorsTest.TestFour );
  RunTest('TestSix', @PrimeFactorsTest.TestSix );
  RunTest('TestEight', @PrimeFactorsTest.TestEight );
  RunTest('TestNine', @PrimeFactorsTest.TestNine );
END;
yield
FUNCTION TPrimeFactors.generate(i:Longint):ArrayListPtr;
VAR factors:ArrayListPtr;
    candidate:Longint;
BEGIN
  factors := new(ArrayListPtr, Init);

  FOR candidate := 2 TO i DO
  BEGIN
    WHILE i MOD candidate = 0 DO
    BEGIN
      factors^.Add(candidate);
      i := i DIV candidate;
    END;
  END;

  generate := factors;
END;
ArrayListPtr is a pointer to a variable sized, user defined list backed by an array of Longints, similar to Java's ArrayList<Integer>. I can't deny I'm a Java guy. Everything I code looks like Java :-) (Probably I would have used a linked list back then instead of a complex object. Something like
TYPE PrimeFactorPtr = ^PrimeFactor;
  PrimeFactor = RECORD
    value:LongInt;
    next:PrimeFactorPtr;
  END;
Still the procedure body looks the same and the kata does not change much.)

(Download full source)

13 February 2010

Testing For All One's Worth

In the end of last year, after a long break, the third part of the 'Code Cop' series has been published in the well known German magazine iX:

Crash DummyTägliche Builds mit automatisierten Tests (Daily Builds with Automated Testing) (iX 1/2010). [... Automated testing is vital for the quality assurance. Unit-tests are applied easily using JUnit. The same is true for functional testing thanks to a number of already existing tools. By adding testing capabilities to the build, developers are more willing to write tests. In the end the analysis of the code coverage achieved by the tests reveals some possible weak points. ...]

(Download source code of Ant/JUnit/HttpUnit and EMMA integration.)

ReferencesSome other test and code coverage tools mentioned in the article are AgitarOne, dbUnit, Clover, Cobertura, HtmlUnit, JCoverage, Jester, Jtest, JUnitPerf, Selenium, XMLUnit (incomplete list).

(List of all my publications with abstracts.)