diff options
Diffstat (limited to 'libgo/go/exp/ssh/session_test.go')
-rw-r--r-- | libgo/go/exp/ssh/session_test.go | 264 |
1 files changed, 240 insertions, 24 deletions
diff --git a/libgo/go/exp/ssh/session_test.go b/libgo/go/exp/ssh/session_test.go index d4818c29f70..a28ead08736 100644 --- a/libgo/go/exp/ssh/session_test.go +++ b/libgo/go/exp/ssh/session_test.go @@ -12,8 +12,10 @@ import ( "testing" ) +type serverType func(*channel) + // dial constructs a new test server and returns a *ClientConn. -func dial(t *testing.T) *ClientConn { +func dial(handler serverType, t *testing.T) *ClientConn { pw := password("tiger") serverConfig.PasswordCallback = func(user, pass string) bool { return user == "testuser" && pass == string(pw) @@ -50,27 +52,7 @@ func dial(t *testing.T) *ClientConn { continue } ch.Accept() - go func() { - defer ch.Close() - // this string is returned to stdout - shell := NewServerShell(ch, "golang") - shell.ReadLine() - type exitMsg struct { - PeersId uint32 - Request string - WantReply bool - Status uint32 - } - // TODO(dfc) converting to the concrete type should not be - // necessary to send a packet. - msg := exitMsg{ - PeersId: ch.(*channel).theirId, - Request: "exit-status", - WantReply: false, - Status: 0, - } - ch.(*channel).serverConn.writePacket(marshal(msgChannelRequest, msg)) - }() + go handler(ch.(*channel)) } t.Log("done") }() @@ -91,7 +73,7 @@ func dial(t *testing.T) *ClientConn { // Test a simple string is returned to session.Stdout. func TestSessionShell(t *testing.T) { - conn := dial(t) + conn := dial(shellHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { @@ -116,7 +98,7 @@ func TestSessionShell(t *testing.T) { // Test a simple string is returned via StdoutPipe. func TestSessionStdoutPipe(t *testing.T) { - conn := dial(t) + conn := dial(shellHandler, t) defer conn.Close() session, err := conn.NewSession() if err != nil { @@ -147,3 +129,237 @@ func TestSessionStdoutPipe(t *testing.T) { t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual) } } + +// Test non-0 exit status is returned correctly. +func TestExitStatusNonZero(t *testing.T) { + conn := dial(exitStatusNonZeroHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %s", err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + err = session.Wait() + if err == nil { + t.Fatalf("expected command to fail but it didn't") + } + e, ok := err.(*ExitError) + if !ok { + t.Fatalf("expected *ExitError but got %T", err) + } + if e.ExitStatus() != 15 { + t.Fatalf("expected command to exit with 15 but got %s", e.ExitStatus()) + } +} + +// Test 0 exit status is returned correctly. +func TestExitStatusZero(t *testing.T) { + conn := dial(exitStatusZeroHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %s", err) + } + defer session.Close() + + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + err = session.Wait() + if err != nil { + t.Fatalf("expected nil but got %s", err) + } +} + +// Test exit signal and status are both returned correctly. +func TestExitSignalAndStatus(t *testing.T) { + conn := dial(exitSignalAndStatusHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %s", err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + err = session.Wait() + if err == nil { + t.Fatalf("expected command to fail but it didn't") + } + e, ok := err.(*ExitError) + if !ok { + t.Fatalf("expected *ExitError but got %T", err) + } + if e.Signal() != "TERM" || e.ExitStatus() != 15 { + t.Fatalf("expected command to exit with signal TERM and status 15 but got signal %s and status %v", e.Signal(), e.ExitStatus()) + } +} + +// Test exit signal and status are both returned correctly. +func TestKnownExitSignalOnly(t *testing.T) { + conn := dial(exitSignalHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %s", err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + err = session.Wait() + if err == nil { + t.Fatalf("expected command to fail but it didn't") + } + e, ok := err.(*ExitError) + if !ok { + t.Fatalf("expected *ExitError but got %T", err) + } + if e.Signal() != "TERM" || e.ExitStatus() != 143 { + t.Fatalf("expected command to exit with signal TERM and status 143 but got signal %s and status %v", e.Signal(), e.ExitStatus()) + } +} + +// Test exit signal and status are both returned correctly. +func TestUnknownExitSignal(t *testing.T) { + conn := dial(exitSignalUnknownHandler, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %s", err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + err = session.Wait() + if err == nil { + t.Fatalf("expected command to fail but it didn't") + } + e, ok := err.(*ExitError) + if !ok { + t.Fatalf("expected *ExitError but got %T", err) + } + if e.Signal() != "SYS" || e.ExitStatus() != 128 { + t.Fatalf("expected command to exit with signal SYS and status 128 but got signal %s and status %v", e.Signal(), e.ExitStatus()) + } +} + +// Test WaitMsg is not returned if the channel closes abruptly. +func TestExitWithoutStatusOrSignal(t *testing.T) { + conn := dial(exitWithoutSignalOrStatus, t) + defer conn.Close() + session, err := conn.NewSession() + if err != nil { + t.Fatalf("Unable to request new session: %s", err) + } + defer session.Close() + if err := session.Shell(); err != nil { + t.Fatalf("Unable to execute command: %s", err) + } + err = session.Wait() + if err == nil { + t.Fatalf("expected command to fail but it didn't") + } + _, ok := err.(*ExitError) + if ok { + // you can't actually test for errors.errorString + // because it's not exported. + t.Fatalf("expected *errorString but got %T", err) + } +} + +type exitStatusMsg struct { + PeersId uint32 + Request string + WantReply bool + Status uint32 +} + +type exitSignalMsg struct { + PeersId uint32 + Request string + WantReply bool + Signal string + CoreDumped bool + Errmsg string + Lang string +} + +func exitStatusZeroHandler(ch *channel) { + defer ch.Close() + // this string is returned to stdout + shell := NewServerShell(ch, "> ") + shell.ReadLine() + sendStatus(0, ch) +} + +func exitStatusNonZeroHandler(ch *channel) { + defer ch.Close() + shell := NewServerShell(ch, "> ") + shell.ReadLine() + sendStatus(15, ch) +} + +func exitSignalAndStatusHandler(ch *channel) { + defer ch.Close() + shell := NewServerShell(ch, "> ") + shell.ReadLine() + sendStatus(15, ch) + sendSignal("TERM", ch) +} + +func exitSignalHandler(ch *channel) { + defer ch.Close() + shell := NewServerShell(ch, "> ") + shell.ReadLine() + sendSignal("TERM", ch) +} + +func exitSignalUnknownHandler(ch *channel) { + defer ch.Close() + shell := NewServerShell(ch, "> ") + shell.ReadLine() + sendSignal("SYS", ch) +} + +func exitWithoutSignalOrStatus(ch *channel) { + defer ch.Close() + shell := NewServerShell(ch, "> ") + shell.ReadLine() +} + +func shellHandler(ch *channel) { + defer ch.Close() + // this string is returned to stdout + shell := NewServerShell(ch, "golang") + shell.ReadLine() + sendStatus(0, ch) +} + +func sendStatus(status uint32, ch *channel) { + msg := exitStatusMsg{ + PeersId: ch.theirId, + Request: "exit-status", + WantReply: false, + Status: status, + } + ch.serverConn.writePacket(marshal(msgChannelRequest, msg)) +} + +func sendSignal(signal string, ch *channel) { + sig := exitSignalMsg{ + PeersId: ch.theirId, + Request: "exit-signal", + WantReply: false, + Signal: signal, + CoreDumped: false, + Errmsg: "Process terminated", + Lang: "en-GB-oed", + } + ch.serverConn.writePacket(marshal(msgChannelRequest, sig)) +} |