github.com/svanharmelen/air@v0.0.0-20230413074108-3c14aafcb1c0/runner/util_test.go (about) 1 package runner 2 3 import ( 4 "errors" 5 "fmt" 6 "io/ioutil" 7 "os" 8 "os/exec" 9 "path/filepath" 10 "reflect" 11 "runtime" 12 "strconv" 13 "strings" 14 "syscall" 15 "testing" 16 "time" 17 18 "github.com/stretchr/testify/assert" 19 ) 20 21 func TestIsDirRootPath(t *testing.T) { 22 result := isDir(".") 23 if result != true { 24 t.Errorf("expected '%t' but got '%t'", true, result) 25 } 26 } 27 28 func TestIsDirMainFile(t *testing.T) { 29 result := isDir("main.go") 30 if result != false { 31 t.Errorf("expected '%t' but got '%t'", true, result) 32 } 33 } 34 35 func TestIsDirFileNot(t *testing.T) { 36 result := isDir("main.go") 37 if result != false { 38 t.Errorf("expected '%t' but got '%t'", true, result) 39 } 40 } 41 42 func TestExpandPathWithDot(t *testing.T) { 43 path, _ := expandPath(".") 44 wd, _ := os.Getwd() 45 if path != wd { 46 t.Errorf("expected '%s' but got '%s'", wd, path) 47 } 48 } 49 50 func TestExpandPathWithHomePath(t *testing.T) { 51 path := "~/.conf" 52 result, _ := expandPath(path) 53 home := os.Getenv("HOME") 54 want := home + path[1:] 55 if result != want { 56 t.Errorf("expected '%s' but got '%s'", want, result) 57 } 58 } 59 60 func TestFileChecksum(t *testing.T) { 61 tests := []struct { 62 name string 63 fileContents []byte 64 expectedChecksum string 65 expectedChecksumError string 66 }{ 67 { 68 name: "empty", 69 fileContents: []byte(``), 70 expectedChecksum: "", 71 expectedChecksumError: "empty file, forcing rebuild without updating checksum", 72 }, 73 { 74 name: "simple", 75 fileContents: []byte(`foo`), 76 expectedChecksum: "2c26b46b68ffc68ff99b453c1d30413413422d706483bfa0f98a5e886266e7ae", 77 expectedChecksumError: "", 78 }, 79 { 80 name: "binary", 81 fileContents: []byte{0xF}, // invalid UTF-8 codepoint 82 expectedChecksum: "dc0e9c3658a1a3ed1ec94274d8b19925c93e1abb7ddba294923ad9bde30f8cb8", 83 expectedChecksumError: "", 84 }, 85 } 86 87 for _, test := range tests { 88 t.Run(test.name, func(t *testing.T) { 89 f, err := os.CreateTemp("", "") 90 if err != nil { 91 t.Fatalf("couldn't create temp file for test: %v", err) 92 } 93 94 defer func() { 95 if err := f.Close(); err != nil { 96 t.Errorf("error closing temp file: %v", err) 97 } 98 if err := os.Remove(f.Name()); err != nil { 99 t.Errorf("error removing temp file: %v", err) 100 } 101 }() 102 103 _, err = f.Write(test.fileContents) 104 if err != nil { 105 t.Fatalf("couldn't write to temp file for test: %v", err) 106 } 107 108 checksum, err := fileChecksum(f.Name()) 109 if err != nil && err.Error() != test.expectedChecksumError { 110 t.Errorf("expected '%s' but got '%s'", test.expectedChecksumError, err.Error()) 111 } 112 113 if checksum != test.expectedChecksum { 114 t.Errorf("expected '%s' but got '%s'", test.expectedChecksum, checksum) 115 } 116 }) 117 } 118 } 119 120 func TestChecksumMap(t *testing.T) { 121 m := &checksumMap{m: make(map[string]string, 3)} 122 123 if !m.updateFileChecksum("foo.txt", "abcxyz") { 124 t.Errorf("expected no entry for foo.txt, but had one") 125 } 126 127 if m.updateFileChecksum("foo.txt", "abcxyz") { 128 t.Errorf("expected matching entry for foo.txt") 129 } 130 131 if !m.updateFileChecksum("foo.txt", "123456") { 132 t.Errorf("expected matching entry for foo.txt") 133 } 134 135 if !m.updateFileChecksum("bar.txt", "123456") { 136 t.Errorf("expected no entry for bar.txt, but had one") 137 } 138 } 139 140 func TestAdaptToVariousPlatforms(t *testing.T) { 141 config := &Config{ 142 Build: cfgBuild{ 143 Bin: "tmp\\main.exe -dev", 144 }, 145 } 146 adaptToVariousPlatforms(config) 147 if config.Build.Bin != "tmp\\main.exe -dev" { 148 t.Errorf("expected '%s' but got '%s'", "tmp\\main.exe -dev", config.Build.Bin) 149 } 150 } 151 152 func Test_killCmd_no_process(t *testing.T) { 153 e := Engine{ 154 config: &Config{ 155 Build: cfgBuild{ 156 SendInterrupt: false, 157 }, 158 }, 159 } 160 _, err := e.killCmd(&exec.Cmd{ 161 Process: &os.Process{ 162 Pid: 9999, 163 }, 164 }) 165 if err == nil { 166 t.Errorf("expected error but got none") 167 } 168 if !errors.Is(err, syscall.ESRCH) { 169 t.Errorf("expected '%s' but got '%s'", syscall.ESRCH, errors.Unwrap(err)) 170 } 171 } 172 173 func Test_killCmd_SendInterrupt_false(t *testing.T) { 174 _, b, _, _ := runtime.Caller(0) 175 176 // Root folder of this project 177 dir := filepath.Dir(b) 178 err := os.Chdir(dir) 179 if err != nil { 180 t.Fatalf("couldn't change directory: %v", err) 181 } 182 183 // clean file before test 184 os.Remove("pid") 185 defer os.Remove("pid") 186 e := Engine{ 187 config: &Config{ 188 Build: cfgBuild{ 189 SendInterrupt: false, 190 }, 191 }, 192 } 193 startChan := make(chan struct { 194 pid int 195 cmd *exec.Cmd 196 }) 197 go func() { 198 cmd, _, _, err := e.startCmd("sh _testdata/run-many-processes.sh") 199 if err != nil { 200 t.Errorf("failed to start command: %v", err) 201 return 202 } 203 pid := cmd.Process.Pid 204 t.Logf("process pid is %v", pid) 205 startChan <- struct { 206 pid int 207 cmd *exec.Cmd 208 }{pid: pid, cmd: cmd} 209 cmd.Wait() 210 t.Logf("wait finished") 211 }() 212 resp := <-startChan 213 t.Logf("process started. checking pid %v", resp.pid) 214 time.Sleep(2 * time.Second) 215 t.Logf("%v", resp.cmd.Process.Pid) 216 pid, err := e.killCmd(resp.cmd) 217 if err != nil { 218 t.Fatalf("failed to kill command: %v", err) 219 } 220 t.Logf("%v was been killed", pid) 221 // check processes were being killed 222 // read pids from file 223 bytesRead, _ := ioutil.ReadFile("pid") 224 lines := strings.Split(string(bytesRead), "\n") 225 for _, line := range lines { 226 _, err := strconv.Atoi(line) 227 if err != nil { 228 t.Logf("failed to covert str to int %v", err) 229 continue 230 } 231 _, err = exec.Command("ps", "-p", line, "-o", "comm= ").Output() 232 if err == nil { 233 t.Fatalf("process should be killed %v", line) 234 } 235 } 236 } 237 238 func TestGetStructureFieldTagMap(t *testing.T) { 239 c := Config{} 240 tagMap := flatConfig(c) 241 for _, i2 := range tagMap { 242 fmt.Printf("%v\n", i2.fieldPath) 243 } 244 } 245 246 func TestSetStructValue(t *testing.T) { 247 c := Config{} 248 v := reflect.ValueOf(&c) 249 setValue2Struct(v, "TmpDir", "asdasd") 250 assert.Equal(t, "asdasd", c.TmpDir) 251 } 252 253 func TestNestStructValue(t *testing.T) { 254 c := Config{} 255 v := reflect.ValueOf(&c) 256 setValue2Struct(v, "Build.Cmd", "asdasd") 257 assert.Equal(t, "asdasd", c.Build.Cmd) 258 } 259 260 func TestNestStructArrayValue(t *testing.T) { 261 c := Config{} 262 v := reflect.ValueOf(&c) 263 setValue2Struct(v, "Build.ExcludeDir", "dir1,dir2") 264 assert.Equal(t, []string{"dir1", "dir2"}, c.Build.ExcludeDir) 265 } 266 267 func TestNestStructArrayValueOverride(t *testing.T) { 268 c := Config{ 269 Build: cfgBuild{ 270 ExcludeDir: []string{"default1", "default2"}, 271 }, 272 } 273 v := reflect.ValueOf(&c) 274 setValue2Struct(v, "Build.ExcludeDir", "dir1,dir2") 275 assert.Equal(t, []string{"dir1", "dir2"}, c.Build.ExcludeDir) 276 } 277 278 func TestCheckIncludeFile(t *testing.T) { 279 e := Engine{ 280 config: &Config{ 281 Build: cfgBuild{ 282 IncludeFile: []string{"main.go"}, 283 }, 284 }, 285 } 286 assert.Equal(t, e.checkIncludeFile("main.go"), true) 287 assert.Equal(t, e.checkIncludeFile("no.go"), false) 288 assert.Equal(t, e.checkIncludeFile("."), false) 289 }