Browse code

Mock Hijack and Implement Unit test for Attach

Guillaume J. Charmes authored on 2013/05/10 13:55:08
Showing 2 changed files
... ...
@@ -702,8 +702,94 @@ func TestPostContainersWait(t *testing.T) {
702 702
 }
703 703
 
704 704
 func TestPostContainersAttach(t *testing.T) {
705
-	//FIXME: Implement this test
706
-	t.Log("Test on implemented")
705
+	runtime, err := newTestRuntime()
706
+	if err != nil {
707
+		t.Fatal(err)
708
+	}
709
+	defer nuke(runtime)
710
+
711
+	srv := &Server{runtime: runtime}
712
+
713
+	container, err := NewBuilder(runtime).Create(
714
+		&Config{
715
+			Image:     GetTestImage(runtime).Id,
716
+			Cmd:       []string{"/bin/cat"},
717
+			OpenStdin: true,
718
+		},
719
+	)
720
+	if err != nil {
721
+		t.Fatal(err)
722
+	}
723
+	defer runtime.Destroy(container)
724
+
725
+	// Start the process
726
+	if err := container.Start(); err != nil {
727
+		t.Fatal(err)
728
+	}
729
+
730
+	stdin, stdinPipe := io.Pipe()
731
+	stdout, stdoutPipe := io.Pipe()
732
+
733
+	// Attach to it
734
+	c1 := make(chan struct{})
735
+	go func() {
736
+		// We're simulating a disconnect so the return value doesn't matter. What matters is the
737
+		// fact that CmdAttach returns.
738
+
739
+		r := &hijackTester{
740
+			ResponseRecorder: httptest.NewRecorder(),
741
+			in:               stdin,
742
+			out:              stdoutPipe,
743
+		}
744
+
745
+		req, err := http.NewRequest("POST", "/containers/"+container.Id+"/attach?stream=1&stdin=1&stdout=1&stderr=1", bytes.NewReader([]byte{}))
746
+		if err != nil {
747
+			t.Fatal(err)
748
+		}
749
+
750
+		body, err := postContainersAttach(srv, r, req, map[string]string{"name": container.Id})
751
+		close(c1)
752
+		if err != nil {
753
+			t.Fatal(err)
754
+		}
755
+		if body != nil {
756
+			t.Fatalf("No body expected, received: %s\n", body)
757
+		}
758
+	}()
759
+
760
+	// Acknowledge hijack
761
+	setTimeout(t, "hijack acknowledge timed out", 2*time.Second, func() {
762
+		stdout.Read([]byte{})
763
+		stdout.Read(make([]byte, 4096))
764
+	})
765
+
766
+	setTimeout(t, "read/write assertion timed out", 2*time.Second, func() {
767
+		if err := assertPipe("hello\n", "hello", stdout, stdinPipe, 15); err != nil {
768
+			t.Fatal(err)
769
+		}
770
+	})
771
+
772
+	// Close pipes (client disconnects)
773
+	if err := closeWrap(stdin, stdinPipe, stdout, stdoutPipe); err != nil {
774
+		t.Fatal(err)
775
+	}
776
+
777
+	// Wait for attach to finish, the client disconnected, therefore, Attach finished his job
778
+	setTimeout(t, "Waiting for CmdAttach timed out", 2*time.Second, func() {
779
+		<-c1
780
+	})
781
+
782
+	// We closed stdin, expect /bin/cat to still be running
783
+	// Wait a little bit to make sure container.monitor() did his thing
784
+	err = container.WaitTimeout(500 * time.Millisecond)
785
+	if err == nil || !container.State.Running {
786
+		t.Fatalf("/bin/cat is not running after closing stdin")
787
+	}
788
+
789
+	// Try to avoid the timeoout in destroy. Best effort, don't check error
790
+	cStdin, _ := container.StdinPipe()
791
+	cStdin.Close()
792
+	container.Wait()
707 793
 }
708 794
 
709 795
 // FIXME: Test deleting runnign container
... ...
@@ -760,5 +846,32 @@ func TestDeleteContainers(t *testing.T) {
760 760
 
761 761
 func TestDeleteImages(t *testing.T) {
762 762
 	//FIXME: Implement this test
763
-	t.Log("Test on implemented")
763
+	t.Log("Test not implemented")
764
+}
765
+
766
+// Mocked types for tests
767
+type NopConn struct {
768
+	io.ReadCloser
769
+	io.Writer
770
+}
771
+
772
+func (c *NopConn) LocalAddr() net.Addr                { return nil }
773
+func (c *NopConn) RemoteAddr() net.Addr               { return nil }
774
+func (c *NopConn) SetDeadline(t time.Time) error      { return nil }
775
+func (c *NopConn) SetReadDeadline(t time.Time) error  { return nil }
776
+func (c *NopConn) SetWriteDeadline(t time.Time) error { return nil }
777
+
778
+type hijackTester struct {
779
+	*httptest.ResponseRecorder
780
+	in  io.ReadCloser
781
+	out io.Writer
782
+}
783
+
784
+func (t *hijackTester) Hijack() (net.Conn, *bufio.ReadWriter, error) {
785
+	bufrw := bufio.NewReadWriter(bufio.NewReader(t.in), bufio.NewWriter(t.out))
786
+	conn := &NopConn{
787
+		ReadCloser: t.in,
788
+		Writer:     t.out,
789
+	}
790
+	return conn, bufrw, nil
764 791
 }
... ...
@@ -1,17 +1,15 @@
1 1
 package docker
2 2
 
3 3
 import (
4
-	/*"bufio"
4
+	"bufio"
5 5
 	"fmt"
6
-	"github.com/dotcloud/docker/rcli"
7 6
 	"io"
8
-	"io/ioutil"
9
-	"strings"*/
7
+	_ "io/ioutil"
8
+	"strings"
10 9
 	"testing"
11 10
 	"time"
12 11
 )
13 12
 
14
-/*TODO
15 13
 func closeWrap(args ...io.Closer) error {
16 14
 	e := false
17 15
 	ret := fmt.Errorf("Error closing elements")
... ...
@@ -26,7 +24,7 @@ func closeWrap(args ...io.Closer) error {
26 26
 	}
27 27
 	return nil
28 28
 }
29
-*/
29
+
30 30
 func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
31 31
 	c := make(chan bool)
32 32
 
... ...
@@ -44,7 +42,6 @@ func setTimeout(t *testing.T, msg string, d time.Duration, f func()) {
44 44
 	}
45 45
 }
46 46
 
47
-/*TODO
48 47
 func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error {
49 48
 	for i := 0; i < count; i++ {
50 49
 		if _, err := w.Write([]byte(input)); err != nil {
... ...
@@ -61,6 +58,7 @@ func assertPipe(input, output string, r io.Reader, w io.Writer, count int) error
61 61
 	return nil
62 62
 }
63 63
 
64
+/*TODO
64 65
 func cmdWait(srv *Server, container *Container) error {
65 66
 	stdout, stdoutPipe := io.Pipe()
66 67