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