On 1/11/16 4:53 PM, Chad Granum wrote:
> Test::More/Test::Builder work VERY hard to ensure nothing inside them alters 
> $! or $@. This is for
> thing like this:
> 
>         ok(do_something_scary());
>         is($!, 0, "expected $! val");
>         is($@, undef, '$@ not changed');
> 
> Without Test::More/Builder being careful to support this, the second 2 
> assertions could fail because
> something inside ok() modifies $! or $@.

If your test functions modify the really common parts of your global 
environment then it becomes
very difficult to test them... and testing error reporting is a very common 
thing you'd want to do!

Kent already pointed out why changing this makes testing $! and $@ very, very 
awkward.  Let's see
that again.

    my ( $error, $exception );
    ok(do {
              local $@;
              local $!;
              my $ret  = do_something_scary());
              ( $error, $exception ) = ($!, $@);
              $ret
    });
    is($error, 0, "expected $! val");
    is($exception, undef, '$@ not changed);

Gross.  I don't know how many times I've written code like this.  I hate it.  I 
always encapsulate
it somehow.  And when a library doesn't have good global discipline it makes it 
even harder for me
to have good global discipline.

We tell the users that $! and $@ are only safe per function call.  Then we 
encourage them all over
the docs and interface to pass function return values directly into test 
functions.  Then we tell
them they should be testing their error cases... but to do safely requires 
gross scaffolding.
That's not fair to the user.  The result will be that people won't test their 
error conditions,
library quality will drop, and you'll waste a lot of time on a bug that should 
have been tested.

The argument that $! is only reliable per function call, that's a lowest common 
denominator
thinking.  One of the fundamental design principles of Test::Builder was that 
it had to be the
GREATEST common denominator!

I don't write libraries to the lowest common denominator.  I write libraries 
that raise the bar and
encourage others to do so as well.  Perl 5's error handling is bad, but that 
doesn't mean my
library's error handling also has to be bad.  As library authors we've been 
spending decades working
around Perl 5's bad parts.  We know how to do it better.  This is 
extraordinarily important for a
language's testing library: if you can't test it (or it's gross and annoying) 
then it won't happen.
 If you want to see better global discipline in Perl libraries, then the 
testing library has to
start first, because everything will use it.

Test libraries are first and foremost about the user.  They encourage the user 
to more and better
testing!  Implementation effort is a much, much lesser concern.  And all the 
positives reasons below
not to do it are implementation reasons.  There are no positives for the Test2 
user.

> *Reasons for dropping this promise from Test2:*
> 
>   * Simplifies code
>   * $! and $@ are altered by many many things, adding { local ... } around 
> all of them is a pain
>   * Sometimes internals you don't expect to set $! will
>   * Perl itself documents that you cannot depend on $! and $@ being unchanged 
> past the immediate
>     line after you set it.
>   * Test::Builder will continue to protect $! and $@, so nothing will break
>   * Test2 is new, nothing depends on it preserving these

The one below is the only reason that talks about making Test2 better for the 
user.

> *Reasons not to drop it:*
> 
>   * It is helpful to people who might not realize $! and $@ are often altered 
> unexpectedly.
> 
> I am asking for input in case I missed any reasons/arguments for either side. 
> In either case
> backwards compatibility will be preserved.
> 
> -Chad
> 
> 

Reply via email to