[fpc-pascal] FPCUnit test + raise E;
Sven Barth
pascaldragon at googlemail.com
Sun Sep 15 21:25:50 CEST 2013
On 15.09.2013 13:05, Marcos Douglas wrote:
> On Sun, Sep 15, 2013 at 6:56 AM, Sven Barth <pascaldragon at googlemail.com> wrote:
>> On 15.09.2013 03:21, Marcos Douglas wrote:
>>>
>>> Hi,
>>>
>>> 1) I have a code like that:
>>>
>>> procedure TghSQLConnector.Connect;
>>> begin
>>> try
>>> FLib.Connect;
>>> except
>>> on E: Exception do
>>> DoOnException(E);
>>> end;
>>> end;
>>>
>>> https://github.com/mdbs99/Greyhound/blob/0.1.8/src/ghsql.pas#L1565
>>>
>>>
>>> 2) DoOnException was implemented so:
>>>
>>> procedure TghSQLHandler.DoOnException(E: Exception);
>>> begin
>>> if Assigned(FOnException) then
>>> FOnException(Self, E)
>>> else
>>> raise E;
>>> end;
>>>
>>> https://github.com/mdbs99/Greyhound/blob/0.1.8/src/ghsql.pas#L443
>>>
>>>
>>> So, as you see, if occurs a Exception the code checks if the event
>>> (OnException) was setted. If yes, call user implementation; if no,
>>> call raise E;
>>> It works... but not when I uses in FPCUnit tests.
>>>
>>> See a simple test (this works):
>>>
>>> procedure TghSQLConnectorTest.TestOnException;
>>> begin
>>> // catch
>>> FConn.OnException := @DoOnException;
>>> FConn.Script.Text := 'foo';
>>> FConn.Execute;
>>> end;
>>>
>>> https://github.com/mdbs99/Greyhound/blob/0.1.8/test/ghsqltest.pas#L246
>>>
>>>
>>> The code DoOnException is:
>>>
>>> procedure TghSQLTest.DoOnException(Sender: TObject; E: Exception);
>>> begin
>>> AssertTrue(Assigned(E));
>>> end;
>>>
>>> https://github.com/mdbs99/Greyhound/blob/0.1.8/test/ghsqltest.pas#L141
>>>
>>> So, if I change the test like bellow... BUM! SIGSEGV!!
>>>
>>> procedure TghSQLConnectorTest.TestOnException;
>>> begin
>>> // removed >>> FConn.OnException := @DoOnException;
>>> FConn.Script.Text := 'foo';
>>> FConn.Execute;
>>> end;
>>>
>>>
>>> The ERROR is:
>>> [Content]
>>> Project test raised exception class 'External: SIGSEGV'.
>>>
>>> At address 40B758
>>
>>
>> Could you as a test replace the
>>
>> raise E
>>
>> with
>>
>> raise E at get_caller_addr(get_frame), get_caller_frame(get_frame);
>>
>> and check whether this makes a difference?
>
> I tried. No makes difference.
> (could you explain which the difference to call "raise" using "raise E
> at [...]"?)
The raise-at statement allows you to raise an exception for a specific
address. This allows you to have e.g. a error handling procedure (to
wrap a more compilated generation of a exception class) but have the
exception address still point to the original call site. E.g.:
=== code begin ===
procedure Error(aArg: Boolean);
begin
if aArg then
raise Exception.Create('Test')
else
raise Exception.Create('Test') at get_caller_addr(get_frame),
get_caller_frame(get_frame);
end;
procedure Test1;
begin
Error(True);
end;
procedure Test2;
begin
Error(False);
end;
begin
try
Test1;
except
DumpExceptionBackTrace(Output);
end;
try
Test2;
except
DumpExceptionBackTrace(Output);
end;
end;
=== code end ===
In case one the stack trace will contain Test1 and Error in case two the
stack trace will be only Test2.
> I implemented -- but not up to Git yet -- some like that:
> procedure TghSQLHandler.DoOnException(E: Exception);
> var
> NewEx: EghSQLError;
> begin
> if Assigned(FOnException) then
> FOnException(Self, E)
> else
> begin
> NewEx := EghSQLError.Create(Self, E.Message); // <<<
> NewEx.InnerException := E; // <<<
> raise NewEx;
> end;
> end;
>
> So, if I recreate the Exception, it works in any cases. As you see, I
> created a new property (InnerException) to save the original
> exception to know what the real Exception happened...
> But I think this introduces much more overhead to processing.
Can you access fields of the InnerException? Maybe it's a problem of
reraising an existing exception...
> In my code I have classes that inherited from TghSQLHandler. This
> class have the DoOnException method.
> So DoOnException can be call many times in override methods. Maybe the
> program "broke" the stack because "raise E" can be call and raise an
> exception; the next "level" raise another; and next call again. :/
Could possibly be. If you could reproduce it in an as simple example as
possible that would help.
Another thing you could try (just for testing): change your exception
handler procedure to a function that returns bool and use it like this:
=== code begin ===
procedure TghSQLConnector.Connect;
begin
try
FLib.Connect;
except
on E: Exception do
if not DoOnException(E) then
raise;
end;
end;
=== code end ===
Regards,
Sven
More information about the fpc-pascal
mailing list