I think there's several concepts here that are getting tangled:

1. A way to locally change the default T::B object
2. A way to share state between multiple T::B objects
3. A way to copy state between multiple  T::B objects
4. A way to locally change the configuration of a Test::Builder object.

Remember, state of the test (counter, details, etc...) and configuration
(level, output filehandles, etc...) are seperate concepts.

#1 might be as simple as:

   local $Test::Builder::default = $my_object;

This lets you use a subclass in a single local method.

#2 could be just:

    $tb->state($other_tb->state);

ie. each T::B object would contain a T::B::State object and simply be able
to pass it around via the normal state() accessor.

#3 would be just:

    $tb->state($tb->clone_state);

$tb->clone_state() just makes a copy of its state before returning it.

Finally, there would be a push/pop_state() so you can locally play with a
T::B object and then restore it to its original state.

    $tb->push_state();       # this is $tb->push_state($tb->clone_state);
    ...do your stuff...
    $tb->pop_state();

You could get the same effect with something like this:

    my $orig_state = $tb->state;
    $tb->state($tb->clone_state);
    ...do your stuff...
    $tb->state($orig_state);

#4 would be done by having clone_/push_/pop_/config methods like state()
above.

Stacks would be per object.


On Fri, Nov 15, 2002 at 12:58:46AM +0000, Adrian Howard wrote:
> A specific example:
> 
> In Test::Class I would like to override ok() so that it reports
> Test::Class->current_method as the default test name. The 'nice' way
> of doing this would be to create my own subclass of T::B with an
> appropriately overridden ok() method.
> 
> However, I still want to allow the user to run non-T::C based tests
> before and after the T::C tests.
> 
> This means that my custom T::B class will need to share the state of
> the T::B object used before and after the test class was run.

I'd say this would be done like so:

    package My::Test::Builder;

    sub new {
        my $class = shift;
        my $self = My::Test::Builder->create;
        $self->state(Test::Builder->new->state);
        return $self;
    }

    package Test::Class;

    my $TB = My::Test::Builder->new;

so that My::Test::Builder->new does the right thing.


> >We can seperate them out into Test::Builder configuration and test state.
> >
> >This is test state and would be shared amongst the T::B objects in the
> >default stack
> >
> >$Test_Died
> >$Have_Plan
> >$No_Plan
> >$Curr_Test
> >$Original_Pid
> >@Test_Results
> >@Test_Details
> >$Expected_Tests
> >$Skip_All
> >
> >and these are T::B configuration values and wouldn't be shared, though 
> >they
> >would be initially copied.
> >
> >$Level
> >$Exported_To
> >$Use_Nums
> >$No_Header
> >$No_Ending
> >the output filehandles
> 
> I'm not convinced about the separation between test state and T::B
> configuration - what advantage does it give you?
> 
> I can see situations where you would want to share all the state, and
> situations where you want completely different state - but can't think
> of any where you would want to share part of the state.

One is the state of the test.  The other is the configuration of the T::B
object.  Two totally different things.

If you have two T::B objects both printing test results, you want to share
the test state, but you might not want to share the configuration.

Consider your Test::Class example above.  Since Test::Class's $TB object has
a configuration seperate from Test::More's, you could safely do this:

    package Test::Class;
    my $TB = My::Test::Builder->new;
    $TB->level(5);

and not interfere with Test::More or anyone else.


> >So I guess we need several constructors.
> 
> If you have explicit access to the state I think you can get away with one 
> (and the mangled new(), which is really just there for backwards 
> compatibility).
> 
> >real constructor      - a "normal" constructor
> 
> my $new = Test::Builder->create();
> 
> >singleton constructor - returns a single, default object.  This object
> >                     is actually a wrapper around the default object
> >                     stack.
> 
>       my $default = Test::Builder->new();             # or ...
>       my $default = $Test::Builder::Default;  # or ...
>       my $default = Test::Builder->default;
> 
> depending on how you want to play with the API. Not really a
> constructor - just provides access to the default object created by
> Test::Builder.

I think what it will do is create a new Test::Builder object, plug in a
default Test::Builder::State object and... not sure what to do about the
config.  Share it?  Copy it?  From where?


> >copy               - copies the state of one object to a new one
> 
> use Storable qw(dclone);
> my $clone_tb = Test::Builder->create(state => dclone($tb->state));

<wrist slap>Module dependency</wrist slap>.

Besides, that wouldn't even work.  Consider how the existing T::B stores
things.  Better to have T::B::State implement its own clone method.


> >copy & share state    - like copy, but test state is shared between the
> >                             original and the copy
> 
> my $shared_tb = Test::Builder->create(state => $tb->state);

You're right, this one isn't necessary given the *state methods sketched out
above.


> >with those plus the push/pop_stack methods you can pick and choose what 
> >sort
> >of state sharing you want.
> 
> If we have explicit access to the state - is there any advantage in
> having a stack of objects?
> 
> Localising $Test::Builder::Default, or saving/restoring T::B->default
> would seem to give you all the functionality necessary. (You could
> always subclass T::B if you really wanted it :-)

Its more convenient.


> I really don't want to think about how all this will interact with 
> threading :-/

(Famous last words) There shouldn't be a problem.


-- 

Michael G. Schwern   <[EMAIL PROTECTED]>    http://www.pobox.com/~schwern/
Perl Quality Assurance      <[EMAIL PROTECTED]>         Kwalitee Is Job One
It's Tobacco time!

Reply via email to