Browse Source

Engine: better testing of streams and of basic engine primitives. Coverage=81.2%

Solomon Hykes 11 years ago
parent
commit
3553a803e3
3 changed files with 379 additions and 0 deletions
  1. 47 0
      engine/engine_test.go
  2. 80 0
      engine/job_test.go
  3. 252 0
      engine/streams_test.go

+ 47 - 0
engine/engine_test.go

@@ -1,6 +1,9 @@
 package engine
 
 import (
+	"io/ioutil"
+	"os"
+	"path"
 	"testing"
 )
 
@@ -54,3 +57,47 @@ func TestJob(t *testing.T) {
 		t.Fatalf("handler dummy2 was not found in job2")
 	}
 }
+
+func TestEngineRoot(t *testing.T) {
+	tmp, err := ioutil.TempDir("", "docker-test-TestEngineCreateDir")
+	if err != nil {
+		t.Fatal(err)
+	}
+	defer os.RemoveAll(tmp)
+	dir := path.Join(tmp, "dir")
+	eng, err := New(dir)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if st, err := os.Stat(dir); err != nil {
+		t.Fatal(err)
+	} else if !st.IsDir() {
+		t.Fatalf("engine.New() created something other than a directory at %s", dir)
+	}
+	if r := eng.Root(); r != dir {
+		t.Fatalf("Expected: %v\nReceived: %v", dir, r)
+	}
+}
+
+func TestEngineString(t *testing.T) {
+	eng1 := newTestEngine(t)
+	defer os.RemoveAll(eng1.Root())
+	eng2 := newTestEngine(t)
+	defer os.RemoveAll(eng2.Root())
+	s1 := eng1.String()
+	s2 := eng2.String()
+	if eng1 == eng2 {
+		t.Fatalf("Different engines should have different names (%v == %v)", s1, s2)
+	}
+}
+
+func TestEngineLogf(t *testing.T) {
+	eng := newTestEngine(t)
+	defer os.RemoveAll(eng.Root())
+	input := "Test log line"
+	if n, err := eng.Logf("%s\n", input); err != nil {
+		t.Fatal(err)
+	} else if n < len(input) {
+		t.Fatalf("Test: Logf() should print at least as much as the input\ninput=%d\nprinted=%d", len(input), n)
+	}
+}

+ 80 - 0
engine/job_test.go

@@ -0,0 +1,80 @@
+package engine
+
+import (
+	"os"
+	"testing"
+)
+
+func TestJobStatusOK(t *testing.T) {
+	eng := newTestEngine(t)
+	defer os.RemoveAll(eng.Root())
+	eng.Register("return_ok", func(job *Job) Status { return StatusOK })
+	err := eng.Job("return_ok").Run()
+	if err != nil {
+		t.Fatalf("Expected: err=%v\nReceived: err=%v", nil, err)
+	}
+}
+
+func TestJobStatusErr(t *testing.T) {
+	eng := newTestEngine(t)
+	defer os.RemoveAll(eng.Root())
+	eng.Register("return_err", func(job *Job) Status { return StatusErr })
+	err := eng.Job("return_err").Run()
+	if err == nil {
+		t.Fatalf("When a job returns StatusErr, Run() should return an error")
+	}
+}
+
+func TestJobStatusNotFound(t *testing.T) {
+	eng := newTestEngine(t)
+	defer os.RemoveAll(eng.Root())
+	eng.Register("return_not_found", func(job *Job) Status { return StatusNotFound })
+	err := eng.Job("return_not_found").Run()
+	if err == nil {
+		t.Fatalf("When a job returns StatusNotFound, Run() should return an error")
+	}
+}
+
+func TestJobStdoutString(t *testing.T) {
+	eng := newTestEngine(t)
+	defer os.RemoveAll(eng.Root())
+	// FIXME: test multiple combinations of output and status
+	eng.Register("say_something_in_stdout", func(job *Job) Status {
+		job.Printf("Hello world\n")
+		return StatusOK
+	})
+
+	job := eng.Job("say_something_in_stdout")
+	var output string
+	if err := job.Stdout.AddString(&output); err != nil {
+		t.Fatal(err)
+	}
+	if err := job.Run(); err != nil {
+		t.Fatal(err)
+	}
+	if expectedOutput := "Hello world"; output != expectedOutput {
+		t.Fatalf("Stdout last line:\nExpected: %v\nReceived: %v", expectedOutput, output)
+	}
+}
+
+func TestJobStderrString(t *testing.T) {
+	eng := newTestEngine(t)
+	defer os.RemoveAll(eng.Root())
+	// FIXME: test multiple combinations of output and status
+	eng.Register("say_something_in_stderr", func(job *Job) Status {
+		job.Errorf("Warning, something might happen\nHere it comes!\nOh no...\nSomething happened\n")
+		return StatusOK
+	})
+
+	job := eng.Job("say_something_in_stderr")
+	var output string
+	if err := job.Stderr.AddString(&output); err != nil {
+		t.Fatal(err)
+	}
+	if err := job.Run(); err != nil {
+		t.Fatal(err)
+	}
+	if expectedOutput := "Something happened"; output != expectedOutput {
+		t.Fatalf("Stderr last line:\nExpected: %v\nReceived: %v", expectedOutput, output)
+	}
+}

+ 252 - 0
engine/streams_test.go

@@ -0,0 +1,252 @@
+package engine
+
+import (
+	"bufio"
+	"bytes"
+	"fmt"
+	"io/ioutil"
+	"strings"
+	"testing"
+)
+
+func TestOutputAddString(t *testing.T) {
+	var testInputs = [][2]string{
+		{
+			"hello, world!",
+			"hello, world!",
+		},
+
+		{
+			"One\nTwo\nThree",
+			"Three",
+		},
+
+		{
+			"",
+			"",
+		},
+
+		{
+			"A line\nThen another nl-terminated line\n",
+			"Then another nl-terminated line",
+		},
+
+		{
+			"A line followed by an empty line\n\n",
+			"",
+		},
+	}
+	for _, testData := range testInputs {
+		input := testData[0]
+		expectedOutput := testData[1]
+		o := NewOutput()
+		var output string
+		if err := o.AddString(&output); err != nil {
+			t.Error(err)
+		}
+		if n, err := o.Write([]byte(input)); err != nil {
+			t.Error(err)
+		} else if n != len(input) {
+			t.Errorf("Expected %d, got %d", len(input), n)
+		}
+		o.Close()
+		if output != expectedOutput {
+			t.Errorf("Last line is not stored as return string.\nInput:   '%s'\nExpected: '%s'\nGot:       '%s'", input, expectedOutput, output)
+		}
+	}
+}
+
+type sentinelWriteCloser struct {
+	calledWrite bool
+	calledClose bool
+}
+
+func (w *sentinelWriteCloser) Write(p []byte) (int, error) {
+	w.calledWrite = true
+	return len(p), nil
+}
+
+func (w *sentinelWriteCloser) Close() error {
+	w.calledClose = true
+	return nil
+}
+
+func TestOutputAddClose(t *testing.T) {
+	o := NewOutput()
+	var s sentinelWriteCloser
+	if err := o.Add(&s); err != nil {
+		t.Fatal(err)
+	}
+	if err := o.Close(); err != nil {
+		t.Fatal(err)
+	}
+	// Write data after the output is closed.
+	// Write should succeed, but no destination should receive it.
+	if _, err := o.Write([]byte("foo bar")); err != nil {
+		t.Fatal(err)
+	}
+	if !s.calledClose {
+		t.Fatal("Output.Close() didn't close the destination")
+	}
+}
+
+func TestOutputAddPipe(t *testing.T) {
+	var testInputs = []string{
+		"hello, world!",
+		"One\nTwo\nThree",
+		"",
+		"A line\nThen another nl-terminated line\n",
+		"A line followed by an empty line\n\n",
+	}
+	for _, input := range testInputs {
+		expectedOutput := input
+		o := NewOutput()
+		r, err := o.AddPipe()
+		if err != nil {
+			t.Fatal(err)
+		}
+		go func(o *Output) {
+			if n, err := o.Write([]byte(input)); err != nil {
+				t.Error(err)
+			} else if n != len(input) {
+				t.Errorf("Expected %d, got %d", len(input), n)
+			}
+			if err := o.Close(); err != nil {
+				t.Error(err)
+			}
+		}(o)
+		output, err := ioutil.ReadAll(r)
+		if err != nil {
+			t.Fatal(err)
+		}
+		if string(output) != expectedOutput {
+			t.Errorf("Last line is not stored as return string.\nExpected: '%s'\nGot:       '%s'", expectedOutput, output)
+		}
+	}
+}
+
+func TestTail(t *testing.T) {
+	var tests = make(map[string][][]string)
+	tests["hello, world!"] = [][]string{
+		{},
+		{"hello, world!"},
+		{"hello, world!"},
+		{"hello, world!"},
+	}
+	tests["One\nTwo\nThree"] = [][]string{
+		{},
+		{"Three"},
+		{"Two", "Three"},
+		{"One", "Two", "Three"},
+	}
+	for input, outputs := range tests {
+		for n, expectedOutput := range outputs {
+			var output []string
+			Tail(strings.NewReader(input), n, &output)
+			if fmt.Sprintf("%v", output) != fmt.Sprintf("%v", expectedOutput) {
+				t.Errorf("Tail n=%d returned wrong result.\nExpected: '%s'\nGot     : '%s'", expectedOutput, output)
+			}
+		}
+	}
+}
+
+func TestOutputAddTail(t *testing.T) {
+	var tests = make(map[string][][]string)
+	tests["hello, world!"] = [][]string{
+		{},
+		{"hello, world!"},
+		{"hello, world!"},
+		{"hello, world!"},
+	}
+	tests["One\nTwo\nThree"] = [][]string{
+		{},
+		{"Three"},
+		{"Two", "Three"},
+		{"One", "Two", "Three"},
+	}
+	for input, outputs := range tests {
+		for n, expectedOutput := range outputs {
+			o := NewOutput()
+			var output []string
+			if err := o.AddTail(&output, n); err != nil {
+				t.Error(err)
+			}
+			if n, err := o.Write([]byte(input)); err != nil {
+				t.Error(err)
+			} else if n != len(input) {
+				t.Errorf("Expected %d, got %d", len(input), n)
+			}
+			o.Close()
+			if fmt.Sprintf("%v", output) != fmt.Sprintf("%v", expectedOutput) {
+				t.Errorf("Tail(%d) returned wrong result.\nExpected: %v\nGot:      %v", n, expectedOutput, output)
+			}
+		}
+	}
+}
+
+func lastLine(txt string) string {
+	scanner := bufio.NewScanner(strings.NewReader(txt))
+	var lastLine string
+	for scanner.Scan() {
+		lastLine = scanner.Text()
+	}
+	return lastLine
+}
+
+func TestOutputAdd(t *testing.T) {
+	o := NewOutput()
+	b := &bytes.Buffer{}
+	o.Add(b)
+	input := "hello, world!"
+	if n, err := o.Write([]byte(input)); err != nil {
+		t.Fatal(err)
+	} else if n != len(input) {
+		t.Fatalf("Expected %d, got %d", len(input), n)
+	}
+	if output := b.String(); output != input {
+		t.Fatal("Received wrong data from Add.\nExpected: '%s'\nGot:     '%s'", input, output)
+	}
+}
+
+func TestInputAddEmpty(t *testing.T) {
+	i := NewInput()
+	var b bytes.Buffer
+	if err := i.Add(&b); err != nil {
+		t.Fatal(err)
+	}
+	data, err := ioutil.ReadAll(i)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if len(data) > 0 {
+		t.Fatalf("Read from empty input shoul yield no data")
+	}
+}
+
+func TestInputAddTwo(t *testing.T) {
+	i := NewInput()
+	var b1 bytes.Buffer
+	// First add should succeed
+	if err := i.Add(&b1); err != nil {
+		t.Fatal(err)
+	}
+	var b2 bytes.Buffer
+	// Second add should fail
+	if err := i.Add(&b2); err == nil {
+		t.Fatalf("Adding a second source should return an error")
+	}
+}
+
+func TestInputAddNotEmpty(t *testing.T) {
+	i := NewInput()
+	b := bytes.NewBufferString("hello world\nabc")
+	expectedResult := b.String()
+	i.Add(b)
+	result, err := ioutil.ReadAll(i)
+	if err != nil {
+		t.Fatal(err)
+	}
+	if string(result) != expectedResult {
+		t.Fatalf("Expected: %v\nReceived: %v", expectedResult, result)
+	}
+}