github.com/keybase/client/go@v0.0.0-20241007131713-f10651d043c8/updater/command/command_test.go (about) 1 // Copyright 2016 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package command 5 6 import ( 7 "os" 8 "os/exec" 9 "path/filepath" 10 "reflect" 11 "runtime" 12 "strings" 13 "testing" 14 "time" 15 16 "github.com/keybase/go-logging" 17 "github.com/stretchr/testify/assert" 18 "github.com/stretchr/testify/require" 19 ) 20 21 var testLog = &logging.Logger{Module: "test"} 22 23 func TestExecEmpty(t *testing.T) { 24 result, err := Exec("", nil, time.Second, testLog) 25 assert.EqualError(t, err, "No command") 26 assert.Equal(t, result.Stdout.String(), "") 27 assert.Equal(t, result.Stderr.String(), "") 28 } 29 30 func TestExecInvalid(t *testing.T) { 31 result, err := Exec("invalidexecutable", nil, time.Second, testLog) 32 assert.Error(t, err) 33 require.True(t, strings.HasPrefix(err.Error(), `exec: "invalidexecutable": executable file not found in `)) 34 assert.Equal(t, result.Stdout.String(), "") 35 assert.Equal(t, result.Stderr.String(), "") 36 } 37 38 func TestExecEcho(t *testing.T) { 39 if runtime.GOOS == "windows" { 40 t.Skip("Unsupported on windows") 41 } 42 result, err := Exec("echo", []string{"arg1", "arg2"}, time.Second, testLog) 43 assert.NoError(t, err) 44 assert.Equal(t, result.Stdout.String(), "arg1 arg2\n") 45 } 46 47 func TestExecNil(t *testing.T) { 48 execCmd := func(name string, arg ...string) *exec.Cmd { 49 return nil 50 } 51 _, err := execWithFunc("echo", []string{"arg1", "arg2"}, nil, execCmd, time.Second, testLog) 52 require.Error(t, err) 53 } 54 55 func TestExecTimeout(t *testing.T) { 56 start := time.Now() 57 timeout := 10 * time.Millisecond 58 result, err := Exec("sleep", []string{"10"}, timeout, testLog) 59 elapsed := time.Since(start) 60 t.Logf("We elapsed %s", elapsed) 61 if elapsed < timeout { 62 t.Error("We didn't actually sleep more than a second") 63 } 64 assert.Equal(t, result.Stdout.String(), "") 65 assert.Equal(t, result.Stderr.String(), "") 66 require.EqualError(t, err, "Timed out") 67 } 68 69 func TestExecBadTimeout(t *testing.T) { 70 result, err := Exec("sleep", []string{"1"}, -time.Second, testLog) 71 assert.Equal(t, result.Stdout.String(), "") 72 assert.Equal(t, result.Stderr.String(), "") 73 assert.EqualError(t, err, "Invalid timeout: -1s") 74 } 75 76 type testObj struct { 77 StringVar string `json:"stringVar"` 78 NumberVar int `json:"numberVar"` 79 BoolVar bool `json:"boolVar"` 80 ObjectVar testNestedObj `json:"objectVar"` 81 } 82 83 type testNestedObj struct { 84 FloatVar float64 `json:"floatVar"` 85 } 86 87 const testJSON = `{ 88 "stringVar": "hi", 89 "numberVar": 1, 90 "boolVar": true, 91 "objectVar": { 92 "floatVar": 1.23 93 } 94 }` 95 96 var testVal = testObj{ 97 StringVar: "hi", 98 NumberVar: 1, 99 BoolVar: true, 100 ObjectVar: testNestedObj{ 101 FloatVar: 1.23, 102 }, 103 } 104 105 func TestExecForJSON(t *testing.T) { 106 var testValOut testObj 107 err := ExecForJSON("echo", []string{testJSON}, &testValOut, time.Second, testLog) 108 assert.NoError(t, err) 109 t.Logf("Out: %#v", testValOut) 110 if !reflect.DeepEqual(testVal, testValOut) { 111 t.Errorf("Invalid object: %#v", testValOut) 112 } 113 } 114 115 func TestExecForJSONEmpty(t *testing.T) { 116 err := ExecForJSON("", nil, nil, time.Second, testLog) 117 require.Error(t, err) 118 } 119 120 func TestExecForJSONInvalidObject(t *testing.T) { 121 // Valid JSON, but not the right object 122 validJSON := `{"stringVar": true}` 123 var testValOut testObj 124 err := ExecForJSON("echo", []string{validJSON}, &testValOut, time.Second, testLog) 125 require.Error(t, err) 126 t.Logf("Error: %s", err) 127 } 128 129 // TestExecForJSONAddingInvalidInput tests valid JSON input with invalid input after. 130 // We still succeed in this case since we got valid input to start. 131 func TestExecForJSONAddingInvalidInput(t *testing.T) { 132 var testValOut testObj 133 err := ExecForJSON("echo", []string{testJSON + "bad input"}, &testValOut, time.Second, testLog) 134 assert.NoError(t, err) 135 t.Logf("Out: %#v", testValOut) 136 if !reflect.DeepEqual(testVal, testValOut) { 137 t.Errorf("Invalid object: %#v", testValOut) 138 } 139 } 140 141 func TestExecForJSONTimeout(t *testing.T) { 142 var testValOut testObj 143 err := ExecForJSON("sleep", []string{"10"}, &testValOut, 10*time.Millisecond, testLog) 144 if assert.Error(t, err) { 145 assert.Equal(t, err.Error(), "Timed out") 146 } 147 } 148 149 // TestExecTimeoutProcessKilled checks to make sure process is killed after timeout 150 func TestExecTimeoutProcessKilled(t *testing.T) { 151 if runtime.GOOS == "windows" { 152 t.Skip("Unsupported on windows") 153 } 154 result, err := execWithFunc("sleep", []string{"10"}, nil, exec.Command, 10*time.Millisecond, testLog) 155 assert.Equal(t, result.Stdout.String(), "") 156 assert.Equal(t, result.Stderr.String(), "") 157 assert.Error(t, err) 158 require.NotNil(t, result.Process) 159 findProcess, _ := os.FindProcess(result.Process.Pid) 160 // This should error since killing a non-existant process should error 161 perr := findProcess.Kill() 162 assert.NotNil(t, perr, "Should have errored killing since killing non-existant process should error") 163 } 164 165 // TestExecNoExit runs a go binary called test from package go-updater/test, 166 // that should be installed prior to running the tests. 167 func TestExecNoExit(t *testing.T) { 168 path := filepath.Join(os.Getenv("GOPATH"), "bin", "test") 169 _, err := Exec(path, []string{"noexit"}, 10*time.Millisecond, testLog) 170 require.EqualError(t, err, "Timed out") 171 } 172 173 func TestExecOutput(t *testing.T) { 174 path := filepath.Join(os.Getenv("GOPATH"), "bin", "test") 175 result, err := execWithFunc(path, []string{"output"}, nil, exec.Command, time.Second, testLog) 176 assert.NoError(t, err) 177 assert.Equal(t, "stdout output\n", result.Stdout.String()) 178 assert.Equal(t, "stderr output\n", result.Stderr.String()) 179 } 180 181 func TestProgramArgsWith(t *testing.T) { 182 assert.Equal(t, []string(nil), Program{Args: nil}.ArgsWith(nil)) 183 assert.Equal(t, []string(nil), Program{Args: []string{}}.ArgsWith(nil)) 184 assert.Equal(t, []string{}, Program{Args: nil}.ArgsWith([]string{})) 185 assert.Equal(t, []string{}, Program{Args: []string{}}.ArgsWith([]string{})) 186 assert.Equal(t, []string{"1"}, Program{Args: []string{"1"}}.ArgsWith(nil)) 187 assert.Equal(t, []string{"1", "2"}, Program{Args: []string{"1"}}.ArgsWith([]string{"2"})) 188 assert.Equal(t, []string{"2"}, Program{Args: []string{}}.ArgsWith([]string{"2"})) 189 }