Skip to content

tests: Add Ctrl-C interrupt tests using extended repl_ framework#18435

Merged
dpgeorge merged 2 commits into
micropython:masterfrom
andrewleech:unix-test-ctrl-c
May 7, 2026
Merged

tests: Add Ctrl-C interrupt tests using extended repl_ framework#18435
dpgeorge merged 2 commits into
micropython:masterfrom
andrewleech:unix-test-ctrl-c

Conversation

@andrewleech
Copy link
Copy Markdown
Contributor

Summary

This PR adds Unix port test coverage for Ctrl-C (SIGINT) interrupt handling in MicroPython's REPL by extending the existing repl_ test framework to support signal generation through proper PTY configuration.

The framework now configures terminal attributes to enable signal generation (ISIG flag) and sets up the PTY as the controlling terminal for the MicroPython subprocess, allowing {\x03} sequences in test files to trigger actual SIGINT delivery rather than just sending the byte.

Two test cases cover the critical interrupt scenarios:

  • Canceling paste mode with Ctrl-C
  • Interrupting executing code (e.g., during time.sleep())

Testing

Tested on Linux (Unix port) with the new test files:

  • tests/cmdline/repl_ctrl_c_paste_cancel.py - verifies Ctrl-C exits paste mode cleanly
  • tests/cmdline/repl_ctrl_c_interrupt_execution.py - verifies Ctrl-C raises KeyboardInterrupt during execution

Both tests pass and demonstrate correct signal handling and REPL recovery.

The changes are isolated to the Unix port's test infrastructure and gracefully skip on platforms without PTY support (already handled by existing Windows/msys/cygwin detection).

Trade-offs and Alternatives

Added a try/finally block around PTY usage which increases indentation depth slightly, but this ensures proper cleanup of file descriptors even if subprocess creation fails.

The normalize_newlines() function handles the PTY double-newline quirk (\r\r\n) which may be platform-specific, but the extra replacement pass has negligible performance impact in the test suite.

@codecov
Copy link
Copy Markdown

codecov Bot commented Nov 19, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 98.47%. Comparing base (6bbd7bc) to head (30781dc).

Additional details and impacted files
@@            Coverage Diff             @@
##           master   #18435      +/-   ##
==========================================
+ Coverage   98.46%   98.47%   +0.01%     
==========================================
  Files         176      176              
  Lines       22831    22831              
==========================================
+ Hits        22480    22483       +3     
+ Misses        351      348       -3     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@dpgeorge dpgeorge added the tests Relates to tests/ directory in source label Nov 19, 2025
@andrewleech andrewleech force-pushed the unix-test-ctrl-c branch 3 times, most recently from 8c0c04a to 66d819f Compare March 30, 2026 13:28
@dpgeorge
Copy link
Copy Markdown
Member

@andrewleech please check CI here: ruff formatting, and the unix port macos seems to lockup during the tests.

@andrewleech andrewleech force-pushed the unix-test-ctrl-c branch 6 times, most recently from cfdbe88 to 001c59d Compare April 17, 2026 12:31
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 17, 2026

Code size report:

Reference:  samd: Convert port to use new event waiting functions. [1f601e8]
Comparison: tests: Add Ctrl-C interrupt test for the repl_ test framework. [merge of dcc243d]
  mpy-cross:    +0 +0.000% 
   bare-arm:    +0 +0.000% 
minimal x86:    +0 +0.000% 
   unix x64:    +0 +0.000% standard
      stm32:    +0 +0.000% PYBV10
      esp32:    +0 +0.000% ESP32_GENERIC
     mimxrt:    +0 +0.000% TEENSY40
        rp2:    +0 +0.000% RPI_PICO_W
       samd:    +0 +0.000% ADAFRUIT_ITSYBITSY_M4_EXPRESS
  qemu rv32:    +0 +0.000% VIRT_RV32

@andrewleech
Copy link
Copy Markdown
Contributor Author

Thanks @dpgeorge, both fixed now. Branch is rebased and split into two commits.

The macOS hang is the same tcdrain() issue we hit with pyserial PTYs (pyserial/pyserial#822). TCSAFLUSH in mp_hal_stdio_mode_raw/mp_hal_stdio_mode_orig calls tcdrain() which blocks on macOS until the PTY master has read all pending slave output. First commit changes both to TCSANOW, I don't think the drain is doing anything useful here but happy to discuss.

The test approach is also reworked. The controlling terminal setup (setsid/TIOCSCTTY/tcsetpgrp) is now gated behind a # sigint: directive rather than applied to all repl_ tests. The PTY needs to be the child's controlling terminal for \x03 to generate SIGINT, since pyexec_friendly_repl restores original terminal settings (ISIG on) before executing user code.

Also dropped repl_ctrl_c_paste_cancel.py (already covered by repl_paste.py) and reduced the sleep from 30s to 10s.

@andrewleech andrewleech force-pushed the unix-test-ctrl-c branch 3 times, most recently from 5db8be3 to dcc243d Compare April 19, 2026 09:10
@dpgeorge
Copy link
Copy Markdown
Member

Thanks for updating.

I've rerun the unix CI on this PR 10 times now, to test reliability. Over those 10 runs I saw only one error, on the unix qemu_arm job running the tests, cmdline/repl_emacs_keys.py failed. But I don't think that's anything to do with the changes in this PR.

On other PRs I do sometimes see failures with cmdline/repl_autocomplete_underscore.py, and maybe the changes here will resolve that.

@andrewleech
Copy link
Copy Markdown
Contributor Author

I've seen a few jobs intermittently fail still, often possibly related to the load on the ci servers at the time, eg. #19103

Though considering repl_emacs_keys.py doesn't have a timing / timeout loop that doesn't seem as likely the cause here.

pi-anl added 2 commits May 8, 2026 00:45
Change tcsetattr from TCSAFLUSH to TCSANOW in mp_hal_stdio_mode_raw
and mp_hal_stdio_mode_orig. The TCSAFLUSH flag calls tcdrain() before
applying terminal settings, which blocks indefinitely on macOS when
used with PTY devices if the master side has not read all pending
output. This is a known macOS kernel behavior (XNU ttywait loops
until the output queue is consumed by the master).

The drain serves no practical purpose on the unix port since output
is written via write() syscalls that complete before tcsetattr is
called. The input flush is also unnecessary since MicroPython's
readline handles stale input gracefully.

Two repl_ test .exp files are updated to account for the newline
after Ctrl-D (end paste mode) no longer being discarded by the input
flush; it produces an extra empty prompt line.

Signed-off-by: Andrew Leech <[email protected]>
Adds a "# sigint:" directive for repl_ tests that need Ctrl-C to
generate SIGINT via the PTY terminal driver. When present, the child
process is set up with the PTY as its controlling terminal (via
setsid/TIOCSCTTY/tcsetpgrp) so that \x03 written to the PTY master
generates SIGINT for the child's process group.

This works because MicroPython's REPL restores original terminal
settings (with ISIG enabled) before executing user code, allowing the
terminal driver to convert \x03 into SIGINT during blocking operations.

Test added:
- repl_ctrl_c_interrupt_execution.py: Verifies Ctrl-C interrupts a
  blocking time.sleep() call and the REPL remains functional afterward.

Also wraps PTY fd handling in try/finally for all repl_ tests.

Signed-off-by: Andrew Leech <[email protected]>
@dpgeorge dpgeorge force-pushed the unix-test-ctrl-c branch from dcc243d to 30781dc Compare May 7, 2026 14:46
@dpgeorge dpgeorge merged commit 30781dc into micropython:master May 7, 2026
57 of 60 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

tests Relates to tests/ directory in source

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants