Subject: Re: bug#67862: 30.0.50; Handler-bind and ert-test-error-debug

From: Stefan Monnier <[email-masked]>
Date: Thu, 01 Feb 2024 21:46:32 -0500

Hi J.P.,

>   (ert-deftest my-baseline ()
>     (error "Done wrong"))
>
>   (ert-deftest my-filter ()
>     (let ((proc
>            (start-process "my-filter" (current-buffer) "sh" "-c"
>                           "for i in $(seq 99); do echo $i; sleep 0.01; done")))
>       (set-process-filter proc
>                           (lambda (_ string)
>                             (when (string-search "42" string)
>                               (error "Encountered bad value"))))
>       (with-timeout (5 (ert-fail "Timed out"))
>         (while (process-live-p proc)
>           (accept-process-output nil 0.01)))))
>
>   (ert-deftest my-timer ()
>     (run-at-time 0.2 nil (lambda () (error "Encountered a problem")))
>     (with-timeout (5 (ert-fail "Timed out"))
>       (while (accept-process-output nil 5))))

Thanks.  Indeed, `handler-bind` behaves differently than
`debug-on-error` here.  The reason is:

- For `my-filter`, process.c:6329 it's the `!NILP (Vdebug_on_error)` test in:

    internal_condition_case_1 (read_process_output_call,
			       list3 (outstream, make_lisp_proc (p), text),
			       !NILP (Vdebug_on_error) ? Qnil : Qerror,
			       read_process_output_error_handler);

- For `my-timer`, it's the `condition-case-unless-debug` in timer.el:319:

        (condition-case-unless-debug err
            ;; Timer functions should not change the current buffer.
            ;; If they do, all kinds of nasty surprises can happen,
            ;; and it can be hellish to track down their source.
            (save-current-buffer
              (apply (timer--function timer) (timer--args timer)))
          (error (message "Error running timer%s: %S"
                          (if (symbolp (timer--function timer))
                              (format-message " `%s'" (timer--function timer))
                            "")
                          err)))

The reason to catch errors in those two cases is that these codes are
normally run asynchronously, so it makes sense to catch errors rather
than propagate them up to some unrelated ambient code being executed.

For `my-filter` I can see a good argument that errors should be
propagated when you pass `proc` explicitly to `accept-process-output`
and the filter/sentinel that signals the error is indeed this very
process: in that case, there is a very clear connection between the
filter/sentinel and the ambient code which would justify propagating
the error.

For `my-timer`, on the other hand, hmm...

        Stefan