github.com/connorvict/air@v0.0.0-20231005162537-279bf07db0d5/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  }