| A signal is not a failure abb8e36 nandi 19h ago | 1 | ## Telling a signal apart from a failure. |
| 2 | ## |
| 3 | ## Nothing in this suite raises a real EINTR — a test binary has no signals |
| 4 | ## worth the name, which is exactly why the app hit this and the tests never |
| 5 | ## did. So the errors here are built by hand, and what is checked is the |
| 6 | ## judgement: which ones are retried, which are not, and that a retry gives up |
| 7 | ## rather than spinning. |
| 8 | |
| 9 | import std/[os, unittest] |
| 10 | import std/posix as p |
| 11 | import frq/eintr |
| 12 | |
| 13 | proc osError(code: cint, msg: string): ref OSError = |
| 14 | result = newException(OSError, msg) |
| 15 | result.errorCode = code.int32 |
| 16 | |
| 17 | suite "interrupted": |
| 18 | test "an OSError carrying EINTR is only a signal": |
| 19 | check interrupted(osError(p.EINTR, "Interrupted system call")) |
| 20 | test "any other OSError is a real failure": |
| 21 | check not interrupted(osError(p.ECONNRESET, "Connection reset by peer")) |
| 22 | check not interrupted(osError(p.EPIPE, "Broken pipe")) |
| 23 | test "and so is an error that is not an OSError at all": |
| 24 | check not interrupted(newException(ValueError, "nonsense")) |
| 25 | test "but the words count where the code was lost": |
| 26 | # `httpclient` catches an OSError in places and re-raises its text, which |
| 27 | # keeps the message and drops the code. |
| 28 | check interrupted(newException(IOError, "Interrupted system call")) |
| 29 | |
| 30 | suite "retrying": |
| 31 | test "a body that works runs once": |
| 32 | var runs = 0 |
| 33 | retrying 3: |
| 34 | runs.inc |
| 35 | check runs == 1 |
| 36 | |
| 37 | test "one cut short by a signal is run again": |
| 38 | var runs = 0 |
| 39 | retrying 3: |
| 40 | runs.inc |
| 41 | if runs < 3: raise osError(p.EINTR, "Interrupted system call") |
| 42 | check runs == 3 |
| 43 | |
| 44 | test "a real failure is raised at once, not retried": |
| 45 | var runs = 0 |
| 46 | expect OSError: |
| 47 | retrying 5: |
| 48 | runs.inc |
| 49 | raise osError(p.ECONNREFUSED, "Connection refused") |
| 50 | check runs == 1 |
| 51 | |
| 52 | test "and a signal that never stops gives up rather than spinning": |
| 53 | var runs = 0 |
| 54 | expect OSError: |
| 55 | retrying 4: |
| 56 | runs.inc |
| 57 | raise osError(p.EINTR, "Interrupted system call") |
| 58 | check runs == 4 |