github.com/olivere/camlistore@v0.0.0-20140121221811-1b7ac2da0199/third_party/code.google.com/p/go.crypto/ssh/session_test.go (about)

     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.
     4  
     5  package ssh
     6  
     7  // Session tests.
     8  
     9  import (
    10  	"bytes"
    11  	"exp/terminal"
    12  	"io"
    13  	"testing"
    14  )
    15  
    16  type serverType func(*channel)
    17  
    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(conn *ServerConn, user, pass string) bool {
    22  		return user == "testuser" && pass == string(pw)
    23  	}
    24  	serverConfig.PublicKeyCallback = nil
    25  
    26  	l, err := Listen("tcp", "127.0.0.1:0", serverConfig)
    27  	if err != nil {
    28  		t.Fatalf("unable to listen: %s", err)
    29  	}
    30  	go func() {
    31  		defer l.Close()
    32  		conn, err := l.Accept()
    33  		if err != nil {
    34  			t.Errorf("Unable to accept: %v", err)
    35  			return
    36  		}
    37  		defer conn.Close()
    38  		if err := conn.Handshake(); err != nil {
    39  			t.Errorf("Unable to handshake: %v", err)
    40  			return
    41  		}
    42  		for {
    43  			ch, err := conn.Accept()
    44  			if err == io.EOF {
    45  				return
    46  			}
    47  			if err != nil {
    48  				t.Errorf("Unable to accept incoming channel request: %v", err)
    49  				return
    50  			}
    51  			if ch.ChannelType() != "session" {
    52  				ch.Reject(UnknownChannelType, "unknown channel type")
    53  				continue
    54  			}
    55  			ch.Accept()
    56  			go handler(ch.(*channel))
    57  		}
    58  		t.Log("done")
    59  	}()
    60  
    61  	config := &ClientConfig{
    62  		User: "testuser",
    63  		Auth: []ClientAuth{
    64  			ClientAuthPassword(pw),
    65  		},
    66  	}
    67  
    68  	c, err := Dial("tcp", l.Addr().String(), config)
    69  	if err != nil {
    70  		t.Fatalf("unable to dial remote side: %s", err)
    71  	}
    72  	return c
    73  }
    74  
    75  // Test a simple string is returned to session.Stdout.
    76  func TestSessionShell(t *testing.T) {
    77  	conn := dial(shellHandler, t)
    78  	defer conn.Close()
    79  	session, err := conn.NewSession()
    80  	if err != nil {
    81  		t.Fatalf("Unable to request new session: %s", err)
    82  	}
    83  	defer session.Close()
    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)
    88  	}
    89  	if err := session.Wait(); err != nil {
    90  		t.Fatalf("Remote command did not exit cleanly: %s", err)
    91  	}
    92  	actual := stdout.String()
    93  	if actual != "golang" {
    94  		t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
    95  	}
    96  }
    97  
    98  // TODO(dfc) add support for Std{in,err}Pipe when the Server supports it.
    99  
   100  // Test a simple string is returned via StdoutPipe.
   101  func TestSessionStdoutPipe(t *testing.T) {
   102  	conn := dial(shellHandler, t)
   103  	defer conn.Close()
   104  	session, err := conn.NewSession()
   105  	if err != nil {
   106  		t.Fatalf("Unable to request new session: %s", err)
   107  	}
   108  	defer session.Close()
   109  	stdout, err := session.StdoutPipe()
   110  	if err != nil {
   111  		t.Fatalf("Unable to request StdoutPipe(): %v", err)
   112  	}
   113  	var buf bytes.Buffer
   114  	if err := session.Shell(); err != nil {
   115  		t.Fatalf("Unable to execute command: %s", err)
   116  	}
   117  	done := make(chan bool, 1)
   118  	go func() {
   119  		if _, err := io.Copy(&buf, stdout); err != nil {
   120  			t.Errorf("Copy of stdout failed: %v", err)
   121  		}
   122  		done <- true
   123  	}()
   124  	if err := session.Wait(); err != nil {
   125  		t.Fatalf("Remote command did not exit cleanly: %s", err)
   126  	}
   127  	<-done
   128  	actual := buf.String()
   129  	if actual != "golang" {
   130  		t.Fatalf("Remote shell did not return expected string: expected=golang, actual=%s", actual)
   131  	}
   132  }
   133  
   134  // Test non-0 exit status is returned correctly.
   135  func TestExitStatusNonZero(t *testing.T) {
   136  	conn := dial(exitStatusNonZeroHandler, t)
   137  	defer conn.Close()
   138  	session, err := conn.NewSession()
   139  	if err != nil {
   140  		t.Fatalf("Unable to request new session: %s", err)
   141  	}
   142  	defer session.Close()
   143  	if err := session.Shell(); err != nil {
   144  		t.Fatalf("Unable to execute command: %s", err)
   145  	}
   146  	err = session.Wait()
   147  	if err == nil {
   148  		t.Fatalf("expected command to fail but it didn't")
   149  	}
   150  	e, ok := err.(*ExitError)
   151  	if !ok {
   152  		t.Fatalf("expected *ExitError but got %T", err)
   153  	}
   154  	if e.ExitStatus() != 15 {
   155  		t.Fatalf("expected command to exit with 15 but got %s", e.ExitStatus())
   156  	}
   157  }
   158  
   159  // Test 0 exit status is returned correctly.
   160  func TestExitStatusZero(t *testing.T) {
   161  	conn := dial(exitStatusZeroHandler, t)
   162  	defer conn.Close()
   163  	session, err := conn.NewSession()
   164  	if err != nil {
   165  		t.Fatalf("Unable to request new session: %s", err)
   166  	}
   167  	defer session.Close()
   168  
   169  	if err := session.Shell(); err != nil {
   170  		t.Fatalf("Unable to execute command: %s", err)
   171  	}
   172  	err = session.Wait()
   173  	if err != nil {
   174  		t.Fatalf("expected nil but got %s", err)
   175  	}
   176  }
   177  
   178  // Test exit signal and status are both returned correctly.
   179  func TestExitSignalAndStatus(t *testing.T) {
   180  	conn := dial(exitSignalAndStatusHandler, t)
   181  	defer conn.Close()
   182  	session, err := conn.NewSession()
   183  	if err != nil {
   184  		t.Fatalf("Unable to request new session: %s", err)
   185  	}
   186  	defer session.Close()
   187  	if err := session.Shell(); err != nil {
   188  		t.Fatalf("Unable to execute command: %s", err)
   189  	}
   190  	err = session.Wait()
   191  	if err == nil {
   192  		t.Fatalf("expected command to fail but it didn't")
   193  	}
   194  	e, ok := err.(*ExitError)
   195  	if !ok {
   196  		t.Fatalf("expected *ExitError but got %T", err)
   197  	}
   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())
   200  	}
   201  }
   202  
   203  // Test exit signal and status are both returned correctly.
   204  func TestKnownExitSignalOnly(t *testing.T) {
   205  	conn := dial(exitSignalHandler, t)
   206  	defer conn.Close()
   207  	session, err := conn.NewSession()
   208  	if err != nil {
   209  		t.Fatalf("Unable to request new session: %s", err)
   210  	}
   211  	defer session.Close()
   212  	if err := session.Shell(); err != nil {
   213  		t.Fatalf("Unable to execute command: %s", err)
   214  	}
   215  	err = session.Wait()
   216  	if err == nil {
   217  		t.Fatalf("expected command to fail but it didn't")
   218  	}
   219  	e, ok := err.(*ExitError)
   220  	if !ok {
   221  		t.Fatalf("expected *ExitError but got %T", err)
   222  	}
   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())
   225  	}
   226  }
   227  
   228  // Test exit signal and status are both returned correctly.
   229  func TestUnknownExitSignal(t *testing.T) {
   230  	conn := dial(exitSignalUnknownHandler, t)
   231  	defer conn.Close()
   232  	session, err := conn.NewSession()
   233  	if err != nil {
   234  		t.Fatalf("Unable to request new session: %s", err)
   235  	}
   236  	defer session.Close()
   237  	if err := session.Shell(); err != nil {
   238  		t.Fatalf("Unable to execute command: %s", err)
   239  	}
   240  	err = session.Wait()
   241  	if err == nil {
   242  		t.Fatalf("expected command to fail but it didn't")
   243  	}
   244  	e, ok := err.(*ExitError)
   245  	if !ok {
   246  		t.Fatalf("expected *ExitError but got %T", err)
   247  	}
   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())
   250  	}
   251  }
   252  
   253  // Test WaitMsg is not returned if the channel closes abruptly.
   254  func TestExitWithoutStatusOrSignal(t *testing.T) {
   255  	conn := dial(exitWithoutSignalOrStatus, t)
   256  	defer conn.Close()
   257  	session, err := conn.NewSession()
   258  	if err != nil {
   259  		t.Fatalf("Unable to request new session: %s", err)
   260  	}
   261  	defer session.Close()
   262  	if err := session.Shell(); err != nil {
   263  		t.Fatalf("Unable to execute command: %s", err)
   264  	}
   265  	err = session.Wait()
   266  	if err == nil {
   267  		t.Fatalf("expected command to fail but it didn't")
   268  	}
   269  	_, ok := err.(*ExitError)
   270  	if ok {
   271  		// you can't actually test for errors.errorString
   272  		// because it's not exported.
   273  		t.Fatalf("expected *errorString but got %T", err)
   274  	}
   275  }
   276  
   277  type exitStatusMsg struct {
   278  	PeersId   uint32
   279  	Request   string
   280  	WantReply bool
   281  	Status    uint32
   282  }
   283  
   284  type exitSignalMsg struct {
   285  	PeersId    uint32
   286  	Request    string
   287  	WantReply  bool
   288  	Signal     string
   289  	CoreDumped bool
   290  	Errmsg     string
   291  	Lang       string
   292  }
   293  
   294  func newServerShell(ch *channel, prompt string) *ServerTerminal {
   295  	term := terminal.NewTerminal(ch, prompt)
   296  	return &ServerTerminal{
   297  		Term:    term,
   298  		Channel: ch,
   299  	}
   300  }
   301  
   302  func exitStatusZeroHandler(ch *channel) {
   303  	defer ch.Close()
   304  	// this string is returned to stdout
   305  	shell := newServerShell(ch, "> ")
   306  	shell.ReadLine()
   307  	sendStatus(0, ch)
   308  }
   309  
   310  func exitStatusNonZeroHandler(ch *channel) {
   311  	defer ch.Close()
   312  	shell := newServerShell(ch, "> ")
   313  	shell.ReadLine()
   314  	sendStatus(15, ch)
   315  }
   316  
   317  func exitSignalAndStatusHandler(ch *channel) {
   318  	defer ch.Close()
   319  	shell := newServerShell(ch, "> ")
   320  	shell.ReadLine()
   321  	sendStatus(15, ch)
   322  	sendSignal("TERM", ch)
   323  }
   324  
   325  func exitSignalHandler(ch *channel) {
   326  	defer ch.Close()
   327  	shell := newServerShell(ch, "> ")
   328  	shell.ReadLine()
   329  	sendSignal("TERM", ch)
   330  }
   331  
   332  func exitSignalUnknownHandler(ch *channel) {
   333  	defer ch.Close()
   334  	shell := newServerShell(ch, "> ")
   335  	shell.ReadLine()
   336  	sendSignal("SYS", ch)
   337  }
   338  
   339  func exitWithoutSignalOrStatus(ch *channel) {
   340  	defer ch.Close()
   341  	shell := newServerShell(ch, "> ")
   342  	shell.ReadLine()
   343  }
   344  
   345  func shellHandler(ch *channel) {
   346  	defer ch.Close()
   347  	// this string is returned to stdout
   348  	shell := newServerShell(ch, "golang")
   349  	shell.ReadLine()
   350  	sendStatus(0, ch)
   351  }
   352  
   353  func sendStatus(status uint32, ch *channel) {
   354  	msg := exitStatusMsg{
   355  		PeersId:   ch.theirId,
   356  		Request:   "exit-status",
   357  		WantReply: false,
   358  		Status:    status,
   359  	}
   360  	ch.serverConn.writePacket(marshal(msgChannelRequest, msg))
   361  }
   362  
   363  func sendSignal(signal string, ch *channel) {
   364  	sig := exitSignalMsg{
   365  		PeersId:    ch.theirId,
   366  		Request:    "exit-signal",
   367  		WantReply:  false,
   368  		Signal:     signal,
   369  		CoreDumped: false,
   370  		Errmsg:     "Process terminated",
   371  		Lang:       "en-GB-oed",
   372  	}
   373  	ch.serverConn.writePacket(marshal(msgChannelRequest, sig))
   374  }