Re: skip test interface

2001-07-19 Thread barries

On Thu, Jul 19, 2001 at 06:04:18PM -0400, [EMAIL PROTECTED] wrote:
> On Thu, Jul 19, 2001 at 10:17:07AM -0400, barries wrote:
> > The only pain I see there is the hardcoded test numbers in both places
> 
> Yes, that's just an artifact of how HiRes.t is written.  It rolls its
> own test functions.  Ignore that bit.

Actually, they make a good analog for test names, so the comment becomes:
that syntax is nice except that test names must be repeatedly entered
and kept in sync when skip()ing.

> > Of course, if you're brave/knave enough to not plan or use test numbers:
> > 
> >use Test::More 'noplan' ;
> > 
> >if ( $have_ualarm ) {
> >   ...do tests...
> >}
> > 
> > makes for short, simple tests.
> 
> Problem there is there's no external indication that anything got
> skipped. :( That and using no_plan requires you to upgrade
> Test::Harness.

Yup.  I could live with it for "casual" tests in a CPAN module, and I'd
call skip() for tests I cared about.  It also decouples skip() from
having a one-to-one correspondence with ok()s, making it easy to skip a
bunch of tests.

I think what you and I have both been beating our heads against, from
different directions, is that Perl just doesn't provide the syntax and
flow of control directives to do skip/ok/todo gracefully, where
gracefully means:

   1) Procedural control flow (ie non-declarative syntax, no "test
  executive")
   2) No need to duplicate test metadata (numbers, names, whatever)
   3) Ability to skip easily without and a call to skip() and an else
  block
   4) Ability to todoify/untodoify easily, without typing in the test
  name or number in the plan.  Hmmm, see todo_because below, though

The closure-as-first-arg-no-sub-keyword syntax is seductive, but
something like skip just won't feel right until you can give the \&
prototype in later positions.  And even then, I think it'll look goofy.

So.

How about a syntax like:

   use Test::Named ;

   =test foo

  skip "no foo!" unless $have_foo ;
  is foo, 10 ;  # Borrowed from Test::More, IIRC
  reset_foo ;

   =test bar

  todo_because "bar not low enough" ;
  ok bar ;

   =endtests

Obviously, a source filter.  The output might look like:

   Test::Named::begin('foo',__FILE__,__LINE__); eval{ #=test foo

  skip "no foo!" unless $have_foo ;
  is foo, 10 ;  # Borrowed from Test::More, IIRC

   } ; Test::Named::end(); Test::Named::begin( 'bar' ) ; eval { # =test bar

  todo_because "bar not low enough" ;
  ok bar ;

   } ; Test::Named::finish() # =end tests

Details:

   1) skip dies, Test::Named::end() "catches" it, emits message
   2) todo_because sets a flag, is(), etc. check flag, T::N::begin clears it
   3) T::N emits the plan line upon input EOF
   4) test messages all would have name, file, and line number, perhaps
  test number (especially if no name given)
   5) Multiple tests can have same name, differentiate by line number
   6) Perl's line numbering is maintained one-to-one by filter
   7) Result primitives:
 ok ;
 ok $cond ;
 notok ;
 notok $cond ;
 is $got, $expected ;
 isnt $got, $expected ;
 like $got, $regexp ;
 unlike $got, $regexp ;
   8) Non-result primitives:
 =test 
 =endtest
 =endtests
 skip $why ; # exits test immediately, others don't
 todo_because $why ; # alters behavior of result primitives
   9) A test must call a result primitive or it fails

Benefits:

   1) it's POD-like and readable, people will grok most of it readily
   2) Flow is obvious
   3) tests are counted and planned for you
   4) skip is natural Perl syntax
   5) todo_because is natural perl syntax (could be just "todo")
   6) metadata (test names) look like metadata
   7) a test name is only ever mentioned once
   8) all primitives bone simple APIs
   9) can put Perl code between/around =test...=endtest blocks
   10) Each test is in a block, so lexicals that are used in multiple
   tests myst be placed between tests (=endtest...my $foo;...=test),
   clearly differentiating intratest from intertest lexicals.

Cons:

   1) it's a source filter (though a simple, robust one)
   2) the eval blocks are non-obvious. Alternatives:
  - Could use SIG{__DIE__} instead of eval {}, but that's Eevil.
  - Could attempt to find occurences of
/^\s*skip\b/ and make them gotos, but that's an error prone parsing
process.
   3) Lexical visibility is limited to one test (could be a benefit, see
  #10 above)

Odd thought: could do away with almost all of the result primitives and
use expect/got primitives:

   =test foo

  expect 10 ;  # Could be qr/.../, etc.
  got foo ;

   =test bar

  dont_expect 10 ; # must not be 10!
  got bar ;

   =endtests

I have a source filter started to do this, need to play with it some
more.  Feedback?

- Barrie



Re: skip test interface

2001-07-20 Thread barries

On Fri, Jul 20, 2001 at 01:30:39AM -0400, Michael G Schwern wrote:
> On Fri, Jul 20, 2001 at 12:51:19AM -0400, barries wrote:
> >1) skip dies, Test::Named::end() "catches" it, emits message
> >2) todo_because sets a flag, is(), etc. check flag, T::N::begin clears it
> 
> Thank you, you just gave me a wonderful idea:

Worth every penny paid ;-).

> skip() will throw an exception.  test_block() will catch that
> exception and print something like:

Yeah, I played with this syntax a bit too, and decided I really like hte
metadata (test id, number of tests) at the top.  What I cam up with was

   test {
  test_name "foo" ;
  skip "barf" if $emetic ;
  is $stomach, $stable ;
   } ;

with test_name() being optional.  I didn't think of the number of tests
feature.  Could also do:

   test {
  plan tests => 3, name => "foo", todo => [2,3] ;
  skip "barf" if $emetic ;
  is $stomach, $stable ;
  is $ear, $stable ;
  is $floor,   $stable ;
   } ;

or some such.  The nice thing is that if you don't want the fancy
features, you don't need them.  It scales well from simple, unplanned
scripting to tight, planned testing as need be throughout the script.

> I think this will work.  I just need a better name than test_block().

Me too.

- Barrie