# 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;