Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 1 | package rt_test |
| 2 | |
| 3 | import ( |
| 4 | "fmt" |
| 5 | "io" |
| 6 | "os" |
| 7 | "syscall" |
| 8 | "testing" |
| 9 | "time" |
| 10 | |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 11 | "v.io/core/veyron2" |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 12 | |
Jiri Simsa | 764efb7 | 2014-12-25 20:57:03 -0800 | [diff] [blame] | 13 | "v.io/core/veyron/lib/expect" |
| 14 | "v.io/core/veyron/lib/modules" |
| 15 | "v.io/core/veyron/lib/signals" |
| 16 | "v.io/core/veyron/lib/testutil" |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 17 | ) |
| 18 | |
| 19 | var cstderr io.Writer |
| 20 | |
| 21 | func init() { |
| 22 | testutil.Init() |
| 23 | if testing.Verbose() { |
| 24 | cstderr = os.Stderr |
| 25 | } |
| 26 | } |
| 27 | |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 28 | func newShell(t *testing.T) *modules.Shell { |
| 29 | sh, err := modules.NewShell(nil) |
| 30 | if err != nil { |
| 31 | t.Fatalf("unexpected error: %s", err) |
| 32 | } |
| 33 | return sh |
| 34 | } |
| 35 | |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 36 | // TestSimpleServerSignal verifies that sending a signal to the simple server |
| 37 | // causes it to exit cleanly. |
| 38 | func TestSimpleServerSignal(t *testing.T) { |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 39 | sh := newShell(t) |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 40 | defer sh.Cleanup(os.Stdout, cstderr) |
| 41 | h, _ := sh.Start("simpleServerProgram", nil) |
| 42 | s := expect.NewSession(t, h.Stdout(), time.Minute) |
| 43 | s.Expect("Ready") |
| 44 | syscall.Kill(h.Pid(), syscall.SIGINT) |
| 45 | s.Expect("Received signal interrupt") |
| 46 | s.Expect("Interruptible cleanup") |
| 47 | s.Expect("Deferred cleanup") |
| 48 | fmt.Fprintln(h.Stdin(), "close") |
| 49 | s.ExpectEOF() |
| 50 | } |
| 51 | |
| 52 | // TestSimpleServerLocalStop verifies that sending a local stop command to the |
| 53 | // simple server causes it to exit cleanly. |
| 54 | func TestSimpleServerLocalStop(t *testing.T) { |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 55 | sh := newShell(t) |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 56 | defer sh.Cleanup(os.Stdout, cstderr) |
| 57 | h, _ := sh.Start("simpleServerProgram", nil) |
| 58 | s := expect.NewSession(t, h.Stdout(), time.Minute) |
| 59 | s.Expect("Ready") |
| 60 | fmt.Fprintln(h.Stdin(), "stop") |
| 61 | s.Expect(fmt.Sprintf("Received signal %s", veyron2.LocalStop)) |
| 62 | s.Expect("Interruptible cleanup") |
| 63 | s.Expect("Deferred cleanup") |
| 64 | fmt.Fprintln(h.Stdin(), "close") |
| 65 | s.ExpectEOF() |
| 66 | } |
| 67 | |
| 68 | // TestSimpleServerDoubleSignal verifies that sending a succession of two |
| 69 | // signals to the simple server causes it to initiate the cleanup sequence on |
| 70 | // the first signal and then exit immediately on the second signal. |
| 71 | func TestSimpleServerDoubleSignal(t *testing.T) { |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 72 | sh := newShell(t) |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 73 | defer sh.Cleanup(os.Stdout, cstderr) |
| 74 | h, _ := sh.Start("simpleServerProgram", nil) |
| 75 | s := expect.NewSession(t, h.Stdout(), time.Minute) |
| 76 | s.Expect("Ready") |
| 77 | syscall.Kill(h.Pid(), syscall.SIGINT) |
| 78 | s.Expect("Received signal interrupt") |
| 79 | syscall.Kill(h.Pid(), syscall.SIGINT) |
| 80 | err := h.Shutdown(os.Stdout, cstderr) |
| 81 | if err == nil { |
| 82 | t.Fatalf("expected an error") |
| 83 | } |
| 84 | if got, want := err.Error(), fmt.Sprintf("exit status %d", signals.DoubleStopExitCode); got != want { |
| 85 | t.Errorf("got %q, want %q", got, want) |
| 86 | } |
| 87 | } |
| 88 | |
| 89 | // TestSimpleServerLocalForceStop verifies that sending a local ForceStop |
| 90 | // command to the simple server causes it to exit immediately. |
| 91 | func TestSimpleServerLocalForceStop(t *testing.T) { |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 92 | sh := newShell(t) |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 93 | defer sh.Cleanup(os.Stdout, cstderr) |
| 94 | h, _ := sh.Start("simpleServerProgram", nil) |
| 95 | s := expect.NewSession(t, h.Stdout(), time.Minute) |
| 96 | s.Expect("Ready") |
| 97 | fmt.Fprintln(h.Stdin(), "forcestop") |
| 98 | s.Expect("straight exit") |
| 99 | err := h.Shutdown(os.Stdout, cstderr) |
| 100 | if err == nil { |
| 101 | t.Fatalf("expected an error") |
| 102 | } |
| 103 | if got, want := err.Error(), fmt.Sprintf("exit status %d", veyron2.ForceStopExitCode); got != want { |
| 104 | t.Errorf("got %q, want %q", got, want) |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | // TestSimpleServerKill demonstrates that a SIGKILL still forces the server |
| 109 | // to exit regardless of our signal handling. |
| 110 | func TestSimpleServerKill(t *testing.T) { |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 111 | sh := newShell(t) |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 112 | defer sh.Cleanup(os.Stdout, cstderr) |
| 113 | h, _ := sh.Start("simpleServerProgram", nil) |
| 114 | s := expect.NewSession(t, h.Stdout(), time.Minute) |
| 115 | s.Expect("Ready") |
| 116 | syscall.Kill(h.Pid(), syscall.SIGKILL) |
| 117 | err := h.Shutdown(os.Stdout, cstderr) |
| 118 | if err == nil { |
| 119 | t.Fatalf("expected an error") |
| 120 | } |
| 121 | if got, want := err.Error(), "signal: killed"; got != want { |
| 122 | t.Errorf("got %q, want %q", got, want) |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | // TestComplexServerSignal verifies that sending a signal to the complex server |
| 127 | // initiates the cleanup sequence in that server (we observe the printouts |
| 128 | // corresponding to all the simulated sequential/parallel and |
| 129 | // blocking/interruptible shutdown steps), and then exits cleanly. |
| 130 | func TestComplexServerSignal(t *testing.T) { |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 131 | sh := newShell(t) |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 132 | defer sh.Cleanup(os.Stdout, cstderr) |
| 133 | h, _ := sh.Start("complexServerProgram", nil) |
| 134 | s := expect.NewSession(t, h.Stdout(), time.Minute) |
| 135 | s.Expect("Ready") |
| 136 | syscall.Kill(h.Pid(), syscall.SIGINT) |
| 137 | s.Expect("Received signal interrupt") |
| 138 | s.ExpectSetRE("Sequential blocking cleanup", |
| 139 | "Sequential interruptible cleanup", |
| 140 | "Parallel blocking cleanup1", |
| 141 | "Parallel blocking cleanup2", |
| 142 | "Parallel interruptible cleanup1", |
| 143 | "Parallel interruptible cleanup2") |
| 144 | fmt.Fprintln(h.Stdin(), "close") |
| 145 | s.ExpectEOF() |
| 146 | } |
| 147 | |
| 148 | // TestComplexServerLocalStop verifies that sending a local stop command to the |
| 149 | // complex server initiates the cleanup sequence in that server (we observe the |
| 150 | // printouts corresponding to all the simulated sequential/parallel and |
| 151 | // blocking/interruptible shutdown steps), and then exits cleanly. |
| 152 | func TestComplexServerLocalStop(t *testing.T) { |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 153 | sh := newShell(t) |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 154 | defer sh.Cleanup(os.Stdout, cstderr) |
| 155 | h, _ := sh.Start("complexServerProgram", nil) |
| 156 | s := expect.NewSession(t, h.Stdout(), time.Minute) |
| 157 | s.Expect("Ready") |
| 158 | |
| 159 | fmt.Fprintln(h.Stdin(), "stop") |
| 160 | s.Expect(fmt.Sprintf("Stop %s", veyron2.LocalStop)) |
| 161 | s.ExpectSetRE( |
| 162 | "Sequential blocking cleanup", |
| 163 | "Sequential interruptible cleanup", |
| 164 | "Parallel blocking cleanup1", |
| 165 | "Parallel blocking cleanup2", |
| 166 | "Parallel interruptible cleanup1", |
| 167 | "Parallel interruptible cleanup2", |
| 168 | ) |
| 169 | fmt.Fprintln(h.Stdin(), "close") |
| 170 | s.ExpectEOF() |
| 171 | } |
| 172 | |
| 173 | // TestComplexServerDoubleSignal verifies that sending a succession of two |
| 174 | // signals to the complex server has the expected effect: the first signal |
| 175 | // initiates the cleanup steps and the second signal kills the process, but only |
| 176 | // after the blocking shutdown steps were allowed to complete (as observed by |
| 177 | // the corresponding printouts from the server). Note that we have no |
| 178 | // expectations on whether or not the interruptible shutdown steps execute. |
| 179 | func TestComplexServerDoubleSignal(t *testing.T) { |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 180 | sh := newShell(t) |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 181 | defer sh.Cleanup(os.Stdout, cstderr) |
| 182 | h, _ := sh.Start("complexServerProgram", nil) |
| 183 | s := expect.NewSession(t, h.Stdout(), time.Minute) |
| 184 | s.Expect("Ready") |
| 185 | syscall.Kill(h.Pid(), syscall.SIGINT) |
| 186 | s.Expect("Received signal interrupt") |
| 187 | syscall.Kill(h.Pid(), syscall.SIGINT) |
| 188 | s.ExpectSetEventuallyRE( |
| 189 | "Sequential blocking cleanup", |
| 190 | "Parallel blocking cleanup1", |
| 191 | "Parallel blocking cleanup2") |
| 192 | err := h.Shutdown(os.Stdout, cstderr) |
| 193 | if err == nil { |
| 194 | t.Fatalf("expected an error") |
| 195 | } |
| 196 | if got, want := err.Error(), fmt.Sprintf("exit status %d", signals.DoubleStopExitCode); got != want { |
| 197 | t.Errorf("got %q, want %q", got, want) |
| 198 | } |
| 199 | } |
| 200 | |
| 201 | // TestComplexServerLocalForceStop verifies that sending a local ForceStop |
| 202 | // command to the complex server forces it to exit immediately. |
| 203 | func TestComplexServerLocalForceStop(t *testing.T) { |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 204 | sh := newShell(t) |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 205 | defer sh.Cleanup(os.Stdout, cstderr) |
| 206 | h, _ := sh.Start("complexServerProgram", nil) |
| 207 | s := expect.NewSession(t, h.Stdout(), time.Minute) |
| 208 | s.Expect("Ready") |
| 209 | fmt.Fprintln(h.Stdin(), "forcestop") |
| 210 | s.Expect("straight exit") |
| 211 | err := h.Shutdown(os.Stdout, cstderr) |
| 212 | if err == nil { |
| 213 | t.Fatalf("expected an error") |
| 214 | } |
| 215 | if got, want := err.Error(), fmt.Sprintf("exit status %d", veyron2.ForceStopExitCode); got != want { |
| 216 | t.Errorf("got %q, want %q", got, want) |
| 217 | } |
| 218 | } |
| 219 | |
| 220 | // TestComplexServerKill demonstrates that a SIGKILL still forces the server to |
| 221 | // exit regardless of our signal handling. |
| 222 | func TestComplexServerKill(t *testing.T) { |
Cosmos Nicolaou | 344cc4a | 2014-11-26 15:38:43 -0800 | [diff] [blame] | 223 | sh := newShell(t) |
Cosmos Nicolaou | a429eff | 2014-11-19 14:02:34 -0800 | [diff] [blame] | 224 | defer sh.Cleanup(os.Stdout, cstderr) |
| 225 | h, _ := sh.Start("complexServerProgram", nil) |
| 226 | s := expect.NewSession(t, h.Stdout(), time.Minute) |
| 227 | s.Expect("Ready") |
| 228 | syscall.Kill(h.Pid(), syscall.SIGKILL) |
| 229 | err := h.Shutdown(os.Stdout, cstderr) |
| 230 | if err == nil { |
| 231 | t.Fatalf("expected an error") |
| 232 | } |
| 233 | if got, want := err.Error(), "signal: killed"; got != want { |
| 234 | t.Errorf("got %q, want %q", got, want) |
| 235 | } |
| 236 | } |