#!/usr/bin/perl -w # test out signal emission hooks. use strict; use warnings; use Test::More tests => 77; use Glib ':constants'; Glib::Type->register_object ( 'Glib::Object', 'Foo', signals => { wave => {}, nod => { param_types => [qw(Glib::String Glib::Int)], }, wink => { flags => ['run-last', 'detailed'], param_types => [qw(Glib::Boolean)], return_type => 'Glib::Boolean', }, gesture => { flags => ['no-hooks'], }, }, ); Glib::Type->register_object ('Foo', 'Bar'); my $foo = Glib::Object::new ('Foo'); isa_ok ($foo, 'Foo'); isa_ok ($foo, 'Glib::Object'); # add each emission hook a different way my $wave_hook = $foo->signal_add_emission_hook (wave => \&generic_hook_data, {foo=>'bar'}); ok ($wave_hook, 'added hook for wave'); my $wink_hook = Foo->signal_add_emission_hook (wink => \&generic_hook_no_data); ok ($wave_hook, 'added hook for wink'); my $nod_hook = Glib::Object::signal_add_emission_hook ('Foo', 'nod', 'generic_hook_no_data'); ok ($wave_hook, 'added hook for nod'); { # This shouldn't work, as the signal is flagged as no-hooks. # It appears to generate a warning from GLib through g_log; let's # trap that. local $SIG{__WARN__} = sub { ok(1, "got warning text $_[0]"); }; my $gesture_hook = Glib::Object::signal_add_emission_hook ('Foo', 'gesture', 'generic_hook_data', {foo=>'bar'}); ok (!$gesture_hook, 'can\'t add a hook for gesture'); } # connect with detail; notify is the obvious choice, but it is defined # as no-hooks, so that won't work. let's just make something up. my $detailed_hook = Foo->signal_add_emission_hook ('wink::sly', \&generic_hook_no_data); ok ($detailed_hook, 'added hook for wink::sly'); # we can connect a hook to an inherited signal. the hook will be invoked # for emission of the signal from *any* class. my $bar_wave_hook = Bar->signal_add_emission_hook (wave => \&generic_hook_no_data); ok ($bar_wave_hook); $foo->signal_connect ("wink" => sub { ok (1, "plain old wink")}); $foo->signal_connect ("wink::sly" => sub { ok (1, "wink::sly")}); # emit some signals... # these variables communicate with generic_hook_no_data(). my $emission_count = 0; my %emissions = (); my $detail = undef; # there are two hooks connected to this one. print "\nemitting wave\n"; $foo->signal_emit ('wave'); is ($emissions{wave}, 2); print "\nemitting nod\n"; $foo->signal_emit ('nod', "Whee!", 42); print "\nemitting wink\n"; my $ret = $foo->signal_emit ('wink', TRUE); $detail = 'sly'; print "\nemitting wink::$detail\n"; my $n_before = $emission_count; $foo->signal_emit ("wink::$detail", FALSE); is ($emission_count - $n_before, 2, 'detailed emission results in two hooks'); print "\nemitting gesture\n"; $n_before = $emission_count; $foo->signal_emit ('gesture'); is ($emission_count, $n_before, 'no hook here'); print "\n"; is ($emission_count, 6, 'total emissions'); is ($emissions{'wave'}, 2, 'emissions for wave'); is ($emissions{'nod'}, 1, 'emissions for nod'); is ($emissions{'wink'}, 3, 'emissions for wink'); is ($emissions{'gesture'}, undef, 'emissions for gesture'); # remove all the hooks and emit again. # the emission count should not change. Foo->signal_remove_emission_hook (wave => $wave_hook); Foo->signal_remove_emission_hook (wave => $bar_wave_hook); Foo->signal_remove_emission_hook (nod => $nod_hook); Foo->signal_remove_emission_hook (wink => $wink_hook); Foo->signal_remove_emission_hook (wink => $detailed_hook); $n_before = $emission_count; $foo->signal_emit ('wave'); $foo->signal_emit ('nod', "Whee!", 42); $ret = $foo->signal_emit ('wink', TRUE); $foo->signal_emit ("wink::$detail", FALSE); $foo->signal_emit ('gesture'); is ($emission_count, $n_before, 'no hooks here'); # test a self-removing hook. Foo->signal_add_emission_hook (wave => sub { ok (1, 'got hooked'); $emission_count++; FALSE }); $n_before = $emission_count; $foo->signal_emit ('wave'); $foo->signal_emit ('wave'); is ($emission_count - $n_before, 1, 'two emissions, one hook'); sub generic_hook_no_data { my ($ihint, $param_list) = @_; print "in hook for $ihint->{signal_name} $ihint->{run_type}\n"; $emission_count++; $emissions{$ihint->{signal_name}}++; use Data::Dumper; print Dumper([$ihint, $param_list]); isa_ok ($ihint, 'HASH'); ok (exists $ihint->{signal_name}, 'ihint is valid'); is ($ihint->{detail}, $detail, 'detail'); isa_ok ($param_list, 'ARRAY'); ok (@$param_list > 0, 'at least one thing in param_list'); # GSignal doesn't care what the instance's type is, but we only # bind it to Glib::Object. isa_ok ($param_list->[0], 'Glib::Object'); my $info = $param_list->[0]->signal_query ($ihint->{signal_name}); ok (defined $info, 'found info about the signal'); is (scalar(@$param_list), 1 + scalar(@{ $info->{param_types} }), 'parameter count'); return TRUE; } sub generic_hook_data { my ($ihint, $param_list, $user_data) = @_; isa_ok ($user_data, 'HASH'); is ($user_data->{foo}, 'bar', 'user data is valid'); # verify the invocation hint. my $other_hint = $param_list->[0]->signal_get_invocation_hint(); is_deeply ($ihint, $other_hint); return generic_hook_no_data ($ihint, $param_list); } # vim: set et ts=4 sw=4 sts=4 syntax=perl :