Tuesday, April 3, 2007

Write the code testable

A good programmer...

A bad programmer, just have the ability to grasp the immediate problem. Usually by hacking away trying to implement a feature. Then starts to think about how the feature should be verified. By then it is highly probable that the solution is already untestable.

It is design work to implement testable code, but it is even harder to implement testability after the implementation is done.

Monday, April 2, 2007

About deterministic testing...

Consider the following common piece of code:

static Bool
mmGenCon23FollowReferences(MmLockNodeP lockNode)
{
Bool scannedObject = FALSE;
ObjectP obj;
MmDumpEnvS dumpEnv;
MmBalanceWorkSetP ws;

mmRegisterScanDumpHandler(&dumpEnv);
ws = mmGetCurrentBalanceWorkSet();
obj = mmBalanceGetReference(ws);
while (obj != NULL)
{
scannedObject = TRUE;
mmGenCon23ScanObject(&dumpEnv, ws, obj);
obj = mmBalanceGetReference(ws);

if (lockNode != NULL && --mmObjectCounter == 0) {
mmGenConMarkCheckForContentionPhase23(lockNode);
mmObjectCounter = OBJECTS_BEFORE_CONTENTIONCHECK;
}
}
mmBalanceReturnWorkSet();
mmDeregisterScanDumpHandler(&dumpEnv);

return scannedObject;
}

This is inherently impossible to run in any kind of functional testing. Why?
It has some interaction point that actually not is known when calling this method:

  • dumpEnv
  • ws
  • mmObjectCounter (Global var.)

dumpEnv
Is populated from the method

mmRegisterScanDumpHandler(MmDumpEnvP dumpEnv)

ws
The workset is something related to the current thread

&(vmtGetGcEnv(tsGetCurrentThread())->balanceWorkSet);

This method does not considering that the other methods may get their values 'randomly' from other methods or global variables but the method can be testable if the method would look something like:

static Bool
mmGenCon23FollowReferences(MmLockNodeP lockNode, MmDumpEnvP dumpEnv, MmDumpEnvP dumpEnv, MmBalanceWorkSetP ws, int* counter)
{
.
.
.
}