+ my($tname) = @_;
+
+ $tname = "Main" if !defined $tname;
+ $this->{tree} = $this->_gather_tests($tname)
+ or die "No tests defined";
+ #$this->{tree}->print(0);
+
+ my @conn = @{ $this->{connections} };
+ foreach my $conn (@conn) {
+ $this->_start_test($conn, "");
+ }
+
+ while ((my $i0 = ZOOM::event(\@conn)) != 0) {
+ my $conn = $conn[$i0-1];
+ my $target = $conn->option("host");
+ my $ev = $conn->last_event();
+ my $evstr = ZOOM::event_str($ev);
+ $this->log("irspy_event", "$target event $ev ($evstr)");
+
+ my $task = $conn->current_task();
+ my $res;
+ eval {
+ $conn->_check();
+ }; if ($@) {
+ # This is a nasty hack. 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. Let's hope
+ # this doesn't come back and bite us.
+ if ($ev == ZOOM::Event::RECV_DATA ||
+ $ev == ZOOM::Event::RECV_APDU ||
+ $ev == ZOOM::Event::ZEND) {
+ $this->log("irspy_event", "$target ignoring error ",
+ "on event $ev ($evstr): $@");
+ } else {
+ my $sub = $task->{cb}->{exception};
+ die $@ if !defined $sub;
+ $res = &$sub($conn, $task, $@);
+ goto HANDLE_RESULT;
+ }
+ }
+
+ my $sub = $task ? $task->{cb}->{$ev} : undef;
+ if (!defined $sub) {
+ $conn->log("irspy_unhandled", "event $ev ($evstr)");
+ # Catch the case of a pure-container test ending
+ if ($ev == ZOOM::Event::ZEND && !$conn->current_task()) {
+ $conn->log("irspy", "last event, no task queued");
+ goto NEXT_TEST;
+ }
+ next;
+ }