# vim: ts=2 sw=2 expandtab use strict; use lib qw(./mylib ../mylib); use Test::More tests => 38; sub POE::Kernel::ASSERT_DEFAULT () { 1 } BEGIN { package POE::Kernel; use constant TRACE_DEFAULT => exists($INC{'Devel/Cover.pm'}); } BEGIN { use_ok("POE") } sub BOGUS_SESSION () { 31415 } my $baseline_event = 0; my $baseline_refcount = 0; # This subsystem is still very closely tied to POE::Kernel, so we # can't call initialize ourselves. TODO Separate it, if possible, # enough to make this feasible. { # Create a new event, and verify that it's good. my $event_id = $poe_kernel->_data_ev_enqueue( $poe_kernel, # session $poe_kernel, # source_session "event", # event POE::Kernel::ET_ALARM, # event type [], # etc __FILE__, # file __LINE__, # line "called_from",# caller state 0, # time (beginning thereof) ); # Event 1 is the kernel's performance poll timer. is( $event_id, $baseline_event + 1, "first user created event has correct ID" ); # Kernel should therefore have one events due. # A nonexistent session should have zero. is( $poe_kernel->_data_ev_get_count_from($poe_kernel->ID), $baseline_event, "POE::Kernel has enqueued correct number of events" ); is( $poe_kernel->_data_ev_get_count_to($poe_kernel->ID), $baseline_event + 1, "POE::Kernel has three events enqueued for it" ); is( $poe_kernel->_data_ev_get_count_from("nothing"), 0, "unknown session has enqueued no events" ); is( $poe_kernel->_data_ev_get_count_to("nothing"), 0, "unknown session has no events enqueued for it" ); # Performance timer only counts once now. is( $poe_kernel->_data_ses_refcount($poe_kernel->ID), $baseline_refcount + 1, "POE::Kernel's timer count is correct" ); } { # Dispatch due events, and stuff. $poe_kernel->_data_ev_dispatch_due(); check_references( $poe_kernel, 0, 0, 0, "after due events are dispatched" ); } # Test timer maintenance functions. Add some alarms: Three with # identical names, and one with another name. Remember the ID of one # of them, so we can remove it explicitly. The other three should # remain. Remove them by name, and both the remaining ones with the # same name should disappear. The final alarm will be removed by # clearing alarms for the session. my @ids; for (1..4) { my $timer_name = "timer"; $timer_name = "other-timer" if $_ == 4; push( @ids, $poe_kernel->_data_ev_enqueue( $poe_kernel, # session $poe_kernel, # source_session $timer_name, # event POE::Kernel::ET_ALARM, # event type [], # etc __FILE__, # file __LINE__, # line undef, # called from state $_, # time ) ); } # The from and to counts should add up to the reference count. check_references( $poe_kernel, 0, 0, 4, "after some timers are enqueued" ); { # Remove one of the alarms by its ID. my ($time, $event) = $poe_kernel->_data_ev_clear_alarm_by_id( $poe_kernel->ID(), $ids[1] ); is($time, 2, "removed event has the expected due time"); is( $event->[POE::Kernel::EV_NAME], "timer", "removed event has the expected name" ); check_references( $poe_kernel, 0, 0, 3, "after a single named event is removed" ); } { # Try to remove a nonexistent alarm by the ID it would have if it # did exist, except it doesn't. my ($time, $event) = $poe_kernel->_data_ev_clear_alarm_by_id( $poe_kernel->ID(), 8675309 ); ok(!defined($time), "can't clear bogus alarm by nonexistent ID"); check_references( $poe_kernel, 0, 0, 3, "after trying to clear a bogus alarm" ); } # Remove an alarm by name, except that this is for a nonexistent # session. $poe_kernel->_data_ev_clear_alarm_by_name(BOGUS_SESSION, "timer"); check_references( $poe_kernel, 0, 0, 3, "after removing timers from a bogus session" ); is( $poe_kernel->_data_ev_get_count_from(BOGUS_SESSION), 0, "bogus session has created no events" ); is( $poe_kernel->_data_ev_get_count_to(BOGUS_SESSION), 0, "bogus session has no events enqueued for it" ); # Remove the alarm by name, for real. We should be down to one timer # (the original poll thing). $poe_kernel->_data_ev_clear_alarm_by_name($poe_kernel->ID(), "timer"); check_references( $poe_kernel, 0, 0, 1, "after removing 'timer' by name" ); { # Try to remove timers from some other (nonexistent should be ok) # session. my @removed = $poe_kernel->_data_ev_clear_alarm_by_session(8675309); is(@removed, 0, "didn't remove alarm from nonexistent session"); } { # Remove the last of the timers. The Kernel session is the only # reference left for it. my @removed = $poe_kernel->_data_ev_clear_alarm_by_session($poe_kernel->ID()); is(@removed, 1, "removed the last alarm successfully"); # Verify that the removed timer is the correct one. We still have # the signal polling timer around there somewhere. my ($removed_name, $removed_time, $removed_args) = @{$removed[0]}; is($removed_name, "other-timer", "last alarm had the corrent name"); is($removed_time, 4, "last alarm had the corrent due time"); check_references( $poe_kernel, 0, 0, 0, "after clearing all alarms for a session" ); } # Remove all events for the kernel session. Now it should be able to # finalize cleanly. $poe_kernel->_data_ev_clear_session($poe_kernel); { # Catch a trap when enqueuing an event for a nonexistent session. eval { $poe_kernel->_data_ev_enqueue( "moo", # dest session "moo", # source session "event", # event name POE::Kernel::ET_ALARM, # event type [], # etc __FILE__, # file __LINE__, # line undef, # called from state 1, # due time ); }; ok( $@ && $@ =~ /Can't locate object method "ID"/, "trap while enqueuing event for non-existent session" ); } { # Exercise _data_ev_clear_session when events are sent from one # session to another. my $session = POE::Session->create( inline_states => { _start => sub { }, _stop => sub { }, } ); $poe_kernel->_data_ev_enqueue( $session, # dest session $poe_kernel, # source session "event-1", # event name POE::Kernel::ET_POST, # event type [], # etc __FILE__, # file __LINE__, # line undef, # called from state 1, # due time ); $poe_kernel->_data_ev_enqueue( $poe_kernel, # dest session $session, # source session "event-2", # event name POE::Kernel::ET_POST, # event type [], # etc __FILE__, # file __LINE__, # line undef, # called from state 2, # due time ); check_references( $poe_kernel, 1, 1, 1, "after creating inter-session messages" ); $poe_kernel->_data_ev_clear_session($session->ID()); check_references( $poe_kernel, 1, 0, 0, "after clearing inter-session messages" ); $poe_kernel->_data_ev_clear_session($poe_kernel->ID()); check_references( $poe_kernel, 1, 0, 0, "after clearing kernel messages" ); } # A final test. ok( $poe_kernel->_data_ev_finalize(), "POE::Resource::Events finalized cleanly" ); # END OF EXECUTION HERE, BUT I CAN'T USE EXIT # Every time we cross-check a session for events and reference counts, # there should be twice as many references as events. This is because # each event counts twice: once because the session sent the event, # and again because the event was due for the session. Check that the # from- and to counts add up to the reference count, and that they are # equal. # # The "base" references are ones from sources other than events. In # later tests, they're from the addition of another session. sub check_references { my ($session, $base_ref, $expected_from, $expected_to, $when) = @_; my $from_count = $poe_kernel->_data_ev_get_count_from($session->ID); my $to_count = $poe_kernel->_data_ev_get_count_to($session->ID); # Reference count stopped being simply the from + to + base counts. #my $ref_count = $poe_kernel->_data_ses_refcount($session->ID); #my $check_sum = $from_count + $to_count + $base_ref; #is($check_sum, $ref_count, "refcnts $ref_count == $check_sum $when"); is( $from_count, $expected_from, "from evcount $from_count == $expected_from $when" ); is( $to_count, $expected_to, "to evcount $to_count == $expected_to $when" ); } # We created a session, so run it. POE::Kernel->run(); 1;