+ my $i0 = ZOOM::event(\@conn);
+ $this->log("irspy_event",
+ "ZOOM_event(", scalar(@conn), " connections) = $i0");
+ last if $i0 == 0 || $i0 == -3; # no events or no connections
+ my $conn = $conn[$i0-1];
+ my $ev = $conn->last_event();
+ my $evstr = ZOOM::event_str($ev);
+ $conn->log("irspy_event", "event $ev ($evstr)");
+
+ my $task = $conn->current_task();
+ die "$conn has no current task for event $ev ($evstr)" if !$task;
+ eval { $conn->_check() };
+ if ($@ &&
+ ($ev == ZOOM::Event::RECV_DATA ||
+ $ev == ZOOM::Event::RECV_APDU ||
+ $ev == ZOOM::Event::ZEND)) {
+ # An error in, say, a search response, becomes visible to
+ # ZOOM before the Receive Data event is sent and persists
+ # until after the End, which means that successive events
+ # each report the same error. So we just ignore errors on
+ # "unimportant" events. ### But this doesn't work for,
+ # say, a Connection Refused, as the only event that shows
+ # us this error is the End.
+ $conn->log("irspy_event", "ignoring error ",
+ "on event $ev ($evstr): $@");
+ next;
+ }
+
+ my $res;
+ if ($@) {
+ my $sub = $task->{cb}->{exception};
+ die $@ if !defined $sub;
+ $res = &$sub($conn, $task, $task->udata(), $@);
+ } else {
+ my $sub = $task->{cb}->{$ev};
+ if (!defined $sub) {
+ $conn->log("irspy_unhandled", "event $ev ($evstr)");
+ next;
+ }
+
+ $res = &$sub($conn, $task, $task->udata(), $ev);
+ }
+
+ if ($res == ZOOM::IRSpy::Status::OK) {
+ # Nothing to do -- life continues
+
+ } elsif ($res == ZOOM::IRSpy::Status::TASK_DONE) {
+ my $task = $conn->current_task();
+ die "no task for TASK_DONE on $conn" if !$task;
+ die "next task already defined for $conn" if $conn->next_task();
+ $conn->log("irspy_task", "completed task $task");
+ $conn->next_task($task->{next});
+ $conn->current_task(0);
+
+ } elsif ($res == ZOOM::IRSpy::Status::TEST_GOOD ||
+ $res == ZOOM::IRSpy::Status::TEST_BAD) {
+ my $x = ($res == ZOOM::IRSpy::Status::TEST_GOOD) ? "good" : "bad";
+ $conn->log("irspy_task", "test ended during task $task ($x)");
+ $conn->log("irspy_test", "test completed ($x)");
+ $conn->current_task(0);
+ $conn->next_task(0);
+ if ($res == ZOOM::IRSpy::Status::TEST_BAD) {
+ my $address = $conn->option('current_test_address');
+ ($address, my $n) = $this->_last_sibling_test($address);
+ if (defined $address) {
+ $conn->log("irspy_test", "skipped $n tests");
+ $conn->option(current_test_address => $address);
+ $nskipped += $n;
+ }
+ }
+ } else {
+ die "unknown callback return-value '$res'";
+ }