gtk2-perl CVS 20021017



Hi again,

Some more thoughts:

1) If there are any functions eg in src/Gtk2.c update_ui that could be
recursive perhaps the names should be made different by prefixing
them with an '_' underscore (rather than what I did by adding a suffix
of '1') so that Gtk2.pm has sub update_ui that calls _update_ui in the
src.Gtk2.c file.

2) The old perl way of connecting signals allowed for multiple items
of data to be passed as args after the callback arg. I don't know how
it works but the effect is that the args to the callback can be got by:
  my ($widget, @data) = @_;

If the callback is for an event, the event structurea is added at the
end so a signal handler could get the args eg:
  my ($class, $data1, $data2, $data3, $event) = @_;

depending on how the signal was connected. I am not sure what the best
answer is - I remember some threads on the gtk-perl list ages ago.

I guess that it might be simplest to insist that all data is passed in
an anonymous array/hash so that only a ref is passed - which is what
happens already I guess :)

3) I have written (and attached) a Gtk2::Test module and a script that
will generate test scripts for each widget mentioned in the gtk2 docs. 
These scripts can be extended as methods are added. I guess that if you
want to use them it would be best to add them to the t/ directory after 
the widgets are added. 

I have also attached two minimal versions that I have edited to run. You 
will see that a script can easily test several widgets and it might be 
better to concatenate the tests when the widgets become stable.

I guess that the test scripts could be written (if missing) by the
compile-widgets script so that they magically appear when the widgets 
are added to the Gtk2/ directory but they would lose the sequence
prefix that ensures that they are run in a sensible order.

Regards, Dermot
#!/usr/bin/perl
#==============================================================================
#=== This is common code for test scripts
#==============================================================================
require 5.000; use strict 'vars', 'refs', 'subs';

BEGIN {
    use Test;
    use Data::Dumper;
    use UNIVERSAL qw(can isa);
    use POSIX qw(isdigit);
    use Exporter qw ();

=pod

=head1 NAME

Gtk2::Test - Utility methods for testing Gtk2-Perl

=head1 SYNOPSIS

 BEGIN {
    use Gtk2::Test;
    @test_plan = (
        [$USE, 'Gtk2'],
            ["init()"],
            ["timeout_add(2000, 'handler', 'data')"],
            ["timeout_remove(\$ret)", '== 1'],
            ["update_ui"],
    );
    plan tests => scalar @test_plan;
 }

 $DEBUG = $ARGV[0] || 0;

 &do_tests;

=head1 DESCRIPTION

Gtk2::Test provides some utility methods that are used to test Gtk2-Perl 
directly or during 'make test'. There are several exported variables that
can be used to work with - such as the current class, value returned, 
error string and current object. 
The methods carry out the tests and report all actions requested.

The methods can be called directly or indirectly but the simplest way is
to specify a test_plan and run it automatically.

=head1 C<@test_plan>

This is the real user interface. All the tests are defined in an array that
is exported from Gtk2::Test. 

The array holds all the tests in the order that they should be run.

Each test is defined as an anonymous array of up to 4 elements and
there are several possible test specifications:

=over 4

=item C<[$USE, 'Classname']>

This will try to 'use Classname;' and report any failures.

BYPRODUCT - C<$class> is set to 'Classname'

=item C<['new()']>

This will construct an object $o with the args supplied (none in this case)
The actual call will be C<$o = new Classname()>

BYPRODUCT - C<$o> is set to the object returned from the method call

=item C<['method()']>

This will call this method with the args supplied (none in this case)
The actual call will be C<$o->init()>

BYPRODUCT - C<$ret> is set to the value returned from the method call

=item C<["method('arg1', 'arg2')", '== 1']>

This will call this method with the args supplied and check that the
returned value (stored in C<$ret>) is numerically equal to 1

BYPRODUCT - C<$ret> is set to the value returned from the method call

=item C<[$GET_SET, 'method', "'Expected'", "'New value'"]>

This will call C<get_method()> and check that it returns 'Expected'

Then it calls C<set_method('New value')>

Finally calls C<get_method()> again and checks that it returns 'New value'

The comparison depends on the first expected value - if it is digits the
checks are C<($ret == $expected)> otherwise C<($ret eq $expected)>

=back

=head2 Checks

The returned value (stored in C<$ret>) is compared as specified in the
second arg. Any code supplied is C<eval()>ed and failures passed back.

=head1 VARIABLES

=over 4

=item C<$o>

This is the widget instance that was constructed and is used for all
subsequent tests.

=item C<$save>

This where you can save widget instances for use later - ie $o->add($save);

=item C<$err>

The error string returned by the latest test.

=item C<$ret>

The value that was returned by the latest method call

=item C<$DEBUG>

Setting $DEBUG to a number > 0 will cause debugging messages to be printed

 Level  Output
 1      Method calls that will be tested
 2      Failed check on expected value
 3      new() calls
 4      All checks made on expected value
 5      A Data::Dumper print of $o on failure

=back

=cut

=head1 CONSTANTS

=over 4

=item C<$USE>   

This is a request to use() a class

=item C<$GET_SET>

This is a request to get/set/get a widget property

=item Values to use in C<@test_plan>

These values can be used in the testplan to avoid escaping strings and to
make sure that the values get through to the test evals

 C<$NULL>  Null string ('')
 C<$ZERO>  Zero (0)
 C<$TRUE>
 C<$FALSE>

=item Placeholder args

 C<$NOARGS>
 C<$NOCHECK>

=item C<$FAIL>

Returned in the event of failure.

=item C<$OK>

Returned if test succeeded

=back

=cut

    use vars qw( 
        @ISA @EXPORT 

        @test_plan 

        $DEBUG 
        $USE 
        $GET_SET
        $OK 
        $FAIL 
        $NOARGS 
        $NOCHECK
        $NULL $ZERO $TRUE $FALSE

        $class 
        $err 
        $ret 
        $o $save

        );
    $DEBUG = 0;
    $FAIL    = '__FAIL';
    $OK = '__OK';
#    $NOARGS = '__NOARGS';
    $NOCHECK = undef;
    $USE = '__USE';
    $GET_SET = '__GET_SET';
    $NULL = '';
    $ZERO = "'0'";
    $TRUE = 1;
    $FALSE = $ZERO;
    @ISA = qw(Test);
    # These symbols (globals and functions) are always exported
    @EXPORT = qw(
        $DEBUG  
        $FAIL $OK $NOARGS
        $USE
        $class $err $o $save $ret @test_plan
    );
    print "This might take a while if widgets have not yet been compiled\n";
}

=head1 TESTING METHODS

The class methods provide ways to test methods automatically or manually.

Do remember to add 1 to the C<plan tests> line in C<BEGIN>
for each manual test that you add to your test script.

=over 4

=cut
=item C<&do_tests()>

Run all the tests in the test_plan

e.g. C<&do_tests;>

=cut
sub do_tests {
    my ($t, $method, $args, $call);
    foreach $t (@test_plan) {
        $call = $t->[0];
        if (" $USE use " =~ / $call /) {
            ok(&try_use($t->[1]), $OK, $err);
        } else {
            $call =~ /\(/ or $call .= "()";
            $call =~ /\)/ or $call .= ")";
            $call =~ /(.+)\((.*)\)$/;
            $method = $1; $args = $2;

            if ($method =~ '^new') {
                ok(&try_new($method, $args), $OK, $err);
            } elsif (" $GET_SET get_set " =~ / $method /) {
                ok(&try_get_set($t->[1], $t->[2], $t->[3]), $OK, $err);
            } else {
                ok(&try_method($method, $args, $t->[1]), $OK, $err);
            }
        }
    }
}

=item C<&try_method($method, $args, $expect)>

Run the method in the current class with the supplied arg string and check
that C<$expect> is returned if specified.

e.g. C<ok(&try_method('get_border_width', '== 2'), $OK, $err);>

=cut
sub try_method {
    my ($method, $args, $expect) = @_;
    $args ||= '';
    my $res;
    $err = '';
    unless ($o->can($method)) {
        $err = "$class\->$method - no such method";
        print Dumper($o) if $DEBUG > 4;
        return $FAIL;
    }
    my $expr = "\$ret = \$o->$method($args)";
    print "$expr - " if $DEBUG > 0;
    eval $expr;
    if ($@) {
        $err = "Failed to call $class\->$method($args): ".$@;
        print Dumper($o) if $DEBUG > 4;
        return $FAIL;
    }
    if (defined $expect) {
#        print "$ret $expect"."\n";
        $expr = "\$res = '$ret' $expect";
        print "Checking if '$ret' $expect - " if $DEBUG > 3;
        eval $expr;
        unless ($res) {
            $err = "Value returned by $class\->$method($args) ".
                "was '$ret' which does NOT $expect";
            print "$err\n" if $DEBUG > 1;
            print Dumper($o) if $DEBUG > 4;
            return $FAIL;
        }
    }
    $err = '';
    return $OK;
}

=item C<&try_get_set($method, $old, $new)>

Run the C<get_method()> in the current class and check it equals C<$old>
Then run C<set_method($new)> and finally run C<get_method> again to check
that C<$new> is returned.

e.g. C<ok(&try_get_set('resizable', $TRUE, $FALSE), $OK, $err);>

=cut
sub try_get_set {
    my ($method, $old, $new) = @_;
    if (isdigit($old)) {
        (&try_method("get_$method", $NOARGS, "== $old") eq $OK) or return $FAIL;
        (&try_method("set_$method", $new) eq $OK) or return $FAIL;
        (&try_method("get_$method", $NOARGS, "== $new") eq $OK) or return $FAIL;
    } else {
        (&try_method("get_$method", $NOARGS, "eq $old") eq $OK) or return $FAIL;
        (&try_method("set_$method", $new) eq $OK) or return $FAIL;
        (&try_method("get_$method", $NOARGS, "eq $new") eq $OK) or return $FAIL;
    }
    return $OK;
}

=item C<&try_use($class)>

Tries to C<use $class;>.

e.g. C<ok(&try_use('Classname'), $OK, $err);>

=cut
sub try_use {
    $class = shift;
    $o = $class;
    my $expr = "use $class";
    print "---------------------------\n".
        "$expr - " if $DEBUG > 2;
    eval $expr;
    if ($@) {
        $err = "Could not use $class";
        print Dumper($o) if $DEBUG > 4;
        return $FAIL;
    }
    return $OK;
}

=item C<&try_new($method, $args)>

Tries to call C<$o = $class->$method($args);>.

e.g. C<ok(&try_new('new_with_label', 'Label text'), $OK, $err);>

=cut
sub try_new {
    my ($call, $args) = @_;
    my $expr = "\$o = $call ${class}($args)";
    print "$expr - " if $DEBUG > 2;
    eval $expr;
    if ($@) {
        $err = "$class\->$call($args) FAILED: ". $@;
        print Dumper($o) if $DEBUG > 4;
        return $FAIL;
    }
    print Dumper($o) if $DEBUG > 4;
    if ($o->isa($class)) {
        return $OK;
    } else {
        $err = "Widget is not a $class";
        print Dumper($o) if $DEBUG > 4;
        return $FAIL;
    }
}

=item C<&test_string($class)>

Returns a skeleton test script for specified widget

e.g. C<print(&test_string("Gtk2::Gdk::Pixmap"));>

=cut
sub test_string {
    my ($class) = @_;
return
"#!/usr/bin/perl
#==============================================================================
#=== This is a script to test $class
#==============================================================================
require 5.000; use strict 'vars', 'refs', 'subs';

BEGIN { 
    use Gtk2::Test; 
    \ test_plan = (
        [\$USE, 'Gtk2'],
            [\"init()\"],
        [\$USE, '$class'],
            [\"new()\"],
            [\"method('args')\", \"eq 'Expected'\"],
            [\$GET_SET, 'method', \"'Expected'\", \"'New value'\"],
    );
    plan tests => scalar \ test_plan;
}

\$DEBUG = \$ARGV[0] || 0;

\&do_tests;
";
}

=back

=cut

1;

Attachment: write_test_scripts.pl
Description: Perl program

Attachment: 010_Gtk2_main.t
Description: Troff document

Attachment: 041_Gtk2_Window.t
Description: Troff document



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]