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 <name>
         =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 Eeeeeeeeeevil.
      - 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

Reply via email to