summaryrefslogtreecommitdiff
path: root/libgo/go/exp/ssh/session_test.go
diff options
context:
space:
mode:
Diffstat (limited to 'libgo/go/exp/ssh/session_test.go')
-rw-r--r--libgo/go/exp/ssh/session_test.go264
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))
+}