v.io/jiri@v0.0.0-20160715023856-abfb8b131290/runutil/executor_test.go (about)

     1  // Copyright 2015 The Vanadium 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 runutil_test
     6  
     7  import (
     8  	"bufio"
     9  	"bytes"
    10  	"fmt"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"strings"
    16  	"testing"
    17  	"time"
    18  
    19  	"v.io/jiri/runutil"
    20  	"v.io/x/lib/envvar"
    21  	"v.io/x/lib/lookpath"
    22  )
    23  
    24  const timedCommandTimeout = 3 * time.Second
    25  
    26  var forever time.Duration
    27  
    28  func removeTimestamps(t *testing.T, buffer *bytes.Buffer) string {
    29  	result := ""
    30  	scanner := bufio.NewScanner(buffer)
    31  	for scanner.Scan() {
    32  		line := scanner.Text()
    33  		if index := strings.Index(line, ">>"); index != -1 {
    34  			result += line[index:] + "\n"
    35  		} else {
    36  			result += line + "\n"
    37  		}
    38  	}
    39  	if err := scanner.Err(); err != nil {
    40  		t.Fatalf("Scan() failed: %v", err)
    41  	}
    42  	return result
    43  }
    44  
    45  func osPath(t *testing.T, bin string) string {
    46  	path, err := lookpath.Look(envvar.SliceToMap(os.Environ()), bin)
    47  	if err != nil {
    48  		t.Fatal(err)
    49  	}
    50  	return path
    51  }
    52  
    53  func removeTimestampsAndPath(t *testing.T, buffer *bytes.Buffer, bin string) string {
    54  	s := removeTimestamps(t, buffer)
    55  	return strings.Replace(s, osPath(t, bin), bin, 1)
    56  }
    57  
    58  func TestCommandOK(t *testing.T) {
    59  	var out bytes.Buffer
    60  	s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true)
    61  	if err := s.Last("go", "run", "./testdata/ok_hello.go"); err != nil {
    62  		t.Fatalf(`Command("go run ./testdata/ok_hello.go") failed: %v`, err)
    63  	}
    64  	if got, want := removeTimestampsAndPath(t, &out, "go"), ">> go run ./testdata/ok_hello.go\n>> OK\nhello\n"; got != want {
    65  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
    66  	}
    67  	var cout bytes.Buffer
    68  	if err := s.Capture(&cout, nil).Last("go", "run", "./testdata/ok_hello.go"); err != nil {
    69  		t.Fatalf(`Command("go run ./testdata/ok_hello.go") failed: %v`, err)
    70  	}
    71  	if got, want := cout.String(), "hello\n"; got != want {
    72  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
    73  	}
    74  }
    75  
    76  func TestCommandFail(t *testing.T) {
    77  	var out bytes.Buffer
    78  	s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true)
    79  	if err := s.Last("go", "run", "./testdata/fail_hello.go"); err == nil {
    80  		t.Fatalf(`Command("go run ./testdata/fail_hello.go") did not fail when it should`)
    81  	}
    82  
    83  	if got, wantCommon, want1, want2 := removeTimestampsAndPath(t, &out, "go"), ">> go run ./testdata/fail_hello.go\n>> FAILED: exit status 1\n", "hello\nexit status 1\n", "exit status 1\nhello\n"; got != wantCommon+want1 && got != wantCommon+want2 {
    84  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v\nor\n%v\n", got, wantCommon+want1, wantCommon+want2)
    85  	}
    86  	var cout, cerr bytes.Buffer
    87  	if err := s.Capture(&cout, &cerr).Last("go", "run", "./testdata/fail_hello.go"); err == nil {
    88  		t.Fatalf(`Command("go run ./testdata/fail_hello.go") did not fail when it should`)
    89  	}
    90  	if got, want := cout.String(), "hello\n"; got != want {
    91  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
    92  	}
    93  	if got, want := cerr.String(), "exit status 1\n"; got != want {
    94  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
    95  	}
    96  }
    97  
    98  func TestCommandWithOptsOK(t *testing.T) {
    99  	var cmdOut, runOut bytes.Buffer
   100  	s := runutil.NewSequence(nil, os.Stdin, &runOut, ioutil.Discard, false, true)
   101  	if err := s.Capture(&cmdOut, nil).Verbose(false).Last("go", "run", "./testdata/ok_hello.go"); err != nil {
   102  		t.Fatalf(`CommandWithOpts("go run ./testdata/ok_hello.go") failed: %v`, err)
   103  	}
   104  	if got, want := removeTimestampsAndPath(t, &runOut, "go"), ">> go run ./testdata/ok_hello.go\n>> OK\n"; got != want {
   105  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   106  	}
   107  	if got, want := strings.TrimSpace(cmdOut.String()), "hello"; got != want {
   108  		t.Fatalf("unexpected output: got %v, want %v", got, want)
   109  	}
   110  }
   111  
   112  func TestCommandWithOptsFail(t *testing.T) {
   113  	var cmdOut, runOut bytes.Buffer
   114  	s := runutil.NewSequence(nil, os.Stdin, &runOut, ioutil.Discard, false, true)
   115  	if err := s.Capture(&cmdOut, nil).Verbose(false).Last("go", "run", "./testdata/fail_hello.go"); err == nil {
   116  		t.Fatalf(`CommandWithOpts("go run ./testdata/fail_hello.go") did not fail when it should`)
   117  	}
   118  	if got, want := removeTimestampsAndPath(t, &runOut, "go"), ">> go run ./testdata/fail_hello.go\n>> FAILED: exit status 1\n"; got != want {
   119  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   120  	}
   121  	if got, want := strings.TrimSpace(cmdOut.String()), "hello"; got != want {
   122  		t.Fatalf("unexpected output: got %v, want %v", got, want)
   123  	}
   124  }
   125  
   126  func TestTimedCommandOK(t *testing.T) {
   127  	var out bytes.Buffer
   128  	s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true)
   129  	bin, err := buildTestProgram("fast_hello")
   130  	if bin != "" {
   131  		defer os.RemoveAll(filepath.Dir(bin))
   132  	}
   133  	if err != nil {
   134  		t.Fatalf("%v", err)
   135  	}
   136  	if err := s.Timeout(2 * time.Minute).Last(bin); err != nil {
   137  		t.Fatalf(`TimedCommand("go run ./testdata/fast_hello.go") failed: %v`, err)
   138  	}
   139  	if got, want := removeTimestamps(t, &out), fmt.Sprintf(">> %s\n>> OK\nhello\n", bin); got != want {
   140  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   141  	}
   142  }
   143  
   144  func TestTimedCommandFail(t *testing.T) {
   145  	var out, stderr bytes.Buffer
   146  	s := runutil.NewSequence(nil, os.Stdin, &out, &stderr, false, true)
   147  	bin, err := buildTestProgram("slow_hello")
   148  	if bin != "" {
   149  		defer os.RemoveAll(filepath.Dir(bin))
   150  	}
   151  	if err != nil {
   152  		t.Fatalf("%v", err)
   153  	}
   154  	if err := s.Timeout(timedCommandTimeout).Last(bin); err == nil {
   155  		t.Fatalf(`TimedCommand("go run ./testdata/slow_hello.go") did not fail when it should`)
   156  	} else if got, want := runutil.IsTimeout(err), true; got != want {
   157  		t.Fatalf("unexpected error: got %v, want %v", got, want)
   158  	}
   159  	o := removeTimestamps(t, &out)
   160  	o = strings.Replace(o, bin, "slow_hello", 1)
   161  	if got, want := o, `>> slow_hello
   162  >> TIMED OUT
   163  hello
   164  >> Waiting for command to exit: ["`+bin+`"]
   165  `; !strings.HasPrefix(o, want) {
   166  		t.Errorf("output doesn't start with %v, got: %v (stderr: %v)", want, got, stderr.String())
   167  	}
   168  }
   169  
   170  func TestTimedCommandWithOptsOK(t *testing.T) {
   171  	var cmdOut, runOut bytes.Buffer
   172  	s := runutil.NewSequence(nil, os.Stdin, &runOut, ioutil.Discard, false, true)
   173  	bin, err := buildTestProgram("fast_hello")
   174  	if bin != "" {
   175  		defer os.RemoveAll(filepath.Dir(bin))
   176  	}
   177  	if err != nil {
   178  		t.Fatalf("%v", err)
   179  	}
   180  
   181  	if err := s.Timeout(2*time.Minute).Verbose(false).Capture(&cmdOut, nil).Last(bin); err != nil {
   182  		t.Fatalf(`TimedCommandWithOpts("go run ./testdata/fast_hello.go") failed: %v`, err)
   183  	}
   184  	if got, want := removeTimestamps(t, &runOut), fmt.Sprintf(">> %s\n>> OK\n", bin); got != want {
   185  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   186  	}
   187  	if got, want := strings.TrimSpace(cmdOut.String()), "hello"; got != want {
   188  		t.Fatalf("unexpected output: got %v, want %v", got, want)
   189  	}
   190  }
   191  
   192  func TestTimedCommandWithOptsFail(t *testing.T) {
   193  	var cmdOut, runOut bytes.Buffer
   194  	s := runutil.NewSequence(nil, os.Stdin, &runOut, ioutil.Discard, false, true)
   195  	bin, err := buildTestProgram("slow_hello")
   196  	if bin != "" {
   197  		defer os.RemoveAll(filepath.Dir(bin))
   198  	}
   199  	if err != nil {
   200  		t.Fatalf("%v", err)
   201  	}
   202  	if err := s.Timeout(timedCommandTimeout).Capture(&cmdOut, nil).Verbose(false).Last(bin); err == nil {
   203  		t.Fatalf(`TimedCommandWithOpts("go run ./testdata/slow_hello.go") did not fail when it should`)
   204  	} else if got, want := runutil.IsTimeout(err), true; got != want {
   205  		t.Fatalf("unexpected error: got %v, want %v", got, want)
   206  	}
   207  	if got, want := removeTimestamps(t, &runOut), fmt.Sprintf(">> %s\n>> TIMED OUT\n", bin); got != want {
   208  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   209  	}
   210  	if got, want := strings.TrimSpace(cmdOut.String()), "hello"; got != want {
   211  		t.Fatalf("unexpected output: got %v, want %v", got, want)
   212  	}
   213  }
   214  
   215  func TestFunctionOK(t *testing.T) {
   216  	var out bytes.Buffer
   217  	s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true)
   218  	fn := func() error {
   219  		cmd := exec.Command("go", "run", "./testdata/ok_hello.go")
   220  		cmd.Stdout = &out
   221  		return cmd.Run()
   222  	}
   223  	if err := s.Capture(&out, nil).Call(fn, "%v %v %v", "go", "run", "./testdata/ok_hello.go").Done(); err != nil {
   224  		t.Fatalf(`Function("go run ./testdata/ok_hello.go") failed: %v`, err)
   225  	}
   226  	if got, want := removeTimestamps(t, &out), ">> go run ./testdata/ok_hello.go\nhello\n>> OK\n"; got != want {
   227  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   228  	}
   229  }
   230  
   231  func TestFunctionFail(t *testing.T) {
   232  	var out bytes.Buffer
   233  	s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true)
   234  	fn := func() error {
   235  		cmd := exec.Command("go", "run", "./testdata/fail_hello.go")
   236  		cmd.Stdout = &out
   237  		cmd.Stdout = &out
   238  		if err := cmd.Run(); err != nil {
   239  			return fmt.Errorf("the function failed")
   240  		}
   241  
   242  		return nil
   243  	}
   244  	if err := s.Capture(&out, nil).Call(fn, "%v %v %v", "go", "run", "./testdata/fail_hello.go").Done(); err == nil {
   245  		t.Fatalf(`Function("go run ./testdata/fail_hello.go") did not fail when it should`)
   246  	}
   247  	if got, want := removeTimestamps(t, &out), ">> go run ./testdata/fail_hello.go\nhello\n>> FAILED: the function failed\n"; got != want {
   248  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   249  	}
   250  }
   251  
   252  func TestFunctionWithOptsOK(t *testing.T) {
   253  	var out bytes.Buffer
   254  	s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, false)
   255  
   256  	fn := func() error {
   257  		cmd := exec.Command("go", "run", "./testdata/ok_hello.go")
   258  		cmd.Stdout = &out
   259  		if err := cmd.Run(); err != nil {
   260  			return fmt.Errorf("the function failed")
   261  		}
   262  		return nil
   263  	}
   264  	if err := s.Capture(&out, nil).Verbose(true).Call(fn, "%v %v %v", "go", "run", "./testdata/ok_hello.go").Done(); err != nil {
   265  		t.Fatalf(`FunctionWithOpts("go run ./testdata/ok_hello.go") failed: %v`, err)
   266  	}
   267  	if got, want := removeTimestamps(t, &out), ">> go run ./testdata/ok_hello.go\nhello\n>> OK\n"; got != want {
   268  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   269  	}
   270  }
   271  
   272  func TestFunctionWithOptsFail(t *testing.T) {
   273  	var out bytes.Buffer
   274  	s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, false)
   275  	fn := func() error {
   276  		cmd := exec.Command("go", "run", "./testdata/fail_hello.go")
   277  		cmd.Stdout = &out
   278  		if err := cmd.Run(); err != nil {
   279  			return fmt.Errorf("the function failed")
   280  		}
   281  		return nil
   282  	}
   283  	if err := s.Capture(&out, nil).Verbose(true).Call(fn, "%v %v %v", "go", "run", "./testdata/fail_hello.go").Done(); err == nil {
   284  		t.Fatalf(`FunctionWithOpts("go run ./testdata/fail_hello.go") did not fail when it should`)
   285  	}
   286  	if got, want := removeTimestamps(t, &out), ">> go run ./testdata/fail_hello.go\nhello\n>> FAILED: the function failed\n"; got != want {
   287  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   288  	}
   289  }
   290  
   291  func TestOutput(t *testing.T) {
   292  	var out bytes.Buffer
   293  	s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true)
   294  	s.Output([]string{"hello", "world"})
   295  	if got, want := removeTimestamps(t, &out), ">> hello\n>> world\n"; got != want {
   296  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   297  	}
   298  }
   299  
   300  func TestOutputWithOpts(t *testing.T) {
   301  	var out bytes.Buffer
   302  	s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, false)
   303  	s.Verbose(true).Output([]string{"hello", "world"})
   304  	if got, want := removeTimestamps(t, &out), ">> hello\n>> world\n"; got != want {
   305  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   306  	}
   307  }
   308  
   309  func TestNested(t *testing.T) {
   310  	var out bytes.Buffer
   311  	s := runutil.NewSequence(nil, os.Stdin, &out, ioutil.Discard, false, true)
   312  	fn := func() error {
   313  		s.Output([]string{"hello", "world"})
   314  		return nil
   315  	}
   316  	s.Call(fn, "%v", "greetings").Done()
   317  	if got, want := removeTimestamps(t, &out), ">> greetings\n>>>> hello\n>>>> world\n>> OK\n"; got != want {
   318  		t.Fatalf("unexpected output:\ngot\n%v\nwant\n%v", got, want)
   319  	}
   320  }
   321  
   322  func buildTestProgram(fileName string) (string, error) {
   323  	tmpDir, err := ioutil.TempDir("", "runtest")
   324  	if err != nil {
   325  		return "", fmt.Errorf("TempDir() failed: %v", err)
   326  	}
   327  	bin := filepath.Join(tmpDir, fileName)
   328  	buildArgs := []string{"build", "-o", bin, fmt.Sprintf("./testdata/%s.go", fileName)}
   329  	s := runutil.NewSequence(nil, os.Stdin, os.Stdout, os.Stderr, false, true)
   330  	if err := s.Last("go", buildArgs...); err != nil {
   331  		return "", err
   332  	}
   333  	return bin, nil
   334  }