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