In Automated Testing of Window Forms
I wrote that various elements of the form's definition can be verified automatically.
One thing to check is whether there are any event handlers defined
that aren't actually hooked to the respective events (hence, called here "dangling" event handlers).
For instance, that there's a FormShow procedure, but the OnShow event doesn't have a handler assigned
(or has a different handler assigned).
Usually, when you define event handlers using Delphi's designer, you won't get this problem.
But when using Visual Form Inheritance (VFI), moving code between classes in a hierarchy,
refactoring, and performing similar operations on form classes,
definitions in the form's .dfm file may get out of sync. with the actual code.
Hence the procedure below:
procedure CheckDanglingEventHandlers(AWinControl: TWinControl);
// Tests whether for a control named N (e.g. Form) and its event OnX (e.g. OnShow)
// there is a procedure NX (e.g. FormShow) that is NOT defined
// as a handler of to the OnX event.
{$IFOPT C+ }
var
LPropList: PPropList; // list of published properties
k, LCount: integer;
LPropName: stirng; // current property's name
LExpectedHandlerName: string; // expected name of the event handler
LExpectedHandlerAddr: pointer; // address of the expected event handler
LActualHandlerInfo: TMethod; // method info of the actual event handler
{$ENDIF }
begin
{$IFOPT C+ }
// Get the list of published properties and their count:
LCount := GetPropList(AWinControl, LPropList);
try
for k := 0 to LCount - 1 do
begin
// For each property:
LPropName := LPropList[k].Name;
if Copy(LPropName, 1, 2) = 'On' then
begin
// Get the expected handler name (e.g. FormShow);
// CustomForms need to be handled a bit differently here -
// the default name of the handler will be FormShow (for OnFormShow),
// regardless of the form's name:
LExpectedHandlerName := Copy(LPropName, 3, Length(LPropName));
if AWinControl is TCustomForm then
LExpectedHandlerName := 'Form' + LExpectedHandlerName
else
LExpectedHandlerName := AWinControl.Name + LExpectedHandlerName;
// Get the address of the default and actual event handlers:
LExpectedHandlerAddr := AWinControl.MethodAddress(LExpectedHandlerName);
LActualHandlerInfo := GetMethodProp(AWinControl, LPropName);
// Either there's no method with the expected event handler name,
// or that method's code is the same as the actual event handler's code.
Assert((LExpectedHandlerAddr = NIL) or
((LExpectedHandlerAddr = LActualHandlerInfo.Code) and
(LActualHandlerInfo.Data <> NIL)),
AWinControl.Name + '-Event ' + LPropName + ': ' + LExpectedHandlerName);
end;
end;
finally
FreeMemAndNIL(LPropList);
end;
{$ENDIF }
end;
This procedure should be called after a form is created.
However, this is a run-time check, so either you need to open each form in your application manually,
or add unit tests that would create each form of the application automatically.
OTOH, you could write a simplified Delphi parser and a .dfm parser,
and check this at the source code level... ;-)
Top
|