github.com/stevenmatthewt/agent@v3.5.4+incompatible/bootstrap/shell/shell_test.go (about)

     1  package shell_test
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io/ioutil"
     8  	"log"
     9  	"os"
    10  	"os/exec"
    11  	"path/filepath"
    12  	"runtime"
    13  	"testing"
    14  	"time"
    15  
    16  	"github.com/buildkite/agent/bootstrap/shell"
    17  	"github.com/buildkite/bintest"
    18  )
    19  
    20  func TestRunAndCaptureWithTTY(t *testing.T) {
    21  	sshKeygen, err := bintest.CompileProxy("ssh-keygen")
    22  	if err != nil {
    23  		t.Fatal(err)
    24  	}
    25  	defer sshKeygen.Close()
    26  
    27  	sh := newShellForTest(t)
    28  	sh.PTY = true
    29  
    30  	go func() {
    31  		call := <-sshKeygen.Ch
    32  		fmt.Fprintln(call.Stdout, "Llama party! 🎉")
    33  		call.Exit(0)
    34  	}()
    35  
    36  	actual, err := sh.RunAndCapture(sshKeygen.Path, "-f", "my_hosts", "-F", "llamas.com")
    37  	if err != nil {
    38  		t.Error(err)
    39  	}
    40  
    41  	if expected := "Llama party! 🎉"; string(actual) != expected {
    42  		t.Fatalf("Expected %q, got %q", expected, actual)
    43  	}
    44  }
    45  
    46  func TestRun(t *testing.T) {
    47  	sshKeygen, err := bintest.CompileProxy("ssh-keygen")
    48  	if err != nil {
    49  		t.Fatal(err)
    50  	}
    51  	defer sshKeygen.Close()
    52  
    53  	out := &bytes.Buffer{}
    54  
    55  	sh := newShellForTest(t)
    56  	sh.PTY = false
    57  	sh.Writer = out
    58  	sh.Logger = &shell.WriterLogger{Writer: out, Ansi: false}
    59  
    60  	go func() {
    61  		call := <-sshKeygen.Ch
    62  		fmt.Fprintln(call.Stdout, "Llama party! 🎉")
    63  		call.Exit(0)
    64  	}()
    65  
    66  	if err = sh.Run(sshKeygen.Path, "-f", "my_hosts", "-F", "llamas.com"); err != nil {
    67  		t.Fatal(err)
    68  	}
    69  
    70  	actual := out.String()
    71  
    72  	promptPrefix := "$"
    73  	if runtime.GOOS == "windows" {
    74  		promptPrefix = ">"
    75  	}
    76  
    77  	if expected := promptPrefix + " " + sshKeygen.Path + " -f my_hosts -F llamas.com\nLlama party! 🎉\n"; actual != expected {
    78  		t.Fatalf("Expected %q, got %q", expected, actual)
    79  	}
    80  }
    81  
    82  func TestDefaultWorkingDirFromSystem(t *testing.T) {
    83  	sh, err := shell.New()
    84  	if err != nil {
    85  		t.Fatal(err)
    86  	}
    87  
    88  	currentWd, _ := os.Getwd()
    89  	if actual := sh.Getwd(); actual != currentWd {
    90  		t.Fatalf("Expected working dir %q, got %q", currentWd, actual)
    91  	}
    92  }
    93  
    94  func TestWorkingDir(t *testing.T) {
    95  	tempDir, err := ioutil.TempDir("", "shelltest")
    96  	if err != nil {
    97  		t.Fatal(err)
    98  	}
    99  	defer os.RemoveAll(tempDir)
   100  
   101  	// macos has a symlinked temp dir
   102  	if runtime.GOOS == "darwin" {
   103  		tempDir, _ = filepath.EvalSymlinks(tempDir)
   104  	}
   105  
   106  	dirs := []string{tempDir, "my", "test", "dirs"}
   107  
   108  	if err := os.MkdirAll(filepath.Join(dirs...), 0700); err != nil {
   109  		t.Fatal(err)
   110  	}
   111  
   112  	currentWd, _ := os.Getwd()
   113  
   114  	sh, err := shell.New()
   115  	sh.Logger = shell.DiscardLogger
   116  
   117  	if err != nil {
   118  		t.Fatal(err)
   119  	}
   120  
   121  	for idx := range dirs {
   122  		dir := filepath.Join(dirs[0 : idx+1]...)
   123  
   124  		if err := sh.Chdir(dir); err != nil {
   125  			t.Fatal(err)
   126  		}
   127  
   128  		if actual := sh.Getwd(); actual != dir {
   129  			t.Fatalf("Expected working dir %q, got %q", dir, actual)
   130  		}
   131  
   132  		var out string
   133  
   134  		// there is no pwd for windows, and getting it requires using a shell builtin
   135  		if runtime.GOOS == "windows" {
   136  			out, err = sh.RunAndCapture("cmd", "/c", "echo", "%cd%")
   137  			if err != nil {
   138  				t.Fatal(err)
   139  			}
   140  		} else {
   141  			out, err = sh.RunAndCapture("pwd")
   142  			if err != nil {
   143  				t.Fatal(err)
   144  			}
   145  		}
   146  
   147  		if actual := out; actual != dir {
   148  			t.Fatalf("Expected working dir (from pwd command) %q, got %q", dir, actual)
   149  		}
   150  	}
   151  
   152  	afterWd, _ := os.Getwd()
   153  	if afterWd != currentWd {
   154  		t.Fatalf("Expected working dir to be the same as before shell commands ran")
   155  	}
   156  }
   157  
   158  func TestLockFileRetriesAndTimesOut(t *testing.T) {
   159  	dir, err := ioutil.TempDir("", "shelltest")
   160  	if err != nil {
   161  		t.Fatal(err)
   162  	}
   163  	defer os.RemoveAll(dir)
   164  
   165  	sh := newShellForTest(t)
   166  	sh.Logger = shell.DiscardLogger
   167  
   168  	lockPath := filepath.Join(dir, "my.lock")
   169  
   170  	// acquire a lock in another process
   171  	cmd, err := acquireLockInOtherProcess(lockPath)
   172  	if err != nil {
   173  		t.Fatal(err)
   174  	}
   175  
   176  	defer cmd.Process.Kill()
   177  
   178  	// acquire lock
   179  	_, err = sh.LockFile(lockPath, time.Second*2)
   180  	if err != context.DeadlineExceeded {
   181  		t.Fatalf("Expected DeadlineExceeded error, got %v", err)
   182  	}
   183  }
   184  
   185  func acquireLockInOtherProcess(lockfile string) (*exec.Cmd, error) {
   186  	cmd := exec.Command(os.Args[0], "-test.run=TestAcquiringLockHelperProcess", "--", lockfile)
   187  	cmd.Env = []string{"GO_WANT_HELPER_PROCESS=1"}
   188  
   189  	err := cmd.Start()
   190  	if err != nil {
   191  		return cmd, err
   192  	}
   193  
   194  	// wait for the above process to get a lock
   195  	for {
   196  		if _, err = os.Stat(lockfile); os.IsNotExist(err) {
   197  			time.Sleep(time.Millisecond * 10)
   198  			continue
   199  		}
   200  		break
   201  	}
   202  
   203  	return cmd, nil
   204  }
   205  
   206  // TestAcquiringLockHelperProcess isn't a real test. It's used as a helper process
   207  func TestAcquiringLockHelperProcess(t *testing.T) {
   208  	if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
   209  		return
   210  	}
   211  
   212  	fileName := os.Args[len(os.Args)-1]
   213  	sh := newShellForTest(t)
   214  
   215  	log.Printf("Locking %s", fileName)
   216  	if _, err := sh.LockFile(fileName, time.Second*10); err != nil {
   217  		os.Exit(1)
   218  	}
   219  
   220  	log.Printf("Acquired lock %s", fileName)
   221  	c := make(chan struct{})
   222  	<-c
   223  }
   224  
   225  func newShellForTest(t *testing.T) *shell.Shell {
   226  	sh, err := shell.New()
   227  	if err != nil {
   228  		t.Fatal(err)
   229  	}
   230  	sh.Logger = shell.DiscardLogger
   231  	return sh
   232  }