[sorry for the delay] Here's a more fleshed-out example...

This example is talking about two modules:

One that provides the write_variants sub and some related classes.
It's dumb, with no DBI or DBIT knowledge at all. Let's call it
WriteVariants for now.

The other is closer to the original DBIT 'DSN provider'. Let's call it
DBIT::Provider for now. It calls WriteVariants with the appropriate
arguments to create the variant test files needed for DBIT.
That's a set of tests, plus a list of callbacks.


Rough terminology:

A 'setting' is an instance of a trivial class that can provide a string
of code for setup and teardown of a setting.

new_env_setting is a function that returns an instance of 'setting'
object that represents an environment variable.

new_multi_setting is a function that returns an instance of 'setting'
object that represents an ordered list of other settings.

A 'setting provider' callback returns a hash of name-settings pairs.


Example:

write_variants(
    providers => [  # the order is important
        \&dbi_settings_provider,
        \&driver_settings_provider,
        \&dbd_settings_provider,
    ],
    test_cases => { # details not important here
        # 
        01basics => TC->new(...),
        02dbidrv => TC->new(...),
        ...
    },
);


sub dbi_settings_provider {

    my %settings = (
        pureperl => new_env_setting(DBI_PUREPERL => 2),
        gofer    => new_env_setting(DBI_AUTOPROXY => 
'dbi:Gofer:transport=null;policy=pedantic'),
    );

    # Add combinations:
    # Returns the original settings plus extras created by combining.
    # In this case returns one extra key-value pair, i.e.:
    # $settings{pureperl_gofer} = new_multi_setting( $settings{pureperl}, 
$settings{gofer} );
    %settings = add_combinations(%settings);

    # add a 'null setting' that tests plain DBI with default environment
    $settings{plain} = undef;

    return %settings;
}

This dbi_settings_provider returns four sets of name-settings pairs.
One has no settings, two have a single setting, and one has two settings.


sub driver_settings_provider {
    my ($settings_context) = @_;

    my @drivers = Test::Database->list_drivers("available");

    # filter out non-pureperl drivers if testing with DBI_PUREPERL
    @drivers = grep { driver_is_pureperl($_) } @drivers
        if $settings_context->env('DBI_PUREPERL');

    return map { $_ => new_env_setting(DBI_DRIVER => $_) } @drivers;
}

This driver_settings_provider is called once for each of the
name-settings pairs returned by dbi_settings_provider.

The $settings_context argument is an object representing the
settings that are 'in effect' for this call.

It returns a name-settings pair for each driver that's
testable with the current settings (e.g. DBI_PUREPERL).


sub dbd_settings_provider {
    my ($settings_context) = @_;

    # this would dispatch to plug-ins based on the value of
    # $settings_context->env('DBI_DRIVER')

    return %settings;
}

This dbd_settings_provider is called once for each of the
name-settings pairs returned by driver_settings_provider
for each of name-settings pairs returned by dbi_settings_provider.

For example, the plug-in for the DBM driver would return settings for
combinations of dbm_mldbm and dbm_type. The plug-in for the CSV driver
would return settings for csv_class etc. Both would have variants for
SQL::Nano vs SQL::Statement.


write_variants(
    providers => [  # the order is important
        \&dbi_settings_provider,
        \&driver_settings_provider,
        \&dbd_settings_provider,
    ],
    test_cases => { # details not important here
        # 
        foo => TC->new(...),
        bar => TC->new(...),
        ...
    },
);

When write_variants is called, as shown above, it would call 
dbi_settings_provider,
for each setting returned by that sub it would call driver_settings_provider,
for each setting returned by that sub it would call dbd_settings_provider
for each setting returned by that sub it would write the test_cases
using the current settings from each provider being iterated over.

As each test case it written it would include the setup and teardown
code for each of the current settings. Each test case could be an
instance of a trivial object with a simple API so people could use their
favorite template system if they want.

When write_variants returns we'd have a directory tree like this:

    t/
      plain/
        csv/
          sql_nano/
            foo.t
            bar.t
          sql_statement/
            foo.t
            bar.t
        SQLite/
          foo.t
          bar.t
      gofer/
        csv/
          sql_nano/
            ...
          sql_statement/
            ...
        SQLite/
          ...
      pureperl_gofer/
        .../

etc.

Clearly the example above is rough but I hope it's enough to give a good
sense of the approach I'm suggesting.

I think this gives us good separation of concerns. There's a clear
division of responsibility between WriteVariants on one hand, plug-in
modules providing variants on the other, and DBIT::Provider glueing them
together in the middle. All with clear responsibilities and interfaces.

WriteVariants (or whatever it gets called) is clearly not dependant on DBI
and I can't see any reason DBIT::Provider (or whatever it gets called)
should be either.

Any thouhts?

Tim.

Reply via email to