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!