1 // Copyright 2011 The Go Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style
3 // license that can be found in the LICENSE file.
16 type serverType func(*channel)
18 // dial constructs a new test server and returns a *ClientConn.
19 func dial(handler serverType, t *testing.T) *ClientConn {
20 pw := password("tiger")
21 serverConfig.PasswordCallback = func(user, pass string) bool {
22 return user == "testuser" && pass == string(pw)
24 serverConfig.PublicKeyCallback = nil
26 l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
28 t.Fatalf("unable to listen: %s", err)
32 conn, err := l.Accept()
34 t.Errorf("Unable to accept: %v", err)
38 if err := conn.Handshake(); err != nil {
39 t.Errorf("Unable to handshake: %v", err)
43 ch, err := conn.Accept()
48 t.Errorf("Unable to accept incoming channel request: %v", err)
51 if ch.ChannelType() != "session" {
52 ch.Reject(UnknownChannelType, "unknown channel type")
56 go handler(ch.(*channel))
61 config := &ClientConfig{
64 ClientAuthPassword(pw),
68 c, err := Dial("tcp", l.Addr().String(), config)
70 t.Fatalf("unable to dial remote side: %s", err)
75 // Test a simple string is returned to session.Stdout.
76 func TestSessionShell(t *testing.T) {
77 conn := dial(shellHandler, t)
79 session, err := conn.NewSession()
81 t.Fatalf("Unable to request new session: %s", err)
84 stdout := new(bytes.Buffer)
85 session.Stdout = stdout
86 if err := session.Shell(); err != nil {
87 t.Fatalf("Unable to execute command: %s", err)
89 if err := session.Wait(); err != nil {
90 t.Fatalf("Remote command did not exit cleanly: %s", err)
92 actual := stdout.String()
93 if actual != "golang" {
94 t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
98 // TODO(dfc) add support for Std{in,err}Pipe when the Server supports it.
100 // Test a simple string is returned via StdoutPipe.
101 func TestSessionStdoutPipe(t *testing.T) {
102 conn := dial(shellHandler, t)
104 session, err := conn.NewSession()
106 t.Fatalf("Unable to request new session: %s", err)
108 defer session.Close()
109 stdout, err := session.StdoutPipe()
111 t.Fatalf("Unable to request StdoutPipe(): %v", err)
114 if err := session.Shell(); err != nil {
115 t.Fatalf("Unable to execute command: %s", err)
117 done := make(chan bool, 1)
119 if _, err := io.Copy(&buf, stdout); err != nil {
120 t.Errorf("Copy of stdout failed: %v", err)
124 if err := session.Wait(); err != nil {
125 t.Fatalf("Remote command did not exit cleanly: %s", err)
128 actual := buf.String()
129 if actual != "golang" {
130 t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
134 // Test non-0 exit status is returned correctly.
135 func TestExitStatusNonZero(t *testing.T) {
136 conn := dial(exitStatusNonZeroHandler, t)
138 session, err := conn.NewSession()
140 t.Fatalf("Unable to request new session: %s", err)
142 defer session.Close()
143 if err := session.Shell(); err != nil {
144 t.Fatalf("Unable to execute command: %s", err)
148 t.Fatalf("expected command to fail but it didn't")
150 e, ok := err.(*ExitError)
152 t.Fatalf("expected *ExitError but got %T", err)
154 if e.ExitStatus() != 15 {
155 t.Fatalf("expected command to exit with 15 but got %s", e.ExitStatus())
159 // Test 0 exit status is returned correctly.
160 func TestExitStatusZero(t *testing.T) {
161 conn := dial(exitStatusZeroHandler, t)
163 session, err := conn.NewSession()
165 t.Fatalf("Unable to request new session: %s", err)
167 defer session.Close()
169 if err := session.Shell(); err != nil {
170 t.Fatalf("Unable to execute command: %s", err)
174 t.Fatalf("expected nil but got %s", err)
178 // Test exit signal and status are both returned correctly.
179 func TestExitSignalAndStatus(t *testing.T) {
180 conn := dial(exitSignalAndStatusHandler, t)
182 session, err := conn.NewSession()
184 t.Fatalf("Unable to request new session: %s", err)
186 defer session.Close()
187 if err := session.Shell(); err != nil {
188 t.Fatalf("Unable to execute command: %s", err)
192 t.Fatalf("expected command to fail but it didn't")
194 e, ok := err.(*ExitError)
196 t.Fatalf("expected *ExitError but got %T", err)
198 if e.Signal() != "TERM" || e.ExitStatus() != 15 {
199 t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus())
203 // Test exit signal and status are both returned correctly.
204 func TestKnownExitSignalOnly(t *testing.T) {
205 conn := dial(exitSignalHandler, t)
207 session, err := conn.NewSession()
209 t.Fatalf("Unable to request new session: %s", err)
211 defer session.Close()
212 if err := session.Shell(); err != nil {
213 t.Fatalf("Unable to execute command: %s", err)
217 t.Fatalf("expected command to fail but it didn't")
219 e, ok := err.(*ExitError)
221 t.Fatalf("expected *ExitError but got %T", err)
223 if e.Signal() != "TERM" || e.ExitStatus() != 143 {
224 t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus())
228 // Test exit signal and status are both returned correctly.
229 func TestUnknownExitSignal(t *testing.T) {
230 conn := dial(exitSignalUnknownHandler, t)
232 session, err := conn.NewSession()
234 t.Fatalf("Unable to request new session: %s", err)
236 defer session.Close()
237 if err := session.Shell(); err != nil {
238 t.Fatalf("Unable to execute command: %s", err)
242 t.Fatalf("expected command to fail but it didn't")
244 e, ok := err.(*ExitError)
246 t.Fatalf("expected *ExitError but got %T", err)
248 if e.Signal() != "SYS" || e.ExitStatus() != 128 {
249 t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus())
253 // Test WaitMsg is not returned if the channel closes abruptly.
254 func TestExitWithoutStatusOrSignal(t *testing.T) {
255 conn := dial(exitWithoutSignalOrStatus, t)
257 session, err := conn.NewSession()
259 t.Fatalf("Unable to request new session: %s", err)
261 defer session.Close()
262 if err := session.Shell(); err != nil {
263 t.Fatalf("Unable to execute command: %s", err)
267 t.Fatalf("expected command to fail but it didn't")
269 _, ok := err.(*ExitError)
271 // you can't actually test for errors.errorString
272 // because it's not exported.
273 t.Fatalf("expected *errorString but got %T", err)
277 type exitStatusMsg struct {
284 type exitSignalMsg struct {
294 func newServerShell(ch *channel, prompt string) *ServerTerminal {
295 term := terminal.NewTerminal(ch, prompt)
296 return &ServerTerminal{
302 func exitStatusZeroHandler(ch *channel) {
304 // this string is returned to stdout
305 shell := newServerShell(ch, "> ")
310 func exitStatusNonZeroHandler(ch *channel) {
312 shell := newServerShell(ch, "> ")
317 func exitSignalAndStatusHandler(ch *channel) {
319 shell := newServerShell(ch, "> ")
322 sendSignal("TERM", ch)
325 func exitSignalHandler(ch *channel) {
327 shell := newServerShell(ch, "> ")
329 sendSignal("TERM", ch)
332 func exitSignalUnknownHandler(ch *channel) {
334 shell := newServerShell(ch, "> ")
336 sendSignal("SYS", ch)
339 func exitWithoutSignalOrStatus(ch *channel) {
341 shell := newServerShell(ch, "> ")
345 func shellHandler(ch *channel) {
347 // this string is returned to stdout
348 shell := newServerShell(ch, "golang")
353 func sendStatus(status uint32, ch *channel) {
354 msg := exitStatusMsg{
356 Request: "exit-status",
360 ch.serverConn.writePacket(marshal(msgChannelRequest, msg))
363 func sendSignal(signal string, ch *channel) {
364 sig := exitSignalMsg{
366 Request: "exit-signal",
370 Errmsg: "Process terminated",
373 ch.serverConn.writePacket(marshal(msgChannelRequest, sig))