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!