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  }